Calling function from one class in another class - python

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.

Related

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.

When trying to list pickled data I only get data that was last stored, how do I display all pickled data?

I want to display all my pickled values in a list. However, when I run the program and press the button that takes me to the page where all the values will be displayed, I'm only shown a single value. This value is from the last time the program was run. When I close the program and try again it displays the value from my last attempt.
here's what I'm talking about: https://imgur.com/a/m79BkMY
This is the code where the pickling and display of data happens
class Streak():
def __init__(self, action, streak_num, day, hour, minute, stored_streaks):
self.action = action
self.streak_num = streak_num
self.day = day
self.hour = hour
self.minute = minute
def get_time(self):
self.action = app.frames[PageOne].action_text.get()
self.streak_num = int(app.frames[PageOne].streak_num_text.get())
self.day = int(app.frames[PageOne].due_day.get())
self.hour = int(app.frames[PageOne].due_hour.get())
self.minute = int(app.frames[PageOne].due_minute.get())
stored_streaks = {}
total = (self.day * 86400) + (self.hour * 3600) + (self.minute * 60)
streak_input = {"action": self.action , "streak_num": self.streak_num,
"total": total}
store_streak = stored_streaks.update(streak_input.copy())
with open("streak.pkl", "wb") as wpickle_file:
pickle.dump(stored_streaks, wpickle_file)
print(streak_input)
app.show_frame(PageTwo)
def read_file(self):
with open("streak.pkl", "rb") as pickle_file:
stored_streaks = pickle.load(pickle_file)
#stored_streaks.update(streak_input) ## comeback to this
return stored_streaks
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
streak_title = tk.Label(self, text="New Streak", font=XLARGE_FONT)
streak_title.pack()
action_label = tk.Label(self, text="Action", font=LARGE_FONT)
action_label.pack(anchor="w", padx=30, pady=10)
self.action_text = tk.Entry(self, width=250)
self.action_text.pack(anchor="w", padx=30)
streak_num_label = tk.Label(self, text="Streak #", font=LARGE_FONT)
streak_num_label.pack(anchor="w", padx=30, pady=10)
self.streak_num_text = tk.Entry(self, width=250)
self.streak_num_text.pack(anchor="w", padx=30, pady=10)
day_label = tk.Label(self, text="Day(s)")
day_label.pack()
self.due_day = tk.Entry(self)
self.due_day.pack()
hour_label = tk.Label(self, text="Hour(s)")
hour_label.pack()
self.due_hour = tk.Entry(self)
self.due_hour.pack()
minute_label = tk.Label(self, text="Minute(s)")
minute_label.pack()
self.due_minute = tk.Entry(self)
self.due_minute.pack()
get_time_btn = tk.Button(self, bg="medium purple", text="Add",
command=lambda: Streak.get_time(self))
get_time_btn.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
test_label = tk.Label(self, text="TEST", font=XLARGE_FONT)
test_label.pack()
streak_list = Streak.read_file(self)
action_list = streak_list.get("action", "none")
display_list = [action_list]
for action_list in display_list:
list = tk.Button(self, bg="medium purple", text=action_list)
list.pack()
back_btn = tk.Button(self, bg="medium purple", text="BACK",
command=lambda: controller.show_frame(PageOne))
back_btn.pack()
I included print so I could make sure that the get_time function was working. From the first image this was printed:
{'action': 'first', 'streak_num': 1, 'total': 120}
When I print streak_list it prints the data that was input before the program closed

Defining a global function at the beginning, but it needs variables not defined yet

I´ve been working on a bhaskara equation solver with Interface(tkinter) on python. I´ve watched some tutorials and I´ve split the Pages into classes. The basic idea of this application is the following:
It starts at StartPage, where the user clicks on the select values button and this opens up PageOne. At this page the user should type the desired values and click the select values button (I´m sorry, I couldn't find a way to save all the values at one, if you know how to do this too, please let me know). After the user saves the values, he goes back to StartPage and clicks the Calculate button. If all values are OK, it shows the result variable.
import tkinter as tk
from tkinter import ttk, END
import math
LARGE_FONT =("Verdana", 12)
def calculate():
global value_a
global value_b
global value_c
value_list = [value_a, value_b, value_c]
if "" in value_list:
return False
else:
delta = (int(value_b.get())**2) - 4*int(value_a.get())*int(value_c.get())
if delta >= 0:
delta_root = math.sqrt(delta)
bhask_pos = int(-value_b.get()) + (delta_root)/2*int(value_a.get())
bhask_neg = int(-value_b.get()) - (delta_root)/2*int(value_a.get())
global result
result = "The equation", int(value_a.get()), "x² +", int(value_b.get()), "x +", int(value_c.get()), "has the results ", int(bhask_pos), "and ", int(bhask_neg)
else:
pass
return True
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self,*args, **kwargs)
#self.geometry("720x360")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_columnconfigure(0, weight=1)
container.grid_rowconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne):
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)
button = ttk.Button(self, text="Insert values", command=lambda: controller.show_frame(PageOne))
button.pack(side="top", padx=10, pady=20, expand=False)
canvas = tk.Canvas(self, width=400, height=200, bg="#C0C0C0", bd="10")
canvas.pack(side="bottom", padx=10, pady=20, expand=False)
if calculate() == False:
canvas.create_text(30, 30, text="Error. Check if you selected all values")
elif calculate() == True:
canvas.create_text(30, 30, text=result)
else:
pass
calculation_button = ttk.Button(self, text="Calculate", command=calculate)
calculation_button.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def get_entry_data_a():
global value_1
value_1 = int(value_a.get())
entry_a.delete(0, END)
print(value_1)#just for debugging
def get_entry_data_b():
global value_2
value_2 = int(value_b.get())
entry_b.delete(0, END)
print(value_2)
def get_entry_data_c():
global value_3
value_3 = int(value_c.get())
entry_c.delete(0, END)
print(value_3)
def event_data_a(event):
value_1 = int(value_a.get())
entry_a.delete(0, END)
print(value_1)
def event_data_b(event):
value_2 = int(value_b.get())
entry_b.delete(0, END)
print(value_2)
def event_data_c(event):
value_3 = int(value_c.get())
entry_c.delete(0, END)
print(value_3)
text_a = tk.Label(self, text="value from a:", padx=10, pady=10)
text_a.grid(row=1, column=1)
text_b = tk.Label(self, text="value from b:", padx=10, pady=10)
text_b.grid(row=2, column=1)
text_c = tk.Label(self, text="value from c", padx=10, pady=10)
text_c.grid(row=3, column=1)
value_a = tk.IntVar()
entry_a = tk.Entry(self, textvariable=value_a)
entry_a.grid(row=1, column=2)
entry_a.delete(0, END)
button_a = ttk.Button(self, text="Save value", command=get_entry_data_a)
button_a.grid(row=1, column=3, padx=10, pady=10)
value_b = tk.IntVar()
entry_b = tk.Entry(self, textvariable=value_b)
entry_b.grid(row=2, column=2)
entry_b.delete(0, END)
button_b = ttk.Button(self, text="Save value", command=get_entry_data_b)
button_b.grid(row=2, column=3, padx=10, pady=10)
value_c = tk.IntVar()
entry_c = tk.Entry(self, textvariable=value_c)
entry_c.grid(row=3, column=2)
entry_c.delete(0, END)
button_c = ttk.Button(self, text="Save value", command=get_entry_data_c)
button_c.grid(row=3, column=3,padx=10, pady=10)
entry_a.bind("<Return>", event_data_a)
entry_b.bind("<Return>", event_data_b)
entry_c.bind("<Return>", event_data_c)
back_button = ttk.Button(self, text="Return to Start Page", command=lambda:controller.show_frame(StartPage))
back_button.grid(row=5, column=2, padx=20, pady=20)
app = App()
app.mainloop()
The results should appear in the canvas, as well as a message error if the user does not select a value. My actual problem is that the canvas object is created in StartPage, so I need to define the function before the StartPage class (because I need the boolean result returned from the function calculate() to create the text in the canvas), but the actual values that the user chooses comes only at the end of the code. How is it possible to use those values? Where should I define the function calculate() in this case?
This is the error message:
line 10, in calculate
value_list = [value_a, value_b, value_c]
NameError: name 'value_a' is not defined
To get the globals working properly, just make sure to give them initial values before their first used. The easiest way to do this is to assign them default values at the module level. For example:
value_a = ""
value_b = ""
value_c = ""
def calculate():
global value_a
global value_b
global value_c
# ...
That should get your code working. But while Python supports globals, they're almost never the right choice. Look at the logic of your application and think of ways to use local scope more. One possibility is to set value_a, value_b, and value_c as instance variable in your App class, since it's accessible as controller in both frames.
For example:
def calculate(value_a, value_b, value_c):
value_list = [value_a, value_b, value_c]
# ...
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self,*args, **kwargs)
self.value_a = ""
self.value_b = ""
self.value_c = ""
# ...
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# ...
if calculate(controller.value_a, controller.value_b, controller.value_c):
# ...
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def get_entry_data_a():
value_1 = int(controller.value_a.get())
entry_a.delete(0, END)
print(value_1)#just for debugging
# ...
controller.value_a = tk.IntVar()

time.sleep blocks pressing tkinter.Button

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.

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