Python GUI Calculator backspace and clear - python-3.x

I'm making a simple calculator using tkinter. I have managed to get everything to work except for my backsapce and clear buttons.The error message I get is: 'TypeError: backspace() takes 0 positional arguments but 1 was given' for backspace and 'TypeError: clear() takes 0 positional arguments but 1 was given' for clear. I have looked around online tutorials but most of the tutorial I've found don't seem to have this problem.
from tkinter import *
root = Tk()
root.geometry("275x300")
root.title("Calculator")
def calculatorTitle():
labelTitle=Label(root, text="Calculator", bg = "black", fg = "white")
labelTitle.config(font = ("Verdana", 12, "bold"))
labelTitle.grid(row=0, column=0, columnspan=4, padx=55, pady=5)
e=Entry(root,width=20,font="Arial 12",justify='right', bg='yellow', fg = 'blue')
e.grid(row=1,column=0,columnspan=4, pady = 5)
def addEntry(ch):
e.insert(20, ch)
def CalculateEntry(expression):
expression = e.get()
expression = eval(expression)
e.delete(0, END)
e.insert(20, expression)
def clear():
e.delete(0, END)
return
def backspace():
current = e.get()
lenght = len(current)-1
e.delete(lenght, END)
def calculatorBoard():
b1=Button(root,text='1',width=5,command=lambda:addEntry(1))
b2=Button(root,text='2',width=5,command=lambda:addEntry(2))
b3=Button(root,text='3',width=5,command=lambda:addEntry(3))
bAddition=Button(root,text='+',width=5,command=lambda:addEntry('+'))
b4=Button(root,text='1',width=5,command=lambda:addEntry(4))
b5=Button(root,text='5',width=5,command=lambda:addEntry(5))
b6=Button(root,text='6',width=5,command=lambda:addEntry(6))
bSubtract=Button(root,text='-',width=5,command=lambda:addEntry('-'))
b7=Button(root,text='7',width=5,command=lambda:addEntry(7))
b8=Button(root,text='8',width=5,command=lambda:addEntry(8))
b9=Button(root,text='9',width=5,command=lambda:addEntry(9))
bMultiply=Button(root,text='*',width=5,command=lambda:addEntry('*'))
bClear=Button(root,text='CE',width=5,command=lambda:clear('CE'))
b0=Button(root,text='0',width=5,command=lambda:addEntry(0))
bBackspace=Button(root,text='<-',width=5,command=lambda:backspace('<-'))
bDivide=Button(root,text='/',width=5,command=lambda:addEntry('/'))
bEqual=Button(root,text='=',width=27,command=lambda:CalculateEntry('='))
b1.grid(row=2,column=0,pady = 10 )
b2.grid(row=2,column=1,pady = 10 )
b3.grid(row=2,column=2,pady = 10 )
bAddition.grid(row=2,column=3,pady = 10 )
b4.grid(row=3,column=0,pady = 10 )
b5.grid(row=3,column=1,pady = 10 )
b6.grid(row=3,column=2,pady = 10 )
bSubtract.grid(row=3,column=3,pady = 10 )
b7.grid(row=4,column=0,pady = 10 )
b8.grid(row=4,column=1,pady = 10 )
b9.grid(row=4,column=2,pady = 10 )
bMultiply.grid(row=4,column=3,pady = 10 )
bClear.grid(row=5,column=0,pady = 10 )
b0.grid(row=5,column=1,pady = 10 )
bBackspace.grid(row=5,column=2,pady = 10 )
bDivide.grid(row=5,column=3,pady = 10 )
bEqual.grid(row= 6, column= 0, columnspan= 4, pady= 10)
calculatorTitle()
calculatorBoard()
root.mainloop()
Edit: This part if an assignment where I have to follow some instructions.

def clear() and def backspace(): you define the functions with zero arguments.
clear('CE') and backspace('<-'): You call the functions with one argument
To fix the problem, either redefine the functions to take an argument,
def clear(arg):
# do whatever
def backspace(arg):
# do whatever
or change the call to not pass any.
bClear=Button(root,text='CE',width=5,command=clear)
bBackspace=Button(root,text='<-',width=5,command=backspace)
Be sure not to do both -- you'll run into the opposite problem.

Related

How to pass a value to an entry validatecommand

I am validating a form in tkinter using validatecommand. There are two entries, one should only accept integers and the other floats.
Here is the basic code:
import tkinter as tk
class Form(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
validCmd1 = (self.register(self.val1), "%P")
validCmd2 = (self.register(self.val2), "%P")
self.lab1 = tk.Label(self, text = "Float:")
self.lab1.grid(row = 1, column = 0, padx = 10, pady = 10)
self.ent1 = tk.Entry(self, validate = "key", validatecommand = validCmd1)
self.ent1.grid(row = 1, column = 1)
self.lab2 = tk.Label(self, text = "Integer:")
self.lab2.grid(row = 2, column = 0, padx = 10, pady = (0,10))
self.ent2 = tk.Entry(self, validate = "key", validatecommand = validCmd2)
self.ent2.grid(row = 2, column = 1)
def val1(self, value):
try:
float(value)
except:
return False
else:
return True
def val2(self, value):
try:
int(value)
except:
return False
else:
return True
app = Form()
app.mainloop()
Instead of writing two different functions for two very similar tasks, I tried to pass an integer to the validate command (1 for float, 2 for integer). I tried to use lambdas to pass values to the validation functions, first on the self.register part: self.register(lambda: self.val1(1)) but this gave TypeError: <lambda>() takes 0 positional arguments but 1 was given and the second time I tried using lambda on the validatecommand command: validatecommand = lambda: validCmd1(1) which gives TypeError: 'tuple' object is not callable.Is it possible to pass values to a validation command?
The validatecommand option takes a tuple that can have any arguments you want. For example, if you want to pass an integer it's as easy as this:
validCmd1 = (self.register(self.val), 1, "%P")
validCmd2 = (self.register(self.val), 2, "%P")
def val(self, arg, value):
print(f"val1 arg='{arg}' value='{value}'")
...
Sometimes it's easier to understand if you separate the registration of the command from the value passed in to validatecommand. For example:
vcmd = self.register(self.val)
...
self.ent1 = tk.Entry(..., validatecommand = (vcmd, 1, "%P"))
self.ent2 = tk.Entry(..., validatecommand = (vcmd, 2, "%P"))

How to print the same text multiple times(5 times) in tkinter/GUI?

from tkinter import *
root=Tk()
textbox=Text(root)
textbox.pack()
button1=Button(root, text='Output Name', command=lambda : print('Hello'))
button1.pack()
def redirector(inputStr):
textbox.insert(INSERT, inputStr)
sys.stdout.write = redirector
root.mainloop()
This is my code with out a timer to do it five times.
This seems a little bit like homework, so lets try to get you on the right track over outright providing the code to accomplish this.
You're going to want to create a loop that performs your code a certain number of times. Let's say we just want to output a certain string 5 times. As an example, here's some really simple code:
def testPrint():
print('I am text!')
for i in range(5):
testPrint()
This will create a function called testPrint() that prints text "I am Text!", then run that function 5 times in a loop. If you can apply this to the section of code you need to run 5 times, it should solve the problem you are facing.
This worked for me. It creates a table using the .messagebox module. You can enter your name into the entry label. Then, when you click the button it returns "Hello (name)".
from tkinter import *
from tkinter.messagebox import *
master = Tk()
label1 = Label(master, text = 'Name:', relief = 'groove', width = 19)
entry1 = Entry(master, relief = 'groove', width = 20)
blank1 = Entry(master, relief = 'groove', width = 20)
def show_answer():
a = entry1.get()
b = "Hello",a
blank1.insert(0, b)
button1 = Button(master, text = 'Output Name', relief = 'groove', width = 20, command =show_answer)
#Geometry
label1.grid( row = 1, column = 1, padx = 10 )
entry1.grid( row = 1, column = 2, padx = 10 )
blank1.grid( row = 1, column = 3, padx = 10 )
button1.grid( row = 2, column = 2, columnspan = 2)
#Static Properties
master.title('Hello')

can't multiply sequence by non-int of type 'float' error with tkinter Entry

I'm creating a game : The canon-man. You have to touch a target, specifying the angle and the velocity. The game is separated in 3 phases :
1- You choose "play", "options" or "quit"
2- You choose the difficulty "easy", "medium" or "hard"
3- The game appears and you can choose the parameters.
My game is separated in two classes, the first one(Canon) which handle the first and second phases and the second one(Man) which handle the third phase.
I can access the first two phases but when I click on "easy" "medium" or "hard"
I get this error :
" can't multiply sequence by non-int of type 'float' "
and the third window appears like this (Tkinter's Entrys are not here) :
third window
here is my class Man (a part of it) :
def base(self):
self.height_c = 300
self.width_c = 700
self.choix = 0
self.lang = 0
self.flag = 0
bool(self.flag)
self.t = 0
self.x = 130
self.y = 225
self.root = Tk()
self.root.title("ShotDown")
self.can = Canvas(self.root, width=self.width_c, height=self.height_c)
self.can.grid(row=0, column=4, rowspan=4)
self.cible = PhotoImage(file="\image\\cible.png")
self.canon = PhotoImage(file="\image\\canon.png")
self.canontete = PhotoImage(file="\image\\canontete.png")
self.photo = PhotoImage(file="\image\\paysage_700.png")
self.hc45 = PhotoImage(file="\image\hc45.png")
self.Play = PhotoImage(file="\image\\Play.gif")
self.angle = Entry(self.root, textvariable="hey")
# initialisation:
#
# choix du fond
if self.lang == "0000":
self.photo = PhotoImage(file="\image\\paysage_700.png")
elif self.lang == "0001":
self.photo = PhotoImage(file="\image\\mer.png")
elif self.lang == "0010":
self.photo = PhotoImage(file="\image\\interstellar.png")
self.y_cible = random.randint(60, 240)
self.can.create_image(0, 0, anchor=NW, image=self.photo)
self.image = self.can.create_image(-50, -50, image=self.hc45)
# Creation des differents boutons
#
bou1 = Button(self.root, text='Quitter', width=8, command=self.root.quit)
bou1.grid(row=0, column=2)
bou2 = Button(self.root, text='Demarrer', width=8, command=Man.start_it(self))
bou2.grid(row=0, column=3)
bou3 = Button(self.root, text='Arreter', width=8, command=Man.stop_it)
# bou3.grid(row=2, column=1)
New_angle = StringVar()
New_angle.set("Saisir Nouvel Angle")
text_angle = Label(self.root, text="Angle :")
text_angle.grid(row=1, column=2)
self.angle = Entry(self.root, textvariable=New_angle)
self.angle.grid(row=1, column=3)
New_vitesse = StringVar()
New_vitesse.set("Saisir Nouvelle Vitesse")
text_vitesse = Label(self.root, text="Vitesse :")
self.in_vitesse = Entry(self.root, textvariable=New_vitesse)
text_vitesse.grid(row=2, column=2)
self.in_vitesse.grid(row=2, column=3)
bou_tir = Button(self.root, text='Tirer', width=8, command=Man.start_it(self))
bou_tir.grid(row=0, column=3)
if self.flag == 0:
self.base = self.can.create_image(50, 260, image=self.canontete)
self.can.create_image(self.width_c - 50, self.y_cible, image=self.cible)
# demarrage de la boucle principale
#
self.root.mainloop()
If you don't understand something, feel free to ask ^^.
Thanks.
EDIT :
#calico_ " - Without seeing all of your code ". Sorry, I didn't show where the error was:
self.can.update()
b = float(self.angle.get() * pi / 180)
Vo = float(self.in_vitesse.get())
self.t += 0.025
self.x += (Vo * cos(b) * self.t)
self.y -= ((-0.5 * 9.81 * (self.t ** 2)) + (Vo * sin(b) * self.t))
print(self.x, self.y)
Without seeing all of your code, or the complete exception, it is hard to tell you exactly whats wrong. But, here is an explanation of the error and likely why you're seeing it:
Sequences can be multiplied in Python. Here we create a list (a type of sequence), and then multiply it by 3:
>>> x = [1,2,3]
>>> y = x*3
The result is not the product of each item by the factor. Rather, it is the original sequence three times:
>>> y
[1, 2, 3, 1, 2, 3, 1, 2, 3]
Another piece of the puzzle - When you divide two numbers, the resulting value is usually a float, not an int:
>>> x = 6/3
>>> type(x)
<class 'float'>
And you cannot multiply a sequence by a float, only an integer. Exactly what the exception says..
So, look for a part of your code where you're multiplying sequences. The complete Traceback will help you find the lines causing the error. Just guessing, but what you probably want is list comprehension:
>>> y = [val*3 for val in x]
>>> y
[3, 6, 9]

How can I use "sleep" properly?

So, I know about the sleep function, but it doesn't work how I expected it to work.
If I do something like this:
from time import sleep
print('Something')
sleep (10)
print('Something')
It works how (I think) it should (it prints one thing, waits and then prints the other one).
But in my code it doesn't work like that.
This is the whole code:
from tkinter import *
from time import sleep
# Fenster
window = Tk()
window.title('Test')
c = Canvas(window, height=450, width=800)
c.pack()
# Bildelemente
Hintergrund = PhotoImage(file='Hintergrund.gif')
Background = Label(image=Hintergrund)
Background.image = Hintergrund
Background.place(x=0, y=0)
Boden = PhotoImage(file='Boden.gif')
Ground = Label(image=Boden)
Ground.image = Boden
Ground.place(x=0, y=300)
Char = PhotoImage(file='Char.gif')
Character = Label(image=Char)
Character.image = Char
Character.pack()
# Koordinaten ermitteln
def coordinatesX(id_num):
pos = c.coords(id_num)
x = (pos[0] + pos[2])/2
return x
def coordinatesY(id_num):
pos = c.coords(id_num)
y = (pos[1] + pos[3])/2
return y
# Charakter bewegen
def Char_move(event):
if event.keysym == 'Right' and coordinatesX(Char_) < 800 - 50:
c.move(Char_, 10, 0)
Character.place(x=(coordinatesX(Char_)), y=(coordinatesY(Char_)))
if event.keysym == 'Left' and coordinatesX(Char_) > 0:
c.move(Char_, -10, 0)
Character.place(x=(coordinatesX(Char_)), y=(coordinatesY(Char_)))
if event.keysym == 'Up' and coordinatesY(Char_) < 300 and coordinatesY(Char_) > 0:
jump()
sleep(5)
print('NA')
c.bind_all('<Key>', Char_move)
def jump():
c.move(Char_, 0, -50)
Character.place(x=(coordinatesX(Char_)), y=(coordinatesY(Char_)))
# Objekt zur Postitionsbestimmung vom Charakter
Char_ = c.create_oval(0, 0, 50, 50, fill='red')
c.move(Char_, 100, 223)
Character.place(x=(coordinatesX(Char_)), y=(coordinatesY(Char_)))
In this part I want it to wait and then do something (in this "example" print):
jump()
sleep(5)
print('NA')
But when I run the program and hit 'Up', it waits and then the object goes up and the program prints "NA" at the same time.
How do I have to do this so that the object goes up, it waits and then prints something?
Do not use sleep. Use the tkinter after method instead.
jump()
window.after(5000, lambda x: print('NA'))
Sleep freezes your gui. Jump is executed but sleep prevents the gui from being redrawn, therefore you do not see any change. After is a method from Tk and allows you to schedule operations.

Having an issue getting TKinter to track mouse movement to an object

I was unsure whether to post the full code or not, but here's what I have:
from tkinter import *
from random import randint
HEIGHT = 500
WIDTH = 800
MID_X = WIDTH/2
MID_Y = HEIGHT/2
SHIP_R = 15
SHIP_SPD = 10
bub_id = list()
bub_r = list()
bub_speed = list()
MIN_BUB_R = 10
MAX_BUB_R = 30
MAX_BUB_SPD = 6
GAP = 100
window = Tk()
window.title('Bubble Blaster')
c = Canvas(window, width=WIDTH, height=HEIGHT, bg='darkblue')
c.pack()
ship_id = c.create_polygon(5, 5, 5, 25, 30, 15, fill='red')
ship_id2 = c.create_oval(0, 0, 30, 30, outline='red')
c.move(ship_id, MID_X, MID_Y)
c.move(ship_id2, MID_X, MID_Y)
def move_ship(event):
fixed = True
while fixed == True:
ship_x, ship_y = event.x, event.y
c.move(ship_id, ship_x, ship_y)
c.move(ship_id2, ship_x, ship_y)
sleep(0.01)
def create_bubble():
x = WIDTH + GAP
y = randint(0, HEIGHT)
r = randint(MIN_BUB_R, MAX_BUB_R)
id1 = c.create_oval(x-r, y-r, x+r, y+r, outline='white')
bub_id.append(id1)
bub_r.append(r)
bub_speed.append(randint(1, MAX_BUB_SPD))
def move_bubbles():
for i in range(len(bub_id)):
c.move(bub_id[i], -bub_speed[i], 0)
def get_coords(id_num):
pos = c.coords(id_num)
x = (pos[0] + pos[2])/2
y = (pos[1] + pos[3])/2
return x, y
def del_bubble(i):
del bub_r[i]
del bub_speed[i]
c.delete(bub_id[i])
del bub_id[i]
def clean_up_bubs():
for i in range(len(bub_id)-1, -1, -1):
x, y = get_coords(bub_id[i])
if x < -GAP:
del_bubble(i)
from math import sqrt
def distance(id1, id2):
x1, y1 = get_coords(id1)
x2, y2 = get_coords(id2)
return sqrt((x2-x1)**2 + (y2-y1)**2)
def collision():
points = 0
for bub in range(len(bub_id)-1, -1, -1):
if distance(ship_id2, bub_id[bub]) < (SHIP_R+bub_r[bub]):
points += (bub_r[bub] + bub_speed[bub])
del_bubble(bub)
return points
c.create_text(50, 30, text='TIME', fill='white')
c.create_text(150, 30, text='SCORE', fill='white')
time_text = c.create_text(50, 50, fill='white')
score_text = c.create_text (150, 50, fill='white')
def show_score(score):
c.itemconfig(score_text, text=str(score))
def show_time(time_left):
c.itemconfig(time_text, text=str(time_left))
from time import sleep, time
BUB_CHANCE = 20
TIME_LIMIT = 30
BONUS_SCORE = 1000
# MAIN GAME LOOP
c.bind("<B1_Motion>", move_ship)
score = 0
bonus = 0
end = time() + TIME_LIMIT
while time() < end:
if randint(1, BUB_CHANCE) == 1:
create_bubble()
move_bubbles()
move_ship("<B1_Motion>")
clean_up_bubs()
score += collision()
if (int(score / BONUS_SCORE)) > bonus:
bonus += 1
end += TIME_LIMIT
show_score(score)
show_time(int(end-time()))
window.update()
sleep(0.01)
c.create_text(MID_X, MID_Y, \
text='PARTY TIME, EXCELLENT', fil='white', font=('Helvetica', 30))
c.create_text(MID_X, MID_Y + 30, \
text='Score: ' + str(score), fill='white')
c.create_text(MID_X, MID_Y + 45, \
text='BONU TIME: ' + str(bonus*TIME_LIMIT), fill='white')
I'm a complete beginner when it comes to python, and have been given an assignment to only use tkinter and the standard libraries to give mouse movement to this "game". I just can't seem to get the right grasp of it. Any suggestions would be appreciated!
The first step is to remove your while loop, and put most of your functionality into a function. Put everything you want to do in a single frame of animation into this function.
Next, call this function on a regular interval using the after command. This allows the event loop to run continuously, which is important for your UI to be responsive. You can do your time() < end calculation inside this function, and use the result to break the cycle once time is up.
It looks something like this:
def draw_one_frame():
if randint(1, BUB_CHANCE) == 1:
create_bubble()
move_bubbles()
# move_ship("<B1_Motion>")
clean_up_bubs()
score += collision()
if (int(score / BONUS_SCORE)) > bonus:
bonus += 1
end += TIME_LIMIT
show_score(score)
show_time(int(end-time()))
if time() < end:
after(10, draw_one_frame)
You can use the first parameter to after to control how many frames per second you wish your program to run at.
Next, you need to handle mouse movement. You do this by creating a binding to the movement of the mouse. You do this outside of the draw_one_frame method. It looks something like this:
c.bind("<B1-Motion>", move_ship)
You also need to remove the infinite loop from move_ship, and also remove the sleep. You simply need to do all the calculations for the current mouse position. The function will be already be looping -- being called once each time the mouse moves.
Finally, you need to call window.mainloop() after all your other code, so that the program can process events. This should be the very last line of your program. It will run until the window is destroyed.

Resources