将用户输入从 child 传递到 parent window python tkinter

Pass user input from child to parent window python tkinter

我对 Python 很陌生,对 tkinter 更陌生。我已经搜索了这个问题的答案并得到了一些看起来很接近的东西,但是 none 我已经能够成功实现。

基本上我想做的是创建一个主 window 和一个下拉列表,供用户 select 从中选择一个项目。当用户生成 selection 时,会进行简单的计算并将结果打印到屏幕上。其中一个选项是 'User Defined'。制作此 selection 后,我想使用 Toplevel 创建一个 child window,允许用户进行一些输入并点击 'OK',此时完成相同的计算并在主要 window 上显示相同的数据,就好像他们 select 编辑了 pre-defined 选项一样。

我可以让 windows 根据需要打开和关闭,但是当用户定义一个新项目时,我无法将他们的输入传回主程序以进行必要的计算(这将然后是微不足道的显示)。如何将用户输入传回主 window?

在下面的示例代码中,下拉列表中的前 3 个项目一切正常,但对于用户定义它什么都不做,因为我不知道如何 return 用户输入的值来执行计算。

提前感谢任何help/advice。

import tkinter as tk
from tkinter import ttk
import numpy as np


class MainGUI:
    def __init__(self, master):
        self.master = master
        master.title('Bolted Joint Analysis')
        master.geometry('500x500')

        # Adds tabs to main window
        self.nb = ttk.Notebook(master)
        self.nb.grid(row=1, column=0, columnspan=50, rowspan=49, sticky='NESW')
        self.tab1 = ttk.Frame(self.nb)
        self.nb.add(self.tab1, text='Tab1')
        self.tab2 = ttk.Frame(self.nb)
        self.nb.add(self.tab2, text='Tab2')

        # defines a grid 50 x 50 cells in the main window & tabs
        rows = 0
        while rows < 50:
            master.rowconfigure(rows, weight=1)
            master.columnconfigure(rows, weight=1)

            self.tab1.rowconfigure(rows, weight=1)
            self.tab1.columnconfigure(rows, weight=1)

            self.tab2.rowconfigure(rows, weight=1)
            self.tab2.columnconfigure(rows, weight=1)

            rows += 1

        # Add Tab1 Labels
        self.boltLabel = tk.Label(self.tab1, text="Select A Bolt:")
        self.boltLabel.grid(column=0, row=1, sticky='SW')
        self.labelMajD = tk.Label(self.tab1, text="Bolt Major Dia. [in]:")
        self.labelMajD.grid(column=0, row=4, sticky='W')
        self.labelMinD = tk.Label(self.tab1, text="Bolt Minor Dia. [in]:")
        self.labelMinD.grid(column=0, row=5, sticky='W')
        self.labelPitchD = tk.Label(self.tab1, text="Bolt Pitch Dia. [in]:")
        self.labelPitchD.grid(column=0, row=6, sticky='W')

        # Add Tab1 Dropdown List - Bolt Choices
        self.boltValue = tk.StringVar()
        self.BoltList = ttk.Combobox(self.tab1, textvariable=self.boltValue, state='readonly')
        self.BoltList['values'] = ('',
                                   '#2-56 (UNC)',
                                   '1-1/2"-12 (UNF)',
                                   'User Defined')

        self.BoltList.grid(column=0, row=2, sticky='NS')
        self.BoltList.current(0)
        self.BoltList.bind("<<ComboboxSelected>>", self.boltSelectFunc)

        # Add Tab1 Entry boxes to display values
        self.majDiaBox = tk.Entry(self.tab1)
        self.majDiaBox.insert(0, '0.0000')
        self.majDiaBox.configure(state='disabled')
        self.majDiaBox.grid(column=1, row=4, sticky='NS')

        self.minDiaBox = tk.Entry(self.tab1)
        self.minDiaBox.insert(0, '0.0000')
        self.minDiaBox.configure(state='disabled')
        self.minDiaBox.grid(column=1, row=5, sticky='NS')

        self.pitchDiaBox = tk.Entry(self.tab1)
        self.pitchDiaBox.insert(0, '0.0000')
        self.pitchDiaBox.configure(state='disabled')
        self.pitchDiaBox.grid(column=1, row=6, sticky='NS')

    def UsrDefBolt(self):
        self.newWindow = tk.Toplevel(self.master)
        self.app = ChildWindow(self.newWindow)

    def boltSelectFunc(self, event):
        self.bolt = self.boltValue.get()
        print(self.bolt)

        if (self.bolt == ''):
            self.majDiaBox.configure(state='normal')
            self.majDiaBox.delete(0, 'end')
            self.majDiaBox.insert(0, '0.0000')
            self.majDiaBox.configure(state='disabled')
            self.minDiaBox.configure(state='normal')
            self.minDiaBox.delete(0, 'end')
            self.minDiaBox.insert(0, '0.0000')
            self.minDiaBox.configure(state='disabled')
            self.pitchDiaBox.configure(state='normal')
            self.pitchDiaBox.delete(0, 'end')
            self.pitchDiaBox.insert(0, '0.0000')
            self.pitchDiaBox.configure(state='disabled')

        elif (self.bolt == 'User Defined'):
            self.newBoltData = None
            self.UsrDefBolt()
            # self.boltSpecs = self.boltBasics(d, n)    # need to return d, n from child window to run this calculation

        else:
            if (self.bolt[0] == '#'):
                lhs, rhs = self.bolt.split("-")
                d = float(lhs[1:]) * 0.013 + .060
                n = float(rhs.split(" ")[0])
                self.boltSpecs = self.boltBasics(d, n)

                self.majDiaBox.configure(state='normal')
                self.majDiaBox.delete(0, 'end')
                self.majDiaBox.insert(0, format(self.boltSpecs['d'], '.4f'))
                self.majDiaBox.configure(state='disabled')
                self.minDiaBox.configure(state='normal')
                self.minDiaBox.delete(0, 'end')
                self.minDiaBox.insert(0, format(self.boltSpecs['dm'], '.4f'))
                self.minDiaBox.configure(state='disabled')
                self.pitchDiaBox.configure(state='normal')
                self.pitchDiaBox.delete(0, 'end')
                self.pitchDiaBox.insert(0, format(self.boltSpecs['dp'], '.4f'))
                self.pitchDiaBox.configure(state='disabled')

            else:
                lhs, rhs = self.bolt.split("\"-")
                n = float(rhs.split(" ")[0])

                if ("-" in lhs):
                    d = float(eval(lhs.replace("-", "+")))
                    self.boltSpecs = self.boltBasics(d, n)

                    self.majDiaBox.configure(state='normal')
                    self.majDiaBox.delete(0, 'end')
                    self.majDiaBox.insert(0, format(self.boltSpecs['d'], '.4f'))
                    self.majDiaBox.configure(state='disabled')
                    self.minDiaBox.configure(state='normal')
                    self.minDiaBox.delete(0, 'end')
                    self.minDiaBox.insert(0, format(self.boltSpecs['dm'], '.4f'))
                    self.minDiaBox.configure(state='disabled')
                    self.pitchDiaBox.configure(state='normal')
                    self.pitchDiaBox.delete(0, 'end')
                    self.pitchDiaBox.insert(0, format(self.boltSpecs['dp'], '.4f'))
                    self.pitchDiaBox.configure(state='disabled')

                else:
                    d = float(eval(lhs))
                    self.boltSpecs = self.boltBasics(d, n)

                    self.majDiaBox.configure(state='normal')
                    self.majDiaBox.delete(0, 'end')
                    self.majDiaBox.insert(0, format(self.boltSpecs['d'], '.4f'))
                    self.majDiaBox.configure(state='disabled')
                    self.minDiaBox.configure(state='normal')
                    self.minDiaBox.delete(0, 'end')
                    self.minDiaBox.insert(0, format(self.boltSpecs['dm'], '.4f'))
                    self.minDiaBox.configure(state='disabled')
                    self.pitchDiaBox.configure(state='normal')
                    self.pitchDiaBox.delete(0, 'end')
                    self.pitchDiaBox.insert(0, format(self.boltSpecs['dp'], '.4f'))
                    self.pitchDiaBox.configure(state='disabled')

    def boltBasics(self, d, n):
        P = 1.0 / n           # in - thread pitch
        dm = d - (1.299038 * P)  # in - external thread minor diameter
        dp = d - (0.649519 * P)  # in - bolt pitch Diameter
        return{'d': d, 'dm': dm, 'dp': dp}


class ChildWindow():
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        master.title('User Defined Bolt Info')
        master.geometry('350x250')
        master.focus_set()

        rows = 0
        while rows < 10:
            master.rowconfigure(rows, weight=1)
            master.columnconfigure(rows, weight=1)
            rows += 1

        self.boltName = tk.Label(master, text="Bolt Name (e.g. NewBolt1):")
        self.boltName.grid(column=5, row=1, sticky='NSEW')
        self.boltDia = tk.Label(master, text="Bolt Major Diameter [in]:")
        self.boltDia.grid(column=5, row=3, sticky='NSEW')
        self.boltTPI = tk.Label(master, text="Bolt Threads per Inch (TPI) [-]:")
        self.boltTPI.grid(column=5, row=5, sticky='NSEW')

        self.bName = tk.StringVar()
        self.bDia = tk.StringVar()
        self.bTPI = tk.StringVar()

        self.nameInput = tk.Entry(master, textvariable=self.bName)
        self.nameInput.insert(0, 'BoltName')
        self.nameInput.grid(column=5, row=2, sticky='NSEW')
        self.diaInput = tk.Entry(master, textvariable=self.bDia)
        self.diaInput.insert(0, '0.0000')
        self.diaInput.grid(column=5, row=4, sticky='NSEW')
        self.tpiInput = tk.Entry(master, textvariable=self.bTPI)
        self.tpiInput.insert(0, '0.0000')
        self.tpiInput.grid(column=5, row=6, sticky='NSEW')

        # Create button to save user defined bolt
        self.saveBoltBtn = tk.Button(master, text='Save Bolt', command=self.saveBolt)
        self.saveBoltBtn.bind('<Return>', self.saveBolt)
        self.saveBoltBtn.grid(column=5, row=8, sticky='NSEW')

    def saveBolt(self, *event):
        self.data = {}
        self.data['name'] = self.bName.get()
        self.data['d'] = float(self.bDia.get())
        self.data['n'] = float(self.bTPI.get())

        # NEED TO RETURN THIS DATA TO PARENT WINDOW

        self.master.destroy()


def main():
    root = tk.Tk()
    app = MainGUI(root)
    root.mainloop()


if __name__ == '__main__':
    main()

老实说,我没有阅读所有代码,但要将您的 "ChildWindow" class 的输入传递给您的 "MainGUI" 只需执行以下操作:

在您的 MainGUI 中 class 在您的 init 函数之前或在您的 init 函数中使用您想要的输入定义一个变量(不要忘记声明 self.your_variable)。

def __init__(self):
   self.input_from_child = None

由于您在 "Main()" 函数中初始化了 MainGUI,因此您可以在 ChildWindow 函数中更改变量:

app.input_from_child = "Whatever"

然后只需使用 MainGUI 中的变量 class 进行计算

我认为您将从此处继承 Tk class 和 Toplevel class 中受益。 这样您就可以简化在 class 之间传递数据的方式。我已经重写了您的代码以展示您如何在 2 classes 之间传递数据。在此示例中,我创建了一个名为 do_somthing_with_data 的方法,它将打印 self.data 的结果。我向您的主 class 添加了一个 class 属性,名为 self.data 并且从您的 Toplevel class 我通过引用 master 来操作这个 class 属性。从这里开始,您只需使用 self.data 进行数据操作,就像使用其他选项一样。

import tkinter as tk
from tkinter import ttk

class MainGUI(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title('Bolted Joint Analysis')
        self.geometry('500x500')
        # Adds tabs to main window
        self.nb = ttk.Notebook(self)
        self.nb.grid(row=1, column=0, columnspan=50, rowspan=49, sticky='NESW')
        self.tab1 = ttk.Frame(self.nb)
        self.nb.add(self.tab1, text='Tab1')
        self.tab2 = ttk.Frame(self.nb)
        self.nb.add(self.tab2, text='Tab2')
        self.data = {}
        # defines a grid 50 x 50 cells in the main window & tabs
        rows = 0
        while rows < 50:
            self.rowconfigure(rows, weight=1)
            self.columnconfigure(rows, weight=1)
            self.tab1.rowconfigure(rows, weight=1)
            self.tab1.columnconfigure(rows, weight=1)
            self.tab2.rowconfigure(rows, weight=1)
            self.tab2.columnconfigure(rows, weight=1)
            rows += 1
        # Add Tab1 Labels
        self.boltLabel = tk.Label(self.tab1, text="Select A Bolt:")
        self.boltLabel.grid(column=0, row=1, sticky='SW')
        self.labelMajD = tk.Label(self.tab1, text="Bolt Major Dia. [in]:")
        self.labelMajD.grid(column=0, row=4, sticky='W')
        self.labelMinD = tk.Label(self.tab1, text="Bolt Minor Dia. [in]:")
        self.labelMinD.grid(column=0, row=5, sticky='W')
        self.labelPitchD = tk.Label(self.tab1, text="Bolt Pitch Dia. [in]:")
        self.labelPitchD.grid(column=0, row=6, sticky='W')
        # Add Tab1 Dropdown List - Bolt Choices
        self.boltValue = tk.StringVar()
        self.BoltList = ttk.Combobox(self.tab1, textvariable=self.boltValue, state='readonly')
        self.BoltList['values'] = ('', '#2-56 (UNC)', '1-1/2"-12 (UNF)', 'User Defined')
        self.BoltList.grid(column=0, row=2, sticky='NS')
        self.BoltList.current(0)
        self.BoltList.bind("<<ComboboxSelected>>", self.boltSelectFunc)
        # Add Tab1 Entry boxes to display values
        self.majDiaBox = tk.Entry(self.tab1)
        self.majDiaBox.insert(0, '0.0000')
        self.majDiaBox.configure(state='disabled')
        self.majDiaBox.grid(column=1, row=4, sticky='NS')
        self.minDiaBox = tk.Entry(self.tab1)
        self.minDiaBox.insert(0, '0.0000')
        self.minDiaBox.configure(state='disabled')
        self.minDiaBox.grid(column=1, row=5, sticky='NS')
        self.pitchDiaBox = tk.Entry(self.tab1)
        self.pitchDiaBox.insert(0, '0.0000')
        self.pitchDiaBox.configure(state='disabled')
        self.pitchDiaBox.grid(column=1, row=6, sticky='NS')

    def do_somthing_with_data(self):
        print(self.data)

    def boltSelectFunc(self, event):
        self.bolt = self.boltValue.get()
        print(self.bolt)

        if (self.bolt == ''):
            self.majDiaBox.configure(state='normal')
            self.majDiaBox.delete(0, 'end')
            self.majDiaBox.insert(0, '0.0000')
            self.majDiaBox.configure(state='disabled')
            self.minDiaBox.configure(state='normal')
            self.minDiaBox.delete(0, 'end')
            self.minDiaBox.insert(0, '0.0000')
            self.minDiaBox.configure(state='disabled')
            self.pitchDiaBox.configure(state='normal')
            self.pitchDiaBox.delete(0, 'end')
            self.pitchDiaBox.insert(0, '0.0000')
            self.pitchDiaBox.configure(state='disabled')

        elif (self.bolt == 'User Defined'):
            self.newBoltData = None
            ChildWindow(self)


            # self.boltSpecs = self.boltBasics(d, n)    # need to return d, n from child window to run this calculation

        else:
            if (self.bolt[0] == '#'):
                lhs, rhs = self.bolt.split("-")
                d = float(lhs[1:]) * 0.013 + .060
                n = float(rhs.split(" ")[0])
                self.boltSpecs = self.boltBasics(d, n)
                self.majDiaBox.configure(state='normal')
                self.majDiaBox.delete(0, 'end')
                self.majDiaBox.insert(0, format(self.boltSpecs['d'], '.4f'))
                self.majDiaBox.configure(state='disabled')
                self.minDiaBox.configure(state='normal')
                self.minDiaBox.delete(0, 'end')
                self.minDiaBox.insert(0, format(self.boltSpecs['dm'], '.4f'))
                self.minDiaBox.configure(state='disabled')
                self.pitchDiaBox.configure(state='normal')
                self.pitchDiaBox.delete(0, 'end')
                self.pitchDiaBox.insert(0, format(self.boltSpecs['dp'], '.4f'))
                self.pitchDiaBox.configure(state='disabled')
            else:
                lhs, rhs = self.bolt.split("\"-")
                n = float(rhs.split(" ")[0])
                if ("-" in lhs):
                    d = float(eval(lhs.replace("-", "+")))
                    self.boltSpecs = self.boltBasics(d, n)
                    self.majDiaBox.configure(state='normal')
                    self.majDiaBox.delete(0, 'end')
                    self.majDiaBox.insert(0, format(self.boltSpecs['d'], '.4f'))
                    self.majDiaBox.configure(state='disabled')
                    self.minDiaBox.configure(state='normal')
                    self.minDiaBox.delete(0, 'end')
                    self.minDiaBox.insert(0, format(self.boltSpecs['dm'], '.4f'))
                    self.minDiaBox.configure(state='disabled')
                    self.pitchDiaBox.configure(state='normal')
                    self.pitchDiaBox.delete(0, 'end')
                    self.pitchDiaBox.insert(0, format(self.boltSpecs['dp'], '.4f'))
                    self.pitchDiaBox.configure(state='disabled')
                else:
                    d = float(eval(lhs))
                    self.boltSpecs = self.boltBasics(d, n)
                    self.majDiaBox.configure(state='normal')
                    self.majDiaBox.delete(0, 'end')
                    self.majDiaBox.insert(0, format(self.boltSpecs['d'], '.4f'))
                    self.majDiaBox.configure(state='disabled')
                    self.minDiaBox.configure(state='normal')
                    self.minDiaBox.delete(0, 'end')
                    self.minDiaBox.insert(0, format(self.boltSpecs['dm'], '.4f'))
                    self.minDiaBox.configure(state='disabled')
                    self.pitchDiaBox.configure(state='normal')
                    self.pitchDiaBox.delete(0, 'end')
                    self.pitchDiaBox.insert(0, format(self.boltSpecs['dp'], '.4f'))
                    self.pitchDiaBox.configure(state='disabled')

    def boltBasics(self, d, n):
        P = 1.0 / n           # in - thread pitch
        dm = d - (1.299038 * P)  # in - external thread minor diameter
        dp = d - (0.649519 * P)  # in - bolt pitch Diameter
        return{'d': d, 'dm': dm, 'dp': dp}


class ChildWindow(tk.Toplevel):
    def __init__(self, master):
        tk.Toplevel.__init__(self, master)
        self.frame = tk.Frame(self)
        self.title('User Defined Bolt Info')
        self.geometry('350x250')
        self.focus_set()
        rows = 0
        while rows < 10:
            self.rowconfigure(rows, weight=1)
            self.columnconfigure(rows, weight=1)
            rows += 1

        self.boltName = tk.Label(self, text="Bolt Name (e.g. NewBolt1):")
        self.boltName.grid(column=5, row=1, sticky='NSEW')
        self.boltDia = tk.Label(self, text="Bolt Major Diameter [in]:")
        self.boltDia.grid(column=5, row=3, sticky='NSEW')
        self.boltTPI = tk.Label(self, text="Bolt Threads per Inch (TPI) [-]:")
        self.boltTPI.grid(column=5, row=5, sticky='NSEW')
        self.bName = tk.StringVar()
        self.bDia = tk.StringVar()
        self.bTPI = tk.StringVar()
        self.nameInput = tk.Entry(self, textvariable=self.bName)
        self.nameInput.insert(0, 'BoltName')
        self.nameInput.grid(column=5, row=2, sticky='NSEW')
        self.diaInput = tk.Entry(self, textvariable=self.bDia)
        self.diaInput.insert(0, '0.0000')
        self.diaInput.grid(column=5, row=4, sticky='NSEW')
        self.tpiInput = tk.Entry(self, textvariable=self.bTPI)
        self.tpiInput.insert(0, '0.0000')
        self.tpiInput.grid(column=5, row=6, sticky='NSEW')
        # Create button to save user defined bolt
        self.saveBoltBtn = tk.Button(self, text='Save Bolt', command=self.saveBolt)
        self.saveBoltBtn.bind('<Return>', self.saveBolt)
        self.saveBoltBtn.grid(column=5, row=8, sticky='NSEW')

    def saveBolt(self, *event):
        self.master.data = {}
        self.master.data['name'] = self.bName.get()
        self.master.data['d'] = float(self.bDia.get())
        self.master.data['n'] = float(self.bTPI.get())
        self.master.do_somthing_with_data()
        # NEED TO RETURN THIS DATA TO PARENT WINDOW
        self.destroy()


def main():
    MainGUI().mainloop()

if __name__ == '__main__':
    main()