I'm pretty new to Python, and even more new to tkinter. I've searched for an answer to this question and gotten some things that seem close, but none that I have been able to successfully implement.
Basically what I'm trying to do is create a main window with a dropdown list for the user to select an item from. When the user makes a selection, a simple calculation is done and the result printed to the screen. One of the choices is 'User Defined'. When this selection is made, I would like to create a child window using Toplevel that allows the user to make a few inputs and hit 'OK', at which point the same calculations are done and the same data displayed on the main window as if they had selected a pre-defined option.
I can get the windows to open and close as I want, but when the user defines a new item I can't pass their input back to the main program to make the necessary calculations (which would then be trivial to display). How do I pass the user inputs back to the main window?
In the sample code below, everything works for the first 3 items in the dropdown list, but for User Defined it does nothing since I can't figure out how to return the user inputted values to perform the calculations.
Thanks in advance for any 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()
i honestly didnt read all the code but to pass a input from your "ChildWindow" class to your "MainGUI" just do the following:
In your MainGUI class define a variable with the input you want either before your init function or in your init function (dont forget to declare self.your_variable).
Since you initialized the MainGUI in your "Main()" function, in your ChildWindow function you can just then change the variable:
And then just use the variable inside the MainGUI class to do your calculations
I think you would benefit from inheriting from the
Tk
class andToplevel
class here. This way you can simplify how you pass data between the classes. I have rewritten your code to show how you can pass data between the 2 classes. In this example I created a method calleddo_somthing_with_data
that will print the results ofself.data
. I added a class attribute to your main class calledself.data
and from your Toplevel class I manipulate this class attribute by referencing master. From here you simply need to do the data manipulation withself.data
that you would do with your other options.