Не удается закрыть корневое окно tkinter после закрытия modalPane - PullRequest
0 голосов
/ 06 февраля 2019

Я клонировал класс под названием ListBoxChoice, найденный в Интернете (добавив некоторые необходимые функции) ниже:

from Tkinter import *

class ListBoxChoice(object):
    def __init__(self, master=None, title=None, message=None,\
                 list=[]):
    self.master = master
    self.value = None
    self.list = list[:]

    self.modalPane = Toplevel(self.master)

    self.modalPane.transient(self.master)
    self.modalPane.grab_set()

    self.modalPane.bind("<Return>", self._choose)
    self.modalPane.bind("<Escape>", self._cancel)

    if title:
        self.modalPane.title(title)

    if message:
        Label(self.modalPane, text=message).pack(padx=5, pady=5)

    listFrame = Frame(self.modalPane)
    listFrame.pack(side=TOP, padx=5, pady=5)

    scrollBar = Scrollbar(listFrame)
    scrollBar.pack(side=RIGHT, fill=Y)

    # get the largest value of the 'list' to set the width
    widthOfList = 0
    for k in list:
        if len(str(k)) > widthOfList:
            widthOfList = len(str(k))

    # now pad some space to back of the widthOfList
    widthOfList = widthOfList + 2

    self.listBox = Listbox(listFrame, selectmode=SINGLE,\
                   width=widthOfList)

    self.listBox.pack(side=LEFT, fill=Y)
    scrollBar.config(command=self.listBox.yview)

    self.listBox.config(yscrollcommand=scrollBar.set)
    self.list.sort()

    for item in self.list:
        self.listBox.insert(END, item)

    buttonFrame = Frame(self.modalPane)
    buttonFrame.pack(side=BOTTOM)

    chooseButton = Button(buttonFrame, text="Choose",\
                   command=self._choose)
    chooseButton.pack()

    cancelButton = Button(buttonFrame, text="Cancel",\
                   command=self._cancel)
    cancelButton.pack(side=RIGHT)

    def _choose(self, event=None):
        try:
            firstIndex = self.listBox.curselection()[0]
            self.value = self.list[int(firstIndex)]
        except IndexError:
            self.value = None
        self.modalPane.destroy()

    def _cancel(self, event=None):
        self.modalPane.destroy()

    def returnValue(self):
        self.master.wait_window(self.modalPane)
        return self.value

if __name__ == '__main__':
    import random
    root = Tk()

    returnValue = True
    list = [random.randint(1,100) for x in range(50)]
    while returnValue:
        returnValue = ListBoxChoice(root, "Number Picking",\
                     "Pick one of these crazy random numbers",\
                     list).returnValue()
        print returnValue

Теперь в этом примере сказано сделать что-то вроде этого:results = ListBoxChoice(root, list=listOfItems).returnValue().

Я пытаюсь предоставить список значений, из которых пользователь выбирает одно значение.Окно должно закрыться, прежде чем я буду использовать результаты выбранного значения.Вот этот код:

from tkinter import Tk, Label
form ListBoxChoice import ListBoxChoice
...
eventList = ["20190120","20190127","20190203"]
root = Tk()
root.withdraw() # This causes the ListBoxChoice object not to appear
selectValue = ListBoxChoice(root, title="Event",\
              message="Pick Event", list=eventList).returnValue()
root.wait_window() # Modal Pane/window closes but not the root
print("selectValue:", selectValue)

Окно root расположено за modalPane (Toplevel).Я должен закрыть это окно, прежде чем процесс вызова продолжится.Таким образом, на месте есть блок.

Я пытался поставить команду sleep(1.01) выше, но безрезультатно.Как заставить ListBoxChoice закрыться после того, как выбор был сделан до моего print оператора selectValue?Поскольку именно в этот момент я хочу использовать результаты для построения графика данных.

Если я не использую root.wait_winow(), то только когда график закрыт (конец процесса), ListBoxChoice окно также закрыто.

Предложения?

1 Ответ

0 голосов
/ 07 февраля 2019

Немного обновлено

Вот версия класса ListBoxChoice, которая, я думаю, работает так, как вы хотите.Я немного обновил свой предыдущий ответ, поэтому класс теперь определен в отдельном модуле с именем listboxchoice.py.Это не изменило ничего, что я мог видеть, когда я тестировал - другими словами, это все еще кажется работающим - но я хотел более точно смоделировать то, как вы сказали, что вы используете это, комментарии.

Он все еще используетwait_window() потому что для этого требуется , чтобы дать возможность запускать обязательный цикл обработки событий tkinter (поскольку mainloop() нигде не вызывается).В статье Диалог Windows есть несколько хороших справочных материалов о программировании диалогов tkiner, которые могут оказаться полезными.Добавленный вызов root.withdraw() устраняет проблему невозможности закрыть его, потому что его там нет.Это нормально, поскольку нет необходимости отображать пустое окно.

test_lbc.py

import random
try:
    import Tkinter as tk  # Python 2
except ModuleNotFoundError:
    import tkinter as tk  # Python 3
from listboxchoice import ListBoxChoice


root = tk.Tk()
root.withdraw()  # Hide root window.

values = [random.randint(1, 100) for _ in range(50)]
choice = None

while choice is None:
    choice = ListBoxChoice(root, "Number Picking",
                           "Pick one of these crazy random numbers",
                           values).returnValue()
print('choice: {}'.format(choice))

listboxchoice.py

""" ListBoxChoice widget to display a list of values and allow user to
    choose one of them.
"""
try:
    import Tkinter as tk  # Python 2
except ModuleNotFoundError:
    import tkinter as tk  # Python 3


class ListBoxChoice(object):
    def __init__(self, master=None, title=None, message=None, values=None):
        self.master = master
        self.value = None
        if values is None:  # Avoid use of mutable default argument value.
            raise RuntimeError('No values argument provided.')
        self.values = values[:]  # Create copy.

        self.modalPane = tk.Toplevel(self.master, takefocus=True)
        self.modalPane.bind("<Return>", self._choose)
        self.modalPane.bind("<Escape>", self._cancel)

        if title:
            self.modalPane.title(title)

        if message:
            tk.Label(self.modalPane, text=message).pack(padx=5, pady=5)

        listFrame = tk.Frame(self.modalPane)
        listFrame.pack(side=tk.TOP, padx=5, pady=5)

        scrollBar = tk.Scrollbar(listFrame)
        scrollBar.pack(side=tk.RIGHT, fill=tk.Y)

        # Get length the largest value in 'values'.
        widthOfList = max(len(str(value)) for value in values)
        widthOfList += 2  # Add some padding.

        self.listBox = tk.Listbox(listFrame, selectmode=tk.SINGLE, width=widthOfList)

        self.listBox.pack(side=tk.LEFT, fill=tk.Y)
        scrollBar.config(command=self.listBox.yview)

        self.listBox.config(yscrollcommand=scrollBar.set)
        self.values.sort()

        for item in self.values:
            self.listBox.insert(tk.END, item)

        buttonFrame = tk.Frame(self.modalPane)
        buttonFrame.pack(side=tk.BOTTOM)

        chooseButton = tk.Button(buttonFrame, text="Choose", command=self._choose)
        chooseButton.pack()

        cancelButton = tk.Button(buttonFrame, text="Cancel", command=self._cancel)
        cancelButton.pack(side=tk.RIGHT)

    def _choose(self, event=None):
        try:
            firstIndex = self.listBox.curselection()[0]
            self.value = self.values[int(firstIndex)]
        except IndexError:
            self.value = None
        self.modalPane.destroy()

    def _cancel(self, event=None):
        self.modalPane.destroy()

    def returnValue(self):
        self.master.wait_window(self.modalPane)
        return self.value
...