#!/usr/bin/python ''' handle display related work upon Tkinter ''' import Tkinter import tkMessageBox import sys import ri_cmd import ri_widget import os.path var_dict={} # language should be all lower case letters language='english' def init(base_w): ''' base_w - base widget instance ''' global root_window root_window = Tkinter.Tk() # grid management for root_window root_window.columnconfigure(0, weight=1) root_window.rowconfigure(0, weight=1) root_window.geometry("%sx%s+0+0" %(root_window.winfo_screenwidth(),root_window.winfo_screenheight())) root_window.title('Linx') # bind WM_DELETE_WINDOW root_window.protocol("WM_DELETE_WINDOW", root_window.quit) global base_widget base_widget = create_widget_sub(base_w, root_window) def quit(): ''' exit root window ''' root_window.quit() def set_step_info(s): ''' set some information in base widget ''' # set step name which will be shown on base widget column 1 row 0 title = translate_text(s) var_dict['main.step_name'].set(title) def set_help_info(s): ''' set help information in base widget''' help = translate_text(s) w = ri_widget.Widget.dict['main.help'].tk_widget w.configure(state='normal') w.delete(1.0,Tkinter.END) w.insert(1.0,help) w.configure(state='disable') def create_widget(w): ''' w - widget instance ''' create_widget_sub(w, base_widget) # set step name if 'name' in dir(w): set_step_info(w.name) set_help_info('#'+w.name+'.help') class MyImage: ''' MyImage - a dummy class to hold Image variable ''' count = 0 def translate_text(txt): ''' multi-language support, translate t if needed ''' key = txt.lower() if key in ri_widget.Text.dict.keys() and (txt[0] == '#' or language != 'english'): return getattr(ri_widget.Text.dict[key], language) else: return txt def modify_attributes(attr_dict): ''' modify values in attr_dict to suit Tk usage ''' for a in attr_dict.keys(): if a == 'image': # I think there is a bug in Tkinter on Image processing # I have to bypass it in the following way: image_var = 'a' + str(MyImage.count) drt = os.path.join(os.path.dirname(__file__), '../data') setattr(MyImage, image_var, Tkinter.PhotoImage(file=os.path.join(drt, attr_dict[a]))) MyImage.count += 1 attr_dict[a] = getattr(MyImage, image_var) elif a == 'command': attr_dict[a] = getattr(sys.modules["ri_cmd"], attr_dict[a]) elif a == "textvariable" or a == "listvariable" or a == 'variable': attr_dict[a] = var_dict[attr_dict[a]] elif a == "text": attr_dict[a] = translate_text(attr_dict[a]) def create_widget_sub(w, p_win): ''' w - widget instance p_win - Tk parent window ''' # name type and value for v_n, v_t, v_v in w.variables: if not v_n in var_dict.keys(): # if not yet in dict, create it var_dict[v_n] = getattr(Tkinter, v_t)(value=v_v) # change attr, if needed to suit tk tk_attr = dict(w.attr) modify_attributes(tk_attr) tk_func = getattr(Tkinter, w.tp) w_win = tk_func(p_win, tk_attr) # save tk widget instance into w (widget instance) setattr(w, 'tk_widget', w_win) # display sub widgets for sub_w in w.widgets: create_widget_sub(sub_w, w_win) # process action init if 'action' in dir(w) and 'init' in w.action.dict: getattr(sys.modules['ri_cmd'], w.action.dict['init'])() # handle scroll bar for sub_widgets if 'action' in dir(w): # ing for scrolling, ed for scrolled for ing, ed in w.action.scrolls: ing_win = w.dict[ing].tk_widget ed_win = w.dict[ed].tk_widget if ing_win['orient'] == 'vertical': ed_cmd_name = 'yscrollcommand' ing_cmd = getattr(ed_win, 'yview') else: ed_cmd_name = 'xscrollcommand' ing_cmd = getattr(ed_win, 'xview') ing_win.configure(command=ing_cmd) ed_win[ed_cmd_name]=ing_win.set # grid management if 'grid_management' in dir(w): for cf in w.grid_management.cf_list: cf_func = getattr(w_win, cf[0]) cf_func( cf[1], weight=cf[2]) # grid location if 'grid_location' in dir(w): w_win.grid(w.grid_location.dict) # handle bindings for bd in w.bindings: w_win.bind(bd[0], getattr(ri_cmd, bd[1])) return w_win # When creating widget, I have to create each sub widget. # while destroying a widget, only destroy one. Tk functions will # destroy all descendant. # so init is a little different from quit. def process_action_quit(w): ''' process action quit ''' if 'action' in dir(w) and 'quit' in w.action.dict: getattr(sys.modules['ri_cmd'], w.action.dict['quit'])() if 'widgets' in dir(w): for sub_w in w.widgets: process_action_quit(sub_w) def destroy_widget(w): ''' w - Widget instance ''' process_action_quit(w) w.tk_widget.destroy() def create_message_box(w): ''' display MessageBox w - MessageBox instance''' disp = getattr(tkMessageBox, w.tp) code = disp(translate_text(w.title), translate_text(w.message)) return code def create_top_window(w): ''' display TopWindow w - TopWindow instance ''' tk_attr = dict(w.attr) modify_attributes(tk_attr) w_win = Tkinter.Toplevel(root_window, tk_attr) for sub_w in w.widgets: create_widget_sub(sub_w, w_win) if 'grid_management' in dir(w): for cf in w.grid_management.cf_list: cf_func = getattr(w_win, cf[0]) cf_func(cf[1], weight=cf[2]) # save tk widget instance into w (TopWindow instance) setattr(w, 'tk_widget', w_win) # 'bind' it to root window w_win.transient(root_window) # grab all events into it while True: try: w_win.grab_set() except Tkinter.TclError: pass else: break w_win.wait_window() return w_win def destroy_top_window(w): ''' w - Toplevel instance ''' w.tk_widget.destroy() class SoftwarePackageWindow(): ''' Toplevel window for a group of software packages class member --------------------------- dict - class static member instance member --------------------------- group - data layer group instance win - Tk widget selection - StringVar variable for 'select_all' opt_vars - a list to hold variables containing status for optional packages opt_chks - a list to hold Checkbuttons for optional packages ''' dict = {} def __init__(self, g): self.group = g self.opt_vars = [] self.opt_chks = [] SoftwarePackageWindow.dict[g.name] = self def select_all(self): ''' callback function for check button select_all ''' if self.selection.get() == 'all': for ck in self.opt_chks: ck.configure(state='disable') else: for ck in self.opt_chks: ck.configure(state='normal') def ok(self): ''' callback function for button OK ''' self.group.selection = self.selection.get() for i in range(len(self.group.optional)): # install field, yes or no self.group.optional[i][1] = self.opt_vars[i].get() # clear variables and check buttons self.opt_vars = [] self.opt_chks = [] self.win.destroy() def cancel(self): ''' callback function for button cancel ''' # clear variables and check buttons self.opt_vars = [] self.opt_chks = [] self.win.destroy() def show(self): win = Tkinter.Toplevel(root_window) self.win = win win.geometry("%sx%s+%s+%s" %(int(root_window.winfo_screenwidth()*0.8),\ int(root_window.winfo_screenheight()*0.8),\ int(root_window.winfo_screenwidth()*0.1),\ int(root_window.winfo_screenheight()*0.1))) win.columnconfigure(0, weight=1) win.rowconfigure(1, weight=1) win.rowconfigure(4, weight=1) text_mdt = translate_text('Mandatory') Tkinter.Label(win, text=text_mdt).grid(column=0, row=0) cnv1 = Tkinter.Canvas(win) hs1 = Tkinter.Scrollbar(win, orient='horizontal') hs1.configure(command=cnv1.xview) vs1 = Tkinter.Scrollbar(win, orient='vertical') vs1.configure(command=cnv1.yview) cnv1.grid(column=0, row=1, sticky='NWES') vs1.grid(column=1, row=1, sticky='NSW') hs1.grid(column=0, row=2, sticky='NWE') cnv1.configure(yscrollcommand=vs1.set, xscrollcommand=hs1.set) fr1 = Tkinter.Frame(cnv1) cnv1.create_window((0,0), window=fr1, anchor='nw') #column set to 5 fr1.columnconfigure(0, weight=1) fr1.columnconfigure(1, weight=1) fr1.columnconfigure(2, weight=1) fr1.columnconfigure(3, weight=1) fr1.columnconfigure(4, weight=1) for i in range(len(self.group.mandatory)): Tkinter.Label(fr1, text=self.group.mandatory[i]).grid(column=i%5, row=i/5, sticky='NWES') text_opt = translate_text('Optional') Tkinter.Label(win, text=text_opt).grid(column=0, row=3) self.selection = Tkinter.StringVar(value=self.group.selection) text_sal = translate_text('Select all') chk_sa = Tkinter.Checkbutton(win, text=text_sal, command=self.select_all, variable=self.selection, onvalue='all', offvalue='manual') chk_sa.grid(column=1, row=3) if self.group.selection == 'all': chk_sa.select() else: chk_sa.deselect() cnv2 = Tkinter.Canvas(win) hs2 = Tkinter.Scrollbar(win, orient='horizontal') hs2.configure(command=cnv2.xview) vs2 = Tkinter.Scrollbar(win, orient='vertical') vs2.configure(command=cnv2.yview) cnv2.grid(column=0, row=4, sticky='NWES') vs2.grid(column=1, row=4, sticky='NSW') hs2.grid(column=0, row=5, sticky='NWE') cnv2.configure(yscrollcommand=vs2.set, xscrollcommand=hs2.set) fr2 = Tkinter.Frame(cnv2) cnv2.create_window((0,0), window=fr2, anchor='nw') #column set to 5 fr2.columnconfigure(0, weight=1) fr2.columnconfigure(1, weight=1) fr2.columnconfigure(2, weight=1) fr2.columnconfigure(3, weight=1) fr2.columnconfigure(4, weight=1) for i in range(len(self.group.optional)): name, y_n = self.group.optional[i] v = Tkinter.StringVar(value='no') self.opt_vars.append(v) chk = Tkinter.Checkbutton(fr2, text=name, onvalue='yes', offvalue='no', variable=self.opt_vars[i]) self.opt_chks.append(chk) chk.grid(column=i%5, row=i/5, sticky='NWS') if y_n == 'yes': chk.select() else: chk.deselect() if self.group.selection == 'all': chk.configure(state='disable') Tkinter.Button(win, text='OK', command=self.ok).grid(column=0, row=6, pady=2) Tkinter.Button(win, text='Cancel', command=self.cancel).grid(column=1, row=6, pady=2) # bind WM_DELETE_WINDOW win.protocol("WM_DELETE_WINDOW", self.cancel) win.after_idle(lambda : cnv1.configure(scrollregion=(0,0, fr1.winfo_width(), fr1.winfo_height()))) win.after_idle(lambda : cnv2.configure(scrollregion=(0,0, fr2.winfo_width(), fr2.winfo_height()))) win.transient(root_window) # grab all events into it while True: try: win.grab_set() except Tkinter.TclError: pass else: break win.wait_window()