grid_notebook.py


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

tab_of_notebook_changed  Word MatchCase (0) 
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 """ 11 Notebook widget where grid geometry manager layout is defined. 12 13 Each target position on the target GUI has a Tk widget type and generic name (like Button_1). 14 15 More proper names can be added later (e.g. Button_1 can be named as Button_SaveFile) 16 Detailed widget properties like color, style or text are assigned outside this file. 17 These widgets only define locations and widget type. 18 """ 19 20 import sys 21 22 23 from tkinter import * 24 import tkinter.messagebox 25 from tkinter import Button, Canvas, Checkbutton, Entry, Frame, Label, LabelFrame 26 from tkinter import Listbox, Message, Radiobutton, Spinbox, Text 27 from tkinter import OptionMenu # ttk OptionMenu seems to be broken 28 from tkinter.ttk import Combobox, Progressbar, Separator, Treeview, Style, Notebook 29 30 from tkgridgui.edit_Dialog import Edit_Properties_Dialog 31 from tkgridgui.wt_select_Dialog import Wt_Select_Dialog 32 from tkgridgui.comp_tree import CNode, ComponentTree 33 34 supportedTkWidgetSet = set( ['Button', 'Canvas', 'Checkbutton', 35 'Combobox', 'Entry', 'Frame','Label', 'LabelFrame','Listbox', 'Message', 36 'Menubutton', 'Notebook' ,'OptionMenu','Progressbar', 37 'Radiobutton', "RadioGroup", 'Scale', 'Separator', 'Spinbox', 'Text', 'Treeview', 'Tab'] ) 38 39 40 CONTROLS = [ 41 ("Button", "#7CFC00"), # lawngreen 42 ("Canvas", "#40E0D0"), # turquiose 43 ("Checkbutton", "#66CDAA"), # mediumaquamarine 44 ("Combobox", "#CD853F"), # peru 45 ("Entry", "#3CB371"), # mediumseagreen 46 ("Frame", "#F0E68C"), # khaki 47 ("Label", "#FFA500"), # orange 48 ("LabelFrame", "#FF7F50"), # coral 49 ("Listbox", "#F08080"), # lightcoral 50 ("Message", "#D8BFD8"), # thistle 51 ("Menubutton", "#DA70D6"), # orchid 52 ("Notebook", "#FA8072"), # salmon 53 ("OptionMenu", "#9370DB"), # mediumpurple 54 ("Progressbar", "#FF69B4"), # hotpink 55 ("Radiobutton", "#FFFF00"), # yellow: also alternate form 56 ("RadioGroup", "#FFD700"), # gold: also alternate form 57 ("Scale", "#FF4500"), # orangered both vertical and horizontal 58 ("Separator", "#DC143C"), # crimson both vertical and horizontal 59 ("Spinbox", "#1E90FF"), # dodgerblue 60 ("Text", "#D2B48C"), # tan 61 ("Treeview", "#BC8F8F"), # rosybrown 62 ] 63 64 ContainerControlsL = ['Frame','LabelFrame','Notebook','RadioGroup','Tab'] 65 66 CONTROL_COLOR_D = {} # index=Control Name: value=color hex string (initialized from CONTROLS) 67 CONTROL_NEXT_NUMBER_D = {} # index=Control Name, value=next number for generic names 68 69 for widget_type, color in CONTROLS: 70 CONTROL_COLOR_D[widget_type] = color 71 CONTROL_NEXT_NUMBER_D[widget_type] = 1 # all start with 1 72 73 CONTROL_COLOR_D['Tab'] = '#cccccc' 74 CONTROL_NEXT_NUMBER_D['Tab'] = 1 75 76 def intCast( val=0 ): 77 try: 78 return int(val) 79 except: 80 return 0 81 82 MAX_NUM_COLS = 10 83 MAX_NUM_ROWS = 15 84 85 class NotebookGridDes( Frame ): 86 """A ttk.Notebook used to design grid geometry manager layouts""" 87 88 89 def __init__(self, grid_gui, mainFrame, MainWin, name='grid_notebook', num_cols=5, num_rows=8): 90 """Create ttk.Notebook used to design grid geometry manager layouts""" 91 92 self.grid_gui = grid_gui 93 self.mainFrame = mainFrame 94 self.MainWin = MainWin 95 96 self.num_cols_inp = num_cols 97 self.num_rows_inp = num_rows 98 self.tab_num_rows_colsD = {} # index=tab_label: value=(num_row, num_cols) 99 100 Frame.__init__(self, mainFrame, name=name) 101 self.pack(expand=Y, fill=BOTH) 102 103 self.notebook = None 104 self.initialize_NotebookGridDes() 105 106 def initialize_NotebookGridDes(self): 107 self.tabLabelL = [] # list of notebook tabs in order of addition 108 self.nbFrameL = [] # list of Frame objects for each tab in notebook 109 110 # need to have widget dictionaries for each tab of notebook 111 self.colInsertWidgetD = {} # index=(tab_label, col_interface): value=Label Widget 112 self.rowInsertWidgetD = {} # index=(tab_label, row_interface): value=Label Widget 113 114 self.colWeightsWidgetD = {} # index=(tab_label, col_interface): value=Label Widget 115 self.rowWeightsWidgetD = {} # index=(tab_label, row_interface): value=Label Widget 116 117 self.interface_gridBoxWidgetD = {} # index=(tab_label, row_interface, col_interface): value=Label Widget 118 self.default_label_font = ("Courier", 8, "normal") # will be set when interface_gridBoxWidgetD is initialized 119 120 # NOTE: row_target, col_target for defined_target_widgetD 121 self.defined_target_widgetD = {} # index=(tab_label, row_target, col_target): value=placementWidgetType or '' 122 self.label_obj_from_nameD = {} # index=widget_name (e.g. Button_1): 123 # value=(tab_label, row_target, col_target, placementWidgetType, Labelobj) 124 125 del self.notebook # start over 126 self.notebook = Notebook(self, name='main') 127 self.notebook.pack(fill=BOTH, expand=Y, padx=2, pady=3) 128 129 self.current_label_being_edited = None # used as flag in onGridBoxLeave to maintain hightlight 130 131 132 self.must_confirm_box_h = True 133 self.box_h = 46 # initial guesstimate of widget height 134 self.box_h3 = self.box_h // 3 135 self.box_h23 = (self.box_h * 2) // 3 136 137 self.must_confirm_box_w = True 138 self.box_w = 116 # initial guesstimate of widget width 139 self.box_w3 = self.box_w // 3 140 self.box_w23 = (self.box_w * 2) // 3 141 142 self.drag_set = set() # set of possible target widgets for drag and drop 143 144 145 self.create_tab(tab_label="Main") 146 #self.create_tab(tab_label="Test Frame") 147 148 149 def highlight_grid_widget(self, widget_name): 150 if widget_name in self.label_obj_from_nameD: 151 _, _, _, _, label = self.label_obj_from_nameD[ widget_name ] 152 label['font'] = ("Helvetica", 12, "bold italic underline") 153 154 def unhighlight_grid_widget(self, widget_name): 155 if widget_name in self.label_obj_from_nameD: 156 _, _, _, _, label = self.label_obj_from_nameD[ widget_name ] 157 label['font'] = self.default_label_font 158 159 160 def set_label_text_bg_from_widget_name(self, widget_name, placementWidgetType, Labelobj, row_target, col_target): 161 """Try to add some helpful info onto line3 of labels""" 162 163 Labelobj["background"] = CONTROL_COLOR_D[ placementWidgetType ] # aka widget_type 164 165 if widget_name not in self.label_obj_from_nameD: 166 167 Labelobj["text" ] = "(%d,%d)\n%s\n" % (row_target, col_target, widget_name) 168 else: 169 def fitted_line3( s ): 170 if len(s)<15: 171 return s 172 return s[:12] + '...' 173 174 line3 = '' 175 176 # only works if target_app has widget_name 177 if widget_name in self.grid_gui.target_app.compObjD: 178 c = self.grid_gui.target_app.compObjD[ widget_name ] 179 text = c.user_tkOptionD.get('text','') 180 181 if text and (text != widget_name): 182 line3 = fitted_line3( text ) 183 elif c.user_tkOptionD.get('docstring',''): 184 line3 = fitted_line3( c.user_tkOptionD.get('docstring','') ) 185 186 Labelobj["text" ] = "(%d,%d)\n%s\n%s" % (row_target, col_target, widget_name, line3) 187 188 189 def set_row_column_weights_from_target_app(self): 190 """Needs to set all weight Label widgets to show row/col weights""" 191 # self.colWeightsWidgetD = {} # index=(tab_label, col_interface): value=Label Widget 192 193 # rowD and colD: index=(tab_label,row_target), value=wt 194 rowD, colD = self.grid_gui.target_app.get_a_full_desc_of_weights() 195 196 for (tab_label, col_interface), label in list(self.colWeightsWidgetD.items()): 197 col_target = col_interface - 1 198 if (tab_label, col_target) in colD: 199 wt = colD[ (tab_label, col_target) ] 200 else: 201 wt = 0 202 203 label["text" ] = "col:%i\nwt:%s"%(col_target, wt) 204 if wt > 0: 205 label["background"] = "#ff6060" 206 else: 207 label["background"] = "#daa520" # goldenrod "#ff8080" # light coral 208 209 for (tab_label, row_interface), label in list(self.rowWeightsWidgetD.items()): 210 row_target = row_interface - 1 211 if (tab_label, row_target) in rowD: 212 wt = rowD[ (tab_label, row_target) ] 213 else: 214 wt = 0 215 216 label["text" ] = "row:%i\nwt:%s"%(row_target, wt) 217 if wt > 0: 218 label["background"] = "#ff6060" 219 else: 220 label["background"] = "#daa520" # goldenrod "#ff8080" # light coral 221 222 def repaint_all_labels(self): 223 224 # start by painting all label widgets lemonchiffon 225 for tab_label in self.tabLabelL: 226 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 227 228 for row_target in range( num_rows ): 229 row_interface = row_target + 1 230 for col_target in range( num_cols ): 231 col_interface = col_target + 1 232 label = self.interface_gridBoxWidgetD[ (tab_label, row_interface, col_interface) ] 233 label["text" ] = "(%d,%d)\n\n" % (row_target, col_target) 234 label["background"] = "#FFFACD" # lemonchiffon 235 236 # place defined widgets where they belong 237 238 # save any info on rowspan and columnspan 239 row_col_spanL = [] # do not place rowspan or columnspan on 1st pass. Save for 2nd pass. 240 241 for widget_name, (tab_label, row_target, col_target, widget_type, label) in list(self.label_obj_from_nameD.items()): 242 243 row_interface = row_target + 1 244 col_interface = col_target + 1 245 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 246 247 label = self.interface_gridBoxWidgetD[(tab_label, row_interface, col_interface)] 248 self.set_label_text_bg_from_widget_name( widget_name, widget_type, label, row_target, col_target ) 249 # label["text" ] = "(%d,%d)\n%s\n" % (row_target, col_target, widget_name) 250 # label["background"] = CONTROL_COLOR_D[ widget_type ] 251 252 253 # if any rowspan/columnspan, show them 254 target_comp = self.grid_gui.target_app.compObjD[ widget_name ] 255 rowspan = max(1, intCast( target_comp.user_tkOptionD.get('rowspan',1) )) 256 columnspan = max(1, intCast( target_comp.user_tkOptionD.get('columnspan',1) )) 257 if rowspan>1 or columnspan>1: 258 # need to show rowspan/columnspan grid labels. 259 for i in range( int(columnspan) ): 260 for j in range( int(rowspan) ): 261 if i!=0 or j!=0: 262 col_interface = col_target + 1 + i 263 row_interface = row_target + 1 + j 264 265 ctarg = col_target + i 266 rtarg = row_target + j 267 268 # Make sure grid is big enough to hold span labels 269 270 if (ctarg < num_cols) and (rtarg < num_rows): 271 # ignore any rowspan or columnspan that falls off the grid 272 273 label = self.interface_gridBoxWidgetD[(tab_label, row_interface, col_interface)] 274 #label["text" ] = "(%d,%d)\n%s\n%s" % (row_target, col_target, widget_name, '---SPAN---') 275 #label["background"] = CONTROL_COLOR_D[widget_type] 276 277 if rowspan>1 and columnspan>1: 278 text = "(%d,%d)\n%s\n%s" % (row_target, col_target, widget_name, 'col+rowspan') 279 elif rowspan>1: 280 text = "(%d,%d)\n%s\n%s" % (row_target, col_target, widget_name, 'rowspan') 281 else: 282 text = "(%d,%d)\n%s\n%s" % (row_target, col_target, widget_name, 'columnspan') 283 284 bg = CONTROL_COLOR_D[widget_type] 285 286 row_col_spanL.append( (label, text, bg, tab_label, row_interface, col_interface) ) 287 288 if row_col_spanL: 289 for (label, text, bg, tab_label, row_interface, col_interface) in row_col_spanL: 290 if self.widget_is_empty(label): 291 label["text" ] = text 292 label["background"] = bg 293 else: 294 label = self.interface_gridBoxWidgetD[(tab_label, row_interface, col_interface)] 295 sL = label["text"].split('\n') 296 sL[-1] = '???SPAN ERROR???' 297 label["text"] = '\n'.join(sL) 298 label["background"] = "#ff9797" 299 300 301 def set_complete_list_of_widgets(self, widgetL): 302 """ 303 Start over with full list of tuples describing all the widgets. 304 Tuples = (widget_type, widget_name, tab_label, row_target, col_target) 305 """ 306 307 # put the widgets into order of dependency 308 # widgetL = list of (c.widget_type, c.widget_name, c.tab_label, c.row, c.col) 309 ct = ComponentTree() 310 for (widget_type, widget_name, tab_label, row, col) in widgetL: 311 c = self.grid_gui.target_app.compObjD[ widget_name ] 312 ct.add_node( CNode(widget_name, tab_label, c) ) 313 314 cnodeL = ct.get_ordered_components() 315 cL = [cnode.component for cnode in cnodeL] 316 widgetL = [(c.widget_type, c.widget_name, c.tab_label, c.row, c.col) for c in cL ] 317 318 #print("NOTE... called set_complete_list_of_widgets.") 319 #print( widgetL ) 320 321 self.initialize_NotebookGridDes() 322 323 for widget_type, color in CONTROLS: 324 CONTROL_NEXT_NUMBER_D[widget_type] = 1 # all start with 1 325 CONTROL_NEXT_NUMBER_D['Tab'] = 1 326 327 # set CONTROL_NEXT_NUMBER_D values to match input list of widgets 328 for (widget_type, widget_name, tab_label, row_target, col_target) in widgetL: 329 try: 330 num = int( widget_name.split('_')[-1] ) 331 CONTROL_NEXT_NUMBER_D[widget_type] = max(num+1, CONTROL_NEXT_NUMBER_D[widget_type]) 332 except: 333 print("ERROR... Widget Number is SURELY Screwed Up.") 334 print( (widget_type, widget_name, tab_label, row_target, col_target) ) 335 336 # create any widgets and/or tabs 337 row_col_spanL = [] # do not place rowspan or columnspan on 1st pass. Save for 2nd pass. 338 for (placementWidgetType, widget_name, tab_label, row_target, col_target) in widgetL: 339 340 # If widget is a ContainerControlsL, then create a tab on Notebook 341 # ['Frame','LabelFrame','Notebook','RadioGroup'] 342 if (placementWidgetType in ContainerControlsL) and (widget_name not in self.tabLabelL): 343 self.create_tab(tab_label=widget_name) 344 elif tab_label not in self.tabLabelL: 345 self.create_tab(tab_label=tab_label) 346 347 # make sure the grid is big enough 348 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 349 if col_target >= num_cols: 350 self.set_current_tab_by_label( tab_label ) 351 for ic in range(num_cols, col_target+1): 352 self.insert_column( ic ) 353 if row_target >= num_rows: 354 self.set_current_tab_by_label( tab_label ) 355 for ir in range(num_rows, row_target+1): 356 self.insert_row( ir ) 357 358 359 self.defined_target_widgetD[(tab_label, row_target, col_target)] = placementWidgetType 360 row_interface, col_interface = row_target+1, col_target+1 361 362 label = self.interface_gridBoxWidgetD[(tab_label, row_interface, col_interface)] 363 self.set_label_text_bg_from_widget_name( widget_name, placementWidgetType, label, row_target, col_target ) 364 # label["text" ] = "(%d,%d)\n%s\n" % (row_target, col_target, widget_name) 365 # label["background"] = CONTROL_COLOR_D[placementWidgetType] 366 367 self.label_obj_from_nameD[ widget_name ] = (tab_label, row_target, col_target, placementWidgetType, label) 368 369 370 # repaint the NotebookGridDes Labels 371 self.repaint_all_labels() 372 373 # refresh the PreviewWin 374 self.grid_gui.refresh_preview_win() 375 376 377 def make_complete_list_of_widgets(self): 378 """Return info about all of the placed widgets""" 379 widgetL = [] 380 381 for widget_name, (tab_label, row_target, col_target, widget_type, label) in list(self.label_obj_from_nameD.items()): 382 widgetL.append( (widget_type, widget_name, tab_label, row_target, col_target) ) 383 384 return widgetL 385 386 def current_tab_label(self): 387 """return the label of the current tab.""" 388 return self.notebook.tab( self.notebook.select(), 'text' ) 389 390 def current_tab_index(self): 391 """return the index of the current tab.""" 392 return self.notebook.index('current') 393 394 def set_current_tab_by_label(self, tab_label): 395 """Switch to the tab with label "tab_label".""" 396 try: 397 i = self.tabLabelL.index( tab_label ) 398 except: 399 i = 0 400 self.set_status_msg( "Error... could not find tab label " + tab_label, also_print=True) 401 self.notebook.select(i) 402 403 def get_tab_index_by_label(self, tab_label): 404 """return the tab index for label "tab_label".""" 405 try: 406 i = self.tabLabelL.index( tab_label ) 407 except: 408 i = 0 409 self.set_status_msg( "Error... could not find tab label " + tab_label, also_print=True) 410 return i 411 412 413 def find_empty_space_on_tab(self, tab_label): 414 """return a (row_target, col_target) on "tab_label" notebook page.""" 415 try: 416 i = self.tabLabelL.index( tab_label ) 417 except: 418 self.set_status_msg( "Error... could not find tab label OR empty space " + tab_label, also_print=True) 419 return (None, None) 420 421 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 422 423 # search all locations for an empty spot 424 for col_target in range( num_cols ): 425 for row_target in range( num_rows ): 426 # index=(tab_label, row_target, col_target): value=placementWidgetType 427 if self.defined_target_widgetD[(tab_label, row_target, col_target)] == '': 428 return (row_target, col_target) 429 430 # if nothing found, then issue a warning and return (None, None) 431 self.set_status_msg( "Error... could not empty space on: " + tab_label, also_print=True) 432 return (None, None) 433 434 435 436 def set_status_msg(self, msg, also_print=False): 437 """Set the status message in the main window""" 438 if also_print: 439 print( msg ) 440 441 self.MainWin.statusMessage.set( msg ) 442 443 def make_col_control(self, tab_label, col_interface): 444 """Make a control Label on "tab_label" for column.""" 445 446 i_tab = self.get_tab_index_by_label( tab_label ) 447 nbFrame = self.nbFrameL[ i_tab ] 448 449 col_target = col_interface - 1 450 451 label = Label(nbFrame, width=16) 452 s = "col:%i"%col_target 453 label["text" ] = "+" + s.center(14," ") + "+" 454 label["font" ] = self.default_label_font 455 label["background"] = "#87CEFA" # lightskyblue 456 label["relief"] = "raised" 457 458 self.colInsertWidgetD[tab_label, col_interface] = label 459 460 label.grid(row=0, column=col_interface) 461 462 label.bind("<Button-1>", self.onColInsertClicked) 463 label.bind("<Enter>", self.onColControlEnter) 464 label.bind("<Leave>", self.onControlLeave) 465 label.bind("<Motion>", self.onColControlMove) 466 467 # Put column weight controls below widget labels 468 row_weight_controls = 99 469 470 label = Label(nbFrame, width=8) 471 label["text" ] = "col:%i\nwt:0"%col_target 472 label["font" ] = ("Courier", 10, "normal") 473 label["background"] = "#daa520" # goldenrod "#ff8080" # light coral 474 label["relief"] = "raised" 475 476 self.colWeightsWidgetD[tab_label, col_interface] = label 477 478 label.grid(row=row_weight_controls, column=col_interface) 479 480 label.bind("<Button-1>", self.onSetColumnWeightClicked) 481 482 483 484 def make_row_control(self, tab_label, row_interface): 485 """Make a control Label on "tab_label" for row.""" 486 487 i_tab = self.get_tab_index_by_label( tab_label ) 488 nbFrame = self.nbFrameL[ i_tab ] 489 490 row_target = row_interface - 1 491 492 label = Label(nbFrame, width=8) 493 label["text" ] = "+\nrow:%i\n+"%row_target 494 label["font" ] = self.default_label_font 495 label["background"] = "#87CEFA" # lightskyblue 496 label["relief"] = "raised" 497 498 self.rowInsertWidgetD[tab_label, row_interface] = label 499 500 label.grid(row=row_interface, column=0) 501 502 label.bind("<Button-1>", self.onRowInsertClicked) 503 label.bind("<Enter>", self.onRowControlEnter) 504 label.bind("<Leave>", self.onControlLeave) 505 label.bind("<Motion>", self.onRowControlMove) 506 507 # Put row weight controls below widget labels 508 col_weight_controls = 99 509 510 label = Label(nbFrame, width=8) 511 label["text" ] = "row:%i\nwt:0"%row_target 512 label["font" ] = ("Courier", 10, "normal") 513 label["background"] = "#daa520" # goldenrod "#ff8080" # light coral 514 label["relief"] = "raised" 515 516 self.rowWeightsWidgetD[tab_label, row_interface] = label 517 518 label.grid(row=row_interface, column=col_weight_controls) 519 520 label.bind("<Button-1>", self.onSetRowWeightClicked) 521 522 523 def target_grid_is_empty(self, tab_label, row_target, col_target): 524 """See if position on notebook tab_label is empty""" 525 if self.defined_target_widgetD[(tab_label, row_target, col_target)]: 526 return False 527 else: 528 return True 529 530 def current_grid_is_empty(self, row_target, col_target): 531 """See if position on CURRENT notebook tab_label is empty""" 532 tab_label = self.current_tab_label() 533 return self.target_grid_is_empty( tab_label, row_target, col_target ) 534 535 def widget_is_empty(self, widget): 536 """Given a Label widget, see if it is available for widget placement""" 537 sL = widget["text"].split('\n') 538 if len(sL)==3: 539 if sL[1].strip() == '': 540 return True 541 return False 542 543 def widget_is_container(self, label_widget): 544 _, _, widget_name, _ = self.get_all_widget_info( label_widget ) 545 _, _, _, widget_type, _ = self.label_obj_from_nameD.get( widget_name, '' ) 546 547 if widget_type in ContainerControlsL: 548 return True 549 else: 550 return False 551 552 def get_widget_type(self, label_widget): 553 _, _, widget_name, _ = self.get_all_widget_info( label_widget ) 554 _, _, _, widget_type, _ = self.label_obj_from_nameD.get( widget_name, '' ) 555 return widget_type 556 557 def get_widget_rc_target(self, widget): 558 """Given a Label widget, get the row_target, col_target from the "text" string.""" 559 560 # assume that the Label is in the field of target widgets 561 sL = widget["text"].split('\n') 562 row, col = sL[0][1:-1].split(',') # get row col from string like "(7,8)" 563 row_target, col_target = int(row), int(col) 564 return row_target, col_target 565 566 #def get_widget_rc_target_from_name(self, name): # name is like Button_3 567 # """Given a Label widget, get the row_target, col_target from the "text" string.""" 568 # (tab_label, rtarg, col_target, placementWidgetType, label) = self.label_obj_from_nameD[name] 569 # return self.get_widget_rc_target( label ) 570 571 572 def get_all_widget_info(self, label_widget): 573 """Given a Label widget, get the row_target, col_target, name and line3 from the "text" string.""" 574 575 # assume that the Label is in the field of target widgets 576 sL = label_widget["text"].split('\n') 577 row, col = sL[0][1:-1].split(',') # get row col from string like "(7,8)" 578 row_target, col_target = int(row), int(col) 579 580 widget_name = sL[1] 581 line3 = sL[2] 582 583 return row_target, col_target, widget_name, line3 584 585 #def get_all_widget_info_from_name(self, name): # name is like Button_3 586 # """Given a widget name, get the row_target, col_target, name and line3 from the "text" string.""" 587 # (tab_label, rtarg, col_target, placementWidgetType, label) = self.label_obj_from_nameD[name] 588 # return self.get_all_widget_info( label ) 589 590 def make_target_grid_label(self, tab_label, row_target, col_target): 591 """ 592 Add a Label object to represent target GUI grid location. 593 These positions are all empty right now. 594 """ 595 596 i_tab = self.get_tab_index_by_label( tab_label ) 597 nbFrame = self.nbFrameL[ i_tab ] 598 599 row_interface = row_target + 1 600 col_interface = col_target + 1 601 602 label = Label(nbFrame, width=16) 603 label["text" ] = "(%d,%d)\n\n" % (row_target, col_target) 604 label["font" ] = self.default_label_font 605 label["background"] = "#FFFACD" # lemonchiffon 606 label["relief"] = "groove" 607 608 self.interface_gridBoxWidgetD[(tab_label, row_interface,col_interface)] = label 609 self.defined_target_widgetD[(tab_label, row_target, col_target)] = '' 610 611 label.grid(row=row_interface, column=col_interface) 612 613 label.bind("<Enter>", self.onGridBoxEnter) 614 label.bind("<Leave>", self.onGridBoxLeave) 615 label.bind("<Button-1>", self.onGridBoxClicked) 616 617 label.bind("<Button-3>", self.onGridBoxRightClicked) 618 619 620 label.bind('<B1-Motion>', self.onLeftDrag) 621 label.bind("<ButtonRelease-1>", self.onLeftDrop) 622 623 624 def onLeftDrag(self, event): 625 #print( 'Got left mouse button drag:',) 626 #print( 'Widget=%s X=%s Y=%s' % (event.widget["text"].replace('\n',' '), event.x, event.y)) 627 628 xroot = event.widget.winfo_rootx() 629 yroot = event.widget.winfo_rooty() 630 631 try: 632 w2 = self.winfo_containing(event.x+xroot, event.y+yroot) 633 #print( '... Widget#2=%s' % (w2["text"].replace('\n',' '),)) 634 except: 635 w2 = None 636 637 # restore any Labels that were dragged over 638 for w in self.drag_set: 639 if w != w2: 640 if self.widget_is_empty( w ): 641 w["background"] = "#FFFACD" # lemonchiffon 642 #print('Restore Empty',w["text"].replace('\n',' ')) 643 else: 644 widget_type = self.get_widget_type(w) 645 w["background"] = CONTROL_COLOR_D[ widget_type ] 646 #print('Restore Widget',w["text"].replace('\n',' ')) 647 self.drag_set.clear() 648 649 # set background of potential drop target Label 650 if w2 is not None and (w2 != event.widget): 651 self.drag_set.add( w2 ) 652 if self.widget_is_empty( w2 ) or self.widget_is_container(w2): 653 w2["background"] = "#FF00FF" # magenta 654 655 def onLeftDrop(self, event): 656 657 xroot = event.widget.winfo_rootx() 658 yroot = event.widget.winfo_rooty() 659 660 # w2 is Label object that event.widget is dropped upon 661 w2 = self.winfo_containing(event.x+xroot, event.y+yroot) 662 #print('xxxDropped Widget=%s on %s'% (event.widget["text"].replace('\n',' '), w2["text"].replace('\n',' '))) 663 664 # Might be here from simple Click. Check for any drag_set members before using Drop Logic. 665 if self.drag_set: 666 667 old_row_target, old_col_target, widget_name, line3 = self.get_all_widget_info( event.widget ) 668 new_row_target, new_col_target = self.get_widget_rc_target( w2 ) 669 670 # only try to move if there is an actual movement 671 if (old_row_target != new_row_target) or ( old_col_target != new_col_target): 672 673 if widget_name.startswith('Tab_'): 674 self.grid_gui.target_app.arrange_notebook_tabs( widget_name, new_row_target, new_col_target ) 675 676 s = 'Dropped Widget=%s on %s'% (event.widget["text"].replace('\n',' '), w2["text"].replace('\n',' ')) 677 self.set_status_msg(s) 678 self.move_widget_on_current_tab( event.widget, new_row_target, new_col_target, w2) 679 680 self.repaint_all_labels() 681 682 def move_widget_on_current_tab(self, widget, new_row_target, new_col_target, w2): 683 """Move widget from current location to new location. (might move to another tab)""" 684 685 tab_label = self.current_tab_label() 686 687 # w2 is Label object that widget is dropped upon 688 if self.target_grid_is_empty( tab_label, new_row_target, new_col_target): 689 w2_type = '' 690 else: 691 w2_row_target, w2_col_target, w2_name, w2_line3 = self.get_all_widget_info( w2 ) 692 w2_type = self.defined_target_widgetD[(tab_label, w2_row_target, w2_col_target)] 693 694 695 # can only move to target location if it is empty. 696 if w2_type == '': 697 698 old_row_target, old_col_target, widget_name, line3 = self.get_all_widget_info( widget ) 699 placementWidgetType = self.defined_target_widgetD[(tab_label, old_row_target, old_col_target)] 700 701 # clear the old location 702 self.defined_target_widgetD[(tab_label, old_row_target, old_col_target)] = '' 703 widget["text" ] = "(%d,%d)\n\n" % (old_row_target, old_col_target) 704 widget["font" ] = self.default_label_font 705 widget["background"] = "#FFFACD" # lemonchiffon 706 707 # set up the new location 708 self.defined_target_widgetD[(tab_label, new_row_target, new_col_target)] = placementWidgetType 709 new_row_interface, new_col_interface = new_row_target + 1, new_col_target + 1 710 711 self.label_obj_from_nameD[ widget_name ] = (tab_label, new_row_target, new_col_target, placementWidgetType, widget) 712 713 label = self.interface_gridBoxWidgetD[(tab_label, new_row_interface, new_col_interface)] 714 715 self.set_label_text_bg_from_widget_name( widget_name, placementWidgetType, label, new_row_target, new_col_target ) 716 # label["text" ] = "(%d,%d)\n%s\n" % (row_target, col_target, widget_name) 717 # label["background"] = CONTROL_COLOR_D[placementWidgetType] 718 719 s = "Moved widget "+widget_name+" at (%s,%i,%i)"%(tab_label, old_row_target, old_col_target) +\ 720 " to (%s,%i,%i)"%(tab_label, new_row_target, new_col_target) 721 #print(s) 722 self.set_status_msg(s) 723 724 self.grid_gui.refresh_preview_win() 725 726 elif self.widget_is_container( w2 ) and (w2 != widget) : # move to container's tab 727 728 old_row_target, old_col_target, widget_name, line3 = self.get_all_widget_info( widget ) 729 730 w2_type = self.get_widget_type( w2 ) 731 w2["background"] = CONTROL_COLOR_D[ w2_type ] 732 733 # if current position on current tab is open on new tab, put it there 734 if self.target_grid_is_empty( w2_name, old_row_target, old_col_target): 735 new_row_target, new_col_target = old_row_target, old_col_target 736 else: 737 new_row_target, new_col_target = self.find_empty_space_on_tab( w2_name ) 738 739 if new_row_target is not None: # i.e. if a place was found on the other tab. 740 741 old_row_target, old_col_target, widget_name, line3 = self.get_all_widget_info( widget ) 742 placementWidgetType = self.defined_target_widgetD[(tab_label, old_row_target, old_col_target)] 743 744 # clear the old location 745 self.defined_target_widgetD[(tab_label, old_row_target, old_col_target)] = '' 746 widget["text" ] = "(%d,%d)\n\n" % (old_row_target, old_col_target) 747 widget["font" ] = self.default_label_font 748 widget["background"] = "#FFFACD" # lemonchiffon 749 750 # set up the new location 751 self.defined_target_widgetD[(w2_name, new_row_target, new_col_target)] = placementWidgetType 752 new_row_interface, new_col_interface = new_row_target + 1, new_col_target + 1 753 754 self.label_obj_from_nameD[ widget_name ] = (w2_name, new_row_target, new_col_target, placementWidgetType, widget) 755 756 label = self.interface_gridBoxWidgetD[(w2_name, new_row_interface, new_col_interface)] 757 self.set_label_text_bg_from_widget_name( widget_name, placementWidgetType, label, row_target, col_target ) 758 # label["text" ] = "(%d,%d)\n%s\n" % (row_target, col_target, widget_name) 759 # label["background"] = CONTROL_COLOR_D[placementWidgetType] 760 761 762 s = 'Moved widget "%s" to tab_label "%s" (%s,%s)'%(widget_name, w2_name, new_row_target, new_col_target) 763 #print("="*55) 764 #print( s ) 765 #print("="*55) 766 self.set_status_msg( s ) 767 768 self.grid_gui.refresh_preview_win() 769 770 else: 771 s = "Error... tried to move widget to Occupied Target Location (%i,%i)"%(new_row_target, new_col_target) 772 #print( s ) 773 self.set_status_msg( s ) 774 775 776 def create_tab(self, tab_label="New Tab"): 777 """Add new tab to grid notebook for Label objects.""" 778 779 nbFrame = Frame(self.notebook, name='panel_%i'%len(self.tabLabelL) ) 780 self.tabLabelL.append( tab_label ) 781 self.nbFrameL.append( nbFrame ) 782 783 nbFrame.pack(side=TOP, fill=BOTH, expand=Y) 784 785 # Place Row and Col controls that Insert or Delete Rows and Cols 786 for row_interface in range(1, self.num_rows_inp+1): 787 self.make_row_control( tab_label, row_interface) 788 789 for col_interface in range(1, self.num_cols_inp+1): 790 self.make_col_control( tab_label, col_interface ) 791 792 # add grid labels 793 """Show only num_row x num_col widgets... i.e. No scroll currently available""" 794 self.tab_num_rows_colsD[ tab_label ] = (self.num_rows_inp, self.num_cols_inp) 795 for row_target in range( self.num_rows_inp ): 796 for col_target in range( self.num_cols_inp ): 797 self.make_target_grid_label( tab_label, row_target, col_target) 798 799 # add tab to notebook 800 self.notebook.add(nbFrame, text=tab_label, underline=0, padding=2) 801 #print("tabs() =", self.notebook.tabs()) 802 803 def append_column(self): 804 """Append a column to the grid of the current notebook tab.""" 805 tab_label = self.current_tab_label() 806 807 # increase number of columns for current tab 808 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 809 num_cols += 1 810 self.tab_num_rows_colsD[ tab_label ] = (num_rows, num_cols) 811 812 end_col_interface = num_cols # NEED num_cols for EACH TAB 813 self.make_col_control( tab_label, end_col_interface ) 814 815 # add another column at the end with the right (col,row) label 816 end_col_target = end_col_interface - 1 817 for row_target in range( num_rows ): 818 self.make_target_grid_label( tab_label, row_target, end_col_target) 819 820 def insert_column(self, col_target): 821 """Insert a column into the grid of the current notebook tab.""" 822 tab_label = self.current_tab_label() 823 824 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 825 if num_cols >= MAX_NUM_COLS: 826 tkinter.messagebox.showinfo("Insert Warning...", 827 "Can NOT have more than %i columns\n\n"%MAX_NUM_COLS\ 828 +"For now, simply use Frame objects to expand the grid area.") 829 return 830 831 self.append_column() 832 # ---------------------------- 833 834 # make a copy of current widget info 835 copy_defined_target_widgetD = self.defined_target_widgetD.copy() # index=(tab_label, row_target, col_target): value=placementWidgetType or '' 836 copy_label_obj_from_nameD = self.label_obj_from_nameD.copy() # index=widget_name: value=(tab_label, row_target, col_target, placementWidgetType, Labelobj) 837 838 # empty out the main data 839 self.defined_target_widgetD = {} # index=(tab_label, row_target, col_target): value=placementWidgetType 840 for key in list(copy_defined_target_widgetD.keys()): 841 self.defined_target_widgetD[key] = '' 842 843 self.label_obj_from_nameD = {} # index=widget_name: value=(tab_label, row_target, col_target, placementWidgetType, Labelobj) 844 845 for widget_name,(wtab_label, wrow_target, wcol_target, placementWidgetType, wLabelobj) in list(copy_label_obj_from_nameD.items()): 846 847 placementWidgetType = copy_defined_target_widgetD[ (wtab_label, wrow_target, wcol_target) ] 848 849 if (tab_label==wtab_label) and (wcol_target>=col_target): 850 851 self.label_obj_from_nameD[ widget_name ] = (wtab_label, wrow_target, wcol_target+1, placementWidgetType, wLabelobj) 852 target_comp = self.grid_gui.target_app.compObjD[ widget_name ] 853 target_comp.row = target_comp.col + 1 854 855 self.defined_target_widgetD[ (wtab_label, wrow_target, wcol_target+1) ] = placementWidgetType 856 857 #if self.grid_gui.PreviewWin is not None: 858 # self.grid_gui.PreviewWin.widget_ijD[wrow_target, wcol_target+1] =\ 859 # self.grid_gui.PreviewWin.widget_ijD[wrow_target, wcol_target]# index=(i,j) grid position: value=widget 860 # del self.grid_gui.PreviewWin.widget_ijD[wrow_target, wcol_target] 861 862 else: 863 self.label_obj_from_nameD[ widget_name ] = (wtab_label, wrow_target, wcol_target, placementWidgetType, wLabelobj) 864 865 self.defined_target_widgetD[ (wtab_label, wrow_target, wcol_target) ] = placementWidgetType 866 867 868 self.repaint_all_labels() 869 self.grid_gui.refresh_preview_win() 870 self.set_status_msg('Inserted Column at col:%s'%col_target) 871 872 def append_row(self): 873 """Append a new row at the bottom of the current notebook tab.""" 874 tab_label = self.current_tab_label() 875 876 # increase number of rows for current tab 877 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 878 num_rows += 1 879 self.tab_num_rows_colsD[ tab_label ] = (num_rows, num_cols) 880 881 end_row_interface = num_rows # NEED num_rows for EACH TAB 882 self.make_row_control( tab_label, end_row_interface ) 883 884 # add another row at the end with the right (col,row) label 885 end_row_target = end_row_interface - 1 886 for col_target in range( num_cols ): 887 self.make_target_grid_label( tab_label, end_row_target, col_target) 888 889 def insert_row(self, row_target): 890 """Insert a row into the grid of the current notebook tab.""" 891 tab_label = self.current_tab_label() 892 893 894 num_rows, num_cols = self.tab_num_rows_colsD[ tab_label ] 895 if num_rows >= MAX_NUM_ROWS: 896 tkinter.messagebox.showinfo("Insert Warning...", 897 "Can NOT have more than %i rows\n\n"%MAX_NUM_ROWS\ 898 +"For now, simply use Frame objects to expand the grid area.") 899 return 900 901 902 self.append_row() 903 904 # ---------------------------- 905 906 # make a copy of current widget info 907 copy_defined_target_widgetD = self.defined_target_widgetD.copy() # index=(tab_label, row_target, col_target): value=placementWidgetType 908 copy_label_obj_from_nameD = self.label_obj_from_nameD.copy() # index=widget_name: value=(tab_label, row_target, col_target, placementWidgetType, Labelobj) 909 910 # empty out the main data 911 self.defined_target_widgetD = {} # index=(tab_label, row_target, col_target): value=placementWidgetType 912 for key in list(copy_defined_target_widgetD.keys()): 913 self.defined_target_widgetD[key] = '' 914 915 self.label_obj_from_nameD = {} # index=widget_name: value=(tab_label, row_target, col_target, placementWidgetType, Labelobj) 916 917 for widget_name,(wtab_label, wrow_target, wcol_target, placementWidgetType, wLabelobj) in list(copy_label_obj_from_nameD.items()): 918 919 placementWidgetType = copy_defined_target_widgetD[ (wtab_label, wrow_target, wcol_target) ] 920 921 if (tab_label==wtab_label) and (wrow_target>=row_target): 922 923 self.label_obj_from_nameD[ widget_name ] = (wtab_label, wrow_target+1, wcol_target, placementWidgetType, wLabelobj) 924 target_comp = self.grid_gui.target_app.compObjD[ widget_name ] 925 target_comp.row = target_comp.row + 1 926 927 self.defined_target_widgetD[ (wtab_label, wrow_target+1, wcol_target) ] = placementWidgetType 928 929 else: 930 self.label_obj_from_nameD[ widget_name ] = (wtab_label, wrow_target, wcol_target, placementWidgetType, wLabelobj) 931 932 self.defined_target_widgetD[ (wtab_label, wrow_target, wcol_target) ] = placementWidgetType 933 934 935 self.repaint_all_labels() 936 self.grid_gui.refresh_preview_win() 937 self.set_status_msg('Inserted Row at row:%s'%row_target) 938 939 def onSetColumnWeightClicked(self, event): 940 tab_label = self.current_tab_label() 941 942 sL = event.widget["text"].split('\n') # weight labels have 2 lines 943 col = int( sL[0].split(':')[-1] ) 944 945 #print( "Set Weight for Column %i"%col, ' on tab ',tab_label ) 946 947 dialog = Wt_Select_Dialog(self.master, "Column %i Weight"%col) 948 949 if dialog.result is not None: 950 wt = intCast( dialog.result['weight'] ) 951 952 self.grid_gui.target_app.set_a_col_weight(tab_label, col, wt) 953 954 event.widget["text" ] = "col:%i\nwt:%s"%(col, wt) 955 if wt > 0: 956 event.widget["background"] = "#ff6060" 957 else: 958 event.widget["background"] = "#daa520" # goldenrod "#ff8080" # light coral 959 960 self.grid_gui.refresh_preview_win() 961 962 self.set_status_msg('Set Column Weight=%s at col:%s'%(wt, col) ) 963 964 965 def onSetRowWeightClicked(self, event): 966 tab_label = self.current_tab_label() 967 968 sL = event.widget["text"].split('\n') # weight labels have 2 lines 969 row = int( sL[0].split(':')[-1] ) 970 971 #print( "Set Weight for Row %i"%row, ' on tab ',tab_label ) 972 973 dialog = Wt_Select_Dialog(self.master, "Row %i Weight"%row) 974 975 if dialog.result is not None: 976 wt = intCast( dialog.result['weight'] ) 977 978 self.grid_gui.target_app.set_a_row_weight(tab_label, row, wt) 979 980 event.widget["text" ] = "row:%i\nwt:%s"%(row, wt) 981 if wt > 0: 982 event.widget["background"] = "#ff6060" 983 else: 984 event.widget["background"] = "#daa520" # goldenrod "#ff8080" # light coral 985 986 self.grid_gui.refresh_preview_win() 987 988 self.set_status_msg('Set Row Weight=%s at row:%s'%(wt, row) ) 989 990 # mouse clicked widget 991 def onColInsertClicked(self, event): 992 """Col Control is Clicked. Will Insert or Delete a Col""" 993 tab_label = self.current_tab_label() 994 995 #print( 'Clicked "%s" Column Control.'%tab_label ) 996 #print(" event x,y =", event.x, event.y) 997 #print(" widget w,h =", event.widget.winfo_width(), event.widget.winfo_height()) 998 999 sL = event.widget["text"].split() # col labels are a single line 1000 col_target = int( sL[1].strip().split(':')[-1] ) 1001 1002 x = int( event.x ) 1003 if x < self.box_w3: 1004 #print(" Insert a new col at col_target #%i"%col_target) 1005 self.set_status_msg("Insert a new col at col_target #%i"%col_target) 1006 1007 self.insert_column( col_target ) 1008 elif x > self.box_w23: 1009 self.set_status_msg("Insert a new col at col_target #%i"%(col_target+1) ) 1010 1011 self.insert_column( col_target+1 ) 1012 else: 1013 self.set_status_msg("") 1014 1015 # mouse clicked widget 1016 def onRowInsertClicked(self, event): 1017 """Row Control is Clicked. Will Insert or Delete a Row.""" 1018 tab_label = self.current_tab_label() 1019 1020 #print( 'Clicked "%s" Row Control.'%tab_label ) 1021 #print(" event x,y =", event.x, event.y) 1022 #print(" widget w,h =", event.widget.winfo_width(), event.widget.winfo_height()) 1023 1024 sL = event.widget["text"].split('\n') # row labels have 3 lines 1025 row = int( sL[1].strip().split(':')[-1] ) 1026 1027 y = int( event.y ) 1028 1029 if y < self.box_h3: 1030 #print(" Insert a new row at row #%i"%row) 1031 self.set_status_msg("Insert a new row at row #%i"%row) 1032 self.insert_row( row ) 1033 elif y > self.box_h23: 1034 self.set_status_msg("Insert a new row at row #%i"%(row+1)) 1035 self.insert_row( row+1 ) 1036 else: 1037 self.set_status_msg("") 1038 1039 def onRowControlMove(self, event): 1040 """Movement inside a Row Control. Changes cursor for Insert and Delete.""" 1041 y = int( event.y ) 1042 1043 if y < self.box_h3: 1044 #print(y,"<",self.box_h3) 1045 event.widget.config(cursor='plus') 1046 elif y > self.box_h23: 1047 #print(y,">",self.box_h23) 1048 event.widget.config(cursor='plus') 1049 else: 1050 #print(self.box_h3,"<",y,"<",self.box_h3) 1051 event.widget.config(cursor='arrow') 1052 1053 def onColControlMove(self, event): 1054 """Movement inside a Col Control. Changes cursor for Insert and Delete.""" 1055 x = int( event.x ) 1056 1057 if x < self.box_w3: 1058 #print(x,"<",self.box_h3) 1059 event.widget.config(cursor='plus') 1060 elif x > self.box_w23: 1061 #print(x,">",self.box_h23) 1062 event.widget.config(cursor='plus') 1063 else: 1064 #print(self.box_h3,"<",x,"<",self.box_h3) 1065 event.widget.config(cursor='arrow') 1066 1067 1068 # mouse is over a widget 1069 def onRowControlEnter(self, event): 1070 """When cursor moves inside a Row Control. Changes widget border relief.""" 1071 event.widget["relief"] = "sunken" 1072 1073 # confirm box height on first entry 1074 if self.must_confirm_box_h: 1075 self.must_confirm_box_h = False 1076 self.box_h = event.widget.winfo_height() 1077 self.box_h3 = self.box_h // 3 1078 self.box_h23 = (self.box_h * 2) // 3 1079 1080 # mouse is over a widget 1081 def onColControlEnter(self, event): 1082 """When cursor moves inside a Col Control. Changes widget border relief.""" 1083 event.widget["relief"] = "sunken" 1084 1085 # confirm box width on first entry 1086 if self.must_confirm_box_w: 1087 self.must_confirm_box_w = False 1088 self.box_w = event.widget.winfo_width() 1089 self.box_w3 = self.box_w // 3 1090 self.box_w23 = (self.box_w * 2) // 3 1091 1092 # mouse is leaving a widget 1093 def onControlLeave(self, event): 1094 """Restore widget border relief when cursor leaves Row or Col Control.""" 1095 event.widget["relief"] = "raised" 1096 1097 # mouse is over a widget 1098 def onGridBoxEnter(self, event): 1099 """When cursor moves inside a Grid Box change widget border relief.""" 1100 event.widget["relief"] = "ridge" 1101 #event.widget["borderwidth"] = 3 1102 #event.widget["padx"] = 0 1103 #event.widget["pady"] = 0 1104 1105 background = event.widget.cget("background") 1106 if background == "#FFFACD": 1107 event.widget["background"] = "#FFFFEE" 1108 1109 if self.must_confirm_box_h: 1110 self.must_confirm_box_h = False 1111 self.box_h = event.widget.winfo_height() 1112 self.box_h3 = self.box_h // 3 1113 self.box_h23 = (self.box_h * 2) // 3 1114 1115 self.must_confirm_box_w = False 1116 self.box_w = event.widget.winfo_width() 1117 self.box_w3 = self.box_w // 3 1118 self.box_w23 = (self.box_w * 2) // 3 1119 1120 # highlight widget in PreviewWin 1121 if not self.widget_is_empty(event.widget): 1122 _, _, widget_name, _ = self.get_all_widget_info( event.widget ) 1123 if widget_name in self.grid_gui.target_app.compObjD: 1124 target_comp = self.grid_gui.target_app.compObjD[ widget_name ] 1125 1126 target_comp.highlight_pw_widget() 1127 1128 def set_label_widget_background(self, label_widget): 1129 1130 tab_label = self.current_tab_label() 1131 row_target, col_target = self.get_widget_rc_target( label_widget ) 1132 placementWidgetType = self.defined_target_widgetD[(tab_label, row_target, col_target)] 1133 1134 if placementWidgetType: 1135 label_widget["background"] = CONTROL_COLOR_D[placementWidgetType] 1136 else: 1137 label_widget["background"] = "#FFFACD" # lemonchiffon 1138 1139 1140 # mouse is leaving a widget 1141 def onGridBoxLeave(self, event): 1142 """Restore Grid Box border relief when cursor leaves.""" 1143 1144 if event.widget == self.current_label_being_edited: 1145 return 1146 1147 event.widget["relief"] = "groove" 1148 #event.widget["borderwidth"] = 2 1149 #event.widget["padx"] = 1 1150 #event.widget["pady"] = 1 1151 1152 self.set_label_widget_background( event.widget ) 1153 1154 # return PreviewWin widget to normal 1155 if not self.widget_is_empty(event.widget): 1156 _, _, widget_name, _ = self.get_all_widget_info( event.widget ) 1157 if widget_name in self.grid_gui.target_app.compObjD: 1158 target_comp = self.grid_gui.target_app.compObjD[ widget_name ] 1159 1160 target_comp.unhighlight_pw_widget() 1161 1162 # mouse clicked widget 1163 def onGridBoxClicked(self, event): 1164 """ 1165 Detect a Click on Label widget in main grid. 1166 Each Label widget represents GUI widget that will be added to created GUI. 1167 """ 1168 1169 tab_label = self.current_tab_label() 1170 1171 row_target, col_target = self.get_widget_rc_target( event.widget ) 1172 1173 row_interface, col_interface = row_target+1, col_target+1 1174 #print('row_target=%d, col_target=%d'%(row_target, col_target)) 1175 #print("Clicked (%i,%i)"%(row_target, col_target ),' on ',tab_label) 1176 1177 #print("self.defined_target_widgetD = ",self.defined_target_widgetD) 1178 1179 # see what widget is selected for placement on the main form 1180 # (might be placing a duplicate of a selected widget) 1181 s_dup = self.grid_gui.dup_widget_label["text"].strip() 1182 1183 if tab_label.startswith('Notebook'): 1184 placementWidgetType = 'Tab' 1185 self.dup_source_widget_name = '' 1186 self.set_status_msg(" Only Tab Inserts Allowed on Notebook" , also_print=True) 1187 1188 elif s_dup and s_dup.startswith('('): # i.e. widget has a (row,col) position 1189 sL = self.grid_gui.dup_widget_label["text"].split('\n') 1190 1191 # save source widget name for use in target_tk_app_def.TargetTkAppDef making widget 1192 self.dup_source_widget_name = sL[1] 1193 placementWidgetType = self.dup_source_widget_name.split('_')[0] 1194 1195 else: 1196 placementWidgetType = self.MainWin.placementWidgetType_svar.get() 1197 self.dup_source_widget_name = '' 1198 1199 # ======================= PlacementWidgetType decided ======================= 1200 # Check for existing widget at grid location row, col in current tab_label 1201 if ((tab_label, row_target, col_target) in self.defined_target_widgetD) and \ 1202 self.defined_target_widgetD[(tab_label, row_target, col_target)]: 1203 1204 # get widget_type for widget already in (tab_label, row_target, col_target) 1205 ptype = self.defined_target_widgetD[ (tab_label, row_target, col_target) ] 1206 self.set_status_msg(ptype + " Widget is at (%i, %i) "%(row_target, col_target) ) 1207 #print( ptype + " Widget is at (%s, %i, %i) "%(tab_label, row_target, col_target) ) 1208 1209 # toggle dup_widget_label if already set to this choice 1210 if self.grid_gui.dup_widget_label["text" ] == event.widget["text"]: 1211 # set duplicate widget back to plain 1212 #self.grid_gui.dup_widget_label["text" ] = "\n\n" 1213 #self.grid_gui.dup_widget_label["background"] = self.grid_gui.dup_widget_label_plain_bg # "#FFFACD" # lemonchiffon 1214 1215 val = self.grid_gui.MainWin.placementWidgetType_svar.get() 1216 self.grid_gui.set_placement_widget_label( val ) 1217 1218 else: 1219 self.grid_gui.set_duplication_widget_label( event.widget ) 1220 1221 1222 else: # If row,col is empty, place a new widget here. 1223 1224 # if it's a legal widget, (i.e. is in tkWidgets Dictionary), place it. 1225 if placementWidgetType in supportedTkWidgetSet: 1226 1227 num_widget = CONTROL_NEXT_NUMBER_D[placementWidgetType] 1228 CONTROL_NEXT_NUMBER_D[placementWidgetType] += 1 1229 widget_name = placementWidgetType + "_%i"%num_widget 1230 1231 if tab_label.startswith('Notebook'): 1232 # tell target_app about the new tab 1233 self.grid_gui.target_app.add_tab_to_notebook(row_target, col_target, widget_name, tab_label) # tab_label is Notebook_XXX 1234 1235 self.defined_target_widgetD[(tab_label, row_target, col_target)] = placementWidgetType 1236 1237 label = self.interface_gridBoxWidgetD[(tab_label, row_interface, col_interface)] 1238 self.set_label_text_bg_from_widget_name( widget_name, placementWidgetType, label, row_target, col_target ) 1239 # label["text" ] = "(%d,%d)\n%s\n" % (row_target, col_target, widget_name) 1240 # label["background"] = CONTROL_COLOR_D[placementWidgetType] 1241 1242 self.label_obj_from_nameD[ widget_name ] = (tab_label, row_target, col_target, placementWidgetType, label) 1243 1244 self.set_status_msg("Placing widget "+placementWidgetType+" at (%s, %i,%i)"%(tab_label, row_target, col_target)) 1245 #print( ("Placing widget "+placementWidgetType+" at (%s, %i,%i)"%(tab_label, row_target, col_target)) ) 1246 1247 # If widget is a ContainerControlsL, then create a tab on Notebook 1248 # ['Frame','LabelFrame','Notebook','RadioGroup'] 1249 if placementWidgetType in ContainerControlsL: 1250 self.create_tab(tab_label=widget_name) 1251 1252 self.grid_gui.refresh_preview_win() 1253 self.repaint_all_labels() 1254 1255 else: 1256 self.set_status_msg("Widget "+placementWidgetType+" NOT Recognized", also_print=True) 1257 1258 # right button click 1259 def onGridBoxRightClicked(self, event): 1260 """ 1261 Detect a RightClick on Label widget in current grid. 1262 If the Label widget is assigned, then it will be edited. 1263 """ 1264 1265 tab_label = self.current_tab_label() 1266 1267 row_target, col_target = self.get_widget_rc_target( event.widget ) 1268 1269 row_interface, col_interface = row_target+1, col_target+1 1270 #print('row_target=%d, col_target=%d'%(row_target, col_target)) 1271 #print("Clicked (%i,%i)"%(row_target, col_target ),' on ',tab_label) 1272 1273 #print("self.defined_target_widgetD = ",self.defined_target_widgetD) 1274 1275 # see what widget is selected for placement on the main form 1276 placementWidgetType = self.MainWin.placementWidgetType_svar.get() 1277 #print("placementWidgetType = " + placementWidgetType) 1278 1279 # Check for existing widget at grid location row, col in current tab_label 1280 if ((tab_label, row_target, col_target) in self.defined_target_widgetD) and \ 1281 self.defined_target_widgetD[(tab_label, row_target, col_target)]: 1282 1283 # get widget_type for widget already in (tab_label, row_target, col_target) 1284 ptype = self.defined_target_widgetD[ (tab_label, row_target, col_target) ] 1285 self.set_status_msg('Editing ' +ptype + " Widget at (%i, %i) "%(row_target, col_target) ) 1286 #print( ptype + " Widget is at (%s, %i, %i) "%(tab_label, row_target, col_target) ) 1287 1288 # get label object, and set up for any property editing 1289 label = self.interface_gridBoxWidgetD[(tab_label, row_interface, col_interface)] 1290 1291 # prepare to call property editor 1292 _, _, widget_name, _ = self.get_all_widget_info( label ) 1293 1294 self.highlight_grid_widget( widget_name ) 1295 1296 1297 # get default and user properties 1298 if 1:#self.grid_gui.PreviewWin is not None: 1299 1300 target_comp = self.grid_gui.target_app.compObjD[ widget_name ] 1301 dialogOptionsD = {} 1302 for key,val in list(target_comp.default_tkOptionD.items()): 1303 dialogOptionsD[key] = (val, "def.") 1304 1305 for key,val in list(target_comp.user_tkOptionD.items()): # user_tkOptionD holds tk options set by user. (i.e. not same as default_tkOptionD) 1306 dialogOptionsD[key] = (val, "USER VALUE") 1307 1308 dialogOptionsD['child_widget_list'] = self.grid_gui.target_app.get_names_of_containers_widgets( widget_name ) 1309 1310 # highlight widget on PreviewWin 1311 target_comp.highlight_pw_widget() # onGridBoxLeave tries to override this when Edit_Properties_Dialog is called. 1312 1313 #self.grid_gui.PreviewWin.update() 1314 1315 #else: 1316 # dialogOptionsD = None 1317 # target_comp = None 1318 1319 # call property editor 1320 1321 self.current_label_being_edited = event.widget # used as flag in onGridBoxLeave to maintain hightlight 1322 1323 dialog = Edit_Properties_Dialog(self.master, widget_name, dialogOptionsD=dialogOptionsD) 1324 1325 self.current_label_being_edited = None # used as flag in onGridBoxLeave to maintain hightlight 1326 1327 def unhighlight_edited_widget(): 1328 # restore label on grid to normal 1329 self.unhighlight_grid_widget( widget_name ) 1330 if target_comp is not None: 1331 target_comp.unhighlight_pw_widget() 1332 1333 # use dialog results to set properties 1334 if dialog.result is None: 1335 self.master.after( 500, unhighlight_edited_widget ) # give user half a sectond to see highlight 1336 else: 1337 # check for delete widget command 1338 if ("DeleteWidget" in dialog.result) and (dialog.result["DeleteWidget"]=="yes"): 1339 1340 unhighlight_edited_widget() 1341 1342 _, _, _, widget_type, _ = self.label_obj_from_nameD.get( widget_name, '' ) 1343 1344 if widget_type in ContainerControlsL: 1345 1346 self.grid_gui.target_app.removeContainerByName( widget_name ) 1347 1348 else: 1349 #print("Deleting ",widget_name) 1350 self.delete_widget_by_name( widget_name ) 1351 self.grid_gui.target_app.delComponentByName( widget_name ) 1352 1353 else: # not deleting, so update user_tkOptionD for widget 1354 self.master.after( 500, unhighlight_edited_widget ) # give user half a sectond to see highlight 1355 1356 #print('grid_notebook dialog.result =',dialog.result) 1357 if target_comp is not None: 1358 target_comp.user_tkOptionD.update( dialog.result ) 1359 if target_comp.widget_type != "Tab": 1360 target_comp.set_user_properties() 1361 1362 # need to repostion Canvas Label 1363 if target_comp.widget_type == "Canvas": 1364 target_comp.pw_widget.native_widget.delete( 'all' ) 1365 w = int(target_comp.user_tkOptionD['width']) 1366 h = int(target_comp.user_tkOptionD['height']) 1367 target_comp.pw_widget.native_widget.create_text(w//2,h//2, text=target_comp.widget_name, 1368 fill="black", width=w, anchor='center') 1369 elif target_comp.widget_type == "Tab": 1370 self.grid_gui.target_app.change_notebook_tab_label( target_comp.widget_name, target_comp.user_tkOptionD['text'] ) 1371 1372 self.grid_gui.refresh_preview_win() 1373 1374 1375 # reinitialize display 1376 #widgetL = [(c.widget_type, widget_name, c.tab_label, c.row, c.col) for widget_name,c in self.grid_gui.target_app.compObjD.items()] 1377 self.repaint_all_labels( ) 1378 1379 if self.grid_gui.PreviewWin is not None: 1380 self.grid_gui.PreviewWin.maybe_resize_preview_win() 1381 1382 1383 1384 def delete_widget_by_name(self, widget_name): 1385 1386 (tab_label, row_target, col_target, placementWidgetType, label) = self.label_obj_from_nameD[ widget_name ] 1387 1388 if placementWidgetType in ContainerControlsL: 1389 self.set_status_msg("Can NOT delete Containeer Widget "+placementWidgetType+\ 1390 " at (%s,%i,%i)"%(tab_label, row_target, col_target), also_print=True) 1391 1392 tkinter.messagebox.showinfo("Delete Warning...", 1393 "Can NOT delete Containeer Widget "+placementWidgetType+'\n\n'+\ 1394 " at (%s,%i,%i)"%(tab_label, row_target, col_target)) 1395 1396 else: 1397 # if this is just added, reset numbering in CONTROL_NEXT_NUMBER_D 1398 num_widget = int( widget_name.split('_')[-1] ) 1399 if CONTROL_NEXT_NUMBER_D[placementWidgetType] == num_widget + 1: 1400 CONTROL_NEXT_NUMBER_D[placementWidgetType] = num_widget 1401 1402 self.defined_target_widgetD[(tab_label, row_target, col_target)] = '' 1403 1404 label["text" ] = "(%d,%d)\n\n" % (row_target, col_target) 1405 label["background"] = "#FFFACD" # lemonchiffon 1406 1407 del self.label_obj_from_nameD[ widget_name ] 1408 1409 self.set_status_msg("Deleted widget "+placementWidgetType+\ 1410 " at (%s,%i,%i)"%(tab_label, row_target, col_target), also_print=True) 1411 1412 self.grid_gui.refresh_preview_win() 1413 1414 1415 1416 1417 if __name__ == '__main__': 1418 1419 from tkgridgui.target_tk_app_def import TargetTkAppDef 1420 from tkgridgui.preview_win import PreviewWin 1421 1422 class MockGridGUI(object): 1423 def __init__(self, MainWin): 1424 self.MainWin = MainWin 1425 topFrame = Frame( MainWin ) # frame for controls 1426 1427 self.grid_frame = Frame(topFrame) 1428 self.grid_notebook = NotebookGridDes( self, self.grid_frame, MainWin, num_cols=6, num_rows=10) 1429 self.grid_frame.pack(anchor=N, side=LEFT) 1430 1431 topFrame.pack(fill=BOTH, expand=Y) 1432 1433 # make a Status Bar 1434 statframe = Frame(MainWin) 1435 MainWin.statusMessage = StringVar() 1436 MainWin.statusMessage.set('Welcome to TkGridGUI') 1437 self.statusbar = Label(statframe, textvariable=MainWin.statusMessage, 1438 relief=SUNKEN, anchor=W) 1439 self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM) 1440 statframe.pack(anchor=SW, fill=X, side=BOTTOM) 1441 1442 1443 MainWin.placementWidgetType_svar=StringVar() 1444 MainWin.placementWidgetType_svar.set('Button') 1445 MainWin.bind("<Key>", self.key_set_placement_widget) 1446 1447 self.target_app = TargetTkAppDef( name='myApp') 1448 self.PreviewWin = None 1449 1450 def key_set_placement_widget(self, event): 1451 """When the user hits a number key, set the placementWidgetType to that index of CONTROLS""" 1452 try: 1453 i = int( event.char ) 1454 MainWin.placementWidgetType_svar.set( CONTROLS[i][0] ) 1455 msg = 'placementWidgetType_svar set to:' + CONTROLS[i][0] 1456 #print( msg ) 1457 self.MainWin.statusMessage.set( msg ) 1458 except: 1459 pass 1460 1461 def refresh_preview_win(self): 1462 """Get all widget info from grid_notebook and pass into to target_app.""" 1463 if self.PreviewWin is None: 1464 self.PreviewWin = PreviewWin( self.MainWin ) 1465 self.target_app.set_PreviewWin( self.PreviewWin ) 1466 #self.grid_gui.PreviewWin = PreviewWin 1467 1468 widgetL = self.grid_notebook.make_complete_list_of_widgets() 1469 #print("========== PreviewWin Widget Info.") 1470 for w in widgetL: 1471 #print( w ) 1472 (widget_type, widget_name, tab_label, row_target, col_target) = w 1473 1474 self.target_app.maybe_add_component( widget_type=widget_type, 1475 widget_name=widget_name, 1476 tab_label=tab_label, 1477 row=row_target, col=col_target) 1478 self.target_app.show_preview() 1479 #print("="*55) 1480 1481 root = Tk() 1482 MainWin = root 1483 MainWin.title('Main Window') 1484 #MainWin.geometry('320x320+10+10') 1485 MainWin.config(background='#FFFACD')#'lemonchiffon': '#FFFACD' 1486 1487 grid_gui = MockGridGUI( MainWin ) 1488 1489 MainWin.mainloop() 1490