Tkinter How to fix After issue caused by Recursion - python

WORK_MIN = 12
SHORT_BREAK_MIN = 5
LONG_BREAK_MIN = 20
reps = 0
def start_timer():
global reps
work_min = WORK_MIN * 60
long_break = LONG_BREAK_MIN * 60
short_break = SHORT_BREAK_MIN * 60
while True:
reps += 1
if reps % 8 == 0:
countdown(long_break)
elif reps % 2 == 1:
countdown(work_min)
else:
countdown(short_break)
def countdown(second_count):
minute_time = int(second_count / 60)
# minute_time = math.floor(second_count / 60)
if len(str(minute_time)) == 1:
minute_time = f"0{minute_time}"
second_time = second_count % 60
if len(str(second_time)) == 1:
second_time = f"0{second_time}"
if second_count > 0:
window.after(1000, countdown, second_count - 1)
window = Tk()
window.title("Pomodoro Practice")
window.config(padx=100, pady=50, bg=YELLOW)
window.minsize(width=300, height=300)
start_timer()
window.mainloop()
Whenever I run this code, it skips over window.after(1000, countdown, second_count - 1) and returns back to the while loop in start_timer(). I would like the countdown function to be called recursively after waiting for a second until second count == 0.

Add break to the end of all your while conditions, and add an else condition to the end of countdown that restarts start_timer.
WORK_MIN = 12
SHORT_BREAK_MIN = 5
LONG_BREAK_MIN = 20
reps = 0
def start_timer():
global reps
work_min = WORK_MIN * 60
long_break = LONG_BREAK_MIN * 60
short_break = SHORT_BREAK_MIN * 60
while True:
reps += 1
if reps % 8 == 0:
countdown(long_break)
break
elif reps % 2 == 1:
countdown(work_min)
break
else:
countdown(short_break)
break
def countdown(second_count):
minute_time = int(second_count / 60)
# minute_time = math.floor(second_count / 60)
if len(str(minute_time)) == 1:
minute_time = f"0{minute_time}"
second_time = second_count % 60
if len(str(second_time)) == 1:
second_time = f"0{second_time}"
canvas.itemconfig(timer_text, text=f"{minute_time}:{second_time}")
if second_count > 0:
window.after(1000, countdown, second_count - 1)
else:
start_timer()
start_timer()
You can also just get rid of the while loop entirely, but still add the condition to the end of countdown. However, if you are going to "trim the fat", trim it all. It's interesting that you work for 12 minutes, break for 5 (3 times) and then you work for 12 and break for 20. So, in 83 minutes you work 48 and break for 35. Sounds like a helluva job. If you worked for me you could have a float('inf') minute break ... cause you'd be fired. :D
import tkinter as tk
window = tk.Tk()
canvas = tk.Canvas(window, width=800, height=600)
canvas.pack()
timer_text = canvas.create_text(50, 50, font='Helvetica 24 bold')
reps = 0
def start_timer():
global reps
reps += 1
if reps % 8 == 0:
countdown(20*60)
elif reps % 2 == 1:
countdown(12*60)
else:
countdown(5*60)
def countdown(seconds):
canvas.itemconfig(timer_text, text=f"{seconds//60:02}:{seconds%60:02}")
window.after(1000, countdown, seconds-1) if seconds else start_timer()
start_timer()
if __name__ == '__main__':
window.mainloop()

Related

How can I use a single button to pause and unpause timer in tkinter? (without using pygame)

I'm working on a simple timer that counts down 30 mins for me to study and 5 mins for a break. So far, the start_timer function and count_down function work well, I just cannot figure out how to write the pause function. I did some research for a few days. Most articles are using pygame or to bind with different keys. I am wondering what function I should use for one tkinter button to pause/unpause my timer if something comes up and I want to pause the timer till I'm back.
Thank you #TimRoberts, I can pause the timer now. However, I don't know how to unpause the timer to let it continue counting down.
from tkinter import *
import math
WORK_MIN = 30
BREAK_MIN = 5
reps = 0
paused = False
# --------------------------- TIMER ---------------------------- #
def start_timer():
global reps
reps += 1
work_sec = WORK_MIN * 60
break_sec = BREAK_MIN * 60
if reps % 2 == 1:
title_label.config(text="Study")
count_down(work_sec)
else:
title_label.config(text="Break")
count_down(break_sec)
window.attributes('-topmost', 0)
# ------------------------ COUNTDOWN--------------------------- #
def count_down(count):
global paused
count_min = math.floor(count / 60)
count_sec = count % 60
if count_min < 10:
count_min = f"0{count_min}"
if count_sec < 10:
count_sec = f"0{count_sec}"
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}" )
if count > 0:
if not paused:
count -= 1
window.after(1000, count_down, count-1)
else:
start_timer()
# ---------------------------- PAUSE ------------------------------- #
def pause_function():
global paused
paused = not paused
# ---------------------------- UI ------------------------------- #
window = Tk()
title_label = Label(text="Timer")
title_label.grid(column=1, row=0)
check_marks = Label(text="")
check_marks.grid(column=1, row=4)
canvas = Canvas(width=200, height=224, bg="lightblue")
timer_text = canvas.create_text(100, 128, text="00:00", fill="white", font=("Courier", 45, "bold"))
canvas.grid(column=1, row=1)
start_button = Button(text="Start", command=start_timer)
start_button.grid(column=0, row=2)
pause_button = Button(text="Pause", command=pause_function)
pause_button.grid(column=2, row=2)
window.mainloop()
You need to do the "after" call even if you're paused, otherwise you'll never notice when you unpause. Also, since you're decrementing count once, you don't need to do it again:
def count_down(count):
count_min = count // 60
count_sec = count % 60
canvas.itemconfig(timer_text, text=f"{count_min:02d}:{count_sec:02d}" )
if count:
if not paused:
count -= 1
window.after(1000, count_down, count)
else:
start_timer()
If you want to be tricky, you could use:
if count:
count -= not paused
since True is 1 and False is 0.

I made a minesweeper game with tkinter

I'm a total beginner who just started learning to code with the book "Head First Learn to Code". I've just finished the book and coded a minesweeper game with python3 on my mac. I hope to get some advice on the game I made. If you feel like the code is too long to read, here are some specific questions I'd like to ask:
What does this: flag = flag.resize((12, 12), Image.ANTIALIAS) actually do, why do I need to assign it a variable instead of just doing this: flag.resize((12, 12), Image.ANTIALIAS) as I'd do with other objects
Is there a better way to create a stopwatch? Mine is the function update_time.
Did I make any mistake regarding the conventions of python? Please point out some for me.
Any help would be much appreciated!
Here's my code:
from tkinter import *
import random as rd
from tkinter import messagebox
from PIL import ImageTk, Image
import sys
class GameGrid(Frame): #the game
def __init__(self, master, height, width, mines_count, player):
Frame.__init__(self, master)
self.grid(row=0)
self.master = master
if sys.platform == 'win32': #checking os
self.platform = 'windows'
else:
self.platform = 'macos'
self.height = height #storing height, width, mines_count, and player's name
self.width = width
self.mines_count = mines_count
self.player_name = player
self.play_time = 0 #initiating play_time and other values
self.lost = False
self.won = False
self.notmine = height * width - mines_count #calculate the number of tiles that are not mines
flag = Image.open('flag.png') #creating and storing flag and bomb images
flag = flag.resize((12, 12), Image.ANTIALIAS)
bomb = Image.open('bomb.png')
bomb = bomb.resize((12, 12), Image.ANTIALIAS)
self.flag = ImageTk.PhotoImage(flag)
self.bomb = ImageTk.PhotoImage(bomb)
grid_model = [[0]*width for item in [0]*height] #creating a list to hold 1's and 0's
while mines_count > 0: #1 is mine, 0 is normal
randi = rd.randint(0, height-1) #putting mines into the list by generating random coordinates
randj = rd.randint(0, width-1) #and storing mine in the corresponding place
if grid_model[randi][randj] == 0:
grid_model[randi][randj] = 1
mines_count -= 1
self.tiles = {} #creating Tiles and storing them using dictionary
for i in range(height):
for j in range(width):
if grid_model[i][j] == 1:
self.tiles[i, j] = Tile(self, i, j, True)
else:
mine_neighbors = 0 #counting nearby mines if Tile in creation is not a mine
if i - 1 >= 0:
if grid_model[i-1][j] == 1:
mine_neighbors += 1
if i - 1 >= 0 and j - 1 >= 0:
if grid_model[i-1][j-1] == 1:
mine_neighbors += 1
if i - 1 >= 0 and j + 1 < width:
if grid_model[i-1][j+1] == 1:
mine_neighbors += 1
if j - 1 >= 0:
if grid_model[i][j-1] == 1:
mine_neighbors += 1
if j + 1 < width:
if grid_model[i][j+1] == 1:
mine_neighbors += 1
if i + 1 < height:
if grid_model[i+1][j] == 1:
mine_neighbors += 1
if i + 1 < height and j - 1 >= 0:
if grid_model[i+1][j-1] == 1:
mine_neighbors += 1
if i + 1 < height and j + 1 < width:
if grid_model[i+1][j+1] == 1:
mine_neighbors += 1
self.tiles[i, j] = Tile(self, i, j, False, mine_neighbors)
def reveal_surroundings(self, i, j): #reveal nearby tiles
revealing = []
width = self.width
height = self.height
if i - 1 >= 0:
revealing.append(self.tiles[i-1, j])
if i - 1 >= 0 and j - 1 >= 0:
revealing.append(self.tiles[i-1, j-1])
if i - 1 >= 0 and j + 1 < width:
revealing.append(self.tiles[i-1, j+1])
if j - 1 >= 0:
revealing.append(self.tiles[i, j-1])
if j + 1 < width:
revealing.append(self.tiles[i, j+1])
if i + 1 < height:
revealing.append(self.tiles[i+1, j])
if i + 1 < height and j - 1 >= 0:
revealing.append(self.tiles[i+1, j-1])
if i + 1 < height and j + 1 < width:
revealing.append(self.tiles[i+1, j+1])
for tile in revealing:
tile.reveal()
def lose(self): #show if lost, stop the clock
global stp
stp = True
self.lost = True
if self.platform == 'windows':
for tile in self.tiles:
if self.tiles[tile].mine:
self.tiles[tile].config(bg='red')
else:
for tile in self.tiles:
if self.tiles[tile].mine:
self.tiles[tile].config(image=self.bomb, padx=9, pady=4, bg='red')
self.tiles[tile].unbind('<Button-1>')
self.tiles[tile].unbind('<Button-2>')
messagebox.showerror(message='Boom, Game Over!!')
self.score = ScoreBoard(self.master)
def win(self): #show if won, stop the clock, creating a window recording scores
global mn, sc, stp
stp = True
self.won = True
for tile in self.tiles:
if self.tiles[tile].mine:
self.tiles[tile].config(image=self.bomb, padx=9, pady=4, bg='red')
self.tiles[tile].unbind('<Button-1>')
self.tiles[tile].unbind('<Button-2>')
messagebox.showinfo(message='Congrats, You Survived ;)')
play_time = str(m) + ' mins, ' + str(s) + ' secs'
self.score = ScoreBoard(self.master, self.player_name, play_time)
class ScoreBoard(Toplevel): #for score recording
def __init__(self, master, name=None, time=None):
Toplevel.__init__(self, master)
self.title('Hall of Fame')
fin_text = ''
if name != None: #writing in the record if there is one
self.board = open('ScoreBoard.txt', 'r') #assigning the text inside ScoreBoard.txt to board_text
board_text = '' #and writing it into ScoreBoard.txt
for line in self.board:
board_text = board_text + line
self.board = open('ScoreBoard.txt', 'w')
self.record = name + ' ' + time
self.board.write(board_text + '\n' + self.record)
self.board = open('ScoreBoard.txt', 'r') #reading text in ScoreBoard and put it on the window
for line in self.board:
fin_text = fin_text + line
self.lbl = Label(self, text=fin_text)
self.lbl.pack()
self.geometry('300x300')
self.board.close()
class Tile(Label): #the Tile
def __init__(self, master, i, j, mine, mine_neighbors=None):
Label.__init__(self, master, width=2, relief=RAISED)
self.grid(row=i, column=j)
self.game = master #storing row, column, is mine or not, count of nearby mines
self.mine = mine
self.row = i
self.col = j
self.mine_neighbors = mine_neighbors
self.revealed = False
self.marked = False
self.bind('<Button-1>', self.reveal) #bind Tile: reveal(left click), mark(right click)
self.bind('<Button-2>', self.mark)
def reveal(self, event=None): #revealing tile
if self.mine:
self.game.lose()
return
else:
if not self.revealed:
self.revealed = True
self.mark()
self.unbind('<Button-1>')
self.unbind('<Button-2>')
if self.mine_neighbors == 0: #if no nearby mines, reveal nearby tiles
self.config(text='', relief=SUNKEN, bg='lightgrey', image='', padx=1, pady=1)
self.game.reveal_surroundings(self.row, self.col)
else:
self.config(text=self.mine_neighbors, relief=SUNKEN, bg='lightgrey', image='', padx=1, pady=1)
self.game.notmine -= 1
if self.game.notmine == 0:
self.game.win()
def mark(self,event=None): #marking tile
if self.game.platform == 'windows':
if not self.marked:
self.config(text='*')
self.marked = True
else:
self.config(text='')
self.marked = False
else:
if not self.marked:
self.config(image=self.game.flag, padx=9, pady=4)
self.marked = True
else:
self.config(image='', padx=1, pady=1)
self.marked = False
stp = False #used to stop the clock when lost or won
def update_time(): #a stopwatch
global m, s, timer, stp
if stp != True:
s = s + 1
if s == 60:
m = m + 1
s = 0
mn = str(m) #making the clock look better by adding a 0 when the number
sc = str(s) #of second or minute is just one digit, e.g. 01, 06, 09..
if len(sc) == 1 and len(mn) == 1:
sc = '0' + sc
mn = '0' + mn
timer.config(text=mn+':'+sc)
elif len(mn) == 1 and len(sc) != 1:
mn = '0' + str(m)
timer.config(text=mn+':'+str(s))
elif len(sc) == 1 and len(mn) != 1:
sc = '0' + sc
timer.config(text=mn+':'+sc)
timer.after(1000, update_time)
def play(height, width, mines_count, player): #initiating the game
global s, m, timer
m = 0
s = -1
time = str(m) + ':' + str(s)
root = Tk()
root.title('MineSweeper')
root.resizable(False, False)
timer = Label(root, text='%i:%i'%(m,s)) #creating stopwatch and update it every second
timer.grid(row=1)
update_time()
game = GameGrid(root, height, width, mines_count, player)
root.mainloop()
if __name__ == '__main__':
play(10, 10, 10, 'Harley')

Two-Button Menu Iteration

I've got a script that I'm adapting to micropython on a 2040, and I want to use two buttons to navigate the menu structure. I can't figure out how to make the iterate loop in the multi-choice menus work right... here's what I've got so far:
""" fit: a productivity logger """
import time
import sys
import os
import uhashlib
import machine
def final_print(sec,final_hash,final_survey):
""" leaves the summary on the screen before shutting down """
mins = sec // 60
sec = sec % 60
hours = mins // 60
mins = mins % 60
short_sec = int(sec)
duration = (str(hours) + "/" + str(mins) + "/" + str(short_sec))
print("> fit the",str(final_hash)," went ",str(final_survey)," lasted //",str(duration))
def timer_down(f_seconds,timer_focus):
""" counts down for defined period """
now = time.time()
end = now + f_seconds
while now < end:
now = time.time()
fit_progress(now,end,timer_focus,f_seconds)
b1pressed = button1.value()
time.sleep(0.01)
if not b1pressed:
print('Ended Manually!')
break
def timer_up(timer_focus):
""" counts up for indefinite period """
now = time.time()
while True:
minutes = int((time.time() - now) / 60)
print(str(timer_focus)," for ",str(minutes))
b1pressed = button1.value()
time.sleep(0.01)
if not b1pressed:
print('Ended Manually!')
break
def fit_progress(now,end,timer_focus,f_seconds):
""" tracks progress of a count-down fit and prints to screen """
remain = end - now
f_minutes = int((remain)/60)
j = 1 - (remain / f_seconds)
pct = int(100*j)
print(str(timer_focus),str(f_minutes),str(pct))
def multi_choice(options):
done = 0
while done == 0:
for i in options:
b1pressed = button1.value()
b2pressed = button2.value()
time.sleep(.01)
b1released = button1.value()
b2released = button2.value()
if b2pressed and not b1pressed:
print(i," b2 pressed")
continue
if b1pressed and not b2pressed:
print(i," b1 pressed")
time.sleep(2)
return i
button1 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
button2 = machine.Pin(3, machine.Pin.IN, machine.Pin.PULL_UP)
print("format?")
fType = multi_choice(['30-minute fit','60-minute fit','indefinite fit'])
print(fType," selected")
print("focus?")
F_FOCUS = multi_choice(['personal fit','work fit','learn fit','admin fit'])
print(F_FOCUS," selected")
fStart = time.time()
if fType == "30-minute fit":
timer_down(1800,F_FOCUS)
elif fType == "60-minute fit":
timer_down(3600,F_FOCUS)
elif fType == "indefinite fit":
timer_up(F_FOCUS)
else:
sys.exit()
fEnd = time.time()
print("sentiment?")
F_SURVEY = multi_choice(['+','=','-'])
print(F_SURVEY," selected")
fDuration = fEnd - fStart
F_HASH = uhashlib.sha256(str(fEnd).encode('utf-8')).digest()
F_HASH_SHORT = F_HASH[0:3]
fitdb = open("data.csv","a")
fitdb.write(str(F_HASH)+","+str(fType)+","+str(F_FOCUS)+","+str(F_SURVEY)+","+str(fStart)+","+str(fEnd)+","+str(fDuration)+"\n")
fitdb.close()
final_print(fDuration,F_HASH_SHORT,F_SURVEY)
print(F_HASH_SHORT," ",F_HASH)
In particular, this is the logic I'm wrangling with:
def multi_choice(options):
done = 0
while done == 0:
for i in options:
b1pressed = button1.value()
b2pressed = button2.value()
time.sleep(.01)
b1released = button1.value()
b2released = button2.value()
if b2pressed and not b1pressed:
print(i," b2 pressed")
continue
if b1pressed and not b2pressed:
print(i," b1 pressed")
time.sleep(2)
return i
Here's what I'm wanting to do:
For each item in the set,
Display the item, wait for button press
If button 1 is pressed, select that item, return the item.
If button 2 is pressed, display the next item, wait for button press.
(iterate until button 1 pressed to select item)
Again, this is micropython, so I don't have all the modules you'd think available... woudl be best to do this in raw code.
0.01 second is way too short. The key is, after you detect "button down", you need to wait for "button up". You need something like:
def wait_for_btn_up(btn):
count = 2
while count > 0:
if btn.value();
count = 2
else:
count -= 1
time.sleep(0.01)
def multi_choice(options):
for i in options:
print( "trying", i )
# Wait for any button press.
while 1:
b1pressed = button1.value()
b2pressed = button2.value()
if b1pressed or b2pressed:
break
if b1pressed:
print( i, "chosen" )
wait_for_btn_up(button1)
return i
# We know B2 was pressed.
wait_for_btn_up(button2)

How to restart a game?

I'm making a game with tkinter where the player must dodge acid rain.
I'm almost done with it, I'm just needing a way of restarting the whole game after the Up key is pressed. I already made a function that's being called after rain hit the player. This function checks with an event if the up key is pressed and triggers than the restart function in which the code for restarting the game comes. All inside the Game class.
Here's the code:
import random
import time
from winsound import *
from tkinter import *
class Game():
def __init__(self):
self.high_file = open('HIGHSCORE.txt','r')
self.high = self.high_file.read()
self.tk = Tk()
self.tk.title('DeadlyRain')
self.tk.resizable(0,1)
self.tk.wm_attributes('-topmost',1)
self.canvas =
Canvas(self.tk,width=400,height=500,highlightthickness=0)
self.canvas.pack()
self.tk.update()
self.bg = PhotoImage(file="bg.gif")
self.canvas.create_image(0,0,image=self.bg,anchor='nw')
self.score_txt = self.canvas.create_text(320,12,text='Score: 0', \
fill='white',font=('Fixedsys',17))
self.high_txt = self.canvas.create_text(310,30, \
text='HighScore: %s' % self.high,\
fill='white',font=('Fixedsys',16))
self.player_img = PhotoImage(file='player\\player0.gif')
self.player = self.canvas.create_image(150,396, \
image=self.player_img,
anchor='nw')
self.rain_img = [PhotoImage(file='rain\\rain1.gif'),
PhotoImage(file='rain\\rain2.gif'),
PhotoImage(file='rain\\rain3.gif')]
self.images_left = [
PhotoImage(file="player\\playerL1.gif"),
PhotoImage(file="player\\playerL2.gif"),
PhotoImage(file="player\\playerL3.gif")]
self.images_right = [
PhotoImage(file="player\\playerR1.gif"),
PhotoImage(file="player\\playerR2.gif"),
PhotoImage(file="player\\playerR3.gif")]
self.current_image = 0
self.current_image_add = 1
self.last_time = time.time()
self.new_high = False
self.player_x = 0
self.rain_drops = []
self.rain_count = 0
self.points = 0
self.speed = 2
self.list = list(range(0,9999))
self.needed_rain_count = 100
self.running = True
self.canvas.bind_all('<KeyPress-Left>',self.left)
self.canvas.bind_all('<KeyPress-Right>',self.right)
def animate(self):
if self.player_x != 0 :
if time.time() - self.last_time > 0.1:
self.last_time = time.time()
self.current_image += self.current_image_add
if self.current_image >= 2:
self.current_image_add = -1
if self.current_image <= 0:
self.current_image_add = 1
if self.player_x < 0:
self.canvas.itemconfig(self.player,
image=self.images_left[self.current_image])
elif self.player_x > 0:
self.canvas.itemconfig(self.player,
image=self.images_right[self.current_image])
def score(self):
self.points += 1
if self.points > int(self.high):
self.canvas.itemconfig(self.high_txt, \
text='HighScore: %s' % self.points)
self.new_high = True
self.canvas.itemconfig(self.score_txt, \
text='Score: %s' % self.points)
if self.points >= 20 and self.points <= 34:
self.needed_rain_count = 80
elif self.points >= 35 and self.points <= 49:
self.needed_rain_count = 60
elif self.points >= 50 and self.points <= 64:
self.needed_rain_count = 40
elif self.points >= 65 and self.points <= 79:
self.needed_rain_count = 20
def gameover(self):
self.running = False
self.canvas.create_text(200,250,text='GAMEOVER')
if self.new_high == True:
new_high = open('HIGHSCORE.txt','w')
new_high.write(str(self.points))
new_high.close()
self.high_file.close()
self.canvas.bind_all('<KeyPress-Up>',self.restart) #restart func
is called
self.high_file.close()
def restart(self,evt):
#insert code for restarting game here
def left(self,evt):
self.player_x = -2
def right(self,evt):
self.player_x = 2
def move(self):
self.animate()
self.canvas.move(self.player,self.player_x,0)
pos = self.canvas.coords(self.player)
if pos[0] <= -8:
self.player_x = 0
elif pos[0] >= self.canvas.winfo_width() - 40:
self.player_x = 0
def rain(self):
if self.rain_count == self.needed_rain_count:
for x in self.list:
x =self.canvas.create_image(random.randint(0,501), \
0,image=random.choice(self.rain_img))
self.rain_drops.append(x)
self.rain_count = 0
self.list.remove(x)
break
def rain_fall(self,id):
co = self.canvas.coords(id)
if co[1] > 500:
self.rain_drops.remove(id)
self.canvas.delete(id)
self.score()
self.canvas.move(id,0,self.speed)
def rain_hit(self,id):
rpos = self.canvas.coords(id)
ppos = self.canvas.coords(self.player)
if rpos[1] >= ppos[1] and rpos[1] <= ppos[1]+68:
if rpos[0] >= ppos[0] and rpos[0] <= ppos[0]+48:
self.gameover()
def mainloop(self):
PlaySound("sound\\rain.wav", SND_ASYNC)
while 1:
if self.running == True:
self.rain_count += 1
self.move()
self.rain()
for rain in self.rain_drops:
self.rain_hit(rain)
self.rain_fall(rain)
self.tk.update_idletasks()
self.tk.update()
time.sleep(0.01)
g = Game()
g.mainloop()
You could juste remove the while 1 from the mainloop method and use a boolean to keep looping. You could switch that boolean to false when the UP key is pressed and when the player is hit by the rain.
Then you can just do :
while 1 :
g = Game()
g.mainloop()
and your game will loop for ever.
You write the mainloop() inside the init function not outside the init function. if you write the main loop outside the tkinter window creation part then the init function cannot find the mainloop which is essential to define all the code inside window creation part and mainloop .
You write mainloop after self.canvas.bind_all('<KeyPress-Right>',self.right) part.

Python Tkinter 2 classes

I have 2 classes, i create the instance of each class like this
if __name__ == '__main__':
root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.focus_set() # <-- move focus to this widget
root.geometry("%dx%d+0+0" % (w, h))
root.title("Circus ticketing system")
UI_Class = GUI(root)
Program_Class = Program()
root.mainloop()
i set variables in GUI like this:
self.tenAmShowBtn = Button(self.fMain,text='10am Show',command=Program_Class.tenAmShow)
self.tenAmShowBtn.grid(column=0,row=2)
and to access variables in (ex)GUI from Program i do like this:
UI_Class.tenAmShowBtn.config(bg='#00f0ff')
but i keep getting errors like NameError: name 'UI_Class' is not defined
and NameError: name 'Program_Class' is not defined
and AttributeError: 'GUI' object has no attribute 'tenAmShowBtn'i don't understand how the hell do i do this, the Program class continuosly has to access the GUI and edit variables and widgets, but i can't access variables, keep getting error after error, it woks great when i put everything in 1 class, i just CAN'T get this to work with 2 separete classes.
I put the whole code because i cannot explain in just bits of code, i aplogise.
try:
# for Python2
from Tkinter import *
except ImportError:
# for Python3
from tkinter import *
from random import randint
#from user import Program
class GUI:
def __init__(self, parent):
self.seats_being_bought1 = IntVar()#Light-Blue buttons number for textvariable
self.seats_being_bought1.set(0)
self.seats_being_bought2 = IntVar()#Light-Blue buttons number for textvariable
self.seats_being_bought2.set(0)
self.seats_being_bought3 = IntVar()#Light-Blue buttons number for textvariable
self.seats_being_bought3.set(0)
self.available_seats1 = IntVar() #Non-red buttons
self.available_seats1.set("0")
self.available_seats2 = IntVar() #Non-red buttons
self.available_seats2.set(0)
self.available_seats3 = IntVar() #Non-red buttons
self.available_seats3.set(0)
self.pricePerTicket1 = IntVar()
self.pricePerTicket1.set(5)
self.pricePerTicket2 = IntVar()#<> Ticket/Seat Price Variables
self.pricePerTicket2.set(5)
self.pricePerTicket3 = IntVar()
self.pricePerTicket3.set(12)
self.totalPrice1 = IntVar()
self.totalPrice1.set(0)
self.totalPrice2 = IntVar()#total price variables
self.totalPrice2.set(0)
self.totalPrice3 = IntVar()
self.totalPrice3.set(0)
self.totalTicketsSold = IntVar()#Total seats sold
self.totalTicketsSold.set(0)
self.totalIncome = IntVar()#Total income
self.totalIncome.set(0)
self.white = '#ffffff' #save time
self.currentShow = StringVar()#currrent show
self.instructions_text = 'Select the show you desire, then click one of the seats to the right and click the buy button'
self.button_list1={} #all seats(Buttons)as matrix/object
self.button_list2={}
self.button_list3={}
self.total_seats1 = StringVar()
self.total_seats2 = StringVar()
self.total_seats3 = StringVar()
self.total_seats1.set('/250')
self.total_seats2.set('/150')
self.total_seats3.set('/150')
self.selected_button_list1=[] #Light-Blue buttons
self.selected_button_list2=[] #Light-Blue buttons
self.selected_button_list3=[] #Light-Blue buttons
self.previousTransactionButtonList1 = []#List of Seats in the last transaction
self.previousTransactionButtonList2 = []
self.previousTransactionButtonList3 = []
self.automatic_seat_selection_no = IntVar()#automatic seat selection number
self.automatic_seat_selection_no.set(0)
self.f1 = Frame(parent) #Frame for Seats/Buttons
self.seatTitle = Label(self.f1,bg=self.white,textvariable=self.currentShow).grid(row=0,column=0,columnspan=11,sticky=E+W)
self.f1.grid(column=2,row=0)
self.f2 = Frame(parent) #Frame for Seats/Buttons
self.seatTitle = Label(self.f2,bg=self.white,textvariable=self.currentShow).grid(row=0,column=0,columnspan=11,sticky=E+W)
self.f2.grid(column=2,row=0)
self.f3 = Frame(parent) #Frame for Seats/Buttons
self.seatTitle = Label(self.f3,bg=self.white,textvariable=self.currentShow).grid(row=0,column=0,columnspan=11,sticky=E+W)
self.f3.grid(column=2,row=0)
self.f2.grid_remove() #Hide other 2 frames
self.f3.grid_remove() #Hide other 2 frames
#self.create_UI(parent)
#START WITH 10AM SHOW
#self.currentShow.set('10Am Show')
#self.tenAmShowBtn.config(bg='#00f0ff')
Program.tenAmShow(Program)
Program.displaySeats(10)#10 refers to 10am show
#CREATING OTHER SEATS BUT THEIR FRAMES ARE HIDDEN
Program.displaySeats(3)
Program.displaySeats(8)
def create_UI(self,parent):
self.fMain = Frame(parent)#Frame for rest of program
fLegend = Frame(self.fMain)#Frame for Legend
legendLabel = Label(fLegend,text='Legend').grid(column=0,row=0,columnspan=3,sticky=E+W)
legendRed = Label(fLegend,text='seat123',fg='#ff0000',bg='#ff0000').grid(column=0,row=1,sticky=E+W)
legendRed1 = Label(fLegend,text=' =').grid(column=1,row=1)
legendRed2 = Label(fLegend,text='Taken Seat').grid(column=2,row=1)
legendLightRed = Label(fLegend,text='seat123',fg='#f99999',bg='#f99999').grid(column=0,row=2,sticky=E+W)
legendLightRed1 = Label(fLegend,text=' =').grid(column=1,row=2)
legendLightRed2 = Label(fLegend,text='Bought Seat').grid(column=2,row=2)
legendLightBlue = Label(fLegend,text='seat123',fg='#00f0ff',bg='#00f0ff').grid(column=0,row=3,sticky=E+W)
legendLightBlue1 = Label(fLegend,text=' =').grid(column=1,row=3)
legendLightBlue2 = Label(fLegend,text='Selected Seat').grid(column=2,row=3)
fLegend.grid(column=0,row=0,columnspan=3)
#end Legend frame
self.instructions = Label(self.fMain, text=self.instructions_text).grid(column=0,row=1,columnspan=3)
#Show Selection gui
self.tenAmShowBtn = Button(self.fMain,text='10am Show',command=Program_Class.tenAmShow)
self.tenAmShowBtn.grid(column=0,row=2)
self.threePmShowBtn = Button(self.fMain,text='3pm Show',command=Program_Class.threePmShow)
self.threePmShowBtn.grid(column=1,row=2)
self.eightPmShowBtn = Button(self.fMain,text='8Pm Show',command=Program_Class.eightPmShow)
self.eightPmShowBtn.grid(column=2,row=2)
#Purchase details and commands gui
self.seat_amountLabel = Label(self.fMain, text='Amount of seats Available').grid(column=0, row=3)
self.seat_amountEntry = Label(self.fMain, textvariable=self.available_seats1,bg="#444444",fg='#ffffff',anchor=E)
self.seat_amountEntry.grid(column=1,row=3,sticky=E+W)
self.seat_amountTotal = Label(self.fMain,textvariable=self.total_seats1 ,bg='#444444',fg='#ffffff',anchor=W)
self.seat_amountTotal.grid(column=2,row=3,sticky=E+W)
self.seatsBeingBoughtLabel = Label(self.fMain,text='Amount of seats being purchased').grid(column=0,row=4)
self.seatsBeingBoughtVal = Label(self.fMain, textvariable=self.seats_being_bought1,bg="#444444",fg='#ffffff')
self.seatsBeingBoughtVal.grid(column=1,row=4,columnspan=2,sticky=E+W)
#price per ticket
self.pricePerTicketLabel = Label(self.fMain,text='Cost per Ticket/Seat in $').grid(column=0,row=5)
self.pricePerTicketVal = Label(self.fMain,textvariable=self.pricePerTicket1,bg='#ffffff',fg='#444444')
self.pricePerTicketVal.grid(column=1,row=5,columnspan=2,sticky=E+W)
#total price
self.totalPriceLabel = Label(self.fMain,text='Total Price in $').grid(column=0,row=6)
self.totalPriceVal = Label(self.fMain,textvariable=self.totalPrice1,bg='#ffffff',fg='#444444')
self.totalPriceVal.grid(column=1,row=6,columnspan=2,sticky=E+W)
#Automatically select seats
self.auto_seat_selection_label = Label(self.fMain,text='Amount of seats to buy:').grid(column=0,row=7)
self.auto_seat_selection_entry = Entry(self.fMain, textvariable=self.automatic_seat_selection_no).grid(column=1,row=7,columnspan=2,sticky=E+W)
#cancel and purchase button
self.resetBtn = Button(self.fMain,text="Cancel/Reset",bg='#ff0000',command=Program_Class.CancelTransaction).grid(column=0,row=8,columnspan=1,sticky=E+W)
self.buyTicketsBtn = Button(self.fMain,text="Buy ticket",bg='#00f0ff',command=Program_Class.click_function).grid(column=1,row=8,columnspan=2,sticky=E+W)
#totals
self.totalTicketsSoldLabel = Label(self.fMain,text='Total tickets sold:').grid(column=0,row=9)
self.totalTicketsSoldVal = Label(self.fMain,textvariable=self.totalTicketsSold).grid(column=1,row=9,columnspan=2)
self.totalIncomeLabel = Label(self.fMain,text='Total Income for the Day in $:').grid(column=0,row=10)
self.totalIncomeVal = Label(self.fMain,textvariable=self.totalIncome).grid(column=1,row=10,columnspan=2)
self.fMain.grid(column=1,row=0,sticky=N)
class Program:
def __init__(self):
print('test')
def click_function(self): #Buy Button click
show = self.currentShow.get()
action = 0
inputed_seat = self.automatic_seat_selection_no.get()
#for automatic seat selection
if(inputed_seat != 0):
if(show == '10Am Show'):
button_list = self.button_list1
available_seats = self.available_seats1.get()
elif(show == '3Pm Show'):
button_list = self.button_list2
available_seats = self.available_seats2.get()
else:
button_list = self.button_list3
available_seats = self.available_seats3.get()
counter_var = 1
seat = 1
while counter_var <= inputed_seat:#i use while loop instead of for loop so i can continue/extend the loop if i find a taken/red seat
if(inputed_seat > available_seats):
print('Not enough seats available')
break
else:
if seat in button_list:
if(button_list[seat]['bg'] != '#f99999' and button_list[seat]['bg'] != '#00f0ff'):
self.SeatClick(seat)
else:
counter_var -= 1
else:#seat is taken/red
if(available_seats == 0):
print('Not enough seats available')
break
else:
counter_var -= 1
counter_var += 1
seat += 1
self.automatic_seat_selection_no.set(0)
self.click_function()
else:#for manual seat selection
if(show == '10Am Show'):
action = len(self.selected_button_list1)
elif(show == '3Pm Show'):
action = len(self.selected_button_list2)
else:
action = len(self.selected_button_list3)
if(action != 0):
if(show == '10Am Show'):
del self.previousTransactionButtonList1[:]#Clear last transaction
for i in range(len(self.selected_button_list1)):
self.button_list1[self.selected_button_list1[i]].config(bg='#f99999')
self.available_seats1.set(self.available_seats1.get() - 1)
#save to previous transactions
self.previousTransactionButtonList1.append(self.button_list1[self.selected_button_list1[i]])
self.selected_button_list1 = []
self.seats_being_bought1.set(0)
self.totalPrice1.set(0)
elif(show == '3Pm Show'):
del self.previousTransactionButtonList2[:]#Clear last transaction
for i in range(len(self.selected_button_list2)):
self.button_list2[self.selected_button_list2[i]].config(bg='#f99999')
self.available_seats2.set(self.available_seats2.get() - 1)
#save to previous transactions
self.previousTransactionButtonList2.append(self.button_list2[self.selected_button_list2[i]])
self.selected_button_list2 = []
self.seats_being_bought2.set(0)
self.totalPrice2.set(0)
else:
del self.previousTransactionButtonList3[:]#Clear last transaction
for i in range(len(self.selected_button_list3)):
self.button_list3[self.selected_button_list3[i]].config(bg='#f99999')
self.available_seats3.set(self.available_seats3.get() - 1)
#save to previous transactions
self.previousTransactionButtonList3.append(self.button_list3[self.selected_button_list3[i]])
self.selected_button_list3 = []
self.seats_being_bought3.set(0)
self.totalPrice3.set(0)
#get total seats sold INITIAL ONLY, SPECIFIC FUNCTION AT END OF PROGRAM
self.resetVal()
else:
print('No Seats Selected!')
def CancelTransaction(self):
show = self.currentShow.get()
if(show == '10Am Show' and len(self.previousTransactionButtonList1) >= 1):
for x in range(len(self.previousTransactionButtonList1)):
self.previousTransactionButtonList1[x].config(bg='SystemButtonFace')#make button return to available color
self.available_seats1.set(self.available_seats1.get() + 1)#Adjust available seat counter
self.totalTicketsSold.set(self.totalTicketsSold.get() - 1)
self.resetVal()
del self.previousTransactionButtonList1[:]#delete/clear previous transaction
elif(show == '3Pm Show' and len(self.previousTransactionButtonList2) >= 1):
for x in range(len(self.previousTransactionButtonList2)):
self.previousTransactionButtonList2[x].config(bg='SystemButtonFace')
self.available_seats2.set(self.available_seats2.get() + 1)#Adjust available seat counter
self.totalTicketsSold.set(self.totalTicketsSold.get() - 1)
self.resetVal()
del self.previousTransactionButtonList2[:]#delete/clear previous transaction
elif(show == '8Pm Show' and len(self.previousTransactionButtonList3) >= 1):
for x in range(len(self.previousTransactionButtonList3)):
self.previousTransactionButtonList3[x].config(bg='SystemButtonFace')
self.available_seats3.set(self.available_seats3.get() + 1)#Adjust available seat counter
self.totalTicketsSold.set(self.totalTicketsSold.get() - 1)
self.resetVal()
del self.previousTransactionButtonList3[:]#delete/clear previous transaction
else:
print('no previous transaction found')
def seatCounter(self,taken,show): #to count available seats
if(show == 1):
self.available_seats1.set(self.available_seats1.get() - taken)
elif(show == 2):
self.available_seats2.set(self.available_seats2.get() - taken)
else:
self.available_seats3.set(self.available_seats3.get() - taken)
#just to initially update the variables
def resetVal(self):
ticketSold1 = 250 - self.available_seats1.get()
ticketSold2 = 150 - self.available_seats2.get()
ticketSold3 = 150 - self.available_seats3.get()
self.totalTicketsSold.set(ticketSold1 + ticketSold2 + ticketSold3)
self.totalIncome.set((ticketSold1 * 5) + (ticketSold2 * 5) + (ticketSold3 * 12))
#CLICK ON SEAT/BUTTON
def SeatClick(self,seat_no):
show = self.currentShow.get()
action = 0
if(show == '10Am Show'):
action = self.button_list1[seat_no]['bg']
elif(show == '3Pm Show'):
action = self.button_list2[seat_no]['bg']
elif(show == '8Pm Show'):
action = self.button_list3[seat_no]['bg']
else:
return False
if(action == '#f99999'):
print('already bought')
else:
if(show == '10Am Show'):
if(seat_no in self.selected_button_list1):#IF Seat/Button already selected, then remove .after(1000, self.update_clock)
self.button_list1[seat_no].config(bg='SystemButtonFace')
self.selected_button_list1.remove(seat_no)
self.seats_being_bought1.set(str(len(self.selected_button_list1)))
self.totalPrice1.set(self.pricePerTicket1.get() * len(self.selected_button_list1))
else:
self.button_list1[seat_no].config(bg='#00f0ff')#IF Seat/Button not selected then toggle it
self.selected_button_list1.append(seat_no)
self.seats_being_bought1.set(str(len(self.selected_button_list1)))
self.totalPrice1.set(self.pricePerTicket1.get() * len(self.selected_button_list1))
elif(show == '3Pm Show'):
if(seat_no in self.selected_button_list2):
self.button_list2[seat_no].config(bg='SystemButtonFace')#IF Seat/Button already selected, then remove
self.selected_button_list2.remove(seat_no)
self.seats_being_bought2.set(str(len(self.selected_button_list2)))
self.totalPrice2.set(self.pricePerTicket2.get() * len(self.selected_button_list2))
else:
self.button_list2[seat_no].config(bg='#00f0ff')#IF Seat/Button not selected then toggle it
self.selected_button_list2.append(seat_no)
self.seats_being_bought2.set(len(self.selected_button_list2))
self.totalPrice2.set(self.pricePerTicket2.get() * len(self.selected_button_list2))
else:
if(seat_no in self.selected_button_list3):
self.button_list3[seat_no].config(bg='SystemButtonFace')#IF Seat/Button already selected, then remove
self.selected_button_list3.remove(seat_no)
self.seats_being_bought3.set(str(len(self.selected_button_list3)))
self.totalPrice3.set(self.pricePerTicket3.get() * len(self.selected_button_list3))
else:
self.button_list3[seat_no].config(bg='#00f0ff')#IF Seat/Button not selected then toggle it 613553
self.selected_button_list3.append(seat_no)
self.seats_being_bought3.set(len(self.selected_button_list3))
self.totalPrice3.set(self.pricePerTicket3.get() * len(self.selected_button_list3))
# SHOW SELECTION
def tenAmShow(self):
UI_Class.tenAmShowBtn.config(bg='#00f0ff')
self.threePmShowBtn.config(bg=self.white)
self.eightPmShowBtn.config(bg=self.white)
self.currentShow.set('10Am Show')
self.seat_amountEntry.config(textvariable = self.available_seats1)
self.seatsBeingBoughtVal.config(textvariable=self.seats_being_bought1)
self.pricePerTicketVal.config(textvariable=self.pricePerTicket1)
self.totalPriceVal.config(textvariable=self.totalPrice1)
self.seat_amountTotal.config(textvariable=self.total_seats1)
self.f1.grid()
self.f2.grid_remove()
self.f3.grid_remove()
def threePmShow(self):
self.threePmShowBtn.config(bg='#00f0ff')
self.tenAmShowBtn.config(bg=self.white)
self.eightPmShowBtn.config(bg=self.white)
self.currentShow.set('3Pm Show')
self.seat_amountEntry.config(textvariable = self.available_seats2)
self.seatsBeingBoughtVal.config(textvariable=self.seats_being_bought2)
self.pricePerTicketVal.config(textvariable=self.pricePerTicket2)
self.totalPriceVal.config(textvariable=self.totalPrice2)
self.seat_amountTotal.config(textvariable=self.total_seats2)
self.f1.grid_remove()
self.f2.grid()
self.f3.grid_remove()
def eightPmShow(self):
self.eightPmShowBtn.config(bg='#00f0ff')
self.tenAmShowBtn.config(bg=self.white)
self.threePmShowBtn.config(bg=self.white)
self.currentShow.set('8Pm Show')
self.seat_amountEntry.config(textvariable = self.available_seats3)
self.seatsBeingBoughtVal.config(textvariable= self.seats_being_bought3)
self.pricePerTicketVal.config(textvariable=self.pricePerTicket3)
self.totalPriceVal.config(textvariable=self.totalPrice3)
self.seat_amountTotal.config(textvariable=self.total_seats3)
self.f1.grid_remove()
self.f2.grid_remove()
self.f3.grid()
#BUTTON/SEAT CREATION AND DISPLAY
def createSeats(self,num_of_seats,frame_pointer):
col = num_of_seats / 10
if(frame_pointer == 1):
seat_counter1 = 1
for x in range(int(col)):
X = x + 10
for y in range(1, 11):
taken_seats = randint(1,3)
if(taken_seats == 3):
b1 = Button(
self.f1, text='Seat%d' % seat_counter1,
name='seat%d' % seat_counter1, bg='#ff0000'
)
b1.grid(row=X, column=y)
seat_counter1 += 1
self.seatCounter(1, 1)
else:
b1 = Button(
self.f1, text='Seat%d' % seat_counter1,
name='seat%d' % seat_counter1,command= lambda j = seat_counter1: self.SeatClick(j)
)
b1.grid(row=X, column=y)
self.button_list1[seat_counter1] = b1
seat_counter1 += 1
elif(frame_pointer == 2):
seat_counter2 = 1
for x in range(int(col)):
X = x + 10
for y in range(1, 11):
taken_seats = randint(1,3)
if(taken_seats == 3):
b2 = Button(
self.f2, text='Seat%d' % seat_counter2,
name='seat%d' % seat_counter2, bg='#ff0000'
)
b2.grid(row=X, column=y)
seat_counter2 += 1
self.seatCounter(1, 2)
else:
b2 = Button(
self.f2, text='Seat%d' % seat_counter2,
name='seat%d' % seat_counter2,command= lambda j = seat_counter2: self.SeatClick(j)
)
b2.grid(row=X, column=y)
self.button_list2[seat_counter2] = b2
seat_counter2 += 1
else:
seat_counter3 = 1
for x in range(int(col)):
X = x + 10
for y in range(1, 11):
taken_seats = randint(1,3)
if(taken_seats == 3):
b3 = Button(
self.f3, text='Seat%d' % seat_counter3,
name='seat%d' % seat_counter3, bg='#ff0000'
)
b3.grid(row=X, column=y)
seat_counter3 += 1
self.seatCounter(1, 3)
else:
b3 = Button(
self.f3, text='Seat%d' % seat_counter3,
name='seat%d' % seat_counter3,command= lambda j = seat_counter3: self.SeatClick(j)
)
b3.grid(row=X, column=y)
self.button_list3[seat_counter3] = b3
seat_counter3 += 1
def displaySeats(self,show):
if(show == 10):
self.available_seats1.set(250)
self.createSeats(250, 1)
elif(show == 3):
self.available_seats2.set(150)
self.createSeats(150, 2)
else:
self.available_seats3.set(150)
self.createSeats(150, 3)
self.resetVal()
if __name__ == '__main__':
root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
#root.overrideredirect(1) #removes menubar
root.focus_set() # <-- move focus to this widget
root.geometry("%dx%d+0+0" % (w, h))
root.title("Circus ticketing system")
UI_Class = GUI(root)
Program_Class = Program()
root.mainloop()
the Program_Class object is instantiated after the UI_Class object and the latter doesn't reference the prior. Try passing Program_Class to the GUI constructor:
Program_Class = Program()
UI_Class = GUI(root, Program_Class)
where:
class GUI:
def __init__(self, parent, program):
self.program = program
then you should be able to reference self.program.

Categories