ValueError with entry-field. Tkinter math-program - python-3.x

writing a tkinter program for my kids to do some math.
It first shows a label with the math-question, for example "What is 5 + 3?"
Then an inputfield (entry) shows to get the answer (entry-field checks if integers are entered)
A Button is created to start a function to check the solution.
Function to create random numbers and create entry-field:
def Do_Math(self):
nr1 = random.choice(self.numbers) #random number from list
nr2 = random.choice(self.numbers) #random number from list
do_math = nr1 + nr2 #do the math
txt_som = Label(tk, text = 'wat is de som van ' + str(nr1) + ' en ' + str(nr2) + ' ?', state='normal', \
font=('Courier', 14, "bold"), justify=LEFT, padx = 10, background='LightCyan2')
txt_som.place(x = 20, y = 170)
tk.update()
oplossing = Entry(tk, validate="key") #create input-field #validate to check for integer
oplossing['validatecommand'] = (oplossing.register(self.testVal),'%P','%d') #validate to check for integer
oplossing.place(x = 20, y = 200) #place inputfield on screen
oplossing.focus_set() #set focus so user can directly input.
self.oplossing = oplossing
self.do_math = do_math
#knop voor ingeven oplossing:
knop_oplossing = Button(tk, text='Ok?', command=self.solution) #create button
knop_oplossing.place(x = 20, y = 250) #place button on screen
tk.update()
Function to check the solution:
If the solution is good then the above function runs again to create new numbers.
def solution(self):
while True:
if int(self.oplossing.get()) == self.do_math:
#self.oplossing.delete(0, END) #clear entr-value
play.random_sound('good')
self.lbl.place(self.lbl.pi) #Place label
self.lbl_w.place_forget() #Hide label
tk.update()
#global score
#score = score + 1
#print('GOED GEDAAN!', score, 'van', asked_maths, 'pogingen goed')
game.Do_Math() #Choose new random numbers
elif int(self.oplossing.get()) != self.do_math:
play.random_sound('wrong')
self.lbl.place_forget() #Hide label
self.lbl_w.place(self.lbl_w.pi) #Place label
tk.update()
break #Go out the while loop and do nothing
Every runs ok, but in the background i get following error every time the solution is good:
if int(self.oplossing.get()) == self.do_math:
ValueError: invalid literal for int() with base 10: ''
I think the problem is that the function "solution" tries this:
int(self.oplossing.get()) == self.do_math
but the entry-field has no value jet.
What do you think? I wanna solve these error messages before going further.
Thnx in advanced!

Related

how do i add play again for tkinter python?

I can't find a source on how to add play again for my python code. I am currently using a snake game that i found on YouTube but I honestly am having trouble figuring this out.
github.com/Amfuhr/Snake-game.git
def game_over():
canvas.delete(ALL)
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2,
font=('consolas',70), text="GAME OVER", fill="red", tag="gameover")
The full script:
#The tkinter package (“Tk interface”) is the standard Python interface to the Tcl/Tk GUI toolkit.
#Tkinter are available on most Unix platforms, including macOS, as well as on Windows systems.
from tkinter import *
import random
#box size, game speed, and color schemes
#classes for the game settings
GAME_WIDTH = 700
GAME_HEIGHT = 700
SPEED = 400
#Snake and food size
SPACE_SIZE = 50
BODY_PARTS = 3
#class for snake and food
#RGB for the color of the backgrund, snake, and food
SNAKE_COLOR = "white"
FOOD_COLOR = "blue"
BACKGROUND_COLOR = "#000000"
class Snake:
#set the body size of the snake, a list of square graphics
def __init__(self):
self.body_size = BODY_PARTS
self.coordinates = []
self.squares = []
#list of coordinates
for i in range(0, BODY_PARTS):
self.coordinates.append([0, 0])
for x, y in self.coordinates:
square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR, tag="snake")
self.squares.append(square)
class Food:
def __init__(self):
#food is set into random location using the random module
#based on orientation. We use fill to set the food color
x = random.randint(0, (GAME_WIDTH / SPACE_SIZE)-1) * SPACE_SIZE
y = random.randint(0, (GAME_HEIGHT / SPACE_SIZE) - 1) * SPACE_SIZE
self.coordinates = [x, y]
canvas.create_oval(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=FOOD_COLOR, tag="food")
def next_turn(snake, food):
#head of the snake and the direction of the snake movements
x, y = snake.coordinates[0]
if direction == "up":
y -= SPACE_SIZE
elif direction == "down":
y += SPACE_SIZE
elif direction == "left":
x -= SPACE_SIZE
elif direction == "right":
x += SPACE_SIZE
snake.coordinates.insert(0, (x, y))
square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR)
snake.squares.insert(0, square)
if x == food.coordinates[0] and y == food.coordinates[1]:
global score
score += 1
label.config(text="Score:{}".format(score))
canvas.delete("food")
food = Food()
else:
del snake.coordinates[-1]
canvas.delete(snake.squares[-1])
del snake.squares[-1]
if check_collisions(snake):
game_over()
else:
window.after(SPEED, next_turn, snake, food)
def change_direction(new_direction):
global direction
if new_direction == 'left':
if direction != 'right':
direction = new_direction
elif new_direction == 'right':
if direction != 'left':
direction = new_direction
elif new_direction == 'up':
if direction != 'down':
direction = new_direction
elif new_direction == 'down':
if direction != 'up':
direction = new_direction
def check_collisions(snake):
x, y = snake.coordinates[0]
if x < 0 or x >= GAME_WIDTH:
return True
elif y < 0 or y >= GAME_HEIGHT:
return True
for body_part in snake.coordinates[1:]:
if x == body_part[0] and y == body_part[1]:
return True
return False
def game_over():
canvas.delete(ALL)
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2,
font=('consolas',70), text="GAME OVER", fill="red", tag="gameover")
#Window sizing so it does not game
window = Tk()
window.title("Snake game")
window.resizable(False, False)
score = 0
direction = 'down'
#this is to show the score in a specific font size
label = Label(window, text="Score:{}".format(score), font=('consolas', 40))
label.pack()
#canvas sets the background and opens the window
canvas = Canvas(window, bg=BACKGROUND_COLOR, height=GAME_HEIGHT, width=GAME_WIDTH)
canvas.pack()
window.update()
window_width = window.winfo_width()
window_height = window.winfo_height()
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
x = int((screen_width/2) - (window_width/2))
y = int((screen_height/2) - (window_height/2))
# Geometry method is used to set the dimensions of the
# Tkinter window and is used to set the position of the main
# window on the user’s desktop.
window.geometry(f"{window_width}x{window_height}+{x}+{y}")
window.bind('<Left>', lambda event: change_direction('left'))
window.bind('<Right>', lambda event: change_direction('right'))
window.bind('<Up>', lambda event: change_direction('up'))
window.bind('<Down>', lambda event: change_direction('down'))
snake = Snake()
food = Food()
next_turn(snake, food)
window.mainloop()
Think about it this way. Your app works upon running the first time around, and you just want to repeat, that behavior, (aka run that portion of the code again), once the initial game is over without having to restart the app.
This is precisely what functions are for.
The portion of your code that executes the game is just the last three lines of your script.
snake = Snake()
food = Food()
next_turn(snake, food)
So what you can do is stick those 3 lines into a function, and put the function call just before the mainloop call where the lines used to be.
def start_game(*arg):
canvas.delete(ALL)
snake = Snake()
food = Food()
next_turn()
start_game()
window.mainloop()
So now in order to restart the game, all you need to do is call the start_game function. Adding the canvas.delete(ALL) ensures that the "GAME OVER" message is removed before the new game starts.
Now all you need is a trigger. For this you can add a double-click signal on the canvas so that when it says "GAME OVER" all you need to do is double-click on the window and the new game starts. You can even add an instruction for the user.
To do this just add a window.bind call for the double-click action gesture:
window.bind('<Double-Button-1>', start_game)
Then to add the note to your "Game Over" message in your game_over function:
def game_over():
canvas.delete(ALL)
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2,
font=('consolas',70), text="GAME OVER",
fill="red", tag="gameover")
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/1.8,
font=('consolas',20), text="double click for new game",
fill='green', tag='newgame')
And that should do it. This solution does create a new issue though, since there are no conditions on the new event binder, double clicking the screen will start a new game even if the current game is still ongoing.
I will leave this for you to try to solve. Or if you prefer you can use a different trigger.

How to make a button Stay where it is when text is changing length

So i Have been trying to make this button stay in the same position but when ever the text changes its length the button always moves
i have tried removing the row and column values that doesn't work
i have tried changing the row and column values that doesn't work
so i don't really know what to do
from tkinter import *
win = Tk()
win.title('ab')
win.config(bg='blue')
win.geometry('200x100')
i = 0
def changetext():
global i
i = i + 1
if i == 1:
lbl.config(text='a')
if i == 2:
lbl.config(text='ab')
if i == 3:
lbl.config(text='abc')
if i == 4:
lbl.config(text='abcd')
if i == 5:
lbl.config(text='abcde')
lbl = Label(win,text='111111111111', font=("Courier", 9))
lbl.grid(row=1,column=2)
btn = Button(win,text='u', command =changetext)
btn.grid(row=2,column=2)
win.mainloop()
When you make the button (or pretty much any widget for that matter), you can define its width.
lbl = Label(win, width= 20, text = '111111111111', font = ("Courier",9))
btn = Button(win, width = 20, text = 'u', command = changetext)

Function traceback says not receiving positional arguments

Issue begins at def displayAnswer
import tkinter as tk
#icftk stands for incompressible-flow toolkit
"""This program is being built to aid in getting all
parameters of a flow given certain initial conditions"""
#CREATES THE WINDOW
root = tk.Tk()
root.title("Incompressible Fluid Toolkit")
class flow:
"""This class contains all necessary parameters needed to define a flow that i
incompressible, 1D-Steady, idiabatic and encounters no energy gain or loss"""
def __init__(self,vel,diameter,density,viscosity,massflow = 0, Re = 0, newdia = 1, jetforce = 0, newvel = 0):
"""initialize a fluid with given basic measured properties"""
self.vel = vel
self.diameter = diameter
self.density = density
self.viscosity = viscosity
self.massflow = massflow # mass flow rate
self.Re = Re #Reynolds Number
self.newdia = newdia # downstream diameter for velocity change
self.jetforce = jetforce # force the stream can produce normal to a surface
self.newvel = newvel # new velocity after a cross sectional area change
def reynolds(self):
"""This function calculates reynolds
Pass ro, v, D and mu in the same unit system, in that order"""
self.Re = (self.diameter*self.vel*self.density)/(self.viscosity)
print(f"The Reynolds number for this flow is {self.Re}")
def mdot(self):
"""This function finds the mass flowrate of a flow"""
flowarea = 3.14159*(self.diameter**2) / 4
self.massflow = self.density*self.vel*flowarea
print(f"The mass flowrate is {self.massflow}")
def streamforce(self):
"""This function gives the max force that the fluid jet can apply
normal to a surface perpendicular to the flow"""
self.jetforce = self.massflow*self.vel
print(f"The maximum force the jet can apply is {self.jetforce}")
def velchange(self):
"""This function is used to determine the velocity change of
a flow when there is a change in cross sectional area of the pipe"""
newarea = 3.14159 * (self.newdia**2) / 4
self.newvel = self.massflow/(self.density*newarea)
print(f"At the location of the area change, there is a velocity change from {self.vel} to {self.newvel}")
#ALL ABOVE FUNCTIONS HAVE BEEN CONFIRMED TO WORK WITH GIVEN TEST CONDITIONS BELOW
#use test case velocity = 18.64, diameter = 0.017, density = 1.23, and viscosity = 0.0000184
#Display Entry Boxes
velo = tk.Label(root, text="Flow Velocity") # Create a text label
velo.grid(row = 0, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
veloent = tk.Entry()
veloent.grid(row = 0, column = 1, pady = 10)
diam = tk.Label(root, text="Pipe Diameter") # Create a text label
diam.grid(row = 1, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
diament = tk.Entry()
diament.grid(row = 1, column = 1, pady = 10)
dens = tk.Label(root, text="Fluid Density") # Create a text label
dens.grid(row = 2, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
densent = tk.Entry()
densent.grid(row = 2, column = 1, pady = 10)
visc = tk.Label(root, text="Fluid Viscosity") # Create a text label
visc.grid(row = 3, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
viscent = tk.Entry()
viscent.grid(row = 3, column = 1, pady = 10)
#Display answers at the bottom of the window
def displayAnswer(veloent,diament,densent,viscent):
ve = float(veloent)#gets velocity entry and turns it into a float
di = float(diament)#gets diameter entry and turns it into a float
de = float(densent)#gets density entry and turns it into a float
vi = float(viscent)#gets viscosity entry and turns it into a float
fluid = flow(ve,di,de,vi)
fluid.reynolds()
fluid.mdot()
fluid.streamforce()
reynoldsanswer = tk.Label(root, text = "f{fluid.reynolds}")
reynoldsanswer.grid(row = 5)
mdotanswer = tk.Label(root, text = "f{fluid.mdot}")
mdotanswer.grid(row = 6)
streamforceanswer = tk.Label(root, text = "f{fluid.streamforce}")
streamforceanswer.grid(row = 7)
calculatebutton = tk.Button(root,command = displayAnswer)
calculatebutton.grid(row = 4)
root.mainloop()
I am new to tkinter, trying to get experience designing simple GUI. I am using a button to initiate a calculation to obtain values about an incompressible flow. When the button is pressed, the console throws this error.
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
TypeError: displayAnswer() missing 4 required positional arguments: 'veloent', 'diament', 'densent', and 'viscent'
Similarly, if I try to convert the Entry into a float from a string outside of the function, the console throws a cannot convert string to float error.
Honestly not sure if all of the code is even right, but I'll cross these bridges one at a time.
Any insight is appreciated.
Best,
T
The problem is that your function requires parameters, but never uses them. When you call the function from a button, the button will not by default pass any options. That's why you get missing 4 required positional arguments - the function requires four, the button passes zero.
Since your function is actually doing the right thing and fetching the values it needs, there's no need to pass in the parameters. The simple fix is to simply remove them from the definition of the function:
def displayAnswer():
ve = float(veloent)#gets velocity entry and turns it into a float
di = float(diament)#gets diameter entry and turns it into a float
de = float(densent)#gets density entry and turns it into a float
vi = float(viscent)#gets viscosity entry and turns it into a float
...

How to fix 'NameError: name 'imgpath' is not defined' error in Python3 and tkinter to show a picture

I want to show the current moon phase in tkinter for today or a specific date. When I run the code I get the Error: "NameError: name 'imgpath' is not defined". I understand that I have to press the button first. How can I set the variable "imgpath" to an global variable.
I want to change the picture when I choose another date.
from tkinter import *
import ephem
from datetime import date, datetime
fenster = Tk()
def button_action():
station = ephem.Observer()
dates = dateeingabe.get()
if (dates == ""):
station.date = datetime.utcnow()
else:
station.date = dateeingabe.get()
#Mondphase
Moon = ephem.Moon()
Moon.compute(station)
drei = round(Moon.phase, 0)
eins = ephem.next_full_moon(station.date)
zwei = ephem.next_new_moon (station.date)
if drei == 0:
phasen = int(0)
elif drei == 100:
phasen = int(100)
elif (ephem.next_full_moon(station.date) > ephem.next_new_moon(station.date)):
phasen = int(100-drei+100)
else:
phasen = int(drei)
imgpath = str("/home/user/moon/"+str(phasen)+".png")
pic = PhotoImage(file=imgpath)
picz = Label(fenster, image=pic)
dateeingabe = Entry(fenster, bd=5, width=40)
start_button = Button(fenster, text="Start", command=button_action)
dateeingabe.grid(row = 1, column = 1)
start_button.grid(row = 2, column = 1)
picz.grid(row = 3, column = 1)
fenster.mainloop()
Your program executes top to bottom, so when you try to set the path to imgpath in
pic = PhotoImage(file=imgpath)
It throws an error because imgpath is defined once you call button_action in this code, two lines below:
start_button = Button(fenster, text="Start", command=button_action)
To convert imgpath to a global variable you can declare it as so :
global imgpath
Although that will not solve your problem as you're calling the PhotoImage before you're calling button_action.
Maybe try shifting that code below the Button code?
Hope this helps!

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