diff --git a/README.md b/README.md index b632d3b..26a73bf 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,58 @@ -# QLC-File-Ferret -A Utility for cleaning up QLC+ files, and moving functions between files. +QLC+ File Ferret +================ -WORK IN PROGRESS!! +**WARNING! VERY EARLY PROTOTYPE! NOT READY FOR PRODUCTION USE!!!** -This is not even beginning to be Alpha yet. +We use QLC+ a lot. -It probably wont' even run at this point. +We tend to have a single 'base' file, and then make specialised files per +event. -I'm just prototyping at the moment. +We have a few problems: + +- Our 'base' file tends to get quite messy +- There's no easy way to move functions from a specialised file back into + the base, or if one operator makes a cool sequence for a specific dance + in one file, to move it to another. + +This utility will try to help. + +So far: +------- + +Can move functions (scenes, chasers, collections, sequences, shows) from one +file to another. + +**WARNING!** + +It doesn't yet have any concept of moving fixtures around - so if you have +different fixtures in your different files, I have no clue what will happen. + +Next: +----- + +1) It has the capacity to see which functions are 'orphans' - not used by any + other function, or attached to anything in the Virtual Console. There should + be a way to display that, and clean it up. + +2) We need a TON of tests to confirm that it's not gonna screw up our files. + This has only been made as a quick few-hours-between-shows hacky prototype. + +3) Searching / Filtering / Sorting of functions by name, + and by orphan-parent status. + +4) Creating / Moving / Renaming Functions, with some semblance of automation, + to allow (say) moving all functions used ONLY by a chaser into a folder of + the same name, or renaming them all to helpful things... + +5) Fixture aware! So checking files are compatible (have compatible fixtures) + or at least figuring out what to do when there are differences. + +6) Make it prettier. Icons, statusbar, etc. + +To use: +------- + + python3 gui.py + +And you can probably figure it out. diff --git a/gui.py b/gui.py index e7faa7b..e8c234c 100644 --- a/gui.py +++ b/gui.py @@ -35,7 +35,7 @@ ] # Global State: (oh noes) -CLIPBOARD = [] +CLIPBOARD = set() class QLCFileBox(ttk.Frame): ''' @@ -155,12 +155,12 @@ def copySelected(self): func = self.qfile.function_by_id(iid) if not func: continue - if not func in CLIPBOARD: - CLIPBOARD.append(func) + CLIPBOARD.add(func) - for func in self.qfile.subfunction_ids(func, recurse=True): - if not func in CLIPBOARD: - CLIPBOARD.append(func) + for subfunc in self.qfile.subfunction_ids(func, recurse=True): + CLIPBOARD.add(subfunc) + + print('%i items in clipboard' % len(CLIPBOARD)) def pasteToHere(self): @@ -174,6 +174,7 @@ class Application(ttk.Frame): ''' def __init__(self, master=None): tk.Frame.__init__(self, master) + self.pack() self.create_widgets() @@ -217,6 +218,7 @@ def load_file(self, filename=None): if __name__ == '__main__': app = Application() + app.master.title('QLC+ Multi-file Helper Utility') for f in (sys.argv[1:]): diff --git a/reader.py b/reader.py index 2eac4fa..c51825b 100644 --- a/reader.py +++ b/reader.py @@ -26,7 +26,7 @@ def write(self, filename, *vargs, **kwargs): with open(filename, 'w') as f: # apparently etree cannot write doctypes :-( # oh well. we can. - f.write('\n\n') + f.write('\n\n') new_file.write(f, encoding="unicode", xml_declaration=False, *vargs, **kwargs) def list_functions(self): @@ -95,7 +95,7 @@ def function_by_id(self, fid): def subfunction_ids(self, func, recurse=False): if func.attrib['Type'] == 'Show': - for f in func.findall("*//qlc:ShowFunction", NS): + for f in func.findall(".//qlc:ShowFunction", NS): subfunc = self.function_by_id(f.attrib["ID"]) yield subfunc if recurse: @@ -103,15 +103,29 @@ def subfunction_ids(self, func, recurse=False): elif func.attrib['Type'] == 'Sequence': yield func.attrib['BoundScene'] elif func.attrib['Type'] in ('Collection', 'Chaser'): - for f in func.findall("*//qlc:Step", NS): + print(ET.tostring(func)) + for f in func.findall(".//qlc:Step", NS): + print('step!') + print(ET.tostring(f)) subfunc = self.function_by_id(f.text) yield subfunc if recurse: yield from self.subfunction_ids(subfunc) - else: - print("No Subfunctions!") + def subfunction_id_replace(self, func, old_id, new_id): + if func.attrib['Type'] == 'Show': + for f in func.findall(".//qlc:ShowFunction", NS): + if f.attrib["ID"] == old_id: + f.attrib["ID"] = new_id + elif func.attrib['Type'] == 'Sequence': + if func.attrib['BoundScene'] == old_id: + func.attrib['BoundScene'] = new_id + elif func.attrib['Type'] in ('Collection', 'Chaser'): + for f in func.findall(".//qlc:Step", NS): + if f.text == old_id: + f.text = new_id + def paste_functions_here(self, clipboard): @@ -132,14 +146,19 @@ def paste_functions_here(self, clipboard): # TODO: Look for Duplicate Functions - and don't copy them automatically, # but instead ask the user what to do. + replace_tables = {} + for f in new_functions: if f.attrib['ID'] in current_ids: fresh_id += 1 + + # Fix any references to this function_id in other functions: + for ff in new_functions: + self.subfunction_id_replace(ff, f.attrib['ID'], str(fresh_id)) + + # And update this function itself... f.attrib['ID'] = str(fresh_id) current_ids.add(f.attrib['ID']) - # TODO: - # - go through all other functions looking for any uses of the old - # function id, and replace it with the new one. enginenode = self.root.find('qlc:Engine', NS)