target_tk_app_def.py


directory : D:\2018_py_proj\TkGridGUI\tkgridgui
Line Count : 1071
Line Clip Size : No Clipping

tab_of_notebook_changed  Word MatchCase (1) 805 
1 #!/usr/bin/env python 2 # -*- coding: ascii -*- 3 from __future__ import print_function 4 from __future__ import unicode_literals 5 6 from future import standard_library 7 standard_library.install_aliases() 8 from builtins import range 9 from builtins import object 10 from builtins import str 11 """ 12 Define the target TK Application 13 14 Holds values of all controls on main GUI (grid_gui.GridGUI). 15 Provides Read and Save routines for full GUI definition. 16 17 Has not only widget types and locations from grid_notebook.NotebookGridDes, 18 but also any additional property assignments that were made by EditWin. 19 """ 20 21 import os 22 import sys 23 import binascii 24 25 26 from tkinter import * 27 import tkinter.messagebox 28 from tkinter import Button, Canvas, Checkbutton, Entry, Frame, Label, LabelFrame 29 from tkinter import Listbox, Message, Radiobutton, Spinbox, Text 30 from tkinter import OptionMenu # ttk OptionMenu seems to be broken 31 from tkinter.ttk import Combobox, Progressbar, Separator, Treeview, Style, Notebook 32 33 from tkgridgui.edit_options import get_properties_dict, set_attribute_if_possible 34 from tkgridgui.edit_Dialog import Edit_Properties_Dialog 35 36 from tkgridgui.grid_notebook import ContainerControlsL, intCast 37 from tkgridgui.config_file import ConfigInterface 38 from tkgridgui.comp_tree import CNode, ComponentTree 39 from tkgridgui.preview_win_widgets import PW_Widget, SCROLL_Y_WIDGETS, SCROLL_X_WIDGETS, tkWidgetsD 40 41 # the "width" property of these widgets refers to character width. 42 TEXT_WIDTH_WIDGETS = set(['Button','Checkbutton','Combobox','Entry','Label', #'Listbox', 43 'Radiobutton','Spinbox','Text',"Message", "Menubutton", "Tab"]) 44 45 reqd_variable_typeD = {'Entry':'StringVar', 'OptionMenu':'StringVar', 'Combobox':'StringVar', 46 'Checkbutton':'StringVar', 'Menubutton':'StringVar', 47 'Radiobutton':'StringVar', 'Scale':'StringVar'} 48 49 DEBUG_PRINT = 0 50 51 52 def make_weights_dict_from_str( wt_str ): 53 """return a dict with index=row or col: value=wt""" 54 weightD = {} # index=row: value=wt 55 sL = wt_str.split() # split string by spaces. may get multiple column settings 56 for s in sL: 57 cL = s.split(':') # each setting is of the form col:wt 58 rc = intCast( cL[0] ) 59 wt = intCast( cL[-1] ) 60 weightD[rc] = wt 61 62 return weightD 63 64 def add_entry_to_weight_str( rc_inp, wt_inp, current_str): 65 """ 66 Reset string representation of tab_label row_weights or col_weights ("row:wt" or "col:wt") 67 If wt==0, then must remove any row entry other than 0. 68 If the row is already present with a different wt, must detect it and change wt. 69 """ 70 71 currentD = make_weights_dict_from_str( current_str ) # index=row: value=wt 72 73 currentD[rc_inp] = wt_inp # may overwrite an existing value with 0 74 75 full_str = ' '.join( ['%s:%s'%(rc,wt) for rc,wt in list(currentD.items()) if wt>0 ] ) 76 77 return full_str 78 79 class Component( object ): 80 """Carries info for each widget in the target app.""" 81 82 def __str__(self): 83 return '<%s, %s>'%(self.widget_name, self.tab_label) 84 85 86 def __init__(self, widget_type="Button", widget_name="Button_1", tab_label="Main", 87 row=1, col=1, target_app=None ): 88 89 self.widget_type = widget_type # like Button or Canvas 90 self.widget_name = widget_name # like Button_1 or Canvas_23 91 self.tab_label = tab_label # tab names in NotebookGridDes ("Main" is main Frame in app) 92 self.row = row 93 self.col = col 94 self.target_app = target_app # allows call-back to TargetTkAppDef 95 96 self.default_tkOptionD = {} # holds default widget options when 1st created (used to detect changes to options) 97 98 self.user_tkOptionD = {} # holds tk options set by user. (i.e. not same as default_tkOptionD) 99 100 self.user_tkOptionD['docstring'] = '' 101 102 if widget_type != 'Tab': 103 self.user_tkOptionD['sticky'] = '' 104 self.user_tkOptionD['columnspan'] = '' 105 self.user_tkOptionD['rowspan'] = '' 106 107 # Containers might have row/col weights set 108 if self.widget_type in ContainerControlsL: 109 self.user_tkOptionD['row_weights'] = '' 110 self.user_tkOptionD['col_weights'] = '' 111 112 113 # Some widgets have special values that need to be set 114 if widget_type == "Spinbox": 115 self.user_tkOptionD['from_'] = 1 116 self.user_tkOptionD['to'] = 10 117 elif widget_type == "Radiobutton": 118 self.user_tkOptionD['value'] = widget_name.split('_')[-1] # should be the Radiobutton number 119 elif widget_type == "Combobox": 120 self.user_tkOptionD['values'] = 'Mine Yours Ours' 121 elif widget_type == "Separator": 122 self.user_tkOptionD['sticky'] = 'ew' # Separator is invisible w/o sticky 123 elif widget_type == 'Text': 124 self.user_tkOptionD['scrolly'] = 'yes' 125 elif widget_type in SCROLL_Y_WIDGETS: 126 self.user_tkOptionD['scrolly'] = 'no' # only Text gets default 'yes' 127 128 if widget_type in SCROLL_X_WIDGETS: 129 self.user_tkOptionD['scrollx'] = 'no' 130 131 132 # set a "reasonable" default width and height 133 if widget_type in TEXT_WIDTH_WIDGETS: 134 self.width_type = "text" 135 self.user_tkOptionD['text'] = widget_name 136 137 if widget_type == 'Text': 138 self.user_tkOptionD['width'] = 40 139 self.user_tkOptionD['height'] = 12 140 elif widget_type == "Message": 141 self.user_tkOptionD['width'] = 55 # percent of message length 142 elif widget_type == "Tab": 143 pass 144 else: 145 self.user_tkOptionD['width'] = 15 146 147 elif widget_type == "Listbox": 148 self.user_tkOptionD['width'] = 18 149 self.user_tkOptionD['height'] = 12 150 151 elif widget_type == "OptionMenu": 152 self.user_tkOptionD['width'] = 20 153 self.user_tkOptionD['height'] = 2 154 155 elif widget_type == "Menubutton": 156 self.user_tkOptionD['width'] = 20 157 self.user_tkOptionD['height'] = 2 158 159 elif widget_type == "Treeview": 160 pass 161 162 elif widget_type == "Scale": 163 pass 164 165 elif widget_type == "Notebook": 166 self.width_type = "pixel" 167 self.user_tkOptionD['width'] = 400 168 self.user_tkOptionD['height'] = 300 169 170 # Notebook Tab labels separated by \n 171 self.user_tkOptionD['tab_labels'] = 'Mickey\nGoofy\nPopeye' 172 173 self.tab_nameL = [] # tuples of Tab name and label, e.g. (row, col, Tab_1, text) 174 175 else: 176 self.width_type = "pixel" 177 self.user_tkOptionD['width'] = 60 178 self.user_tkOptionD['height'] = 50 179 180 self.pw_widget = None # use maybe_make_widget to create 181 self.disp_frame = None 182 183 # may need a tk variable for PreviewWin 184 tkvar_type = reqd_variable_typeD.get( self.widget_type, '' ) # e.g. StringVar 185 self.tkvar = None 186 self.tkvar_list = None # Menubutton has a list of tk variables 187 if tkvar_type: 188 try: 189 if tkvar_type == "StringVar": 190 self.tkvar = StringVar() 191 elif tkvar_type == "IntVar": 192 self.tkvar = IntVar() 193 elif tkvar_type == "DoubleVar": 194 self.tkvar = DoubleVar() 195 else: 196 print("ERROR... do not recognize tk variable type = ", tkvar_type) 197 198 if widget_type=="Menubutton": 199 self.tkvar_list = [] 200 for _ in range(3): 201 self.tkvar_list.append( StringVar() ) 202 except: 203 print("WARNING... Failed to make ",tkvar_type," for ",self.widget_name) 204 205 206 def get_docstring(self): 207 """For documenting generated source code.""" 208 209 loc_str = ' at %s(%s,%s)'%(self.tab_label, self.row, self.col) 210 211 # if the user supplied a docstring, use it. 212 if self.user_tkOptionD['docstring']: 213 return '%12s: '%self.widget_type + self.user_tkOptionD['docstring'] + ' :' + loc_str 214 215 # --------------------- 216 text = self.user_tkOptionD.get('text', '') 217 if text == self.widget_name: 218 text = '' 219 if text: 220 return '%12s: '%self.widget_type + text + ' :' + loc_str 221 222 # --------------------- 223 value = self.user_tkOptionD.get('value', '') 224 if value: 225 return '%12s: '%self.widget_type + value + ' :' + loc_str 226 227 # --------------------- 228 values = self.user_tkOptionD.get('values', '') 229 if values: 230 return '%12s: '%self.widget_type + values + ' :' + loc_str 231 232 # --------------------- 233 from_ = '%s'%self.user_tkOptionD.get('from_', '') 234 if from_: 235 return '%12s: '%self.widget_type + from_ + ' to ' + '%s'%self.user_tkOptionD.get('to', '') + ' :' + loc_str 236 237 # --------------------- 238 return '%12s: '%self.widget_type + loc_str 239 240 def highlight_pw_widget(self): 241 242 if self.widget_type == "Tab": 243 notebook_name = self.tab_label 244 nb_obj = self.target_app.compObjD[ notebook_name ] 245 if nb_obj.pw_widget is not None: 246 nb_obj.pw_widget.pw_highlight_widget() 247 248 self.target_app.grid_notebook.grid_gui.select_preview_tab( self.widget_name ) 249 250 elif self.pw_widget is not None: 251 self.pw_widget.pw_highlight_widget() 252 253 254 def highlight_grid_widget(self): 255 if self.target_app.grid_notebook is not None: 256 self.target_app.grid_notebook.highlight_grid_widget( self.widget_name ) 257 258 def highlight_widget(self): # highlight BOTH PreviewWin and grid_notebook 259 self.highlight_pw_widget() 260 self.highlight_grid_widget() 261 262 def unhighlight_pw_widget(self): 263 if self.widget_type == "Tab": 264 notebook_name = self.tab_label 265 nb_obj = self.target_app.compObjD[ notebook_name ] 266 if nb_obj.pw_widget is not None: 267 nb_obj.pw_widget.pw_unhighlight_widget() 268 269 270 elif self.pw_widget is not None: 271 self.pw_widget.pw_unhighlight_widget() 272 273 def unhighlight_grid_widget(self): 274 if self.target_app.grid_notebook is not None: 275 self.target_app.grid_notebook.unhighlight_grid_widget( self.widget_name ) 276 277 def unhighlight_widget(self): # un-highlight BOTH PreviewWin and grid_notebook 278 self.unhighlight_pw_widget() 279 self.unhighlight_grid_widget() 280 281 def widget_has_moved(self, tab_label="Main", row=1, col=1): 282 """Test for widget having been moved""" 283 if (tab_label, row, col) == (self.tab_label, self.row, self.col): 284 return False 285 else: 286 return True 287 288 def reset_location(self, tab_label="Main", row=1, col=1): 289 self.tab_label = tab_label # tab names in NotebookGridDes ("Main" is main Frame in app) 290 self.row = row 291 self.col = col 292 293 def maybe_make_widget(self, disp_frame): 294 """If not already created, create the pw_widget""" 295 296 # Tab should never get here. 297 if self.pw_widget is None: 298 299 self.disp_frame = disp_frame 300 301 # disp_frame is the native_widget of pw_widget's parent 302 self.pw_widget = PW_Widget( disp_frame, self ) 303 304 self.set_widget_tk_properties() 305 306 #if self.pw_widget is not None: bind preview widget 307 self.pw_widget.native_widget.bind("<Button-3>", self.Widget_Right_Click) 308 309 return True 310 else: 311 return False 312 313 def Widget_Right_Click(self, __event): # event not used 314 #print( "Widget_Right_Click on ",self.widget_name ) 315 316 # set tab of grid_notebook to this widget's tab_label 317 nb_obj = self.target_app.grid_notebook 318 nb_obj.set_current_tab_by_label( self.tab_label ) 319 320 self.highlight_widget() 321 322 # put all pw_widget properties into dialogOptionsD for editing 323 dialogOptionsD = {} 324 for key,val in list(self.default_tkOptionD.items()): 325 dialogOptionsD[key] = (val, "def.") 326 327 for key,val in list(self.user_tkOptionD.items()): # user_tkOptionD holds tk options set by user. (i.e. not same as default_tkOptionD) 328 dialogOptionsD[key] = (val, "USER VALUE") 329 330 dialogOptionsD['child_widget_list'] = self.target_app.get_names_of_containers_widgets( self.widget_name ) 331 332 # get grid_notebook label object to highlight it. 333 (tab_label, rtarg, col_target, placementWidgetType, label) = \ 334 self.target_app.grid_notebook.label_obj_from_nameD[ self.widget_name ] 335 336 self.target_app.grid_notebook.current_label_being_edited = label # used as flag in onGridBoxLeave to maintain hightlight 337 338 dialog = Edit_Properties_Dialog(self.target_app.grid_notebook.master, self.widget_name, dialogOptionsD=dialogOptionsD) 339 340 self.target_app.grid_notebook.current_label_being_edited = None # used as flag in onGridBoxLeave to maintain hightlight 341 342 self.unhighlight_widget() 343 344 if dialog.result is not None: 345 # check for delete widget command 346 if ("DeleteWidget" in dialog.result) and (dialog.result["DeleteWidget"]=="yes"): 347 348 if self.widget_type in ContainerControlsL: 349 350 self.target_app.removeContainerByName( self.widget_name ) 351 else: 352 353 #print("Deleting ",self.widget_name) 354 self.target_app.grid_notebook.delete_widget_by_name( self.widget_name ) 355 356 self.target_app.delComponentByName( self.widget_name ) 357 358 else: 359 #preview_comp = self.target_app.compObjD[ self.widget_name ] 360 #print('target_app dialog.result =',dialog.result) 361 self.user_tkOptionD.update( dialog.result ) 362 self.set_user_properties() 363 364 #self.target_app.grid_notebook.grid_gui.refresh_preview_win() 365 #self.target_app.PreviewWin.update_idletasks() 366 #self.target_app.PreviewWin.update() # <-- update idle tasks ?? 367 #self.target_app.PreviewWin.setActive() 368 369 # need to repostion Canvas Label 370 if self.widget_type == "Canvas": 371 self.pw_widget.native_widget.delete( 'all' ) 372 w = int(self.user_tkOptionD['width']) 373 h = int(self.user_tkOptionD['height']) 374 self.pw_widget.native_widget.create_text(w//2,h//2, text=self.widget_name, 375 fill="black", width=w, anchor='center') 376 377 if self.target_app.PreviewWin is not None: 378 self.target_app.PreviewWin.maybe_resize_preview_win() 379 380 def destroy_preview_widget(self): 381 """destroy pw_widget if present""" 382 if self.pw_widget is not None: 383 self.pw_widget.destroy() 384 self.pw_widget = None 385 self.disp_frame = None 386 387 def set_widget_tk_properties(self): 388 389 if self.pw_widget is None: 390 return 391 392 #if self.widget_type == 'Tab': 393 # return 394 395 #... now done by PW_Widget 396 # get the default property options for native_widget when 1st created. 397 #self.default_tkOptionD = get_properties_dict( self.pw_widget ) 398 399 # set any user options (at this point only width and height) 400 #for key, val in self.user_tkOptionD.items(): 401 # set_attribute_if_possible(self.pw_widget, key, val) 402 self.pw_widget.set_native_widget_attr() 403 404 # set text of widgets with insert method 405 try: 406 self.pw_widget.native_widget.delete(0, END) 407 except: 408 pass 409 try: 410 self.pw_widget.native_widget.insert(END, self.widget_name) 411 except: 412 pass 413 414 def set_user_properties(self): 415 416 self.pw_widget.set_native_widget_attr() 417 418 # set any user options (at this point only width and height) 419 #for key, val in self.user_tkOptionD.items(): 420 # set_attribute_if_possible(self.pw_widget.native_widget, key, val) 421 422 #self.target_app.PreviewWin.update_idletasks() 423 424 def get_property(self, attr_name): 425 if attr_name in self.user_tkOptionD: 426 return self.user_tkOptionD[ attr_name ] 427 428 if attr_name in self.default_tkOptionD: 429 return self.default_tkOptionD[ attr_name ] 430 return None 431 432 def set_a_row_weight(self, row_inp, wt_inp): 433 """container objects might have weights on rows and columns""" 434 self.user_tkOptionD['row_weights'] = add_entry_to_weight_str( row_inp, wt_inp, self.user_tkOptionD['row_weights']) 435 436 def set_a_col_weight(self, col_inp, wt_inp): 437 """container objects might have weights on rows and columns""" 438 self.user_tkOptionD['col_weights'] = add_entry_to_weight_str( col_inp, wt_inp, self.user_tkOptionD['col_weights']) 439 440 class TargetTkAppDef( object ): 441 442 def __init__(self, name='myApp', PreviewWin=None, grid_notebook=None ): 443 444 self.name = name 445 446 self.PreviewWin = PreviewWin # display for widgets 447 self.grid_notebook = grid_notebook 448 self.init_properties() 449 450 # if crc_reference does not match a call to get_model_crc, then the model has changed. 451 self.crc_reference = self.get_model_crc() 452 453 def init_properties(self): 454 # form and widgets have non-tk options here (e.g. name, x, y) 455 self.app_attrD = {'name':self.name,'x':350, 'y':20, 'width':300, 'height':300, 456 'guitype':'main', 'hideokbutton':'no', 457 'hasmenu':'no', 458 'menu':'File\n New\n Open\n Save\n\n Exit\nHelp\n Sounds\n Moo\n Meow', 459 'add_menu_ctrl_keys':'yes', 460 'hasstatusbar':'no','hasstddialmess':'no', 'hasstddialfile':'no', 461 'hasstddialcolor':'no', 'hasstdalarm':'no', 'resizable':'yes', 462 'row_weights':'', 'col_weights':''} 463 464 self.tkOptionD = {} # form and widgets have Tk options here (e.g. width, background, etc.) 465 466 # compObjD has attributes like row, col, widget_name, tab_label, etc. 467 self.compObjD = {} # index=widget_name: value=Component object 468 469 self.tab_ownerD = {} # index=Tab name: value=Notebook name 470 471 def reset_crc_reference(self): 472 """When a model is first read, or after it is saved, reset the crc_reference.""" 473 self.crc_reference = self.get_model_crc() 474 475 def model_has_changed(self): 476 """Returns True if the model has changed (i.e. CRC no longer matches.)""" 477 return self.crc_reference != self.get_model_crc() 478 479 def get_model_crc(self): 480 """Use a calculated cylic redundancy check (CRC) to detect changes to model.""" 481 sL = [] # will concatenate a list of strings for final calc. 482 483 for key in sorted(dir(self), key=lambda s: s.lower()): 484 if key.startswith('__') or key in ('PreviewWin','grid_notebook','crc_reference'): 485 pass 486 else: 487 val = getattr(self, key) 488 if type(val) in (int, dict, list, float, str, type(None)): 489 #print(key,'self crc') 490 sL.append( repr(val) ) 491 492 for widget_name in sorted(list(self.compObjD.keys()), key=lambda s: s.lower()): 493 c = self.compObjD[ widget_name ] 494 495 for key in sorted(dir(c), key=lambda s: s.lower()): 496 if key.startswith('__') or key in ('PreviewWin','grid_notebook'): 497 pass 498 else: 499 val = getattr(c, key) 500 if type(val) in (int, dict, list, float, str, type(None)): 501 #print(key,'c crc') 502 sL.append( repr(val) ) 503 # create a single string 504 s = ''.join(sL) 505 #if sys.version_info < (3,): 506 return binascii.crc32( binascii.a2b_qp(s) ) & 0xffffffff # to get same value for all python platforms, use & 0xffffffff 507 #else: 508 # return binascii.crc32( binascii.a2b_qp(s) ) & 0xffffffff # to get same value for all python platforms, use & 0xffffffff 509 510 def change_notebook_tab_label(self, tab_name_inp, tab_label_inp): 511 notebook_name = self.tab_ownerD[ tab_name_inp ] 512 nb_obj = self.compObjD[ notebook_name ] 513 514 for itab, (row, col, tab_name, tab_label) in enumerate( nb_obj.tab_nameL ): 515 if tab_name == tab_name_inp: 516 if tab_label_inp: 517 tab_label = tab_label_inp 518 nb_obj.tab_nameL[ itab ] = (row, col, tab_name_inp, tab_label) 519 break 520 521 # arrange labels in pasted order. sorted by (row, col) 522 nb_obj.tab_nameL = sorted( nb_obj.tab_nameL ) 523 nb_obj.user_tkOptionD['tab_labels'] = '\n'.join( [t[3] for t in nb_obj.tab_nameL] ) 524 525 526 def arrange_notebook_tabs(self, tab_name_inp, row_inp, col_inp): 527 528 notebook_name = self.tab_ownerD[ tab_name_inp ] 529 nb_obj = self.compObjD[ notebook_name ] 530 531 for itab, (row, col, tab_name, tab_label) in enumerate( nb_obj.tab_nameL ): 532 if tab_name == tab_name_inp: 533 nb_obj.tab_nameL[ itab ] = (row_inp, col_inp, tab_name_inp, tab_label) 534 break 535 536 # arrange labels in pasted order. sorted by (row, col) 537 nb_obj.tab_nameL = sorted( nb_obj.tab_nameL ) 538 nb_obj.user_tkOptionD['tab_labels'] = '\n'.join( [t[3] for t in nb_obj.tab_nameL] ) 539 540 #print('tab_nameL',nb_obj.tab_nameL) 541 542 if nb_obj.pw_widget: 543 nb_obj.pw_widget.destroy() 544 nb_obj.pw_widget = None 545 #print('Eliminated ',notebook_name,'Preview Object') 546 547 def add_tab_to_notebook(self, row, col, tab_name, notebook_name): 548 self.tab_ownerD[ tab_name ] = notebook_name 549 550 #print('compObjD.keys() =',self.compObjD.keys()) 551 nb_obj = self.compObjD[ notebook_name ] 552 553 nb_obj.tab_nameL.append( (row, col, tab_name, tab_name) ) # make label same as name for now 554 #print(notebook_name, ' tab_nameL= ', nb_obj.tab_nameL) 555 556 # arrange labels in pasted order. sorted by (row, col) 557 self.arrange_notebook_tabs( tab_name, row, col ) 558 559 560 def get_tab_native_widget(self, tab_name ): 561 notebook_name = self.tab_ownerD[ tab_name ] 562 nb_obj = self.compObjD[ notebook_name ] 563 #print('.......... nb_obj =', nb_obj) 564 565 n = 0 566 #print(' nb_obj.tab_nameL=',nb_obj.tab_nameL) 567 for i, (row, col, t_name, tab_label) in enumerate( nb_obj.tab_nameL ): 568 #print('In get_tab_native_widget, tab_name=',tab_name,' t_name=',t_name) 569 if t_name == tab_name: 570 n = i 571 break 572 573 #print('.......... In get_tab_native_widget, tab_name=',tab_name,' n=',n) 574 return nb_obj.pw_widget.tab_frameL[ n ] 575 576 577 def reinitialize(self): 578 """Delete everything and start over""" 579 self.destroy_all_preview_widgets() 580 self.del_all_components() 581 self.init_properties() 582 583 def get_a_full_desc_of_weights(self): 584 """ 585 Returns a two dict of non-zero weights, a row dict and a column dict. 586 The indeces are of the form (tab_label, row) and (tab_label, col) 587 The values are the wt value 588 """ 589 full_rowD = {} # index=(tab_label,row_target), value=wt 590 full_colD = {} # index=(tab_label,col_target), value=wt 591 # first add Main entries 592 rowD = make_weights_dict_from_str( self.app_attrD['row_weights'] ) 593 for row,wt in list(rowD.items()): 594 full_rowD[ ("Main", row) ] = wt 595 596 colD = make_weights_dict_from_str( self.app_attrD['col_weights'] ) 597 for col,wt in list(colD.items()): 598 full_colD[ ("Main", col) ] = wt 599 600 #print("...NOTICE...Need to iterate container widgets and add weights") 601 for widget_name, c in list(self.compObjD.items()): 602 if c.widget_type in ContainerControlsL: 603 604 rowD = make_weights_dict_from_str( c.user_tkOptionD['row_weights'] ) 605 for row,wt in list(rowD.items()): 606 full_rowD[ (c.widget_name, row) ] = wt 607 608 colD = make_weights_dict_from_str( c.user_tkOptionD['col_weights'] ) 609 for col,wt in list(colD.items()): 610 full_colD[ (c.widget_name, col) ] = wt 611 612 #print('full_rowD = ',full_rowD) 613 #print('full_colD = ',full_colD) 614 return full_rowD, full_colD 615 616 def set_a_row_weight(self, tab_label, row_inp, wt_inp): 617 if tab_label=="Main": 618 self.app_attrD['row_weights'] = add_entry_to_weight_str( row_inp, wt_inp, self.app_attrD['row_weights']) 619 else: 620 c = self.compObjD[ tab_label ] 621 c.user_tkOptionD['row_weights'] = add_entry_to_weight_str( row_inp, wt_inp, c.user_tkOptionD['row_weights']) 622 623 def set_a_col_weight(self, tab_label, col_inp, wt_inp): 624 if tab_label=="Main": 625 self.app_attrD['col_weights'] = add_entry_to_weight_str( col_inp, wt_inp, self.app_attrD['col_weights']) 626 else: 627 c = self.compObjD[ tab_label ] 628 c.user_tkOptionD['col_weights'] = add_entry_to_weight_str( col_inp, wt_inp, c.user_tkOptionD['col_weights']) 629 630 def setSpecialOption(self, name, value): 631 self.app_attrD[name] = value 632 633 def getSpecialOption(self, name ): 634 return self.app_attrD.get(name, '') 635 636 def set_PreviewWin(self, PreviewWin): 637 self.PreviewWin = PreviewWin 638 639 def set_Notebook( self, grid_notebook ): 640 self.grid_notebook = grid_notebook 641 642 643 def show_preview(self): 644 """Loops over all components, adds them to PreviewWin if they are new or have changed.""" 645 646 if self.PreviewWin is None: 647 return 648 649 ct = ComponentTree() 650 for widget_name, c in list(self.compObjD.items()): 651 ct.add_node( CNode(widget_name, c.tab_label, c) ) 652 653 containerD = {"Main":self.PreviewWin.prevFrame} # index=tab_label: value=tk parent object 654 655 cnodeL = ct.get_ordered_components() 656 657 # --------- set up info for any Tab or Notebook Component objects ----------- 658 # Notebook Tab labels separated by \n 659 #self.user_tkOptionD['tab_labels'] = 'Mickey\nGoofy\nPopeye' 660 #self.tab_nameL = [] # tuples of Tab name and label, e.g. (row, col, Tab_1, text) 661 662 nb_tab_nameLD = {} # index=notebook_name: value=tab_nameL 663 for cn in cnodeL: 664 c = cn.component 665 if c.widget_type == 'Notebook': 666 nb_tab_nameLD[c.widget_name] = [] 667 elif c.widget_type == 'Tab': 668 nb_tab_nameLD[c.tab_label].append( (c.row, c.col, c.widget_name, c.user_tkOptionD['text']) ) 669 nb_tab_nameLD[c.tab_label] = sorted( nb_tab_nameLD[c.tab_label] ) 670 671 672 # --------- make preview components ----------- 673 for cn in cnodeL: 674 c = cn.component 675 #print('In show_preview, widget_name =',c.widget_name, ' tab_label=',c.tab_label, 676 # '\n ... containerD.keys()=',containerD.keys()) 677 678 if c.widget_type == 'Tab': 679 self.tab_ownerD[ c.widget_name ] = c.tab_label 680 containerD[ c.widget_name ] = self.get_tab_native_widget( c.widget_name ) 681 else: 682 if c.widget_type == 'Notebook': 683 c.tab_nameL = nb_tab_nameLD[ c.widget_name ] 684 685 parent_component = self.compObjD.get( c.tab_label , None ) 686 # parent_name, parent Component, parent container widget 687 c.maybe_make_widget( containerD[c.tab_label] ) # returns True if had to make it. 688 689 if c.pw_widget: 690 self.PreviewWin.add_widget(c.row, c.col, c.pw_widget) 691 containerD[ c.widget_name ] = c.pw_widget.native_widget # put possible parent widgets into dictionary 692 693 # set any column or row weights 694 if self.grid_notebook is not None: 695 #print("Setting columnconfigure and rowconfigure") 696 #print(' self.app_attrD["col_weights"] = ', self.app_attrD["col_weights"]) 697 self.grid_notebook.set_row_column_weights_from_target_app() 698 # ------------------------------------------- 699 # rowD and colD: index=(tab_label,row_target), value=wt 700 rowD, colD = self.get_a_full_desc_of_weights() 701 702 for (tab_label, row_target),wt in list(rowD.items()): 703 parent = containerD.get( tab_label, None ) 704 if parent is not None: 705 parent.rowconfigure(row_target, weight=wt) 706 707 708 for (tab_label, col_target),wt in list(colD.items()): 709 parent = containerD.get( tab_label, None ) 710 if parent is not None: 711 parent.columnconfigure(col_target, weight=wt) 712 713 # ----------------------------------------- 714 #weightD = make_weights_dict_from_str( self.app_attrD["col_weights"] ) 715 #for col,wt in weightD.items(): 716 # #print('col=%s, wt=%s'%(col,wt)) 717 # parent = containerD.get( "Main", None ) 718 # if parent is not None: 719 # parent.columnconfigure(col, weight=wt) 720 # #print('executed col=%s, wt=%s'%(col,wt)) 721 # 722 #weightD = make_weights_dict_from_str( self.app_attrD["row_weights"] ) 723 #for row,wt in weightD.items(): 724 # parent = containerD.get( "Main", None ) 725 # #print('row=%s, wt=%s'%(row,wt)) 726 # if parent is not None: 727 # parent.rowconfigure(row, weight=wt) 728 # #print('executed row=%s, wt=%s'%(row,wt)) 729 730 731 def destroy_all_preview_widgets(self): 732 """destroy pw_widget if present""" 733 for c in list(self.compObjD.values()): 734 if c.pw_widget is not None: 735 c.destroy_preview_widget() 736 #print( 'Destroyed all preview widgets' ) 737 738 def del_all_components(self): 739 for c in list(self.compObjD.values()): 740 c.destroy_preview_widget() 741 self.compObjD = {} 742 #print( 'Deleted all preview widgets' ) 743 744 def delComponentByName(self, widget_name): 745 '''MUST have "widget_name" value in compObjD dictionary''' 746 747 if widget_name in self.compObjD: 748 c = self.compObjD[ widget_name ] 749 c.destroy_preview_widget() 750 del self.compObjD[ widget_name ] 751 #print( 'Deleted "%s" from form'%widget_name ) 752 753 def maybe_add_component(self, widget_type="Button", widget_name="Button_1", tab_label="Main", 754 row=1, col=1): 755 """If the component is not already in app, add it.""" 756 757 if widget_name in self.compObjD: 758 c = self.compObjD[ widget_name ] 759 760 if c.widget_has_moved( tab_label=tab_label, row=row, col=col ): 761 c.destroy_preview_widget() 762 c.reset_location( tab_label=tab_label, row=row, col=col ) 763 else: 764 c = Component( widget_type=widget_type, widget_name=widget_name, tab_label=tab_label, 765 row=row, col=col, target_app=self ) 766 self.compObjD[ widget_name ] = c 767 768 # # duplicating a widget, so copy over all the user_tkOptionD properties 769 if self.grid_notebook is not None: 770 if self.grid_notebook.dup_source_widget_name: 771 c_src = self.compObjD[ self.grid_notebook.dup_source_widget_name ] 772 773 for key,val in list(c_src.user_tkOptionD.items()): 774 # set all attr to source widget values, unless it's the dup_source_widget_name 775 if val != self.grid_notebook.dup_source_widget_name: 776 c.user_tkOptionD[key] = val 777 778 if widget_type == "Notebook": 779 c.grid_notebook = self.grid_notebook 780 781 def removeContainerByName(self, container_name): 782 """ 783 container_name is a widget_name to be removed. 784 The approach is to collect the names of remaining components and rebuild the target_app. 785 """ 786 del_child_nameL = self.get_names_of_containers_widgets( container_name ) 787 del_child_nameL.append( container_name ) 788 789 # if container is a Tab, then need to correct its Notebook tab_labels attribute. 790 if container_name.startswith('Tab_'): 791 c_tab = self.compObjD[ container_name ] 792 notebook_name = c_tab.tab_label 793 c_nb = self.compObjD[ notebook_name ] 794 sL = c_nb.user_tkOptionD['tab_labels'].split('\n') 795 sL = [s for s in sL if s != container_name] 796 c_nb.user_tkOptionD['tab_labels'] = '\n'.join(sL) 797 798 # get list of components in add-order. 799 cnodeL = self.get_ordered_components() 800 801 # reinitialize, but save app_attr values 802 save_tkOptionD = self.tkOptionD.copy() 803 self.reinitialize() 804 self.grid_notebook.initialize_NotebookGridDes() 805 self.grid_notebook.notebook.bind("<<NotebookTabChanged>>", self.grid_notebook.grid_gui.tab_of_notebook_changed) 806 807 self.tkOptionD = save_tkOptionD 808 809 # add back in all the undeleted components 810 #print('del_child_nameL =', del_child_nameL) 811 for cn in cnodeL: 812 c = cn.component 813 if c.widget_name not in del_child_nameL: 814 #print('adding back in', c.widget_name) 815 816 817 self.maybe_add_component( widget_type=c.widget_type, widget_name=c.widget_name, 818 tab_label=c.tab_label, row=c.row, col=c.col) 819 820 c_new = self.compObjD[ c.widget_name ] 821 #print('c_new =', c_new) 822 823 for key, val in list(c.user_tkOptionD.items()): 824 if key == 'tab_labels': 825 val = val.replace('\\n', '\n') 826 c_new.user_tkOptionD[key] = val 827 828 elif key not in ['widget_type', 'tab_label', 'row', 'col']: 829 c_new.user_tkOptionD[key] = val 830 831 # put the widgets on the grid_notebook 832 widgetL = [] 833 for cn in cnodeL: 834 c = cn.component 835 if c.widget_name not in del_child_nameL: 836 widgetL.append( (c.widget_type, c.widget_name, c.tab_label, c.row, c.col) ) 837 838 self.grid_notebook.set_complete_list_of_widgets( widgetL ) 839 840 self.grid_notebook.repaint_all_labels() 841 842 self.grid_notebook.grid_gui.refresh_preview_win() 843 self.grid_notebook.grid_gui.restore_black_listbox() 844 845 def get_ordered_components(self): 846 """Return a complete list of all components in add-order.""" 847 848 ct = ComponentTree() 849 for widget_name, c in list(self.compObjD.items()): 850 ct.add_node( CNode(widget_name, c.tab_label, c) ) 851 852 cnodeL = ct.get_ordered_components() 853 return cnodeL 854 855 def get_names_of_containers_widgets(self, container_name): 856 """Return the widget_name value for all widgets in container_name.""" 857 858 c_cont = self.compObjD[ container_name ] 859 if c_cont.widget_type not in ContainerControlsL: 860 return [] # if not a container, simply return an empty list. 861 862 ct = ComponentTree() 863 for widget_name, c in list(self.compObjD.items()): 864 ct.add_node( CNode(widget_name, c.tab_label, c) ) 865 866 cnodeL = ct.get_ordered_components() 867 868 child_tab_set = set([container_name]) 869 child_name_set = set() 870 num_kids = 0 871 872 while True: 873 for cn in cnodeL: 874 c = cn.component 875 876 if c.tab_label in child_tab_set: 877 child_name_set.add( c.widget_name ) 878 879 if c.widget_type in ContainerControlsL: 880 child_tab_set.add( c.widget_name ) 881 882 if len(child_name_set) == num_kids: 883 break 884 num_kids = len(child_name_set) 885 886 return sorted( list(child_name_set) ) 887 888 889 def readAppDefFile(self, readPathName): 890 891 def maybe_int( s ): 892 try: 893 result = int(s.strip()) 894 except: 895 result = s 896 return result 897 898 if readPathName: 899 self.reinitialize() 900 901 fullpath = os.path.abspath( readPathName ) 902 fname = os.path.basename( fullpath ) 903 name = fname.split('.')[0] 904 suffix = fname.split('.')[1] 905 if suffix.lower() != 'def': 906 print( 'WARNING... Expected *.def file, got',fname ) 907 908 # make an instance of ConfigInterface 909 cf = ConfigInterface( config_filename=fullpath ) 910 911 infoD = cf.get_dictionary() # includes all sections 912 913 for sec_name in cf.get_sectionL(): 914 #print("Section:",sec_name) 915 #print(infoD[sec_name]) 916 #print() 917 918 D = infoD[sec_name] 919 for key,val in list(D.items()): 920 D[key] = maybe_int( val ) 921 922 if sec_name=='app_attr': 923 for key,val in list(D.items()): 924 if key in ('menu', 'tablabels'): 925 val = val.replace('\\n', '\n') 926 val = val.replace('\\t', ' ') 927 928 if key in self.app_attrD: 929 self.app_attrD[key] = val 930 else: 931 self.tkOptionD[key] = val 932 933 else: # a component 934 self.maybe_add_component( widget_type=D['widget_type'], widget_name=sec_name, 935 tab_label=D['tab_label'], row=D['row'], col=D['col']) 936 937 c = self.compObjD[ sec_name ] 938 for key, val in list(D.items()): 939 if key == 'tab_labels': 940 val = val.replace('\\n', '\n') 941 c.user_tkOptionD[key] = val 942 #print('Made tab_labels =', val.replace('\n',' | ')) 943 944 elif key not in ['widget_type', 'tab_label', 'row', 'col']: 945 c.user_tkOptionD[key] = val 946 947 948 if self.grid_notebook is not None: 949 self.grid_notebook.set_row_column_weights_from_target_app() 950 #print('self.app_attrD["col_weights"] = ', self.app_attrD["col_weights"]) 951 952 self.reset_crc_reference() # set to current value of crc_reference 953 954 def saveAppDefFile(self, savePathName=''): 955 956 if savePathName: 957 if 1:#try: 958 # make sure we have full path 959 fullpath = os.path.abspath(savePathName) 960 fname = os.path.basename( fullpath ) 961 name = fname.split('.')[0] 962 suffix = fname.split('.')[1] 963 if suffix.lower() != 'def': 964 print( 'ERROR... illegal file name',fname ) 965 return 0 # indicates an error 966 967 self.name = name 968 curdir = os.path.dirname( fullpath ) 969 fName = os.path.normpath(curdir +'/'+ name + '.def') # def file name 970 971 # see if file already exists, if so then move it to backup 972 if os.path.exists( fName ): 973 backup_fname = name + '.def.bak' 974 full_backup = os.path.abspath( backup_fname ) 975 976 if os.path.exists( full_backup ): 977 os.remove( full_backup ) 978 print('Deleted Previous Backup:', backup_fname) 979 980 print(" Created Backup File:",backup_fname) 981 os.rename( fName, full_backup) 982 983 984 if DEBUG_PRINT: print( 'app definition saved to',fName ) 985 self.app_attrD['name'] = name 986 987 # set to actual width and height of PreviewWin 988 #print("TargetTkAppDef PreviewWin set to: ", self.PreviewWin) 989 990 if self.PreviewWin is not None: 991 self.app_attrD['width'] = self.PreviewWin.winfo_width() 992 self.app_attrD['height'] = self.PreviewWin.winfo_height() 993 self.app_attrD['x'] = self.PreviewWin.winfo_x() 994 self.app_attrD['y'] = self.PreviewWin.winfo_y() 995 else: 996 print('WARNING... no PreviewWin for Save Info.') 997 998 # make an instance of ConfigInterface 999 cf = ConfigInterface( config_filename=fName ) 1000 1001 for key, val in list(self.app_attrD.items()): 1002 if key == 'menu': 1003 val = val.replace('\n','\\n') 1004 val = val.replace('\t','\\t') 1005 1006 cf.set('app_attr', key, val) 1007 1008 keyL = sorted( self.compObjD.keys() ) 1009 for key in keyL: 1010 c = self.compObjD[ key ] 1011 1012 # save attributes not in user_tkOptionD 1013 for a in ['widget_type', 'tab_label', 'row', 'col']: 1014 cf.set(c.widget_name, a, getattr(c, a)) 1015 1016 # save user_tkOptionD 1017 for a, val in list(c.user_tkOptionD.items()): 1018 if a=='sticky': # only save sticky, if user input it. 1019 if val: 1020 cf.set(c.widget_name, a, val) 1021 elif a == 'tab_labels': 1022 val = val.replace('\n','\\n') 1023 val = val.replace('\t','\\t') 1024 cf.set(c.widget_name, a, val) 1025 #print('Saving tab_labels as: ',val) 1026 else: 1027 cf.set(c.widget_name, a, val) 1028 1029 1030 cf.save_file() 1031 1032 self.reset_crc_reference() # set to current value of crc_reference 1033 1034 return 1 # indicates a good save 1035 1036 else:#except: 1037 print( 'ERROR... saving file:',savePathName ) 1038 return 0 # indicates an error 1039 else: 1040 print("ERROR... no file name in saveAppDefFile.") 1041 return 0 1042 1043 1044 if __name__ == "__main__": 1045 1046 fd = TargetTkAppDef( 'myTestApp' ) 1047 fd.tkOptionD['background']='yellow' 1048 1049 N = (len( fd.compObjD )+2) * 20 1050 I = len( fd.compObjD ) + 1 1051 fd.maybe_add_component(widget_type="Button", widget_name="Button_1", tab_label="Main", 1052 row=1, col=1) 1053 #fd.saveAppDefFile() 1054 1055 fd.maybe_add_component(widget_type="Label", widget_name="Label_1", tab_label="Main", 1056 row=1, col=2) 1057 #fd.saveAppDefFile() 1058 1059 for obj in list(fd.compObjD.values()): 1060 print( 'for "%s" object "%s"'%(obj.widget_name, obj.widget_type) ) 1061 if obj.default_tkOptionD: 1062 print('...default properties') 1063 for key,val in list(obj.default_tkOptionD.items()): 1064 print( key,val ) 1065 print('...user properties') 1066 for key,val in list(obj.user_tkOptionD.items()): 1067 print( key,val ) 1068 print() 1069 1070 print( 'fd.get_model_crc() =',fd.get_model_crc() ) 1071