I'm trying to make a mode for a simple game where you catch items as they fall down using tkinter.
In this mode, you have 60 seconds to catch as many items as you can. All the timer methods I've tried pause the whole program
...tried using an empty label, but the .after pauses the whole program
timerlabel = tkinter.Label(text="")
def timer():
global t, timerdisplay
while t > 0:
t -= 1
timerlabel.after(1000)
c.delete(timerdisplay)
timerdisplay = c.create_text(200, 12, text=t)
c.update()
any idea how to do this?
This is the better way to do it, specifically because after(n) freezes the program for the given time period. Create a function that accepts a number and displays that number. Then, it subtracts one and then reschedules itself to run one second in the future until the number becomes zero.
def timer(t):
global timerdisplay
c.delete(timerdisplay)
timerdisplay = c.create_text(200, 12, text=t)
if t >= 1:
c.after(1000, timer, t-1)
timer(timerdisplay, 10)
To optimize this, you can pass in the canvas item along with the number. You can also just reconfigure the text item rather than deleting and restoring it.
def timer(timerdisplay, t):
c.itemconfigure(timerdisplay, text=t)
if t >= 1:
c.after(1000, timer, timerdisplay, t-1)
timerdisplay = c.create_text(200, 12)
timer(timerdisplay, 10)
Related
Hello I am making a timer. Here is my code:
from tkinter import*
import time
Screen=Tk()
Screen.resizable(0,0)
myText=Label(Screen,text="Welcome To X Timer!",font=(None,50),bg="green")
myText.pack()
aText=Label(Screen,text="0:0",font=(None,30))
aText.pack()
def start_timer():
x=1
while(True):
time.sleep(1)
x=x+1
itemconfigure(aText,text=x)
strBTN=Button(Screen,text="Start",bg="purple",font=
("Helvetica",45),command=start_timer)
strBTN.pack()
Screen.mainloop()
But on line 14 is says: Error:itemconfigure is not defined. Please help!
It's unclear exactly what it is you're trying to do, but your start_timer function is an infinite busy loop that will hang your GUI, so I assume that's not it! Maybe you meant to call Tk.after?
def start_timer(x=0):
x+=1
Screen.after(1000, lambda x=x: start_timer(x))
# 1000 is the time (in milliseconds) before the callback should be invoked again
# lambda x=x... is the callback itself. It binds start_timer's x to the scope of
# the lambda, then calls start_timer with that x.
itemconfigure(aText,text=x)
I'm going out on a limb and say that you expect itemconfigure(aText, text=x) to change the text on the label? You should instead be using:
...
aText.config(text=x)
To change the text of a Label you have to use Label's method config(). So, instead of itemconfigure(aText,text=x), do aText.config(text=x). I think itemconfigure() function doesn't exist.
Also, there are other problems. For example, if you define a function with an infinite loop as a button callback, the button will always remain pressed (buttons remain pressed till the callback finishes). That's why I recommend you to use Screen's method after() at the end of the callback, and make it execute the same function.
after() executes a function after the number of milliseconds entered, so Screen.after(1000, function) will pause the execution during a second and execute the function.
Also you can use s variable to store the seconds. When s equals 60, it resets to 0 and increases in 1 the number of minutes (m).
Here you have the code:
from tkinter import*
Screen=Tk()
Screen.resizable(0,0)
myText=Label(Screen,text="Welcome To X Timer!",font=(None,50),bg="green")
myText.pack()
aText=Label(Screen,text="0:0",font=(None,30))
aText.pack()
def start_timer():
global s, m, aText, Screen
aText.config(text = str(m) + ":" + str(s))
s += 1
if s == 60:
s = 0
m += 1
Screen.after(1000,start_timer)
s = 0
m = 0
strBTN=Button(Screen,text="Start",bg="purple",font=("Helvetica",45),command=start_timer)
strBTN.pack()
Screen.mainloop()
This one should work (in my computer it works properly). If you don't understand something just ask it.
So basically, I have text which is typed out character by character. with the code:
text = "test"
delta = 40
delay = 0
for i in range(len(text) + 1):
s = test_string[:i]
update_text = lambda s=s: canvas.itemconfigure(variable, text=s)
canvas.after(delay, update_text)
delay += delta
This is all inside of a function, lets call: def NewEvent(). What I want to do is create a text button with the text "Skip" which changes delta to a lower number, thus speeding up the animation upon click. I cant seem to figure it out, normally when you make text clickable, it has something along the lines of:
skipbutton = canvas.create_text((400,100), activefill="Medium Turquoise", text="Skip", fill="White", font=('Arial', 30), tags="skip")
canvas.tag_bind('skip', '<ButtonPress-1>', function)
The problem is, it needs to stay within the same function. So I thought of creating an if statement similar like this:
if delta is 40 and skip is ____:
delta = 10
However, I dont know what would come after- (skip is) for it to work, or even if this would work at all... Any help would be appreciated.
You are doing animation in a way that makes your problem very difficult to solve. The problem is that you are scheduling all of the frames of animation before you display the first frame. In order to change the speed you would have to cancel all of the pending jobs and recreate new jobs. This is not the proper way to do animation in Tkinter.
A better solution is to only have a single job active at one time. You do this by having a function that displays one frame of animation and then schedules itself to run again in the future.
The general structure looks like this:
def animate():
<draw one frame of animation>
if <there are more frames>:
root.after(delay, animate)
In your case, each "frame" is simply adding one character to a character string, and your condition at the end is to simply check to see if there are more characters.
A simple implementation is to pass a string into the animate function, have it pull the first character off of the string and append it to the display.
For example:
def update_text(text):
char = text[0]
remainder = text[1:]
current_string = canvas.itemcget(variable, "text")
new_string = current_string + char
canvas.itemconfigure(variable, text=new_string)
if len(remainder) > 0:
canvas.after(delay, update_text, remainder)
To start the animation, give it the string you want to display:
update_text("Hello, world")
This this function depends on a global variable for the delay, writing a function to speed the animation up or slow it down only requires that you modify this delay:
def speedup():
global delay
delay = int(delay/2)
You can call it from a button quite easily:
tk.Button(root, text="Speed up!", command=speedup)
I have a game that I've written for my first project and I'd like to have a system where I can play and pause the game. When you click the unpause button, I want it to call a function every 1 second that advances the date. Time.sleep stops the whole program so it's not useful to me and I cant seem to restart threads after I've started one. Here's the advancing day function.
def time():
global timer
timer = threading.Timer(1.0, time)
timer.start()
thirtyonemonths = [1, 3, 5, 7, 8, 10, 12]
thirtymonths = [4, 6, 9, 11]
globalvars.day = globalvars.day + 1
for self in thirtyonemonths:
if self == globalvars.month:
print "hi"
if globalvars.day == 32:
globalvars.day = 1
globalvars.month = globalvars.month + 1
for self in thirtymonths:
if self == globalvars.month:
print "hi"
if globalvars.day == 31:
globalvars.day = 1
globalvars.month = globalvars.month + 1
if globalvars.month == 2:
print "hi"
if globalvars.day == 29:
globalvars.day = 1
globalvars.month = globalvars.month + 1
if globalvars.month == 13:
globalvars.month = 1
globalvars.year = globalvars.year + 1
threading.Thread.time(self)
timer = threading.Timer(1.0, time)
Later I have the code for when the button is clicked that checks if it's paused or not
if b.collidepoint(pos):
if globalvars.ispaused == 1:
globalvars.ispaused = 0
timer.start()
break
if globalvars.ispaused == 0:
globalvars.ispaused = 1
timer.cancel()
break
Everything works perfectly up until I press the button a third time. Does anyone know a way I can restart the thread or maybe use a different method to do what I want?
Without seeing the rest of your code, it's hard to be sure where the problem is, but my guess would be that sometimes, when you click the button, ispaused is 1, but timer is an expired timer rather than a paused one. Calling start on an expired timer has no effect.
While that could be fixed, there are easier ways to do this.
For one thing, it looks like you're using some kind of GUI or game framework here. I have no idea which one you're using, but pretty much every one of them has a function to do timers (in the main event loop, as opposed to in a separate thread, but that's not the key thing here) that are more powerful than threading.Thread—in particular, that can automatically recur every second until canceled. That would obviously make your life easier.
If not, it's pretty easy to write your own repeating Timer, or to just find one on PyPI. Notice that the threading docs start with a link to the source code. That's because, like many modules in the stdlib, threading is written to be simple and easy to understand, to serve as sample code on top of being useful in its own right. In particular, Timer is dead simple, and it should be pretty obvious how to extend it: Just put a loop around the run method.
At the start of your function you've set up a new global each time and a timer:
global timer
timer = threading.Timer(1.0, time)
timer.start()
Then at the end of the function you have threading.Thread.time(self) which isn't needed and needs to be removed. Then after the function declaration you have timer = threading.Timer(1.0, time) which may be an error because when it is first called, the global timer may not have been created yet. Replace that last line of code with time() to just call the function immediately the first time. Changing these two lines will probably fix your code good enough.
Also, you have your for loops like this:
for self in thirtyonemonths:
and the problem with this would be the use of the keyword self . If this function is defined inside a class, then this use of self may be interpreted as a reference to the object. It is usually better not to use keywords such as self as iterators. Replace all uses of self with something else, like m to improve your code.
I'm working on a galactica type of game using pygame and livewires. However, in this game, instead of enemy's, there are balloons that you fire at. Every 25 mouse clicks, I have the balloons move down a row using the dy property set to a value of 1. If a balloon reaches the bottom, the game is over. However, I'm having some trouble figuring out how to get this to run only for, say, 1 second, or 2 seconds. Because I don't have a way to "time" the results, the dy value just indefinitely gets set to 1. Therefore, after the first 25 clicks, the row just keeps moving down. This is ok, but like I said, it's not my intended result.
Here is the code I have so far for this action:
if games.mouse.is_pressed(0):
new_missile = missile(self.left + 6, self.top)
games.screen.add(new_missile)
MISSILE_WAIT = 0 #25
CLICKS += 1
if CLICKS == 25:
SPEED = 1
CLICKS = 0
CLICKS, and MISSILE_WAIT are global variables that are created and set to an initial value of 0 before this block of code. What I'm trying to figure out is the algorithim to put underneath the if CLICKS statement. I've looked through the python documentation on the time module, but just can't seem to find anything that would suit this purpose. Also, I don't think using a while loop would work here, because the computer checks those results instantly, while I need an actual timer.
I'm not sure if I got your question but what I can suggest is that:
class Foo():
def __init__(self):
self.start_time = time.time()
self.time_delay = 25 # seconds
def my_balloon_func(self):
if(time.time() - self.start_time) > self.time_delay:
self.start_time = time.time()
else:
# do something
I'm trying to write a python game loop that hopefully takes into account FPS. What is the correct way to call the loop? Some of the possibilities I've considered are below. I'm trying not to use a library like pygame.
1.
while True:
mainLoop()
2.
def mainLoop():
# run some game code
time.sleep(Interval)
mainLoop()
3.
def mainLoop():
# run some game code
threading.timer(Interval, mainLoop).start()
4.
Use sched.scheduler?
If I understood correctly you want to base your game logic on a time delta.
Try getting a time delta between every frame and then have your objects move with respect to that time delta.
import time
while True:
# dt is the time delta in seconds (float).
currentTime = time.time()
dt = currentTime - lastFrameTime
lastFrameTime = currentTime
game_logic(dt)
def game_logic(dt):
# Where speed might be a vector. E.g speed.x = 1 means
# you will move by 1 unit per second on x's direction.
plane.position += speed * dt;
If you also want to limit your frames per second, an easy way would be sleeping the appropriate amount of time after every update.
FPS = 60
while True:
sleepTime = 1./FPS - (currentTime - lastFrameTime)
if sleepTime > 0:
time.sleep(sleepTime)
Be aware thought that this will only work if your hardware is more than fast enough for your game. For more information about game loops check this.
PS) Sorry for the Javaish variable names... Just took a break from some Java coding.