I am trying to make a simple calculator with GUI. So what I have so far are the buttons and the Entry box setup but I have no idea how to display text on them. So Currently, the "equation" is equal to 0. What I want to do is when the user presses the equal button, it would display the solved equation to the Entry called inputbox. What will I have to do to display the equation back to the inputbox?
import sys
from Tkinter import *
#modules
equation = '0'
def Entrybox():
theinput = inputbox.get()
if type(theinput) is float:
pass
elif type(theinput) is int:
equation += theinput
else:
return 'Numbers Only'
def bp1():
equation = equation + '1'
def bp2():
equation = equation + '2'
def bp3():
equation = equation + '3'
def bp4():
equation = equation + '4'
def bp5():
equation = equation + '5'
def bp6():
equation = equation + '6'
def bp7():
equation = equation + '7'
def bp8():
equation = equation + '8'
def bp9():
equation = equation + '9'
def bp0():
equation = equation + '0'
def bpplus():
equation = equation + '+'
def bpmin():
equation = equation + '-'
def bpmulti():
equation = equation + '*'
def bpdiv():
equation = equation + '/'
def bpequal():
eval(equation)
return
def bpclear():
equation = equation - equation
gui = Tk()
inputvar = StringVar()
#gui Size
gui.geometry('360x400')
#title
gui.title("A Lucas Calculator")
#The input box
inputbox = Entry(gui, textvariable = inputvar, bd = 10,width = 34)
inputbox.place(x = 40, y = 50)
#buttons
number3 = Button(gui, text = '3',font = 15,width = 4,command = bp3)
number3.place(x = 200,y = 260)
number2 = Button(gui, text = '2',font = 15,width = 4,command = bp2)
number2.place(x = 120,y = 260)
number1 = Button(gui, text = '1',font = 15,width = 4, command = bp1)
number1.place(x = 40,y = 260)
number6 = Button(gui, text = '6',font = 15,width = 4,command = bp6)
number6.place(x = 200,y = 200)
number5 = Button(gui, text = '5',font = 15,width = 4,command = bp5)
number5.place(x = 120 ,y = 200)
number4 = Button(gui, text = '4',font = 15,width = 4,command = bp4)
number4.place(x = 40, y = 200)
number9 = Button(gui, text = '9',font = 15,width = 4, command = bp9)
number9.place(x = 200,y = 140)
number8 = Button(gui, text = '8',font = 15,width = 4,command = bp8)
number8.place(x = 120,y = 140)
number7 = Button(gui, text = '7',font = 15,width = 4,command = bp7)
number7.place(x = 40,y = 140)
number0 = Button(gui, text = "0",font = 15,width = 4,command = bp0)
number0.place(x = 120,y = 318)
signplus = Button(gui, text = "+", font = 14, width = 3,bg = 'red', fg = 'white',command = bpplus)
signplus.place(x = 280,y = 140)
signmin = Button(gui, text = "-", font = 14, width = 3,command = bpmin)
signmin.place(x = 280,y = 200)
signmulti = Button(gui, text = "X", font = 14, width = 3,command = bpmulti)
signmulti.place(x = 280,y = 260)
signdiv = Button(gui, text = "/", font = 14, width = 3,command = bpdiv)
signdiv.place(x = 280,y = 318)
signequal = Button(gui, text = "=", font = 15, width = 4,bg = 'blue',fg = 'white',command = bpequal)
signequal.place(x = 200, y = 318)
clearbutton = Button(gui, text = "Clear", font = 14, width = 4,bg = "green", fg = "white",command = bpclear)
clearbutton.place(x= 40, y = 318)
gui.mainloop()
Since you've already attached a textvariable to the Entry, the easiest thing to do is to use it:
inputvar.set('numbers only')
The tutorial on The Variable Classes in the Tkinter book explains in more detail… but there's really not much more to explain.
(I'm assuming inputvar was properly created as a Tkinter.StringVar() somewhere in the code you haven't shown us, and that you're keeping it around somewhere, like an instance variable, for later use. If not, obviously you'd need to fix those things.)
If you weren't using a textvariable, see the Entry docs for details on other ways to do it.
Unfortunately, your code function as written is too broken to actually get this far.
First, the Entrybox function that you defined never gets called, so nothing you do in that function can possibly have any effect. I'm not sure where you wanted it to get called, so I can't fix that for you.
And there's nowhere you can actually put such a call, because all of your event handler functions will raise an exception as soon as you click them, because they all look like this:
equation = equation + '1'
Whenever you assign to a variable in a function, that means it's a local variable, unless you explicitly say otherwise. But you don't have a local variable named equation until after this assignment finishes—and yet you're trying to use equation + '1' as the value. So, you will get UnboundLocalError: local variable 'equation' referenced before assignment.
You really shouldn't be using global variables in the first place (a GUI frame is one of the paradigm use cases for a class, which is why all of the non-trivial Tkinter examples are written that way, and you should follow those examples). If you really want to use globals, you can, but then you need to be explicit, like this:
def bp1():
global equation
equation = equation + '1'
Meanwhile, once you figure out where and how to call Entrybox, there are a number of problems with it:
def Entrybox():
theinput = inputbox.get()
if type(theinput) is float:
pass
elif type(theinput) is int:
equation += theinput
else:
return 'Numbers Only'
If you've got an inputvar, you really should be using inputvar.get(), not going straight to the Entry. As written, it won't cause any problems, but it makes your code harder to follow.
More seriously, Entry.get always returns a string,* so that theinput can never be an int or a float. So, your comparisons make no sense. And even if they did make sense, that's not the way to check types in Python—use isinstance(theinput, int), not type(theinput). And if you really need to compare types (which is very rare), you should use ==, not is. In general, if you don't know which one you want, you want ==; only use is for specific idiomatic cases (like is None), or when you want to check that two expressions name the exact same object, not just that they have the same value.
Anyway, if you want to check whether the string in inputvar is a string that could be converted to an int, the way to do that is:
try:
value = int(theinput)
equation += theinput
except:
return 'Numbers Only'
And all of the same things apply for float, of course.
Once you fix all of those problems, and any others I didn't spot from a cursory glance, then you can change that return 'Numbers Only' to inputvar.set('Numbers Only'), and you're done.
However, there's a much, much better design for this whole thing. Just make each of the buttons insert a character onto the end of the Entry, and use the Entry's contents (which you can get from inputvar) as the equation, and use either Entry validation or StringVar tracing to handle illegal key entries. There's an example in that blog post, and another example in the official Entry docs linked above.
* Actually, it may be either kind of string—str or unicode—but let's ignore that.
Related
The following code works for requesting input from a user through the Tkinter GUI and turning that input into a usable variable in the main script. However, any value that I put as the last in a list in the if statement (here "4") will hang and crash the program upon enter. This was also the case for "n" in a yes/no scenario. It also happens if I replace the if statement with a while not in [values] - the final value will crash the program. Is this just a quirk of Tkinter or is there something that I am missing?
import tkinter as tk
from tkinter import *
# get choice back from user
global result
badinput = True
while badinput == True:
boxwidth = 1
result = getinput(boxwidth).strip().lower()
if result in ['1', '2', '3', '4']:
badinput = False
# iterate through play options
if result == '1':
# Do Something
elif result =='2':
# Do Something
elif result =='3':
# Do Something
else:
# Do Something
def getinput(boxwidth):
# declaring string variable for storing user input
answer_var = tk.StringVar()
# defining a function that will
# get the answer and set it
def user_response(event):
answer_var.set(answer_entry.get())
return
answer_entry = tk.Entry(root, width = boxwidth, borderwidth = 5)
# making it so that enter calls function
answer_entry.bind('<Return>', user_response)
# placing the entry
answer_entry.pack()
answer_entry.focus()
answer_entry.wait_variable(answer_var)
answer_entry.destroy()
return answer_var.get()
In case anyone is following this question, I did end up solving my problem with a simple if statement within the callback. I can feed a dynamic "choicelist" of acceptable responses into the callback upon user return. If the answer is validated, the gate_var triggers the wait function and sends the program and user response back into the program.
'''
def getinput(boxwidth, choicelist):
# declaring string variable for storing user input
answer_var = tk.StringVar()
gate_var = tk.StringVar()
dumplist = []
# defining a function that will
# get the answer and set it
def user_response(event):
answer_var.set(answer_entry.get())
if choicelist == None:
clearscreen(dumplist)
gate_var.set(answer_entry.get())
return
if answer_var.get() in choicelist:
# passes a validated entry on to gate variable
clearscreen(dumplist)
gate_var.set(answer_entry.get())
else:
# return to entry function and waits if invalid entry
clearscreen(dumplist)
ErrorLabel = tk.Label(root, text = "That is not a valid response.")
ErrorLabel.pack()
ErrorLabel.config(font = ('verdana', 18), bg ='#BE9CCA')
dumplist.append(ErrorLabel)
return
global topentry
if topentry == True:
answer_entry = tk.Entry(top, width = boxwidth, borderwidth = 5)
else:
answer_entry = tk.Entry(root, width = boxwidth, borderwidth = 5)
# making it so that enter calls function
answer_entry.bind('<Return>', user_response)
# placing the entry
answer_entry.pack()
answer_entry.focus()
answer_entry.wait_variable(gate_var)
answer_entry.destroy()
return answer_var.get()
'''
Here is my code. I want my variable "a" to hold a default value 0 whenever the user has not given any particular value. I have given my code below:
from tkinter import *
from tkinter import messagebox
def sample():
a= show_values1()
v= int(a)+10
print (v)
def show_values1(event=None):
global a
a= i1.get()
print(a)
if int(a) > ul or int(a) < ll :
print('Limit Exceed')
messagebox.showerror("Error", "Please enter values from -100 to 100")
else :
return a
root= Tk()
ul= 100
ll=-100
i1 = inputBox = Entry()
i1.place(x=70, y=48, height=20)
bu1=Button(text='Enter', command = show_values1)
bu1.place(x = 70, y = 28, width=40, height=20)
bu2=Button(text='SEND', command = sample)
bu2.bind('<Return>', lambda x: show_values1(event=None))
bu2.place(x = 70, y = 98, width=40, height=20)
root.mainloop()
If no value has been entered, i1.get() will have a value of empty string "", so you could just test for that inside showvalues1:
if a == "":
return 0
As li.get() returns a string. Nothing entered will be a string of zero length. You can test for that and then set a to zero. For axample, after your a = i1.get() statement, insert:
if len(a) == 0:
a = "0"
Maybe you also need to handle cases where get() returns values that are not able to be cast to type int. :-)
I'm sure the answer to this is obvious but I cannot spot it! I have a very basic quiz(using Tkinter and Python 3) which uses 2 arrays , the question is displayed and then the answer entered is matched using the array index when the submit button is clicked.
The question at index 0 is displayed twice- cannot see why
The score does not increment correctly- even though it is a global variable- it just shows 1 each time.
How can I get the quiz to move to a print statement after the end of the list is reached?
I have tried putting an IF statement in the submit function to check the value of i but cannot get this to work. Can anyone point out my errors please?
from tkinter import *
global questions
questions =["What is the name of the Simpsons' next door neighbour?","What is the name of the school bus driver?",
"Who runs the Kwik-e-mart?","What does Bart do at the end of the opening credits?"]
global answers
answers = [ "Ned Flanders","Otto","Apu","Write On The Blackboard"]
global score
score = 0
global i
i = 0
def submit():
'''runs the submit button'''
global i
global score
question.config(text=questions[i])
if answer.get().lower()==answers[i].lower():
score+=1
else:
score=score
i+=1
scoretxt.config(text =str(score))
answer.delete(0,END)
window = Tk()
window.title("Simpsons Quiz")
window.wm_iconbitmap("homer.ico")
window.configure(background ="#ffd600")
banner = PhotoImage(file ="the-simpsons-banner.gif")
Label(window,image = banner).grid(row = 0,columnspan = 6)
Label(window,text = "Question : ",bg ="#ffd600",justify=LEFT).grid(row = 1,column = 0)
Label(window,text = "Type answer here: ",bg = "#ffd600",justify=LEFT).grid(row = 3, column = 0)
scoreLabel =Label(window,bg = "#ffd600")
scoretxt = Label(window,text ="Your score is: ?",bg = "#ffd600")
scoreLabel.grid(row=5,column = 2)
scoretxt.grid(row = 6,column = 2)
question=Label(window,bg = "white",text= questions[0],justify=LEFT)
question.grid(row =1,column=1)
answer = Entry(window,bg ="white",width = 30)
answer.grid(row = 3,column=1)
# make a submit button
Button(window,text= "Submit",bg = "white",command = submit).grid(row = 3,column = 2)
mainloop()
1) You are printing the question before you increment i. That's why you get twice.
2) It is always 1 becuase of your usage of global. In python you use global keyword in which scope you want to change your variable. Sadly, my english is not good enough to explain this. Please check out these answers.
3) You can use try-except block. I used it there because that is the exact line where you get the error. You can expand its range.
from tkinter import *
questions =["What is the name of the Simpsons' next door neighbour?","What is the name of the school bus driver?",
"Who runs the Kwik-e-mart?","What does Bart do at the end of the opening credits?"]
answers = [ "Ned Flanders","Otto","Apu","Write On The Blackboard"]
#removed globals from here
score = 0
i = 0
def submit():
'''runs the submit button'''
global i
global score
if answer.get().lower()==answers[i].lower():
score+=1
i+=1 #first increment, then show the question since you already show it at startup
try: #since you get the IndexError on this line, I used on here
question.config(text=questions[i])
except IndexError:
print ("something")
scoretxt.config(text = "Your score is: {}".format(str(score)))
answer.delete(0,END)
window = Tk()
window.title("Simpsons Quiz")
window.wm_iconbitmap("homer.ico")
window.configure(background ="#ffd600")
banner = PhotoImage(file ="the-simpsons-banner.gif")
Label(window,image = banner).grid(row = 0,columnspan = 6)
Label(window,text = "Question : ",bg ="#ffd600",justify=LEFT).grid(row = 1,column = 0)
Label(window,text = "Type answer here: ",bg = "#ffd600",justify=LEFT).grid(row = 3, column = 0)
scoreLabel =Label(window,bg = "#ffd600")
scoretxt = Label(window,text ="Your score is: ?",bg = "#ffd600")
scoreLabel.grid(row=5,column = 2)
scoretxt.grid(row = 6,column = 2)
question=Label(window,bg = "white",text= questions[0],justify=LEFT)
question.grid(row =1,column=1)
answer = Entry(window,bg ="white",width = 30)
answer.grid(row = 3,column=1)
# make a submit button
Button(window,text= "Submit",bg = "white",command = submit).grid(row = 3,column = 2)
mainloop()
Also you might want to use a dictionary instead of questions-answers lists.
I am writing a program in Python that is a scoring system for a dice game. The game can have any amount of players so I have an input that allows the user to say how many players they have.
I have been able to print Header Labels into the GUI with each players name. However my attempt to print a text box for each person so that their round score can be input is giving me trouble. I tried running a for loop up to the number of players and printing a text box for each person. The problem with that method, is that it is reusing my self.PlayerXroundScore so only the last text box created is usable.
Here is my code, I have tried to comment as best as possible to make it easier to read.
#Allows user to input total number of players
NumPlayers = input("How many players? ")
#Creates a list that is the number of players long
NameList = [0]*NumPlayers
#Allows for input of each Players name
#and stores those names in the list NameList
for i in range(0,NumPlayers):
x = raw_input("Player %d Name? " %(i+1))
NameList[i] = x
#creates the GUI
from Tkinter import *
from tkMessageBox import *
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.Title = ("10,000 scorekeeping")
self.Header = Label(self, text = "Welcome to 10,000 scoring Module, Have Fun!!", font = ("helvetica", "20", "bold")).grid(row = 0, column = 0, columnspan = (NumPlayers * 3))
for NameCount in range(1,(NumPlayers+1)):
#Allows me to create the names as column headers
self.PlayerName = Label(self, text = "%s" %NameList[NameCount - 1],font = ("helvetica","12","bold")).grid(row = 1, column = ((2 * NameCount)))
#This if just makes things more aesthetically pleasing, not relevant to my question
if NameCount < (NumPlayers):
self.PlayerName = Label(self, text = "|",font = ("helvetica","12","bold")).grid(row = 1, column = ((2 * NameCount + 1)))
#This is my problem
#It succesffully prints the correct number of text boxes
#however upon button click which calls the vals in each text box
#only the last created box is useful
#because that is the box corresponding to PlayerXroundScore
self.PlayerXroundScore = Entry(self, width = 4)
self.PlayerXroundScore.grid(row = 2, column = (2 * NameCount))
self.PlayerXroundScore.insert(0, "0000")
self.NextRound = Button(self, text = "Next round", command = self.CalcRoundTotals)
self.NextRound.grid(row = 1, column = 0)
#This is not completed yet, because I wanted to make sure this is the best way to do it before putting in the time
#Its obviously doing erroneous things but that will change,
#I will encounter the same problem in quite a few different places
#but if it can be figured out this once, I can incorporate it elsewhere
def CalcRoundTotals(self):
print x
if __name__ == "__main__":
a = App()
a.mainloop()
It really has me vexed. I thought about concatenating, however, when doing self.ConcatenatedVarName = Entry(...) I don't quite know how to do that. Because, when I concatenate, i do eval("Player" + CounterInForLoop + "roundScore") but SPE doesn't like it when I do that.
Any help at all would be great. I would really like to not write 50(?) if statements that print different amounts of text boxes if i == NumPlayers
Thank you.
Nevermind, figured it out on my own, here is the solution for anyone else with a similar problem.
self.PlayerTextBox = []
for NameCount in range(1,(NumPlayers + 1)):
self.PlayerTextBox.append(Entry(self, width = 4))
self.PlayerTextBox[NameCount - 1].grid(row = 2, column =
(2 * NameCount))
self.PlayerTextBox[NameCount - 1].insert(0, " 0")
Have some other things to figure out as far as how to print things, but those are minor and I will be able to fix them once I try to.
Thank you to anyone who looked at it and tried to figure it out even if you were unsuccessful.
Hello :) In the typing practice program I am creating in python 3, I have a for...loop that takes the characters from a global list of a certain paragraphs characters and compares it to the character the user is typing.
For some reason, the for loop wont iterate and won't go on to the next character. After some research, I figured out that you have to use local variables in order to do this but that doesn't work either. Here is my code:
import random
from tkinter import *
root=Tk()
a_var = StringVar()
words = []
eachword = []
global eachchar
charlist = []
x=random.randint(0,10)
def typingpractice():
realcharlist = []
something = []
paralist = []
#x=random.randint(0,10)
file = open("groupproject.txt")
line = file.readline()
paraWords = []
for line in file:
newline = line.replace("\n","")
paralist.append(line.replace("\n", ""))
paraWords.append(newline.split(" "))
wordList = []
for c in paraWords[x]:
charlist.append(c)
for b in range(0,len(charlist)):
wordList.append(charlist[b])
#print (wordList)
for p in charlist[b]:
realcharlist.append(p)
#print(realcharlist)
a=Canvas(root, width=500, height=500)
a.pack()
a.create_text(250,50, text = "Typing Fun", width = 500, font = "Verdana", fill = "purple")
a.create_text(250,300, text = paralist[x], width = 500, font = "Times", fill = "purple")
a = Entry(root, width = 100)
a.pack()
a.focus_set()
a["textvariable"] = a_var
def compare(s, realcharlist):
for g in realcharlist:
print ("s:",s)
print ("g:",g)
if s == g:
print ("y")
a['fg'] = 'green'
break
else:
print ("n")
a['fg'] = 'red'
break
def callback(*args):
global s
# print ("b:",xcount)
s = a.get()
s = s[-1:]
compare(s, realcharlist)
a_var.trace_variable("w", callback) #CALL WHEN VARIABLE IS WRITTEN
def practicetest():
print ("nothing here yet")
b = Button(root, text="Start Practice", command=typingpractice)
b.pack()
d = Button(root, text="Test", command=practicetest)
d.pack()
root.mainloop()
The text file "groupproject.txt" is an external text file that contains 10 one-line paragraphs, each character of each paragraph is being compared to what the user is typing in.
Any help on how to make the for loop work would be greatly appreciated. Thanks :D
Basically what happened was there was a break in the for loop so it would always iterate itself starting from 0 again.