如何在 TkInter 中创建子窗口并与父窗口通信

问题描述 投票:0回答:3

我正在使用 TkInter 创建一些对话框,并且需要能够在单击父级中的按钮时打开子子窗口(模式或非模式)。然后,子窗口将允许创建数据记录,并且需要将该数据(记录或操作被取消)传送回父窗口。到目前为止我已经:

import sel_company_dlg

from Tkinter import Tk

def main():
    root = Tk()
    myCmp = sel_company_dlg.SelCompanyDlg(root)
    root.mainloop()

if __name__ == '__main__':
    main()

这将调用顶层对话框,允许用户选择公司。公司选择对话框如下所示:

class SelCompanyDlg(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent_ = parent
        self.frame_ = Frame( self.parent_ )
        // .. more init stuff ..
        self.btNew_ = Button( self.frame_, text="New ...", command=self.onNew )

    def onNew(self):
        root = Toplevel()
        myCmp = company_dlg.CompanyDlg(root)

单击“新建...”按钮时,将显示“创建公司”对话框,用户可以在其中填写公司详细信息并单击“创建”或“取消”。这是开头部分: class CompanyDlg(Frame): def __init__(self, parent): Frame.__init__(self, parent) // etc.

我正在努力寻找在 
onNew()

中调用子对话框的最佳方法 - 我所拥有的方法有效,但我不相信这是最好的方法,而且,我不知道如何与子对话框。


我尝试查看在线教程/参考资料,但我发现的内容要么太简单,要么专注于诸如

tkMessageBox.showinfo()

之类的事情,这不是我想要的。

    

python tkinter
3个回答
14
投票

我通常有一个应用程序对象,它充当整个 GUI 的控制器。通常这与主窗口是同一个类,或者它可以是一个单独的类,甚至定义为一个 mixin。该应用程序对象具有对话框可以调用的方法来将数据提供给应用程序。

例如:

class ChildDialog(tk.Toplevel): def __init__(self, parent, app, ...) self.app = app ... self.ok_button = tk.Button(parent, ..., command=self.on_ok) ... def on_ok(self): # send the data to the parent self.app.new_data(... data from this dialog ...) class MainApplication(tk.Tk): ... def on_show_dialog(self): dialog = ChildDialog(self) dialog.show() def new_data(self, data): ... process data that was passed in from a dialog ...

创建对话框时,您传入对应用程序对象的引用。然后,对话框知道调用该对象上的特定方法以将数据发送回应用程序。

如果您不喜欢整个模型/视图/控制器的事情,您可以轻松地传入一个函数而不是一个对象,有效地告诉对话框“当您想给我数据时调用此函数”。


2
投票

如果不是这种情况,新的 tk.Toplevel 窗口应该由根窗口的子窗口 (child1) 创建,而不是根窗口本身。如果它已经由根窗口创建并且当前出现在用户屏幕上,则它应该获得 focus() 而不是由“child1”重新初始化。

根窗口被包装在一个名为 App() 的类中,并且两个“子”窗口都是由根类 App() 中的方法创建的。

如果给该方法的参数为 True,我必须在安静模式下初始化“child2”。我想这就是那个纠缠的错误。如果问题很严重,则问题发生在 Windows 7 64 位上。

我尝试过这个(示例):

import tkinter as tk from tkinter import ttk class App(tk.Tk): def __init__(self): tk.Tk.__init__(self) top = self.winfo_toplevel() self.menuBar = tk.Menu(top) top['menu'] = self.menuBar self.menuBar.add_command(label='Child1', command=self.__create_child1) self.menuBar.add_command(label='Child2', command=lambda: self.__create_child2(True)) self.TestLabel = ttk.Label(self, text='Use the buttons from the toplevel menu.') self.TestLabel.pack() self.__create_child2(False) def __create_child1(self): self.Child1Window = tk.Toplevel(master=self, width=100, height=100) self.Child1WindowButton = ttk.Button(self.Child1Window, text='Focus Child2 window else create Child2 window', command=self.CheckForChild2) self.Child1WindowButton.pack() def __create_child2(self, givenarg): self.Child2Window = tk.Toplevel(master=self, width=100, height=100) if givenarg == False: self.Child2Window.withdraw() # Init some vars or widgets self.Child2Window = None else: self.Child2Window.TestLabel = ttk.Label(self.Child2Window, text='This is Child 2') self.Child2Window.TestLabel.pack() def CheckForChild2(self): if self.Child2Window: if self.Child2Window.winfo_exists(): self.Child2Window.focus() else: self.__create_child2(True) else: self.__create_child2(True) if __name__ == '__main__': App().mainloop()

问题来了:
我无法检查“child2”是否已经存在。出现错误:_tkinter.TclError:错误的窗口路径名

解决方案:

获得正确“窗口路径名称”的唯一方法是,不直接在“child2”窗口上调用 winfo_exists() 方法,而是调用“child1”窗口的主窗口并添加相应的属性,然后添加您要使用的主窗口。

示例(方法 CheckForChild2 的编辑):

def CheckForChild2(self): if self.Child2Window: if self.Child1Window.master.Child2Window.winfo_exists(): self.Child1Window.master.Child2Window.focus() else: self.__create_child2(True) else: self.__create_child2(True)



0
投票

enter image description here 为此,我有一个简单的数据类:

@dataclass class Config : tempPort : str = '' tempBaudIndex : int = 12 # 9600 voltPort : str = '' voltBaudIndex : int = 12 # 9600

我使用作为 kwarg 传递的 
config

参数创建对话框:

dialog = Settings(master = self,
                  config = self._connection, # <--- last connection configuration (defaults for the dialog)
                  title = 'Communication settings',
                  topmost = True,
                  windowtype = 'dialog',
                  resizable = (False, False))
dialog.grab_set()
self.wait_window(dialog)

if dialog.config == None : # means user pressed Cancel
    return

self._connection = dataclasses.replace(dialog.config) # means user pressed Ok, and we store the config

Cancel

上,对话框会将配置重置为

None
    

© www.soinside.com 2019 - 2024. All rights reserved.