Based on repeated calls to the sleep() function, what is the minimum amount of time that the given code will require to execute? - python

I am trying to figure out 2 questions- Firstly, I would like to know the minimum amount of time that the following code will require to execute, given the repeated calls to the sleep() function. My given code is:
from tkinter import *
from time import *
root = Tk()
canvas = Canvas(root, width=600, height=400)
canvas.pack()
a = canvas.create_line(10, 50, 20, 60, fill="#0000AA", width=2)
for i in range (0, 500):
canvas.move(a, 1, 0.2)
root.update()
sleep(0.02)
At first I thought maybe it was 0.02, but that answer seems blatantly obvious and does not take into mind the loop. So I am a bit confused.
Also, why is it that the actual execution of the above code will take longer than the time calculated according to the calls to the sleep() function?

So the sleep is going to consume 10 seconds (500 * 0.02) and the other calls canvas.move() and root.update() both are going to take some time. Probably not a ton, another second or so.
The easiest thing to do is just time the whole thing by wrapping your code in some timing code:
import datetime
start = datetime.datetime.now()
# your code here
end = datetime.datetime.now()
print(end-start)

Related

How can i make this code do what it's supposed to do?

So, what I am trying to do is when you open the window, it starts a process in which every 0.2 seconds it changes the first and 3rd value of the color (in which it converts the elements of the range into a hex value and then a string) to go from rgb( 86, 32, 86) to rgb(126, 32, 126). Although I thought this might just work, it doesn't. I only get a background of the first color and that's all.
from tkinter import *
import time
root = Tk()
for i in range(86,126):
h = hex(i)
h = str(h)
h = h[2] + h[3]
root.configure(background=("#" + h + "32" + h ))
time.sleep(0.2)
root.mainloop()
You must use the after function to give the window system time to process updates. Calling window update functions in a loop like that on the main thread will lock up the window until the loop terminates.
Try moving the code in the for loop into a new function, say updateBackground, and making it call itself recursively using after:
def updateBackground(i):
# ...
if i < 126:
root.after(200, lambda: updateBackground(i + 1))
Note that I used a lambda in order to increment i.
Credit: https://stackoverflow.com/a/36670519/1757964
Your main issue with this code is the use of sleep(). Because Tkinter is a single thread application and is event driven what ends up happening when you use sleep() the entire Tkinter instance freezes.
To work around this Tkinter provides a method called After() that is designed to schedule an event to happen after a time. So to get the same affect you are trying to get with sleep we can instead create a function that can call itself after 0.2 sec and provide it with the starting number and ending number.
from tkinter import *
root = Tk()
def do_something(start_number, end_number):
if start_number <= end_number:
h = str(hex(start_number))[2:] # combined all your work on h to one line
root.configure(background=("#{}32{}".format(h, h)))
start_number += 1
root.after(200, do_something, start_number, end_number)
do_something(86, 126)
root.mainloop()
Note that the color change is mild and hard to see. If you increase the `end_number thought it will become more obvious.

Python : Tkinter lag

I have a question .
I'm making a platformer game in tkinter and I have an issue :
I have for now : player , blocks and coins .
I'm updating the player's move and it's animation and the coin's animation and for some reason when I'm putting too much coins , the player's movement starts lagging.
Note: I'm using the after function of tkinter for animations for player's movement + animation and same goes for the coins .
For other things like gravity and etc I'm using just threads .
code of coins updating :
def coinsCheckCollision(self):
cRemove = None
indexRemove = -1
count = 0
for c in self.frame.coins:
x, y , width , height = c.getRectangle()
xP = self.player.getX; yP = self.player.getY; wP = self.player.getWidth; hP = self.player.getHeight
if collisionDetect(xP , x, yP , y, wP , width, hP , height) or collisionDetect(x , xP , y , yP , width , wP , height , hP):
if count not in coinsRemoved:
indexRemove = count
if indexRemove != -1:
if indexRemove not in coinsRemoved:
coinsRemoved.append(indexRemove)
count +=1
def coinsUpdateAnimations(self):
count = 0
for c in self.frame.coins:
if count not in coinsRemoved:
self.img = c.getAnimation()
self.img = ImageTk.PhotoImage(self.img)
self.frame.coinsImages[count] = self.img
else:
if self.frame.coinsImages[count] is not '' :
self.frame.coinsImages[count] = ''
self.frame.canvas.delete('coinB'+str(count))
what = self.frame.canvas.itemconfig('coin' + str(count), image=self.frame.coinsImages[count])
count += 1
self.coinsCheckCollision()
self.frame.frame.after(40 , self.coinsUpdateAnimations)
Anyway , the question in short is : why when I'm updating multiple things that aren't really "related" to each other , the gui starts lagging ?
Your design seems to expect your functions to run every 40ms. Maybe +/- a few ms, but averaging 25 times per second.
But that's not what happens.
First, how many coins do you have, and how complicated is that collisionDetect function? If it only takes a tiny fraction of 1ms to run through that loop, it's no big deal, but think about what happens if it takes, say, 15ms: You wait 40ms, then do 15ms of work, then wait another 40ms, then do 15ms of work, etc. So your work is running only 15 times per second, instead of 25.
Now imagine each coin takes, say, 0.2ms. At 3 coins, there's a lag of 0.6ms, which is barely noticeably. But at 100 coins, there's a lag of 20ms. That slows the coins down by 50%, which is pretty obviously noticeable.
Second, as the docs say:
Tkinter only guarantees that the callback will not be called earlier than that; if the system is busy, the actual delay may be much longer.
Being off a few ms randomly in either direction might be fine; it would all average out in the end. But after is always a few ms late, never a few ms early, so instead of averaging out, it just builds up and you get further and further behind.
And, worse, if one of your functions gets behind, it will tend to make the delay in each after a bit longer—so it won't just be your coin animation slowing down 50%, but the whole game slowing down by some unpredictable amount arbitrarily between 0-50%, but probably enough to be noticeable.
To solve both problems, you need to carry around something like the time you expected to run at, then, instead of doing after(40), you do something like this:
expected_time += 40
delay = expected_time - current_time
after(max(0, delay), func)
To put it in concrete (although untested) terms, using the datetime module:
def __init__(self):
self.next_frame_time = datetime.datetime.now()
self.schedule()
def schedule(self):
self.next_frame_time += datetime.timedelta(seconds=0.040)
now = datetime.datetime.now()
delta = max(datetime.timedelta(), now - self.next_frame_time)
self.frame.frame.after(delta.total_seconds * 1000, self.coinsUpdateAnimations)
def coinsUpdateAnimations(self):
# all the existing code before the last two lines
self.coinsCheckCollision()
self.schedule()
This still won't solve things if the total work you do takes more than 40ms, of course. Imagine that you spend 50ms, then do an after(0, func), which triggers at least 10ms late, and then spend another 50ms, then the next after(0, func) triggers at least 20ms late, and so on. If you can't do all of your work in something that's usually significantly less than 40ms, you won't be able to keep up. You have to either:
Find a way to optimize your code (e.g., maybe you can use a better algorithm, or use numpy instead of a for loop),
Redesign your game to do less work, or
Slow down your frame rate to something you actually can keep up with.
A possibly better solution is to stop trying to bend Tkinter into a gaming framework. It's not designed for that, doesn't help you get all the fiddly details right, and doesn't work all that well even once you do get them right.
By contrast, something like Pygame Zero is, as the name implies, designed for creating games. And designed to make it easy enough that people with a lot less Python experience than you seem to have can use it.
For example, instead of an event loop that runs at whatever speed your OS wants to run it, making it your responsibility to get everything timed right, Pygame Zero runs a frame loop that calls your update function N times per second, as close to evenly as possible. And it has built-in functions for things like collision detection, drawing animated sprites, etc.

python appjar function doesn't thread

having issues trying to get threading working in python using the awesome Appjar package.
The following program needs to count through a list, and update a progress bar simultaneously. I've followed the appjar documentation for threading, but it's returning NameError: name 'percent_complete' is not defined in the app.thread (line 35), in which you're meant to insert function params - my code is below:
from appJar import gui
import time
# define method the counts through a list of numbers, and updates the progress meter
def press(btn):
objects = [1,3,6]
total = len(objects)
current_object = 0
for i in objects:
print(i)
current_object += 1
current_percent_complete = (current_object / total) * 100
updateMeter(current_percent_complete)
time.sleep(1)
def updateMeter(percent_complete):
app.queueFunction(app.setMeter, "progress", percent_complete)
# create a GUI variable called app
app = gui("Login Window")
app.setBg("orange")
app.setFont(18)
# add GUI elements : a label, a meter, & a button
app.addLabel("title", "COUNTER")
app.setLabelBg("title", "blue")
app.setLabelFg("title", "orange")
app.addMeter("progress")
app.setMeterFill("progress", "green")
app.addButton("START COUNTING", press)
# put the updateMeter function in its own thread
app.thread(updateMeter, percent_complete)
# start the GUI
app.go()
I can get rid of the error by defining percent_complete like so:
from appJar import gui
import time
# define method the counts through a list of numbers, and updates the progress meter
percent_complete = 0
def press(btn):
...
However, when GUI loads and button is pressed it doesn't thread. Instead it iterates through the list, then updates the progress bar afterwards.
Has anyone come across the same issue? any insight would be awesomely appreciated!
Thanks!
There are a couple of issues here:
First, I'm not sure your maths result in good percentages to update the meter with, so you might not see much change - should you be using i?
Second, the GUI won't be updated until the loop (and the sleeps inside it) all complete. Instead, you should try counting how many items to process, and iterating through them with an after() function, see here: http://appjar.info/pythonLoopsAndSleeps/#conditional-loops
Third, the call to app.thread() at the end doesn't achieve much - it calls the update_meter() function with a parameter that doesn't exist, it can be removed.
Fourth, the actual update_meter() function isn't necessary, as you're not really using a thread - that can be removed as well...
Give this a try, once you've had a look at the maths:
current_object = 0
def press(btn):
global current_object
current_object = 0
processList()
def processList():
global current_object
objects = [1,3,6]
total = len(objects)
if current_object < total:
i = objects[current_object]
print(i)
current_object += 1
current_percent_complete = (current_object / total) * 100
app.setMeter("progress", current_percent_complete)
app.after(1000, processList)
UPDATE: just to clarify on the maths issue, you're dividing one integer by another: 0/3, 1/3, 2/3, 3/3 and so on. In python2 this will result in 0, in python3 you'll get fractions.

Using time.time() to time a function often return 0 seconds

I have to time the implementation I did of an algorithm in one of my classes, and I am using the time.time() function to do so. After implementing it, I have to run that algorithm on a number of data files which contains small and bigger data sets in order to formally analyse its complexity.
Unfortunately, on the small data sets, I get a runtime of 0 seconds even if I get a precision of 0.000000000000000001 with that function when looking at the runtimes of the bigger data sets and I cannot believe that it really takes less than that on the smaller data sets.
My question is: Is there a problem using this function (and if so, is there another function I can use that has a better precision)? Or am I doing something wrong?
Here is my code if ever you need it:
import sys, time
import random
from utility import parseSystemArguments, printResults
...
def main(ville):
start = time.time()
solution = dynamique(ville) # Algorithm implementation
end = time.time()
return (end - start, solution)
if __name__ == "__main__":
sys.argv.insert(1, "-a")
sys.argv.insert(2, "3")
(algoNumber, ville, printList) = parseSystemArguments()
(algoTime, solution) = main(ville)
printResults(algoTime, solution, printList)
The printResults function:
def printResults(time, solution, printList=True):
print ("Temps d'execution = " + str(time) + "s")
if printList:
print (solution)
The solution to my problem was to use the timeit module instead of the time module.
import timeit
...
def main(ville):
start = timeit.default_timer()
solution = dynamique(ville)
end = timeit.default_timer()
return (end - start, solution)
Don't confuse the resolution of the system time with the resolution of a floating point number. The time resolution on a computer is only as frequent as the system clock is updated. How often the system clock is updated varies from machine to machine, so to ensure that you will see a difference with time, you will need to make sure it executes for a millisecond or more. Try putting it into a loop like this:
start = time.time()
k = 100000
for i in range(k)
solution = dynamique(ville)
end = time.time()
return ((end - start)/k, solution)
In the final tally, you then need to divide by the number of loop iterations to know how long your code actually runs once through. You may need to increase k to get a good measure of the execution time, or you may need to decrease it if your computer is running in the loop for a very long time.

self." ". after() does not work properly?

I am trying to make a timer to execute a block of code every second let's say, using tkinter in python. But instead of executing the code every second, which is moving a label across a canvas, it seems to buffer and wait until the loop is finished and only then display the moved label. Beneath is the piece of coding where I think the problem is found. I personally think the for-loop in the second function is creating problems, but I don't know how to solve this.
def roll(self):
number=randint(2,12)
print number
if self.a==0:
self.place_player_1(self.start_turn_pos_1,number+self.start_turn_pos_1)
self.start_turn_pos_1+=number
elif self.a==1:
self.place_player_2(self.start_turn_pos_2,number+self.start_turn_pos_2)
self.start_turn_pos_2+=number
return number
def place_player_1(self,start_turn_pos_1,number):
#Define the board
for i in range(self.start_turn_pos_1,number+1,1):
self.c.after(1000,self.move_1(i))
def move_1(self,i):
e1=streets_x[i]
g1=streets_y[i]
self.label_player1.place(x=e1,y=g1)
self.move_1(i) calls the method immediately. To postpone the call:
self.c.after(1000, self.move_1, i) #note: no parentheses
To repeat the call every second, add .after call at the end of self.move_1 method:
def place_player_1(self,start_turn_pos_1,number):
self.c.after(1000, self.move_1, start_turn_pos_1, number) # call in a sec
def move_1(self,i, limit):
e1=streets_x[i]
g1=streets_y[i]
self.label_player1.place(x=e1,y=g1)
if i < limit: # schedule the next call
self.c.after(1000, self.move_1, i + 1, limit)
See setTimeout(), setInterval() analogs in Python using tkinter, or gtk, or twisted.
All function calls happen at the same time:
self.c.after(1000,self.move_1(i))
Because the are called after 1000 milliseconds.
Make the delay larger for each step. For example:
def place_player_1(self,start_turn_pos_1,number):
#Define the board
delay = 1000
for index, i in enumerate(range(self.start_turn_pos_1, number + 1), 1):
self.c.after(delay * index, self.move_1, i)
Now you schedule the function calls for different times.

Categories