1 """GUI support for user scripting.
2
3 Copyright 2014 Research Foundation State University of New York
4 This file is part of QUB Express.
5
6 QUB Express is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 QUB Express is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License,
17 named LICENSE.txt, in the QUB Express program directory. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 """
21
22 import os
23 import gobject
24 import gtk
25
26 import qubx.faces
27 import qubx.GTK
28 import qubx.pyenv
29 import qubx.pyenvGTK
30 import qubx.settings
31 import qubx.tree
32 from qubx.GTK import pack_item, pack_label, pack_button, build_menuitem
33 from qubx.util_types import *
34
39
45
47 - def __init__(self, name, node, paths, path_nodes):
61 try:
62 i = path_or_i / 1
63 except:
64 i = self.paths.index(path_or_i)
65 self.node.remove(self.path_nodes[i])
66 del self.path_nodes[i]
67 del self.paths[i]
68 self.OnRemoved(self, i)
70 p, pn = self.paths[a], self.path_nodes[a]
71 self.node.remove(self.path_nodes[a])
72 del self.paths[a]
73 del self.path_nodes[a]
74 if b:
75 print 'ins',a,b
76 self.node.insert(pn, self.path_nodes[b-1])
77 else:
78 print 'ins',a
79 self.node.insert(pn)
80 self.paths.insert(b, p)
81 self.path_nodes.insert(b, pn)
83 self.paths[i] = new_path
84 self.path_nodes[i].data = new_path
85
102 try:
103 i = name_or_i / 1
104 except:
105 i = 0
106 while i < len(self.sequences):
107 if self.sequences[i].name == name_or_i:
108 break
109 else:
110 raise KeyError(name_or_i)
111 self.props.active.remove(self.sequences[i].node)
112 del self.sequences[i]
113 self.OnRemoved(i)
117
119 __explore_featured = ['seqs', 'toolbar', 'book', 'index', 'seq', 'import_seq', 'export_seq']
120 - def __init__(self, name='Seq', global_name='QubX.Admin.Seq'):
121 super(SeqFace, self).__init__(name, global_name)
122 self.__ref = Reffer()
123 self.seqs = ScriptSeqs()
124 self.seqs.OnInserted += self.__ref(self.__onInsertedSeq)
125 self.seqs.OnRemoved += self.__ref(self.__onRemovedSeq)
126 self.seqs.OnRenamed += self.__ref(self.__onRenamedSeq)
127 self.__index = -1
128 self.__seq = None
129 self.__serial = 1
130 self.__running_all = False
131 self.__run_count = 0
132 self.__views = []
133 self.__path = str(self.seqs.props.active['Path'].data)
134
135 h = pack_item(gtk.HBox(), self)
136 mb = pack_item(gtk.MenuBar(), h)
137 mnu = gtk.Menu()
138 mnuRoot = build_menuitem('Sequence', submenu=mnu)
139 mb.append(mnuRoot)
140 self.itemNew = build_menuitem('New sequence...', self.__ref(self.__onClickNew), menu=mnu)
141 self.mnuChoose = gtk.Menu()
142 self.mnuChooseItems = []
143 self.itemChoose = build_menuitem('Choose sequence', submenu=self.mnuChoose, menu=mnu)
144 self.itemDel = build_menuitem('Delete sequence', self.__ref(self.__onClickDel), menu=mnu)
145 mnu.append(gtk.SeparatorMenuItem())
146 self.itemExport = build_menuitem('Export sequence...', self.__ref(self.__onClickExport), menu=mnu)
147 self.itemImport = build_menuitem('Import sequence...', self.__ref(self.__onClickImport), menu=mnu)
148 mnu.append(gtk.SeparatorMenuItem())
149 self.itemAdd = build_menuitem('Add script to sequence...', self.__ref(self.__onClickAdd), menu=mnu)
150 self.itemRem = build_menuitem('Remove script from sequence', self.__ref(self.__onClickRem), menu=mnu)
151 mnu.append(gtk.SeparatorMenuItem())
152 self.itemRun = build_menuitem('Run sequence', self.__ref(self.__onClickRun), menu=mnu)
153 mnu.show_all()
154
155 pack_label('Current sequence: ', h)
156 self.txtSeqName = pack_label('', h)
157
158 self.toolbar = pack_item(gtk.Toolbar(), h)
159 self.toolbar.set_size_request(100, -1)
160 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
161 self.toolbar.set_tooltips(True)
162 self.btnStopSeq = gtk.ToolButton(gtk.STOCK_MEDIA_STOP)
163 self.btnStopSeq.connect('clicked', self.__onClickRun)
164 self.btnStopSeq.show()
165 self.btnStopSeq.set_sensitive(False)
166 self.toolbar.insert(self.btnStopSeq, -1)
167 self.btnPlaySeq = gtk.ToolButton(gtk.STOCK_MEDIA_PLAY)
168 self.btnPlaySeq.connect('clicked', self.__onClickRun)
169 self.btnPlaySeq.show()
170 self.toolbar.insert(self.btnPlaySeq, -1)
171
172 self.btnRem = pack_button('X', h, self.__onClickRem, at_end=True)
173 self.btnRem.set_tooltip_text('Remove this tab from the sequence')
174 self.panTabTools = pack_item(gtk.HBox(), h, at_end=True)
175 self.txtScriptName = pack_label('', h, at_end=True)
176
177 self.book = pack_item(gtk.Notebook(), self, expand=True)
178 self.book.set_scrollable(True)
179 self.book.connect('switch_page', self.__onSwitchPage)
180 self.book.connect('page_reordered', self.__onPageReordered)
181 page0 = gtk.VBox()
182 page0.show()
183 self.book.insert_page(page0, build_tab_button('+', self.__onClickAdd))
184
185
186 for i, seq in enumerate(self.seqs.sequences):
187 self.__onInsertedSeq(i, seq)
188 if self.seqs.sequences and (self.__index < 0):
189 self.index = 0
190
191 index = property(lambda self: self.__index, lambda self, x: self.set_index(x))
192 seq = property(lambda self: self.__seq)
193
200 self.mnuChoose.remove(self.mnuChooseItems[i])
201 del self.mnuChooseItems[i]
202 if i == self.__index:
203 if i == len(self.mnuChooseItems):
204 self.index = i - 1
205 else:
206 self.index = i
214 name = qubx.pyenvGTK.prompt_entry('Name of new script sequence:', "seq%i"%self.__serial)
215 if name is None:
216 return
217 self.__serial += 1
218 self.seqs.add_seq(name)
219 self.index = len(self.seqs.sequences) - 1
221 if qubx.pyenvGTK.prompt_choices("Delete the sequence? This can't be undone. (Individual scripts will not be deleted.)"):
222 self.seqs.del_seq(self.index)
256 for p in altpaths:
257 abspath = os.path.abspath(os.path.join(p, relpath))
258 if os.path.exists(abspath):
259 seq.append(abspath)
260 return True
261 return False
263 name = os.path.split(relpaths[0])[1]
264 response = qubx.pyenvGTK.show_message("A script could not be found:\n%s\nTry to locate it?" % relpaths[0],
265 buttons=gtk.BUTTONS_YES_NO,
266 title="Importing script sequence...",
267 parent=self.parent_window)
268 if response == gtk.RESPONSE_YES:
269 qubx.GTK.Open("Locate %s" % name, self.__path,
270 lambda pathname: self.__pick_missing(seq, relpaths, altpaths, pathname),
271 filters=[('Python scripts', '.py')],
272 do_on_cancel=lambda: self.__pick_missing(seq, relpaths, altpaths, None),
273 parent=self.parent_window)
274 elif len(relpaths) > 1:
275 self.__try_appends(seq, relpaths[1:], altpaths)
284
302 path = seq.paths[ix]
303 fname = os.path.split(path)[1]
304 mod = qubx.pyenv.ScriptModule(fname, qubx.pyenv.env.globals)
305 mod.OnPlay += self.__ref(self.__onPlayScript)
306 mod.OnStop += self.__ref(self.__onStopScript)
307 view = qubx.pyenvGTK.PythonScriptView(vertical=None, script_module=mod)
308 self.__views.insert(ix, view)
309 try:
310 view.open(path)
311 self.__after_insert(seq, ix, path, view)
312 except:
313 qubx.GTK.Open('The script "%s" could not be opened. Please find it, or Cancel' % path, os.path.split(path)[0],
314 bind_with_args(self.__after_insert, seq, ix, path, view),
315 [('Python Scripts', '.py')],
316 do_on_cancel=bind(self.__after_insert_missing, seq, ix, path, view))
318 view.append_script("# missing file: %s\n# asking for new location...Canceled.\n# will try again next launch." % path)
319 self.__after_insert(seq, ix, path, view)
333 mod = self.__views[ix].exec_script
334 mod.OnPlay -= self.__ref(self.__onPlayScript)
335 mod.OnStop -= self.__ref(self.__onStopScript)
336 del self.__views[ix]
337 self.book.remove_page(ix)
338 n = self.book.get_n_pages()
339 if (n > 1) and (self.book.get_current_page() == (n-1)):
340 self.book.set_current_page(n-2)
346 self.seqs.props.active['Path'].data = self.__path = os.path.split(path)[0]
347 self.__seq.append(path)
348 gobject.idle_add(self.book.set_current_page, self.book.get_n_pages()-2)
350 if self.book.get_n_pages() > 1:
351 self.seq.remove(self.book.get_current_page())
352 - def __onSwitchPage(self, book, page, ix):
353 self.panTabTools.foreach(lambda item: self.panTabTools.remove(item))
354 if ix < len(self.__views):
355 pack_item(self.__views[ix].toolbar, self.panTabTools, expand=True)
356 self.txtScriptName.set_text("%s:" % os.path.split(self.__seq.paths[ix])[1])
357 elif len(self.__views):
358 gobject.idle_add(self.book.set_current_page, len(self.__views)-1)
359 else:
360 self.txtScriptName.set_text('')
361
363 old_num = self.__views.index(child)
364 if page_num == len(self.__views):
365 print 'unreorder',page_num,old_num
366 gobject.idle_add(self.book.reorder_child, child, old_num)
367 if old_num != (len(self.__views)-1):
368 gobject.idle_add(self.book.reorder_child, child, len(self.__views)-1)
369 elif page_num != old_num:
370 print 'real reorder',page_num,old_num
371 self.__views[old_num], self.__views[page_num] = self.__views[page_num], self.__views[old_num]
372 self.__seq.reorder(old_num, page_num)
373 else:
374 print 'neither',page_num,old_num
375
377 if self.__running_all:
378 self.panTabTools.hide()
379 self.book.set_current_page(self.__run_ix)
380 return
381 if not self.__run_count:
382 self.toolbar.set_sensitive(False)
383 self.__run_count += 1
385 if self.__running_all:
386 self.__run_ix += 1
387 if self.__run_ix == len(self.__seq.paths):
388 self.__running_all = False
389 self.btnPlaySeq.set_sensitive(True)
390 self.btnStopSeq.set_sensitive(False)
391 self.panTabTools.show()
392 else:
393 self.__views[self.__run_ix].run()
394 self.__run_count -= 1
395 if not self.__run_count:
396 self.toolbar.set_sensitive(True)
397 self.btnPlaySeq.set_sensitive(True)
398
400 if (not self.__seq) or (not self.__seq.paths):
401 return
402 if not self.__running_all:
403 self.__running_all = True
404 self.__run_ix = 0
405 self.btnStopSeq.set_sensitive(True)
406 self.btnPlaySeq.set_sensitive(False)
407 self.__views[0].run()
408 else:
409 self.__running_all = False
410 self.__run_count = 1
411 self.btnStopSeq.set_sensitive(False)
412 self.__views[self.__run_ix].exec_script.stop()
413