-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgui.py
263 lines (190 loc) · 8.3 KB
/
gui.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
'''
"QLC+ File Ferret" - (C) 2019 Daniel Fairhead
---------------------------------------------
gui.py
- The main Graphical User Interface
'''
########
# One Day TODO:
# - Checking Fixtures as well when moving functions.
########
import sys
from os.path import basename, abspath, dirname, join as joindir, splitext
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.filedialog import askopenfile, asksaveasfile
from tkinter.messagebox import askyesno
from reader import QLCFile
# Constants:
FUNCTION_TYPES = [
#('QLC Function TYPE', 'Plural Display Name')
('Scene', 'Scenes'),
('Sequence', 'Sequences'),
('Collection', 'Collections'),
('EFX', 'EFX'),
('Audio', 'Audio Files'),
('Video', 'Video Files'),
('Show', 'Shows'),
('Chaser', 'Chasers'),
('RGBMatrix', 'RGB Matrix Functions'),
('Script', 'Scripts'),
]
ICONS = {}
# Global State: (oh noes)
CLIPBOARD = set()
class QLCFileBox(ttk.Frame):
'''
Multiple files can be loaded into the main window. Each one gets its own
QLCFileBox which is a vertical strip with a treeview showing all the functions,
some filtering/searching tools and some buttons to Copy/Paste/Delete functions.
---
The TreeView widget stores the Function ID (fid) as its unique item idenifier (iid)
which is useful for getting back to the original function in the XML file.
However, since we also need some 'fake' other nodes in the tree (Such as section
nodes, and possibly folder, etc later on) all function iids are prefixed with 'FUNC:'
and all section nodes are prefixed with '_'.
'''
def __init__(self, master=None, filename=None):
tk.Frame.__init__(self, master)
self.pack(fill=tk.BOTH, expand=1)
self.filename=filename
self.create_widgets()
self.load_file()
self.update_treeview()
def create_widgets(self):
self.filenameText = tk.StringVar()
ttk.Label(self, textvariable=self.filenameText).pack()
self.filterText = ttk.Entry(self) # TODO
self.filterText.bind('<Key>', self.update_filter)
self.filterText.pack(padx=0.1, fill=tk.X, expand=1)
self.functionList = ttk.Treeview(self)
self.functionList.pack(fill=tk.BOTH, expand=1)
self.toolbar = tk.Frame(self)
self.saveBtn = ttk.Button(self, text="Save As", command=self.save_as)
self.saveBtn.pack(side=tk.RIGHT)
self.saveBtn = ttk.Button(self, text="Close") # TODO
self.saveBtn.pack(side=tk.RIGHT)
self.pasteBtn = ttk.Button(self, text="Paste here", command=self.pasteToHere)
self.pasteBtn.pack(side=tk.RIGHT)
ttk.Button(self, text="Copy Selected", command=self.copySelected).pack(side=tk.RIGHT)
self.toolbar.pack(fill=tk.BOTH, expand=1)
def save_as(self):
oldpath = abspath(self.filename)
base, extn = splitext(basename(oldpath))
new_default_name = base + '_modified' + extn
filename = asksaveasfile(
title="Save File as",
defaultextension=".qxf",
initialdir=dirname(oldpath),
initialfile=new_default_name,
filetypes=[("QLC+ File", '*.qxw')],
)
if not filename: return
filename = filename.name
self.qfile.write(filename)
self.filename = filename
self.filenameText.set(basename(self.filename))
self.update_treeview()
def load_file(self):
self.qfile = QLCFile(self.filename)
self.filenameText.set(basename(self.filename))
def update_filter(self, event):
self.update_treeview()
def update_treeview(self):
'''
This should be able to be called multiple times without mucking up
data. It's called after file load, and also after paste / delete
operations. If performance is an issue those might get replaced with
simpler refresh functions.
'''
funcs = self.qfile.list_functions()
used_ids = self.qfile.all_used_function_ids()
self.functionList.delete(*self.functionList.get_children())
# Top level tree items (Sections):
for iid, plural in FUNCTION_TYPES:
self.functionList.insert('', 'end', '_' + iid, text=plural, open=True, image=ICONS[iid])
allcount = 0
orphancount = 0
# TODO: Add an extra column showing if a function is used or not?
# TODO: some kind of renaming functions capacity?
# TODO: An 'auto-update all buttons and sliders' in the VC to have new names?
# TODO: Show in folders mode
# TODO: Create folders and sort out functions into new folders depending on usage.
# TODO: Images for function type...
# TODO: Checking / Fixing Folders, what if something has a path that doesn't exist?
filtertext = self.filterText.get()
for f in funcs:
allcount += 1
name = "{} [{}]".format(f.attrib["Name"],f.attrib["ID"])
if not filtertext in name.lower(): continue
self.functionList.insert('_' + f.attrib["Type"],'end', 'FUNC:' + f.attrib["ID"], text=name)
#[print(x) for x in self.qfile.subfunction_ids(f)]
#if f.attrib['ID'] not in used_ids:
# self.orphanFunctions.insert(tk.END, name)
# orphancount += 1
#self.allFuncLabel.set("All Functions ({})".format(allcount))
#self.orphanFuncLabel.set("Orphan Functions ({})".format(orphancount))
def copySelected(self):
# Only get selected functions, and strip FUNC: treeview iid prefix:
selected_ids = [iid[5:] for iid in self.functionList.selection()
if iid.startswith('FUNC:')]
CLIPBOARD.clear()
for func in self.qfile.iter_functions_for_clipboard(selected_ids):
CLIPBOARD.add(func)
print('%i items in clipboard' % len(CLIPBOARD))
def pasteToHere(self):
self.qfile.paste_functions_here(CLIPBOARD)
self.update_treeview()
class Application(ttk.Frame):
'''
The main Application Frame.
'''
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.create_widgets()
def createToolbar(self):
self.toolBar = tk.Frame()
def btn(text, command):
b = ttk.Button(self.toolBar, text=text, command=command)
b.pack(side=tk.LEFT)
return b
btn('Quit', self.quit)
btn('Load File', self.load_file)
# TODO: a Status bar or something showing whats in the clipboard?
# TODO: an UNDO system?
self.toolBar.pack(side=tk.TOP)
def create_widgets(self):
self.createToolbar()
self.filesArea = tk.PanedWindow(orient=tk.HORIZONTAL, sashpad=10, sashwidth=2, height=400, width=600)
self.filesArea.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1)
def load_file(self, filename=None):
filename = filename or askopenfile(
filetypes=[("QLC+ File",'*.qxw'),
("XML File", '*.xml'),
('Other...','*')])
if not filename:
return
if hasattr(filename, 'name'):
filename = filename.name
x = QLCFileBox(self.filesArea, filename)
self.filesArea.add(x, minsize=100)
# resize all panes:
panes = self.filesArea.panes()
panesize = self.filesArea.winfo_width() / len(panes)
for pane in panes:
self.filesArea.paneconfigure(pane, width = panesize)
if __name__ == '__main__':
app = Application()
app.master.title('QLC+ Multi-file Helper Utility')
iconsdir = joindir(dirname(abspath(__file__)), 'icons')
for functype, _ in FUNCTION_TYPES:
icon = tk.PhotoImage(file=joindir(iconsdir, functype.lower() + '.png'))
icon = icon.subsample(2, 2)
ICONS[functype] = icon
for f in (sys.argv[1:]):
try:
app.load_file(f)
except Exception as e:
print(e)
app.mainloop()