Tkinter OptionMenu Portable Message Box
I am trying to create a light weight cross platform Message Box that contains a list of items. Ideally it has an API that allows you to pass in a message to to display, a title, and tuple of choices. When pressing OK it would return the currently selected choice. It would also be preferred that the required modules be part of the standard python distributions.
Easygui has what I am looking for called a choicebox found at http://easygui.sourceforge.net/download/version0.95/tutorial/index.html#contents_item_10.1. However the window it pops up is monstrous and it always sorts your list of choices alphabetically. Because of these 'features', easygui is not ideal.
I have also looked into bwidgets, pmw, and Tix. While trying these I have come across a few issues including: difficultly finding working examples and failures across different platforms.
My working model is using Tkinter's OptionMenu and pickle to return the data (see code samples below). While this works, it is rather annoying having to save the choice to the file system to avoid using global variables. Is there a way to return the selection upon destruction of the gui?
Any help / advice would be greatly appreciated. Note that these examples are only for reference, they may or may not run properly on your system.
State Management Module
import pickle
def store(pkl_path, data_to_store):
try:
fid = open(pkl_path, 'w')
pickle.dump(data_to_store, fid)
except:
print 'Unable to store data in ' + pkl_path
else:
fid.close()
def load(pkl_path):
try:
fid = open(pkl_path, 'r')
loaded_state = pickle.load(fid)
fid.close()
except:
loaded_state = None
else:
fid.close()
return loaded_state
Menu Module
from Tkinter import *
def Prompt_Dropdown_Ok_Cancel(title, options, pickle_file, default_selection=0):
master = Tk()
master.title(title)
var = StringVar(master)
var.set(options[default_selection]) # default value
w = OptionMenu(master, var, *options)
w.pack()
def ok():
state.store(pickle_file, var.get())
master.quit()
def cancel():
state.store(pickle_file, None)
master.quit()
button = Button(master, text="OK", command=ok)
button.pack()
b2 = Button(master, text="Cancel", command=cancel)
b2.pack()
mainloop()
Example Usage
from menu_module import *
def display_com_selection():
pkl_path = '.tmp/comm_selection'
title = 'COM Port Selection'
Prompt_Dropdown_Ok_Cancel(title,get_available_com(),pkl_path)
selection = state.load(pkl_path)
return selection
EDIT
Disregarding my concern about global variables, I tried an implementation using them to see if it was any easier. It makes things substantially easier, however my question still stands for a better way to do this.
Below is the reworked Menu Module
from Tkinter import *
Prompt_Dropdown_Ok_Cancel_Selection = None
def Prompt_Dropdown_Ok_Cancel(title, message, options, default_selection=0):开发者_StackOverflow中文版
master = Tk()
master.title(title)
var = StringVar(master)
var.set(options[default_selection]) # default value
l = Label(master, text=message)
l.pack()
w = OptionMenu(master, var, *options)
w.pack(fill=BOTH, expand=1)
def ok():
global Prompt_Dropdown_Ok_Cancel_Selection
Prompt_Dropdown_Ok_Cancel_Selection = str(var.get())
master.destroy()
def cancel():
global Prompt_Dropdown_Ok_Cancel_Selection
Prompt_Dropdown_Ok_Cancel_Selection = str(var.get())
master.destroy()
button = Button(master, text="OK", command=ok)
button.pack(side=LEFT)
b2 = Button(master, text="Cancel", command=cancel)
b2.pack(side=LEFT)
mainloop()
return Prompt_Dropdown_Ok_Cancel_Selection
The normal way dialogs work is something like this:
mydialog = SomeDialogClass(...)
result = mydialog.Show()
if result == "OK":
print "you clicked OK; dialog value is", mydialog.GetValue()
else:
print "you clicked cancel"
mydialog.Destroy()
This is pseudocode, intended to be GUI toolkit agnostic (though admittedly it looks a lot like wxPython). The main idea is, you create the dialog as an object, ask the object to show itself, wait until the user is done (by virtue of clicking "OK" or "Cancel"), then asking the object for its data and then finally destroying the object (or, keep it around for re-use).
A second way to do this is to write your code such that you give the dialog a function to call in order to set the value. Something like this:
mydialog = SomeDialogClass(..., callback=self.foo)
....
def foo(self, button, result):
if button == "OK":
print "you clicked OK; result is", result
elif button == "Cancel":
print "you clicked Cancel"
This second method works well if your dialog is not modal (ie: your program continues to run while the dialog is present).
精彩评论