I'm trying to make a chess AI. I need a chess set in order to play. I made the pieces (they can be dragged), board, clock, etc. Right now, I am trying to make the clock alternate when someone plays. During one of my tests to make the clock alternate, the clock for the ai suddenly disappeared. I checked if I forgot anything like did I add the grid method, put it in the root, saved, etc. I did all of them. I tried changing the numbers in row. It was originally 0. I tried 1 and it reappeared. But it was not in the place I wanted, so I changed the number again from 1 to 2 to see if it still works. But it disappeared. I put back one but it was still gone(I checked twice if I saved). Here's the code for the ai clock:
from root_frame import Root
from tkinter import Label
ai_turn = False
class AI:
def __init__(self):
self.the_screen = Root()
self.ai_label = Label(self.the_screen.root, text = "AI", font = "Helvetica 18 bold", width = 40)
self.ai_clock = Label(self.the_screen.root, font = "Helvetica 18 bold")
def set_timer(self):
self.t = 600
return self.t
def countdown(self):
global ai_turn
if self.t > 0 and ai_turn == True:
self.convert()
self.t = self.t - 1
self.ai_clock.after(1000, lambda: self.countdown())
print("it's running dum dum")
elif self.t == 0:
self.ai_clock.config(text = "ai loose")
elif ai_turn == False:
print("it's not running dum dum")
self.t = self.t
def convert(self):
self.seconds = self.t % (24 * 3600)
self.seconds %= 3600
self.minutes = self.t // 60
self.seconds %= 60
self.ai_clock.config(text = "%02d:%02d" % (self.minutes, self.seconds))
def stop(self):
global ai_turn
ai_turn = False
if ai_turn == False:
print("ai stopped, value: {}".format(ai_turn))
def go(self):
global ai_turn
ai_turn = True
if ai_turn == True:
print("ai active, value: {}".format(ai_turn))
And here is where I grid them:
class Screen:
def __init__(self):
self.AI = AI()
self.Human = Human()
self.AI.ai_clock.grid(row = 0, column = 9)
self.Human.human_clock.grid(row = 7, column = 8)
"Run functions to activate the clocks"
self.AI.set_timer()
self.AI.countdown()
self.Human.set_timer()
self.Human.countdown()
And finally, here is root_frame:
from tkinter import Tk
class Root:
root = Tk()
(There's indent because it was in a method)Thanks!
You call self.AI.countdown() exactly once. In it, you only start the timer if self.t>0 and ai_turn == True. However, ai_turn is False, so the timer never starts. If you set ai_turn to True at the start of the program the timer will work.
All you have to do to discover this is put a print statement right before your if statement to verify your assumptions about those variables. Also, if you give the label a distinct color you will see that it is on the screen but very small because by default it has no text in it.
In other words, the label appears just fine, and is updated properly as long as you have the logic in place to start the timer.
Related
I just recently made a pomodoro clock implementation on python and I was facing some trouble with its execution but I came up with a solution for it but I observed a discrepancy in its execution.
So my whole code and implementation looks something like this (my code is not clean or pretty; sorry about that, I am one of those people who uses a mixture of tabs and spaces):
from tkinter import *
from time import *
from threading import Thread
# ---------------------------- CONSTANTS ------------------------------- #
PINK = "#e2979c"
RED = "#e7305b"
GREEN = "#9bdeac"
YELLOW = "#f7f5dd"
FONT_NAME = "Courier"
WORK_MIN = 25
SHORT_BREAK_MIN = 5*60000
LONG_BREAK_MIN = 20*60000
class Reset:
def __init__(self):
self.click = False
self.activation = False
def button_command(self):
self.click = True
def timer_command(self):
return self.click
# ---------------------------- TIMER RESET ------------------------------- #
timer_reset = Reset()
reset = timer_reset.click
# ---------------------------- TIMER MECHANISM ------------------------------- #
def timer_mechanism():
minute=0
if counter[0]<60000:
if counter[0] % 1000 == 0 and counter[0]/1000<10:
canvas.itemconfig(clock_counter, text=(f"00:0{int(counter[0]/1000)}"))
canvas.update_idletasks()
elif counter[0]% 1000==0 and counter[0]/1000>=10:
canvas.itemconfig(clock_counter, text=(f"00:{int(counter[0] / 1000)}"))
canvas.update_idletasks()
elif counter[0]>=60000:
if counter[0]%1000==0:
transition=int(counter[0]/1000)
while transition>=60:
minute+=1
transition-=60
second=transition
if second<10 and minute<10:
canvas.itemconfig(clock_counter,text=(f"0{minute}:0{second}"))
canvas.update_idletasks()
elif second>=10 and minute<10:
canvas.itemconfig(clock_counter,text=(f"0{minute}:{second}"))
canvas.update_idletasks()
elif minute>=10 and second<10:
canvas.itemconfig(clock_counter, text=(f"{minute}:0{second}"))
canvas.update_idletasks()
else:
canvas.itemconfig(clock_counter,text=(f"{minute}:{second}"))
canvas.update_idletasks()
# -----------------------------New countdown mechanism----------------------------#
i = [4]
def start_countdown():
if not timer_reset.click and i[0] > 0:
# keep listening to the function that receives from the button while doing an after for the 25 min and 10 min respectively.3
timer.config(text="Work", fg=GREEN)
window.update_idletasks()
# the solution to this problem is to break up the after process instead of doing it all at the same time make use of small increments so that this after could finish withing a second or two and then go to another function that contains all the timer_reset.click values update and come everytime until we reach a limit somewhere probably an array value that will be updated until we get to the 25 minute mark so this insures as the signal from the button will be accepted.
execute("Work")
# start=time()
# end=time()
# while end-start<5:
# end=time()
# timer_reset.click=timer_reset.timer_command()
else:
window.destroy()
timer_reset.click = False
i[0] = 4
beginning()
def rest():
global start_time
start_time=0
timer_reset.activation = True
if not timer_reset.click:
global restloop
restloop = True
global workloop
workloop = False
timer.config(text="Break", fg=PINK)
if i[0] == 4:
global frame
frame = Frame(window, width=20, height=20)
frame.grid(row=3, column=1)
tick = Label(frame, text="✔", font=(FONT_NAME, 12, "bold"), bg=YELLOW, fg=GREEN)
tick.pack(side=LEFT)
window.update_idletasks()
if i[0] > 0:
execute("Break")
if timer_reset.click:
timer_reset.click = False
window.destroy()
beginning()
else:
window.destroy()
beginning()
else:
window.destroy()
i[0] = 4
timer_reset.click = False
beginning()
i[0] -= 1
counter=[0]
def execute(identifier):
if identifier=="Work":
window.after(1,mirror,LONG_BREAK_MIN)
if identifier=="Break":
window.after(1,mirror,SHORT_BREAK_MIN)
def mirror(value):
if timer_reset.click:
window.destroy()
i[0]=4
timer_reset.click=False
counter[0]=0
beginning()
elif counter[0]<value:
counter[0]+=1
timer_mechanism()
if value==LONG_BREAK_MIN:
execute("Work")
if value==SHORT_BREAK_MIN:
execute("Break")
elif value==LONG_BREAK_MIN:
counter[0]=0
window.deiconify()
window.attributes("-topmost",1)
window.after(300,window.attributes,"-topmost",0)
window.update()
rest()
elif value==SHORT_BREAK_MIN:
counter[0]=0
window.deiconify()
window.attributes("-topmost",1)
window.after(300,window.attributes,"-topmost",0)
window.update()
start_countdown()
# ---------------------------- UI SETUP ------------------------------- #
def beginning():
global window
window = Tk(className="Pomodoro")
window.title("Pomodoro")
window.config(pady=50, padx=100, bg=YELLOW)
window.wm_state("normal")
global timer
timer = Label(text="Timer", font=(FONT_NAME, 50, "bold"), bg=YELLOW, foreground=GREEN)
timer.grid(row=0, column=1)
global canvas
canvas = Canvas(width=200, height=223, bg=YELLOW, highlightthickness=0)
tomato = PhotoImage(file="tomato.png")
canvas.create_image(100, 100, image=tomato)
global clock_counter
clock_counter=canvas.create_text(100, 120, text="00:00", fill="white", font=(FONT_NAME, 35, "bold"))
canvas.grid(row=1, column=1)
start = Button(text="Start", highlightthickness=0, borderwidth=0, command=start_countdown)
start.grid(row=2, column=0)
end = Button(text="Reset", highlightthickness=0, borderwidth=0, command=timer_reset.button_command)
end.grid(row=2, column=2)
window.mainloop()
beginning()
And the UI part of my code looks something like this:
So the discrepnancy I was talking about was that after starting the program and the countdown is happening if by mistake or whatever reason the user presses the Start button again the countdown starts to happen faster and if you keep pressing the start button without hitting the reset button it starts counting down even faster and faster.
Now I think I understand why this might be but this is like a sliver of what the solution might be, I might be completely wrong for all I know but is it because of the after method since every time the user hits the button he is starting another thread of the after method process and this kind of creates a sense/illusion of multiprocessing(parallelism)? I really don't know, to be honest. I might be stretching too far, but please any type of explanation would really be helpful even if it is a jab at my answer. I am very genuinely curious as to why this is happening.
Any help/suggestion will be really helpful.
So I am making a program that has a timer and the timer works, now I am working with a pause function. After some research, I found a function called after_cancel. This function supposedly should cancel the after function as the after function in this situation creates an infinite loop. How do I use the after_cancel properly in this situation or are there any other possible solutions?
Thanks in advance.
t = 60000
global timerState
timerState = True
def pause():
timerLabel.after_cancel(countdown)
timerState = False
timerButton.config(text="Play", command=countdown)
def countdown():
global t
if t == 0:
timer = "00:00"
timerLabel.config(text=timer)
return
if timerState == False:
timerLabel.after_cancel(countdown)
timerButton.config(text="Play", command=countdown)
return
mins = t / 60000
secs = t / 1000
secs = secs - int(mins) * 60
mills = t
mills = mills - int(secs) * 1000
if timerState == True:
timer = "{:02d}:{:02d}".format(int(mins),int(secs))
timerLabel.config(text=timer)
t -= 1
timerLabel.after(1, countdown)
timerButton.config(text="Pause", command=pause)
Most of the time .after_cancel scripts can be avoided by just using if statements. For example look at this:
import tkinter as tk
t = 60000
def pause():
global timerState
timerState = False
timerButton.config(text="Play", command=start_countdown)
def start_countdown():
global timerState
timerState = True
timerButton.config(text="Pause", command=pause)
countdown()
def countdown():
global t
if timerState:
timerLabel.config(text=t)
t -= 1
if t > 0:
timerLabel.after(1, countdown)
root = tk.Tk()
timerLabel = tk.Label(root, text="")
timerLabel.pack()
timerButton = tk.Button(root, text="Play", command=start_countdown)
timerButton.pack()
root.mainloop()
I modified your code to show t without making it in the mm:ss format. The main point is that if timerState is False the timerLabel.after(1, countdown) will never be called so there is no point to having a .after_cancel.
Note: You haven't considered the time taken for your other code so t isn't really in milliseconds (at least for my slow computer).
Here is a demonstration of after and after_cancel
Every after needs to be cancelled in order to clear the event queue.
In this program, each time the button is pressed a time delay event is generated.
The event ID is stored in self.after_time
I have set the delay value to increase by 100 ms with each button press, for demo purposes.
it withdraws the master from view.
When the time delay event is complete it calls self.action
self.action cancels the event with after_cancel( self.after_time )
and the master is made visible, ready for the next button press.
import tkinter
class after_demo:
delay = 100
def __init__( self ):
self.master = tkinter.Tk()
self.master.title( 'After Demo' )
self.control = tkinter.Button(
self.master, text = 'Begin Demo',
width = 40, command = self.pause )
self.control.grid(row=0,column=0,sticky='nsew')
def action( self ):
self.master.after_cancel( self.after_time )
self.control[ 'text' ] = 'Delay( {} ) ms'.format( self.delay )
self.master.deiconify()
def pause( self ):
self.after_time = self.master.after( self.delay, self.action )
self.delay += 100
self.master.withdraw()
if __name__ == '__main__':
timer = after_demo( )
tkinter.mainloop()
There's no help on google, I've asked some people as well but none of them seem to know how to answer my question.
I'm programming a GUI for a project, and it contains an RSS-Feed ticker.
It scrolls through the news and when it updates (every 3 seconds for obvious debug reasons) it speeds up a bit.
This means, if I run the program, after two hours the ticker is scrolling at a non-human readable speed.
The main code wasn't written by me, I modified it and added the update function.
main():
import tkinter as tk
from Press import RSSTicker
def displayRSSticker(win):
# Place RSSTicker portlet
tickerHandle = RSSTicker(win, bg='black', fg='white', highlightthickness=0, font=("avenir", 30))
tickerHandle.pack(side='bottom', fill='x', anchor='se')
def main():
# Set the screen definition, root window initialization
root = tk.Tk()
root.configure(background='black')
width, height = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (width, height))
label = tk.Label(root, text="Monitor Dashboard", bg='black', fg='red')
label.pack(side='bottom', fill='x', anchor='se')
# Display portlet
displayRSSticker(root)
# Loop the GUI manager
root.mainloop(0)
###############################
# MAIN SCRIPT BODY PART #
###############################
if __name__ == "__main__":
main()
RSSTicker class:
import feedparser
import tkinter as tk
class RSSTicker(tk.Text):
# Class constructor
def __init__(self, parent, **params):
super().__init__(parent, height=1, wrap="none", state='disabled', **params)
self.newsFeed = feedparser.parse('http://www.repubblica.it/rss/homepage/rss2.0.xml')
self.update()
# Class methods
def update(self):
self.headlineIndex = 0
self.text = ''
self.pos = 0
self.after_idle(self.updateHeadline)
self.after_idle(self.scroll)
self.after(4000, self.update)
def updateHeadline(self):
try:
self.text += ' ' + self.newsFeed['entries'][self.headlineIndex]['title']
except IndexError:
self.headlineIndex = 0
self.text = self.feed['entries'][self.headlineIndex]['title']
self.headlineIndex += 1
self.after(5000, self.updateHeadline)
def scroll(self):
self.config(state='normal')
if self.pos < len(self.text):
self.insert('end', self.text[self.pos])
self.pos += 1
self.see('end')
self.config(state='disabled')
self.after(180, self.scroll)
I thought the problem lied in the self.pos variable, printing it out resulted in it counting up, resetting to 1 and counting up faster.. But it doesn't seem to be problem causing the acceleration of the ticker.
From what I've understood tho, the problem must be in the scroll method.
If someone understand how to keep the original scroll speed when updated, thank you.
I think you can use a couple tracking variables to make sure that update only starts the loop once and then next time update is called it will just run scroll without starting a new loop. At the same time if scroll is not called by update then it will continue looping as needed.
Change your RSSTicker class to the following:
class RSSTicker(tk.Text):
# Class constructor
def __init__(self, parent, **params):
self.scroll_started = False # Tracker for first update.
super().__init__(parent, height=1, wrap="none", state='disabled', **params)
self.newsFeed = feedparser.parse('http://www.repubblica.it/rss/homepage/rss2.0.xml')
self.update()
def update(self):
self.headlineIndex = 0
self.text = ''
self.pos = 0
self.after_idle(self.updateHeadline)
self.after_idle(lambda: self.scroll('update'))
self.after(4000, self.update)
def updateHeadline(self):
try:
self.text += ' ' + self.newsFeed['entries'][self.headlineIndex]['title']
except IndexError:
self.headlineIndex = 0
self.text = self.feed['entries'][self.headlineIndex]['title']
self.headlineIndex += 1
self.after(5000, self.updateHeadline)
def scroll(self, from_after_or_update = 'after'):
self.config(state='normal')
if self.pos < len(self.text):
self.insert('end', self.text[self.pos])
self.pos += 1
self.see('end')
self.config(state='disabled')
# Check if the loop started by after.
if from_after_or_update != 'update':
self.scroll_started = True
self.after(180, self.scroll)
# If not started by after check to see if it is the 1st time loop is started by "update".
elif self.scroll_started is False and from_after_or_update == 'update':
self.scroll_started = True
self.after(180, self.scroll)
# If neither of the above conditions then do not start loop to prevent multiple loops.
else:
print("ran scroll method without adding new after loop!")
I doing a simple python GUI using tkinter to do screen recording.Basically, I am using ffmpeg commands at the backend with tkinter as the front end triggering the ffmpeg commands.There is something that I stuck with.I dont know why my time is unable to trigger off if I program in this way.
The code below is basically the recording method.You will notice that I am actually trying to update my tkinter GUI in the while loop.This method is actually in my class named Gui_Rec() which contains other methods I need for my screen recording program.
def rec(self):
global videoFile
mydate = datetime.datetime.now()
videoFile = mydate.strftime("\%d%b_%Hh%Mm.avi")
self.l['text']=os.path.expanduser('~')+"\Videos"
self.l1['text']=videoFile
self.b.config(state=DISABLED)
self.b1.config(state=ACTIVE)
t = Thread(target=self.rec_thread)#trigger another method using thread which will run ffmpeg commands here
t.start()
while True:
if self.count_flag == False:
break
self.label['text'] = str("%02dm:%02ds" % (self.mins,self.secs))
if self.secs == 0:
time.sleep(0)
else:
time.sleep(1)
if(self.mins==0 and self.secs==1):
self.b1.config(fg="white")
self.b1.config(bg="red")
self.b.config(fg="white")
self.b.config(bg="white")
if self.secs==60:
self.secs=0
self.mins+=1
self.label['text'] = str("%02dm:%02ds" % (self.mins,self.secs))
main.gui.update()
self.secs = self.secs+1
other method in the class Gui_Rec() then this below
def main():
gui = Gui_Rec()
gui.minsize(300,155)
gui.maxsize(390,195)
gui.title("Desktop REC")
gui.attributes("-topmost", 1)
gui.mainloop() #start mainloop of program
if __name__ == '__main__':
main()
Strangely, if I don't put the above section of code in the the def main(), the GUI will be update with the duration of the time running when rec button is pressed.I don't really know how to go about solving this.Tried putting it in another thread yet it doesn't work as well.Thank you everyone for your help.
The while loop is creating a conflict with Tkinter's mainloop. Threading or multiprocessing are solutions, but I'd recommend looking into Tkinter's after() method. Here's a simplified example of how to handle a timer using after:
from Tkinter import *
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.mins = 0
self.secs = 0
# make a stringvar instance to hold the time
self.timer = StringVar()
self.timer.set('%d:%d' % (self.mins, self.secs))
Label(self, textvariable=self.timer).pack()
Button(self, text='Start', command=self._start_timer).pack()
Button(self, text='Stop', command=self._stop_timer).pack()
def _start_timer(self):
self.secs += 1 # increment seconds
if self.secs == 60: # at every minute,
self.secs = 0 # reset seconds
self.mins += 1 # and increment minutes
self.timer.set('%d:%d' % (self.mins, self.secs))
# set up the after method to repeat this method
# every 1000 ms (1 second)
self.repeater = self.after(1000, self._start_timer)
def _stop_timer(self):
self.after_cancel(self.repeater)
root = Tk()
App(root).pack()
mainloop()
I want to set a label in Tkinter using my countdown timer function. Right now all it does is set the lable to "10" once 10 is reached and I don't really understand why. Also, even if I have the timer print to a terminal instead the "Time's up!" bit never prints.
import time
import tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(text="null")
self.label.pack()
self.countdown()
self.root.mainloop()
# Define a timer.
def countdown(self):
p = 10.00
t = time.time()
n = 0
# Loop while the number of seconds is less than the integer defined in "p"
while n - t < p:
n = time.time()
if n == t + p:
self.label.configure(text="Time's up!")
else:
self.label.configure(text=round(n - t))
app=App()
Tkinter already has an infinite loop running (the event loop), and a way to schedule things to run after a period of time has elapsed (using after). You can take advantage of this by writing a function that calls itself once a second to update the display. You can use a class variable to keep track of the remaining time.
import Tkinter as tk
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label = tk.Label(self, text="", width=10)
self.label.pack()
self.remaining = 0
self.countdown(10)
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="time's up!")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
if __name__ == "__main__":
app = ExampleApp()
app.mainloop()