Getting multi-tabbed tkinter application with buttons to work properly - python

I have a small test application using Python tkinter that I got to work. The problem is that it does not exit properly when the "Quit" button is pressed. It is a two-frame tabbed application where I started with the StackOverflow question ttk tkinter multiple frames/windows.
It is now a full example that works, but needs work because it doesn't quit and exit properly. When I press the "Quit" button, it kills the frame for that tab, but the application doesn't quit and exit properly. I have to hit the Window "X" Close icon to close it.
My main question is how (and where?) do I test for the event on either the "Quit" button on the "Feet to Meters" calculator, or the "Cancel/Quit" button on the BMI calculator.
A second question I have is that the design of the application seems inefficient to me, because it creates two widgets "Frame" objects, each with their own set of buttons, including 2 "quit" buttons. How do I put these tabs and frames into a parent window and then add a quit button on that parent window to close the entire application.
I modified the buttons to properly destroy the Frame that the button is in:
Changed button2 "command=self.quit" to "command=self.destroy"
self.button2 = ttk.Button(self, text="Cancel/Quit", command=self.quit).grid(row=3, column=1, sticky=E)
to
self.button2 = ttk.Button(self, text="Cancel/Quit", command=self.destroy).grid(row=3, column=1, sticky=E)
""" Created on Thu Jul 11 17:20:22 2019 """
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
class App1(ttk.Frame):
""" This application calculates BMI and returns a value. """
def __init__(self, master=None):
ttk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
#text variables
self.i_height = StringVar()
self.i_weight = StringVar()
self.o_bmi = StringVar()
#labels
self.label1 = ttk.Label(self, text="Enter your weight:").grid(row=0, column=0, sticky=W)
self.label2 = ttk.Label(self, text="Enter your height:").grid(row=1, column=0, sticky=W)
self.label3 = ttk.Label(self, text="Your BMI is:").grid(row=2, column=0, sticky=W)
#text boxes
self.textbox1 = ttk.Entry(self, textvariable=self.i_weight).grid(row=0, column=1, sticky=E)
self.textbox2 = ttk.Entry(self, textvariable=self.i_height).grid(row=1, column=1, sticky=E)
self.textbox3 = ttk.Entry(self, textvariable=self.o_bmi).grid(row=2, column=1, sticky=E)
#buttons
self.button1 = ttk.Button(self, text="Ok", command=self.calculateBmi).grid(row=3, column=2, sticky=E)
## Changed button2 "command=self.quit" to "command=self.destroy"
# self.button2 = ttk.Button(self, text="Cancel/Quit", command=self.quit).grid(row=3, column=1, sticky=E)
self.button2 = ttk.Button(self, text="Cancel/Quit", command=self.destroy).grid(row=3, column=1, sticky=E)
#exitApplication = tk.Button(root, text='Exit Application', command=root.destroy)
#canvas1.create_window(85, 300, window=exitApplication)
def calculateBmi(self):
try:
self.weight = float(self.i_weight.get())
self.height = float(self.i_height.get())
self.bmi = self.weight / self.height ** 2.0
self.o_bmi.set(self.bmi)
except ValueError:
messagebox.showinfo("Error", "You can only use numbers.")
finally:
self.i_weight.set("")
self.i_height.set("")
class App2(ttk.Frame):
""" Application to convert feet to meters or vice versa. """
def __init__(self, master=None):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""Create the widgets for the GUI"""
# 1 textbox (stringvar)
self.entry= StringVar()
self.textBox1= ttk.Entry(self, textvariable=self.entry).grid(row=0, column=1)
# 5 labels (3 static, 1 stringvar)
self.displayLabel1 = ttk.Label(self, text="feet").grid(row=0, column=2, sticky=W)
self.displayLabel2 = ttk.Label(self, text="is equivalent to:").grid(row=1, column=0)
self.result= StringVar()
self.displayLabel3 = ttk.Label(self, textvariable=self.result).grid(row=1, column=1)
self.displayLabel4 = ttk.Label(self, text="meters").grid(row=1, column=2, sticky=W)
# 2 buttons
self.calculateButton = ttk.Button(self, text="Calculate", command=self.convert_feet_to_meters).grid(row=2, column=2, sticky=(S,E))
self.quitButton = ttk.Button(self, text="Quit", command=self.destroy).grid(row=2, column=1, sticky=(S,E))
#exitApplication = tk.Button(root, text='Exit Application', command=root.destroy)
#canvas1.create_window(85, 300, window=exitApplication)
def convert_feet_to_meters(self):
"""Converts feet to meters, uses string vars and converts them to floats"""
self.measurement = float(self.entry.get())
self.meters = self.measurement * 0.3048
self.result.set(self.meters)
### CODE BELOW COMMENTED OUT WHEN JOINING ORIGINAL POSTER CODE WITH HIS SOLUTION
### It seems no longer relevant since App1 and App2 have their own buttons.
#def button1_click():
# """ This is for the BMI Calculator Widget """
# root = Tk()
# app = App1(master=root)
# app.mainloop()
#
#def button2_click():
# """ This is for the Feet to Meters Conversion Widget """
# root = Tk()
# app = App2(master=root)
# app.mainloop()
#def main():
# window = Tk()
# button1 = ttk.Button(window, text="bmi calc", command=button1_click).grid(row=0, column=1)
# button2 = ttk.Button(window, text="feet conv", command=button2_click).grid(row=1, column=1)
# window.mainloop()
def main():
#Setup Tk()
window = Tk()
#Setup the notebook (tabs)
notebook = ttk.Notebook(window)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="BMI Calc")
notebook.add(frame2, text="Feet to Meters")
notebook.grid()
#Create tab frames
app1 = App1(master=frame1)
app1.grid()
app2 = App2(master=frame2)
app2.grid()
#Main loop
window.mainloop()
if __name__ == '__main__':
main()
The application doesn't quit when the "Quit" button is pressed. Only the individual frames quit.

Thanks to Martineau for the hint that helped me get this example to work. I declared 'window as a global variable, since it was defined in the name space of the class constructors. Without that, there was an error raised of undefined window. This method breaks the encapsulation and modularity of the classes by passing in the window as a global. If there is a better way to do this, I would like to know.
# -*- coding: utf-8 -*-
""" Created on Thu Jul 11 17:20:22 2019
Filename: tkinter-multiple-frames-windows_v3.py
From question on StackOverflow question "ttk tkinter multiple frames/windows"
at https://stackoverflow.com/questions/6035101/ttk-tkinter-multiple-frames-windows?rq=1
Now a full example that works but it needed some modification to clarify how it works.
"""
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
class BMICalcApp(ttk.Frame):
""" This application calculates BMI and returns a value. """
def __init__(self, master=None):
ttk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
#text variables
self.i_height = StringVar()
self.i_weight = StringVar()
self.o_bmi = StringVar()
#labels
self.label1 = ttk.Label(self, text="Enter your weight:").grid(row=0, column=0, sticky=W)
self.label2 = ttk.Label(self, text="Enter your height:").grid(row=1, column=0, sticky=W)
self.label3 = ttk.Label(self, text="Your BMI is:").grid(row=2, column=0, sticky=W)
#text boxes
self.textbox1 = ttk.Entry(self, textvariable=self.i_weight).grid(row=0, column=1, sticky=E)
self.textbox2 = ttk.Entry(self, textvariable=self.i_height).grid(row=1, column=1, sticky=E)
self.textbox3 = ttk.Entry(self, textvariable=self.o_bmi).grid(row=2, column=1, sticky=E)
#buttons
self.button1 = ttk.Button(self, text="Ok", command=self.calculateBmi).grid(row=3, column=2, sticky=E)
self.button2 = ttk.Button(self, text="Cancel/Quit", command=window.destroy).grid(row=3, column=1, sticky=E)
def calculateBmi(self):
try:
self.weight = float(self.i_weight.get())
self.height = float(self.i_height.get())
self.bmi = self.weight / self.height ** 2.0
self.o_bmi.set(self.bmi)
except ValueError:
messagebox.showinfo("Error", "You can only use numbers.")
finally:
self.i_weight.set("")
self.i_height.set("")
class ConvertFeetMeters(ttk.Frame):
""" Application to convert feet to meters or vice versa. """
def __init__(self, master=None):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""Create the widgets for the GUI"""
# 1 textbox (stringvar)
self.entry= StringVar()
self.textBox1= ttk.Entry(self, textvariable=self.entry).grid(row=0, column=1)
# 5 labels (3 static, 1 stringvar)
self.displayLabel1 = ttk.Label(self, text="feet").grid(row=0, column=2, sticky=W)
self.displayLabel2 = ttk.Label(self, text="is equivalent to:").grid(row=1, column=0)
self.result= StringVar()
self.displayLabel3 = ttk.Label(self, textvariable=self.result).grid(row=1, column=1)
self.displayLabel4 = ttk.Label(self, text="meters").grid(row=1, column=2, sticky=W)
# 2 buttons
self.calculateButton = ttk.Button(self, text="Calculate", command=self.convert_feet_to_meters).grid(row=2, column=2, sticky=(S,E))
self.quitButton = ttk.Button(self, text="Quit", command=window.destroy).grid(row=2, column=1, sticky=(S,E))
def convert_feet_to_meters(self):
"""Converts feet to meters, uses string vars and converts them to floats"""
self.measurement = float(self.entry.get())
self.meters = self.measurement * 0.3048
self.result.set(self.meters)
def main():
#Setup Tk()
global window
window = Tk()
#Setup the notebook (tabs)
notebook = ttk.Notebook(window)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="BMI Calc")
notebook.add(frame2, text="Feet to Meters")
notebook.grid()
#Create tab frames
bmi_calc = BMICalcApp(master=frame1)
bmi_calc.grid()
feet_meters_calc = ConvertFeetMeters(master=frame2)
feet_meters_calc.grid()
#Main loop
window.mainloop()
if __name__ == '__main__':
main()

Related

tkinter: How to get value from ComboBox in one frame with button in different frame

The goal is to allow the user to select from the combobox dropdown and then click Select Files button. The button will get the product type (the value from the combobox) and will open the correct directory based on what is chosen in the combobox. Problem is, I cannot seem to get this value since the button and combobox are in different frames. I feel like I'm missing something basic here.
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd
def select_files(prod_type):
path = f"\\\\10.10.3.7\\Production\\Test_Folder\\{prod_type}"
filetypes = (
('PDF Files', '*.pdf'),
)
filenames = fd.askopenfilenames(
title='Open files',
initialdir=path,
filetypes=filetypes)
for file in filenames:
print(file)
class InputFrame(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
# setup the grid layout manager
self.columnconfigure(0, weight=1)
self.columnconfigure(0, weight=3)
self.__create_widgets()
def __create_widgets(self):
# Product
ttk.Label(self, text='Product:').grid(column=0, row=0, sticky=tk.W)
self.product_type = tk.StringVar()
self.product_combo = ttk.Combobox(self, width=30, textvariable=self.product_type)
self.product_combo['values'] = ('Notepad', 'Flat Notecard', 'Folded Notecard', 'Journal')
self.product_combo.set('Notepad')
self.product_combo['state'] = 'readonly'
self.product_combo.grid(column=1, row=0, sticky=tk.W)
# Algorithm:
ttk.Label(self, text='Algorithm:').grid(column=0, row=1, sticky=tk.W)
algo_var = tk.StringVar()
algo_combo = ttk.Combobox(self, width=30)
algo_combo['values'] = ('panel', 'fuzzy')
algo_combo.set('panel')
algo_combo.grid(column=1, row=1, sticky=tk.W)
# Orientation:
ttk.Label(self, text='Orientation:').grid(column=0, row=2, sticky=tk.W)
orientation_var = tk.StringVar()
orientation_combo = ttk.Combobox(self, width=30, textvariable=orientation_var)
orientation_combo['values'] = ('auto', 'portrait', 'landscape')
orientation_combo.set('auto')
orientation_combo.grid(column=1, row=2, sticky=tk.W)
# Margin:
ttk.Label(self, text='Margin:').grid(column=0, row=3, sticky=tk.W)
margin = ttk.Entry(self, width=30)
margin.grid(column=1, row=3, sticky=tk.W)
# Gap:
ttk.Label(self, text='Gap:').grid(column=0, row=4, sticky=tk.W)
gap = ttk.Entry(self, width=30)
gap.grid(column=1, row=4, sticky=tk.W)
# Repeat:
ttk.Label(self, text='Repeat:').grid(column=0, row=5, sticky=tk.W)
repeat_number = tk.StringVar()
repeat = ttk.Spinbox(self, from_=1, to=10, width=30, textvariable=repeat_number)
repeat.set(1)
repeat.grid(column=1, row=5, sticky=tk.W)
for widget in self.winfo_children():
widget.grid(padx=0, pady=5)
class ButtonFrame(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
# setup the grid layout manager
self.columnconfigure(0, weight=1)
self.__create_widgets()
def __create_widgets(self):
ttk.Button(self, text='Select Files', command=self.on_go_pressed).grid(column=0, row=0)
ttk.Button(self, text='Generate PDF').grid(column=0, row=1)
for widget in self.winfo_children():
widget.grid(padx=0, pady=3)
def on_go_pressed(self):
select_files(InputFrame.product_type.get())
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("PDF n-up")
self.eval('tk::PlaceWindow . center')
self.geometry('400x200')
self.resizable(0, 0)
self.attributes('-toolwindow', True)
# layout on the root window
self.columnconfigure(0, weight=4)
self.columnconfigure(1, weight=1)
self.__create_widgets()
def __create_widgets(self):
# create the input frame
input_frame = InputFrame(self)
input_frame.grid(column=0, row=0)
# create the button frame
button_frame = ButtonFrame(self)
button_frame.grid(column=1, row=0)
if __name__ == '__main__':
app = App()
app.mainloop()
You can pass the product type variable to ButtonFrame when you create it, since you create the ButtonFrame after creating the InputFrame. Here is the code, with changed and added lines marked accordingly:
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd
def select_files(prod_type):
path = f"/home/sam/Pictures/Test/{prod_type}"
filetypes = (
('PDF Files', '*.pdf'),
)
filenames = fd.askopenfilenames(
title='Open files',
initialdir=path,
filetypes=filetypes)
for file in filenames:
print(file)
class InputFrame(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
# setup the grid layout manager
self.columnconfigure(0, weight=1)
self.columnconfigure(0, weight=3)
self.__create_widgets()
def __create_widgets(self):
# Product
ttk.Label(self, text='Product:').grid(column=0, row=0, sticky=tk.W)
self.product_type = tk.StringVar()
self.product_combo = ttk.Combobox(self, width=30, textvariable=self.product_type)
self.product_combo['values'] = ('Notepad', 'Flat Notecard', 'Folded Notecard', 'Journal')
self.product_combo.set('Notepad')
self.product_combo['state'] = 'readonly'
self.product_combo.grid(column=1, row=0, sticky=tk.W)
# Algorithm:
ttk.Label(self, text='Algorithm:').grid(column=0, row=1, sticky=tk.W)
algo_var = tk.StringVar()
algo_combo = ttk.Combobox(self, width=30)
algo_combo['values'] = ('panel', 'fuzzy')
algo_combo.set('panel')
algo_combo.grid(column=1, row=1, sticky=tk.W)
# Orientation:
ttk.Label(self, text='Orientation:').grid(column=0, row=2, sticky=tk.W)
orientation_var = tk.StringVar()
orientation_combo = ttk.Combobox(self, width=30, textvariable=orientation_var)
orientation_combo['values'] = ('auto', 'portrait', 'landscape')
orientation_combo.set('auto')
orientation_combo.grid(column=1, row=2, sticky=tk.W)
# Margin:
ttk.Label(self, text='Margin:').grid(column=0, row=3, sticky=tk.W)
margin = ttk.Entry(self, width=30)
margin.grid(column=1, row=3, sticky=tk.W)
# Gap:
ttk.Label(self, text='Gap:').grid(column=0, row=4, sticky=tk.W)
gap = ttk.Entry(self, width=30)
gap.grid(column=1, row=4, sticky=tk.W)
# Repeat:
ttk.Label(self, text='Repeat:').grid(column=0, row=5, sticky=tk.W)
repeat_number = tk.StringVar()
repeat = ttk.Spinbox(self, from_=1, to=10, width=30, textvariable=repeat_number)
repeat.set(1)
repeat.grid(column=1, row=5, sticky=tk.W)
for widget in self.winfo_children():
widget.grid(padx=0, pady=5)
class ButtonFrame(ttk.Frame):
def __init__(self, parent, product_type): ### EDITED THIS LINE
super().__init__(parent)
# setup the grid layout manager
self.columnconfigure(0, weight=1)
self.product_type = product_type ### ADDED THIS LINE
self.__create_widgets()
def __create_widgets(self):
ttk.Button(self, text='Select Files', command=self.on_go_pressed).grid(column=0, row=0)
ttk.Button(self, text='Generate PDF').grid(column=0, row=1)
for widget in self.winfo_children():
widget.grid(padx=0, pady=3)
def on_go_pressed(self):
select_files(self.product_type.get()) ### EDITED THIS LINE
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("PDF n-up")
self.eval('tk::PlaceWindow . center')
self.geometry('400x200')
self.resizable(0, 0)
# self.attributes('-toolwindow', True)
# layout on the root window
self.columnconfigure(0, weight=4)
self.columnconfigure(1, weight=1)
self.__create_widgets()
def __create_widgets(self):
# create the input frame
input_frame = InputFrame(self)
input_frame.grid(column=0, row=0)
# create the button frame
button_frame = ButtonFrame(self, input_frame.product_type) ### EDITED THIS LINE
button_frame.grid(column=1, row=0)
if __name__ == '__main__':
app = App()
app.mainloop()
Notice how in the line button_frame = ButtonFrame(self, input_frame.product_type), I give it input_frame.product_type as an argument, so that it can access it any time it wants.
The changes to ButtonFrame required for this: I changed the __init__() method so that it can receive another argument. It then assigns the value of the product_type argument to a variable so that it can use it later. When it calls select_files(), it simply uses self.product_type.
As to why calling select_files(InputFrame.product_type.get()) doesn't work: product_type is a variable that is created only when InputFrame is initialized. In the line above, you reference the class itself, not an instance of the class, so it has not been initialized. Therefore, anything that is created in InputFrame.__init__() is not defined, becuase __init__() wasn't called.

Tkinter Widgets next to each other and below

I'm putting widgets into a popup for my GUI. I'm putting the checkboxes along the top of the pop up and I want the label, entry and button to be in a row below them. Whatever I try the label, entry and button always go next to the check boxes. I haven't found a solution which uses pack(). I've tried anchor=but this also didn't do what I wanted.
This is my code:
import tkinter as tk
from tkinter import *
CheckVar1 = IntVar()
CheckVar2 = IntVar()
class PopUp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
popup = tk.Toplevel(self, background='gray15')
popup.wm_title("EMAIL")
self.withdraw()
popup.tkraise(self)
self.c1 = tk.Checkbutton(popup, text="Current", variable=CheckVar1, onvalue =1, offvalue = 0, height=2, width=15)
self.c1.pack(side="left", fill="x")
self.c2 = tk.Checkbutton(popup, text="-1", variable=CheckVar2, onvalue =1, offvalue = 0, height=2, width=15)
self.c2.pack(side="left", fill="x")
label = tk.Label(popup, text="Please Enter Email Address", background='gray15', foreground='snow')
label.pack(side="left", fill="x", pady=10, padx=10)
self.entry = tk.Entry(popup, bd=5, width=35, background='gray30', foreground='snow')
self.entry.pack(side="left", fill="x")
self.button = tk.Button(popup, text="OK", command=self.on_button, background='gray15', foreground='snow')
self.button.pack(side="left", padx=10)
def on_button(self):
address = self.entry.get()
print(address)
time.sleep(10)
self.destroy()
app = PopUp()
app.mainloop
Is there something I can put into pack() so that I can put the widgets next to each other and then put the other below them?
Thanks in advance
Use the grid geometry manager to divide your window into two frames, then pack your widgets into the frames.
from tkinter import *
import time
class PopUp(Tk):
def __init__(self):
Tk.__init__(self)
CheckVar1 = IntVar()
CheckVar2 = IntVar()
popup = Toplevel(self, background='gray15')
popup.wm_title("EMAIL")
self.withdraw()
popup.tkraise(self)
topframe = Frame(popup)
topframe.grid(column=0, row=0)
bottomframe = Frame(popup)
bottomframe.grid(column=0, row=1)
self.c1 = Checkbutton(topframe, text="Current", variable=CheckVar1, onvalue=1, offvalue=0, height=2, width=15)
self.c1.pack(side="left", fill="x")
self.c2 = Checkbutton(topframe, text="-1", variable=CheckVar2, onvalue=1, offvalue=0, height=2, width=15)
self.c2.pack(side="left", fill="x")
label = Label(bottomframe, text="Please Enter Email Address", background='gray15', foreground='snow')
label.pack(side="left", fill="x", pady=10, padx=10)
self.entry = Entry(bottomframe, bd=5, width=35, background='gray30', foreground='snow')
self.entry.pack(side="left", fill="x")
self.button = Button(bottomframe, text="OK", command=self.on_button, background='gray15', foreground='snow')
self.button.pack(side="left", padx=10)
def on_button(self):
address = self.entry.get()
print(address)
time.sleep(10)
self.destroy()
app = PopUp()
app.mainloop()
A few notes:
There is no need to import Tkinter twice
Mainloop is a method, so it's app.mainloop()
CheckVar1 and CheckVar2 must be declared within your class
camel case (capitalising within words, e.g 'CheckVar') is not pythonic

Triggering a button click with the enter key in Python

Please forgive me in advance, as I am very new to Python. I'm putting together this code for a very simple rate calculator and want to make it where when the Enter key is pressed the program calculates the total (executes the calculate command). Python returns an error saying that Calculate is not defined, however I've defined it several lines above. Any ideas?
import tkinter
from tkinter import ttk
from tkinter import *
root = Tk()
root.withdraw()
class Adder(ttk.Frame):
"""The adders gui and functions."""
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
def on_quit(self):
"""Exits program."""
quit()
def calculate(self, *args):
"""Calculates the sum of the two inputted numbers."""
num1 = int(self.num1_entry.get())
num2 = float(self.num2_entry.get())
num3 = ((num1/2000) * num2)
self.answer_label['text'] = num3
def init_gui(self):
self.root.title('Rate Calculator')
self.root.option_add('*tearOff', 'FALSE')
self.grid(column=0, row=0, sticky='nsew')
self.menubar = tkinter.Menu(self.root)
self.menu_file = tkinter.Menu(self.menubar)
self.menu_file.add_command(label='Exit', command=self.on_quit)
self.menu_edit = tkinter.Menu(self.menubar)
self.menubar.add_cascade(menu=self.menu_file, label='File')
self.menubar.add_cascade(menu=self.menu_edit, label='Edit')
self.root.config(menu=self.menubar)
self.num1_entry = ttk.Entry(self, width=5)
self.num1_entry.grid(column=1, row = 2)
self.num2_entry = ttk.Entry(self, width=5)
self.num2_entry.grid(column=3, row=2)
self.calc_button = ttk.Button(self, text='Calculate',
command=self.calculate)
self.calc_button.grid(column=0, row=3, columnspan=4)
self.answer_frame = ttk.LabelFrame(self, text='Answer',
height=100)
self.answer_frame.grid(column=0, row=4, columnspan=4, sticky='nesw')
self.answer_label = ttk.Label(self.answer_frame, text='')
self.answer_label.grid(column=0, row=0)
# Labels that remain constant throughout execution.
ttk.Label(self, text='Rate Calculator').grid(column=0, row=0,
columnspan=4)
ttk.Label(self, text='Weight').grid(column=0, row=2,
sticky='w')
ttk.Label(self, text='Rate').grid(column=2, row=2,
sticky='w')
ttk.Separator(self, orient='horizontal').grid(column=0,
row=1, columnspan=4, sticky='ew')
for child in self.winfo_children():
child.grid_configure(padx=5, pady=5)
root.bind('<Return>', calculate)
if __name__ == '__main__':
root = tkinter.Tk()
Adder(root)
root.mainloop()
I've reorganised your code slightly so it now calls the calculate method either when you press the button or when you hit Enter.
import tkinter as tk
from tkinter import ttk
class Adder(ttk.Frame):
"""The adders gui and functions."""
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
def on_quit(self):
"""Exits program."""
tk.quit()
def calculate(self, *args):
"""Calculates the sum of the two inputted numbers."""
num1 = int(self.num1_entry.get())
num2 = float(self.num2_entry.get())
num3 = ((num1/2000) * num2)
self.answer_label['text'] = num3
def init_gui(self):
self.root.title('Breakbulk Rate Calculator')
self.root.option_add('*tearOff', 'FALSE')
self.grid(column=0, row=0, sticky='nsew')
self.menubar = tk.Menu(self.root)
self.menu_file = tk.Menu(self.menubar)
self.menu_file.add_command(label='Exit', command=self.on_quit)
self.menu_edit = tk.Menu(self.menubar)
self.menubar.add_cascade(menu=self.menu_file, label='File')
self.menubar.add_cascade(menu=self.menu_edit, label='Edit')
self.root.config(menu=self.menubar)
self.num1_entry = ttk.Entry(self, width=5)
self.num1_entry.grid(column=1, row = 2)
self.num2_entry = ttk.Entry(self, width=5)
self.num2_entry.grid(column=3, row=2)
self.calc_button = ttk.Button(self, text='Calculate',
command=self.calculate)
self.calc_button.grid(column=0, row=3, columnspan=4)
self.answer_frame = ttk.LabelFrame(self, text='Answer',
height=100)
self.answer_frame.grid(column=0, row=4, columnspan=4, sticky='nesw')
self.answer_label = ttk.Label(self.answer_frame, text='')
self.answer_label.grid(column=0, row=0)
# Labels that remain constant throughout execution.
ttk.Label(self, text='Breakbulk Rate Calculator').grid(column=0, row=0,
columnspan=4)
ttk.Label(self, text='Weight').grid(column=0, row=2,
sticky='w')
ttk.Label(self, text='Rate').grid(column=2, row=2,
sticky='w')
ttk.Separator(self, orient='horizontal').grid(column=0,
row=1, columnspan=4, sticky='ew')
for child in self.winfo_children():
child.grid_configure(padx=5, pady=5)
self.root.bind('<Return>', lambda event: self.calculate())
if __name__ == '__main__':
root = tk.Tk()
Adder(root)
root.mainloop()
To create a callback with the proper signature for .bind I've used a lambda function that simply discards the Event object that gets passed to it.
I also got rid of the evil from tkinter import * wildcard import.
The problem is coming from this part of your code
root.bind('<Return>', calculate)
change it to
root.bind('<Return>', self.calculate)
it will work.

Python-Tkinter Popup window not working on Macos

I am trying to open a popup window on a click. The same is not getting opened as a popup, but another tab in the already opened window. Also, when I maximize the window the click function doesn't work at all.
My code is as under:
from tkinter import *
from tkinter import ttk
import shelve
import pyperclip
import os
def popupmsg(msg):
popup = Tk()
popup.wm_title("!")
label = ttk.Label(popup, text=msg)
label.pack(side="top", fill="x", pady=10)
B1 = ttk.Button(popup, text="Okay", command = popup.destroy)
B1.pack()
popup.mainloop()
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("uClip - Your personal clipboard")
self.pack(fill = BOTH, expand =1)
global paste_entry
paste_entry = Entry(self)
paste_entry.grid(row=1, sticky="e")
global copy_entry
copy_entry = Entry(self)
copy_entry.grid(row=2, sticky="e")
global delete_entry
delete_entry = Entry(self)
delete_entry.grid(row=3, sticky="e")
button1 = Button(self, text="Paste to uClip", command= lambda:popupmsg("Pop Up")).grid(row=1,column=1)
button2 = Button(self, text="Copy from uClip", command=None).grid(row=2,column=1)
button4 = Button(self, text="Delete from uClip",command=None).grid(row=3, column=1)
button3 = Button(self, text="List all Keywords",command=None).grid(row=4, sticky="e")
button5 = Button(self, text="Clear uClip",command=None).grid(row=5, sticky="e")
root = Tk()
root.geometry("400x300")
root.resizable(None, None)
uClip = Window(root)
root.mainloop()

python, tkinter: how all variables can be obtained by single button from all notebook tabs?

I need to return all variables from all tabs by clicking on ok button.
I have two tabs. What I want is that when I enter some value in 2nd tab, it should automatically appear in first tab in 'height' entry.
Then if I click 'ok' in first tab, it should return all variables(from first tab and 2nd tab) to my 'main' program for further use.
Thanks
from tkinter import *
from tkinter import ttk
class App1(ttk.Frame):
def createWidgets(self):
#text variables
self.i_height = StringVar()
self.i_weight = StringVar()
self.o_bmi = StringVar()
#labels
self.label1 = ttk.Label(self, text="Enter your weight:").grid(row=0, column=0, sticky=W)
self.label2 = ttk.Label(self, text="Enter your height:").grid(row=1, column=0, sticky=W)
self.label3 = ttk.Label(self, text="Your BMI is:").grid(row=2, column=0, sticky=W)
#text boxes
self.textbox1 = ttk.Entry(self, textvariable=self.i_weight).grid(row=0, column=1, sticky=E)
self.textbox2 = ttk.Entry(self, textvariable=self.i_height).grid(row=1, column=1, sticky=E)
self.textbox3 = ttk.Entry(self, textvariable=self.o_bmi).grid(row=2, column=1, sticky=E)
#buttons
self.button1 = ttk.Button(self, text="Cancel/Quit", command=self.quit).grid(row=3, column=1, sticky=E)
self.button1 = ttk.Button(self, text="Ok", command=self.calculateBmi).grid(row=3, column=2, sticky=E)
def calculateBmi(self):
try:
self.weight = float(self.i_weight.get())
self.height = float(self.i_height.get())
self.bmi = self.weight / self.height ** 2.0
self.o_bmi.set(self.bmi)
except ValueError:
messagebox.showinfo("Error", "You can only use numbers.")
finally:
self.i_weight.set("")
self.i_height.set("")
def __init__(self, master=None):
ttk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
class App2(ttk.Frame):
def create_widgets(self):
"""Create the widgets for the GUI"""
#1 textbox (stringvar)
self.entry= StringVar()
self.textBox1= ttk.Entry(self, textvariable=self.entry).grid(row=0, column=1)
#5 labels (3 static, 1 stringvar)
self.displayLabel1 = ttk.Label(self, text="feet").grid(row=0, column=2, sticky=W)
self.displayLabel2 = ttk.Label(self, text="is equivalent to:").grid(row=1, column=0)
self.result= StringVar()
self.displayLabel3 = ttk.Label(self, textvariable=self.result).grid(row=1, column=1)
self.displayLabel4 = ttk.Label(self, text="meters").grid(row=1, column=2, sticky=W)
#2 buttons
self.quitButton = ttk.Button(self, text="Quit", command=self.quit).grid(row=2, column=1, sticky=(S,E))
self.calculateButton = ttk.Button(self, text="Calculate", command=self.convert_feet_to_meters).grid(row=2, column=2, sticky=(S,E))
def convert_feet_to_meters(self):
"""Converts feet to meters, uses string vars and converts them to floats"""
self.measurement = float(self.entry.get())
self.meters = self.measurement * 0.3048
self.result.set(self.meters)
def __init__(self, master=None):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def button1_click():
root = Tk()
app = App1(master=root)
app.mainloop()
def button2_click():
root = Tk()
app = App2(master=root)
app.mainloop()
def main():
#Setup Tk()
window = Tk()
#Setup the notebook (tabs)
notebook = ttk.Notebook(window)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="BMI Calc")
notebook.add(frame2, text="Feet to Meters")
notebook.grid()
#Create tab frames
app1 = App1(master=frame1)
app1.grid()
app2 = App2(master=frame2)
app2.grid()
#Main loop
window.mainloop()
main()
You have some fundamental mistakes in your program -- you cannot have three mainloops running at the same. You should always only have exactly one instance of Tk, and call mainloop exactly once.
Regardless of that, the solution is that you need to create a method or public variable in the app, and then your button callback needs to be able to call that method or access that variable.
For example, you would do it like this:
def callback():
value1 = app1.getValue()
value2 = app2.getValue()
...

Categories

Resources