I am VERY new to coding/Python, but basically I am trying to move a button and label around using .grid, however, the button and label in the StartPage class just won't move to where I ask (or even at all).
Everything in the BMR class works fine (although the positions you see aren't the final positions, I was just checking).
What is the difference? Why do they not appear at the same position if I give the same details in both classes?
import tkinter as tk
class initials(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side='top', fill='both', expand= True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, BMR):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky='nsew')
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame): #GRID WON'T WORK HOW I WANT IT TO
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Start Page")
label.grid(column=3, row=3, sticky='we')
button = tk.Button(self, text="Calculate BMR",
command=lambda: controller.show_frame(BMR))
button.grid(row=4, column=3, sticky='we')
class BMR(tk.Frame): #GRID WORKS PERFECTLY
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="BMR Calculator")
label.grid(column=1,row=1)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.grid(column=2, row=2)
submit = tk.Button(self, text="Calculate")
submit.grid(column=3, row=3)
var1 = tk.IntVar()
tk.Checkbutton(self, text='Male', bg='white', variable=var1).grid(column=4, row=4)
var2= tk.IntVar()
tk.Checkbutton(self, text='Female', bg='white', variable=var2).grid(column=5, row=5)
height_inp = tk.Entry(self, width=20, bg="white").grid(column=6, row=6)
app = initials()
app.mainloop()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Start Page", width = 80)
# Added width property in the line above
# and changed sticky property to N
label.grid(row = 3, column=3, sticky = 'N')
label.width = 20
button = tk.Button(self, text="Calculate BMR",
command=lambda: controller.show_frame(BMR))
button.grid(row=4, column=3)
# Removed sticky property for the button
I understand this is how you wish to position the label and the button.
Pleaase see the comments. You can edit the value for the width property and make it suitable for your frame.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm trying to write the 'ping' output to a text box that will be
created in the 'output' tkinter frame
When I press the 'Submit' button while writing the output to file.
Questions:
1: Is there a way to place the text box inside the 'output' frame?
2: How can I print lines from files inside the 'output' frame?
3: How can I use threading or multiproccess to display the output in realtime?
Before clicking 'Submit':
import tkinter as tk
import subprocess as sub
from multiprocessing import Queue
import os
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.geometry("650x500")
self.title("Gil Shwartz GUI Project")
menu = tk.Frame(self, height=250, width=10, relief="solid")
menu.pack(side=tk.LEFT, fill="both", anchor="w")
container = tk.Frame(self, relief="flat")
container.pack(side=tk.TOP, fill="y", expand=True)
output = tk.LabelFrame(self, text="Output", height=350, width=70)
output.pack(side=tk.BOTTOM, fill="both", expand=True)
menu.grid_columnconfigure(0, weight=1)
menu.grid_rowconfigure(0, weight=1)
self.frames = ["Menu", "MainWelcome", "testPing", "PageOne", "PageTwo"]
self.frames[0] = Menu(parent=menu, controller=self)
self.frames[1] = MainWelcome(parent=container, controller=self)
self.frames[2] = testPing(parent=container, controller=self)
self.frames[3] = PageOne(parent=container, controller=self)
self.frames[4] = PageTwo(parent=container, controller=self)
self.frames[0].grid(row=0, column=0, sticky="nsew")
self.frames[1].grid(row=0, column=0, sticky="nsew")
self.frames[2].grid(row=0, column=0, sticky="nsew")
self.frames[3].grid(row=0, column=0, sticky="nsew")
self.frames[4].grid(row=0, column=0, sticky="nsew")
self.show_frame(1)
def show_frame(self, page_name):
frame = self.frames[page_name]
print(frame)
frame.tkraise()
frame.grid(row=0, column=0, sticky="nsew")
class Menu(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
button1 = tk.Button(self, text="Ping Test",
command=lambda: controller.show_frame(2))
button1.config(bg="royalblue2")
button2 = tk.Button(self, text="Page Two",
command=lambda: controller.show_frame(4))
button2.config(bg="dark violet")
button3 = tk.Button(self, text="Quit",
command=lambda: Menu.terminate(self))
button3.config(bg="gray40")
button1.pack(fill="both", expand=True)
button2.pack(fill="both", expand=True)
button3.pack(fill="both", expand=True)
button1.grid_columnconfigure(0, weight=1)
button2.grid_columnconfigure(0, weight=0)
button3.grid_rowconfigure(0, weight=0)
def terminate(self):
exit()
class MainWelcome(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Text 1", bg="yellow")
label.pack(fill="x", expand=True)
label = tk.Label(self, text="Text 2", bg="yellow")
label.pack(fill="x")
class testPing(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
urlLabel = tk.Label(self, text="Enter URL : ", padx=5, pady=5)
urlLabel.pack(anchor="w")
urlInputBox = tk.Entry(self)
urlInputBox.pack(anchor="w")
urlInputBox.grid_columnconfigure(0, weight=0)
clearFileLabel = tk.Label(self, text="Clear File?", padx=5, pady=5)
clearFileLabel.pack(anchor="w")
clearFile = tk.BooleanVar()
clearFile.set(False)
clearFileRadioYes = tk.Radiobutton(self, text="yes", value=True, var=clearFile,
command=lambda: self.callback(clearFile.get()))
clearFileRadioYes.pack(anchor="w")
clearFileRadioNo = tk.Radiobutton(self, text="no", value=False, var=clearFile,
command=lambda: self.callback(clearFile.get()))
clearFileRadioNo.pack(anchor="w")
urlSubmitButton = tk.Button(self, text="Submit",
command=lambda: self.pingURL(urlInputBox.get(),
clearFile.get()))
urlSubmitButton.pack(side=tk.RIGHT, anchor="e")
def callback(self, clearFile):
print(clearFile) # Debugging Mode - check Radio box Var.
def pingURL(self, host, clearFile):
global r
file = fr'c:/users/{os.getlogin()}/Desktop/ping.txt'
text = tk.Text(self, height=5, width=100, wrap=tk.WORD)
text.pack(side=tk.TOP, expand=True)
if clearFile == True:
with open(file, 'a') as output:
output.truncate(0)
sub.call(['ping', f'{host}'], stdout=output)
else:
with open(file, 'a') as output:
sub.call(['ping', f'{host}'], stdout=output)
output.close()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", bg="red")
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to page 2",
command=lambda: controller.show_frame(2))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", bg="blue")
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(1))
button.pack()
if __name__ == "__main__":
app = GUI()
app.mainloop()
After clicking 'Submit':
First, you have to make the output frame available or else you can't access it later:
self.output = tk.LabelFrame(self, text="Output", height=350, width=70)
self.output.pack(side=tk.BOTTOM, fill="both", expand=True)
Then, in the testPing() class pingURL() method you can pack the Text() widget in the output labelframe:
text = tk.Text(self.controller.output, height=5, width=100, wrap=tk.WORD)
I don't know about using threading or multiproccess. In general I think you will get a better response if you post questions for each of your problems instead of listing them in the same question.
I have a tkinter app in which I have a main canvas with multiple pages (all of which are frames). I pull up the different pages by rasing them with the frame.tkraise() command. I now want to add a scrollbar to the whole thing. The scrollbar appears but without a slider and I am not sure if it cna recognize the change of page.
import tkinter as tk
from tkinter import ttk
class Economics(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
tk.Tk.columnconfigure(self, 0, weight=1)
tk.Tk.rowconfigure(self, 0, weight=1)
self.container = tk.Canvas()
self.container.grid(row=0, column=0, sticky="nsew")
self.container.columnconfigure("all", weight=1)
self.container.rowconfigure("all", weight=1)
self.vscrollbar = tk.Scrollbar(orient="vertical", command=self.container.yview)
self.container.configure(yscrollcomman=self.vscrollbar.set)
self.vscrollbar.grid(row=0, column=1, sticky="ns")
self.frames = {}
for F in (StartPage, ExamplePage1, ExamplePage2): # TUPLE OF PAGES
frame = F(self.container, self)
self.frames[F] = frame
self.show_frame(StartPage)
def show_frame(self, cont):
self.container.delete("all")
frame = self.frames[cont]
self.container.create_window(0, 0, anchor="nw", window=frame)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
button_1 = ttk.Button(self, text="Example Page 1",
command=lambda: controller.show_frame(ExamplePage1))
button_1.grid(row=0, column=0)
button_2 = ttk.Button(self, text="Example Page 2",
command=lambda: controller.show_frame(ExamplePage2))
button_2.grid(row=1, column=0)
class ExamplePage1(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
for i in range(50):
label = tk.Label(self, text="Button {} of 50".format(i+1))
label.grid(row=i, column=0)
button_back = ttk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
button_back.grid(row=0, column=1)
class ExamplePage2(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
for i in range(35):
label = tk.Label(self, text="Button {} of 35".format(i+1))
label.grid(row=i, column=0)
button_back = ttk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
button_back.grid(row=0, column=1)
app = Economics()
app.geometry("800x600")
app.resizable(True, True)
app.mainloop()
In this example file you can see the basic structure of my app with some example widgets and buttons. The scrollbar shows but without the slider. What do I have to change to get a working scrollbar for all pages.
Later on I'm planning to get a horizontal scrollbar as well.
You can't scroll items added to a canvas with pack, place, or grid. A canvas can only scroll items added via the canvas create_* functions, such as create_window.
I'm trying to create a simple GUI with tkinter with buttons and images. I have a startpage with an image and buttons that take you to other pages(frames). The problem is that the image in my startpage persists and displays in the other frames.
The code:
import tkinter as tk
LARGE_FONT = ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.geometry("800x480")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10, padx=10)
#canvas1 = tk.Canvas(width=600, height=300, bg="gray")
#self.img = tk.PhotoImage(file="mario.png")
#canvas1.create_image(20, 20, image=self.img)
#canvas1.pack()
photo = tk.PhotoImage(file="mario.png")
labelimg = tk.Label(image=photo)
labelimg.image = photo # keep a reference!
labelimg.pack()
button = tk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
button2 = tk.Button(self, text="Visit Page 2",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page One",
command=lambda: controller.show_frame(PageOne))
button2.pack()
app = SeaofBTCapp()
app.mainloop()
The issue is that you have not specified labelimg's parent therefore, by default it is the main window. As a consequence, your label is packed below your container and therefore is always visible.
Changing
labelimg = tk.Label(image=photo)
into
labelimg = tk.Label(self, image=photo)
in the class StartPage should solve your problem.
I have a problem with my code. I am unable to pass a variable to another class once a submit button is pressed in my tkinter frame.
I have followed the advice from a post already (How to access variables from different classes in tkinter?), which has helped but I still have issues.
From where to where I need these variables is commented on the code below:
import tkinter as tk
from tkinter import StringVar
LARGE_FONT = ("Verdana", 12)
class Club(tk.Tk):
def get_page(self, page_class):
return self.frames[page_class]
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.shared_data = {
"username": tk.StringVar(),
"password": tk.StringVar(),
}
self.frames = {}
for F in (Terminal, newUser, newUserSubmitButton, delUser):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Terminal)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class Terminal(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Welcome to the club terminal. Click the options below", font=LARGE_FONT)
label.grid(columnspan=3)
button = tk.Button(self, text="Visit new User",
command=lambda: controller.show_frame(newUser))
button.grid(row=1, column=0)
button2 = tk.Button(self, text="Visit del User",
command=lambda: controller.show_frame(delUser))
button2.grid(row=1, column=1)
class newUser(tk.Frame):
def __init__(self, parent, controller):
def submitButton():
username = self.controller.shared_data["username"].get()
print(username)
controller.show_frame(newUserSubmitButton)
##username variable from here to...
tk.Frame.__init__(self, parent)
welcomelabel = tk.Label(self, text="Add New User/s", font=LARGE_FONT)
welcomelabel.grid(columnspan=2, sticky="ew")
userNameLabel = tk.Label(self, text="Username")
userNameLabel.grid(row=1, column=0, sticky="e")
userNameEntry = tk.Entry(self, textvariable=self.controller.shared_data["username"])
userNameEntry.grid(row=1, column=1)
userMemTypeLabel = tk.Label(self, text="Membership Type")
userMemTypeLabel.grid(row=2, column=0, sticky="e")
variable = StringVar(self)
variable.set("Full")
userMemTypeMenu = tk.OptionMenu(self, variable, "Full", "Half")
userMemTypeMenu.grid(row=2, column=1)
userMemYearsLabel = tk.Label(self, text="Years that member is in the club")
userMemYearsLabel.grid(row=3, column=0, sticky="e")
userMemYearsEntry = tk.Entry(self)
userMemYearsEntry.grid(row=3, column=1)
self.controller = controller
newusersubmitbutton = tk.Button(self, text="submit", command=submitButton)
newusersubmitbutton.grid(columnspan=2)
class newUserSubmitButton(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
##username variable goes here
page1 = self.controller.get_page(newUser.submitButton)
page1.username.set("Hello, world")
print(page1)
class delUser(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="del User!!!", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(Terminal))
button1.pack()
button2 = tk.Button(self, text="new User",
command=lambda: controller.show_frame(newUser))
button2.pack()
app = Club()
app.title("Club Terminal")
app.iconbitmap("table.ico")
app.mainloop()
Whenever I run this code, I get an AttributeError: 'newUser' object has no attribute 'controller'.
Any help is greatly appreciated, I'll be more than happy to try any ideas out.
With regards.
There are more problems in this code, but to solve that one, add the line:
self.controller=controller
To the newUser classes __init__ function.
I am coding a program which will need functions to change labels and enter text into text boxes however I do not know how to do this with a new style of programming which I am using (Object Orientated). I have done the program before however I generated the frames using this code:
f = [Frame(root) for i in range(0,5)]
for i in f:
i.place(relx=0,rely=0,relwidth=1,relheight=1)
and then I put it all in one class which I ran however that was bad form so I am redoing it. My code so far is as follows:
import tkinter as tk
import datetime,time,os,sys
import sqlite3 as lite
from datetime import date
Title_Font= ("Times", 18, "underline italic")
unix = time.time()
time_curr = str(datetime.datetime.fromtimestamp(unix).strftime('%H:%M'))
date1 = str(datetime.datetime.fromtimestamp(unix).strftime('%d-%m-%Y'))
class Creating_Stuff(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(3, weight=1)
self.option_add( "*font", "Times 12" )
self.tk_setPalette(background='#bbfff0', foreground='black',
activeBackground='#d9d9d9', activeForeground='#ff9933')
label = tk.Label(self, text="Laptop Booking System", font=Title_Font)
label.grid(row=0, column=1, pady = 30)
time = tk.Label(self, text="Time: " + time_curr + "\nDate: " + date1, font="Times 10")
time.grid(row=0, column=2,columnspan=2)
Booking_1 = tk.Button(self, text="Booking",
command=lambda: controller.show_frame(PageOne),bg='#f2f2f2',width=20)
Booking_1.grid(row=2, column=1, pady=10)
Tbl_q1 = tk.Button(self, text="Table Querying",
command=lambda: controller.show_frame(PageTwo),bg='#f2f2f2',width=20)
Tbl_q1.grid(row=3, column=1, pady=10)
Exit = tk.Button(self, text ="Exit",command=lambda:destroy(),bg='#f2f2f2')
Exit.grid(row=5, column=1)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(3, weight=1)
label = tk.Label(self, text="Page One!!!", font=Title_Font)
label.grid(row=1, column=1)
bk2_menu = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
bk2_menu.grid(row=3, column=1)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(3, weight=1)
label = tk.Label(self, text="Page Two!!!", font=Title_Font)
label.grid(row=1, column=1)
bk2_Menu2 = tk.Button(self, text="Back to menu",
command=lambda: controller.show_frame(StartPage))
bk2_Menu2.grid(row=3, column=1)
app = Creating_Stuff()
def destroy():
app.destroy()
app.title("Laptop Booking System")
app.geometry("700x400")
app.mainloop()
If you try it out it works however it just has 3 different frames. How can I get a button in a frame to make a label say "Hello" in the frame after it is pressed?
I've modified one of your page classes to illustrate how it could be done. It involved adding a Label to hold the message, a Button to control it, and a function, called simply handler(), to call when the latter is pressed. It saves the widgets by making them attributes of the containing Frame subclass instance, self, so they can be easily referenced in the handler() function (without resorting to global variables).
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(3, weight=1)
label = tk.Label(self, text="Page One!!!", font=Title_Font)
label.grid(row=1, column=1)
self.msg_label = tk.Label(self, text="")
self.msg_label.grid(row=2, column=1)
self.msg_button = tk.Button(self, text='Show Message',
command=self.handler)
self.msg_button.grid(row=3, column=1)
self.msg_toggle = False
bk2_menu = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
bk2_menu.grid(row=5, column=1)
def handler(self):
self.msg_toggle = not self.msg_toggle
if self.msg_toggle:
self.msg_label.config(text='Hello')
self.msg_button.config(text='Clear Message')
else:
self.msg_label.config(text='')
self.msg_button.config(text='Show Message')
Screenshots
Before button is pressed:
After button is pressed: