| Home | Trees | Indices | Help |
|
|---|
|
|
1 ######################################################################### 2 # 3 # Date: Nov. 2001 Author: Michel Sanner, Daniel Stoffler 4 # 5 # sanner@scripps.edu 6 # stoffler@scripps.edu 7 # 8 # The Scripps Research Institute (TSRI) 9 # Molecular Graphics Lab 10 # La Jolla, CA 92037, USA 11 # 12 # Copyright: Michel Sanner, Daniel Stoffler and TSRI 13 # 14 ######################################################################### 15 16 #SUGGESTIONS 17 # 18 # - create base class for NEDIAL AND NETHUMBWHEEL 19 # - prefix PortWidget arguments with PW to avoid conflicts with widget args 20 # - might be easier if configure methods were to consume (i.e. pop) the 21 # arguemnts they do handle\ 22 # 23 24 __doc__ = """ 25 This file defines the class PortWidget which is used to wrap GUI elements 26 that are exposed to the visual programming environemnt as widgets. 27 28 Widgets can be bound to ports that accept a single value (i.e. a single 29 parent). They are built from a description dictionary using the 30 port.createWidget, modified using port.configureWidget method and deleted 31 using the port.deleteWidget method. 32 33 The subclasses have to implement the following methods: 34 configure(rebuild=True, **kw) 35 set(value, run=0) 36 getDescr() 37 get() 38 getDataForSaving() 39 40 The method customizeWidget is called after the actual widget has been created. 41 42 Every option that can be passed to the constructor has to be listed in the 43 class variable dictionary configOpts. This dictionary is used to create a form 44 for editing the widget's configuration. The keys of this dictionary are the 45 keywords that can be passed to the constructor or the configue method. 46 47 The configure method has to handle all the keywords of configOpts. In adition 48 it has to recognize all keywords of the widget that is wrapped and configure 49 the widget accordingly. All other arguments have to be ignored. 50 Some options such as master can trigger the rebuilding of a widget. 51 The method returns a tuple (action, descr) where action can be an 52 instance of a new widget or 'rebuild' or 'resize' and descr is widget 53 description modified by the keywords handled by this configuration method. 54 55 The set() method is used to set the widget's value. This is the only alteration 56 of the widget's state which does not set the widget's modified attribute. 57 When this attribute is set, the widget's description will be saved into a 58 network file. Value CANNOT be set using configure but must be set using the 59 set method. The optional argument run=1/0 can be specified to force or prevent 60 the execution of the node. 61 62 The getDescr method has to return the complete description of the widget 63 as a dictionary (just like widgets are specified in node defintions). This 64 dictionnary can be passed directly to the widget's constructor. 65 66 Widget can appear in the node's parameter panel (master=None or 'ParamPanel') 67 or inside the node itself (master='node'). 68 Widgets that appear in nodes can be either displayed or not (node.isexpanded()) 69 70 Every widget is created in (self.master) along with it's label (if any). The 71 widget itself is created in a frame called widgetFrame created in the 72 constructor. This is done so that the widget and the label can be placed using 73 the grid geometry manager. For widgets in nodes the master is the frame 74 node.nodeWidgetMaster created in node.buildNodeIcon. For widgets in the 75 parameter panel the master is node.paramPanel.widgetFrame. 76 """ 77 78 import Tkinter, Pmw, warnings 79 import os, types, string 80 import warnings 81 import user 82 83 from UserDict import UserDict 84 85 from NetworkEditor.itemBase import NetworkItemsBase 86 from mglutil.gui.BasicWidgets.Tk.thumbwheel import ThumbWheel 87 from mglutil.gui.BasicWidgets.Tk.Dial import Dial 88 from mglutil.gui.BasicWidgets.Tk.vector3DGUI import vectorGUI 89 from mglutil.gui.BasicWidgets.Tk.xyzGUI import xyzGUI 90 from mglutil.gui.BasicWidgets.Tk.multiButton import MultiCheckbuttons 91 from mglutil.gui.BasicWidgets.Tk.multiButton import MultiRadiobuttons 92 from mglutil.gui.BasicWidgets.Tk.multiButton import RegexpGUI 93 from mglutil.gui.BasicWidgets.Tk.customizedWidgets import kbComboBox 94 from mglutil.gui.BasicWidgets.Tk.fileBrowsers import FileOpenBrowser, \ 95 FileSaveBrowser 96 from mglutil.util.callback import CallbackManager, CallBackFunction 97 from mglutil.util.relpath import abs2rel 98 99 import Vision 100 if hasattr( Vision, 'networkDefaultDirectory') is False: 101 Vision.networkDefaultDirectory = user.home 102 103105 """base class for network editor widgets 106 port: Port instance to which the widget is bound 107 108 master: parent widget into which the widget will be created 109 labelCfg: configuration dictionary for a Tkinter.Label (use to anme the widget 110 111 ## FIXME .... labelSide is now in the widgetGridCfgXXX 112 labelSide: 'left', 'right', 'bottom', 'top'. Describes relative position 113 of label and widget. Is used to compute row and column for gridding. 114 Defaults to left. 115 labelGridCfg: label gridding options (other than 'row' and 'column') 116 widgetGridCfg: label gridding options (other than 'row' and 'column') 117 NEwidgetPosition: {'posx':i, 'posy':j} will be used to compute real row and col 118 initialValue: used to set widget in constructor 119 """ 120 121 # this dict holds the list of keyword arguments that can be passed to 122 # the constructor or the configure method. The defaultValues are REQUIRED 123 # because they are used to create the widget's attributes 124 125 configOpts = { 126 'master': { 127 'defaultValue':'ParamPanel', 'type':'string', 128 'description': 'name of the Tk frame in which the widget appears', 129 # 'validValues': ['node', 'ParamPanel'] 130 }, 131 'NEwidgetPosition': { 132 'defaultValue':{}, 'type':'dict', 133 'description': "dict of type { 'posx':i, 'posy':j} use to place\ 134 this NEwidget (label+widget) relative to other NEwidgets (2x2 sub-grid)\ 135 also accepts {'row':i, 'column':j} to specify the real row and column", 136 }, 137 ## 'labelSide': { 138 ## 'defaultValue':'left', 'type':'string', 139 ## 'validValues': ['left', 'right', 'top', 'bottom'], 140 ## }, 141 'widgetGridCfg': { 142 'defaultValue': {'labelSide':'left'}, 143 'type':'dict', 144 'description': "grid configuration dict for widgetFrame.\ 145 (other than 'row' and 'column').", 146 }, 147 'labelGridCfg': { 148 'defaultValue': {}, 'type':'dict', 149 'description': "grid configuration dict for widget label. \ 150 (other than 'row' and 'column').", 151 }, 152 'labelCfg': { 153 'defaultValue':{'text':''}, 'type':'dict', 154 'description': 'dict of Tkinter.Label options' 155 }, 156 'initialValue':{ 157 'defaultValue':None, 'type':'None', 158 }, 159 'lockedOnPort':{ 160 'defaultValue':False, 'type':'boolean', 161 'description': 'when True this widget cannot be unbound' 162 }, 163 } 164 165 # dict of option specific to subclasses 166 ownConfigOpts = {} 167 168872 873170 name = port.name 171 NetworkItemsBase.__init__(self, name) 172 173 self.widget = None # will hold the actual widget 174 self.objEditor = None # will point to widget editor when there is one 175 self.widgetFrame = None # parent frame for widget 176 # this allows to grid widgetFrame and tklabel in self.master 177 178 #self.labelSide = None # can be 'left', 'right', 'top', bottom' 179 180 self.port = port 181 self.lockedOnPort = False # when True widget cannot be unbound 182 self.inNode = False # True if widget appears in Node when displayed 183 self._newdata = False # set to 1 by set method 184 # reset to 0 after node ran 185 self.lastUsedValue = None 186 187 self._trigger = None # function to be called when widget changes value 188 self.oldmaster = None # used in rebuilt() when widget moves between panels 189 self.tklabel = None # Tkinter Label object used for widget's label 190 self.labelCfg = {} # options for the widget's label 191 192 # create all attributs that will not be created by configure because 193 # they do not appear on kw 194 # NOTE: use PortWidget instead self here, else we also get all keys 195 # that were added in Subclasses! 196 for key in PortWidget.configOpts.keys(): 197 v = kw.get(key, None) 198 default = self.configOpts[key]['defaultValue'] 199 if isinstance(default, dict): 200 default = default.copy() 201 setattr(self, key, default) 202 #if v is None: # self.configure will not do anyting for this key 203 # setattr(self, key, self.configOpts[key]['defaultValue']) 204 205 self.master = kw.get('master', self.master) # name of the master panel 206 # this will be the master for self.widgetFrame 207 208 node = port.node 209 master = self.master 210 self.inNode = False 211 212 if master == 'node': 213 self.inNode = True 214 self.masterTk = node.nodeWidgetMaster 215 self._trigger = node.schedule 216 elif master == 'ParamPanel': 217 self.masterTk = node.paramPanel.widgetFrame 218 self._trigger = node.paramPanel.run 219 elif master in port.network.userPanels.keys(): 220 self.masterTk = port.network.userPanels[master].frame 221 self._trigger = node.schedule 222 elif master =='macroParamPanel': 223 self.masterTk = node.network.macroNode.paramPanel.widgetFrame 224 self._trigger = node.schedule 225 else: 226 warnings.warn("%s is not a legal master for a widget"%master) 227 return 228 229 # create label 230 self.tklabel = apply( Tkinter.Label, (self.masterTk,), self.labelCfg) 231 self.tklabel.bind("<Button-3>", self.postWidgetMenu) 232 self.tklabel.configure(bg='#c3d0a6') 233 234 apply( self.configure, (False,), kw) # configure without rebuilding 235 236 # create widget frame 237 self.widgetFrame = Tkinter.Frame(self.masterTk) 238 239 self.gridLabelAndWidget() 240 241 # create menu to access port and widget editors and config panel 242 # got rid of useless tearoff 243 self.menu = Tkinter.Menu(port.node.getEditor(), title='Widget Menu', tearoff=False) 244 self.menu.add_separator() 245 246 if master in port.network.userPanels.keys(): 247 self.menu.add_command(label='Label Editor', underline=0, command=self.label_cb) 248 249 self.menu.add_command(label='Port Editor', underline=0, 250 command=port.edit) 251 self.menu.add_command(label='Widget Editor', underline=0, 252 command=self.edit) 253 if not self.lockedOnPort: 254 self.menu.add_command(label='Unbind Widget', underline=0, 255 command=self.port.unbindWidget) 256 257 # we need to add an empty panel menu here, since we will delete it 258 # in postWidgetMenu and rebuild it 259 self.panelmenu = Tkinter.Menu(self.menu, tearoff=0) 260 self.menu.add_cascade(label='Move To', menu=self.panelmenu) 261 262 self.vEditor = port.getEditor() 263 264 self.labelWidgetEditor = None265 266268 #print "moveWidgetToPanel" 269 if name == 'Node': 270 name = 'node' 271 self.configure(master=name)272 273275 """Display menu that allows to display vonfiguration panel or 276 start port or widget editor""" 277 #print "postWidgetMenu" 278 279 self.panelmenu.delete(0, 'end') 280 #self.menu.delete(self.menu.index('Move To')) 281 #self.menu.add_cascade(label='Move To', menu=self.panelmenu) 282 283 cb = CallBackFunction( self.moveWidgetToPanel, 'Node') 284 self.panelmenu.add_command(label='Node', command=cb) 285 286 cb = CallBackFunction( self.moveWidgetToPanel, 'ParamPanel') 287 self.panelmenu.add_command(label='ParamPanel', command=cb) 288 289 if hasattr(self.port.node.network, 'macroNode'): 290 cb = CallBackFunction( self.moveWidgetToPanel, 'macroParamPanel') 291 self.panelmenu.add_command(label='macroParamPanel', command=cb) 292 293 for name in self.port.network.userPanels.keys(): 294 cb = CallBackFunction( self.moveWidgetToPanel, name) 295 self.panelmenu.add_command(label=name, command=cb) 296 297 self.menu.post(event.x_root, event.y_root)298 299301 """ select the right widgetGridCfg dictionary based on the panel 302 by default we use se;f.widgetGridCfg, but then check if there is an entry 303 in the node's widgetDescr that is specific for the master (i.e. 'node', 304 'ParamPanel' or a user panel 305 """ 306 gridCfg = self.widgetGridCfg 307 # check for panel specific gridCfg 308 descr = self.port.node.widgetDescr[self.name] 309 if descr.has_key('widgetGridCfg'+self.master): 310 gridCfg = descr['widgetGridCfg'+self.master] 311 #print 'FFFFFFFF found panel grid', self.master, gridCfg 312 return gridCfg313 314316 #print "gridLabelAndWidget" 317 318 if self.master is not None and self.master in self.port.network.userPanels.keys(): 319 return 320 321 if self.widgetFrame is None: 322 return 323 324 # if row and column are specified they are in widgetGridCfg 325 # self.labelGridCfg can be used to specify other gridding options such 326 # as stick, etc 327 328 port = self.port 329 # default gridCfg is widgetGridCfg 330 #self.widgetGridCfg 331 332 ## WARNING self.widgetGridCfg is the default but we use a panel 333 ## specific dictionary if we find one 334 ## WE always find one for user panels 335 336 # get the right widgetGridCfg dict 337 gridCfg = self.getWidgetGridCfg() 338 339 # find out how many rows and columns 340 self.port.editor.master.update() 341 x,y = self.masterTk.grid_size() 342 343 hasrow = hascol = True 344 labelSide = gridCfg.pop('labelSide', 'left') 345 346 if not gridCfg.has_key('row'): 347 # programmer has not provided a row, pack label's rowspan down if 348 # labelSide is 'top' 349 gridCfg['row'] = y 350 if labelSide=='top': 351 gridCfg['row'] += self.labelGridCfg.get('rowspan', 1) 352 hasrow = False 353 354 if not gridCfg.has_key('column'): 355 # programmer has not provided a column, pack in column 'columspan' 356 # of label if labelSide is left, else pack at column 0 357 gridCfg['column'] = 0 358 if labelSide=='left': 359 gridCfg['column'] += self.labelGridCfg.get('columnspan', 1) 360 hascol = False 361 362 # The label will be placed using this row and column info from 363 # widgetGridCfg and labelSide 364 self.labelGridCfg['row'] = gridCfg['row'] 365 self.labelGridCfg['column'] = gridCfg['column'] 366 367 #print 'HHHHHHHHHHHHH', self.name, self.master, labelSide, hasrow, hascol, x, y, gridCfg 368 if labelSide == 'left': 369 # subtract columspan for widget's column 370 dx = self.labelGridCfg.get('columnspan', 1) 371 self.labelGridCfg['column'] -= dx 372 373 # if label would be grided at a negative column index and no column 374 # was specified by the programmer for the widget, we put the 375 # label at 0 and the widget at the label's columnspan 376 if self.labelGridCfg['column']<0: 377 self.labelGridCfg['column'] = 0 378 gridCfg['column'] = dx 379 380 # grid the label 381 if len(self.labelCfg['text']): 382 apply( self.tklabel.grid, (), self.labelGridCfg) 383 384 # grid the widget 385 apply( self.widgetFrame.grid, (), gridCfg) 386 387 elif labelSide == 'right': 388 # add columspan for widget's column 389 dx = self.labelGridCfg.get('columnspan', 1) 390 self.labelGridCfg['column'] += dx 391 392 # grid the label 393 if len(self.labelCfg['text']): 394 apply( self.tklabel.grid, (), self.labelGridCfg) 395 396 # grid the widget 397 apply( self.widgetFrame.grid, (), gridCfg) 398 399 elif labelSide == 'top': 400 # subtract rowspan for widget's row 401 dx = self.labelGridCfg.get('rowspan', 1) 402 self.labelGridCfg['row'] -= dx 403 # if label would be grided at a negative row index and no row 404 # was specified by the programmer for the widget, we put the 405 # label at 0 and the widget at the label's rowspan 406 if self.labelGridCfg['row']<0: 407 self.labelGridCfg['row'] = 0 408 gridCfg['row'] = dx 409 410 # grid the label 411 if len(self.labelCfg['text']): 412 apply( self.tklabel.grid, (), self.labelGridCfg) 413 414 # grid the widget 415 apply( self.widgetFrame.grid, (), gridCfg) 416 417 elif labelSide == 'bottom': 418 # add rowspan for widget's row 419 dx = self.labelGridCfg.get('rowspan', 1) 420 self.labelGridCfg['row'] += dx 421 422 # grid the label 423 if len(self.labelCfg['text']): 424 apply( self.tklabel.grid, (), self.labelGridCfg) 425 426 # grid the widget 427 apply( self.widgetFrame.grid, (), gridCfg) 428 else: 429 warnings.warn("%s illegal labelSide"%labelSide) 430 431 gridCfg['labelSide'] = labelSide432 433435 """start widget editor""" 436 if self.objEditor is None: 437 form = WidgetEditor(self, None) 438 self.objEditor = form 439 if self.port.objEditor is not None: 440 self.port.objEditor.editWidgetVarTk.set(1)441 442444 #print "label_cb" 445 446 if self.labelWidgetEditor is None: 447 self.labelWidgetEditor = LabelWidgetEditor(panel=self.master, widget=self) 448 else: 449 if self.labelWidgetEditor.master.winfo_ismapped() == 0: 450 self.labelWidgetEditor.master.deiconify() 451 self.labelWidgetEditor.master.lift()452 453455 # hara kiri 456 #if self.inNode and self.port.node.isExpanded(): 457 #self.port.node.hideInNodeWidgets() 458 459 self.tklabel.destroy() 460 self.widgetFrame.destroy() 461 462 if self.labelWidgetEditor is not None: 463 self.labelWidgetEditor.master.destroy() 464 self.labelWidgetEditor = None 465 466 # kill circular references: 467 self.port = None 468 self.objEditor = None 469 self.menu = None 470 self.widget = None 471 self.master = None 472 self.menu = None473 474476 ## handles all keywords found in self.configOpts 477 ## returns action, rebuildDescr where action is either None, 'rebuild' 478 ## or 'resize' 479 ## and rebuildDescr is the decsription dict modified for 480 ## the keywords handled by this configure method 481 482 ## for attributes that are of type dict make copies 483 484 action = None # might become 'rebuild' or 'resize' 485 486 if self.widget is None: 487 rebuildDescr = kw.copy() 488 else: 489 rebuildDescr = self.getDescr().copy() 490 rebuildDescr.update(kw) 491 gridit = False 492 493 # handle labelGridCfg first because handling master keyword might call 494 # panel.getPackingDict which needs self.labelGridCfg to be up to date 495 v = kw.get('labelGridCfg', None) 496 if v: 497 self.labelGridCfg.update(v) 498 # update rebuildDescr 499 rebuildDescr['labelGridCfg'] = self.labelGridCfg 500 gridit = True 501 502 widgetPlacerCfg = kw.get('widgetPlacerCfg', None) 503 if widgetPlacerCfg: # and self.widgetFrame: 504 # update rebuildDescr 505 rebuildDescr['widgetPlacerCfg'] = widgetPlacerCfg 506 507 for k, v in kw.items(): 508 if k == 'master': 509 if self.master not in self.port.network.userPanels.keys() \ 510 or v != self.master: 511 # the last part (or v != self.master) 512 # has been hadded otherwise you need to say twice that you want 513 # to move the widget from the user panel to the node 514 action = 'rebuild' 515 516 # go from string to Tk widget 517 # self.master = self.port.node.findWidgetMasterTk(v) 518 # if v!='node' and v!='ParamPanel': 519 # panel = self.port.editor.panelFromName(v) 520 # if panel: 521 # descr = rebuildDescr.get('widgetGridCfg'+v, None) 522 # if descr is None: 523 # # default value for labelSide is oldmaster's 524 # # labelSide. We get the widgetGridCfg of old master 525 # labelSide = self.getWidgetGridCfg()['labelSide'] 526 # pd = panel.getPackingDict(self, labelSide) 527 # rebuildDescr['widgetGridCfg'+v] = pd 528 # elif not descr.has_key('row') or \ 529 # not descr.has_key('column'): 530 # labelSide = descr.pop('labelSide', 'left') 531 # pd = panel.getPackingDict(self, labelSide) 532 # rebuildDescr['widgetGridCfg'+v].update(pd) 533 534 self.oldmaster = self.master 535 self.master = v 536 rebuildDescr['master'] = v 537 538 elif k == 'initialValue': 539 rebuildDescr[k] = self.initialValue = v 540 541 elif k == 'lockedOnPort': 542 rebuildDescr[k] = self.lockedOnPort = v 543 544 ## elif k=='labelSide': 545 ## val = self.labelSide 546 ## if action!='rebuild': 547 ## if val in ['left', 'right'] and v not in ['left', 'right']: 548 ## action = 'resize' 549 ## if val in ['top', 'bottom'] and v not in ['top', 'bottom']: 550 ## action = 'resize' 551 552 ## rebuildDescr['labelSide'] = v #self.labelSide = v 553 ## gridit = True 554 555 elif k == 'widgetGridCfg': 556 self.widgetGridCfg.update(v) 557 rebuildDescr['widgetGridCfg'] = self.widgetGridCfg 558 gridit = True 559 560 elif k == 'labelCfg': 561 if self.master not in self.port.network.userPanels.keys(): 562 if len(self.labelCfg['text'])==0 and len(v['text'])>0: 563 apply( self.tklabel.grid, (), self.labelGridCfg) 564 565 if len(self.labelCfg['text'])>0 and len(v['text'])==0: 566 self.tklabel.grid_forget() 567 568 self.labelCfg.update(v) 569 rebuildDescr['labelCfg'] = self.labelCfg 570 apply( self.tklabel.configure, (), v) 571 572 if self.master not in self.port.network.userPanels.keys(): 573 gridit = True 574 if self.inNode and action!='rebuild': 575 action = 'resize' 576 577 elif k == 'NEwidgetPosition': 578 assert isinstance(v, dict) 579 self.NEwidgetPosition.update(v)# = v.copy() 580 rebuildDescr['NEwidgetPosition'] = self.NEwidgetPosition 581 gridit = True 582 583 elif k[:13]=='widgetGridCfg' and len(k)>13: 584 # widget panels grid configuration dicts 585 descr = self.port.node.widgetDescr[self.name] 586 if descr.has_key(self.name): 587 descr[self.name].update(v) 588 else: 589 self.port.node.widgetDescr[self.name][k] = v 590 gridit = True 591 592 labelSide = kw.get('labelSide', None) 593 if labelSide: 594 warnings.warn( 595 "'labelSide' in widgetDescr of node %s is deprecated, put labelSide in 'widgetGridCfg'"%self.port.node.name) 596 widgetGridCfg = self.getWidgetGridCfg() 597 widgetGridCfg['labelSide'] = labelSide 598 if action!='rebuild': 599 if labelSide in ['left', 'right'] and labelSide not in [ 600 'left', 'right']: 601 action = 'resize' 602 if labelSide in ['top', 'bottom'] and labelSide not in [ 603 'top', 'bottom']: 604 action = 'resize' 605 gridit = True 606 607 if action=='rebuild' and rebuild: 608 action, rebuildDescr = self.rebuild(rebuildDescr) 609 610 if gridit: 611 if self.widget is not None: 612 if self.tklabel is not None: 613 self.tklabel.grid_forget() 614 self.widgetFrame.grid_forget() 615 self.gridLabelAndWidget() 616 gridit = False 617 618 if action=='resize' and rebuild: 619 if gridit: 620 if self.widget is not None: 621 if self.tklabel is not None: 622 self.tklabel.grid_forget() 623 self.widgetFrame.grid_forget() 624 self.gridLabelAndWidget() 625 self.port.node.autoResize() 626 gridit = False 627 628 if gridit: 629 self.gridLabelAndWidget() 630 631 self._setModified(True) 632 633 return action, rebuildDescr634 635637 #print "rebuild", rebuildDescr 638 639 # if the master has changed and the widget was ina userPanel 640 # remove widget from list in userPanel 641 value = self.get() 642 addWidgetToPanel = False 643 if self.oldmaster != rebuildDescr['master']: 644 ## wdescr = self.port.node.widgetDescr[self.name] 645 ## gcfg = wdescr.get('widgetGridCfg', None) 646 ## if gcfg is not None: 647 ## if not gcfg.has_key('row'): 648 ## rebuildDescr.pop('row', None) 649 ## if not gcfg.has_key('column'): 650 ## rebuildDescr.pop('column', None) 651 ## else: 652 ## rebuildDescr.pop('row', None) 653 ## rebuildDescr.pop('column', None) 654 655 if self.oldmaster in self.port.network.userPanels.keys(): 656 self.port.network.userPanels[self.oldmaster].deleteWidget(self) 657 if rebuildDescr['master'] in self.port.network.userPanels.keys(): 658 addWidgetToPanel = True 659 660 port = self.port 661 # unbind widget (this destroys the old widget) 662 port.unbindWidget() 663 # delete the not needed previousWidgetDescr 664 port._previousWidgetDescr = None 665 666 # create new widget 667 port.createWidget(descr=rebuildDescr) # create new widget 668 port.node.autoResize() 669 if port.node.inNodeWidgetsVisibleByDefault and not \ 670 port.node.isExpanded(): 671 port.node.toggleNodeExpand_cb() 672 673 #newWidget = apply(self.__class__, (self.port,), rebuildDescr) 674 #newWidget._setModified(True) 675 #newWidget.port = self.port 676 newWidget = port.widget 677 if self.objEditor: 678 self.objEditor.widget = newWidget 679 newWidget.objEditor = self.objEditor 680 681 if addWidgetToPanel: 682 panel = port.editor.panelFromName(newWidget.master) 683 if panel: 684 if rebuildDescr.has_key('widgetPlacerCfg'): 685 widgetPlacerCfg = rebuildDescr['widgetPlacerCfg'] 686 else: 687 widgetPlacerCfg = None 688 panel.addWidget(newWidget, widgetPlacerCfg=widgetPlacerCfg) 689 newWidget.set(value,0) 690 691 return newWidget, rebuildDescr692 693695 # returns the widget's description dictionary 696 # such a dictionary 697 masterName = None 698 if self.inNode: 699 masterName = 'node' 700 elif self.masterTk==self.port.node.paramPanel.widgetFrame: 701 masterName = 'ParamPanel' 702 else: 703 node = self.port.node 704 if hasattr(node.network, 'macroNode'): 705 if self.masterTk==node.network.macroNode.paramPanel.widgetFrame: 706 masterName = 'macroParamPanel' 707 else: 708 masterName = self.master 709 else: 710 if self.master in self.port.network.userPanels.keys(): 711 masterName = self.master 712 713 descr = { 714 'class':self.__class__.__name__, 715 # go from Tk widget to string 716 #'master': self.port.node.findWidgetMasterName(self), 717 'master': masterName, 718 #'labelSide': self.labelSide, 719 'initialValue': self.initialValue, 720 } 721 722 if len(self.labelGridCfg.keys()): 723 descr['labelGridCfg'] = self.labelGridCfg 724 if len(self.widgetGridCfg.keys()): 725 descr['widgetGridCfg'] = self.widgetGridCfg 726 descr['labelCfg'] = self.labelCfg 727 for k,v in self.port.node.widgetDescr[self.name].items(): 728 if k[:13]=='widgetGridCfg' and len(k)>13: 729 descr[k] = v 730 731 widgetPlacerCfg = self.widgetFrame.place_info() 732 if widgetPlacerCfg.has_key('relx') and widgetPlacerCfg.has_key('rely'): 733 descr['widgetPlacerCfg'] = {'relx': widgetPlacerCfg['relx'], 734 'rely': widgetPlacerCfg['rely'] } 735 736 return descr737 738740 self._setModified(True) 741 self._newdata = True 742 if self._trigger and run: 743 self._trigger(value)744 745747 # has to be implemented by subclass 748 return None749 750752 # this method is called when a network is saved and the widget 753 # value needs to be saved 754 # by default, it returns what the widget.get method returns 755 # it can be subclassed by widgets in order to provide data that 756 # is different from what the widget.get method returns 757 return self.get()758 759761 self._setModified(True) # setting widget is a _modified event 762 if self._trigger: 763 self._trigger()764 765 770 771773 """Compare this widget to the widgetDescr defined in a given network 774 node base class and return a dictionary with the differences 775 """ 776 #print "PortWidget.compareToOrigWidgetDescr", self 777 778 ownDescr = self.getDescr().copy() 779 dummy = self.port.node.__class__() 780 origWidgetDescr = self.__class__.configOpts.copy() 781 nodeWidgetDescr = dummy.widgetDescr[self.port.name].copy() 782 783 # create a defaults dict to compare agains for labelGridCfg 784 labelGridDefaults = dict( 785 rowspan=1, columnspan=1, sticky='w', padx=0, pady=0, ipadx=0, 786 ipady=0) 787 # update it with whatever we find in the nodeWidgetDescr 788 if nodeWidgetDescr.has_key('labelGridCfg'): 789 labelGridDefaults.update(nodeWidgetDescr['labelGridCfg'] ) 790 791 # create a defaults dict to compare agains for widgetGridCfg 792 # FIXME: DIFFERENT ROWS AND COLUMNS FOR DIFFERENT LABELSIDE VALUES! 793 widgetGridDefaults = dict( 794 row=0, column=1, 795 rowspan=1, columnspan=1, sticky='w', padx=0, pady=0, ipadx=0, 796 ipady=0, labelSide='left', labelCfg=dict(text='')) 797 # update it with whatever we find in the nodeWidgetDescr 798 if nodeWidgetDescr.has_key('widgetGridCfg'): 799 widgetGridDefaults.update(nodeWidgetDescr['widgetGridCfg']) 800 801 descr = {} 802 # compare to widget definitions in node 803 for k,v in ownDescr.items(): 804 if k == 'initialValue': 805 if nodeWidgetDescr.has_key('initialValue'): 806 if v == nodeWidgetDescr['initialValue']: 807 continue 808 elif v == origWidgetDescr['initialValue']['defaultValue']: 809 continue 810 else: 811 descr[k] = v 812 continue 813 814 elif k == 'labelCfg': 815 if nodeWidgetDescr.has_key('labelCfg'): 816 if v['text'] == nodeWidgetDescr['labelCfg']['text']: 817 continue 818 elif origWidgetDescr.has_key('labelCfg'): 819 if v['text'] == origWidgetDescr['labelCfg']['defaultValue']['text']: 820 continue 821 822 descr[k] = v 823 continue 824 825 # labelGridCfg row and column are always automatically computed 826 # by self.gridLabelAndWidget, using 'labelSide' 827 elif k == 'labelGridCfg': 828 tmpdict = {} 829 for tk, tv in v.items(): 830 if tk == 'row': 831 continue 832 elif tk == 'column': 833 continue 834 else: 835 if tv != labelGridDefaults[tk]: 836 tmpdict[tk] = tv 837 838 if len(tmpdict.keys()): 839 descr[k] = tmpdict 840 continue 841 842 elif k == 'widgetGridCfg' or k == 'widgetGridCfg%s'%self.master: 843 tmpdict = {} 844 for tk, tv in v.items(): 845 if tk == 'row': 846 continue 847 elif tk == 'column': 848 continue 849 else: 850 if tv != widgetGridDefaults[tk]: 851 tmpdict[tk] = tv 852 853 if len(tmpdict.keys()): 854 descr[k] = tmpdict 855 continue 856 857 if k in nodeWidgetDescr.keys(): 858 if v != nodeWidgetDescr[k]: 859 descr[k] = v 860 861 862 # compare to default configuration options of widget 863 elif k in origWidgetDescr.keys(): 864 if v != origWidgetDescr[k]['defaultValue']: 865 descr[k] = v 866 867 # not found in either, so we have to add it 868 else: 869 descr[k] = v 870 871 return descr875 """class to manipulate the widget label in user panels 876 """ 877927 928 929879 #print "LabelWidgetEditor.__init__" 880 881 self.master = Tkinter.Toplevel() 882 self.master.title('Widget Label Editor') 883 self.master.protocol("WM_DELETE_WINDOW", self.master.withdraw) 884 885 self.widget = widget 886 self.panel = panel 887 888 labelNameTk = Tkinter.StringVar() 889 labelNameTk.set(widget.labelCfg['text']) 890 labelSideTk = Tkinter.StringVar() 891 labelSideTk.set(widget.widgetGridCfg['labelSide']) 892 893 self.labelName = Tkinter.Label(self.master, text='name:') 894 self.labelName.grid(row=0, column=0, sticky='w') 895 self.entryName = Tkinter.Entry(self.master, 896 width=10, 897 textvariable=labelNameTk ) 898 self.entryName.grid(row=0, column=1, sticky='we') 899 self.entryName.bind('<Return>', self.renameWidget_cb) 900 901 self.comboSide = Pmw.ComboBox( 902 self.master, 903 label_text='side:', 904 labelpos='w', 905 entryfield_value=self.widget.widgetGridCfg['labelSide'], 906 scrolledlist_items=['left', 'right', 'top', 'bottom'], 907 selectioncommand=self.resideWidget_cb, 908 history=False 909 ) 910 self.comboSide.grid(row=0, column=3, sticky='we')911 912914 self.widget.labelCfg['text'] = self.entryName.get() 915 self.widget.tklabel['text'] = self.widget.labelCfg['text'] 916 self.widget.port.network.userPanels[self.panel].rePlaceWidget(self.widget)917 918920 #print "resideWidget_cb" 921 lComboSide = self.comboSide.get() 922 if lComboSide in ['left','right','top','bottom']: 923 self.widget.widgetGridCfg['labelSide'] = self.comboSide.get() 924 self.widget.port.network.userPanels[self.panel].rePlaceWidget(self.widget) 925 else: 926 self.comboSide.set(self.widget.widgetGridCfg['labelSide'])931 """NetworkEditor wrapper for Thumbwheel widget. 932 Handles all PortWidget arguments and all Thumbwheel arguments except for value. 933 Name: default: 934 callback None 935 canvasCfg {} 936 continuous 1 937 height 40 938 increment 0.0 939 lockContinuous 0 940 lockBMin 0 941 lockBMax 0 942 lockBIncrement 0 943 lockIncrement 0 944 lockMin 0 945 lockMax 0 946 lockOneTurn 0 947 lockPrecision 0 948 lockShowLabel 0 949 lockType 0 950 lockValue 0 951 min None 952 max None 953 oneTurn 360. 954 orient 'horizontal' 955 precision 2 956 reportDelta 0 957 showLabel 1 958 type 'float' 959 wheelLabCfg {} 960 width 200 961 wheelPad 6 962 """ 963 964 # this dict holds the list of keyword arguments that can be passed to 965 # the constructor or the configure method. The defaultValues are REQUIRED 966 # because they are used to create the widget's attributes 967 968 configOpts = PortWidget.configOpts.copy() 969 configOpts['initialValue'] = { 970 'defaultValue':0.0, 'type':'float', 971 } 972 ownConfigOpts = { 973 'callback': { 974 'defaultValue':None, 'type': 'None', 975 'description':"???", 976 }, 977 'canvasCfg':{ 978 'defaultValue':{}, 'type':'dict', 979 'description': "???" 980 }, 981 'continuous': { 982 'defaultValue':True, 'type':'boolean', 983 'description':"", 984 }, 985 'height':{ 986 'defaultValue':40, 'min':5, 'max':500, 'type':'int', 987 'description': "Thumbwheel's height" 988 }, 989 'increment': { 990 'defaultValue':0.0, 'type':'float', 991 'description':"", 992 }, 993 'lockContinuous': { 994 'defaultValue':False, 'type':'boolean', 995 'description':"", 996 }, 997 'lockBMin': { 998 'defaultValue':False, 'type':'boolean', 999 'description':"", 1000 }, 1001 'lockBMax': { 1002 'defaultValue':False, 'type':'boolean', 1003 'description':"", 1004 }, 1005 'lockBIncrement': { 1006 'defaultValue':False, 'type':'boolean', 1007 'description':"", 1008 }, 1009 'lockIncrement': { 1010 'defaultValue':False, 'type':'boolean', 1011 'description':"", 1012 }, 1013 'lockMin': { 1014 'defaultValue':False, 'type':'boolean', 1015 'description':"", 1016 }, 1017 'lockMax': { 1018 'defaultValue':False, 'type':'boolean', 1019 'description':"", 1020 }, 1021 'lockOneTurn': { 1022 'defaultValue':False, 'type':'boolean', 1023 'description':"", 1024 }, 1025 'lockPrecision': { 1026 'defaultValue':False, 'type':'boolean', 1027 'description':"", 1028 }, 1029 'lockShowLabel': { 1030 'defaultValue':False, 'type':'boolean', 1031 'description':"", 1032 }, 1033 'lockType': { 1034 'defaultValue':False, 'type':'boolean', 1035 'description':"", 1036 }, 1037 'lockValue': { 1038 'defaultValue':False, 'type':'boolean', 1039 'description':"", 1040 }, 1041 'min': { 1042 'defaultValue':None, 'type':'float', 1043 'description':"", 1044 }, 1045 'max': { 1046 'defaultValue':None, 'type':'float', 1047 'description':"", 1048 }, 1049 'oneTurn': { 1050 'defaultValue':360., 'type':'float', 1051 'description':"horizontal of vertical.", 1052 }, 1053 'orient': { 1054 'defaultValue':'horizontal', 'type':'string', 1055 'description':"Can bei 'horizontal' or 'vertical' or None", 1056 'validValues':['horizontal', 'vertical', None], 1057 }, 1058 'precision': { 1059 'defaultValue':2, 'type':'int', 1060 'validValues': [0,1,2,3,4,5,6,7,8,9], 1061 'description':"", 1062 }, 1063 'reportDelta': { 1064 'defaultValue':False, 'type':'boolean', 1065 'description':"", 1066 }, 1067 'showLabel': { 1068 'defaultValue':True, 'type':'boolean', 1069 'description':"", 1070 }, 1071 'type': { 1072 'defaultValue':'float', 'type':'string', 1073 'validValues': ['float', 'int'], 1074 'description':"", 1075 }, 1076 'wheelLabCfg':{ 1077 'defaultValue':{}, 'type':'dict', 1078 'description': "???" 1079 }, 1080 'width':{ 1081 'defaultValue':200, 'min':10, 'max':500, 'type':'int', 1082 'description': "Thumbwheel's width" 1083 }, 1084 'wheelPad':{ 1085 'defaultValue':6, 'min':1, 'max':500, 'type':'int', 1086 'description': "width of border around thumbwheel in pixels" 1087 }, 1088 } 1089 configOpts.update( ownConfigOpts ) 1090 10911222 12231093 1094 ## # create all attributes that will not be created by configure because 1095 ## # they do not appear on kw 1096 ## for key in self.ownConfigOpts.keys(): 1097 ## v = kw.get(key, None) 1098 ## if v is None: # self.configure will not do anyting for this key 1099 ## setattr(self, key, self.ownConfigOpts[key]['defaultValue']) 1100 1101 # get all arguments handled by NEThumbweel and not by PortWidget 1102 widgetcfg = {} 1103 for k in self.ownConfigOpts.keys(): 1104 if k in kw: 1105 widgetcfg[k] = kw.pop(k) 1106 1107 # call base class constructor 1108 apply( PortWidget.__init__, ( self, port), kw) 1109 1110 # create the Thumbwheel widget 1111 self.widget = apply( ThumbWheel, (self.widgetFrame,), widgetcfg) 1112 self.widget.callbacks.AddCallback(self.newValueCallback) 1113 # rename Options Panel to port name 1114 self.widget.opPanel.setTitle("%s : %s"%(port.node.name, port.name) ) 1115 # overwrite right mouse button click 1116 self.widget.canvas.bind("<Button-3>", self.postWidgetMenu) 1117 self.widget.valueLabel.bind("<Button-3>", self.postWidgetMenu) 1118 1119 # add menu entry to open configuration panel 1120 self.menu.insert_command(0, label='Option Panel', underline=0, 1121 command=self.toggleOptionsPanel) 1122 1123 # register new callback for widget's optionsPanel Apply button 1124 # NOTE: idf.entryByName is at this time not built 1125 for k in self.widget.opPanel.idf: 1126 name = k.get('name', None) 1127 if name and name == 'ApplyButton': 1128 k['command'] = self.optionsPanelApply_cb 1129 elif name and name == 'OKButton': 1130 k['command'] = self.optionsPanelOK_cb 1131 1132 # first, set initial value, else, if we have a min or max, the node 1133 # could run, because such keywords can affect the value 1134 if self.initialValue is not None: 1135 self.set(self.widget.type(self.initialValue), run=0) 1136 1137 # configure without rebuilding to avoid enless loop 1138 apply( self.configure, (False,), widgetcfg)1139 11401142 # call base class configure with rebuild=Flase. If rebuilt is needed 1143 # rebuildDescr will contain w=='rebuild' and rebuildDescr contains 1144 # modified descr 1145 action, rebuildDescr = apply( PortWidget.configure, (self, False), kw) 1146 1147 1148 # handle ownConfigOpts entries 1149 if self.widget is not None: 1150 widgetOpts = {} 1151 for k, v in kw.items(): 1152 if k in self.ownConfigOpts.keys(): 1153 if k in ['width', 'height', 'wheelPad', 'orient']: 1154 action = 'rebuild' 1155 rebuildDescr[k] = v 1156 else: 1157 widgetOpts[k] = v 1158 1159 if len(widgetOpts): 1160 apply( self.widget.configure, (), widgetOpts) 1161 1162 if action=='rebuild' and rebuild: 1163 action, rebuildDescr = self.rebuild(rebuildDescr) 1164 1165 elif action=='resize' and rebuild: 1166 if self.widget and rebuild: # if widget exists 1167 action = None 1168 1169 return action, rebuildDescr1170 11711173 self._setModified(True) 1174 self.widget.setValue(value) 1175 self._newdata = True 1176 if run: 1177 self.scheduleNode()1178 1179 1182 11831185 # register this widget to be modified when opPanel is used 1186 self.widget.opPanel.OK_cb() 1187 self._setModified(True)1188 11891191 # register this widget to be modified when opPanel is used 1192 self.widget.opPanel.Apply_cb() 1193 self._setModified(True)1194 11951197 # rename the options panel title if the node name or port name has 1198 # changed. 1199 self.widget.opPanel.setTitle( 1200 "%s : %s"%(self.port.node.name, self.port.name) ) 1201 self.widget.toggleOptPanel()1202 12031205 cfg = PortWidget.getDescr(self) 1206 for k in self.ownConfigOpts.keys(): 1207 if k == 'type': # type has to be handled separately 1208 _type = self.widget.type 1209 if _type == int: 1210 _type = 'int' 1211 else: 1212 _type = 'float' 1213 if _type != self.ownConfigOpts[k]['defaultValue']: 1214 cfg[k] = _type 1215 continue 1216 val = getattr(self.widget, k) 1217 1218 if val != self.ownConfigOpts[k]['defaultValue']: 1219 cfg[k] = val 1220 1221 return cfg1225 """NetworkEditor wrapper for Dial widget. 1226 Handles all PortWidget arguments and all Dial arguments except for value. 1227 Name: default: 1228 callback None 1229 continuous 1 1230 increment 0.0 1231 lockContinuous 0 1232 lockBMin 0 1233 lockBMax 0 1234 lockBIncrement 0 1235 lockIncrement 0 1236 lockMin 0 1237 lockMax 0 1238 lockOneTurn 0 1239 lockPrecision 0 1240 lockShowLabel 0 1241 lockType 0 1242 lockValue 0 1243 min None 1244 max None 1245 oneTurn 360. 1246 precision 2 1247 showLabel 1 1248 size 50 1249 type 'float' 1250 """ 1251 1252 configOpts = PortWidget.configOpts.copy() 1253 configOpts['initialValue'] = { 1254 'defaultValue':0.0, 'type':'float', 1255 } 1256 1257 ownConfigOpts = { 1258 'callback': { 1259 'defaultValue':None, 'type': 'None', 1260 'description':"???", 1261 }, 1262 'continuous': { 1263 'defaultValue':True, 'type':'boolean', 1264 'description':"", 1265 }, 1266 'increment': { 1267 'defaultValue':0.0, 'type':'float', 1268 'description':"", 1269 }, 1270 'lockContinuous': { 1271 'defaultValue':False, 'type':'boolean', 1272 'description':"", 1273 }, 1274 'lockBMin': { 1275 'defaultValue':False, 'type':'boolean', 1276 'description':"", 1277 }, 1278 'lockBMax': { 1279 'defaultValue':False, 'type':'boolean', 1280 'description':"", 1281 }, 1282 'lockBIncrement': { 1283 'defaultValue':False, 'type':'boolean', 1284 'description':"", 1285 }, 1286 'lockIncrement': { 1287 'defaultValue':False, 'type':'boolean', 1288 'description':"", 1289 }, 1290 'lockMin': { 1291 'defaultValue':False, 'type':'boolean', 1292 'description':"", 1293 }, 1294 'lockMax': { 1295 'defaultValue':False, 'type':'boolean', 1296 'description':"", 1297 }, 1298 'lockOneTurn': { 1299 'defaultValue':False, 'type':'boolean', 1300 'description':"", 1301 }, 1302 'lockPrecision': { 1303 'defaultValue':False, 'type':'boolean', 1304 'description':"", 1305 }, 1306 'lockShowLabel': { 1307 'defaultValue':False, 'type':'boolean', 1308 'description':"", 1309 }, 1310 'lockType': { 1311 'defaultValue':False, 'type':'boolean', 1312 'description':"", 1313 }, 1314 'lockValue': { 1315 'defaultValue':False, 'type':'boolean', 1316 'description':"", 1317 }, 1318 'min': { 1319 'defaultValue':None, 'type':'float', 1320 'description':"", 1321 }, 1322 'max': { 1323 'defaultValue':None, 'type':'float', 1324 'description':"", 1325 }, 1326 'oneTurn': { 1327 'defaultValue':360., 'type':'float', 1328 'description':"", 1329 }, 1330 'precision': { 1331 'defaultValue':2, 'type':'int', 1332 'description':"number of decimals used in label", 1333 }, 1334 'showLabel': { 1335 'defaultValue':True, 'type':'boolean', 1336 'description':"", 1337 }, 1338 'size':{ 1339 'defaultValue': 50, 'min':20, 'max':500, 'type':'int' 1340 }, 1341 'type': { 1342 'defaultValue':'float', 'type':'string', 1343 'validValues': ['float', 'int'], 1344 'description':"", 1345 }, 1346 } 1347 configOpts.update( ownConfigOpts ) 1348 13491351 1352 ## # create all attributes that will not be created by configure because 1353 ## # they do not appear on kw 1354 ## for key in self.ownConfigOpts.keys(): 1355 ## v = kw.get(key, None) 1356 ## if v is None: # self.configure will not do anyting for this key 1357 ## setattr(self, key, self.ownConfigOpts[key]['defaultValue']) 1358 1359 # get all arguments handled by NEThumbweel and not by PortWidget 1360 widgetcfg = {} 1361 for k in self.ownConfigOpts.keys(): 1362 if k in kw: 1363 widgetcfg[k] = kw.pop(k) 1364 1365 # call base class constructor 1366 apply( PortWidget.__init__, ( self, port), kw) 1367 1368