【问题标题】:How to create child window and communicate with parent in TkInter如何在 TkInter 中创建子窗口并与父窗口通信
【发布时间】:2012-05-23 10:27:36
【问题描述】:

我正在使用 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


    【解决方案1】:

    至少有几种方法可以解决您的问题。您的对话框可以直接向主应用程序发送信息,或者您的对话框可以生成一个事件,告诉主应用程序数据确实要从对话框中提取。如果对话框只是改变了某些东西的外观(例如,字体对话框),我通常会生成一个事件。如果对话框创建或删除数据,我通常会让它将信息推送回应用程序。

    我通常有一个应用程序对象作为整个 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 ...
    

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

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

    【讨论】:

    • 非常感谢,真的很有帮助。
    • #bryan-oakley 不错的技术总结。您是否推荐有关父子窗口之间数据交换的此类技术(Internet 或书籍资源)的更多详细参考资料? : - 将事件生成回父级 - 将数据推回父级 - ...
    【解决方案2】:

    在我的一个项目中,我试图在我的根窗口 (self) 的子 tk.Toplevel 窗口 (child1) 中检查,如果用户从根窗口中创建了 tk.Toplevel 窗口 (child2) , 并且如果这个窗口 (child2) 目前出现在用户屏幕上。

    如果不是这种情况,新的 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: bad window path name

    解决方案:

    获得正确的“窗口路径名”的唯一方法是,而不是直接在“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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-24
      相关资源
      最近更新 更多