time.sleep blocks pressing tkinter.Button - python

I am trying to implement an app to show image for 6s and then jump to another page with radio buttons to let them rate their own feeling about the images (give 15s for rating) and then show the next image. However, for counting down 15s, I used time.sleep() function, which make the program very slow for pressing radiobuttons (but my clicking, the button can still be pressed). I am wondering whether there is a way that the count down will not influence the program but just let the program jump to the next page when time up. Here is my code :
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.geometry('1800x970+15+5')
self.title('Emotion Test')
# 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", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, Show_image, Rate_e, EndPage):
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()
frame.execute()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text=instructions, width=1500, height=700, font=TITLE_FONT)
label.pack(side="top")
label.place(x=100, y=40, width=1500, height=700)
button1 = tk.Button(self, text="Start", height = 5, width = 10,
command=lambda: controller.show_frame("Show_image"))
button1.pack(side="bottom")
def execute(self):
current = -1
class EndPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text=instructions, width=1500, height=700,
font=TITLE_FONT)
label.pack(side="top")
label.place(x=100, y=40, width=1500, height=700)
def execute(self):
current = -1
class Show_image(tk.Frame):
def __init__(self, parent, controller):
global seconds
tk.Frame.__init__(self, parent)
self.controller = controller
global lbPic
global timer1
lbPic = tk.Label(self, text='Image System', width=1200, height=900)
lbPic.place(x=20, y=50, width=1200, height=900)
lbPic.pack(side="top", fill="both", expand=True)
timer1 = tk.Label(self, text='timer', width=50, height=10)
timer1.place(x=1325, y=2, width=50, height=10)
timer1.pack(side="bottom", fill="both", expand=True)
def execute(self):
if (self.changePic(1)):
for k in range(seconds, 0, -1):
timer1["text"] = "Time left : " + str(k) + " s"
self.update()
time.sleep(1)
self.controller.show_frame("Rate_e")
else:
self.controller.show_frame("EndPage")
def changePic(self, flag):
global current
new = current + flag
if new<0:
#tkMessageBox.showerror('', 'last picture')
return 0
elif new>=len(pics):
#tkMessageBox.showerror('', 'last picture')
return 0
else:
#get the next picture
pic = pics[new]
im = Image.open(pic)
w, h = im.size
if w>1200:
h = int(h*1200/w)
w = 1200
if h>800:
w = int(w*800/h)
h = 800
im = im.resize((w,h))
im1 = ImageTk.PhotoImage(im)
lbPic['image'] = im1
lbPic.image = im1
current = new
return 1
class Rate_e(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
global wait
self.controller = controller
global lbrate1
global lbrate2
global lbrate3
global timer2
lbrate1 = tk.Label(self, text='Pleasure', width=1400, height=260)
lbrate1.place(x=50, y=10, width=1400, height=260)
lbrate1.pack(side="top", fill="both", expand=True)
lbrate2 = tk.Label(self, text='Excited', width=1400, height=260)
lbrate2.place(x=50, y=230, width=1400, height=260)
lbrate2.pack(side="top", fill="both", expand=True)
lbrate3 = tk.Label(self, text='Controlled', width=1400, height=260)
lbrate3.place(x=50, y=650, width=1400, height=260)
lbrate3.pack(side="top", fill="both", expand=True)
timer2 = tk.Label(self, text='timer', width=50, height=10)
timer2.place(x=800, y=850, width=50, height=10)
timer2.pack(side="bottom", fill="both", expand=True)
MODES = [
("1", 1),
("2", 2),
("3", 3),
("4", 4),
("5", 5),
("6", 6),
("7", 7),
("8", 8),
("9", 9)
]
global v1
v1 = tk.StringVar()
v1.set("score1") # initialize
xaxis = 0
for text, mode in MODES:
b1 = tk.Radiobutton(self, text=text,
variable=v1, value=mode)
b1.config(indicatoron=False,width=10,height=2)
b1.place(x=140 + xaxis*127, y=240 , width=100, height=30)
#b.pack(side="left")
xaxis = xaxis + 1
global v2
v2 = tk.StringVar()
v2.set("score2") # initialize
xaxis = 0
for text, mode in MODES:
b2 = tk.Radiobutton(self, text=text,
variable=v2, value=mode)
b2.config(indicatoron=False,width=10,height=2)
b2.place(x=140 + xaxis*128, y=510 , width=100, height=30)
#b.pack(side="left")
xaxis = xaxis + 1
global v3
v3 = tk.StringVar()
v3.set("score3") # initialize
xaxis = 0
for text, mode in MODES:
b3 = tk.Radiobutton(self, text=text,
variable=v3, value=mode)
b3.config(indicatoron=False,width=10,height=2)
b3.place(x=140 + xaxis*125, y=800 , width=100, height=30)
#b.pack(side="left")
xaxis = xaxis + 1
def execute(self):
for k in range(wait, 0, -1):
timer2["text"] = "Time left : " + str(k) + " s"
self.update()
time.sleep(1)
Pleasure = v1.get()
Excited = v2.get()
Control = v3.get()
print(Pleasure)
print(Excited)
print(Control)
self.controller.show_frame("Show_image")
if __name__ == "__main__":
suffix = ('.jpg', '.bmp', '.png')
pics = [image_path + p for p in os.listdir(image_path) if p.endswith(suffix)]
seconds = 2
wait = 6
current = -1
app = SampleApp()
app.mainloop()

Using time.sleep() in a tkinter program will make it unresponsive since it keeps the mainloop() from executing and able to respond to events.
To void that use the universal widget method after().
So, in your case use:
self.after(1000) # 1000 ms = 1 sec
instead of
time.sleep(1)
Here's some documentation I found about it.

Related

How to make this program to displayed the result correctly

I'm just learning Python and I don't know how to make this program to display result in label that I want and when I click button again I want to the new result replaces the previous one
I want to last class shows result in label or entry when i click 1st button and when i click it again the new result will replace previous.
This program is not finished yet. I don't want to write all code when i have problem with first function of program. Once I deal with this problem, writing the rest of the code will not be difficult
import tkinter as tk
from tkinter import ttk
class tkinterApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
windowWidth = 300
windowHeight = 200
offsetLeft = int( (self.winfo_screenwidth() - windowWidth) / 2 )
offsetTop = int( (self.winfo_screenheight() - windowHeight) / 2 )
self.geometry('{}x{}+{}+{}'.format(windowWidth, windowHeight, offsetLeft, offsetTop))
self.title('Konwerter systemów liczbowych')
self.minsize(300, 200)
container = tk.Frame(self, relief="ridge", width=300, height=200)
container.pack(expand = False)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
self.frames = {}
for F in (StartPage, Decy, decBin):
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)
label = ttk.Label(self, text ="Wybierz system do którego należy Twoja liczba.")
label.grid()
button1 = ttk.Button(self, text ="Decymalny",
command = lambda : controller.show_frame(Decy))
button1.grid(padx = 5, pady = 5)
button2 = ttk.Button(self, text ="Binarny",
command = lambda : controller.show_frame(Binar))
button2.grid(padx = 5, pady = 5)
button3 = ttk.Button(self, text ="Oktalny",
command = lambda : controller.show_frame(Oktal))
button3.grid(padx = 5, pady = 5)
button4 = ttk.Button(self, text ="Heksadecymalny",
command = lambda : controller.show_frame(Heksal))
button4.grid(padx = 5, pady = 5)
class Decy(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text ="Wybierz system na jaki chcesz przekowertować")
label.grid()
label = ttk.Label(self, text ="swoją liczbę.")
label.grid()
button1 = ttk.Button(self, text ="Binarny",
command = lambda : controller.show_frame(decBin))
button1.grid(padx = 5, pady = 5)
button2 = ttk.Button(self, text ="Oktalny",
command = lambda : controller.show_frame(decOkt))
button2.grid(padx = 5, pady = 5)
button2 = ttk.Button(self, text ="Heksadecymalny",
command = lambda : controller.show_frame(decHex))
button2.grid(padx = 5, pady = 5)
button2 = ttk.Button(self, text ="Powrót",
command = lambda : controller.show_frame(StartPage))
button2.grid(padx = 5, pady = 5)
class decBin(tk.Frame):
def clearText(self):
self.entry1.confing(text='')
def oblicz():
dec = wpis.get()
dec = int(dec)
i = 0
bnum = []
while dec!=0:
rem = dec%2
bnum.insert(i, rem)
i = i+1
dec = int(dec/2)
i = i-1
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text ="Wprowadź liczbę i zatwierdź.")
label.grid()
wpis = ttk.Entry(self)
wpis.grid()
button1 = ttk.Button(self, text="Konwertuj", command = oblicz)
button1.grid(padx = 10, pady = 10)
button2 = ttk.Button(self, text ="Powrót", command = lambda : controller.show_frame(StartPage))
button2.grid(padx = 10, pady = 10)
app = tkinterApp()
app.mainloop()
If you want to update an existing Label widget, declare a tk.StringVar() to store the label text, then bind that to your Label's textvariable attribute. Then your Label will automatically update whenever you set() the StringVar.
label_var = tk.StringVar(self, 'Default Value') # both of these args are optional
label = ttk.Label(self, textvariable=label_var) # instantiate Label and bind the var
To update the label:
label_var.set('New String Value')
When you grid()/pack()/place() your Label it will start with the text you gave the StringVar, if any.

Tkinter Updating list from loaded Dataframe

Building tkinter application that loads a selected .csv as a dataframe and prints the "Categories" of dataframe on second page listbox as selectable option.
Data from .csv in DataFrame Form
import pandas as pd
data = [['TNUAX', '1290 Diversified Bond A','Intermediate Core-Plus Bond'],
['ABSZX', 'AB Discovery Value Z','Small Value'],
['AUIAX', 'AB Equity Income A','Large Value']]
df = pd.DataFrame(data, columns=['Ticker', 'Fund Name','Category'])
df
Application Code:
import tkinter as tk
from tkinter import *
import tkinter.ttk as ttk
import pandas as pd
class MyApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky='NSEW')
self.show_frame(PageOne)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class PageOne(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.make_widget()
def make_widget(self):
#Sections of Page/Canvas
self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
#Fonts and sizes
mainf = 'Arial'
headsz = 20
bodysz = 12
def databrowser():
root = tk.Tk()
root.withdraw()
data_f_path = filedialog.askopenfilename()
df = pd.read_csv(data_f_path)
x = df.Category.unique().tolist()
#Score Sort Header
lbl=tk.Label(self.cvs2, text="Score Sort", background= 'white',fg='#529e3f', font=(mainf, headsz))
lbl.place(relx=0.75, rely=0.01, relwidth=0.5, anchor='ne')
#Select Data File (.csv) label
datainlabel=Label(self.cvs2, text="Select Data File (.csv)", bg='white', fg='Black', font=(mainf,bodysz))
datainlabel.place(relx=0.55, rely=0.55, relwidth=0.31, anchor='ne')
#Select Data Input File Button
InSbtn = Button(self.cvs2, text='Browse', font=(mainf, bodysz),command=databrowser) #command=selected_item
InSbtn.place(relx=0.75, rely=0.55, relwidth=0.15, anchor='ne')
def func2():
print('Load List Function Here?')
#Next Page Button
btnChange = Button(self.cvs3, text="Next", font=(mainf,bodysz),command=lambda: [self.controller.show_frame(PageTwo),func2()],
bg="white") #'#529e3f' #acdcea
btnChange.place(relx=.775, rely=.15, width="80", height="40")
def change_page(self):
pass
#Pack Sections
self.cvs.pack()
self.cvs2.pack()
self.cvs3.pack()
class PageTwo(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.make_widget()
def make_widget(self):
#Fonts and sizes
mainf = 'Arial'
headsz = 20
bodysz = 12
#Sections of Page
self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
self.cvslist = Canvas(self, width="500", height="300", background="#acdcea", bd=-2)
#Category Selection List
yscrollbar = Scrollbar(self.cvslist)
yscrollbar.pack(side = RIGHT, fill = Y)
#Create window label
label = Label(self.cvslist,text = "Select Assset Categories : ",font = (mainf, 13),bg='#acdcea', padx = 5, pady = 5)
label.pack()
# Create a listbox
listbox = Listbox(self.cvslist, width=40, height=15, selectmode=MULTIPLE, font=(mainf,11),yscrollcommand = yscrollbar.set)
#Expanse Option
listbox.pack(padx = 10, pady = 10,expand = YES, fill = "both")
#Part that Loads Assets categories from csv in list
for each_item in range(len(x)):
listbox.insert(END, x[each_item])
listbox.itemconfig(each_item, bg = "white")
#Back Page Button
backbutt = tk.Button(self.cvs3, text='Back',font=('Arial',12), bg='white',command=lambda: self.controller.show_frame(PageOne))
backbutt.place(relx=0.225, rely=0.15, anchor='ne', width="80", height="40")
#Pack Sections
self.cvs.pack()
self.cvs2.pack()
self.cvslist.place(relx=0.75, rely=0.2, relwidth=0.5, anchor='ne')
self.cvs3.pack()
if __name__ == '__main__':
app = MyApp()
app.title('Score Sort')
app.mainloop()
The Error:
name 'x' is not defined from the part that loads this listbox
for each_item in range(len(x)):
listbox.insert(END, x[each_item])
listbox.itemconfig(each_item, bg = "white")
My Attmept:
Is to bind a command that loads the list when next button is pressed.
def func2():
print('Load List Function Here?')
#Next Page Button
btnChange = Button(self.cvs3, text="Next", font=(mainf,bodysz),command=lambda: [self.controller.show_frame(PageTwo),func2()],
bg="white") #'#529e3f' #acdcea
btnChange.place(relx=.775, rely=.15, width="80", height="40")
Application with errant section #'out, to visually see app attempt:
import tkinter as tk
from tkinter import *
import tkinter.ttk as ttk
import pandas as pd
class MyApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky='NSEW')
self.show_frame(PageOne)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class PageOne(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.make_widget()
def make_widget(self):
#Sections of Page/Canvas
self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
#Fonts and sizes
mainf = 'Arial'
headsz = 20
bodysz = 12
def databrowser():
root = tk.Tk()
root.withdraw()
data_f_path = filedialog.askopenfilename()
df = pd.read_csv(data_f_path)
x = df.Category.unique().tolist()
#Score Sort Header
lbl=tk.Label(self.cvs2, text="Score Sort", background= 'white',fg='#529e3f', font=(mainf, headsz))
lbl.place(relx=0.75, rely=0.01, relwidth=0.5, anchor='ne')
#Select Data File (.csv) label
datainlabel=Label(self.cvs2, text="Select Data File (.csv)", bg='white', fg='Black', font=(mainf,bodysz))
datainlabel.place(relx=0.55, rely=0.55, relwidth=0.31, anchor='ne')
#Select Data Input File Button
InSbtn = Button(self.cvs2, text='Browse', font=(mainf, bodysz),command=databrowser) #command=selected_item
InSbtn.place(relx=0.75, rely=0.55, relwidth=0.15, anchor='ne')
def func2():
print('Load List Function Here?')
#Next Page Button
btnChange = Button(self.cvs3, text="Next", font=(mainf,bodysz),command=lambda: [self.controller.show_frame(PageTwo),func2()],
bg="white") #'#529e3f' #acdcea
btnChange.place(relx=.775, rely=.15, width="80", height="40")
def change_page(self):
pass
#Pack Sections
self.cvs.pack()
self.cvs2.pack()
self.cvs3.pack()
class PageTwo(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.make_widget()
def make_widget(self):
#Fonts and sizes
mainf = 'Arial'
headsz = 20
bodysz = 12
#Sections of Page
self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
self.cvslist = Canvas(self, width="500", height="300", background="#acdcea", bd=-2)
#Category Selection List
yscrollbar = Scrollbar(self.cvslist)
yscrollbar.pack(side = RIGHT, fill = Y)
#Create window label
label = Label(self.cvslist,text = "Select Assset Categories : ",font = (mainf, 13),bg='#acdcea', padx = 5, pady = 5)
label.pack()
# Create a listbox
listbox = Listbox(self.cvslist, width=40, height=15, selectmode=MULTIPLE, font=(mainf,11),yscrollcommand = yscrollbar.set)
#Expanse Option
listbox.pack(padx = 10, pady = 10,expand = YES, fill = "both")
# #Part that Loads Assets categories from csv in list
# for each_item in range(len(x)):
# listbox.insert(END, x[each_item])
# listbox.itemconfig(each_item, bg = "white")
#Back Page Button
backbutt = tk.Button(self.cvs3, text='Back',font=('Arial',12), bg='white',command=lambda: self.controller.show_frame(PageOne))
backbutt.place(relx=0.225, rely=0.15, anchor='ne', width="80", height="40")
#Pack Sections
self.cvs.pack()
self.cvs2.pack()
self.cvslist.place(relx=0.75, rely=0.2, relwidth=0.5, anchor='ne')
self.cvs3.pack()
if __name__ == '__main__':
app = MyApp()
app.title('Score Sort')
app.mainloop()
Understand this is alot of code and considerably complex from most stackoverflow questions. If anyone can suggest/recommend another platform where I can get hands on help that would be appreciated as well.
I can tell you how to make this work, but this is a really bad design. Your second page is relying on data that was generated by the first page. You need a sensible way to do that communication. In the short term, if you add global x to your databrowser function, it will work.
At the very least, take databrowser out of PageOne.make_widget and make it a global function. You'll still need the global x statement, but at least it makes it clear that it's not private data.

Code organisation and instance of Class with arguments

I'm building my first app with Tkinter and I'm stuck with instance of class.
I followed this tutorial https://pythonprogramming.net/change-show-new-frame-tkinter/ but some questions remain :
- how does mean controller as argument for the init line in the differents class ?
- I would like to create instance of classA in classB and inversely but I'm getting wrong with arguments !
- About organisation code, I created 2 class (one for each frame) and writting function related in the good one, should I split them in a 3rd class ? What is the most convenient way ?
Actualy the code is running but I would like to add some setup tools in the 2nd window !
What do I must do to can modify some parameters in the SettingsPage Class and update and use them in the StartPage class ? For example I would like to modify ecartementlogo.
setp = SettingsPage(controller, StartPage) give me AttributeError: type object 'StartPage' has no attribute 'tk'
Sorry for posting all the code but I not sure about what can I remove for the example
import tkinter as tk
from PIL import Image, ImageDraw, ImageColor, ImageFont, ImageChops, ImageColor, ImageFont, ImageTk as PIL
import os
import utilitary as utilitary
import tkinter.ttk as ttk
from tkinter import filedialog
from configparser import ConfigParser
LARGE_FONT= ("Verdana", 12)
basewidth = 400
config = ConfigParser()
config.read('config.ini')
class DotaCasterKit(tk.Tk):
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self,default='icone.ico')
tk.Tk.wm_title(self, "vs Creator")
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, SettingsPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
menu = tk.Menu(container)
menu.config(background='#2B547E', fg='#2B547E')
tk.Tk.config(self, menu=menu)
file = tk.Menu(menu)
file.add_command(label="Préférences", command=self.show_settings)
file.add_command(label="Exit",command=quit)
menu.add_cascade(label="File", menu=file)
refresh = tk.Menu(menu)
menu.add_cascade(label="Refresh", menu=refresh)
def show_settings(self):
frame = self.frames[SettingsPage]
frame.tkraise()
class StartPage(tk.Frame):
def choose_background(self):
print(self.path_slogos)
self.background_file = filedialog.askopenfilename(initialdir='C:/Users/.../VS')
self.background = PIL.Image.open(self.background_file).convert('RGBA')
self.background_name = os.path.basename(self.background_file)
self.var1.set(self.background_name)
self.miniature = self.background
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas.itemconfig(self.image_on_canvas, image =self.miniature)
def choose_slogos_path(self):
self.path_slogos = filedialog.askdirectory(initialdir='C:/Users/.../Logos')
self.var2.set(os.path.basename(self.path_slogos))
return self.path_slogos
def create_list_logos(self):
self.path_slogos = filedialog.askdirectory(initialdir='C:/Users/.../Logos')
self.var2.set("Dossier : "+os.path.basename(self.path_slogos))
self.files = []
self.list_files_names =[]
print(self.path_slogos)
for r, d, f in os.walk(self.path_slogos):
for file in f:
if '.png' in file and 'background' not in file:
self.files.append(os.path.join(r, file))
name = os.path.basename(file)
name = name[:-4]
self.list_files_names.append(name)
self.liste_1.config(values=self.list_files_names)
self.liste_2.config(values=self.list_files_names)
self.liste_1.current(0)
self.liste_2.current(0)
return self.list_files_names
def create_img(self):
self.composition = self.background
self.ecartementlogo = 550
rift_middle = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 150)
text_middle = 'VS'
text_match_format = self.entry_format_match.get()
rift_match_format = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 60)
rift_score = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 50)
self.score_1 = self.entry_score__1.get()
self.score_2 = self.entry_score__2.get()
self.1=self.liste_1.get()
self.2=self.liste_2.get()
self.logo_1 = PIL.Image.open(self.path_slogos+'/'+self.1+'.png').convert('RGBA')
self.logo_2 = PIL.Image.open(self.path_slogos+'/'+self.2+'.png').convert('RGBA')
#logo 1
self.composition = utilitary.draw_image_advanced(self.composition, self.logo_1,
[960-int(self.ecartementlogo), 550],
[None, 300],
1)
#logo 2
self.composition = utilitary.draw_image_advanced(self.composition, self.logo_2,
[960+int(self.ecartementlogo), 550],
[None, 300],
1)
image_draw = ImageDraw.Draw(self.composition)
#insert text (VS + score)
utilitary.draw_text_center_align(image_draw, [960, 450], text_middle, font=rift_middle, fill=utilitary.colors['white'])
utilitary.draw_text_center_align(image_draw, [960, 600], text_match_format, font=rift_match_format, fill=utilitary.colors['white'])
utilitary.draw_text_center_align(image_draw, [960-self.ecartementlogo, 700], self.score_1, font=rift_score, fill=utilitary.colors['light_red'])
utilitary.draw_text_center_align(image_draw, [960+self.ecartementlogo, 700], self.score_2, font=rift_score, fill=utilitary.colors['light_red'])
if self.var4.get()==0:
pass
if self.var4.get()==1:
config.set('main', 'default_file_background', self.background_file)
config.set('main', 'default_path_slogos', self.path_slogos)
with open('config.ini', 'w') as f:
config.write(f)
print(self.background_file)
print(self.path_slogos)
print("settings saved")
print("image created")
self.miniature = self.composition
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas.itemconfig(self.image_on_canvas, image =self.miniature)
def save_img(self):
self.var5.set("Saved as " +self.1 + '_'+self.2+'.png')
self.composition.save('C:/.../'+self.1 + '_'+self.2+'.png')
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
setp = SettingsPage(controller, StartPage)
### this line don't work ### wrong arguments
self.background_file = config.get('main', 'default_file_background')
self.path_slogos = config.get('main', 'default_path_slogos')
self.group3 = tk.LabelFrame(self, text=" A & Score")
self.group3.pack (side="left", padx=5, pady=5)
self.group4 = tk.LabelFrame(self, text=" B & Score")
self.group4.pack (side="right", padx=5, pady=5)
self.liste_1 = ttk.Combobox(self.group3)
self.liste_1.pack(side="top", padx=5, pady=5)
self.liste_2 = ttk.Combobox(self.group4)
self.liste_2.pack(side="top", padx=5, pady=5)
self.liste_1, self.liste_2 = utilitary.initial_list_logos(self.path_slogos, self.liste_1, self.liste_2)
self.liste_1.current(0)
self.liste_2.current(0)
self.group1 = tk.LabelFrame(self, text="Background")
self.group1.pack (side="top", padx=5, pady=5)
self.button_choose_background_file = tk.Button(self.group1, text="Choose Background")
self.button_choose_background_file.config(command=self.choose_background)
self.button_choose_background_file.pack (side="top", padx=5, pady=5)
self.var1 = tk.StringVar()
self.var1.set(os.path.basename(self.background_file))
self.label_name_background_file = tk.Label(self.group1, textvariable=self.var1)
self.label_name_background_file.pack (side="bottom", padx=5, pady=5)
self.group2 = tk.LabelFrame(self, text="Logos s",labelanchor='ne')
self.group2.pack (side="top", padx=5, pady=5)
self.var2 = tk.StringVar()
self.var2.set("Dossier : "+os.path.basename(self.path_slogos))
self.label_path_slogo = tk.Label(self.group2, textvariable=self.var2)
self.label_path_slogo.pack (side="bottom", padx=5, pady=5)
self.button_list_logos = tk.Button(self.group2, text="Choose logos path")
self.button_list_logos.config(command=self.create_list_logos)
self.button_list_logos.pack (side="top", padx=5, pady=5)
self.score_1 = tk.StringVar()
self.score_1.set("")
self.entry_score__1 = tk.Entry(self.group3,textvariable=self.score_1, width=5, justify='center')
self.entry_score__1.pack(side="bottom", padx=5, pady=5)
self.score_2 = tk.StringVar()
self.score_2.set("")
self.entry_score__2 = tk.Entry(self.group4,textvariable=self.score_2, width=5, justify='center')
self.entry_score__2.pack(side="bottom", padx=5, pady=5)
self.button_show = tk.Button(self, text="Show Image")
self.button_show.config(command=self.create_img)
self.button_show.pack (side="bottom", padx=5, pady=5)
self.button_save_img = tk.Button(self, text="Save as image")
self.button_save_img.config(command=self.save_img)
self.button_save_img.pack (side="bottom", padx=5, pady=5)
self.var5 = tk.StringVar ()
self.var5.set('')
self.label_name_save = tk.Label(self, textvariable =self.var5)
self.label_name_save.pack (side="bottom", padx=5, pady=5)
self.format_match = tk.StringVar()
self.format_match.set("Format")
self.entry_format_match = tk.Entry(self,textvariable=self.format_match, width=10, justify='center')
self.entry_format_match.pack()
self.var4 = tk.IntVar ()
self.var4.set(0)
self.button_save_settings = tk.Checkbutton(self, text="Save settings", variable = self.var4)
self.button_save_settings.pack(side = "bottom")
self.ecartementlogo = tk.IntVar ()
self.var4.set(550)
self.background=PIL.Image.open(self.background_file).convert('RGBA')
self.basewidth = 400
self.miniature = self.background
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
print(self.miniature.size)
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas = tk.Canvas(self,width=400, height=225)
self.image_on_canvas = self.canvas.create_image(0, 0, anchor="nw", image=self.miniature)
self.canvas.pack()
class SettingsPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
startp = StartPage(controller, SettingsPage)
###this line work ### arguments are correct
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Edition Page",
command=lambda: controller.show_frame(StartPage))
button1.pack()
scale_x_logo_ = tk.Scale(self, orient='horizontal', from_=0, to=startp.background.size[0]/2,
resolution=10, length=350,
label='Placement horizontal logo', variable= startp.ecartementlogo)
scale_x_logo_.pack()
app = DotaCasterKit()
app.geometry("%dx%d%+d%+d" % (850,620,100,100))
app.mainloop()
This line used in the SettingsPage Class is ok : startp = StartPage(controller, SettingsPage)
I would say I need to write the reverse one but I having error with arguments (controller, StartPage) BUT this one setp = SettingsPage(controller, StartPage) don't work (in the init line of StartPage Class)
The issue is here:
class StartPage(tk.Frame):
# ... Omitting irrelevant stuff
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
setp = SettingsPage(controller, StartPage) # Right here
# ... More irrelevant stuff
You are passing the class StartPage to the SettingsPage, rather than an instance of that class. SettingsPage then tries to interpret the class as a widget, but a class is not a widget and does not have a tk attribute.
The best way to fix this is to remove that line, since you never use the variable setp. The corresponding line in SettingsPage.__init__ should also be removed.
You should also read the link suggested by #Brian Oakley. Your code currently has many issues. I have fixed one of them, and his link should help you fix more.

Calling function from one class in another class

Before I post my code, this is not all of it but what I feel is relevant to my problem. The first class is run when the user clicks a button, so the class contents (the frame) is displayed. The handler for my frames is:
class Begin(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# Creating the initial frame
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 (LoginScreen, RegisterWindow, RevisionTopics, dataRep, reviseTen, FrameTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
page_name = LoginScreen.__name__
self.frames[page_name] = frame
self.show_frame(LoginScreen) # Shows the page currently being interacted
Now, this is the frame which has the important function, 'start', which I need to run in my second frame.
First frame:
class reviseTen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.startButton = tk.Button(self, text="Click here to start revision session", command = self.start)
self.optionOne = tk.Button(self, text="Option One")
self.optionTwo = tk.Button(self, text="Option Two")
self.optionThree = tk.Button(self, text="Option Three")
self.optionFour = tk.Button(self, text="Option Four")
self.proceedButton = tk.Button(self, text="Proceed to next question", command=lambda: controller.show_frame(FrameTwo))
self.question = tk.Label(self, text="What is the definition of: ")
self.startButton.grid(row=0, column=0)
def start(self): #This is what I wanna use in my second frame
firstTime = True
while firstTime:
self.startButton.destroy()
firstTime = False
words = self.makeDict()
initialListOne = ['Integer', 'Natural number', 'Rational numbers', 'Irrational numbers', 'Real numbers', 'Ordinal numbers', 'Binary', 'Hexadecimal']
listOne = []
for i in initialListOne:
listOne.append(words[i])
initialListTwo = ['Denary to Hex', 'Binary to Hex', 'ASCII', 'Unicode', 'Overflow error', 'Twos complement', 'Bitmapped graphics', 'Resolution']
listTwo = []
for i in initialListTwo:
listTwo.append(words[i])
initialListThree = [ 'Bit Colour Depth', 'Metadata', 'Sample resolution', 'Sample Rate', 'Audio file size', 'Nyquist Theorem', 'MIDI', 'Lossy Compression']
listThree = []
for i in initialListThree:
listThree.append(words[i])
initialListFour = ['Lossless Compression', 'Run Length Encoding', 'Dictionary compression', 'Encryption', 'Encryption steps', 'Caesar cipher',
'Brute force attack', 'Frequency analysis', 'Vernam cipher', 'One-Time Pad']
listFour = []
for i in initialListFour:
listFour.append(words[i])
listOfKeys = [] # Holds the keywords
listOfValues = [] # Holds the definitions
for key in words:
listOfKeys.append(key)
listOfValues.append(words[key])
keywordPosition = random.randint(1, len(listOfKeys)-1)
QKeyword = listOfKeys[keywordPosition]
QDef = listOfValues[keywordPosition]
self.question.grid(row=0, column=0)
self.optionOne.grid(row=1, column=0)
self.optionTwo.grid(row=2, column=0)
self.optionThree.grid(row=3, column=0)
self.optionFour.grid(row=4, column=0)
self.proceedButton.grid(row=5, column=0)
self.question.config(text=("What is the definition of: "+ QKeyword))
randomOne = random.randint(0, len(listOne))
randomTwo = random.randint(0, len(listTwo))
randomThree = random.randint(0, len(listThree))
randomFour = random.randint(0, len(listFour))
selectButton = random.randint(1,4)
if selectButton == 1:
self.optionOne.config(text=QDef)
self.optionTwo.config(text=listOfValues[randomTwo])
self.optionThree.config(text=listOfValues[randomThree])
self.optionFour.config(text=listOfValues[randomFour])
elif selectButton == 2:
self.optionOne.config(text=listOfValues[randomOne])
self.optionTwo.config(text=QDef)
self.optionThree.config(text=listOfValues[randomThree])
self.optionFour.config(text=listOfValues[randomFour])
elif selectButton == 3:
self.optionOne.config(text=listOfValues[randomOne])
self.optionTwo.config(text=listOfValues[randomTwo])
self.optionThree.config(text=QDef)
self.optionFour.config(text=listOfValues[randomFour])
elif selectButton == 4:
self.optionOne.config(text=listOfValues[randomOne])
self.optionTwo.config(text=listOfValues[randomTwo])
self.optionThree.config(text=listOfValues[randomThree])
self.optionFour.config(text=QDef)
def makeDict(self):
dict = {}
con = sql.connect("dataRep.db")
cur = con.cursor()
for column in cur.execute("SELECT keyword, definition FROM words"):
variable = column[0]
variable2 = column[1]
dict[variable] = variable2
return dict
Second Frame:
class FrameTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.optionOne = tk.Button(self, text="Option One")
self.optionTwo = tk.Button(self, text="Option Two")
self.optionThree = tk.Button(self, text="Option Three")
self.optionFour = tk.Button(self, text="Option Four")
self.question = tk.Label(self, text="What is the definition of: ")
# TRIED THIS - screen stays blank (but start method has code that makes the widgets appear
self.start(controller)
def start(self, controller):
self.reviseTen = reviseTen(self, controller)
I need start to complete the exact same functions as it did on the frame 'reviseTen', the function is running but just not doing anything to my second frame. It is just blank. The code for positioning the elements (so they show up) is meant to run after running start...
Is it to do with the way I called it?
Many thanks for any help.
Inherit reviseTen in your class instead of tk.Frame, and call the function with super:
class FrameTwo(reviseTen):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.optionOne = tk.Button(self, text="Option One")
self.optionTwo = tk.Button(self, text="Option Two")
self.optionThree = tk.Button(self, text="Option Three")
self.optionFour = tk.Button(self, text="Option Four")
self.question = tk.Label(self, text="What is the definition of: ")
super(FrameTwo, self).start(controller)
For more info on super, check out some of the answers to this question.

changing frames based on radio button answer

I'm trying a simple experiment, I have 3 frames, frame 1 has two labels - "for page 2" and "for page 3", it also has 2 radio buttons corresponding to the labels. based on which radio button is selected, when the user hits the next page button, I want the button to bring the user to the selected page
this is the code -
import Tkinter as tk
class MainApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the main container that holds all the frames
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 = {}
# adding frames to the dictionary
for F in (Page1,Page2,Page3):
frame = F(container,self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "w")
self.show_frame(Page1)
def show_frame(self,page_name):
#SHOWS A FRAME WITH THE GIVEN NAME
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[page_name]
frame.grid()
#STACKING THE FRAMES
#frame = self.frames[cont]
#frame.tkraise()
class Page1(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl1 = tk.Label(self,text = "for page 2",font =("Helvetica",12,"bold"))
lbl1.grid(row=1,sticky="W")
lbl2 = tk.Label(self,text = "for page 3",font =("Helvetica",12,"bold"))
lbl2.grid(row=1,column=1,sticky="W")
btn1 = tk.Button(self, text="next page", font=('MS', 24, 'bold'))
btn1.grid(row=3,column = 0,columnspan=1)
#btn1['command'] = lambda: controller.show_frame(Page2)
self.var1 = tk.BooleanVar()
rButton1 = tk.Radiobutton(self,variable = self.var1,value=True)
rButton1.grid(row=2,sticky = "W")
rButton2 = tk.Radiobutton(self,variable = self.var1,value=False)
rButton2.grid(row=2,column=1,sticky = "W")
if self.var1.get() == 1:
btn1['command'] = lambda: controller.show_frame(Page3)
if self.var1.get() == 0:
btn1['command'] = lambda: controller.show_frame(Page2)
class Page2(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 2",font=("Helvetica",12,"bold"))
lbl.pack()
class Page3(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 3",font=("Helvetica",12,"bold"))
lbl.pack()
app = MainApp()
app.mainloop()
I assumed that by using a few basic conditions (located in my PageOne class) -
self.var1 = tk.BooleanVar()
rButton1 = tk.Radiobutton(self,variable = self.var1,value=True)
rButton1.grid(row=2,sticky = "W")
rButton2 = tk.Radiobutton(self,variable = self.var1,value=False)
rButton2.grid(row=2,column=1,sticky = "W")
if self.var1.get() == 1:
btn1['command'] = lambda: controller.show_frame(Page3)
if self.var1.get() == 0:
btn1['command'] = lambda: controller.show_frame(Page2)
I would be able to achieve this, but it doesn't seem to work. The conditions in my if statements are integers but to my knowledge 1 represents True and 0; False anyway? what am i doing wrong?
I think this is what you want. I didn't handle making sure the radiobutton isn't selected by default. I left that as an exercise to you. Although, if you're wanting to just switch pages like this I'd just use buttons (tk/ttk.Button), then you don't have to worry about handling the radiobutton. Although, that's just my preference either will work fine of course. You can just bind each button to switch the page. I commented the buttons out in your modified code below.
If you're wanting to create buttons / radiobuttons to have a forward / back option for each page. You can just iterate over the controllers frames to see which is the current, and create two buttons similar to the ones below to move to the other frames.
import tkinter as tk
class MainApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the main container that holds all the frames
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 = {}
# adding frames to the dictionary
for F in (Page1,Page2,Page3):
frame = F(container,self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "w")
self.show_frame(Page1)
def show_frame(self,page_name):
#SHOWS A FRAME WITH THE GIVEN NAME
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[page_name]
frame.grid()
#STACKING THE FRAMES
#frame = self.frames[cont]
#frame.tkraise()
class Page1(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.controller = controller
lbl1 = tk.Label(self,text = "for page 2",font =("Helvetica",12,"bold"))
lbl1.grid(row=1,sticky="W")
lbl2 = tk.Label(self,text = "for page 3",font =("Helvetica",12,"bold"))
lbl2.grid(row=1,column=1,sticky="W")
btn1 = tk.Button(self, text="next page", font=('MS', 24, 'bold'))
btn1.grid(row=3,column = 0,columnspan=1)
#btn1['command'] = lambda: controller.show_frame(Page2)
self.var1 = tk.BooleanVar()
#rButton1 = tk.Button(self, text='Show Page 2', command=lambda: self.controller.show_frame(Page2))
#rButton1.grid(row=2, sticky="W")
#rButton2 = tk.Button(self, text='Show Page 3', command=lambda: self.controller.show_frame(Page3))
#rButton2.grid(row=2, column=1, sticky="W")
rButton1 = tk.Radiobutton(self,variable = self.var1,value=True,
command=self.switch_pages)
rButton1.grid(row=2,sticky = "W")
rButton2 = tk.Radiobutton(self,variable = self.var1,value=False,
command=self.switch_pages)
rButton2.grid(row=2,column=1,sticky = "W")
def switch_pages(self):
if not self.var1.get():
self.controller.show_frame(Page3)
else:
self.controller.show_frame(Page2)
class Page2(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 2",font=("Helvetica",12,"bold"))
lbl.pack()
class Page3(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 3",font=("Helvetica",12,"bold"))
lbl.pack()
app = MainApp()
app.mainloop()

Categories

Resources