1 """GUI support for user scripting.
2
3 Copyright 2007-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 qubx.pyenv
23 import qubx.util_types
24 from qubx.GTK import *
25 import bisect
26 import inspect
27 import os
28 import pprint
29 import pydoc
30 import webbrowser
31
32 -def show_message(message, buttons=gtk.BUTTONS_OK, title="", parent=None):
36
37 -def prompt_choices(message, choices=['Cancel', 'OK'], title="", parent=None):
41
42 -def prompt_entry(message, value, accept=None, format='%s', title="", parent=None):
43 """Returns modified value, or None if canceled."""
44 p = parent or get_active_window()
45 return PromptEntry(message, value, accept, format, title, p)
46
48 """Returns modified value list, or None if canceled.
49
50 @param items: list of tuple (caption, value, accept, format)
51 """
52 p = parent or get_active_window()
53 return PromptEntries(items, title, p)
54
55
56
58 """A widget showing sys.stdout."""
59
60 __explore_featured = ['txtOutput', 'output', 'event', 'write', 'detach']
61
63 gtk.Frame.__init__(self, 'Output')
64 scr = gtk.ScrolledWindow()
65 self.txtOutput = gtk.TextView()
66 self.txtOutput.set_editable(False)
67 SetFixedWidth(self.txtOutput)
68 scr.add(self.txtOutput)
69 self.txtOutput.show()
70 self.output = TextViewAppender(self.txtOutput)
71 self.event = output_event or qubx.pyenv.env.OnOutput
72 self.event += self.output
73 self.add(scr)
74 scr.show()
75 self.write = qubx.pyenv.env.write
77 """Stops receiving stdout. Call this before discarding a short-lived PythonOut."""
78 self.event -= self.output
79
81 """A widget which lets the user execute a line of python."""
82
83 __explore_featured = ['entry', 'btnRun', 'run']
84
95 if event.keyval == keysyms.Return:
96 self.run()
97 self.entry.select_region(0, -1)
103 - def run(self, cmd=None):
104 """Prints cmd to stdout and runs it."""
105 cmd = cmd or self.entry.get_text()
106 if cmd:
107 self.btnRun.set_sensitive(False)
108 qubx.pyenv.env.write('>>> %s\n' % cmd)
109 try:
110 e_val = eval(cmd, qubx.pyenv.env.globals)
111 if not (e_val is None): print repr(e_val)
112 except:
113 qubx.pyenv.env.exec_cmd(cmd)
114 self.btnRun.set_sensitive(True)
116 self.btnRun.set_sensitive(True)
118 self.btnRun.set_sensitive(False)
119
120
122 """Dialog for editing sys.path extensions and the startup script (see qubx.pyenv.PythonEnvironment)."""
123 - def __init__(self, appName="", parent=None):
136 vsplit = pack_item(gtk.VPaned(), self.vbox, expand=True)
137 v = gtk.VBox()
138 v.show()
139 vsplit.pack1(v, True, True)
140 h = pack_item(gtk.HBox(), v)
141 pack_label('sys.path.append:', h)
142 self.txtPath = pack_item(NumEntry(""), h, expand=True)
143 h = pack_item(gtk.HBox(), v)
144 pack_label('init scripts folder:', h)
145 self.txtInitDir = pack_item(NumEntry(""), h, expand=True)
146
147 msg = pack_item(gtk.TextView(), v)
148 buf = msg.get_buffer()
149 buf.set_text("""The following script is run during startup, and when you press Reload below.""")
150 buf.select_range(buf.get_start_iter(), buf.get_end_iter())
151 pack_hsep(5, v)
152
153 self.txtScript = gtk.TextView()
154 SetFixedWidth(self.txtScript)
155 self.bufScript = self.txtScript.get_buffer()
156 pack_scrolled(self.txtScript, v, size_request=(110, 70))
157
158 v = gtk.VBox()
159 v.show()
160 vsplit.pack2(v, False, True)
161
162 self.txtOut = pack_item(PythonOut(), v, expand=True)
163 self.txtOut.set_size_request(110, 70)
164 self.prompt = pack_item(PythonPrompt(), v)
165
166 self.btnReload = pack_button('Reload', self.action_area, at_end=True, on_click=self._onPressReload)
167 self.add_button('Close', gtk.RESPONSE_ACCEPT)
193
194
195
197 """
198 Widget for editing and running user scripts.
199
200 @ivar clean: True if the script has not been modified
201 @ivar script: Contents of the script pane (string)
202 """
203
204 __explore_featured = ['exec_script', 'btnRec', 'btnPlay', 'btnPause', 'btnStep', 'btnStop', 'btnSave', 'btnSaveAs', 'btnOpen', 'btnNew',
205 'bufScript', 'path', 'fname', 'clean', 'toolbar', 'have_controls', 'txtName', 'txtPath', 'btnEnv', 'mnuExplore',
206 'append_script', 'set_clean', 'script', 'set_file', 'promptSave', 'saveAs', 'open', 'check_modified_outside', 'run']
207
208 - def __init__(self, vertical=True, script_module=None):
209
210 gtk.VBox.__init__(self)
211 self.__ref = Reffer()
212 self._build_ui(vertical)
213 self.exec_script = qubx.pyenv.env.exec_script if (script_module is None) else script_module
214
215 self.exec_script.OnPlay += self.__ref(self.__onPlayScript)
216 self.exec_script.OnPause += self.__ref(self.__onPauseScript)
217 self.exec_script.OnResume += self.__ref(self.__onResumeScript)
218 self.exec_script.OnStop += self.__ref(self.__onStopScript)
219
220 self.btnRec.connect('toggled', self._onToggleRec)
221 self.btnPlay.connect('clicked', self._onClickPlay)
222 self.btnPause.connect('clicked', self._onClickPause)
223 self.btnStep.connect('clicked', self._onClickStep)
224 self.btnStop.connect('clicked', self._onClickStop)
225 self.btnSave.connect('clicked', self._onClickSave)
226 if not (vertical is None):
227 self.btnSaveAs.connect('clicked', self._onClickSaveAs)
228 self.btnOpen.connect('clicked', self._onClickOpen)
229 self.btnNew.connect('clicked', self._onClickNew)
230 self.bufScript.connect('changed', self._onScriptChanged)
231
232 self.path, self.fname = '', ''
233 try:
234 self.path = env.globals['documents_path']
235 except:
236 pass
237 self.clean = True
238
240 self.toolbar = gtk.Toolbar()
241 self.toolbar.set_size_request(250, -1)
242 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
243 self.toolbar.set_tooltips(True)
244 self.toolbar.show()
245 self.btnRec = gtk.ToggleToolButton(gtk.STOCK_MEDIA_RECORD)
246 self.btnRec.show()
247 self.toolbar.insert(self.btnRec, -1)
248 self.btnStop = gtk.ToolButton(gtk.STOCK_MEDIA_STOP)
249 self.btnStop.show()
250 self.toolbar.insert(self.btnStop, -1)
251 self.btnPause = gtk.ToolButton(gtk.STOCK_MEDIA_PAUSE)
252
253 self.toolbar.insert(self.btnPause, -1)
254 self.btnStep = gtk.ToolButton(gtk.STOCK_MEDIA_NEXT)
255 self.btnStep.show()
256 self.toolbar.insert(self.btnStep, -1)
257 self.btnPlay = gtk.ToolButton(gtk.STOCK_MEDIA_PLAY)
258 self.btnPlay.show()
259 self.toolbar.insert(self.btnPlay, -1)
260
261 vsv = gtk.VBox()
262 vsv.show()
263 self.have_controls = not (vertical is None)
264 if self.have_controls:
265 hsplit = pack_item(gtk.HPaned(), self)
266 hsplit.pack1(self.toolbar, False, False)
267 sep = gtk.SeparatorToolItem()
268 sep.show()
269 self.toolbar.insert(sep, -1)
270 self.btnNew = gtk.ToolButton(gtk.STOCK_NEW)
271 self.btnNew.show()
272 self.toolbar.insert(self.btnNew, -1)
273 self.btnOpen = gtk.ToolButton(gtk.STOCK_OPEN)
274 self.btnOpen.show()
275 self.toolbar.insert(self.btnOpen, -1)
276 self.btnSave = gtk.ToolButton(gtk.STOCK_SAVE)
277 self.btnSave.show()
278 self.toolbar.insert(self.btnSave, -1)
279 self.btnSaveAs = gtk.ToolButton(gtk.STOCK_SAVE_AS)
280 self.btnSaveAs.show()
281 self.toolbar.insert(self.btnSaveAs, -1)
282
283 vh = gtk.HBox()
284 vh.show()
285 hsplit.pack2(vh, True, True)
286 self.txtName = pack_item(gtk.Entry(), vh, expand=True)
287 self.txtName.set_editable(False)
288 self.txtName.set_width_chars(15)
289 pack_label('in', vh)
290 self.txtPath = pack_item(gtk.Entry(), vh, expand=True)
291 self.txtPath.set_width_chars(12)
292 self.txtPath.set_editable(False)
293 self.btnEnv = pack_button('Environment...', vh, on_click=self._onClickEnv)
294 self.btnEnv.set_tooltip_text('Add folders to the Python search path')
295 self.mnuExplore = pack_item(qubx.GTK.StaticComboList(['Explore...', 'Objects', 'Modules']), vh)
296 self.mnuExplore.OnChange += self.__ref(self.__onChooseExplore)
297 self.mnuExplore.set_tooltip_text("Inspect the running hierarchy of objects and source modules")
298 vsplit = pack_item((vertical and gtk.VPaned or gtk.HPaned)(), self, expand=True)
299 vsplit.pack1(vsv, True, True)
300
301 vsv2 = gtk.VBox()
302 vsv2.show()
303 vsplit.pack2(vsv2, False, True)
304 self.txtOut = pack_item(PythonOut(), vsv2, expand=True)
305 self.txtOut.set_size_request(100, 70)
306 self.prompt = pack_item(PythonPrompt(), vsv2)
307 else:
308 pack_item(vsv, self, expand=True)
309 self.btnSave = gtk.ToolButton(gtk.STOCK_SAVE)
310 self.btnSave.show()
311 self.toolbar.insert(self.btnSave, -1)
312 self.btnNew = self.btnOpen = self.btnSaveAs = self.txtName = self.txtPath = self.btnEnv = self.btnExplore = self.btnModules = None
313
314 self.txtScript = gtk.TextView()
315 SetFixedWidth(self.txtScript)
316 self.bufScript = self.txtScript.get_buffer()
317 self.append_script = qubx.GTK.TextViewAppender(self.txtScript)
318 pack_scrolled(self.txtScript, vsv, expand=True, size_request=(100, 70))
320 self._clean = clean
321 if self.have_controls:
322 self.btnSave.set_sensitive(not clean)
323 clean = property(lambda s: s._clean, set_clean)
325 buf = self.bufScript
326 return buf.get_text(buf.get_start_iter(), buf.get_end_iter())
327 script = property(get_script)
339 """If there are changes, ask to save them.
340 @return: True unless there are changes and the user says Cancel."""
341 if not self.clean:
342 dlg = gtk.MessageDialog(None, buttons=gtk.BUTTONS_NONE,
343 flags=gtk.DIALOG_MODAL,
344 message_format='Save changes to python script %s?' % self.fname)
345 dlg.add_buttons('Yes',gtk.RESPONSE_YES,
346 'No',gtk.RESPONSE_NO,
347 'Cancel',gtk.RESPONSE_CANCEL)
348 response = dlg.run()
349 dlg.destroy()
350 if response == gtk.RESPONSE_YES:
351 return self.saveAs(self.path, self.fname)
352 elif response == gtk.RESPONSE_NO:
353 return True
354 elif response == gtk.RESPONSE_CANCEL:
355 return False
356 else:
357 return True
358 - def saveAs(self, _path=None, _fname=None):
359 """Saves script to _fname in folder _path. If None specified, prompts the user for a location."""
360 if _path and _fname:
361 path, fname = _path, _fname
362 else:
363 return bool(qubx.GTK.SaveAs('Save script as...', _path or self.path, _fname or self.fname,
364 lambda pathname: self.saveAs(*os.path.split(pathname)),
365 filters=[('Python scripts', '.py')]))
366 open(os.path.join(path, fname), 'w').write(self.script)
367 self.set_file(path, fname)
368 self.clean = True
369 return True
378 - def open(self, fname, promptSave=False):
390 if self.exec_script.paused:
391 self.exec_script.resume()
392 else:
393 self.run()
406 self.exec_script.pause()
407 if self.exec_script:
408 self.exec_script.step()
409 else:
410 self.run()
424 self.append_script("%s\n"%expr)
426 if not (self.fstamp is None):
427 st_mtime = os.stat(os.path.join(self.path, self.fname))[8]
428 if st_mtime > self.fstamp:
429 choice = prompt_choices("The script was modified by another program.\nRe-read it from disk?",
430 ["Cancel", "Re-read file", "Use onscreen version"],
431 "Script modified")
432 if choice == 0:
433 return False
434 if choice == 1:
435 self.open(os.path.join(self.path, self.fname))
436 return True
462 module.OnLine += self.__ref(self.__onLine)
463 self.btnPlay.set_sensitive(False)
464 self.btnStop.set_sensitive(True)
465 self.btnRec.set_sensitive(False)
466 self.btnPause.show()
467 self.btnStep.hide()
469 self.btnPlay.set_sensitive(True)
470 self.btnPause.hide()
471 self.btnStep.show()
473 self.btnPlay.set_sensitive(False)
474 self.btnPause.show()
475 self.btnStep.hide()
477 module.OnLine -= self.__ref(self.__onLine)
478 self.btnPlay.set_sensitive(True)
479 self.btnStop.set_sensitive(False)
480 self.btnRec.set_sensitive(True)
481 self.btnPause.hide()
482 self.btnStep.show()
483 - def __onLine(self, module, lineno, line):
484 buf = self.bufScript
485 buf.select_range(buf.get_iter_at_line_index(lineno-1, 0), buf.get_iter_at_line_index(lineno, 0))
486
487
488
490 """
491 Window for editing and running user scripts.
492
493 @ivar clean: True if the script has not been modified
494 @ivar script: Contents of the script pane (string)
495 """
497 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
498 appTitle = appName and (appName+" - ")
499 self.set_title(appTitle+'Python Scripts')
500 self.connect('delete_event', self._onDelete)
501
502 self.view = PythonScriptView()
503 self.add(self.view)
504 self.view.show()
505 clean = property(lambda s: s._clean, lambda s,x: s.view.set_clean(x))
506 script = property(lambda s: s.view.get_script())
514 try:
515 if self.view.promptSave():
516 self.hide()
517 except:
518 traceback.print_exc()
519 return True
523 """
524 If there are changes, ask to save them.
525 @return: True unless there are changes and the user says Cancel."""
526 return self.view.promptSave()
527
528
529
531 """Dialog to help write and debug one-liners."""
532 - def __init__(self, accept, caption, label, help_msg, bind, write_test, parent=None):
533 """
534 @param accept: a func(string) -> val; such as qubx.accept.acceptF
535 @param caption: top line of text explaining what's wanted
536 @param label: e.g. "f(x) = "
537 @param help_msg: a multiline explanation of what's wanted, with examples
538 @param bind: a func(val) that puts val into the global dict;
539 e.g. qubx.pyenv.env.globals['f'] = val
540 @param write_test: a func(val) -> expr; where expr is a one-liner that tests the global binding;
541 e.g. 'f(1.0)'
542 """
543 gtk.Dialog.__init__(self, 'Python Expression Help', parent or get_active_window(), gtk.DIALOG_MODAL)
544 self.expr = ""
545 self.accept = accept
546 self.caption = caption
547 self.label = label
548 self.help_msg = help_msg
549 self.bind = bind
550 self.write_test = write_test
551 self._args = []
552 self._build_ui()
553
554 self.ref = Reffer()
555 self.txtF.OnChange += self.ref(self._onChangeF)
556 self.txtF.OnReject += self.ref(self._onRejectF)
557 self.txtF.OnEdit += self.ref(self._onEditF)
558 self.txtF.OnReset += self.ref(self._onResetF)
559
560 buf = self.txtAbout.get_buffer()
561 buf.insert(buf.get_end_iter(), help_msg)
562
564 h = pack_item(gtk.HBox(False), self.vbox, expand=True)
565 hv = pack_item(gtk.VBox(False), h, expand=True)
566 self.heading = pack_label(self.caption, hv)
567 hvh = pack_item(gtk.HBox(False), hv)
568 pack_label(self.label, hvh)
569 self.txtF = pack_item(NumEntry(None, self.accept), hvh, expand=True)
570 self.txtOut = pack_item(PythonOut(), hv, expand=True)
571 self.txtOut.set_size_request(300, 200)
572 self.prompt = pack_item(PythonPrompt(), hv)
573 self.txtAbout = gtk.TextView()
574 self.txtAbout.set_editable(False)
575 self.txtAbout.set_wrap_mode(gtk.WRAP_WORD)
576 pack_scrolled(self.txtAbout, h, size_request=(300, 300))
577
578 self.btnCancel = self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)
579 self.btnOK = self.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
580
581 - def run(self, expr):
582 """
583 Presents expr for testing and editing.
584 If the return value is gtk.RESPONSE_ACCEPT, self.expr has the edited text."""
585 self.expr = expr
586 try:
587 self.txtF.setValue(self.accept(expr), expr)
588 self._onChangeF(self.txtF, self.txtF.value)
589 except Exception, e:
590 self._onRejectF(self.txtF, expr, e)
591 self.txtF.set_text(expr)
592 try:
593 self.show()
594 response = gtk.Dialog.run(self)
595 except:
596 traceback.print_exc()
597 response = gtk.RESPONSE_REJECT
598 return response
603 self.btnOK.set_sensitive(False)
605 self.btnOK.set_sensitive(False)
606 traceback.print_exc()
618 self.btnOK.set_sensitive(True)
619
652
653 ALTMAP_KEYS = "abcdefghijklmnopqrstuvwxyz1234567890-=`[]\;',./"
654
656 __explore_featured = ['vbox', 'altmap', 'entries']
658 gtk.ScrolledWindow.__init__(self)
659 self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
660 self.__ref = Reffer()
661 self.vbox = gtk.VBox()
662 self.vbox.show()
663 self.add_with_viewport(self.vbox)
664 pack_label('Each %s-combo can execute a Python expression or script file:'%qubx.pyenv.env.global_modifier, self.vbox)
665 self.altmap = qubx.pyenv.env.altmap
666 qubx.pyenv.env.OnSetAlt += self.__ref(self.__onSetAlt)
667 self.entries = {}
668 modname = qubx.pyenv.env.global_modifier
669 for k in ALTMAP_KEYS:
670 h = pack_item(gtk.HBox(), self.vbox)
671 pack_label('%s-%s: ' % (modname, k), h)
672 entry = pack_item(NumEntry(self.altmap[k]), h, expand=True)
673 entry.OnChange += self.__ref(qubx.util_types.bind_with_args(self.__onChangeEntry, k))
674 self.entries[k] = entry
675 entry.btnChoose = pack_button('...', h, on_click=qubx.util_types.bind(self.__onClickChoose, k))
676 self.__path = ''
679 - def __onChangeEntry(self, k, entry, val):
688
689
691 __explore_featured = ['show_private', 'omit_toplevel', 'get_selection', 'OnSelect', 'store', 'col_name', 'cell_name', 'obj',
692 'obj_name', 'populate', 'path_to_names', 'get_name_map', 'get_name_map_all', 'get_full_name', 'get_sub_obj']
693 - def __init__(self, show_private=False, omit_toplevel=False, mark_instances=True):
694 gtk.TreeView.__init__(self)
695
696 self.set_size_request(250, 250)
697 self.__ref = Reffer()
698 self.show_private = show_private
699 self.omit_toplevel = omit_toplevel
700 self.set_headers_visible(False)
701 self.connect('row_expanded', self.__onRowExpanded)
702 self.get_selection().connect('changed', self.__onSelectionChanged)
703 self.OnSelect = WeakEvent()
704
705 self.store = gtk.TreeStore(str, str, object)
706 self.set_model(self.store)
707 self.col_name = gtk.TreeViewColumn('')
708 self.append_column(self.col_name)
709 self.cell_name = gtk.CellRendererText()
710 self.col_name.pack_start(self.cell_name, True)
711 self.col_name.add_attribute(self.cell_name, 'text', 0)
712
713 self.obj = None
714 - def explore(self, obj, global_name, *sels):
723 - def populate(self, it, obj, objname, sels, final=False):
724 nmap, build_name = self.get_name_map(obj, objname)
725
726 names = sorted(nmap.keys())
727
728 instances_ix = bisect.bisect_left(names, 'INSTANCES')
729 bases_ix = bisect.bisect_left(names, 'INSTANCES_AS_BASE')
730 if (not (0 <= instances_ix < len(names))) or (names[instances_ix] != 'INSTANCES'):
731 instances_ix = -1
732 if (not (0 <= bases_ix < len(names))) or (names[bases_ix] != 'INSTANCES_AS_BASE'):
733 bases_ix = -1
734 for ix in reversed(sorted([instances_ix, bases_ix])):
735 if ix >= 0:
736 del names[ix]
737 if bases_ix >= 0:
738 names.insert(0, 'INSTANCES_AS_BASE')
739 if instances_ix >= 0:
740 names.insert(0, 'INSTANCES')
741
742 path = self.store.get_path(it)
743 selected = None
744 for name in names:
745 fullname = build_name(name)
746 decorated_name = name
747 try:
748 if qubx.pyenv.is_functional_class(str(nmap[name].__class__)):
749 decorated_name = '%s()' % name
750 except AttributeError:
751 pass
752 it1 = self.store.append(it, (decorated_name, fullname, nmap[name]))
753 if sels and (name == sels[0]):
754 gobject.idle_add(self.expand_row, self.store.get_path(it1), False)
755 cdr = sels[1:] if (len(sels) > 1) else []
756 selected = self.populate(it1, nmap[name], fullname, cdr) or it1
757 elif not final:
758 self.populate(it1, nmap[name], fullname, [], final=True)
759 return selected
766
767
768
769 it1 = self.store.iter_children(it)
770 while it1:
771 if self.store.iter_children(it1):
772 return
773 it1 = self.store.iter_next(it1)
774
775
776 row = self.store[self.store.get_path(it)]
777 nmap, build_name = self.get_name_map(row[2], row[1])
778 it1 = self.store.iter_children(it)
779 while it1:
780 name = self.store[self.store.get_path(it1)][0]
781 if (len(name) > 2) and (name[-2:] == '()'):
782 name = name[:-2]
783 fullname = build_name(name)
784 self.populate(it1, nmap[name], fullname, [], final=True)
785 it1 = self.store.iter_next(it1)
786
788 return [self.store[path[:i+1]][0] for i in xrange(1, len(path))]
790 if isinstance(obj, dict):
791 return dict((isinstance(k,str) and (k,v) or (str(k),v)) for k,v in obj.iteritems()), lambda name: "%s[%s]" % (objname, repr(name))
792 if isinstance(obj, list) or isinstance(obj, tuple):
793 return ExploreListSlices(obj, objname)
794 if isinstance(obj, ExploreListSlice):
795 return obj.name_map, obj.build_name
796 try:
797 c = obj.__class__
798 if (c == int) or (c == float) or (c == long) or (c == bool) or (c == str):
799 return {}, lambda name: name
800 cs = str(c)
801 if qubx.pyenv.is_functional_class(cs):
802 return {}, lambda name: name
803 if 'GProps' in cs:
804 return {}, lambda name: name
805 except AttributeError:
806 return {}, lambda name: name
807 feat = qubx.pyenv.get_name_map_featured(obj, objname) if not self.show_private else {}
808 if feat:
809 return feat, build_dotname(objname)
810 else:
811 return self.get_name_map_all(obj, objname)
813 try:
814 if self.show_private:
815
816 return (dict((k,v) for (k,v) in inspect.getmembers(obj) if 0 != k.find("__")), build_dotname(objname))
817 else:
818
819 return (dict((k,v) for (k,v) in inspect.getmembers(obj) if -1 == k.find("__")), build_dotname(objname))
820 except:
821 try:
822 if self.show_private:
823 return (obj.__dict__, build_dotname(objname))
824 else:
825 return (dict((k,v) for (k,v) in obj.__dict__.iteritems() if -1 == k.find("__")), build_dotname(objname))
826 except:
827 return {}, lambda name: name
829 if path is None:
830 store, it = self.get_selection().get_selected()
831 if it is None:
832 return self.obj_name
833 return self.get_full_name(store.get_path(it))
834 return self.store[path][1]
836 return self.store[path][2]
837
839 if objname:
840 def build(name):
841 return "%s.%s" % (objname, name)
842 else:
843 def build(name):
844 return name
845 return build
846
848 - def __init__(self, obj, objname, base, count):
849 self.obj, self.objname, self.base, self.count = obj, objname, base, count
850 self.name_map, self.build_name = ExploreListSlices(obj, objname, base, count)
853
855 count = len(obj) if (count is None) else count
856 if count > 10:
857 skip = 10
858 while (10*skip) < count:
859 skip *= 10
860 return (dict(('[%i-%i]'%(i,min(len(obj)-1, i+skip-1)), ExploreListSlice(obj, objname, i, min(skip, base+count-i))) for i in xrange(base, base+count, skip)),
861 lambda name: objname)
862 else:
863 return (dict(('[%i]'%i, obj[i]) for i in xrange(base, base+count)),
864 lambda name: "%s%s" % (objname, name))
865
866
867
868 EXPLORE_STR_NONE, EXPLORE_STR_STR, EXPLORE_STR_PPRINT, EXPLORE_STR_DOC, EXPLORE_STR_CUSTOM = range(5)
869
871 __explore_featured = ['txtStr', 'txtFullname', 'txtRepr', 'btnViewAPI', 'btnEditTree', 'txtCustom', 'obj', 'fullname', 'explore_str']
872 - def __init__(self, appname, parent=None):
873 gtk.VBox.__init__(self)
874 self.set_size_request(600, 300)
875 self.__ref = Reffer()
876 self.__obj = None
877 self.__fullname = ""
878 self.__explore_str = EXPLORE_STR_PPRINT
879 self.__appname = appname
880 self.__parent = parent or get_active_window()
881 self.txtStr = gtk.TextView()
882 SetFixedWidth(self.txtStr)
883
884 self.txtFullname = pack_item(gtk.Entry(), self)
885 self.txtFullname.set_editable(False)
886 self.txtRepr = pack_item(gtk.Entry(), self)
887 self.txtRepr.set_editable(False)
888 h = pack_item(gtk.HBox(), self)
889 pack_label('Details:', h)
890 hv = pack_item(gtk.VBox(), h, expand=True)
891 hvh = pack_item(gtk.HBox(), hv)
892 group = pack_radio('None', hvh, on_toggle=bind(self.click_explore_str, EXPLORE_STR_NONE))
893 self.radExplore = [group]
894 self.radExplore.append(pack_radio('print (__str__)', hvh, group, on_toggle=bind(self.click_explore_str, EXPLORE_STR_STR)))
895 self.radExplore.append(pack_radio('pretty (pprint)', hvh, group, active=True, on_toggle=bind(self.click_explore_str, EXPLORE_STR_PPRINT)))
896 self.radExplore.append(pack_radio('help (__doc__)', hvh, group, on_toggle=bind(self.click_explore_str, EXPLORE_STR_DOC)))
897 self.btnViewAPI = pack_button('View in API...', hvh, self.__onClickViewAPI, at_end=True)
898 self.btnViewAPI.set_tooltip_text('Show the class documentation in a web browser')
899 self.btnEditTree = pack_button('Edit QUB Tree...', hvh, self.__onClickEditTree, at_end=True)
900 hvh = pack_item(gtk.HBox(), hv)
901 self.radExplore.append(pack_radio('custom (lambda obj:)', hvh, group, on_toggle=bind(self.click_explore_str, EXPLORE_STR_CUSTOM)))
902 self.__explore_str = EXPLORE_STR_PPRINT
903 self.txtCustom = pack_item(qubx.GTK.NumEntry(""), hvh, expand=True)
904 self.txtCustom.OnChange += self.__ref(self.__onChangeCustom)
905 pack_scrolled(self.txtStr, self, expand=True)
906 obj = property(lambda self: self.__obj, lambda self, x: self.set_obj(x))
907 fullname = property(lambda self: self.__fullname, lambda self, x: self.set_fullname(x))
908 explore_str = property(lambda self: self.__explore_str, lambda self, x: self.set_explore_str(x))
909 - def set_obj(self, obj, name="", parent=None):
923 if (len(x) > 2) and (x[-2:] == '()'):
924 self.__fullname = x[:-2]
925 else:
926 self.__fullname = x
927 self.txtFullname.set_text(self.__fullname)
962 self.__clicking_explore = True
963 self.set_explore_str(x)
964 self.__clicking_explore = False
973 webbrowser.open(self.__obj_api)
974
975
977 """Modal window to explore python namespace."""
978
979 __explore_featured = ['treeview', 'selection_names', 'objview', 'run']
980
981 - def __init__(self, appname, parent=None, show_private=False, omit_toplevel=False, explore_str=None):
982 gtk.Dialog.__init__(self, '%s - Explore' % appname, parent or get_active_window(), gtk.DIALOG_MODAL)
983 self.resize(640, 480)
984 self.__ref = Reffer()
985 self.treeview = ExploreTreeView(show_private, omit_toplevel)
986 split = pack_item(gtk.HPaned(), self.vbox, expand=True)
987 left = gtk.VBox()
988 left.show()
989 split.pack1(left, True, True)
990 pack_scrolled(self.treeview, left, expand=True)
991 self.treeview.OnSelect += self.__ref(self.__onSelect)
992 self.selection_names = []
993 self.objview = ExploreObjView(appname, parent or get_active_window())
994 self.objview.show()
995 if not (explore_str is None):
996 self.objview.explore_str = explore_str
997 split.pack2(self.objview, True, True)
998 - def run(self, obj, global_name, *sels):
1015
1016
1018 """Shows the Python object hierarchy under one object.
1019
1020 @param obj: anything
1021 @param name: caption for obj
1022 @param selection_names: path that will be expanded initially
1023 @param show_private: (default False) True to show variables with __ in their name
1024
1025 @return: selection_names of last selected sub-object
1026 """
1027 dlg = ExploreDialog(qubx.global_namespace.QubX.appname, qubx.global_namespace.QubX, **kw)
1028 try:
1029 dlg.run(obj, name, *selection_names)
1030 except:
1031 traceback.print_exc()
1032 result = dlg.selection_names
1033 dlg.destroy()
1034 return result
1035
1036 -def explore(*selection_names, **kw):
1037 """Shows the Python object hierarchy in qubx.global_namespace
1038
1039 @param selection_names: path that will be expanded initially
1040 @param show_private: (default False) True to show variables with __ in their name
1041
1042 @return: selection_names of last selected object e.g. ['QubX', 'Models']
1043 """
1044 dlg = ExploreDialog(qubx.global_namespace.QubX.appname, parent=None, omit_toplevel=True, **kw)
1045 qubx.pyenv.env.globals['explorer'] = dlg
1046 try:
1047 dlg.run(qubx.global_namespace, 'qubx.global_namespace', *(selection_names or ['QubX']))
1048 except:
1049 traceback.print_exc()
1050 result = dlg.selection_names
1051 del qubx.pyenv.env.globals['explorer']
1052 dlg.destroy()
1053 return result
1054
1055 qubx.global_namespace.explore_one = explore_one
1056 qubx.global_namespace.explore = explore
1057
1058
1059
1061 r = repr(obj)
1062 class_pat = re.compile(r"<class '(qub.*)\.(.*)'>")
1063 class_template = "http://www.qub.buffalo.edu/src/qub-express/api/%s.%s-class.html#%s"
1064 match = class_pat.match(r)
1065 if match:
1066 return class_template % (match.group(1), match.group(2), name)
1067 module_pat = re.compile(r"<module '(qub[^']*)'")
1068 module_template = "http://www.qub.buffalo.edu/src/qub-express/api/%s-module.html#%s"
1069 match = module_pat.match(r)
1070 if match:
1071 return module_template % (match.group(1), "")
1072 try:
1073 match = class_pat.match(repr(obj.__class__))
1074 if match:
1075 return class_template % (match.group(1), match.group(2), "")
1076 if parent:
1077 match = class_pat.match(repr(parent.__class__))
1078 if match:
1079 return class_template % (match.group(1), match.group(2), name)
1080 except:
1081 pass
1082 try:
1083 if 0 == obj.__module__.index('qub'):
1084 return module_template % (obj.__module__, name)
1085 except:
1086 pass
1087 return ""
1088
1089
1090
1092 __explore_featured = ['module', 'link_ref', 'clear', 'write']
1105 - def write(self, *args):
1106 """Appends text (string arguments) and links (tuple arguments: (text_string, script_string) or (text_string, callable)."""
1107 for arg in args:
1108 if (isinstance(arg, tuple) or isinstance(arg, list)) and (len(arg) == 2):
1109 if callable(arg[1]):
1110 qubx.GTK.HyperTextView2.write(self, self.link_ref(arg[1]), arg[0])
1111 else:
1112 qubx.GTK.HyperTextView2.write(self, self.link_ref(bind(self.module, arg[1])), arg[0])
1113 else:
1114 qubx.GTK.HyperTextView2.write(self, arg)
1115 - def __onExc(self, module, typ, val, tb):
1116 print 'In module %s:' % module.filename
1117 traceback.print_exception(typ, val, tb)
1118