Package qubx :: Module pyenvGTK
[hide private]
[frames] | no frames]

Source Code for Module qubx.pyenvGTK

   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):
33 """Displays a message dialog box.""" 34 p = parent or get_active_window() 35 return ShowMessage(message, buttons, title, p)
36
37 -def prompt_choices(message, choices=['Cancel', 'OK'], title="", parent=None):
38 """Returns index of choice; 0 is also window-close.""" 39 p = parent or get_active_window() 40 return PromptChoices(message, choices, title, p)
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
47 -def prompt_entries(items, title="", parent=None):
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
57 -class PythonOut(gtk.Frame):
58 """A widget showing sys.stdout.""" 59 60 __explore_featured = ['txtOutput', 'output', 'event', 'write', 'detach'] 61
62 - def __init__(self, output_event=None):
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 # backwards-compatibility
76 - def detach(self):
77 """Stops receiving stdout. Call this before discarding a short-lived PythonOut.""" 78 self.event -= self.output
79
80 -class PythonPrompt(gtk.HBox):
81 """A widget which lets the user execute a line of python.""" 82 83 __explore_featured = ['entry', 'btnRun', 'run'] 84
85 - def __init__(self):
86 gtk.HBox.__init__(self) 87 self.__ref = qubx.util_types.Reffer() 88 pack_label('>>> ', self) 89 self.entry = pack_item(gtk.Entry(), self, expand=True) 90 self.entry.connect('key_press_event', self._onCmdKey) 91 self.btnRun = pack_button('Run', self, on_click=self._onPressRun) 92 qubx.pyenv.env.exec_cmd.OnPause += self.__ref(self.__onPause) 93 qubx.pyenv.env.exec_cmd.OnResume += self.__ref(self.__onResume)
94 - def _onCmdKey(self, txt, event):
95 if event.keyval == keysyms.Return: 96 self.run() 97 self.entry.select_region(0, -1)
98 - def _onPressRun(self, btn):
99 if qubx.pyenv.env.exec_cmd.paused: 100 qubx.pyenv.env.exec_cmd.resume() 101 else: 102 self.run()
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)
115 - def __onPause(self, exec_cmd):
116 self.btnRun.set_sensitive(True)
117 - def __onResume(self, exec_cmd):
118 self.btnRun.set_sensitive(False)
119 120
121 -class PythonEnvironmentDialog(gtk.Dialog):
122 """Dialog for editing sys.path extensions and the startup script (see qubx.pyenv.PythonEnvironment)."""
123 - def __init__(self, appName="", parent=None):
124 """ 125 @param appName: top-level application name, for the title bar 126 @param parent: parent gtk.Window, for centering the dialog 127 """ 128 appTitle = appName and (appName+" - ") 129 gtk.Dialog.__init__(self, appTitle+'Python Environment', parent or get_active_window(), gtk.DIALOG_MODAL) 130 self._build_ui() 131 self.ref = Reffer() 132 self.txtPath.OnChange += self.ref(self._onChangePath) 133 self.txtInitDir.OnChange += self.ref(self._onChangeInitDir) 134 self.btnReload.connect('clicked', self._onPressReload)
135 - def _build_ui(self):
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)
168 - def run(self):
169 self.txtPath.value = ';'.join(qubx.pyenv.env.path) 170 self.txtInitDir.value = qubx.pyenv.env.init_script_dir 171 buf = self.bufScript 172 buf.set_text(qubx.pyenv.env.script) 173 response = gtk.Dialog.run(self) 174 qubx.pyenv.env.script = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) 175 qubx.pyenv.env.save() 176 return response
177 - def destroy(self):
178 qubx.pyenv.env.save() 179 self.txtOut.detach() 180 gtk.Dialog.destroy(self)
181 - def _onChangePath(self, txt, val):
182 try: 183 qubx.pyenv.env.path = re.split(" *; *", val) 184 except: 185 traceback.print_exc()
186 - def _onChangeInitDir(self, txt, val):
187 qubx.pyenv.env.init_script_dir = val
188 - def _onPressReload(self, btn):
189 buf = self.bufScript 190 qubx.pyenv.env.script = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) 191 qubx.pyenv.env.save() 192 qubx.pyenv.env.reload()
193 194 195
196 -class PythonScriptView(gtk.VBox):
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 # vertical=None: no controls or output pane or >>> prompt 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
239 - def _build_ui(self, vertical):
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 # self.btnPause.show() 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))
319 - def set_clean(self, clean):
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)
324 - def get_script(self):
325 buf = self.bufScript 326 return buf.get_text(buf.get_start_iter(), buf.get_end_iter())
327 script = property(get_script)
328 - def set_file(self, path, fname):
329 """Sets the folder and file name. Doesn't open or save.""" 330 self.path = os.path.abspath(path) 331 self.fname = fname 332 self.fstamp = os.stat(os.path.join(path, fname))[8] if path else None # st_mtime 333 if self.have_controls: 334 self.txtPath.set_text(self.path) 335 self.txtName.set_text(fname)
336 - def _onScriptChanged(self, buf):
337 self.clean = False
338 - def promptSave(self):
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
370 - def _onClickNew(self, item):
371 if self.promptSave(): 372 self.bufScript.set_text("") 373 self.set_file('', '') 374 gobject.idle_add(self.set_clean, True)
375 - def _onClickOpen(self, item):
376 if self.promptSave(): 377 qubx.GTK.Open('Open script...', self.path, self.open, [('Python scripts', '.py')])
378 - def open(self, fname, promptSave=False):
379 if promptSave and not self.promptSave(): 380 return 381 path, name = os.path.split(fname) 382 self.bufScript.set_text(open(fname, 'r').read()) 383 self.set_file(path, name) 384 self.clean = True
385 - def _onClickSave(self, item):
386 self.saveAs(self.path, self.fname)
387 - def _onClickSaveAs(self, item):
388 self.saveAs()
389 - def _onClickPlay(self, item):
390 if self.exec_script.paused: 391 self.exec_script.resume() 392 else: 393 self.run()
394 - def _onClickStop(self, item):
395 if self.btnRec.get_active(): 396 self.btnRec.set_active(False) 397 #self._onToggleRec(self.btnRec) 398 else: 399 self.exec_script.stop()
400 - def _onClickPause(self, item):
401 if self.btnRec.get_active(): 402 qubx.pyenv.env.OnScriptable('QubX.pause()') 403 else: 404 self.exec_script.pause()
405 - def _onClickStep(self, item):
406 self.exec_script.pause() 407 if self.exec_script: 408 self.exec_script.step() 409 else: 410 self.run()
411 - def _onToggleRec(self, item):
412 recording = item.get_active() 413 self.btnStop.set_sensitive(recording) 414 if recording: 415 self.btnStep.hide() 416 self.btnPause.show() 417 qubx.pyenv.env.OnScriptable += self.__ref(self.__onScriptable) 418 self.append_script("# Recorded by %s %s\n" % (qubx.pyenv.env.globals['QubX'].appname, qubx.pyenv.env.globals['QUBX_VERSION'])) 419 else: 420 self.btnStep.show() 421 self.btnPause.hide() 422 qubx.pyenv.env.OnScriptable -= self.__ref(self.__onScriptable)
423 - def __onScriptable(self, expr):
424 self.append_script("%s\n"%expr)
425 - def check_modified_outside(self):
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
437 - def run(self):
438 if not self.check_modified_outside(): 439 return 440 if self.btnRec.get_active(): 441 self.btnRec.set_active(False) 442 #self._onToggleRec(self.btnRec) 443 if self.exec_script.paused: 444 self.exec_script.resume() 445 else: 446 self.exec_script(self.script) 447 qubx.pyenv.env.write('Done.\n')
448 - def _onClickEnv(self, btn):
449 d = PythonEnvironmentDialog() 450 d.run() 451 d.destroy()
452 - def __onChooseExplore(self, mnu, active_text):
453 if self.mnuExplore.active_i == 1: 454 explore('QubX') 455 elif self.mnuExplore.active_i == 2: 456 qubx.pyenv.mark_instances() 457 explore('qubx', explore_str=EXPLORE_STR_DOC) 458 else: 459 return 460 self.mnuExplore.active_i = 0
461 - def __onPlayScript(self, module):
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()
468 - def __onPauseScript(self, module):
469 self.btnPlay.set_sensitive(True) 470 self.btnPause.hide() 471 self.btnStep.show()
472 - def __onResumeScript(self, module):
473 self.btnPlay.set_sensitive(False) 474 self.btnPause.show() 475 self.btnStep.hide()
476 - def __onStopScript(self, module):
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
489 -class PythonScriptWindow(gtk.Window):
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 """
496 - def __init__(self, appName=""):
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())
507 - def set_file(self, path, fname):
508 """Sets the folder and file name. Doesn't open or save.""" 509 self.path = os.path.abspath(path) 510 self.fname = fname 511 self.txtPath.set_text(self.path) 512 self.txtName.set_text(fname)
513 - def _onDelete(self, w, evt):
514 try: 515 if self.view.promptSave(): 516 self.hide() 517 except: 518 traceback.print_exc() 519 return True # we have handled it
520 - def _onScriptChanged(self, buf):
521 self.clean = False
522 - def promptSave(self):
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
530 -class HelpTestDialog(gtk.Dialog):
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
563 - def _build_ui(self):
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
599 - def destroy(self):
600 self.txtOut.detach() 601 gtk.Dialog.destroy(self)
602 - def _onEditF(self, txt, text):
603 self.btnOK.set_sensitive(False)
604 - def _onRejectF(self, txt, text, exc):
605 self.btnOK.set_sensitive(False) 606 traceback.print_exc()
607 - def _onChangeF(self, txt, val):
608 self.btnOK.set_sensitive(True) 609 self.bind(val) 610 try: 611 if self._args != val.args: 612 self.prompt.entry.set_text(self.write_test(val)) 613 except: 614 self.prompt.entry.set_text(self.write_test(val)) 615 self.expr = txt.text 616 self.prompt.run()
617 - def _onResetF(self, txt, val):
618 self.btnOK.set_sensitive(True)
619
620 -class HelpTestButton(gtk.Button):
621 """ 622 A gtk.Button linked to a qubx.GTK.NumEntry, labeled '?', 623 which brings up an appropriate HelpTestDialog. 624 625 @ivar entry: the linked NumEntry 626 @ivar caption: top line of text explaining what's wanted 627 @ivar label: e.g. "f(x) = " 628 @ivar help_msg: a multiline explanation of what's wanted, with examples 629 @ivar bind: a func(val) that puts val into the global dict 630 e.g. qubx.pyenv.env.globals['f'] = val 631 @ivar write_test: a func(val) -> expr; where expr is a one-liner that tests the global binding 632 e.g. 'f(1.0)' 633 """ 634 635 __explore_featured = ['entry', 'caption', 'label', 'help_msg', 'bind', 'write_test'] 636
637 - def __init__(self, entry):
638 gtk.Button.__init__(self, '?') 639 self.connect('clicked', self._onPress) 640 self.entry = entry 641 self.caption = "" 642 self.label = 'f = ' 643 self.help_msg = "" 644 self.bind = lambda x: qubx.pyenv.env.globals.__setitem__('f', x.f) 645 self.write_test = lambda x: ""
646 - def _onPress(self, btn):
647 dlg = HelpTestDialog(self.entry.accept, self.caption, self.label, self.help_msg, self.bind, self.write_test) 648 if gtk.RESPONSE_ACCEPT == dlg.run(self.entry.get_text()): 649 self.entry.set_text(dlg.expr) # setValue(self.entry.accept(dlg.expr), dlg.expr, True) 650 self.entry.onParse() # self.entry.OnChange(self.entry, self.entry.value) 651 dlg.destroy()
652 653 ALTMAP_KEYS = "abcdefghijklmnopqrstuvwxyz1234567890-=`[]\;',./" 654
655 -class AltMapTable(gtk.ScrolledWindow):
656 __explore_featured = ['vbox', 'altmap', 'entries']
657 - def __init__(self):
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 = ''
677 - def __onSetAlt(self, k, expr):
678 self.entries[k].value = expr
679 - def __onChangeEntry(self, k, entry, val):
681 - def __onClickChoose(self, k):
682 fname = Open('%s: pick a script for %s-%s' % (qubx.pyenv.env.globals['QubX'].appname, qubx.pyenv.env.global_modifier, k), 683 self.__path, qubx.util_types.bind_with_args(qubx.pyenv.env.set_alt, k), 684 filters=[('Python scripts', '.py')]) 685 path = os.path.split(fname)[0] 686 if path: 687 self.__path = path
688 689
690 -class ExploreTreeView(gtk.TreeView):
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):
715 self.obj = obj 716 self.obj_name = global_name 717 self.store.clear() 718 it0 = self.store.append(None, (global_name, global_name, obj)) 719 it_sel = self.populate(it0, obj, (not self.omit_toplevel) and global_name or "", sels) or it0 720 self.expand_row(self.store.get_path(it0), False) 721 gobject.idle_add(self.get_selection().select_iter, it_sel) 722 gobject.idle_add(self.scroll_to_cell, self.store.get_path(it_sel))
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 # pull 'INSTANCES', 'INSTANCES_AS_BASE' to top of listing 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: # no __class__ 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
760 - def __onSelectionChanged(self, treesel):
761 store, it = treesel.get_selected() 762 if not (it is None): 763 path = store.get_path(it) 764 self.OnSelect(self.get_sub_obj(path), *self.path_to_names(path))
765 - def __onRowExpanded(self, treeview, it, path):
766 # populate its children (if not already done) 767 768 # are any children populated? nothing to do. 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 # get object dict corresponding to it row 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
787 - def path_to_names(self, path):
788 return [self.store[path[:i+1]][0] for i in xrange(1, len(path))]
789 - def get_name_map(self, obj, objname):
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 # no need to explore func/methodinspect.getmembers doesn't work with GProps 805 except AttributeError: # no __class__? 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)
812 - def get_name_map_all(self, obj, objname):
813 try: 814 if self.show_private: 815 # omit python's special vars 816 return (dict((k,v) for (k,v) in inspect.getmembers(obj) if 0 != k.find("__")), build_dotname(objname)) 817 else: 818 # omit all vars with __ 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
828 - def get_full_name(self, path=None):
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]
835 - def get_sub_obj(self, path):
836 return self.store[path][2]
837
838 -def build_dotname(objname):
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
847 -class ExploreListSlice(object):
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)
851 - def __str__(self):
852 return str(self.obj[self.base:self.base+self.count])
853
854 -def ExploreListSlices(obj, objname, base=0, count=None):
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
870 -class ExploreObjView(gtk.VBox):
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):
910 self.__obj = obj 911 self.txtRepr.set_text(repr(obj)) 912 self.explore_str = self.__explore_str 913 if isinstance(obj, qubx.tree.Node) or isinstance(obj, qubx.tree_native.Node): 914 self.btnEditTree.show() 915 else: 916 self.btnEditTree.hide() 917 self.__obj_api = get_api_url(obj, name, parent or get_active_window()) 918 if self.__obj_api: 919 self.btnViewAPI.show() 920 else: 921 self.btnViewAPI.hide()
922 - def set_fullname(self, x):
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)
928 - def set_explore_str(self, x):
929 self.__explore_str = x 930 if not self.__clicking_explore: 931 self.radExplore[x].set_active(True) 932 buf = self.txtStr.get_buffer() 933 # some things don't have useful docs, so pretty-print them 934 if x == EXPLORE_STR_DOC: 935 try: 936 if (self.__obj.__class__ in [str, list, tuple]) or isinstance(x, dict): 937 x = EXPLORE_STR_PPRINT 938 except: 939 pass 940 if x == EXPLORE_STR_NONE: 941 buf.set_text("") 942 elif x == EXPLORE_STR_STR: 943 buf.set_text(str(self.__obj)) 944 elif x == EXPLORE_STR_PPRINT: 945 buf.set_text(pprint.pformat(self.__obj)) 946 elif x == EXPLORE_STR_DOC: 947 doc = pydoc.text.document(self.__obj) 948 if (not doc) or (doc[0] == '<'): 949 doc = pydoc.text.document(self.__obj.__class__) 950 doc = re.sub("\b.", "", doc) 951 buf.set_text(doc) 952 elif x == EXPLORE_STR_CUSTOM: 953 expr = self.txtCustom.get_text() 954 if expr: 955 try: 956 buf.set_text(pprint.pformat(eval(expr, qubx.pyenv.env.globals, {"obj": self.__obj}))) 957 except: 958 buf.set_text(traceback.format_exc()) 959 else: 960 buf.set_text("")
961 - def click_explore_str(self, x):
962 self.__clicking_explore = True 963 self.set_explore_str(x) 964 self.__clicking_explore = False
965 - def __onChangeCustom(self, txt, val):
966 self.__clicking_explore = True 967 if self.explore_str == EXPLORE_STR_CUSTOM: 968 self.explore_str = EXPLORE_STR_CUSTOM 969 self.__clicking_explore = False
970 - def __onClickEditTree(self, btn):
971 qubx.treeGTK.UserEdit(self.__obj, self.__parent, self.__appname, self.fullname)
972 - def __onClickViewAPI(self, btn):
973 webbrowser.open(self.__obj_api)
974 975
976 -class ExploreDialog(gtk.Dialog):
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):
999 self.treeview.explore(obj, global_name, *sels) 1000 return gtk.Dialog.run(self)
1001 - def __onSelect(self, obj, *sels):
1002 store, it = self.treeview.get_selection().get_selected() 1003 name = "" 1004 parent = None 1005 if not (it is None): 1006 try: 1007 path = store.get_path(it) 1008 name = store[path][0] 1009 parent = self.treeview.get_sub_obj(path[:-1]) 1010 except: 1011 pass 1012 self.objview.set_obj(obj, name, parent) 1013 self.objview.fullname = self.treeview.get_full_name() 1014 self.selection_names = sels
1015 1016
1017 -def explore_one(obj, name, *selection_names, **kw):
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
1060 -def get_api_url(obj, name, parent):
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 # traceback.print_exc() 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
1091 -class ScriptedLinkView(qubx.GTK.HyperTextView2):
1092 __explore_featured = ['module', 'link_ref', 'clear', 'write']
1093 - def __init__(self, name, *args):
1094 qubx.GTK.HyperTextView2.__init__(self) 1095 self.__ref = Reffer() 1096 self.module = qubx.pyenv.ScriptModule(name, qubx.pyenv.env.globals) 1097 self.module.OnCompileException += self.__ref(self.__onExc) 1098 qubx.pyenv.env.track_module_in_tasks(self.module) 1099 self.link_ref = Reffer() 1100 if args: 1101 self.write(*args)
1102 - def clear(self):
1103 qubx.GTK.HyperTextView2.clear(self) 1104 self.link_ref = Reffer()
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