Портативное окно сообщений Tkinter OptionMenu

Я пытаюсь создать легкое кроссплатформенное окно сообщения, содержащее список элементов. В идеале у него есть API, который позволяет передавать сообщение для отображения, заголовок и набор вариантов. При нажатии OK он вернет текущий выбранный вариант. Также было бы предпочтительно, чтобы необходимые модули были частью стандартных дистрибутивов Python.

В Easygui есть то, что я ищу, это поле выбора, которое можно найти по адресу http://easygui.sourceforge.net/download/version0.95/tutorial/index.html#contents_item_10.1. Однако всплывающее окно чудовищно, и оно всегда сортирует ваш список вариантов по алфавиту. Из-за этих «особенностей» easygui не идеален.

Я также изучал bwidgets, pmw и Tix. Пробуя их, я столкнулся с несколькими проблемами, в том числе с трудностями нахождения рабочих примеров и сбоями на разных платформах.

Моя рабочая модель использует OptionMenu и pickle Tkinter для возврата данных (см. Примеры кода ниже). Хотя это работает, довольно неприятно сохранять выбор в файловой системе, чтобы избежать использования глобальных переменных. Есть ли способ вернуть выделение после уничтожения графического интерфейса?

Любая помощь / совет будут очень благодарны. Обратите внимание, что эти примеры приведены только для справки, они могут работать или не работать должным образом в вашей системе.

Модуль управления состоянием

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

person Adam Lewis    schedule 14.04.2011    source источник


Ответы (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()

Это псевдокод, предназначенный для того, чтобы быть независимым от набора инструментов GUI (хотя, по общему признанию, он очень похож на 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"

Этот второй метод хорошо работает, если ваш диалог не является модальным (т.е. ваша программа продолжает работать, пока присутствует диалог).

person Bryan Oakley    schedule 14.04.2011
comment
Спасибо за ответ. Во-первых, я полностью согласен с вашими утверждениями. Я пытаюсь достичь первого примера, но проблема, с которой я сталкиваюсь, заключается в том, что SomeDialogClass не существует так, как мне нравится. Для этого мне нужно создать свой собственный класс. При этом есть ли у вас идеи, как вернуть значения из диалога без использования глобальных переменных? - person Adam Lewis; 14.04.2011
comment
@ Адам Льюис: Я не понимаю вашего вопроса - в моих примерах глобальные переменные не используются. - person Bryan Oakley; 22.05.2011
comment
Вопрос в том, как вернуть значение из существующего объекта TK, а не просто выводить что-то на стандартный вывод. Единственный способ добиться этого - использовать pickle или глобальные переменные (ни то, ни другое мне не нравится). Я знаю, что некоторые из существующих диалогов возвращают такие вещи, как «да» или «нет», но нет встроенного раскрывающегося диалогового окна, которое бы возвращало выбранное значение. - person Adam Lewis; 23.05.2011
comment
@ Адам Льюис: Я все еще не понимаю проблемы. Мой самый первый пример показывает, как вернуть значение. Разве вы не видите, где возвращается значение? Печать носит исключительно иллюстративный характер. Вы создаете экземпляр класса, вызываете метод Show, и когда он возвращается, вы можете спросить диалоговое окно, какое значение имеет значение. Конечно, вы можете изменить Show (), чтобы он возвращал да или нет, если хотите ... - person Bryan Oakley; 24.05.2011
comment
Я вижу, куда его вернули. Как я уже сказал в своем первом комментарии, это тот вариант использования, который я ищу, но SomeDialogClass, который я ищу, не существует. Я пытаюсь свернуть свой собственный, и когда я вызываю mainloop (см. Мои примеры выше), мне не удалось вернуть какое-либо значение (он возвращает пустой список). Как указано в вопросе выше: есть ли способ вернуть значение при уничтожении из объекта Tk (). - person Adam Lewis; 24.05.2011