2014-09-24 2 views
2

У меня есть приложение на основе TKinter, которое пытается управлять настройками для игры. У пользователей может быть несколько версий этой игры, и в этом случае мне нужно спросить у пользователя, какую установку они хотят управлять при запуске. Эта часть работает достаточно хорошо сама по себе; открывается диалоговое окно выбора после того, как основное окно построено и выполняется модально.Скрытие окна корня Tkinter при показе модального окна

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

Я хотел бы скрыть окно корня во время показа этого диалогового окна, но вызов withdraw в корневом окне просто заставляет модальное диалоговое окно не отображаться - Python просто заканчивается зависанием без использования ЦП, и я могу «Определите, как обойти проблему, не прибегая к немодальному окну (и значительно отличающемуся потоку управления, которого я бы хотел избежать).

Пример кода экспонирование проблему и общую структуру кода (Python 2.7):

from Tkinter import * 
from ttk import * 

class TkGui(object): 
    def __init__(self): 
     self.root = root = Tk() 
     self.root.withdraw() 
     selector = FolderSelection(self.root, ('foo', 'bar')) 
     self.root.deiconify() 
     print(selector.result) 

class ChildWindow(object): #Base class 
    def __init__(self, parent, title): 
     top = self.top = Toplevel(parent) 
     self.parent = parent 
     top.title(title) 
     f = Frame(top) 
     self.create_controls(f) 
     f.pack(fill=BOTH, expand=Y) 

    def create_controls(self, container): 
     pass 

    def make_modal(self, on_cancel): 
     self.top.transient(self.parent) 
     self.top.wait_visibility() # Python will hang here... 
     self.top.grab_set() 
     self.top.focus_set() 
     self.top.protocol("WM_DELETE_WINDOW", on_cancel) 
     self.top.wait_window(self.top) # ...or here, if wait_visibility is removed 

class FolderSelection(ChildWindow): 
    def __init__(self, parent, folders): 
     self.parent = parent 
     self.listvar = Variable(parent) 
     self.folderlist = None 
     super(FolderSelection, self).__init__(parent, 'Select folder') 
     self.result = '' 
     self.listvar.set(folders) 
     self.make_modal(self.cancel) 

    def create_controls(self, container): 
     f = Frame(container) 
     Label(
      f, text='Please select the folder ' 
      'you would like to use.').grid(column=0, row=0) 
     self.folderlist = Listbox(
      f, listvariable=self.listvar, activestyle='dotbox') 
     self.folderlist.grid(column=0, row=1, sticky="nsew") 
     Button(
      f, text='OK', command=self.ok 
      ).grid(column=0, row=2, sticky="s") 
     self.folderlist.bind("<Double-1>", lambda e: self.ok()) 
     f.pack(fill=BOTH, expand=Y) 

    def ok(self): 
     if len(self.folderlist.curselection()) != 0: 
      self.result = self.folderlist.get(self.folderlist.curselection()[0]) 
      self.top.protocol('WM_DELETE_WINDOW', None) 
      self.top.destroy() 

    def cancel(self): 
     self.top.destroy() 

TkGui() 
+0

Пожалуйста, объясните: какая разница - если вы скрываете окно root - диалог является модальным или нет? Результаты кажутся одинаковыми? – NorthCat

+0

@NorthCat Применяя диалог модальный, вызывающий класс будет ждать, пока диалог будет закрыт, прежде чем перейти к следующей строке кода. Если я использую регулярное окно, он продолжит выполнение метода, и диалоговое окно должно будет вызвать событие в вызывающем классе после его завершения. Это также означает, что диалог вдруг должен действительно знать что-то о вызывающем классе. Я очень хотел бы избежать этого, так как я считаю, что он полезен для общей структуры и удобочитаемости кода. –

ответ

2

кажется, что в вашем случае нет никакой разницы, модальное окно или нет. На самом деле вам просто нужно использовать метод wait_window(). Согласовывающ docs:

Во многих ситуациях, это более удобно для обработки диалоговых окон в синхронно; создать диалог, отобразить его, дождаться пользователя , чтобы закрыть диалоговое окно, а затем возобновить выполнение вашего приложения. Метод wait_window - это именно то, что нам нужно; он входит в локальный цикл событий и не возвращается до тех пор, пока данное окно не будет уничтожено (либо с помощью метода destroy, либо явно через оконный менеджер).

Рассмотрим пример с с NonModal окна:

from Tkinter import * 

root = Tk() 

def go(): 
    wdw = Toplevel() 
    wdw.geometry('+400+400') 
    e = Entry(wdw) 
    e.pack() 
    e.focus_set() 
    #wdw.transient(root) 
    #wdw.grab_set() 
    root.wait_window(wdw) 
    print 'done!' 

Button(root, text='Go', command=go).pack() 
Button(root, text='Quit', command=root.destroy).pack() 

root.mainloop() 

При нажатии Go кнопки немодальный появится диалоговое окно, но код Виль исполнения стоп и строки done! будет отображаться только после закрытия диалогового окна ,

Если это поведение, которое вы хотите, то вот ваш пример в измененном виде (я изменил __init__ в TkGui и make_modal метод, также добавил mainloop()):

from Tkinter import * 
from ttk import * 

class TkGui(object): 
    def __init__(self): 
     self.root = root = Tk() 
     self.root.withdraw() 
     selector = FolderSelection(self.root, ('foo', 'bar')) 
     self.root.deiconify() 
     print(selector.result) 

class ChildWindow(object): #Base class 
    def __init__(self, parent, title): 
     top = self.top = Toplevel(parent) 
     self.parent = parent 
     top.title(title) 
     f = Frame(top) 
     self.create_controls(f) 
     f.pack(fill=BOTH, expand=Y) 

    def create_controls(self, container): 
     pass 

    def make_modal(self, on_cancel): 
     #self.top.transient(self.parent) 
     #self.top.wait_visibility() # Python will hang here... 
     #self.top.grab_set() 
     self.top.focus_set() 
     self.top.protocol("WM_DELETE_WINDOW", on_cancel) 
     self.top.wait_window(self.top) # ...or here, if wait_visibility is removed 

class FolderSelection(ChildWindow): 
    def __init__(self, parent, folders): 
     self.parent = parent 
     self.listvar = Variable(parent) 
     self.folderlist = None 
     super(FolderSelection, self).__init__(parent, 'Select folder') 
     self.result = '' 
     self.listvar.set(folders) 
     self.make_modal(self.cancel) 

    def create_controls(self, container): 
     f = Frame(container) 
     Label(
      f, text='Please select the folder ' 
      'you would like to use.').grid(column=0, row=0) 
     self.folderlist = Listbox(
      f, listvariable=self.listvar, activestyle='dotbox') 
     self.folderlist.grid(column=0, row=1, sticky="nsew") 
     Button(
      f, text='OK', command=self.ok 
      ).grid(column=0, row=2, sticky="s") 
     self.folderlist.bind("<Double-1>", lambda e: self.ok()) 
     f.pack(fill=BOTH, expand=Y) 

    def ok(self): 
     if len(self.folderlist.curselection()) != 0: 
      self.result = self.folderlist.get(self.folderlist.curselection()[0]) 
      self.top.protocol('WM_DELETE_WINDOW', None) 
      self.top.destroy() 

    def cancel(self): 
     self.top.destroy() 

TkGui() 
mainloop() 

код останавливается на линии selector = FolderSelection(self.root, ('foo', 'bar')) а затем продолжить после закрытия диалога.

+0

Ваш ответ заставил меня понять, что проблема на самом деле - это только вызов «переходного»; две другие линии в порядке.Я никогда не думал удалить «переходный», потому что это часть того, что делает его * диалоговым *, а не обычным окном, поскольку оно скрывает минимизацию и максимизацию, но поскольку не будет другого окна, для них все будет в порядке , Благодаря! –

Смежные вопросы