After tinkering around with tkinter I can't seem to make my window look
quite how I want it to look. But overall, I'm not sure what the File Edit View
layout is referred to. Is this is a toolbar or a menu?
So far my gui looks much less native osx than I would like. Should I just ditch
tkinter all together?
Does anyone have a code snipped that gives the general osx layout? That would be a big help.
Maybe I'm just not grasping the gui programming aspect conceptually.
thanks
I want to add menus to the following code
from tkinter import *
from tkinter import ttk
def undef(*args):
pass
def undef2(*args):
pass
root = Tk()
root.title("KDM Checker Beta ")
mainframe = ttk.Frame(root, padding="5 5 5 5")
mainframe.grid(column=12, row=12, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
countryvar = StringVar()
country = ttk.Combobox(mainframe, textvariable=countryvar)
country['values'] = ('dolby', 'sony', 'doremi')
country.grid(column=1, row = 1)
DATE = StringVar()
VENUE = StringVar()
UUID = StringVar()
SERVER_SERIAL = StringVar()
DATE_entry = ttk.Entry(mainframe, width=8, textvariable=DATE)
DATE_entry.grid(column=3, row=4, sticky=(W, E))
VENUE_entry = ttk.Entry(mainframe, width=8, textvariable=VENUE)
VENUE_entry.grid(column=3, row=8, sticky=(W, E))
UUID_entry = ttk.Entry(mainframe, width=8, textvariable=UUID)
UUID_entry.grid(column=3, row=16, sticky=(W, E))
state = StringVar()
mount = ttk.Radiobutton(mainframe, text='dolby', variable=state, value='dolby')
ttk.Label(mainframe, textvariable=DATE).grid(column=1, row=4, sticky=(W, E))
ttk.Label(mainframe, textvariable=VENUE).grid(column=1, row=8, sticky=(W, E))
ttk.Label(mainframe, textvariable=UUID).grid(column=1, row=16, sticky=(W, E))
ttk.Label(mainframe, text="KDM Window").grid(column=1, row=4, sticky=E)
ttk.Label(mainframe, text="Venue").grid(column=1, row=8, sticky=E)
ttk.Label(mainframe, text="UUID").grid(column=1, row=16, sticky=E)
for child in mainframe.winfo_children(): child.grid_configure(padx=3, pady=9)
DATE_entry.focus()
root.bind('<Return>', undef)
root.mainloop()
To create a menubar you need to create an instance of the Menu class, then use it as the value of the menubar attribute of your main application. From there you can create other menus and attach them to the menubar with add_cascade.
For example:
import tkinter as tk
import sys
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._create_menubar()
self.text = tk.Text()
self.text.pack(side="top", fill="both", expand=True)
def _create_menubar(self):
# create a menu for the menubar and associate it
# with the window
self.menubar = tk.Menu()
self.configure(menu=self.menubar)
# create a File menu and add it to the menubar
file_menu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Quit", command=self.on_quit)
# create a Edit menu and add it to the menubar
edit_menu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="Edit", menu=edit_menu)
edit_menu.add_command(label="Cut", underline=2, command=self.on_cut)
edit_menu.add_command(label="Copy", underline=0, command=self.on_copy)
edit_menu.add_command(label="Paste", underline=0, command=self.on_paste)
# create a View menu and add it to the menubar
view_menu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="View", menu=view_menu)
view_menu.add_cascade(label="Whatever", command=self.on_whatever)
def log(self, s):
self.text.insert("end", s + "\n")
self.text.see("end")
# not good programming style, but I'm trying to keep
# the example short
def on_cut(self): self.log("cut...")
def on_copy(self): self.log("copy...")
def on_paste(self): self.log("paste...")
def on_quit(self): sys.exit(0)
def on_whatever(self): self.log("whatever...")
if __name__ == "__main__":
app = ExampleApp()
app.mainloop()
Related
I'm making a GUI with Tkinter (Tcl/Tk version 8.6.12) on Python 3.9. Up until now, I mostly had experience with pure Tk, but wanted to try Ttk out. My problem is that running:
someText = tk.StringVar(value="Some Text")
ttk.Label(mainframe, textvariable=someText).grid(column=0, row=0, sticky=(W, E))
does reserve some space for the string, but doesn't display it. When using the tk.Label class, it works like a charm...
What am I doing wrong, or is the ttk.Label class broken?
EDIT: here is my full MNWE (I wrapped everything in a function because I will need to integrate everything with some other code I have already written and this is the best way I've found to do it):
from tkinter import *
from tkinter import ttk
def generateGUI():
# Generates the main window
root = Tk()
root.title("Some interesting title")
root.resizable(FALSE, FALSE)
# Adds a frame for themed tk
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
root.columnconfigure(0, weight=1) # setup the frame to scale with the window horizontally
root.rowconfigure(0, weight=1) # setup the frame to scale with the window vertically
# Creates a canvas
canv = Canvas(mainframe, bg="green")
canv.grid(column=0, row=0, sticky=(N, E, S, W))
# Sets some information panel up
info = ttk.Frame(mainframe)
info.grid(column=1, row=0, sticky=(N, E))
title = StringVar(value="<title>")
text1 = StringVar(value="<text1>")
text2 = StringVar(value="<text2>")
text3 = StringVar(value="<text3>")
# Adds the different labels
ttk.Label(info, font=25, textvariable=title).grid(column=0, row=0, sticky=N, columnspan=2)
ttk.Label(info, text="Info 1: ").grid(column=0, row=1, sticky=W)
ttk.Label(info, text="Info 2: ").grid(column=0, row=2, sticky=W)
ttk.Label(info, text="Info 3: ").grid(column=0, row=3, sticky=W)
ttk.Label(info, textvariable=text1).grid(column=1, row=1, sticky=E)
ttk.Label(info, textvariable=text2).grid(column=1, row=2, sticky=E)
ttk.Label(info, textvariable=text3).grid(column=1, row=3, sticky=E)
for child in mainframe.winfo_children():
child.grid_configure(padx=5, pady=5)
return root
gui = generateGUI()
gui.mainloop()
Your instance of StringVar is a local variable. Make it/them an instance variable if you're using classes, or a global variable if not.
def generateGUI():
global title
...
title = StringVar(value="<title>")
ttk.Label(info, font=25, textvariable=title)
This example code is working. You need to import ttk and change remove sticky.
why_no_sticky - good example on the link how grid works.
from tkinter import ttk
import tkinter as tk
mainframe = tk.Tk()
someText = tk.StringVar(value="Some Text")
label1 = ttk.Label(mainframe, textvariable=someText)
label1.grid(column=0, row=0)
mainframe.mainloop()
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()
When passing variables between functions in the same class in tkinter, it does not work if I just pass in the variable. The parameter seems to only work in the new function if it is an instance variable.
My code is a simple window that closes when the button is pressed if the entry field is not empty.
class simpleGUI:
def __init__(self, root):
height = StringVar()
self.root = root
self.frame = ttk.Frame(self.root, padding='30 30 30 30')
self.frame.grid(row=0, column=0, sticky=(N, W, E, S))
self.root.title("Enter your parameters")
ttk.Label(self.frame, text="Height").grid(row=1, column=1, sticky=(W, E))
# widgets
self.height = ttk.Entry(self.frame, width=7, textvariable=height)
self.height.grid(row=2, column=1, sticky=(W, E))
self.height.focus()
self.button = ttk.Button(self.frame, text="OK", command=self.close_window)
self.button.grid(row=3, column=1, sticky=(W, E))
def close_window(self):
if len(self.height.get())>0:
self.root.destroy()
This only works if the height widget is saved in self.height. If self.height is replaced by height, then it does not work.
For example, if the bottom half of the code is replaced by the following:
# widgets
height = ttk.Entry(self.frame, width=7, textvariable=Height)
height.grid(row=2, column=1, sticky=(W, E))
height.focus()
self.button = ttk.Button(self.frame, text="OK", command=self.close_window(height))
self.button.grid(row=3, column=1, sticky=(W, E))
def close_window(self, height):
if len(height.get())>0:
self.root.destroy()
I dont get any errors in the console It just doesn't close the window anymore and I'm trying to understand why. Advice from more experienced folks will be appreciated.
I want my label to be displayed in the same column as the picture. Both of them are generated with a click. I place them in the same column of the grid, but the image is displayed in the neighbour column. What's the reason and how to correct it?
Here's my simplified code.
from tkinter import *
from tkinter import ttk
class App(Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master, padding='20')
self.grid(column=0, row=0, sticky=(N, W, E, S))
self.create_button()
def create_button(self):
self.button = ttk.Button(self,
text="Click",
width=12,
command=lambda: self.display_name_and_picture()
).grid(column=2, columnspan=2, row=1, sticky=NW)
def display_name_and_picture(self):
random_label = ttk.Label(self, font=(None, 16), text='random random')
random_label.grid(row=0, column=5, sticky=N)
random_image = PhotoImage(file='random.gif')
label = Label(image=random_image)
label.image = random_image
label.grid(row=1, column=5, sticky=NW)
root = Tk()
root.title("Random something...")
root.geometry("600x300")
app = App(root)
root.mainloop()
The culprit is this line
label = Label(image=random_image)
You create label without specifying its parent, so its parent defaults to root. But random_label has app as its parent, and app in turn has root as its parent. So label is gridded side by side with app --- inside root --- and not inside app as you wished. Just change the above line to
label = Label(self, image=random_image)
and you should be fine.
(Well, not totally fine. You should also fix the things people pointed out in comments.)
I monkeyed around with this for a while and produced something that does what you are asking. I would love to have somebody explain why this works. The columns look goofy.
Check it out:
from tkinter import *
from tkinter import ttk
class App(Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master, padding='20')
self.grid(column=0, row=0, sticky=(N, W, E, S))
self.create_button()
def create_button(self):
self.button = ttk.Button(self,
text="Click",
width=12,
command=self.display_name_and_picture
)
self.button.grid(column=0, columnspan=2, row=0, sticky=NW)
def display_name_and_picture(self):
random_label = ttk.Label(self, font=(None, 16), text='random random')
random_label.grid(row=0, column=2)
random_image = PhotoImage(file='random.gif')
label = Label(image=random_image)
label.image = random_image
label.grid(row=1, column=0)
root = Tk()
root.title("Random something...")
root.geometry("600x300")
app = App(root)
root.mainloop()
I'm making my first GUI application and I've run into a silly problem. Resizing the main window doesn't resize its contents and leaves blank space. I've read the TKDocs and they only say you should use sticky and column/row weight attributes but I don't really understand how they work.
Here's my code (only the part covering widgets, if you think problem isn't here I'll post the rest of it):
from tkinter import *
from tkinter import ttk
root = Tk()
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
player1 = StringVar()
player2 = StringVar()
player1.set('Player 1')
player2.set('Player 1')
timer=StringVar()
running=BooleanVar()
running.set(0)
settimer = ttk.Entry(mainframe, width=7, textvariable=timer)
settimer.grid(column=2, row=1, sticky=(N, S))
ttk.Button(mainframe, text="Start", command=start).grid(column=2, row=2, sticky=(N, S))
ttk.Label(mainframe, textvariable=player1, font=TimeFont).grid(column=1, row=3, sticky=(W, S))
ttk.Label(mainframe, textvariable=player2, font=TimeFont).grid(column=3, row=3, sticky=(E, S))
for child in mainframe.winfo_children():
child.grid_configure(padx=80, pady=10)
root.mainloop()
Thanks for your time!
Maybe this will help you in the right direction. Be sure to configure column/row weights at each level.
import tkinter.ttk
from tkinter.constants import *
class Application(tkinter.ttk.Frame):
#classmethod
def main(cls):
tkinter.NoDefaultRoot()
root = tkinter.Tk()
app = cls(root)
app.grid(sticky=NSEW)
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
root.resizable(True, False)
root.mainloop()
def __init__(self, root):
super().__init__(root)
self.create_variables()
self.create_widgets()
self.grid_widgets()
self.grid_columnconfigure(0, weight=1)
def create_variables(self):
self.player1 = tkinter.StringVar(self, 'Player 1')
self.player2 = tkinter.StringVar(self, 'Player 2')
self.timer = tkinter.StringVar(self)
self.running = tkinter.BooleanVar(self)
def create_widgets(self):
self.set_timer = tkinter.ttk.Entry(self, textvariable=self.timer)
self.start = tkinter.ttk.Button(self, text='Start', command=self.start)
self.display1 = tkinter.ttk.Label(self, textvariable=self.player1)
self.display2 = tkinter.ttk.Label(self, textvariable=self.player2)
def grid_widgets(self):
options = dict(sticky=NSEW, padx=3, pady=4)
self.set_timer.grid(column=0, row=0, **options)
self.start.grid(column=0, row=1, **options)
self.display1.grid(column=0, row=2, **options)
self.display2.grid(column=0, row=3, **options)
def start(self):
timer = self.timer.get()
self.player1.set(timer)
self.player2.set(timer)
if __name__ == '__main__':
Application.main()