From e64a532ba6685f985815f90ad0c64819e3fbb019 Mon Sep 17 00:00:00 2001 From: sebmas Date: Tue, 20 Jan 2026 12:10:36 +0100 Subject: [PATCH] added code --- xml_extractor.py | 230 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 xml_extractor.py diff --git a/xml_extractor.py b/xml_extractor.py new file mode 100644 index 0000000..2d104c3 --- /dev/null +++ b/xml_extractor.py @@ -0,0 +1,230 @@ +import tkinter as tk +from tkinter import filedialog, messagebox, ttk +import xml.etree.ElementTree as ET +import os + +class XMLExtractorApp: + def __init__(self, root): + self.root = root + self.root.title("XML Shape to Symbol Extractor") + self.root.geometry("600x600") + self.root.resizable(False, False) + + self.input_file = None + self.tree = None + + self.create_widgets() + + def create_widgets(self): + # Title + title = tk.Label(self.root, text="XML Shape to Symbol Extractor", + font=("Arial", 16, "bold")) + title.pack(pady=10) + + # Info label + info = tk.Label(self.root, text="Extracts from: /layers/layer/shape\nWrites to: /key/kentry/custom/symbol/group/shape", + font=("Arial", 9), fg="gray", justify=tk.CENTER) + info.pack() + + # Input file frame + input_frame = tk.Frame(self.root) + input_frame.pack(pady=10, padx=20, fill=tk.X) + + tk.Label(input_frame, text="Input LXXPLOT File:", + font=("Arial", 10)).pack(anchor=tk.W) + + file_display_frame = tk.Frame(input_frame) + file_display_frame.pack(fill=tk.X, pady=5) + + self.file_label = tk.Label(file_display_frame, text="No file selected", + fg="gray", anchor=tk.W) + self.file_label.pack(side=tk.LEFT, fill=tk.X, expand=True) + + browse_btn = tk.Button(file_display_frame, text="Browse", + command=self.browse_file) + browse_btn.pack(side=tk.RIGHT) + + # Output file frame + output_frame = tk.Frame(self.root) + output_frame.pack(pady=10, padx=20, fill=tk.X) + + tk.Label(output_frame, text="Output LXKEY File:", + font=("Arial", 10)).pack(anchor=tk.W) + + self.output_entry = tk.Entry(output_frame, font=("Arial", 10)) + self.output_entry.pack(fill=tk.X, pady=5) + self.output_entry.insert(0, "symbol_output.lxkey") + + # Additional fields frame + fields_frame = tk.Frame(self.root) + fields_frame.pack(pady=10, padx=20, fill=tk.X) + + tk.Label(fields_frame, text="Additional Tag Values:", + font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=(0, 5)) + + # Name field (displayed as "Type") + name_frame = tk.Frame(fields_frame) + name_frame.pack(fill=tk.X, pady=2) + tk.Label(name_frame, text="Type:", width=10, anchor=tk.W).pack(side=tk.LEFT) + self.name_entry = tk.Entry(name_frame, font=("Arial", 10)) + self.name_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # ID field + id_frame = tk.Frame(fields_frame) + id_frame.pack(fill=tk.X, pady=2) + tk.Label(id_frame, text="ID:", width=10, anchor=tk.W).pack(side=tk.LEFT) + self.id_entry = tk.Entry(id_frame, font=("Arial", 10)) + self.id_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # Fname field + fname_frame = tk.Frame(fields_frame) + fname_frame.pack(fill=tk.X, pady=2) + tk.Label(fname_frame, text="Fname:", width=10, anchor=tk.W).pack(side=tk.LEFT) + self.fname_entry = tk.Entry(fname_frame, font=("Arial", 10)) + self.fname_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # Kind field (dropdown) + kind_frame = tk.Frame(fields_frame) + kind_frame.pack(fill=tk.X, pady=2) + tk.Label(kind_frame, text="Kind:", width=10, anchor=tk.W).pack(side=tk.LEFT) + + # Dictionary mapping display names to values + self.kind_options = { + "eko / ERS": "ers", + "Zoom Leko / ERS": "zers", + "Fresnel / PC": "fres", + "PAR": "par", + "Flood": "fl", + "Striplight": "strip", + "DMX Device": "scroll", + "Mirror": "mirror", + "Focus Point": "focus", + "Other": "misc", + "Automated Fixture": "mover", + "Color Mixing Automated Fixture": "cmymvr", + "Color Mixing Rectangular Beam (obsolete rgb)": "rgb", + "Color Mixing Rectangular Beam (obsolete rgba)": "rgba", + "Color Mixing Rectangular Beam": "led7", + "Color Mixing Striplight": "led7s", + "Color Mixing Leko / ERS": "cmers", + "Color Mixing Zoom ERS": "cmzers", + "Color Mixing DMX Device": "cmscroll", + "Network Device": "netnode" + } + + self.kind_var = tk.StringVar() + self.kind_dropdown = ttk.Combobox(kind_frame, textvariable=self.kind_var, + values=list(self.kind_options.keys()), + state="readonly", + font=("Arial", 10)) + self.kind_dropdown.pack(side=tk.LEFT, fill=tk.X, expand=True) + self.kind_dropdown.current(0) # Set default to first option + + # Extract button + extract_btn = tk.Button(self.root, text="Extract and Convert", + command=self.extract_and_convert, + font=("Arial", 12, "bold"), + bg="#4CAF50", fg="white", + padx=20, pady=10) + extract_btn.pack(pady=20) + + # Status label + self.status_label = tk.Label(self.root, text="", + font=("Arial", 9), fg="blue") + self.status_label.pack(pady=5) + + def browse_file(self): + filename = filedialog.askopenfilename( + title="Select LXXPLOT File", + filetypes=[("LXXPLOT files", "*.lxxplot"), ("All files", "*.*")] + ) + if filename: + self.input_file = filename + self.file_label.config(text=os.path.basename(filename), fg="black") + self.status_label.config(text="File loaded successfully", fg="green") + + def extract_and_convert(self): + # Validate inputs + if not self.input_file: + messagebox.showerror("Error", "Please select an input LXXPLOT file") + return + + output_filename = self.output_entry.get().strip() + if not output_filename: + messagebox.showerror("Error", "Please enter an output file name") + return + + # Get the directory of the input file and create full output path + input_dir = os.path.dirname(self.input_file) + output_file = os.path.join(input_dir, output_filename) + + try: + # Parse the LXXPLOT file (XML format) + tree = ET.parse(self.input_file) + root = tree.getroot() + + # Find the shape tag using the xpath: /layers/layer/shape + shape_element = root.find("./layers/layer/shape") + + if shape_element is None: + messagebox.showerror("Error", + "Path '/layers/layer/shape' not found in the LXXPLOT file") + return + + # Create the new XML structure: /key/kentry/custom/symbol/group/shape + new_root = ET.Element("key") + kentry = ET.SubElement(new_root, "kentry") + + # Add name, kind, and fname as siblings of custom with user-provided values + name = ET.SubElement(kentry, "name") + name.text = self.name_entry.get().strip() + + id_elem = ET.SubElement(kentry, "id") + id_elem.text = self.id_entry.get().strip() + + kind = ET.SubElement(kentry, "kind") + # Get the value corresponding to the selected display name + selected_display = self.kind_var.get() + kind.text = self.kind_options.get(selected_display, "") + + fname = ET.SubElement(kentry, "fname") + fname.text = self.fname_entry.get().strip() + + custom = ET.SubElement(kentry, "custom") + + symbol = ET.SubElement(custom, "symbol") + group = ET.SubElement(symbol, "group") + shape = ET.SubElement(group, "shape") + + # Copy the content from shape element to the new shape element + shape.text = shape_element.text + shape.attrib = shape_element.attrib + + # Copy all child elements from original shape to new shape + for child in shape_element: + shape.append(child) + + # Create and write the new LXKEY file + new_tree = ET.ElementTree(new_root) + ET.indent(new_tree, space=" ") + new_tree.write(output_file, encoding="utf-8", xml_declaration=True) + + self.status_label.config( + text=f"Successfully converted and saved to {output_filename}", + fg="green" + ) + messagebox.showinfo("Success", + f"Shape content extracted and written to /key/kentry/custom/symbol/group/shape\nSaved to: {output_file}") + + except ET.ParseError as e: + messagebox.showerror("Parse Error", + f"Failed to parse XML file:\n{str(e)}") + self.status_label.config(text="Parse error", fg="red") + except Exception as e: + messagebox.showerror("Error", f"An error occurred:\n{str(e)}") + self.status_label.config(text="Error occurred", fg="red") + +if __name__ == "__main__": + root = tk.Tk() + app = XMLExtractorApp(root) + root.mainloop() \ No newline at end of file