Tkinter选项菜单的可移植消息框

1 投票
1 回答
2556 浏览
提问于 2025-04-16 15:43

我想创建一个轻量级的跨平台消息框,里面包含一系列选项。理想情况下,它应该有一个接口,可以让你传入要显示的消息、标题和选项的元组。当你按下“确定”时,它会返回当前选择的选项。最好是所需的模块都能在标准的Python发行版中找到。

Easygui有我想要的功能,叫做choicebox,可以在这个链接找到:http://easygui.sourceforge.net/download/version0.95/tutorial/index.html#contents_item_10.1。不过,它弹出的窗口太大了,而且它总是按字母顺序排列你的选项。由于这些“特性”,easygui并不是最理想的选择。

我还研究过bwidgets、pmw和Tix。在尝试这些时,我遇到了一些问题,包括:很难找到有效的示例,以及在不同平台上运行失败。

我现在的工作模型是使用Tkinter的OptionMenu和pickle来返回数据(见下面的代码示例)。虽然这样可以工作,但每次都要把选择保存到文件系统中,以避免使用全局变量,这让我觉得很烦。有没有办法在GUI关闭时返回选择的结果?

任何帮助或建议都将非常感激。请注意,这些示例仅供参考,可能在你的系统上运行不正常。

状态管理模块

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

菜单模块

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()

示例用法

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

编辑

不考虑我对全局变量的担忧,我尝试了一种使用全局变量的实现,看看是否更简单。这样确实简单了很多,但我仍然希望找到更好的方法。

下面是重新整理的菜单模块

from Tkinter import *
Prompt_Dropdown_Ok_Cancel_Selection = None

def Prompt_Dropdown_Ok_Cancel(title, message, options, default_selection=0):
    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

1 个回答

1

对话框的正常工作方式大致是这样的:

mydialog = SomeDialogClass(...)
result = mydialog.Show()
if  result == "OK":
    print "you clicked OK; dialog value is", mydialog.GetValue()
else:
    print "you clicked cancel"
mydialog.Destroy()

这里用的是伪代码,意思是它不依赖于特定的图形界面工具包(虽然看起来很像wxPython)。主要的思路是,你创建一个对话框对象,让这个对象显示出来,然后等待用户完成操作(比如点击“确定”或“取消”),接着再从这个对象获取用户输入的数据,最后销毁这个对象(或者保留它以便以后再用)。

第二种方法是编写代码,让对话框在需要的时候调用一个函数来设置值。像这样:

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"

这种第二种方法在对话框不是模态的时候效果很好(也就是说,当对话框出现时,程序仍然可以继续运行)。

撰写回答