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.
Related
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)
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.
Sorry about the title, this is a bit of a tough question to phrase. I'm using Python. Basically, I want the program to check a variable indefinitely. If the variable goes above 100 for example, I want code block A to run only once, and then I want the program to do nothing until the variable goes back below 100, then run code block B, and wait again until the variable goes back above 100, and then run block A again, and repeat.
The current setup I've written is as follows:
while on = True:
if value_ind >= 100:
open_time = time()
else:
close_time = time()
calculate_time_open(open_time, close_time)
The obvious problem here is that whichever if/else code block is true will run itself indefinitely, and create multiple entries in my lists for only one event. So, how would I make the code blocks run only once and then wait for a change instead of repeating constantly while waiting for a change? Thanks in advance.
You can use a state machine: your program is in one of two state: "waiting for a high/low value" and behaves appropriately:
THRESHOLD = 100
waiting_for_high_value = True # False means: waiting for low value
while True: # Infinite loop (or "while on", if "on" is a changing variable)
if waiting_for_high_value:
if value_ind >= THRESHOLD:
open_time = time()
waiting_for_high_value = False
else: # Waiting for a low value:
if value < THRESHOLD:
close_time = time()
calculate_time_open(open_time, close_time)
waiting_for_high_value = True
Now, you do need to update you test value value_ind somewhere during the loop. This is best done through a local variable (and not by changing a global variable as an invisible side effect).
PS: The answer above can be generalized to any number of states, and is convenient for adding some code that must be done continuously while waiting. In your particular case, though, you toggle between two states, and maybe there is not much to do while waiting for a change, so Stefan Pochmann's answer might be appropriate too (unless it forces you to duplicate code in the two "wait" loops).
How about this?
while True:
# wait until the variable goes over 100, then do block A once
while value_ind <= 100:
pass
<block A here>
# wait until the variable goes below 100, then do block B once
while value_ind => 100:
pass
<block B here>
This solves your repetition issue. You might better actually wait rather than constantly checking the variable, though, although it depends on what you're actually doing.
Added: Here it is with the actual blocks A and B from your code and using not, which maybe makes it nicer. One of them with parentheses which maybe highlights the condition better. (And with pass not on an extra line... I think that's ok here):
while True:
while not value_ind > 100: pass
open_time = time()
while not (value_ind < 100): pass
close_time = time()
calculate_time_open(open_time, close_time)
I am outputting the stderr to a wx.TextCtrl, after 10 lines I want to delete the first line so there is only ever a maximum of 10 lines in my wx.TextCtrl window.
I have a python script which is using multiple threads and classes. I just can't for the life of me get the below bit of code to work, can someone give me a few hints please?
a = 1
while True:
line = self.process1.stderr.readline().decode('utf-8')
wx.CallAfter(self.frame.running_log1.AppendText, line)
if a >= 10:
s = wx.CallAfter(self.frame.running_log1.GetLineLength, 0) +1
wx.CallAfter(self.frame.running_log1.Remove, 0, s)
print s
a +=1
When run s = None, so fails. I am using wx.CallAfter as I am using threads.
The reason wx.CallAfter returns None is because there isn't anything to return at that point. It can't return the length, because all it has done is made a note that at some point soon it needs to call the function. It hasn't actually called the function, and won't wait until the function has been called.
In this situation I would write a method that would append a line and remove the first line as necessary. This might look something like:
def appendAndTrim(self, line):
self.frame.running_log1.AppendText(line)
self.line_count += 1
if self.line_count > 10:
first_line_length = self.frame.running_log1.GetLineLength(0) + 1
self.frame.running_log1.Remove(0, first_line_length)
I would then pass this single method to wx.CallAfter, rather than making three separate calls to wx.CallAfter:
self.line_count = 0
while True:
line = self.process1.stderr.readline().decode('utf-8')
wx.CallAfter(self.appendAndTrim, line)
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