how to change value of label after button is clicked on Tkinter - python

I am creating a simple bank account GUI , when i click the menu " interest" the variable changed from 1 to 2 , which should change the value of the current balance by 10 percent, however the value stays the same, give your insight.
from tkinter import *
from random import randint
class BankAccount(object):
def __init__(self, initial_balance=0):
self.balance = initial_balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def get_balance(self, initial_balance, rate):
return self.get_balance() * self._rate
class BankAccountWithInterest(BankAccount):
def __init__(self, initial_balance=0, rate=0.1):
BankAccount.__init__(self, initial_balance)
self._rate = rate
def interest(self):
return self.balance * self._rate
balance = (randint(100, 500))
my_account = BankAccount(balance)
my_interest = BankAccountWithInterest(balance)
interest = my_interest.balance + my_interest.interest()
typeOfAccount = "1"
class GUI:
def __init__(self, master):
frame = Frame(master)
frame.pack()
#Toolbar#
toolbar = Frame(root)
toolbar.pack(side=TOP, fill=X)
#Button#
button1 = Button(toolbar, text="Deposit", width = 13, command=self.depositBalance)
button2 = Button(toolbar, text="Withdraw",width = 13, command=self.depositWithdraw)
button1.pack(side=LEFT)
button2.pack(side=RIGHT)
#Menu#
subMenu = Menu(menu)
menu.add_cascade(label="Type of Account", menu=subMenu)
subMenu.add_command(label="Standard", command= self.standard)
subMenu.add_command(label="Interest", command= self.interest)
#Textbox#
self.text = Entry(root)
self.text.pack()
#Labels#
w = Label(root, text="Current Balance:")
w.pack()
w1 = tkinter.StringVar()
if typeOfAccount == "1":
w1 = Label(root, text=my_account.balance)
w1.pack()
elif typeOfAccount == "2":
w1.set(text=interest)
w1.pack()
def depositBalance(self):
a = int(self.text.get())
my_account.balance = a + my_account.balance
print(my_account.balance)
def depositWithdraw(self):
a = int(self.text.get())
my_account.balance = my_account.balance - a
print(my_account.balance)
def standard(self):
typeOfAccount = "1"
def interest(self):
typeOfAccount = "2"
root = Tk()
menu = Menu(root)
root.config(menu=menu)
root.title("Bank Account")
root.minsize(width=250, height=100)
root.maxsize(width=300, height=150)
GUI(root)
root.mainloop()

There are several problems with your code. The multiple class files do not appear to work as you think they should and some things are just wrong. I have reworked your program into what I think it is you are trying to do and I have moved everything into 1 big class as it made more sense in this situation. You were making things more complicated than it needed to be by creating variables outside of the classes and by making several classes. There is nothing really wrong with using several classes but the way you were going about it was making things way more difficult than it needed to be.
First thing I did was to create all the class variables/attributes that we are going to use.
To make things easier on us going forward I made every widget and variable a class attribute by placing self. as a prefix to all the variable/widget names. This will allow us to interact with and change each attribute in any of the class methods without an issue and if you add more options down the road the attributes will already be defined and ready to change.
Next I moved all your separate class methods into the main class to make things easier to work with.
I replaced your if/else statement on the account types into a methods. This will allow us to update label to show the balance or the interest any time the account type changes or an amount is added or removed from the balance.
I modified the depositBalance and depositWithdraw methodsto have a little error handling because your entry field is not restricted to only numbers and can cause errors if the user puts anything else in or leaves it blank.
I change the standard and interest methods to update the typeOfAccount attribute and to update the label by calling on the type_account method.
Last but not least some general clean up so the code did not have pointless spacing and to make sure we follow a DRY (don't repeat yourself) standard.
Below is the reworked code with all the changes I mentioned above. Let me know if this helps and if you are confused on anything.
from tkinter import *
from random import randint
class GUI:
def __init__(self, master):
self.master = master
self.typeOfAccount = "1"
self.balance = (randint(100, 500))
self.rate = 0.1
self.frame = Frame(master)
self.frame.pack()
self.toolbar = Frame(root)
self.toolbar.pack(side=TOP, fill=X)
self.button1 = Button(self.toolbar, text="Deposit", width = 13, command=self.depositBalance)
self.button2 = Button(self.toolbar, text="Withdraw",width = 13, command=self.depositWithdraw)
self.button1.pack(side=LEFT)
self.button2.pack(side=RIGHT)
self.menu = Menu(self.master)
self.master.config(menu = self.menu)
self.subMenu = Menu(self.menu)
self.menu.add_cascade(label="Type of Account", menu=self.subMenu)
self.subMenu.add_command(label="Standard", command=self.standard)
self.subMenu.add_command(label="Interest", command=self.interest)
self.text = Entry(self.master)
self.text.pack()
self.w = Label(root, text="Current Balance: {}".format(self.balance))
self.w.pack()
#removed "tkinter." not needed because of the type of import you used
self.w1 = StringVar()
def type_account(self):
if self.typeOfAccount == "1":
self.w.config(text="Current Balance: {}".format(self.balance))
elif self.typeOfAccount == "2":
interest = self.balance * self.rate
self.w.config(text="Current Balance: {}".format(interest))
def depositBalance(self):
try:
if int(self.text.get()) > 0:
a = int(self.text.get())
self.balance = a + self.balance
self.type_account()
except:
print("Blank or non numbers in entry field")
def depositWithdraw(self):
try:
if int(self.text.get()) > 0:
a = int(self.text.get())
self.balance = self.balance - a
self.type_account()
except:
print("Blank or non numbers in entry field")
def standard(self):
self.typeOfAccount = "1"
self.type_account()
def interest(self):
self.typeOfAccount = "2"
self.type_account()
if __name__ == "__main__":
root = Tk()
root.title("Bank Account")
root.minsize(width=250, height=100)
root.maxsize(width=300, height=150)
app = GUI(root)
root.mainloop()

You should set self.w1, (rather than just w1) for example, then you can update the text from any instance method in that class.

Related

how do i allow a function to be used anywhere in python tkinter?

i have 2 classes and when i run the first one, it works fine but when it gets to the second class i get an error saying AttributeError: 'Question2' object has no attribute 'correct'. how do i make it so that the functions work in both of the class? is there something wrong with my indent? please help me fix this code, thanks:
Edit: i have a problem with using self, if i remove self from the functions it wouldnt work, if i indent it to not be a part of the class, it still wont work, the self gets turns white
class Question1:
def __init__ (self, master):
x = random.randint(5, 12)
y = random.randint(5, 12)
self.master = master
self.user_choice = StringVar()
self.user_choice.set("")
self.frame = Frame(master, padx=200, pady=200)
self.frame.grid()
self.q = Label(self.frame, text="What is {} + {} ?".format(x, y))
self.q.grid(row=0)
self.ans = Entry(self.frame, width=50, textvariable=self.user_choice)
self.ans.grid(row=1)
self.answer = x+y
self.sub = Button(self.frame, text="submit", command=self.correct)
self.sub.grid(row=3)
def correct(self):
global p
if int(self.user_choice.get()) == self.answer:
cor = Label(self.frame,text="Correct!")
cor.grid(row=5, pady=20)
p += 1
if p >= 3:
Question2(self.master)
else:
self.sub.destroy()
nex = Button(self.frame, text="Next", command=self.necs)
nex.grid(row=4)
else:
inc = Label(self.frame,text="incorrect")
inc.grid(row=5, pady=20)
self.sub.destroy()
nex = Button(self.frame, text="Next", command=self.necs)
nex.grid(row=4)
self.frame.destroy()
Question1(self.master)
def necs(self):
self.frame.destroy()
Question1(self.master)
class Question2:
def __init__(self, master):
x = random.randint(2, 2)
y = random.randint(2, 3)
self.master = master
self.user_choice = StringVar()
self.user_choice.set("")
self.frame = Frame(master, padx=200, pady=200)
self.frame.grid()
self.q = Label(self.frame, text="What is {} x {} ?".format(x, y))
self.q.grid(row=0)
self.ans = Entry(self.frame, width=50, textvariable=self.user_choice)
self.ans.grid(row=1)
self.answer = x * y
self.sub = Button(self.frame, text="submit", command=self.correct)
self.sub.grid(row=3)
You can do that by inheriting the properties of Question1 to Question2:
That can be:
class Question2(Question1):
#you can access that by:
self.correct()
Other way is you can define a global function outside both the classes and you can easily access it.
Example:
#somewhere globally:
def correct():
#some code
class Question1():
correct()
class Question2():
correct()
I think you can develop more such ideas of using a function which will be required by multiple classes.
As #JenilDave answered, you need to define function outside class, inherit from other class.explicitly call class.
i.e. for last case:
class Question1:
def correct():
<codes>
class Question2:
q1 = Question1()
q1.correct()
or
Question1.correct(<Question1 instance>)
But since your 'correct' function are heavily dependent to Question1, you can't use either way, and reconstruct your codes.
Working example below:
Instead of generating question Class per questions, send lists of questions to one class.
Every time you succeed third time, you'll move on to next questions by poping list you've provided before.
When Lists are empty, pop() causes IndexError and program closes.
...
Since I can't get what variable 'p' stands for, I'm guessing it's number of successes(passes).
Full Code:
import random
from tkinter import Frame, Label, Entry, Button, StringVar, Tk
class QuestionFrame(Frame):
def __init__(self, master, question_answer):
super().__init__(master)
self.x, self.y = 0, 0
self.master = master
self.entries = question_answer
self.question, self.answer = self.entries.pop(0)
self.success = 0
self.user_choice = StringVar()
self.frame = Frame(master)
self.frame.grid()
self.quest_label = Label(self.frame)
self.ans = Entry(self.frame, width=50, textvariable=self.user_choice)
self.sub = Button(self.frame)
self.quest_label.grid(row=0)
self.ans.grid(row=1)
self.sub.grid(row=3)
self.reload_question()
def reload_question(self):
self.x, self.y = random.sample(range(5, 12), 2)
next_quest = f"What is {self.question.format(self.x, self.y)} ?"
self.quest_label.configure(text=next_quest)
self.ans.delete(0, 'end')
self.sub.configure(text="submit", command=self.correct)
def next(self):
print(self.success)
if self.success == 3:
# loads next entry
try:
self.question, self.answer = self.entries.pop(0)
except IndexError:
self.master.destroy()
else:
self.success = 0
self.reload_question()
else:
self.reload_question()
def correct(self):
self.sub.configure(text="Next", command=self.next)
if int(self.user_choice.get()) == self.answer(self.x, self.y):
self.quest_label['text'] = "Correct!"
self.success += 1
else:
self.quest_label['text'] = "Incorrect!"
if __name__ == '__main__':
# Passing questions with list of (question, answer) tuples.
tests = [("{} x {}", lambda x, y: x*y),
("{} - {}", lambda x, y: x-y)]
root = Tk()
root.title(f'Some fancy title')
window = QuestionFrame(root, tests)
window.mainloop()

I cant update labels in tkinter

I can't seem to update my money counter(var money) when its labeled, I have a button that's supposed to add 0.1 to moneyNum but the money counter doesn't change. I'm new to stack overflow and would appreciate any help.(btw sry if its REALLY messy)
from tkinter import *
import random
from time import sleep
root = Tk()
root.geometry('320x320')
#spacing
spacingTitle = Label(root, text=" \n\n\n\n\n\n\n\n\n")
#title
title = Label(root, text=" \bGamblers Dream\b", font="Helvetica", fg="red")
titleWelcom = Label(root, text=" Welcom to...")
titleWelcom.grid()
title.grid()
#money counter
moneyNum = float(10.0)
money = Label(root, text="money:" + str(moneyNum), font="Helvetica")
money.grid(row=3, column=0)
#moneyClicker
def moneyButtonAdder():
global moneyNum
moneyNum = moneyNum + 0.1
moneyClicker = Button(root, text="click", fg="green", command=moneyButtonAdder)
moneyClicker.grid(row=14)
root.mainloop()
The problem is that once you create a label, you pass the string to it. Label displays the string, and:
changing a string object does not change the label text
changing the integer does not change the string - it lost the whole connection when the new string object was created
So everything is not as procedural as you would have hoped it is.
The solution - use StringVar objects and detect value changes - see this.
So, the solution is:
from tkinter import *
class Observed(object):
"""adapted copy from https://stackoverflow.com/a/6192298/10713244"""
def __init__(self):
self._observed = 10.0
self._observers = []
#property
def observed(self):
return self._observed
#observed.setter
def observed(self, value):
self._observed = value
for callback in self._observers:
print('announcing change')
callback(self._observed)
def bind_to(self, callback):
print('bound')
self._observers.append(callback)
class Observer(object):
def __init__(self, data):
self.text = ''
self.data = data
self.data.bind_to(self.update)
self.tkinter_init()
def update(self, observed):
self.text = 'float: '+str(data._observed)
self.tkinter_update()
def tkinter_init(self):
self.tk = Tk()
self.tvar = StringVar()
self.label = Label(textvariable=self.tvar)
self.label.pack()
def tkinter_update(self):
self.tvar.set(self.text)
if __name__ == '__main__':
data = Observed()
label = Observer(data)
print(label.text)
data.observed = 10.0
print(label.text)
def dec(): data.observed -= 0.1
Button(label.tk, text='decrease', command=dec).pack()
label.tk.mainloop()
Hope that's helpful!

Conditional statement error in Tkinter Bank Account

Heres a simple bank account i coded in Python using Tkinter, the issue im having is with the conditional statements in the withdraw and deposit functions, the code always goes with the else statement although in the standard and interest function it should change the value for the TypeOfAccount. Give your insight.
import tkinter
from tkinter import *
from random import randint
class BankAccount(object):
def __init__(self, initial_balance=0):
self.balance = initial_balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def get_balance(self, initial_balance, rate):
return self.get_balance() * self._rate
class BankAccountWithInterest(BankAccount):
def __init__(self, initial_balance=0, rate=0.1):
BankAccount.__init__(self, initial_balance)
self._rate = rate
def interest(self):
return self.balance * self._rate
balance = (randint(100, 500))
my_account = BankAccount(balance)
my_interest = BankAccountWithInterest(balance)
interest = my_interest.balance + my_interest.interest()
print(interest)
class GUI:
def __init__(self, master):
frame = Frame(master)
frame.pack()
#Toolbar#
toolbar = Frame(root)
toolbar.pack(side=TOP, fill=X)
#Button#
button1 = Button(toolbar, text="Deposit", width = 13, command=self.depositBalance)
button2 = Button(toolbar, text="Withdraw",width = 13, command=self.depositWithdraw)
button1.pack(side=LEFT)
button2.pack(side=RIGHT)
#Menu#
subMenu = Menu(menu)
menu.add_cascade(label="Type of Account", menu=subMenu)
subMenu.add_command(label="Standard", command= self.standard)
subMenu.add_command(label="Interest", command= self.interest)
#Textbox#
self.text = Entry(root)
self.text.pack()
def standard(self):
typeOfAcc = "standard"
w1.config(text=my_account.balance)
w1.pack()
def interest(self):
typeOfAccount = "interest"
w1.config(text=interest)
w1.pack()
def depositBalance(self):
if typeOfAccount == "interest":
a = int(self.text.get())
interest = interest + a
w1.config(text=interest)
elif typeOfAccount == "standard":
a = int(self.text.get())
my_account.balance = my_account.balance + a
print(my_account.balance)
w1.config(text=my_account.balance)
else:
w1.config(text="Error: Select account type")
def depositWithdraw(self):
if typeOfAccount == 1:
a = int(self.text.get())
interest = interest - a
w1.config(text=interest)
elif typeOfAccount == 0:
a = int(self.text.get())
my_account.balance = my_account.balance - a
print(my_account.balance)
w1.config(text=my_account.balance)
else:
w1.config(text="Error: Select account type")
typeOfAccount = 0
root = Tk()
menu = Menu(root)
root.config(menu=menu)
root.title("Bank Account")
root.minsize(width=250, height=100)
root.maxsize(width=300, height=150)
#Labels#
w = Label(root, text="Current Balance:")
w.pack()
w1 = Label(root, text="0")
w1.pack()
GUI(root)
root.mainloop()
Part of the problem is that in your deposit function, you are checking if typeOfAccount is 'standard' or 'interest', while in your withdraw function, you are checking if typeOfAccount is 1 or 0. This inconsistency will cause errors and unexpected behavior.
I also strongly suggest you put these 2 blocks of code:
balance = (randint(100, 500))
my_account = BankAccount(balance)
my_interest = BankAccountWithInterest(balance)
interest = my_interest.balance + my_interest.interest()
print(interest)
typeOfAccount = 0
root = Tk()
menu = Menu(root)
root.config(menu=menu)
root.title("Bank Account")
root.minsize(width=250, height=100)
root.maxsize(width=300, height=150)
#Labels#
w = Label(root, text="Current Balance:")
w.pack()
w1 = Label(root, text="0")
w1.pack()
in your main GUI class.
class Account:
def __init__(self, init_balance=0):
self.balance = init_balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def get_balance(self, init_balance, rate):
return self.get_balance() * self._rate
class InterestAccount(Account):
def __init__(self, init_balance=0, rate=0.1):
super().__init__(init_balance)
self._rate = rate
def interest(self):
return self.balance * self._rate
class GUI(Tk):
def __init__(self):
Tk.__init__(self)
self.title('Bank Account')
#Menu#
menu = Menu(self)
acct_type_menu = Menu(menu)
menu.add_cascade(label='Account Type', menu=acct_type_menu)
acct_type_menu.add_command(label='Standard', command=self.set_type_standard)
acct_type_menu.add_command(label='Interest', command=self.set_type_interest)
self.config(menu=menu)
#Account#
start_balance = randint(100, 500)
self.acct = Account(start_balance)
self.my_interest = InterestAccount(start_balance)
self.interest = self.my_interest.balance + self.my_interest.interest()
#Labels#
Label(self, text='Current Balance:').pack()
self.balance_label = Label(self, text='Error: Select account type')
self.balance_label.pack()
#Button#
btns_frame = Frame(self)
btns_frame.pack(side=TOP, fill=X)
Button(btns_frame, text='Deposit', width=13, command=self.deposit).pack(side=LEFT)
Button(btns_frame, text='Withdraw', width=13, command=self.withdraw).pack(side=RIGHT)
#Textbox#
self.text = Entry(self)
self.text.pack()
def set_type_standard(self):
self.acct_type = 'standard'
self.balance_label.config(text=self.acct.balance)
def set_type_interest(self):
self.acct_type = 'interest'
self.balance_label.config(text=self.interest)
def clear_entry(self):
self.text.delete(0, END)
def deposit(self):
if self.acct_type == 'interest':
a = int(self.text.get())
interest = interest + a
self.balance_label.config(text=self.interest)
elif self.acct_type == 'standard':
a = int(self.text.get())
self.acct.balance += a
self.balance_label.config(text=self.acct.balance)
else:
self.balance_label.config(text='Error: Select account type')
self.clear_entry()
def withdraw(self):
if self.acct_type == 'interest':
a = int(self.text.get())
self.interest -= a
self.balance_label.config(text=self.interest)
elif self.acct_type == 'standard':
a = int(self.text.get())
self.acct.balance -= a
self.balance_label.config(text=self.acct.balance)
else:
self.balance_label.config(text='Error: Select account type')
self.clear_entry()
if __name__ == '__main__':
GUI().mainloop()
Major Changes:
made GUI inherit from Tk
moved more code into GUI class
altered main deposit and withdraw methods to use deposit and withdraw from the Account class
set the initial balance to be Error: Select account type
Minor Changes:
removed code that did nothing
clear entry when Deposit or Withdraw button is clicked
shortened the 2 account class names
made method and variable names more appropriate
EDIT: Because the Entry widget you are using is for deposit and withdrawal amounts, you may want to restrict input of the Entry to be numbers and period only:
class GUI:
def __init__(self):
...
vcmd = (self.register(self.onValidate), '%S')
self.text = Entry(self, validate='key', vcmd=vcmd)
self.text.pack()
...
def onValidate(self, S):
if S in '0123456789.':
return True
return False
...

How to call a function on Tkinter class?(not inside label)

I'm trying to use a function which a placed inside the class of Tkinder but the function(aktodec) can't be found and a get an error. I don't wanna call the def as a command of a button but as a function that will give value to one of my variables
from Tkinter import *
class ADialog:
def __init__(self, parent):
top = self.top = Toplevel(parent)
Label(top, text="Number to convert").pack()
self.numb = Entry(top)
self.numb.pack(padx=15)
Label(top, text="Base of incered number").pack()
self.base = Entry(top)
self.base.pack(padx=15)
Label(top, text="Base you want to be converted").pack()
self.basemet=Entry(top)
self.basemet.pack(padx=15)
b = Button(top, text="OK", command=self.met)
b.pack(pady=5)
def aktodec(self,num,base): #####commands
dec=0
num=num[::-1]
for i in range(len(num)):
s=int(num1[i])*(int(base)**i)
dec=dec+s
def met(self):
num=self.numb=str(self.numb.get())
base=self.base =int(self.base.get())
basemet=self.basemet=int(self.basemet.get())
if base==basemet:
Label(root,text="The number "+self.numb+"is converted to"+self.numb) ##why can't be print??
if base==10:
new=num
else:
new1=self.aktodec(num,base) ####why aktodec doesn't give value to "new"??
Label(root,text="Number is"+str(new))
self.top.destroy()
root = Tk()
def open_dialog():
dial = ADialog(root)
root.wait_window(dial.top)
root.wm_geometry("400x300+20+40")
message=StringVar()
message.set("Complete the form")
Label(root, textvariable=message).pack(padx=30)
root.update()
message.set("Form completed")
Button(root, text="Done", command=root.destroy).pack()
Button(root, text="new", command=open_dialog).pack()
root.update()
root.mainloop()
And also I have a problem whith the label
Label(root,text="The number "+self.numb+"is converted to"+self.numb
which (i don't know why) won't appear to the root even the base=basemet. Help please(it's for a project in this week)!
In Python, a function can't modify some arguments(integers, strings) as perceived by the caller, they are immutable. However, some objects like lists are mutable.
def aktodec(self,num,base):
dec=0
num=num[::-1]
for i in range(len(num)):
s=int(num1[i])*(int(base)**i)
dec=dec+s
return dec
def met(self):
num=self.numb=str(self.numb.get())
base=self.base =int(self.base.get())
basemet=self.basemet=int(self.basemet.get())
new = num
if base==basemet:
Label(root,text="The number "+self.numb+"is converted to"+self.numb).pack()
if base==10:
new=num
else:
new=self.aktodec(num,base)
Label(root,text="Number is"+str(new)).pack()
self.top.destroy()

Label does not update on button click

We have to make a test for elementary school kids to test there skills in math. I need the label on the left to have a random equation, and the right have an entry form, and the far right to have a button that checks if the answer they gave is correct. If they got it right a new equation is given.
from tkinter import Label, Frame, Entry, Button, LEFT, RIGHT, END, TOP, END
from tkinter.messagebox import showinfo
from random import randrange
class Ed(Frame):
'Simple arithmetic education app'
def __init__(self,parent=None):
'constructor'
Frame.__init__(self, parent)
self.pack()
Ed.new_problem(self)
Ed.make_widgets(self)
self.tries = 0
def make_widgets(self):
'defines Ed widgets'
if self.plusminus == 1:
Label(self, text=(self.a ,"+" ,self.b)).pack(side=LEFT)
else:
Label(self, text=(self.a, "-" ,self.b)).pack(side=LEFT)
self.ent = Entry(self)
self.ent.pack(side=LEFT)
Button(self, text='Enter', command=self.evaluate).pack(side=RIGHT)
def new_problem(self):
'creates new arithmetic problem'
self.a = randrange(1,10,1)
self.b = randrange(1,10,1)
self.c = randrange(1,10,1)
if self.c < 5:
self.total = self.a + self.b
self.plusminus = 1 #plusminus = plus
else:
self.total = self.a - self.b
self.plusminus = 0 #plusminus = minus
def evaluate(self):
'handles button "Enter" clicks by comparing answer in entry to correct result'
if self.total == eval(self.ent.get()):
showinfo(title='YAY', message='You are CORRECT!')
self.ent.delete(0,END)
else:
self.ent.delete(0,END)
self.ent.insert(END, 'Wrong. Try again.')
self.tries+=1
Ed().mainloop()
The problem is the equation does not update when the answer is correct. So when I enter 12 when the question is 8 + 4. It cays correct but keeps 8 + 4 tho I want it to change to a new equation.
You're only calling new_problem when your Ed object is instantiated. It's never called again afterwards, and doing so won't actually change the existing set up, as you only create the labels for your widget once, also at instantiation.
What you need to do is pass in a tkinter.StringVar to your Label widget, and then set the arithmetic problem on that variable. You then run new_problem after each successful answer.
I've updated your code slightly to use super and run methods directly on the instance rather than via the class, but this should work:
from tkinter import Label, Frame, Entry, Button, LEFT, RIGHT, END, TOP, END, StringVar
from tkinter.messagebox import showinfo
from random import randrange
class Ed(Frame):
"""Simple arithmetic education app"""
def __init__(self,parent=None):
"""constructor"""
super().__init__(parent)
self.tries = 0
self.problem = StringVar()
self.make_widgets()
self.new_problem()
self.pack()
def make_widgets(self):
"""defines Ed widgets"""
self.label = Label(self, textvariable=self.problem)
self.label.pack(side=LEFT)
self.ent = Entry(self)
self.ent.pack(side=LEFT)
Button(self, text='Enter', command=self.evaluate).pack(side=RIGHT)
def new_problem(self):
"""creates new arithmetic problem"""
self.tries = 0
self.a = randrange(1,10,1)
self.b = randrange(1,10,1)
if randrange(1,10,1) < 5:
self.total = self.a + self.b
self.plusminus = '+'
else:
self.total = self.a - self.b
self.plusminus = '-'
self.problem.set( (self.a , self.plusminus, self.b) )
def evaluate(self):
"""handles button "Enter" clicks by comparing answer in entry to correct result"""
if self.total == int(self.ent.get()):
showinfo(title='YAY', message='You are CORRECT!')
self.ent.delete(0,END)
self.new_problem()
else:
self.ent.delete(0,END)
self.ent.insert(END, 'Wrong. Try again.')
self.tries += 1
Ed().mainloop()
I've also changed your eval to an int...you really don't want to be doing that, because it enables users to inject python code into your application.
For example, I can type the following as my "answer":
showinfo(title="Blah", message="Look ma - No hands!")
That would just bring up a simple message box, but the door is wide open for more involved mischief.

Categories

Resources