preview_win_widgets.py


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

tab_of_notebook_changed  Word MatchCase (4) 145 161 181 193 
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 object 9 """ 10 For PreviewWin encapsulate the standard widgets within a Frame 11 since some situations require a compound object (e.g. scrolling) 12 13 Hide widget.config behind widget.pw_config so that widget attributes 14 and compound attributes can be handled properly. 15 """ 16 17 import os 18 import sys 19 20 from tkinter import * 21 import tkinter.messagebox 22 from tkinter import Button, Canvas, Checkbutton, Entry, Frame, Label, LabelFrame 23 from tkinter import Listbox, Message, Radiobutton, Spinbox, Text 24 from tkinter import OptionMenu # ttk OptionMenu seems to be broken 25 from tkinter.ttk import Combobox, Progressbar, Separator, Treeview, Style, Notebook 26 27 from tkgridgui.edit_options import get_properties_dict, set_attribute_if_possible 28 29 from tkgridgui.grid_notebook import intCast, CONTROL_COLOR_D 30 from tkgridgui.config_file import ConfigInterface 31 32 from tkgridgui.tooltip import CreateToolTip 33 34 # see: http://effbot.org/zone/tkinter-scrollbar-patterns.htm 35 SCROLL_Y_WIDGETS = set(['Canvas','Listbox','Text','Treeview']) # Treeview has .xview() and .yview() methods 36 SCROLL_X_WIDGETS = set(['Canvas','Entry','Listbox','Text','Treeview']) 37 38 tkWidgetsD = {'Button':Button, 'Canvas':Canvas, 'Checkbutton':Checkbutton, 39 'Combobox':Combobox, 'Entry':Entry, 'Frame':Frame, 40 'Label':Label, 'LabelFrame':LabelFrame,'Listbox':Listbox, 'Message':Message, 41 'Menubutton':Menubutton, 'Notebook':Notebook ,'OptionMenu':OptionMenu,'Progressbar':Progressbar, 42 'Radiobutton':Radiobutton, 'RadioGroup':LabelFrame, # <== note that RadioGroup is a LabelFrame 43 'Scale':Scale, 'Separator':Separator, 44 'Spinbox':Spinbox, 'Text':Text, 'Treeview':Treeview } 45 46 47 class PW_Widget( object ): 48 49 def grid(self, row=0, column=0): 50 self.pw_frame.grid( row=row, column=column ) 51 52 def winfo_x(self): 53 return self.pw_frame.winfo_x() 54 55 def winfo_width(self): 56 return self.pw_frame.winfo_width() 57 58 def winfo_y(self): 59 return self.pw_frame.winfo_y() 60 61 def winfo_height(self): 62 return self.pw_frame.winfo_height() 63 64 def destroy(self): 65 self.pw_frame.destroy() 66 67 def destroy_children(self): 68 for child in self.pw_frame.winfo_children(): 69 child.destroy() 70 71 def keys(self): 72 """Only get native_widget.keys()""" 73 return list(self.native_widget.keys()) # + self.cobj.user_tkOptionD.keys() 74 75 def cget(self, name): 76 return self.native_widget.cget( name ) 77 78 def __getitem__(self, attr_name): 79 """expected attr_name values: relief, background, fg, font, etc.""" 80 if attr_name in self.cobj.user_tkOptionD: 81 return self.cobj.user_tkOptionD[ attr_name ] 82 83 if attr_name in self.cobj.default_tkOptionD: 84 return self.cobj.default_tkOptionD[ attr_name ] 85 return None 86 87 def __setitem__(self, key, value): 88 #print("Entering __setitem__ with key=",key,' and value=',value) 89 if value is None: 90 value = '' 91 92 if value: 93 self.cobj.user_tkOptionD[ key ] = value 94 95 elif key in self.cobj.user_tkOptionD: 96 # i.e. no value input, but key is in user_tkOptionD so delete it. 97 del self.cobj.user_tkOptionD[ key ] 98 99 # do nothing if no value and key not already in user_tkOptionD 100 101 #print('Setting Item Attr:',key,' = ',value) 102 self.set_native_widget_attr() 103 104 105 106 def __init__(self, disp_frame, cobj): 107 """ 108 disp_frame is PreviewWin.prevFrame for Main objects 109 disp_frame is the native_widget of pw_widget's parent otherwise 110 111 cobj is target_tk_app_def.Component (e.g. cobj.widget_type, cobj.widget_name, 112 cobj.row, cobj.col, cobj.tab_label, cobj.tkvar, cobj.user_tkOptionD 113 """ 114 self.disp_frame = disp_frame 115 self.cobj = cobj 116 117 self.pw_frame = Frame( disp_frame ) 118 self.pw_frame_background = self.pw_frame['background'] 119 self.pw_frame_borderwidth = self.pw_frame['borderwidth'] 120 self.pw_frame_highlightbackground = self.pw_frame['highlightbackground'] 121 122 #print('disp_frame: ',disp_frame.winfo_class()) 123 #for child in disp_frame.winfo_children(): 124 # print('child: ',child.winfo_class()) 125 # print('child.keys()',child.keys()) 126 127 # just in case row_weight or col_weight is applied to native_widget 128 self.pw_frame.rowconfigure(0, weight=1) 129 self.pw_frame.columnconfigure(0, weight=1) 130 131 132 self.put_native_widget_into_pw_frame() 133 134 def native_widget_clicked(self, event): 135 """When native_widget is clicked, try to change grid_notebook tab.""" 136 137 if self.cobj.target_app.grid_notebook is None: 138 #print('grid_notebook is None') 139 return 140 141 nb_obj = self.cobj.target_app.grid_notebook 142 nb_obj.set_current_tab_by_label( self.cobj.tab_label ) 143 144 145 def tab_of_notebook_changed(self, event): 146 """When PreviewWin Tab changes, try to change grid_notebook tab.""" 147 148 if self.cobj.target_app.grid_notebook is None: 149 #print('grid_notebook is None') 150 return 151 152 nb = self.native_widget 153 text = nb.tab(nb.select(), "text") 154 nb_obj = self.cobj.target_app.grid_notebook 155 156 for itab, (row, col, tab_name, tab_label) in enumerate( self.cobj.tab_nameL ): 157 #print( (row, col, tab_name, tab_label) ) 158 if text == tab_label: 159 nb_obj.set_current_tab_by_label( tab_name ) 160 #nb_obj.pw_widget.native_widget.select( itab ) 161 #print('preview_win_widgets.tab_of_notebook_changed: set PreviewWin Tab to:', itab) 162 break 163 164 def tab_of_notebook_clicked(self, event): 165 166 if self.cobj.target_app.grid_notebook is None: 167 #print('grid_notebook is None') 168 return 169 170 #print('x:', event.x) 171 #print('y:', event.y) 172 173 nb = self.native_widget 174 175 clicked_tab = nb.tk.call(nb._w, "identify", "tab", event.x, event.y) 176 #print('clicked tab:', clicked_tab) 177 178 active_tab = nb.index(nb.select()) 179 #print(' active tab:', active_tab) 180 181 self.tab_of_notebook_changed( event ) 182 183 #if clicked_tab == active_tab: 184 # print( 'clicked_tab == active_tab' ) 185 186 def put_native_widget_into_pw_frame(self): 187 188 cobj = self.cobj 189 190 if cobj.widget_type == 'Notebook': 191 192 self.native_widget = Notebook( self.pw_frame, width=400, height=300 ) 193 #self.native_widget.bind("<<NotebookTabChanged>>", self.tab_of_notebook_changed) 194 self.native_widget.bind("<ButtonRelease-1>", self.tab_of_notebook_clicked) 195 196 tab_label_str = cobj.user_tkOptionD.get('tab_labels', 'Pine\nBirch\nCherry') 197 tab_labelL = tab_label_str.split('\n') 198 199 # First tab is just to identify Notebook_xxx 200 #tab_frame = Frame( self.native_widget ) 201 #self.native_widget.add(tab_frame, text=cobj.widget_name) 202 203 self.tab_frameL = [] # list of Frame objects used as tabs for Notebook 204 205 # add desired tabs after that 206 for tab_str in tab_labelL: 207 tab_frame = Frame( self.native_widget ) 208 self.native_widget.add( tab_frame, text=tab_str ) 209 210 # save Frame objects to put Tab widgets onto. 211 self.tab_frameL.append( tab_frame ) 212 213 elif cobj.widget_type == 'Frame': 214 self.native_widget = Frame( self.pw_frame, bd=2, relief=GROOVE ) 215 216 elif cobj.widget_type == 'Spinbox': 217 self.native_widget = Spinbox(self.pw_frame, from_=0, to=100) 218 219 elif cobj.widget_type == 'Treeview': 220 self.native_widget = Treeview( self.pw_frame ) 221 tree = self.native_widget 222 # Inserted at the root, program chooses id: 223 tree.insert('', 'end', 'widgets', text='Widget Tour') 224 225 # Same thing, but inserted as first child: 226 tree.insert('', 0, 'gallery', text=cobj.widget_name) 227 228 # Treeview chooses the id: 229 id = tree.insert('', 'end', text='Tutorial') 230 231 # Inserted underneath an existing node: 232 for tree_widget in sorted( tkWidgetsD.keys() ): 233 tree.insert('widgets', 'end', text=tree_widget) 234 tree.insert(id, 'end', text='Tree') 235 236 elif cobj.widget_type in ['RadioGroup', 'LabelFrame']: 237 self.native_widget = LabelFrame( self.pw_frame, text=cobj.widget_name ) 238 239 elif cobj.widget_type == "Canvas": 240 self.native_widget = Canvas( self.pw_frame ) 241 w = int(cobj.user_tkOptionD['width']) 242 h = int(cobj.user_tkOptionD['height']) 243 #self.native_widget.create_rectangle((2, 2, w-1, h-1), outline="blue") 244 self.native_widget.config(bg='#aaffaa') 245 self.native_widget.create_text(w//2,h//2, text=cobj.widget_name, 246 fill="black", width=w, anchor='center') 247 248 elif cobj.tkvar is None: 249 # has no tk variable, so don't worry about it 250 self.native_widget = tkWidgetsD[cobj.widget_type]( self.pw_frame ) 251 252 # ============ The following all have tk variable controllers. =============== 253 else: # e.g. StringVar 254 # 'Entry' 'OptionMenu' 'Combobox' 'Checkbutton' 'Radiobutton' 'Scale' 255 if cobj.widget_type == 'Entry': 256 self.native_widget = Entry(self.pw_frame, textvariable=cobj.tkvar) 257 258 elif cobj.widget_type == 'OptionMenu': 259 self.native_widget = OptionMenu(self.pw_frame, cobj.tkvar, "one", "two", "three", "four") 260 cobj.tkvar.set( "two" ) 261 262 elif cobj.widget_type == 'Combobox': 263 self.native_widget = Combobox(self.pw_frame, textvariable=cobj.tkvar) 264 self.native_widget['values'] = ('X', 'Y', 'Z') 265 self.native_widget.current(0) 266 267 elif cobj.widget_type == 'Checkbutton': 268 self.native_widget = Checkbutton(self.pw_frame, variable=cobj.tkvar, onvalue="yes", offvalue="no") 269 cobj.tkvar.set("yes") 270 271 elif cobj.widget_type == 'Menubutton': 272 self.native_widget = Menubutton(self.pw_frame, text=cobj.widget_name, relief=GROOVE) 273 self.native_widget.menu = Menu ( self.native_widget , tearoff = 0 ) 274 self.native_widget["menu"] = self.native_widget.menu 275 for i,tkvar in enumerate(cobj.tkvar_list): 276 self.native_widget.menu.add_checkbutton(label="option %i"%(i+1,), variable=tkvar ) 277 278 elif cobj.widget_type == 'Radiobutton': 279 280 if cobj.tab_label.startswith("RadioGroup"): 281 tkvar = cobj.target_app.compObjD[ cobj.tab_label ].tkvar 282 self.native_widget = Radiobutton(self.pw_frame, variable=tkvar, value=cobj.widget_name) 283 #print(cobj.widget_name,' is part of ',cobj.tab_label) 284 self.native_widget.select() 285 else: 286 self.native_widget = Radiobutton(self.pw_frame, variable=cobj.tkvar, value=cobj.widget_name) 287 #print(cobj.widget_name,' is an isolated radio button') 288 289 290 elif cobj.widget_type == 'Scale': 291 self.native_widget = Scale(self.pw_frame, variable=cobj.tkvar, from_=0, to=100) 292 293 else: 294 print("WARNING... ignoring tk variable for ", cobj.widget_name) 295 self.native_widget = tkWidgetsD[cobj.widget_type]( self.pw_frame ) 296 297 self.has_y_scroll = False # might get set in self.set_native_widget_attr() 298 self.has_x_scroll = False # might get set in self.set_native_widget_attr() 299 self.vbar = False # might get set in self.set_native_widget_attr() 300 self.hbar = False # might get set in self.set_native_widget_attr() 301 302 if cobj.widget_type in ('Treeview',): 303 self.tooltip = CreateToolTip(self.pw_frame, text=cobj.widget_name, 304 background=CONTROL_COLOR_D[ cobj.widget_type ] ) 305 else: 306 self.tooltip = CreateToolTip(self.native_widget, text=cobj.widget_name, 307 background=CONTROL_COLOR_D[ cobj.widget_type ] ) 308 309 #self.native_widget.pack(side=LEFT,expand=True,fill=BOTH) 310 self.native_widget.grid(row=0, column=0) 311 312 cobj.default_tkOptionD = get_properties_dict( self.native_widget ) 313 314 self.set_native_widget_attr() 315 316 # bind to native_widget_clicked so the grid_notebook tab can be set 317 if cobj.widget_type != 'Notebook': 318 self.native_widget.bind("<ButtonRelease-1>", self.native_widget_clicked) 319 320 321 def maybe_add_y_scroll(self): 322 if self.has_y_scroll: 323 return # already have a y scroll widget 324 else: 325 self.has_y_scroll = True 326 if self.cobj.widget_type == 'Canvas': 327 w = int(self.cobj.user_tkOptionD['width']) 328 h = int(self.cobj.user_tkOptionD['height']) 329 self.native_widget.config( scrollregion=(0,0,w,h*2) ) 330 331 vbar=Scrollbar(self.pw_frame,orient=VERTICAL) 332 vbar.grid(row=0, column=1, sticky='ns') 333 vbar.config(command=self.native_widget.yview) 334 335 self.native_widget.config( yscrollcommand=vbar.set ) 336 self.vbar = vbar 337 338 def maybe_add_x_scroll(self): 339 if self.has_x_scroll: 340 return # already have a y scroll widget 341 else: 342 self.has_x_scroll = True 343 if self.cobj.widget_type == 'Canvas': 344 w = int(self.cobj.user_tkOptionD['width']) 345 h = int(self.cobj.user_tkOptionD['height']) 346 if self.has_y_scroll: 347 self.native_widget.config( scrollregion=(0,0,w*2,h*2) ) 348 else: 349 self.native_widget.config( scrollregion=(0,0,w*2,h) ) 350 351 elif self.cobj.widget_type == 'Text': 352 self.native_widget.config( wrap=NONE ) 353 354 hbar=Scrollbar(self.pw_frame,orient=HORIZONTAL) 355 hbar.grid(row=1, column=0, sticky='ew', columnspan=2) 356 hbar.config(command=self.native_widget.xview) 357 358 self.native_widget.config( xscrollcommand=hbar.set ) 359 self.hbar = hbar 360 361 362 def pw_highlight_widget(self): 363 if self['background']: 364 self.native_widget["background"] = "#FF6666" 365 else: 366 self.pw_frame["background"] = "#FF6666" 367 self.pw_frame["highlightbackground"] = "#FF6666" 368 self.pw_frame["borderwidth"] = 5 369 370 #print('did the Highlighting of pw widget') 371 372 def pw_unhighlight_widget(self): 373 if self['background']: # taken from user_tkOptionD or default_tkOptionD 374 self.native_widget["background"] = self['background'] 375 else: 376 self.pw_frame["background"] = self.pw_frame_background 377 self.pw_frame["highlightbackground"] = self.pw_frame_highlightbackground 378 self.pw_frame["borderwidth"] = self.pw_frame_borderwidth 379 380 def handle_scroll_logic(self): 381 382 must_rebuild = False 383 384 if self.cobj.user_tkOptionD.get('scrolly','no') == 'yes': 385 self.maybe_add_y_scroll() 386 elif self.has_y_scroll: 387 must_rebuild = True # request is "no", but already have y scroll 388 389 if self.cobj.user_tkOptionD.get('scrollx','no') == 'yes': 390 self.maybe_add_x_scroll() 391 elif self.has_x_scroll: 392 must_rebuild = True # request is "no", but already have x scroll 393 394 if must_rebuild: 395 self.destroy_children() 396 self.put_native_widget_into_pw_frame() 397 398 def set_native_widget_attr(self): 399 # NEED TO WAIT UNTIL PLACED INTO PARENT GRID BEFORE CHANGING ATTR. 400 # set any user options (at this point only width and height) 401 #print('user_tkOptionD =',self.cobj.user_tkOptionD,' for ',self.cobj.widget_name) 402 403 self.handle_scroll_logic() 404 405 for key, val in list(self.cobj.user_tkOptionD.items()): 406 if key == 'sticky': 407 # for sticky, need to set both pw_frame and native_widget 408 set_attribute_if_possible(self.pw_frame, key, val) 409 set_attribute_if_possible(self.native_widget, key, val) 410 411 elif key in PW_FRAME_ATTR: 412 set_attribute_if_possible(self.pw_frame, key, val) 413 else: 414 set_attribute_if_possible(self.native_widget, key, val) 415 416 417 PW_FRAME_ATTR = ['rowspan','columnspan','sticky'] 418 419 420 if __name__=="__main__": 421 422 from tkgridgui.preview_win import PreviewWin 423 from tkgridgui.target_tk_app_def import Component 424 425 426 widgetL = sorted( tkWidgetsD.keys() ) 427 def get_widget_type_name( i ): 428 wtype = widgetL[ i % len(widgetL) ] 429 i,r = divmod( i, len(widgetL) ) 430 name = wtype + '_%i'%(i+1, ) 431 return wtype, name 432 433 class TestPW(object): 434 def __init__(self, master): 435 frame = Frame(master, width=300, height=300) 436 frame.pack() 437 self.master = master 438 self.x, self.y, self.w, self.h = -1,-1,-1,-1 439 440 self.Button_1 = Button(text="Test PW_Widget", relief="raised", width="15") 441 self.Button_1.place(x=84, y=36) 442 self.Button_1.bind("<ButtonRelease-1>", self.Button_1_Click) 443 444 self.PreviewWin = None 445 self.num_comp = 0 446 447 def cleanupOnQuit(self): 448 #print( 'Doing final cleanup before quitting' ) 449 self.PreviewWin.destroy() 450 self.master.allow_subWindows_to_close = 1 451 self.master.destroy() 452 453 454 def Button_1_Click(self, event): #click method for component ID=1 455 456 if self.PreviewWin is None: 457 self.PreviewWin = PreviewWin( self.master ) 458 else: 459 wtype, name = get_widget_type_name( self.num_comp ) 460 col,row = divmod( self.num_comp, 9 ) 461 462 #print('Adding:',wtype, name,' to row=',row,' col=',col) 463 c = Component( widget_type=wtype, 464 widget_name=name, 465 tab_label='Main', 466 row=row, col=col, target_app=self ) 467 self.num_comp += 1 468 469 if wtype in SCROLL_Y_WIDGETS: 470 c.user_tkOptionD['scrolly'] = 'yes' 471 if wtype in SCROLL_X_WIDGETS: 472 c.user_tkOptionD['scrollx'] = 'yes' 473 474 475 pw = PW_Widget( self.PreviewWin.prevFrame, c ) 476 self.PreviewWin.add_widget( c.row, c.col, pw ) 477 478 #pw.set_native_widget_attr() 479 480 root = Tk() 481 MainWin = root 482 MainWin.title('Main Window') 483 #MainWin.geometry('320x320+10+10') 484 MainWin.config(background='#FFFACD')#'lemonchiffon': '#FFFACD' 485 486 grid_gui = TestPW( MainWin ) 487 488 MainWin.mainloop() 489