Multiple Frames, Multiple Buttons and Labels with changing image, Tkinter - python

I'm designing a GUI with Tkinter. It has many frames(pages) that by pressing a button in one frame, that frame is destroyed and next frame is displayed. Each of the buttons has variable images, so I need a function that rotates the button image of each page being displayed.
I wrote the following code, the Address of the photos changes (self.Address in (def counter) of Pagestart class) but I think button.config cannot update images of button!!!Why???
(For Explain: countercounter function in showframe function of main class is responsible for updating of counter function in pagestart.)
the output of this code display a frame with one button that its image is constant and it can't update.
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
import time
import os
import subprocess as sp
import signal
global counter0, counter1
counter0=0
counter1=0
class Project(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.configure(background="#000000")
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.container=container
self.frames = {}
for F in ( Pagestart, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Pagestart)
def show_frame(self, cont):
self.sw = 1000
self.sh = 1800
self.cont=cont
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[cont]
frame.configure(background="#000000")
frame.grid()
frame.winfo_toplevel().geometry('%dx%d+%d+%d' % (self.sw,self.sh,0,0))
A=Pagestart(parent=self.container, controller=self)
self.Pagestart=Pagestart
B=A.button
def countercounter(B):
def count1():
global counter0, counter1
A.counter()
if (self.cont==Pagestart):
B.after(100,count1)
count1()
countercounter(B)
def twoside(self, inputaddress, startframe, stopframe):
self.input = inputaddress
self.startframe = startframe
self.stopframe = stopframe
global counter0, counter1
def count():
global counter0, counter1
if (counter1==1):
counter0 -=1
if (counter1==0):
counter0 += 1
self.Address=('%s%s' % (str(self.input),str(counter0))+".jpg")
if (counter0==self.stopframe):
counter1=1
if (counter0==self.startframe):
counter1=0
count()
def sendAddress(self):
return self.Address
class Pagestart(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(PageOne))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference0/"
self.controller.twoside(self.inputaddress, 0, 138)
self.Address = self.controller.sendAddress()
self.photo = Image.open(self.Address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image=self.photo
self.button.config(image=self.photo)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(Pagestart))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference1/"
self.controller.twoside(self.inputaddress, 0, 138)
self.Address = self.controller.sendAddress()
self.photo = Image.open(self.Address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image=self.photo
self.button.config(image=self.photo)
if __name__ == "__main__":
app = Project()
app.mainloop()

You don't destroy frame but you only hide it using grid_forget()/grid_remove().
Don't create new instance of Pagestart because you already have old instance of Pagestart which is displayed with
frame = self.frames[cont]
frame.grid()
and you should change image in this instance using ie.
frame.counter()
Working code:
I use self.counter and self.animation_direction instead of global variables counter0, counter1.
I don't use nested functions because it is less readable.
I use change_image() to change image every 100ms
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
class Project(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.counter = 1
self.animation_direction = 1 # it will add `+1` to self.counter
self.sw = 1000
self.sh = 1800
container = tk.Frame(self)
container.configure(background="#000000")
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.container = container
self.frames = {}
for F in ( PageStart, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(PageStart)
def show_frame(self, cont):
self.cont = cont
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[cont]
frame.configure(background="#000000")
frame.grid()
frame.winfo_toplevel().geometry('%dx%d+%d+%d' % (self.sw,self.sh,0,0))
#frame.counter()
self.change_image()
def twoside(self, inputaddress, startframe, stopframe):
self.input = inputaddress
self.startframe = startframe
self.stopframe = stopframe
self.counter += self.animation_direction
self.address = '%s%s.jpg' % (self.input, self.counter)
if self.counter == self.stopframe:
self.animation_direction = -self.animation_direction
if self.counter == self.startframe:
self.animation_direction = -self.animation_direction
def get_address(self):
return self.address
def change_image(self):
if self.cont == PageStart:
self.frames[self.cont].counter()
self.after(100, self.change_image)
class PageStart(tk.Frame): # PEP8: UpperCaseNames for classes
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(PageOne))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference0/"
self.controller.twoside(self.inputaddress, 0, 138)
self.address = self.controller.get_address() # PEP8: lower_case_names for functions/methods and variables
self.photo = Image.open(self.address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image = self.photo
self.button.config(image=self.photo)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(PageStart))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference1/"
self.controller.twoside(self.inputaddress, 0, 138)
self.address = self.controller.get_address()
self.photo = Image.open(self.address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image = self.photo
self.button.config(image=self.photo)
if __name__ == "__main__":
app = Project()
app.mainloop()

Related

Why is my program crashing when using tkinter's after function?

import socket
import time
import tkinter as tk
from tkinter import StringVar
from functools import partial
import Client
import json
LARGE_FONT= ("Verdana", 8)
class MainGUI(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 (ConnectionPage, SystemPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(ConnectionPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def show_error(self, cont, error):
frame = self.frames[cont]
ErrorLabel = tk.Label(frame,text=error,font=LARGE_FONT)
ErrorLabel.pack()
ErrorLabel.after(2500,lambda :ErrorLabel.destroy())
def show_json_info(self,cont,sock):
frame = self.frames[cont]
packet = sock.recv(2000).decode()
JsonDict = json.loads(packet)
SysInfoLabel = tk.Label(frame, text="System Info", font=LARGE_FONT)
SysInfoLabel.pack(pady=10, padx=10)
SysInfoText = tk.Text(frame)
SysInfoText.pack()
SysInfoText.config(font=LARGE_FONT)
SysInfoText.insert("end","System: "+JsonDict["System"]+'\n')
SysInfoText.insert("end","NodeName: " + JsonDict["NodeName"]+'\n')
SysInfoText.insert("end","Release: "+JsonDict["Release"]+'\n')
SysInfoText.insert("end","Version: "+JsonDict["Version"]+'\n')
SysInfoText.insert("end","Machine: "+JsonDict["Machine"]+'\n')
SysInfoText.insert("end","Processor: "+JsonDict["Processor"]+'\n')
frame.after(2500,self.show_json_info,SystemPage,sock)
class ConnectionPage(tk.Frame):
def InitiateConnection(self,controller, Ip,Port):
sock = Client.CheckAddress(Ip,Port)
if sock:
controller.show_frame(SystemPage)
controller.show_json_info(SystemPage, sock)
else:
controller.show_error(ConnectionPage, "Wrong Ip or port")
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Connection Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
IpLabel = tk.Label(self, text="Ip address")
IpLabel.pack()
Ip = StringVar()
IpEntry = tk.Entry(self, textvariable=Ip)
IpEntry.pack()
PortLabel = tk.Label(self, text="Port")
PortLabel.pack()
Port = StringVar()
PortEntry = tk.Entry(self, textvariable=Port)
PortEntry.pack()
cmd = partial(self.InitiateConnection,controller,Ip,Port)
ConnectButton = tk.Button(self, text="Connect", command=cmd)
ConnectButton.pack()
class SystemPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
BackToConnectionButton = tk.Button(self, text="Back to Connection Page",command=lambda: controller.show_frame(ConnectionPage))
BackToConnectionButton.pack()
if __name__ == "__main__":
app = MainGUI()
app.mainloop()
The function show_json_info() is supposed to update the current window every time it runs, when trying to use a regular loop the program crashed, so ive used tkinter's after function. but the same thing happen and the program crashes.
any idea why is this happening? any alternatives I can use to fix the problem?
p.s the function Client.CheckAddress() returns a socket which I receive input from.

tkinter items not being placed inside frame

I'm having an issue where nothing is being placed in the frame and I'm not sure why as the template works when I tested that and can't see where I have done anything different other than use different widgets
Template used: https://www.semicolonworld.com/question/42826/switch-between-two-frames-in-tkinter
This is my code with most of the items removed other than a couple of examples
import tkinter as tk
from tkinter import *
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=False)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
#frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
def check_user_login():
#code removed
return None
#create a canvas to place images on
canvas = tk.Canvas(self, bg = "#5c5c5c",height = 400,width = 800,bd = 0,highlightthickness = 0,relief = "flat")
canvas.place(x = 0, y = 0)
#login button
button = tk.Button(self, text="Go to the start page",command=lambda: controller.show_frame("PageOne"))
button.place(x = 356, y = 290,width = 89,height = 29)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
#create a canvas to place images on
canvas = Canvas(self,bg = "#5c5c5c",height = 400,width = 800,bd = 0,highlightthickness = 0,relief = "flat")
canvas.place(x = 0, y = 0)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = App()
app.geometry("800x400")
# app.configure(bg = "#5c5c5c")
app.resizable(False, False)
app.mainloop()
Any help would be massively appreciated I've been stuck on this for a while

Tkinter config from a different class

I have shortened my code to 100 lines but in a much larger file I cannot properly use the config function to change a label in a different class. If you run my file here you will see I am attempting to change 0 to a different str value.
from tkinter import *
class App(Tk):'
def __init__(self, *args, **kwargs):
#declare our frame
Tk.__init__(self, *args, **kwargs)
#Setup the frame
container = Frame(self)
container.pack(side = "top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
#declare the object to hold the frames
self.frames = {}
#loop through the list of frames
for i in (Page_One, Page_Two):
frame = i(container, self)
self.frames[i] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
#start with the home page
self.show_frame(Page_One)
def show_frame(self, context):
'''This method will show the frame that is passed through it. \n
Used in button by implementing lamda'''
frame = self.frames[context]
frame.tkraise()
class Page_One(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
def change_value(value):
Page_Two.label.config(text = value)
print(value)
Button(self, text='Set value to 1',command=lambda *args: change_value('1')).pack()
Button(self, text='Set value to 2',command=lambda *args: change_value('2')).pack()
Button(self, text='Set value to 3',command=lambda *args: change_value('3')).pack()
Button(self, text='Page 2', command = lambda: controller.show_frame(Page_Two)).pack()
class Page_Two(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = Label(self, text = '0')
label.pack()
Button(self, text='Page 1',command = lambda: controller.show_frame(Page_One)).pack()
app = App()
app.mainloop()

Passing value to tkinter class

This is a part of my actual program. I am trying to write a code that will create few Buttons on loop.
My problem is I am passing a value to a tkinter class but it is not printing the right value instead just prints a ".".
I want to pass "chap" from class PageOne to "class ChapterOne" in the below code it isn't working. I don't have much experience in classes. A help here will be much appreciated.
import tkinter as tk
from PIL import ImageTk, Image
from os import listdir
import yaml
LARGE_FONT = ("Verdana", 12)
grey = "#808080"
offwhite = "#e3e3e3"
hintwa = False
x = ''
class MainBot(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 page in (StartPage, PageOne, ChapterOne):
frame = page(container, self)
print (container)
self.frames[page] = frame
frame.grid(row = 0, column = 0, sticky = 'nsew')
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def yaml_loader(self, filepath):
with open (filepath, "r") as fileread:
self.data = yaml.load(fileread)
return self.data
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, background = offwhite, text= "Start Page", font = LARGE_FONT)
label.pack(pady=10, padx=10)
button_start = tk.Button(self, text = 'NEXT', font = ("default", 15, "bold"), bg='orange', fg = 'white', border=2, height = 2, width = 8, command=lambda: controller.show_frame(PageOne))
button_start.pack()
button_start.place(x=650, y=500)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
index_label = tk.Label(self, text = "~~~~~INDEX~~~~~", font = ("Comicsans", 24, "bold"), background = offwhite)
index_label.pack()
index_label.place(x=200, y=50)
onlyfiles = ['chapter-1.yaml']
for yfiles in onlyfiles:
chap = (yfiles.split("."))[0].split("-")[1]
print (chap)
button_index_one = tk.Button(self, text='Chapter ' + str(chap), font=("default", 14, "bold"), bg='white',
fg='black', border=1, height=2, width=12,
command=lambda: controller.show_frame(ChapterOne(self, chap)))
button_index_one.pack(pady=30, padx=0)
class ChapterOne(tk.Frame):
def __init__(self, parent, chap):
tk.Frame.__init__(self, parent)
print (chap)
app = MainBot()
app.geometry("800x600")
app.mainloop()

Updating frames on Python's Tkinter

I need some help updating frames. I did some research (update() and update_idletasks()) but so far nothing has worked. I could be implementing these methods incorrectly. This is what i have so far...
import tkinter as tk
LARGE_FONT = ("Verdana", 12)
class controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
self.minsize(width=300, height=300)
container.grid()
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {} # Store all the frames
for F in (StartPage, CustomerLocation, CustomerPickDate):
frame = F(container, self) # F is the classes
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage) # Make Start Page on the top
def show_frame(self, cont): # Used to bring the given frame to the top/ show frame
frame = self.frames[cont] # Access the dic of all the frames
frame.tkraise()
def get_page(self, page_class): # Get access to the page and its attributes
return self.frames[page_class]
def combine_funcs(self, *funcs): # Run multi funcs at one time (attach to a button!)
def combined_func(*args, **kwargs):
for f in funcs:
f(*args, **kwargs)
return combined_func
def updateFrame(self,frame):
selectedFrame = self.frames[frame]
selectedFrame.update_idletasks()
#frame.update_idletasks(self)
print("hit")
class CustomerLocation(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.myparent = parent
self.controller = controller
self.configure(background='#ED7D3B')
# ________________________________________________________________
# self.variableLocation is what I want in the next frame
# ________________________________________________________________
self.variableLocation = tk.StringVar(self)
self.variableLocation.set("Select")
alist = ["Blackwood", "Camden", "Philadelphia"]
locationOptionMenu = tk.OptionMenu(self, self.variableLocation, *alist)
locationOptionMenu.pack()
#print(self.variableLocation.get())
nextButton = tk.Button(self, text="Next",
command=lambda: controller.combine_funcs(controller.updateFrame(CustomerPickDate),
controller.show_frame(CustomerPickDate)
))
nextButton.configure(highlightbackground='#ED7D3B')
nextButton.pack()
backButton = tk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
backButton.configure(highlightbackground='#ED7D3B')
backButton.pack()
At this point self.variableLocation should be one of the alist variables. I used the controller.get_page(FRAME) to grab that value. I want to use this value as a label on the next frame.
class CustomerPickDate(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller # Help Access the controller + its methods
self.CustomerLocation = self.controller.get_page(CustomerLocation) # Access to Frame CustomerLocation
self.variableLocation = self.CustomerLocation.variableLocation
print(self.variableLocation.get())
label = tk.Label(self, text="Select Date", font=LARGE_FONT)
label.pack(pady=10, padx=10)
# _______________________________________________________
# I want the self.variableLocation from the pervious frame
# to be here!___________________________________________
self.label2 = tk.Label(self, text="TEST %s" % self.variableLocation.get()) # NEED FIXING/ ABLE TO UPDATE
self.label2.pack()
self.label2.update()
NextButton = tk.Button(self, text="Next")
NextButton.pack()
BackButton = tk.Button(self, text="Back",
command=lambda: controller.show_frame(CustomerLocation))
BackButton.pack()
I am very new to Tkinter, so any feed back would be great!

Categories