示例#1
0
class GridGUI(object):
    """
    tkGridGUI builds a python Tkinter GUI graphic user interface using the grid geometry manager.
    """

    def __init__(self, MainWin):
        """Inits Tkinter window of GridGUI."""
        
        self.root = MainWin
        #MainWin.geometry("800x600") #You want the size of the app to be 500x500
        MainWin.geometry( '+10+30' )
        
        try:
            style = Style(self.root)
            if "win" == platform[:3]:
                style.theme_use('vista')
            elif "darwin" in platform:
                style.theme_use('clam')
            else:
                style.theme_use('clam')
            bg = style.lookup("TLabel", "background")
            self.root.configure(bg=bg)
        except:
            print("OOPS... failed to set style.theme_use... Let's press on.")
        
        self.MainWin = MainWin
        
        MainWin.protocol('WM_DELETE_WINDOW', self.cleanupOnQuit)
        MainWin.allow_subWindows_to_close = 0
        
        self.add_menu_to_MainWin()
        
        topFrame = Frame( MainWin ) # frame for controls
        #topFrame = tx.ScrolledWindow( MainWin )
        
        frame1 = LabelFrame(topFrame, text="Widgets")
        self.place_widget_selection_listbox( frame1 )
        frame1.pack(anchor=NW, side=LEFT)
        
        frame2 = Frame( topFrame ) # frame for radio buttons
        self.place_gui_definition_controls( frame2, MainWin )
        frame2.pack(anchor=N, side=LEFT)

        self.grid_frame = Frame(topFrame) 
        self.grid_notebook = NotebookGridDes(self, self.grid_frame, MainWin, num_cols=5, num_rows=8)
        self.grid_frame.pack(anchor=N, side=LEFT)
                
        topFrame.pack(fill=BOTH, expand=Y)
        
        # make a Status Bar
        statframe = Frame(MainWin)
        MainWin.statusMessage = StringVar()
        MainWin.statusMessage.set('Welcome to TkGridGUI')
        self.statusbar = Label(statframe, textvariable=MainWin.statusMessage, 
            relief=SUNKEN, anchor=W)
        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)
        statframe.pack(anchor=SW, fill=X, side=BOTTOM)
        
        # Initialize some GridGUI parameters
        
        self.current_fileFullName = '' # no file for now
        self.current_filePath = '' # no file for now
        self.current_fileName = '' # no file for now

        self.target_app = TargetTkAppDef( name='myApp')
        self.PreviewWin = None # need to initialize later
        
        self.Listbox_1_Click( 'FromInit' ) # execute selection logic

        
        self.in_reading_mode = False # when True, suppresses some automatic trace actions.
        if len( sys.argv ) == 2:
            fName = sys.argv[1]
            if fName.find('.')<0:
                fName += '.def'
                
            fullpath = os.path.abspath(fName)
            
            if os.path.isfile( fullpath ): # if file exists, read it as a definition file
                self.openFile( fName=fullpath )
            else:
                self.MainWin.statusMessage.set('file "%s" does not exist'%fName)
                
        self.grid_notebook.notebook.bind("<<NotebookTabChanged>>", self.tab_of_notebook_changed)

        self.mouse_location = ''
        self.MainWin.bind("<Enter>", self.onMainWindowEnter)

    def onMainWindowEnter(self, event):
        """Only track Enter... Want last known location."""
        #if self.mouse_location != 'main_window':
        #    print('mouse_location = main_window')
        self.mouse_location = 'main_window'

    def refresh_preview_win(self, allow_destroy_children=True):
        """
        Place all of the widgets from grid_notebook onto PreviewWin.
        May need to delete and rebuild current widgets on PreviewWin.
        """
            
        if self.PreviewWin is None:
            self.PreviewWin = PreviewWin( self.MainWin, grid_gui=self )
            self.target_app.set_PreviewWin( self.PreviewWin )
            self.target_app.set_Notebook( self.grid_notebook )
        else:
            if allow_destroy_children:
                self.PreviewWin.destroy_all_children()
                self.target_app.destroy_all_preview_widgets()
            
        
        widgetL = self.grid_notebook.make_complete_list_of_widgets()
        #print("========== PreviewWin Widget Info.")
        for w in widgetL:
            #print( w )
            (widget_type, widget_name, tab_label, row_target, col_target) = w
            
            self.target_app.maybe_add_component( widget_type=widget_type, 
                                                 widget_name=widget_name, 
                                                 tab_label=tab_label, 
                                                 row=row_target, col=col_target)
        self.target_app.show_preview()
        #print("="*55)
        #print('ref crc_reference =',self.target_app.crc_reference, 'current =', self.target_app.get_model_crc() )
            
        
                
    def add_menu_to_MainWin(self):
        
        # make menus
        self.menuBar = Menu(self.MainWin, relief = 'raised', bd=2)

        # create file pulldown menu
        fileMenu = Menu(self.menuBar, tearoff=0)
        fileMenu.add('command', label = 'New', command=self.newForm, underline=0,accelerator="Ctrl+N")
        fileMenu.add('command', label = 'Open', command=self.openFile, underline=0,accelerator="Ctrl+O")
        fileMenu.add('command', label = 'Save', command=self.saveasFile, underline=0,accelerator="Ctrl+S")
        #fileMenu.add('command', label = 'SaveAs', command = self.saveasFile)
        fileMenu.add('command', label = 'Exit', command=self.cleanupOnQuit, underline=0,accelerator="Ctrl+X")
        self.menuBar.add('cascade', label="File", menu=fileMenu)

        # create options pulldown menu
        optMenu = Menu(self.menuBar, tearoff=0)
        optMenu.add('command', label = 'Font to Clipboard', command =self.FontPickButton_Select, underline=0,accelerator="Ctrl+F")
        optMenu.add('command', label = 'Color to Clipboard', command = self.ColorPickButton_Select, underline=0,accelerator="Ctrl+C")
        optMenu.add('command', label = 'Named Color to Clipboard', command = self.NamedColorPickButton_Select, underline=0,accelerator="Ctrl+K")
        self.menuBar.add('cascade', label="Options", menu=optMenu)
        
        # bind accelerator keys (need lambda since functions don't have "event" parameter)
        self.root.bind("<Control-N>", lambda event: self.newForm())
        self.root.bind("<Control-n>", lambda event: self.newForm())
        
        self.root.bind("<Control-O>", lambda event: self.openFile())
        self.root.bind("<Control-o>", lambda event: self.openFile())
        
        self.root.bind("<Control-S>", lambda event: self.saveasFile())
        self.root.bind("<Control-s>", lambda event: self.saveasFile())
        
        self.root.bind("<Control-X>", lambda event: self.cleanupOnQuit())
        self.root.bind("<Control-x>", lambda event: self.cleanupOnQuit())
            
            
        self.root.bind("<Control-F>", self.FontPickButton_Click)
        self.root.bind("<Control-f>", self.FontPickButton_Click)
        
        self.root.bind("<Control-C>", self.ColorPickButton_Click)
        self.root.bind("<Control-c>", self.ColorPickButton_Click)
        
        self.root.bind("<Control-K>", self.NamedColorPickButton_Click)
        self.root.bind("<Control-k>", self.NamedColorPickButton_Click)

        # create About menu
        self.menuBar.add('command', label="About", command = self.About)

        # create Help menu
        self.menuBar.add('command', label="Help", command = self.Help)

        self.root.config(menu=self.menuBar)

    def mainOrDialog_Callback(self, varName, index, mode):
        #print( "mainOrDialog_Callback varName, index, mode",varName, index, mode )
        #print( "    new StringVar value =",self.MainWin.mainOrDialog.get() )
        
        self.refresh_preview_win()
        if self.MainWin.mainOrDialog.get() == 'dialog':
            if self.MainWin.hideOkChkBox_StringVar.get() == "yes":
                self.PreviewWin.remove_mock_OK_Cancel_Buttons()
            else:
                self.PreviewWin.add_mock_OK_Cancel_Buttons()
        else:
            self.PreviewWin.remove_mock_OK_Cancel_Buttons()
    
    def place_gui_definition_controls(self, frame2, MainWin ):
        
        # show option for Main Window or Dialog
        MainWin.mainOrDialog=StringVar()
        lbframe = LabelFrame(frame2, text="GUI Type")
        lbframe.pack(anchor=W)

        b = Radiobutton(lbframe, text="Main Window", value='main', variable=MainWin.mainOrDialog)
        b.pack(anchor=W)
        b = Radiobutton(lbframe, text="Dialog", value='dialog', variable=MainWin.mainOrDialog)
        b.pack(anchor=W)
        MainWin.mainOrDialog.set('main')
        self.mainOrDialog_traceName = MainWin.mainOrDialog.trace_variable("w", self.mainOrDialog_Callback)
        
        MainWin.hideOkChkBox = Checkbutton(lbframe, text="Hide OK Btn", width="15")
        MainWin.hideOkChkBox.pack(anchor=E, side=TOP)
        MainWin.hideOkChkBox_StringVar = StringVar()
        MainWin.hideOkChkBox_StringVar.set("no")
        MainWin.hideOkChkBox.configure(variable=MainWin.hideOkChkBox_StringVar, onvalue="yes", offvalue="no")
        self.hideOkChkBox_traceName = MainWin.hideOkChkBox_StringVar.trace_variable("w", self.hideOkChkBox_Callback)
        
        # show checkbox for menu and status bar
        lbframe = LabelFrame(frame2, text="Window Options")
        lbframe.pack(anchor=W)
        
        MainWin.menuChkBox = Checkbutton(lbframe, text="Main Menu", width="15")
        MainWin.menuChkBox.pack(anchor=W, side=TOP)
        MainWin.menuChkBox_StringVar = StringVar()
        MainWin.menuChkBox_StringVar.set("no")
        MainWin.menuChkBox.configure(variable=MainWin.menuChkBox_StringVar, onvalue="yes", offvalue="no")
        self.menuChkBox_traceName = MainWin.menuChkBox_StringVar.trace_variable("w", self.menuChkBox_Callback)
        
        MainWin.statusBarChkBox = Checkbutton(lbframe, text="Status Bar", width="15")
        MainWin.statusBarChkBox.pack(anchor=W, side=TOP)
        MainWin.statusBarChkBox_StringVar = StringVar()
        MainWin.statusBarChkBox_StringVar.set("no")
        MainWin.statusBarChkBox.configure(variable=MainWin.statusBarChkBox_StringVar, onvalue="yes", offvalue="no")
        self.statusBarChkBox_traceName = MainWin.statusBarChkBox_StringVar.trace_variable("w", self.statusBarChkBox_Callback)
        
        MainWin.resizableChkBox = Checkbutton(lbframe, text="Resizable", width="15")
        MainWin.resizableChkBox.pack(anchor=W, side=TOP)
        MainWin.resizableChkBox_StringVar = StringVar()
        MainWin.resizableChkBox_StringVar.set("yes")
        MainWin.resizableChkBox.configure(variable=MainWin.resizableChkBox_StringVar, onvalue="yes", offvalue="no")
        self.resizableChkBox_traceName = MainWin.resizableChkBox_StringVar.trace_variable("w", self.resizableChkBox_Callback)
        
        # show choices for standard dialogs
        lbframe = LabelFrame(frame2, text="Standard Dialogs")
        lbframe.pack(anchor=W)

        MainWin.stdDialMessChkBox = Checkbutton(lbframe, text="Messages", width="15")
        MainWin.stdDialMessChkBox.pack(anchor=E, side=TOP)
        MainWin.stdDialMessChkBox_StringVar = StringVar()
        MainWin.stdDialMessChkBox_StringVar.set("no")
        MainWin.stdDialMessChkBox.configure(variable=MainWin.stdDialMessChkBox_StringVar, onvalue="yes", offvalue="no")
        

        MainWin.stdDialColorChkBox = Checkbutton(lbframe, text="Color Choose", width="15")
        MainWin.stdDialColorChkBox.pack(anchor=E, side=TOP)
        MainWin.stdDialColorChkBox_StringVar = StringVar()
        MainWin.stdDialColorChkBox_StringVar.set("no")
        MainWin.stdDialColorChkBox.configure(variable=MainWin.stdDialColorChkBox_StringVar, onvalue="yes", offvalue="no")

        MainWin.stdDialFileChkBox = Checkbutton(lbframe, text="File Open/Save", width="15")
        MainWin.stdDialFileChkBox.pack(anchor=E, side=TOP)
        MainWin.stdDialFileChkBox_StringVar = StringVar()
        MainWin.stdDialFileChkBox_StringVar.set("no")
        MainWin.stdDialFileChkBox.configure(variable=MainWin.stdDialFileChkBox_StringVar, onvalue="yes", offvalue="no")

        MainWin.stdAlarmChkBox = Checkbutton(lbframe, text="Alarm Handler", width="15")
        MainWin.stdAlarmChkBox.pack(anchor=E, side=TOP)
        MainWin.stdAlarmChkBox_StringVar = StringVar()
        MainWin.stdAlarmChkBox_StringVar.set("no")
        MainWin.stdAlarmChkBox.configure(variable=MainWin.stdAlarmChkBox_StringVar, onvalue="yes", offvalue="no")
        
        # put color picker button
        self.ColorPickButton = Button(frame2, text="Put Color on Clipboard", width=18)
        self.ColorPickButton.pack(anchor=W, side=TOP)
        self.ColorPickButton.bind("<ButtonRelease-1>", self.ColorPickButton_Click)
        
        # put color picker button
        self.ColorPickButton = Button(frame2, text="   --> Named Color", width=18)
        self.ColorPickButton.pack(anchor=W, side=TOP)
        self.ColorPickButton.bind("<ButtonRelease-1>", self.NamedColorPickButton_Click)
        
        # put Font picker button
        self.FontPickButton = Button(frame2, text="Put Font on Clipboard", width=18)
        self.FontPickButton.pack(anchor=W, side=TOP)
        self.FontPickButton.bind("<ButtonRelease-1>", self.FontPickButton_Click)
        
        # put All widgets on notebook 
        #self.PlaceAllWidgetsButton = Button(frame2, text="Debug All Widgets", width=18)
        #self.PlaceAllWidgetsButton.pack(anchor=W, side=TOP)
        #self.PlaceAllWidgetsButton.bind("<ButtonRelease-1>", self.PlaceAllWidgetsButton_Click)
        
        # append new row or column to current notebook tab
        add_frame = Frame( frame2 )
        self.AddNewRowButton = Button(add_frame, text="Add Row", width=8)
        self.AddNewRowButton.pack(anchor=W, side=LEFT)
        self.AddNewRowButton.bind("<ButtonRelease-1>", self.AddNewRowButton_Click)

        self.AddNewColButton = Button(add_frame, text="Add Col", width=8)
        self.AddNewColButton.pack(anchor=W, side=LEFT)
        self.AddNewColButton.bind("<ButtonRelease-1>", self.AddNewColButton_Click)
        add_frame.pack(anchor=W, side=TOP)

        # dup_widget_label can be used for set_placement_widget_label, or widget duplication
        self.dup_widget_label_desc = Label(frame2, width=16)
        self.dup_widget_label_desc["text"      ] = "\nduplicate widget\nand its properties"
        self.dup_widget_label_desc.pack(anchor=W)
        
        self.dup_widget_label = Label(frame2, width=16)        
        self.dup_widget_label["text"      ] = "\n\n"
        self.dup_widget_label["font"      ] = ("Courier", 8, "normal")
        self.dup_widget_label["relief"] = "groove"
        self.dup_widget_label.pack(anchor=W)
        self.dup_widget_label_plain_bg = self.dup_widget_label["background"]
        self.dup_widget_label["background"] = "#FFFACD"  # lemonchiffon
        
        # Refresh Preview Window
        #self.PlaceAllWidgetsButton = Button(frame2, text="Refresh Preview", width=18)
        #self.PlaceAllWidgetsButton.pack(anchor=W, side=TOP)
        #self.PlaceAllWidgetsButton.bind("<ButtonRelease-1>", self.GeneralDebugButton_Click)

    def GeneralDebugButton_Click(self, event):
        self.grid_notebook.repaint_all_labels()
        self.refresh_preview_win()

    def AddNewRowButton_Click(self, event):
        self.grid_notebook.append_row()
        
    def AddNewColButton_Click(self, event):
        self.grid_notebook.append_column()
        

    def PlaceAllWidgetsButton_Click(self, event):
        """As DEBUG TOOL, show all widgets in Notebook"""
        
        saved_sel = self.MainWin.placementWidgetType_svar.get()
        saved_tab = self.grid_notebook.current_tab_label()
        
        row = 1 # row_interface
        col = 1 # col_interface
        
        self.grid_notebook.set_current_tab_by_label( "Main" )
        
        for wname, wcolor in CONTROLS:
            self.MainWin.placementWidgetType_svar.set( wname )
            event.widget = self.grid_notebook.interface_gridBoxWidgetD[("Main", row, col)]# row_interface, col_interface
            self.grid_notebook.onGridBoxClicked( event )
            
            if wname in ContainerControlsL:
                new_tab_name = wname + "_%i"%(CONTROL_NEXT_NUMBER_D[wname] -1,)
                self.grid_notebook.set_current_tab_by_label( new_tab_name )
                
                # place some widgets on ContainerControlsL tab
                if wname == 'RadioGroup':
                    self.MainWin.placementWidgetType_svar.set( "Label" )
                    event.widget = self.grid_notebook.interface_gridBoxWidgetD[(new_tab_name, 2, 2)]# row_interface, col_interface
                    self.grid_notebook.onGridBoxClicked( event )
                    
                    for n_radio in range(3):
                        self.MainWin.placementWidgetType_svar.set( "Radiobutton" )
                        event.widget = self.grid_notebook.interface_gridBoxWidgetD[(new_tab_name, 3+n_radio, 2)]# row_interface, col_interface
                        self.grid_notebook.onGridBoxClicked( event )
                        
                    
                else:
                    self.MainWin.placementWidgetType_svar.set( "Button" )
                    event.widget = self.grid_notebook.interface_gridBoxWidgetD[(new_tab_name, 2, 2)]# row_interface, col_interface
                    self.grid_notebook.onGridBoxClicked( event )
                    
                    self.MainWin.placementWidgetType_svar.set( "Label" )
                    event.widget = self.grid_notebook.interface_gridBoxWidgetD[(new_tab_name, 3, 2)]# row_interface, col_interface
                    self.grid_notebook.onGridBoxClicked( event )
                    
                    self.MainWin.placementWidgetType_svar.set( "Entry" )
                    event.widget = self.grid_notebook.interface_gridBoxWidgetD[(new_tab_name, 4, 2)]# row_interface, col_interface
                    self.grid_notebook.onGridBoxClicked( event )
                    
                # restore notebook to Main tab
                self.grid_notebook.set_current_tab_by_label( "Main" )
                
            
            col += 1
            if col % 5 == 0:
                col = 1
                row += 1
                
        # Return Listbox_1 selection to original value
        self.MainWin.placementWidgetType_svar.set( saved_sel )
        self.grid_notebook.set_current_tab_by_label( saved_tab )
        
    def set_status_msg(self, msg):
        self.MainWin.statusMessage.set( msg )

    def gray_out_listbox(self, omit_list=None):
        """if omit_list is None, gray all."""
        if omit_list is None:
            omit_list = []
            
        n = -1
        for index, (wname, wcolor) in enumerate(CONTROLS):
            if wname in omit_list:
                self.Listbox_1.itemconfig(index, fg="black")
                n = index
            else:
                self.Listbox_1.itemconfig(index, fg="gray")
            self.Listbox_1.selection_clear(index)
            
        if n >= 0:
            self.Listbox_1.selection_set(n)
            self.Listbox_1_Click( 'FromGrayOut' ) # event is not used in Listbox_1_Click
    
    def restore_black_listbox(self):
        """Make sure all listbox options show up for all other tabs."""
        
        n = 0
        for i in self.Listbox_1.curselection():
            n = i
            break # set to 1st value encountered
    
        for index, (wname, wcolor) in enumerate(CONTROLS):
            self.Listbox_1.itemconfig(index, fg="black")
            self.Listbox_1.selection_clear(index)
            
        self.Listbox_1.selection_set(n)
        self.Listbox_1_Click( 'FromRestoreBlack' ) # event is not used in Listbox_1_Click

    def select_preview_tab(self, tab_name_inp):
        
        if not self.PreviewWin:
            self.refresh_preview_win() # make PreviewWin if not already done.
            
        tab_comp = self.target_app.compObjD[tab_name_inp]
        notebook_name = tab_comp.tab_label
        nb_obj = self.target_app.compObjD[ notebook_name ]
        
        # get index of tab on preview_win        
        for itab, (row, col, tab_name, tab_label) in enumerate( nb_obj.tab_nameL ):
            if tab_name_inp == tab_name:
                nb_obj.pw_widget.native_widget.select( itab )
                #print('grid_gui.tab_of_notebook_changed: set PreviewWin Tab to:', itab)
                break

    def tab_of_notebook_changed(self, event):
        
        if self.mouse_location == 'preview_win':
            return
        
        # look at the dup widget to see if it's a full widget duplication
        s_dup = self.dup_widget_label["text"].strip()
        if s_dup and s_dup.startswith('('): # i.e. widget has a (row,col) position
            saved_dup_text_on_tab_change = self.dup_widget_label["text"]        
            saved_dup_bg_on_tab_change   = self.dup_widget_label["background"]
        else:
            saved_dup_text_on_tab_change = ''
            saved_dup_bg_on_tab_change   = ''
        
        nb = self.grid_notebook.notebook
        #i = nb.index(nb.select())
        nb_tab_label = nb.tab(nb.select(), "text")
        #print("GridGUI Notebook Tab Set to:", nb_tab_label )

        self.Listbox_1.config( state=NORMAL )

        # gray out some listbox options for RadioGroup
        if nb_tab_label.startswith('RadioGroup'):
            self.gray_out_listbox( omit_list=("Radiobutton","Label") )
                        
        elif nb_tab_label.startswith('Notebook'):
            #print('=========>  Need to add Notebook logic.')
            
            self.gray_out_listbox( omit_list=None )
            self.set_placement_widget_label( 'Tab' )
            self.set_status_msg( "Only Tabs can be added to Notebook" )
            
        else:
            # Make sure all listbox options show up for all other tabs.
            self.restore_black_listbox()
            
            if saved_dup_text_on_tab_change:
                self.dup_widget_label["text"      ] = saved_dup_text_on_tab_change
                self.dup_widget_label["background"] = saved_dup_bg_on_tab_change
                self.dup_widget_label_desc["text"      ] = "\nduplicate widget\nand its properties"
                

        # Cause PreviewWin to switch to same Tab
        if nb_tab_label in self.target_app.compObjD: # i.e. the tab is in the TargetTkAppDef
            w = self.target_app.compObjD[ nb_tab_label ]
            treeL = w.get_tab_label_tree()
            #print('New widget tab_label_tree =', treeL )
            for name in treeL:
                if name.startswith('Tab_'):
                    self.select_preview_tab( name ) # nb_tab_label is tab_name
            
            

    def place_widget_selection_listbox(self, frame1):
        """frame1 in topFrame contains Widgets selection ListBox"""
        
        self.MainWin.placementWidgetType_svar=StringVar()
        self.MainWin.placementWidgetType_svar.set('Button')
        
        # Notebooks handled seperately

        self.Listbox_1 = Listbox(frame1,width=15 ,height=str(len(CONTROLS)), selectmode='single')#, selectmode="extended")
        self.Listbox_1.bind("<ButtonRelease-1>", self.Listbox_1_Click)

        for wname, wcolor in CONTROLS:
            #b = Radiobutton(frame1, text=text, value=cont, variable=self.MainWin.placementWidgetType_svar)
            #b.pack(anchor=W)
            #print("inserting wname into Listbox_1 "+wname)
            self.Listbox_1.insert(END, wname)
        
        self.Listbox_1.pack(anchor=W)
        self.Listbox_1.select_set(0) # make Top Item highlighted
            
    def cleanupOnQuit(self):
                
        if self.target_app.model_has_changed():
            dialog = maybe_save_dialog(self.MainWin, "Save Before Exit?")
            
            if (dialog.result is not None) and ( dialog.result['save_file'] == "yes"):
                self.saveasFile()
                return
            else:
                # ignore any other warnings
                self.target_app.reset_crc_reference() # for detecting changes to model
        
        
        #print( 'Doing final cleanup before quitting' )
        self.MainWin.allow_subWindows_to_close = 1
        self.MainWin.destroy()
    
    def set_duplication_widget_label(self, label_obj):
        """Given the widget Label object, set up for making duplicates."""
        
        self.dup_widget_label["text"      ] = label_obj["text"]
        self.dup_widget_label["background"] = label_obj["background"]
        self.dup_widget_label_desc["text"      ] = "\nduplicate widget\nand its properties"
        
    
    def set_placement_widget_label(self, widget_type):
        """Sets dup_widget_label for a simple Place of widget_type."""
        
        self.dup_widget_label["text"      ] = "\n%s\n"%widget_type
        #self.dup_widget_label["background"] = self.dup_widget_label_plain_bg # "#FFFACD"  # lemonchiffon
        self.dup_widget_label["background"] = CONTROL_COLOR_D[widget_type]
        self.dup_widget_label_desc["text"      ] = "\n\nPlace %s"%widget_type

        self.grid_notebook.dup_source_widget_name = '' # not a dup_widget_label event so no dup_source_widget_name
        
        
    def Listbox_1_Click(self, event): #click method for component ID=1
        
        val = 'Button'
        for i in self.Listbox_1.curselection():
            val = self.Listbox_1.get(i)# .lower()
        
        self.set_status_msg("Selected Widget: "+ val )
                
        self.MainWin.placementWidgetType_svar.set(val)

        tab_label = self.grid_notebook.current_tab_label()
        if not tab_label.startswith('Notebook'):
            # set duplicate widget text and background to indicate normal placement
            self.set_placement_widget_label( val )

    def hideOkChkBox_Callback(self, varName, index, mode):
        #print( 'Hide OK Button in Dialog =',self.MainWin.hideOkChkBox_StringVar.get(), end="\n")
        
        try:
            self.target_app.setSpecialOption('hideokbutton', self.MainWin.hideOkChkBox_StringVar.get())
        except:
            pass

        if self.MainWin.hideOkChkBox_StringVar.get() == "yes":
            self.PreviewWin.remove_mock_OK_Cancel_Buttons()
        else:
            self.PreviewWin.add_mock_OK_Cancel_Buttons()

    def menuChkBox_Callback(self, varName, index, mode):
        #print( 'make menu =',self.MainWin.menuChkBox_StringVar.get(), end="\n")
        
        try:
            self.target_app.setSpecialOption('hasmenu', self.MainWin.menuChkBox_StringVar.get())
            #print('Setting Menu "hasmenu" Option To:',self.MainWin.menuChkBox_StringVar.get())
        except:
            print('WARNING... Failed To Properly Set "hasmenu" Option.')
        
        if not self.in_reading_mode:
            if self.MainWin.menuChkBox_StringVar.get()=='yes' and self.target_app:
                dialog = Menumaker(self.MainWin, "Define Menu Structure",
                    self.target_app.getSpecialOption('menu'))
                #print( dialog.result, end="\n")
                
                if type(dialog.result) == type({}):

                    add_menu_ctrl_keys = dialog.result.get('add_menu_ctrl_keys','yes')
                    self.target_app.setSpecialOption('add_menu_ctrl_keys', add_menu_ctrl_keys)

                    menuStr = dialog.result.get('menu','').strip()
                    if len( menuStr ) > 0:
                        self.target_app.setSpecialOption('menu',menuStr)
                        #print( 'Recording new Menu Definition', end="\n")
                        
                        
        if not self.PreviewWin:
            self.refresh_preview_win() # make PreviewWin if not already done.

        # delete menuBar
        if self.MainWin.menuChkBox_StringVar.get()=='no' and self.target_app:
            if (self.PreviewWin is not None):
                if self.PreviewWin.menuBar:
                    self.PreviewWin.delete_menu()

        # create menuBar
        if self.MainWin.menuChkBox_StringVar.get()=='yes' and self.target_app:
            if (self.PreviewWin is not None):
                if self.PreviewWin.menuBar:
                    self.PreviewWin.delete_menu() # delete so menuBar can be recreated
                    
                menuL = buildMenuSource( self.target_app.getSpecialOption( 'menu' ) )
                menuSrcL = getMenuSource( menuL, rootName='self'  )
                    
                self.PreviewWin.add_menu( menuSrcL )
        
        self.refresh_preview_win() # redraw the form window showing menu state.

        
    def statusBarChkBox_Callback(self, varName, index, mode):
        #print( 'Status Bar =',self.MainWin.statusBarChkBox_StringVar.get(), end="\n")
        
        try:
            self.target_app.setSpecialOption('hasstatusbar', self.MainWin.statusBarChkBox_StringVar.get())
        except:
            pass
            
        if not self.PreviewWin:
            self.refresh_preview_win() # make PreviewWin if not already done.
        
        # add statusbar to PreviewWin
        if self.MainWin.statusBarChkBox_StringVar.get()=='yes' and self.target_app:
            if (self.PreviewWin is not None):
                if not self.PreviewWin.statusbar:
                    self.PreviewWin.add_mock_statusbar()
                    
        # remove statusbar from PreviewWin
        if self.MainWin.statusBarChkBox_StringVar.get()=='no' and self.target_app:
            if (self.PreviewWin is not None):
                if  self.PreviewWin.statusbar:
                    self.PreviewWin.remove_mock_statusbar()
                    
        self.refresh_preview_win() # redraw the form window showing menu state.

    def resizableChkBox_Callback(self, varName, index, mode):
        #print( 'Status Bar =',self.MainWin.resizableChkBox_StringVar.get(), end="\n")
        
        try:
            self.target_app.setSpecialOption('resizable', self.MainWin.resizableChkBox_StringVar.get())
        except:
            pass
                    
        self.refresh_preview_win() # redraw the form window showing menu state.
        
                
    def ColorPickButton_Click(self, event): #put selected color on clipboard
        self.ColorPickButton_Select()
        
    def ColorPickButton_Select(self): #put selected color on clipboard
        self.set_status_msg('Place Selected Color on Clipboard')
        ctup,cstr = tkinter.colorchooser.askcolor(title='Place Selected Color on Clipboard')
        if cstr != None:
            self.set_status_msg('%s is on Clipboard'%cstr)
            self.ColorPickButton.clipboard_clear()
            self.ColorPickButton.clipboard_append(cstr)
            
            #print( 'color chosen=',cstr, end="\n")
                
    def NamedColorPickButton_Click(self, event): #put selected color on clipboard
        self.NamedColorPickButton_Select()
        
    def NamedColorPickButton_Select(self): #put selected color on clipboard
        self.set_status_msg('Place Named Color on Clipboard')
        
        dialog = named_color_picker(self.MainWin, title="Place Named Color on Clipboard")
        if dialog.result is not None:
            (_, _, _, _, _, _, cstr, name) = dialog.result["named_color"]
            
            self.set_status_msg('%s %s is on Clipboard'%(name, cstr) )
            self.ColorPickButton.clipboard_clear()
            self.ColorPickButton.clipboard_append(cstr)
            
    def FontPickButton_Click(self, event):
        self.FontPickButton_Select()
    
    def FontPickButton_Select(self):
        self.set_status_msg('Place Selected Font on Clipboard')
        dialog = get_cross_platform_font(self.MainWin, title="Get Font")
        if dialog.result is not None:
            font_str = dialog.result['full_font_str']
            
            # for example:    Comic\ Sans\ MS 20 bold roman
                
            self.set_status_msg('%s is on Clipboard'%font_str)
            self.ColorPickButton.clipboard_clear()
            self.ColorPickButton.clipboard_append(font_str)
            
            #print( 'font chosen=',font_str, end="\n")
            

    def Help(self):
        
        usage = """
        
Basic Usage: 
  Select Widget in Listbox with Left Click.
  Place Widget with Left Click in grid.

Edit Widget:
  Right Click Widget in Grid or Preview Window.

Move Widget:
  Left Button Drag and Drop in Grid.

Duplicate Widget: 
  Left Click Widget in Grid.
  Left Click in grid to place the duplicate.

Insert Row or Column 
    Left Click on "+" control.

Add Weight to row or column 
    Left Click "wt" control.

Container Widgets
    Select Grid Tab for Main, Frames, RadioGroups etc.
"""
        
        myshowinfo(self.MainWin, title="Help for TkGridGUI",
                   dialogOptions={'info':usage, 'bg':'#EAFFFE'})

    def About(self):
        tkinter.messagebox.showinfo(
            "About TkGridGUI v(%s)"%__version__,
            "TkGridGUI v(%s) is:\n\n"%__version__+\
            "A quick approach to\n"+\
            "building Tkinter applications.\n"+\
            "Written by Charlie Taylor\n"
        )
    
    def newForm(self):
        #print( "New Form" )
        
        if self.target_app.model_has_changed():
            dialog = maybe_save_dialog(self.MainWin, "Save Current File?")
            
            if (dialog.result is not None) and ( dialog.result['save_file'] == "yes" ):
                self.saveasFile()
                return

        self.current_fileFullName = '' # no file for now
        # leave path alone... self.current_filePath = '' # no file for now
        self.current_fileName = '' # no file for now

        self.MainWin.title( 'TkGridGUI: ' + self.current_fileName )
        
        if self.PreviewWin:
            self.PreviewWin.delete_menu()
            self.PreviewWin.remove_mock_statusbar()

        self.target_app.reinitialize()
        self.grid_notebook.initialize_NotebookGridDes()
        self.grid_notebook.notebook.bind("<<NotebookTabChanged>>", self.tab_of_notebook_changed)
        
        self.refresh_preview_win()
        
        # ignore any other warnings
        self.target_app.reset_crc_reference() # for detecting changes to model
        

    def openFile(self, fName=None):
        #print( 'Open File' )
                
        if self.target_app.model_has_changed():
            dialog = maybe_save_dialog(self.MainWin, "Save Current File?")
            
            if (dialog.result is not None) and (dialog.result['save_file'] == "yes"):
                self.saveasFile()
                return
            else:
                # ignore any other warnings
                self.target_app.reset_crc_reference() # for detecting changes to model

        
        self.in_reading_mode = True # when True, suppresses some automatic trace actions.
        if fName is None:
            
            if self.current_filePath:
                initialdir = self.current_filePath
            else:
                initialdir = '.'
            
            filetypes = [
                ('tkGridGUI definition','*.def'),
                ('Any File','*.*')]
            self.pathopen = tkinter.filedialog.askopenfilename(parent=self.MainWin, title='Open tkGridGUI file', 
                filetypes=filetypes, initialdir=initialdir)
            #print('self.pathopen =',self.pathopen)
        else:
            self.pathopen = os.path.abspath(fName)
        
        if self.pathopen:
            
            self.newForm() # clean up any leftovers from another job
            
            full_fname = os.path.abspath( self.pathopen )
            head,tail = os.path.split( full_fname )
            
            self.current_fileFullName = full_fname
            self.current_filePath = head
            self.current_fileName = tail
                        
            self.target_app.readAppDefFile( self.pathopen )

            #'hasmenu':'no', 
            self.MainWin.menuChkBox_StringVar.set( self.target_app.getSpecialOption( 'hasmenu' ) )

            #'hasstatusbar':'no',
            self.MainWin.statusBarChkBox_StringVar.set( self.target_app.getSpecialOption( 'hasstatusbar' ) )

            #'resizable':'no'
            self.MainWin.resizableChkBox_StringVar.set( self.target_app.getSpecialOption( 'resizable' ) )
            
            #'hideokbutton':'no'
            self.MainWin.hideOkChkBox_StringVar.set( self.target_app.getSpecialOption( 'hideokbutton' ) )

            #'guitype':'main',
            self.MainWin.mainOrDialog.set( self.target_app.getSpecialOption( 'guitype' ) )

            #'hasstddialmess':'no', 
            self.MainWin.stdDialMessChkBox_StringVar.set( self.target_app.getSpecialOption( 'hasstddialmess' ) )

            #'hasstddialcolor':'no', 
            self.MainWin.stdDialColorChkBox_StringVar.set( self.target_app.getSpecialOption( 'hasstddialcolor' ) )

            #'hasstddialfile':'no', 
            self.MainWin.stdDialFileChkBox_StringVar.set( self.target_app.getSpecialOption( 'hasstddialfile' ) )

            #'hasstdalarm':'no', 
            self.MainWin.stdAlarmChkBox_StringVar.set( self.target_app.getSpecialOption( 'hasstdalarm' ) )


            #for key,val in self.target_app.app_attrD.items():
            #    print('%20s %s'%(key, str(val).replace('\t','    ')))
            
            widgetL = [(c.widget_type, widget_name, c.tab_label, c.row, c.col) for widget_name,c in list(self.target_app.compObjD.items())]
            self.grid_notebook.set_complete_list_of_widgets( widgetL )
            
            if not self.PreviewWin:
                self.refresh_preview_win() # make PreviewWin if not already done.
            
            # maybe create menuBar
            if self.MainWin.menuChkBox_StringVar.get()=='yes' and self.target_app:
                if (self.PreviewWin is not None):
                    if self.PreviewWin.menuBar:
                        self.PreviewWin.delete_menu() # delete so menuBar can be recreated
                        
                    menuL = buildMenuSource( self.target_app.getSpecialOption( 'menu' ) )
                    menuSrcL = getMenuSource( menuL, rootName='self'  )
                        
                    self.PreviewWin.add_menu( menuSrcL )
            
            
            # maybe add statusbar to PreviewWin
            if self.MainWin.statusBarChkBox_StringVar.get()=='yes' and self.target_app:
                if (self.PreviewWin is not None):
                    if not self.PreviewWin.statusbar:
                        self.PreviewWin.add_mock_statusbar()
            
            if self.PreviewWin:
                w = self.target_app.getSpecialOption( 'width' )
                h = self.target_app.getSpecialOption( 'height' )
                x = self.target_app.getSpecialOption( 'x' )
                y = self.target_app.getSpecialOption( 'y' )
                
                if self.PreviewWin.statusbar:
                    y += 30
                
                self.PreviewWin.geometry( '%ix%i+%i+%i'%(w,h,x,y))
                #self.PreviewWin.geometry( '' ) # allow to resize after set to x,y
                self.PreviewWin.update_idletasks()
                
            self.MainWin.title( 'TkGridGUI: ' + self.current_fileName )

            self.target_app.reset_crc_reference() # for detecting changes to model

        self.in_reading_mode = False # when True, suppresses some automatic trace actions.
        
        self.refresh_preview_win() # redraw the form window showing menu state
        
        self.grid_notebook.notebook.bind("<<NotebookTabChanged>>", self.tab_of_notebook_changed)
        

        
    def saveasFile(self):
        #print( 'Save File to Disk' )
        self.set_status_msg('Save File to Disk')
        
        filetypes = [
            ('TkGridGUI','*.def'),
            ('Any File','*.*')]
        
        if self.current_fileName:
            fname = self.current_fileName
        else:
            fname = ''
                        
        if self.current_filePath:
            initialdir = self.current_filePath
        else:
            initialdir = '.'
            
        fsave = tkinter.filedialog.asksaveasfilename(parent=self.MainWin, title='Saving TkGridGUI Definition File', 
            initialfile=fname, filetypes=filetypes, initialdir=initialdir)
        
        if fsave:
            if not fsave.lower().endswith('.def'):
                fsave += '.def'
                    
            full_fname = os.path.abspath( fsave )
            head,tail = os.path.split( full_fname )
            
            self.current_fileFullName = full_fname
            self.current_filePath = head
            self.current_fileName = tail
            
            # set values in target_app

            #'hasmenu':'no', 
            self.target_app.setSpecialOption( 'hasmenu', self.MainWin.menuChkBox_StringVar.get() )

            #'hasstatusbar':'no',
            self.target_app.setSpecialOption( 'hasstatusbar' , self.MainWin.statusBarChkBox_StringVar.get() )

            #'resizable':'no'
            self.target_app.setSpecialOption( 'resizable', self.MainWin.resizableChkBox_StringVar.get() )

            #'guitype':'main',
            self.target_app.setSpecialOption( 'guitype', self.MainWin.mainOrDialog.get() )
            
            #'hideokbutton':'no'
            self.target_app.setSpecialOption( 'hideokbutton', self.MainWin.hideOkChkBox_StringVar.get() )

            #'hasstddialmess':'no', 
            self.target_app.setSpecialOption( 'hasstddialmess', self.MainWin.stdDialMessChkBox_StringVar.get() )

            #'hasstddialcolor':'no', 
            self.target_app.setSpecialOption( 'hasstddialcolor', self.MainWin.stdDialColorChkBox_StringVar.get() )

            #'hasstddialfile':'no', 
            self.target_app.setSpecialOption( 'hasstddialfile', self.MainWin.stdDialFileChkBox_StringVar.get() )

            #'hasstdalarm':'no', 
            self.target_app.setSpecialOption( 'hasstdalarm', self.MainWin.stdAlarmChkBox_StringVar.get() )
            
            # first save *.def file
            if self.target_app.saveAppDefFile( savePathName=self.current_fileFullName ):
                # if that goes OK, then save *.py file
                                
                self.MainWin.title( 'TkGridGUI: ' + self.current_fileName )
                
                sf = FormSource( self.target_app, self.MainWin, self )
                sf.saveToFile() # save *.py file
                
                head, tail = os.path.split( sf.sourceFile.pathopen )
                self.set_status_msg('Saved File: "%s" and "%s" in "%s"'%(self.current_fileName, tail, head) )
                
                self.target_app.reset_crc_reference() # for detecting changes to model
                
            else:
                self.set_status_msg("WARNING... *.def file save failed.  NO SAVE PERFORMED.")