TkFront uses a similar strategy to control_dialog.py to simplify menu creation and use.
There are three dialog creation layers which can be called from a tkfront menu item:
import tkfront
import sys # for exit and arg
from copy import *
fid = "filename"
def open_callback():
global fid
fid = tkfront.open_dialog("python modules","*.py")
print "Opening:",fid
def save_callback():
global fid
print "Save file:",fid
def saveas_callback():
global filename
fid = tkfront.save_dialog("html files","*.html","tkfront.html")
print "Save as:",fid
def about_callback():
about_text = "TkFront Test Program by Robert Parker (c)2009"
tkfront.about_dialog(about_text)
def tk_key_event(event):
''' transfer key event to other windows if necessary'''
print "Key event code:",event.keycode
Done = False
def program_destroy_callback():
global Done
Done = True
def main():
menu_design = (("File",
(("Open road",open_callback),
("Save road",save_callback),
("SaveAs road",saveas_callback),
("Seperator",),
("Exit",tkfront.quit_callback))
),
("Help",
(("About",about_callback),
)
)
)
control_design = ("Just some text to show\n that it can be done.",
("About>>>",about_callback),"is the same as the menu item",
("QUIT",tkfront.quit_callback,"red")
)
tkfront.parent_destroy_callback = program_destroy_callback
tkfront.make_gui(menu_design,tk_key_event,control_design,"")
while not Done:
tkfront.update("Status Line")
sys.stdout.flush()
if __name__ == '__main__': main()
Running this will give you a simple window:
The callbacks for items in the menu should be familiar to any users of gui system and there is no difference here to a normal Tkinter application. It is how the menu is created in the main() function that simplifies the code. A simple nested tuple called menu design contains all the information required.
Text and buttons that appear on the window are created using a tuple called control_design. Only text and buttons can appear on the main window. Any complex editing of application variables is left to popup dialogs. Keeping it simple is the main criteria here, so buttons cannot even have images or bitmaps. For a fancy gui, either use raw Tkinter or move to a more advanced gui such as gtk/gdk, qt or wx.
The tkfront method make_gui is a wrapper around another method called control_gui. make_gui(menu_design_list,key_event_callback) takes care of Tkinter intialisation and keeps track of the parent window. It's call could be replaced by the following code:
root = Tkinter.Tk()
root.protocol("WM_DELETE_WINDOW", quit_callback)
main_dialog = control_gui(root,menu_design,contol_design)
main_dialog.pack()
tkfront.main_dialog = main_dialog
The tkfront method update() updates both the main Tkinter window and any child popup dialogs.
In this example a the menu has items with callbacks that open_item_dialog() and open_properties_dialog() to bring up managed and auto dialogs respectively. For more details on these see managed_dialog and auto_dialog but the reason for showing them here is to demonstrate the creat_popup_*() function in the tkfront arsenal.
Certainly more complex but it demonstrates how it all comes together.
Here is the code:
import tkfront
import sys # for exit and arg
from copy import *
fid = "filename"
def open_callback():
global fid
fid = tkfront.open_dialog("python modules","*.py")
print "Opening:",fid
def save_callback():
global fid
print "Save file:",fid
def saveas_callback():
global filename
fid = tkfront.save_dialog("html files","*.html","tkfront.html")
print "Save as:",fid
class testClass:
int_value = 3
def __init__(self,name):
self.name = name
self.float_value = 2.3
# make some data to play with
test_items = []
for i in range(10):
test_items.append(testClass("testClass#"+str(i)))
current_itemi = 0
def refreshDialog(d = None):
global current_item
# auto dialog replies with self for apply - but we want the other dialog if it exists
d = tkfront.get_popup_dialog("item_dialog")
if d != None:
new_item = copy(test_items[current_itemi])
d[0].setObj(current_item,new_item)
current_item = new_item
def dialogChange(d):
print "There was a change in the dialog"
d.change()
print "Current float:",current_item.float_value
test_items[current_itemi] = current_item
def previous_item_callback():
changeItem(-1)
def next_item_callback():
changeItem(1)
def changeItem(inc = 1):
global current_itemi
current_itemi += inc
if current_itemi < 0:
current_itemi = 0
elif current_itemi >= len(test_items):
current_itemi = len(test_items) - 1
print "Moving dialog to item#",current_itemi
refreshDialog()
def open_item_dialog():
global current_item
# list the fields that are required as used by control_dialog.py
# (label,initial_value,callback_function,max_value,min_value,float_precision)
current_item = copy(test_items[current_itemi])
dialog_array = (("Name",(current_item,'name')),
("Integer",(current_item,'int_value'),None,10,0),
("Float",(current_item,'float_value'),None,5,-5,3),
("Item->>>",None,previous_item_callback),
("Item+>>>",None,next_item_callback))
tkfront.create_popup_managed_dialog("item_dialog",dialog_array,"test item custom edit",dialogChange)
def open_properties_dialog():
tkfront.create_popup_auto_dialog("properties_dialog",test_items,None,refreshDialog)
def about_callback():
about_text = "TkFront Test Program by Robert Parker (c)2009"
tkfront.about_dialog(about_text)
def error_callback():
# create a non-blocking error
tkfront.error_dialog("TEST",False)
def tk_key_event(event):
''' transfer key event to other windows if necessary'''
print "Key event code:",event.keycode
Done = False
def program_destroy_callback():
global Done
Done = True
def main():
menu_design = (("File",
(("Open",open_callback),
("Save",save_callback),
("SaveAs",saveas_callback),
("Seperator",),
("Exit",tkfront.quit_callback))
),
("Dialogs",
(("Item",open_item_dialog,None,"item_dialog"),
("Network",open_properties_dialog)
)
),
("Help",
(("About",about_callback),
("Error",error_callback)
)
)
)
tkfront.parent_destroy_callback = program_destroy_callback
tkfront.make_gui(menu_design,tk_key_event,None,"0")
count = 0
while not Done:
try:
tkfront.update("%d"%count)
except:
pass # status line can raise "_tkinter.TclError: invalid command"during shutdown
sys.stdout.flush()
count +=1
count = count % 1000
if __name__ == '__main__': main()
The last two values in the line ("Item",open_item_dialog,None,"item_dialog") of menu design tell the menu item that it has two states (ticked or not). Observe that the tick appears when the item dialog exists, cannot be un-ticked and is un-ticked when the item dialog is closed. More on this in menu design protocol.
Class testClass exists only so we have something to edit. Ten instances of this class is created in test_items.
The callback open_item_dialog() is used to create the item dialog using the tkfront method create_popup_managed_dialog(...).
When the apply button is pressed on the dialog, the callback dialog_change(dialog) is used to update the actual data.
The callback refreshDialog() is used to update the values shown in open item dialog. In this case the tkfront method get_popup_dialog(key) is used to find the dialog rather than using a local copy (for no particular reason). This callback is used both when the next and previous buttons are used on the item dialog but also when the alternative properties dialog is used to change a value.
The error_callback() is used to demonstrate non-blocking error boxes which is rather useful in realtime applications.
The menu design list can be a list or tuple of the following elements:
The control design list, used to create text and buttons on the main window. can be a list or tuple of the following elements:
Following are the functions to be used in the tkfront module. You won't need to use them all normally.
First off grab the module tkfront here or the complete tkfront suite of modules as a zip file here. Test programs used on these webpages can also be downloaded here.
Installation is similar to control dialog except that both the auto_dialog.py and control_dialog.py modules must be in the path of your application.
Documentation can be generated by running python help("tkfront").
In case you didn't notice them, please heed the warnings on the microde page.
Copyright Robert Parker November 2009