9

我正在使用 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()哪些不是我想要的。

4

2 回答 2

12

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

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

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

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

于 2012-05-23T10:58:05.677 回答
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)
于 2017-09-13T23:13:59.140 回答