For loop doesn't work properly for unknown reason - python

I have this code:
def show_hide(a, b, c, d):
if not b[0]["text"]: # check if text's length is 0
b[0].configure(text="{}".format(d[0]), bd=2, bg="white", command=lambda: activate_deactivate(a[0], c[0])) # configure button_1
c[0]["text"] = "Status: {}".format(a[0].get()) # set text for label_1
b[1].configure(text="{}".format(d[1]), bd=2, bg="white", command=lambda: activate_deactivate(a[1], c[1])) # configure button_2
c[1]["text"] = "Status: {}".format(a[1].get()) # set text for label_2
else:
b[0].configure(text="", bd=0, bg="#F0F0F0", command=None) # hide the button_1
c[0]["text"] = "" # hide the label_1
b[1].configure(text="", bd=0, bg="#F0F0F0", command=None) # hide the button_2
c[1]["text"] = "" # hide the label_2
My button which calls this function has this command value:
command=lambda: show_hide([status, status_2], [button, button_2], [label, label_2], ["Perform action #1", "Perform action #2"]))
By using it I can show/hide buttons but rewriting the same thing changing a digit in a few places would be tedious. To fix it I tried using this code instead of the original:
def show_hide(a, b, c, d):
for i in range(0, len(a)): # iterates over indexes of items in a,b,c,d lists (all 4 have same length) and passes them to the if/else statement
if not b[i]["text"]: # check if text length is 0
b[i].configure(text="{}".format(d[i]), bd=2, bg="white", command=lambda: activate_deactivate(a[i], c[i])) # configure buton
c[i]["text"] = "Status: {}".format(a[i].get()) # set label's text
else:
b[i].configure(text="", bd=0, bg="#F0F0F0", command=None) # hide button
c[i]["text"] = "" # hide label
Theoretically, this should work fine and using just 4 lines of code (not counting for/if/else lines) do the same thing as original function which would need 4 lines for EACH status+button+label created. BUT, as a matter of fact, it makes my 2 buttons work totally wrong.
I don't really understand what is wrong with it so I can't fully describe the problem, but you can see for yourself by using the test-script I made and replacing the show_hide function definition with the one using for-loop:
import tkinter as tk
import random
class ActionButton(tk.Button):
def __init__(self, *args, **kwargs):
tk.Button.__init__(self, *args, **kwargs)
self.configure(text="", font="courier 20", bd=0)
class ActionLabel(tk.Label):
def __init__(self, *args, **kwargs):
tk.Label.__init__(self, *args, **kwargs)
self.configure(text="", font="courier 14")
def multi(*args):
for func in args:
return func
def show_hide(a, b, c, d):
if not b[0]["text"]:
b[0].configure(text="{}".format(d[0]), bd=2, bg="white", command=lambda: activate_deactivate(a[0], c[0]))
c[0]["text"] = "Status: {}".format(a[0].get())
b[1].configure(text="{}".format(d[1]), bd=2, bg="white", command=lambda: activate_deactivate(a[1], c[1]))
c[1]["text"] = "Status: {}".format(a[1].get())
else:
b[0].configure(text="", bd=0, bg="#F0F0F0", command=None)
c[0]["text"] = ""
b[1].configure(text="", bd=0, bg="#F0F0F0", command=None)
c[1]["text"] = ""
def activate_deactivate(a, c):
if a.get() == "Can be done":
a.set("To be done")
c.configure(text="Status: {}".format(a.get()), fg="blue")
else:
a.set("Can be done")
c.configure(text="Status: {}".format(a.get()), fg="black")
def step_forward(a, b, c):
if a.get() == "To be done":
b.configure(text="", bd=0, bg="#F0F0F0", state="disabled")
c["text"] = ""
result = random.choice(["success", "failure"])
if result == "success":
a.set("Accomplished")
c["fg"] = "green"
else:
a.set("Failed")
c["fg"] = "red"
else:
b.configure(text="", bd=0, bg="#F0F0F0", command=None)
c["text"] = ""
root = tk.Tk()
status = tk.StringVar()
status.set("Can be done")
status_2 = tk.StringVar()
status_2.set("Can be done")
main = tk.Button(root, text="Show/Hide", bg="white", font="courier 30",
command=lambda: show_hide([status, status_2],
[button, button_2],
[label, label_2],
["Perform action #1", "Perform action #2"]))
main.pack()
frame = tk.Frame(root, pady=10)
frame.pack()
frame_1 = tk.Frame(frame, padx=10)
frame_1.pack(side="left")
frame_2 = tk.Frame(frame, padx=10)
frame_2.pack(side="left")
button = ActionButton(frame_1)
button.grid(row=0, column=0)
label = ActionLabel(frame_1)
label.grid(row=1, column=0)
button_2 = ActionButton(frame_2)
button_2.grid(row=0, column=1)
label_2 = ActionLabel(frame_2)
label_2.grid(row=1, column=1)
next_day = tk.Button(root, text="Next day", bg="white", font="courier 30",
command=lambda: multi(step_forward(status, button, label),
step_forward(status_2, button_2, label_2)))
next_day.pack()
root.mainloop()
I hope there's someone who may know how to fix this and maybe even has an idea about how the function could be changed to perform everything properly.

Related

Cannot get function to run from within another function

I'm trying to have a function perform an action using an if statement on a boolean_var().
Essentially, boolean_var.get() -> if True -> popup messagebox message, else -> Pass.
It's correctly "get"-ing the True statement and returning a 1, but will not enact the function I've called/pointed to afterward.
Any advice, or even pointing me to any specific reading material that may help would be greatly appreciated.
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
class WotcPull(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.master=master
self.NewWindow = tk.Toplevel(master)
self.NewWindow.title("Sources")
self.NewWindow.minsize(600, 400)
self.NewWindow.geometry("600x400")
self.NewWindow.columnconfigure(0, weight=1)
self.NewWindow.rowconfigure(0, weight=1)
self.NewWindow.rowconfigure(1, weight=15)
SourceOutputFrame = tk.Frame(
self.NewWindow,
bg="#414041",
borderwidth=1,
padx=3,
pady=10
)
SourceOutputFrame.grid(column=0, row=1, sticky=tk.N+tk.S+tk.W+tk.E)
SourceOutputFrame.columnconfigure(0, weight=1)
SourceOutputFrame.rowconfigure
SubOutputFrame = tk.Frame(
master=SourceOutputFrame,
bg="#414041",
borderwidth=1,
padx=3,
pady=10
)
SubOutputFrame.pack(side='top',fill='both',expand=1)
SubOutputButton["state"] = "disabled"
wotc_var = tk.BooleanVar()
wotc_yes = tk.Radiobutton(
master=SubOutputFrame,
text="Yes",
variable=wotc_var,
value=True,
foreground= "#C9C1B2",
background= "#414041",
borderwidth=1,
font=(25)
).grid(row=0, column=1, sticky=tk.NSEW)#, command=self.callback)
wotc_no = tk.Radiobutton(
master=SubOutputFrame,
text="No",
variable=wotc_var,
foreground= "#C9C1B2",
background= "#414041",
borderwidth=1,
font=(25),
value=False
).grid(row=0, column=2)#, command=self.callback)
def popupmsg():
messagebox.showinfo(message="success") #<- This will not work and I just can't figure out why
def SaveSources():
#messagebox.showinfo(message=wotc_var.get()) <- This works fine and returns 1 if wotc_yes is checked
if wotc_var.get() == 1:
#messagebox.showinfo(message=wotc_var.get()) <- Also works fine and returns 1 if wotc_yes is checked
popupmsg #<- the problem function that will not work for me
else:
messagebox.showinfo(message=wotc_var.get())
SaveSourcesButton = tk.Button(
master=SubOutputFrame,
text='Save',
command=SaveSources,
height=3,
width=15,
bg="#414041",
fg="#C9C1B2"
)
SaveSourcesButton.grid(
row=4,
column=0,
sticky=tk.EW,
padx=10,
pady=10
)

I'm trying to update the scene in tkinter, how can I do it?

I have an initial scene in tkinter, I want to update it when a button is clicked. But I don't know how to do it. I've tried several stuff but they don't work. What would you prefer?
Here's my Frame:
class App(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.master = master
self.screenState = "main" # is either hashtag selection, main, in progress(for hashtag mode), open(for free mode(since we'll need to close it manually))
self.accountName = ""
self.accountPassword = ""
Code of the initial scene:
def GUIMainScreen(self):
# Create input elements where you'll retrieve the username and the password
# Username input
Label1 = tk.Label(self.master, text="USERNAME:", bg="gray")
Label1.place(relx=0.5,rely=0.15,anchor="center")
usernameEntry = tk.Entry(self.master, width=30)
usernameEntry.place(relx=0.5, rely=0.20, anchor="center")
# Passoword input
Label2 = tk.Label(self.master, text="PASSWORD:", bg="gray")
Label2.place(relx=0.5,rely=0.27,anchor="center")
passwordEntry = tk.Entry(self.master, width=30)
passwordEntry.place(relx=0.5, rely=0.32, anchor="center")
# Buttons
# Subscribe & Like posts by hashtag(s)
hashtagB = tk.Button(self.master, text="Automation by Hashtags", bg="blue", width=20, command=lambda: self.switchScreenState("hashtag",usernameEntry.get(),passwordEntry.get()))
hashtagB.place(relx=0.5, rely=0.40, anchor="center")
# Unfollow people that don't follow you
unfollow = tk.Button(self.master, text="Clear Following", bg="blue", width=20)
unfollow.place(relx=0.5, rely=0.50, anchor="center")
# Develop account in free mode
free = tk.Button(self.master, text="Free Mode", bg="blue", width=20)
free.place(relx=0.5, rely=0.60, anchor="center")
Here's where I want to change the scene:
hashtagB = tk.Button(self.master, text="Automation by Hashtags", bg="blue", width=20, command=lambda: self.switchScreenState("hashtag",usernameEntry.get(),passwordEntry.get()))
This is the function that is called:
def switchScreenState(self, newState,username:str,password:str):
self.screenState = newState
if username != "" and password != "":
self.accountName = username
self.accountPassword = password
The second scene:
def hashtagSelectionScreen(self, username,password):
currentBot = Account(username,password)
# Passoword input
LabelHash = tk.Label(self.master, text="HASHTAGS:", bg="gray")
LabelHash.place(relx=0.5,rely=0.35,anchor="center")
hashEntry = tk.Entry(self.master, width=30)
hashEntry.place(relx=0.5, rely=0.40, anchor="center")
startButton = tk.Button(self.master, text="GO!", bg="blue", width=20)
startButton.place(relx=0.5, rely=0.55, anchor="center")
Here's how I present the scene:
def presentCurrentScene(self):
if self.screenState == "main":
print(app.accountName)
self.GUIMainScreen()
self.update()
elif self.screenState == "hashtag":
self.hashtagSelectionScreen(self.accountName,self.accountPassword)
self.update()
elif self.screenState == "open":
# This is for the free mode, since you'll have to stop it manually
pass
else:
# In progress screen for account developing with hashtags, just as same as "open" but you won't have to stop it manually
pass
Here's the main loop of the window:
mainWindow = tk.Tk()
mainWindow.title("Instagram Bot")
mainWindow.geometry("1080x900")
mainWindow.configure(bg="gray")
app = App(mainWindow)
app.presentCurrentScene()
mainWindow.mainloop()
I think the reason why this doesn't work is because mainWindow.mainloop() will never get to detect the change that happens, and the if presentCurrentScene() only runs once, but I just don't know what to do to meet the functionality that I'm looking for.
Thanks a lot in advance :)

How to checkbox to enable a button in Tkinter

self.label_5 = tk.Checkbutton(self.master, text="I agree to the", bg='white', width=14,font=("Arial", 8), command= activator)
self.label_5.place(x=112, y=410)
self.button_2 = tk.Button(text='Proceed', width=20, bg='white', state = tk.DISABLED, bd=1,
highlightbackground='black', font=("Arial", 10)).place(x=208, y = 512)
def activator(button):
if (self.button_2 ['state'] == tk.DISABLED):
self.button_2 ['state'] = tk.NORMAL
else:
self.button_2['state'] = tk.DISABLED
I want to enable the proceed button after I checked the checkbutton but I can't seem to figure it out.
You have to make the following changes to your code:
You have to refer to the function named activator as self.activator when giving it to the Button(button_2) as command.
You have to change the parameter named button of the function named activator to self.
And the most important thing you need to do is move the part of code where you are placing the Button(button_2) and the Checkbutton(label_5), to a new line. Like I have done in the code below. The reason for doing so is that pack, grid and place always return None. And when you do it in the same line where you have created your widgets and assigned them to a variable i.e. button_2 and label_5, the value None gets stored in that widget.
Here's the corrected code:
import tkinter as tk
class Test:
def __init__(self):
self.master = tk.Tk()
self.master.geometry('550x550')
self.label_5 = tk.Checkbutton(self.master, text="I agree to the", bg='white', width=14, font=("Arial", 8),
command=self.activator)
self.label_5.place(x=112, y=410)
self.button_2 = tk.Button(text='Proceed', width=20, bg='white', state=tk.DISABLED, bd=1,
highlightbackground='black', font=("Arial", 10))
self.button_2.place(x=208, y=512)
self.master.mainloop()
def activator(self):
if self.button_2['state'] == tk.DISABLED:
self.button_2['state'] = tk.NORMAL
else:
self.button_2['state'] = tk.DISABLED
if __name__ == '__main__':
Test()

Python - Update GUI so it always uses the updated list

I have a project where I have multiple "Widgets" in a frame. I pass a list of Widgets onto a class called GUI which displays all these "Widgets". I have a button on my frame which needs to delete one of the widgets from the list. Which it does. But the widget itself does not disappear from the frame.
So to clarify
The GUI class has a list of objects [Object, Object, Object, Object]. So It now displays 4 Widget objects. When I press the delete button the new list looks like [Object, Object, Object] but the list in the GUI class is still [Object, Object, Object, Object]. How can I make it so the GUI always uses the newest version of the list?
The code to clarify
import random
from tkinter import *
import datetime
root = Tk()
root.configure(background="white")
root.title("Project 2.1")
Widgets = []
class GUI:
def __init__(self, widgets, master):
self.widgets = widgets
self.master = master
self.master.geometry("{0}x{1}+0+0".format(master.winfo_screenwidth(), master.winfo_screenheight()))
for widget in range(len(widgets)):
widgets[widget].widgetFrame.pack(side=LEFT, pady=20, padx=20, fill=Y)
if len(widgets) == 0:
NoInputLabel = Label(master, text="No modules connected, please connect a module", font='Helvetica 16 bold')
NoInputLabel.pack(side=TOP)
removeButton = Button(master, text="Remove widget", command=self.removeWidget)
removeButton.pack()
print(self.widgets)
def removeWidget(self):
self.widgets = self.widgets[:-1]
print(self.widgets)
class Widget:
def __init__(self, master, name):
self.master = master
self.name = name
colorArray = ["#e3e0f3", "#eebddd", "#80a3db", "#036fa0"]
self.widgetFrame = Frame(master, bg="white")
# widgetFrame.pack(side=LEFT, pady=20, padx=50)
widgetTop = Frame(self.widgetFrame, bg="white")
widgetTop.pack(side=TOP)
widgetCenter = Frame(self.widgetFrame, bg="white")
widgetCenter.pack(side=TOP)
widgetBottom = Frame(self.widgetFrame, bg="white")
widgetBottom.pack(side=TOP, fill=X)
self.WidgetName = Label(widgetTop, text=name, font='Helvetica 16 bold', bg=random.choice(colorArray))
self.WidgetName.pack(fill=X)
self.temperatureSliderLabel = Label(widgetTop, text="Temperature (°C)", bg="white")
self.temperatureSliderLabel.pack()
self.temperatureSlider = Scale(widgetTop, orient=HORIZONTAL, length=250, from_=0, to=40, bg="white")
self.temperatureSlider.pack()
self.lightSliderLabel = Label(widgetTop, text="Light Intensity (%)", bg="white")
self.lightSliderLabel.pack()
self.lightSlider = Scale(widgetTop, orient=HORIZONTAL, length=250, bg="white")
self.lightSlider.pack()
self.maxRolloutPositionLabel = Label(widgetTop, text="Rolling distance (cm): ", bg="white")
self.maxRolloutPositionLabel.pack()
self.maxRolloutEntry = Entry(widgetTop, bg="white")
self.maxRolloutEntry.pack(side=LEFT)
self.submitFormButton = Button(widgetTop, text="Submit", command=self.setSensors, bg="white")
self.submitFormButton.pack(side=LEFT)
self.openSunblindButton = Button(widgetCenter, text="Open sunblind", command=self.openSunblind, state=NORMAL,
bg="#28a745", fg="white")
self.openSunblindButton.pack(side=LEFT)
self.closeSunblindButton = Button(widgetCenter, text="Close sunblind", command=self.closeSunblind,
state=NORMAL, bg="#dc3545", fg="white")
self.closeSunblindButton.pack(side=LEFT)
self.setSunblindStatusButton = Button(widgetCenter, text="Automatic", command=self.setSunblindStatus,
bg="#6c757d", fg="white")
self.setSunblindStatusButton.pack(side=LEFT)
self.sunblindFrame = Frame(widgetBottom, bg="white")
self.sunblindFrame.pack(fill=X)
self.sunblindStatusLabel = Label(self.sunblindFrame, text="Sunblind status:", bg="white", anchor="w")
self.sunblindStatusLabel.pack(side=LEFT, pady=5)
self.sunblindStatus = Label(self.sunblindFrame, text="Manual", bg="white")
self.sunblindStatus.pack(side=RIGHT, pady=5)
self.temperatureFrame = Frame(widgetBottom, bg="white")
self.temperatureFrame.pack(fill=X)
self.temperatureValueLabel = Label(self.temperatureFrame, text="Temperature: ", justify=LEFT, bg="white",
anchor="w")
self.temperatureValueLabel.pack(side=LEFT, pady=5)
self.temperatureValue = Label(self.temperatureFrame, text="", bg="white")
self.temperatureValue.pack(side=RIGHT, pady=5)
self.lightSliderFrame = Frame(widgetBottom, bg="white")
self.lightSliderFrame.pack(fill=X)
self.lightSliderValueLabel = Label(self.lightSliderFrame, text="Light Intensity: ", justify=LEFT, bg="white",
anchor="w")
self.lightSliderValueLabel.pack(side=LEFT, pady=5)
self.lightSliderValue = Label(self.lightSliderFrame, text="", bg="white")
self.lightSliderValue.pack(side=RIGHT, pady=5)
self.rolloutFrame = Frame(widgetBottom, bg="white")
self.rolloutFrame.pack(fill=X)
self.rolloutLabel = Label(self.rolloutFrame, text="Roll-out position: ", justify=LEFT, bg="white", anchor="w")
self.rolloutLabel.pack(side=LEFT, pady=5)
self.rolloutValue = Label(self.rolloutFrame, text="", bg="white")
self.rolloutValue.pack(side=RIGHT, pady=5)
self.variable = StringVar(widgetBottom)
self.variable.set(self.name)
self.chooseArduino = OptionMenu(widgetBottom, self.variable, "Living Room", "Bedroom", "Study", "Sex Dungeon",
"Bingo club")
self.chooseArduino.pack()
self.setNameButton = Button(widgetBottom, text="Set name", command=self.setArduinoName)
self.setNameButton.pack()
def setSensors(self):
print("Set the temperature and light of the sensors")
self.temperatureValue.config(text=str(self.temperatureSlider.get()) + "°C")
self.temperature = self.temperatureSlider.get()
self.lightSliderValue.config(text=str(self.lightSlider.get()))
self.lightIntensity = self.lightSlider.get()
self.maxRolloutPositionLabel.config(text="Rolling distance (cm): " + str(self.maxRolloutEntry.get()))
def setName(self, widgetName):
self.widgetName = widgetName
def openSunblind(self):
print("Set the sunblind to an open state")
self.rolloutValue.config(text="Rolled out")
def closeSunblind(self):
print("Set the sunblind to an closed state")
self.rolloutValue.config(text="Rolled in")
def setSunblindStatus(self):
if self.setSunblindStatusButton.config('text')[-1] == 'Automatic':
self.openSunblindButton.config(state=DISABLED)
self.closeSunblindButton.config(state=DISABLED)
self.setSunblindStatusButton.config(text='Manual')
print("Sunblind is set to: " + self.setSunblindStatusButton['text'])
self.sunblindStatus.config(text="Automatic")
else:
self.openSunblindButton.config(state=NORMAL)
self.closeSunblindButton.config(state=NORMAL)
self.setSunblindStatusButton.config(text='Automatic')
print("Sunblind is set to: " + self.setSunblindStatusButton['text'])
self.sunblindStatus.config(text="Manual")
def setArduinoName(self):
self.WidgetName.config(text=self.variable.get())
def getTemperature(self):
return self.temperature
def getLightIntensity(self):
return self.lightIntensity
Arduino1 = Widget(root, "Arduino 1")
Arduino2 = Widget(root, "Arduino 2")
Arduino3 = Widget(root, "Arduino 3")
Arduino4 = Widget(root, "Arduino 4")
Arduino5 = Widget(root, "Arduino 5")
Widgets.append(Arduino1)
Widgets.append(Arduino2)
Widgets.append(Arduino3)
Widgets.append(Arduino4)
Widgets.append(Arduino5)
Visueel = GUI(Widgets, root)
root.mainloop()
Image
You could try something like
def removeWidget(self):
widget = self.widgets.pop()
widget.widgetFrame.pack_forget()
print(self.widgets)

How to change labels and radiobuttons after a button clicked?

I have programmed a script which takes random four elements from a table and question to the user using tkinter, random and sqlite3. Currently, I can ask a question. Implement four choices with radiobuttons. I can also test if the answer is correct or not and show the result to the user via toplevel().
Problem is, how can I refresh the question after the continue button clicked?
My whole code is below. I have tried refreshing the random numbers and labels under continue_asking or another def called from continue_asking. But it doesn't work at all.
from tkinter import *
from sqlite3 import *
from random import *
class Question(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.prepare_question()
def prepare_question(self):
self.tumu = {0:['ask1','answer1'], # instead of SQL query
1:['ask2','answer2'],
2:['ask3','answer3'],
3:['ask4','answer4']}
self.create_widgets()
def create_widgets(self):
self.choiceFrame = Frame(self)
self.choiceFrame.grid(row=2, column=0)
self.choiceNum = IntVar()
for i in range(4):
Radiobutton(self.choiceFrame, text=self.tumu[i][1], variable=self.choiceNum, value=i) \
.grid(row=2, column=i, padx=5, pady=5)
self.q_num = randrange(4)
self.q_word = self.tumu[self.q_num][0]
lbl_question = Label(self, text="Which one is the meaning of the word: " + self.q_word, font="Courier 12")
lbl_question.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky=W)
txt_question = Text(self, height=1, font="Courier 12", pady=2)
txt_question.tag_configure("myStyle", font="Courier 12 bold")
txt_question.insert("end", "Please choose the answer and ")
txt_question.insert("end", "click okay to see the results.", "myStyle")
txt_question.configure(state="disabled")
txt_question.grid(row=1, column=0, columnspan=4, padx=5, sticky=W)
btn_okay = Button(self, text="Okay", font="12", command=self.a_control)
btn_okay.grid(row=3, column=0, columnspan=2)
def a_control(self):
self.choosenWord = self.q_num
self.frm_answer = Toplevel()
self.frm_answer.title("Result")
self.selectedWord = self.choiceNum.get()
txt_result = Text(self.frm_answer, height=4, width = 40)
if self.choosenWord == self.selectedWord:
txt_result.insert("end", "Congrats! Your answer is correct.\n")
else:
txt_result.insert("end","Your answer is not correct.\n")
txt_result.insert("end", "Correct answer is " + self.tumu[self.q_num][1] + '\n')
txt_result.insert("end", "Please click Continue to continue.\n")
txt_result.insert("end", "Click cancel to quit.")
txt_result.grid(row=0, column=0, columnspan=2, padx = 5, pady=5)
txt_result.configure(state="disabled")
btn_continue = Button(self.frm_answer, text="Continue", command=lambda: self.continue_asking(self.frm_answer))
btn_continue.grid(row=1, column=0, padx=5, pady=5, sticky = W)
btn_quit = Button(self.frm_answer, text="Cancel", command=self.end_asking)
btn_quit.grid(row=1, column=1, padx=5, pady=5, sticky = W)
def continue_asking(self,frm_answer):
frm_answer.destroy()
def end_asking(self):
root.destroy()
root = Tk()
app = Question(root)
root.mainloop()
I have tried adding prepare_question to continue_asking. It keeps asking questions but widgets are not changing. They are just overlapping.
EDIT
So let's restart from scratch, i was totally wrong because no widget was removed and they stacked in the main Frame children list.
You still don't need to write so much code, mostly move some parts.
First, to be able to update the widgets and prepare the new question peacefully, move
self.create_widgets() in the constructor and put the random index self.q_num and self.q_word inside prepare_question, since it belongs to the logic of the question creation.
In create_widgets() you only need to keep some control on the label question, so we add self.lbl_question...
Finally, i suggest to create a new function update_widgets(), but you can put the logic inside continue_asking().
In this function, call prepare_question to update the next question (sql query and random stuff). Since we move the random index, everything is ready to update each widget:
text of the question label
text of radiobuttons. I'm not so proud of the loop to change those, but that'll do the trick. (we keep the values created for the indexes to match the new ones, i'm not very sure about this logic with SQL queries, i follow your first implementation with text=self.tumu[i][1])
If someone can tell how to get the radiobutton value more easily, i'm interested
Here is the whole code:
from tkinter import *
from sqlite3 import *
from random import *
class Question(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.prepare_question()
self.create_widgets()
def prepare_question(self):
self.tumu = {0:['ask1','answer1'], # instead of SQL query
1:['ask2','answer2'],
2:['ask3','answer3'],
3:['ask4','answer4']}
self.q_num = randrange(4)
self.q_word = self.tumu[self.q_num][0]
def create_widgets(self):
self.choiceFrame = Frame(self)
self.choiceFrame.grid(row=2, column=0)
self.choiceNum = IntVar()
for i in range(4):
Radiobutton(self.choiceFrame, text=self.tumu[i][1], variable=self.choiceNum, value=i) \
.grid(row=2, column=i, padx=5, pady=5)
self.lbl_question = Label(self, text="Which one is the meaning of the word: " + self.q_word, font="Courier 12")
self.lbl_question.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky=W)
txt_question = Text(self, height=1, font="Courier 12", pady=2)
txt_question.tag_configure("myStyle", font="Courier 12 bold")
txt_question.insert("end", "Please choose the answer and ")
txt_question.insert("end", "click okay to see the results.", "myStyle")
txt_question.configure(state="disabled")
txt_question.grid(row=1, column=0, columnspan=4, padx=5, sticky=W)
btn_okay = Button(self, text="Okay", font="12", command=self.a_control)
btn_okay.grid(row=3, column=0, columnspan=2)
def a_control(self):
self.choosenWord = self.q_num
self.frm_answer = Toplevel()
self.frm_answer.title("Result")
self.selectedWord = self.choiceNum.get()
txt_result = Text(self.frm_answer, height=4, width = 40)
if self.choosenWord == self.selectedWord:
txt_result.insert("end", "Congrats! Your answer is correct.\n")
else:
txt_result.insert("end","Your answer is not correct.\n")
txt_result.insert("end", "Correct answer is " + self.tumu[self.q_num][1] + '\n')
txt_result.insert("end", "Please click Continue to continue.\n")
txt_result.insert("end", "Click cancel to quit.")
txt_result.grid(row=0, column=0, columnspan=2, padx = 5, pady=5)
txt_result.configure(state="disabled")
btn_continue = Button(self.frm_answer, text="Continue", command=self.continue_asking)
btn_continue.grid(row=1, column=0, padx=5, pady=5, sticky = W)
btn_quit = Button(self.frm_answer, text="Cancel", command=self.end_asking)
btn_quit.grid(row=1, column=1, padx=5, pady=5, sticky = W)
def continue_asking(self):
self.frm_answer.destroy()
self.update_widgets()
def update_widgets(self):
self.prepare_question()
# change question text
self.lbl_question.configure(text = "Which one is the meaning of the word: " + self.q_word)
# change Radiobutton
for child in self.choiceFrame.children.values():
index = child.config()['value'][4]
child.configure(text = self.tumu[index][1])
if index == 0: # reset the focus
child.select()
def end_asking(self):
root.destroy()
root = Tk()
app = Question(root)
root.mainloop()
First crap post: (the not to do part)
You don't need to change so much code to fix the present issue, have you already tried the following ?
def continue_asking(self,frm_answer):
frm_answer.destroy()
self.prepare_question()
I won't review the whole code, there is another place for that, but you can also avoid the lambda when you call continue_asking(), since you store the frame in self.frm_answer
btn_continue = Button(self.frm_answer, text="Continue", command=self.continue_asking)
# [...]
def continue_asking(self):
self.frm_answer.destroy()
self.prepare_question()

Categories

Resources