I'm trying to make a simple slot machine python program for an assignment and I am having trouble getting the slot machine images to update. What I want to happen is for the user to click the button and have the three labels dynamically update with a different picture every 0.1 seconds for 2 seconds. But what is happening is my randint is generating random index numbers for the array, but the label only show a new image on the last randint instance. Here is the code I have:
def update_image():
global images
time.sleep(0.1)
y = images[randint(0, 3)]
slot1.delete()
slot1.configure(image = y)
print(y)
def slot_machine():
x = 0
while x != 10:
slot1.after(0,update_image)
x = x + 1
The problem is that you are calling after(0, ...) which adds a job to the "after" queue to be run as soon as possible. However, your while loop runs extremely quickly and never gives the event loop the chance to process queued events, so the entire loop ends before a single image changes.
Once the event loop is able to process the events, tkinter will try to process all pending events that are past due. Since you used a timeout of zero, they will all be past due so tkinter will run them all as fast as possible.
The better solution is to have the function that updates the image also be responsible for scheduling the next update. For example:
def update_image(delay, count):
<your existing code here>
# schedule the next iteration, until the counter drops to zero
if count > 0:
slot1.after(delay, update_image, delay, count-1)
With that, you call it once and then it calls itself repeatedly after that:
update_image(100, 10)
Related
I need to use the turtle function to create a shape that moves and that can be paused. I figured out how to do that but now I need to make it run for 10 seconds then end. It also needs to print the seconds as the countdown runs and the timer needs to pause when the app is paused.
This is what I have so far.
import turtle
import time
wn = turtle.Screen()
wn.bgcolor('blue')
player = turtle.Turtle()
player.color('yellow')
player.shape('triangle')
player.penup()
speed=1
def PlayerPause():
global speed
speed = 0
def PlayerPlay():
global speed
speed = 1
while True:
player.forward(speed)
player.left(speed)
def Timer():
seconds = 11
for i in range(1,11):
print(str(seconds - i)+ ' Seconds remain')
time.sleep(1)
print('GAME OVER')
turtle.listen()
turtle.onkey(PlayerPause, 'p')
turtle.onkey(PlayerPlay, 'r')
turtle.onkey(Timer, 'e')
wn.mainloop()
I suggest you practice "incremental development." That means instead writing a whole bunch of code and then trying to make it work, you make just one relatively small thing work, then gradually add more functions, one small step at a time.
I'll sketch how you could do this for this question.
Get something to happen once a second. Just make some kind of visible change once a second. It could be making some object alternately appear and disappear. It can be anything that is easy, visible, and doesn't break what you already have. The Screen class has an ontimer method you can use for this.
Only after the first step is working, start counting seconds and displaying the counter. Don't worry about pausing or terminating. Just make a counter that shows increasing numbers as long as the script runs.
Change it to shut everything down when the counter gets to ten.
Change it to count down from ten to zero instead of up from zero to ten.
Change the timer function to check whether the action is paused. If so, it should not change the seconds counter.
If you get stuck on any step, you can post a more specific question.
I'm trying to create a screen time alarm app using tkinter and the time module. I used a while loop to check if the current time equals the alarm that was set by the user. If the current time equals the alarm, a Toplevel window is supposed to pop up. But my program freezes while executing the while loop.
I think I need to use a thread or .after() to fix the code but I don't understand how to use either of them.
P.S. I have already gone through similar questions and solutions on stackoverflow but I'm still confused.
def set_alarm(): #set the alarm by collecting values from the spinboxes
hour = spin_hour.get()
mins = spin_min.get()
sec = spin_sec.get()
while True:
current = strftime("%H:%M:%S",localtime())
current.split(":")
if int(current[0]) == hour and int(current[1]) == mins and int(current[2]) == sec:
ring() #rings an alarm & calls a function to show Toplevel window
break
There's no need to continually check the time. You can use after to schedule the alarm at the given time. Just compute the number of seconds between now and the date in the future.
For example, if the alarm is set for exactly one minute from now, that's 60 seconds or 60,000 milliseconds. To ring the bell in 60,000 milliseconds you would call after like this (assuming root was the reference to the root window):
root.after(60000, ring)
I have a program that randomly selects 13 cards from a full pack and analyses the hands for shape, point count and some other features important to the game of bridge. The program will select and analyse 10**7 hands in about 5 minutes. Checking the Activity Monitor shows that during execution the CPU (which s a 6 Core processor) is devoting about 9% of its time to the program and ~90% of its time it is idle. So it looks like a prime candidate for multiprocessing and I created a multiprocessing version using a Queue to pass information from each process back to the main program. Having navigated the problems of IDLE not working will multiprocessing (I now run it using PyCharm) and that doing a join on a process before it has finished freezes the program, I got it to work.
However, it doesn’t matter how many processes I use 5,10, 25 or 50 the result is always the same. The CPU devotes about 18% of its time to the program and has ~75% of its time idle and the execution time is slightly more than double at a bit over 10 minutes.
Can anyone explain how I can get the processes to take up more of the CPU time and how I can get the execution time to reflect this? Below are the relevant sections fo the program:
import random
import collections
import datetime
import time
from math import log10
from multiprocessing import Process, Queue
NUM_OF_HANDS = 10**6
NUM_OF_PROCESSES = 25
def analyse_hands(numofhands, q):
#code remove as not relevant to the problem
q.put((distribution, points, notrumps))
if __name__ == '__main__':
processlist = []
q = Queue()
handsperprocess = NUM_OF_HANDS // NUM_OF_PROCESSES
print(handsperprocess)
# Set up the processes and get them to do their stuff
start_time = time.time()
for _ in range(NUM_OF_PROCESSES):
p = Process(target=analyse_hands, args=((handsperprocess, q)))
processlist.append(p)
p.start()
# Allow q to get a few items
time.sleep(.05)
while not q.empty():
while not q.empty():
#code remove as not relevant to the problem
# Allow q to be refreshed so allowing all processes to finish before
# doing a join. It seems that doing a join before a process is
# finished will cause the program to lock
time.sleep(.05)
counter['empty'] += 1
for p in processlist:
p.join()
while not q.empty():
# This is never executed as all the processes have finished and q
# emptied before the join command above.
#code remove as not relevant to the problem
finish_time = time.time()
I have no answer to the reason why IDLE will not run a multiprocessor start instruction correctly but I believe the answer to the doubling of the execution times lies in the type of problem I am dealing with. Perhaps others can comment but it seems to me that the overhead involved with adding and removing items to and from the Queue is quite high so that performance improvements will be best achieved when the amount of data being passed via the Queue is small compared with the amount of processing required to obtain that data.
In my program I am creating and passing 10**7 items of data and I suppose it is the overhead of passing this number of items via the Queue that kills any performance improvement from getting the data via separate Processes. By using a map it seems all 10^7 items of data will need to be stored in the map before any further processing can be done. This might improve performance depending on the overhead of using the map and dealing with that amount of data but for the time being I will stick with my original vanilla, single processed code.
I have a piece of code which has to get executed every 100ms and update the GUI. When I am updating the GUI - I am pressing a button, which calls a thread and in turn it calls a target function. The target function gives back the message to the GUI thread using pub sub as follows.
wx.CallAfter(pub.sendMessage, "READ EVENT", arg1=data, arg2=status_read) # This command line is in my target function
pub.subscribe(self.ReadEvent, "READ EVENT") # This is in my GUI file - whihc calls the following function
def ReadEvent(self, arg1, arg2):
if arg2 == 0:
self.MessageBox('The program did not properly read data from MCU \n Contact the Program Developer')
return
else:
self.data = arg1
self.firmware_version_text_control.Clear()
#fwversion = '0x' + ''.join('{:02X}'.format(j) for j in reversed(fwversion))
self.firmware_version_text_control.AppendText(str(SortAndDecode(self.data, 'FwVersion')))
# Pump Model
self.pump_model_text_control.Clear()
self.pump_model_text_control.AppendText(str(SortAndDecode(self.data, 'ModelName')))
# Pump Serial Number
self.pump_serial_number_text_control.Clear()
self.pump_serial_number_text_control.AppendText(str(SortAndDecode(self.data, 'SerialNum'))[:10]) # Personal Hack to not to display the AA , AB and A0
# Pressure GAIN
self.gain_text_control.Clear()
self.gain_text_control.AppendText(str(SortAndDecode(self.data, 'PresGain')))
# Pressure OFFSET Offset
self.offset_text_control.Clear()
self.offset_text_control.AppendText(str(SortAndDecode(self.data, 'PresOffset')))
#Wagner Message:
#self.status_text.SetLabel(str(SortAndDecode(self.data, 'WelcomeMsg')))
# PUMP RUNNING OR STOPPED
if PumpState(SortAndDecode(self.data, 'PumpState')) == 1:
self.led6.SetBackgroundColour('GREEN')
elif PumpState(SortAndDecode(self.data, 'PumpState')) == 0:
self.led6.SetBackgroundColour('RED')
else:
self.status_text.SetLabel(PumpState(SortAndDecode(self.data, 'PumpState')))
# PUMP RPM
self.pump_rpm_text_control.Clear()
if not self.new_model_value.GetValue():
self.pump_rpm_text_control.AppendText("000")
else:
self.pump_rpm_text_control.AppendText(str(self.sheet_num.cell_value(self.sel+1,10)*(SortAndDecode(self.data, 'FrqQ5'))/65536))
# PUMP PRESSURE
self.pressure_text_control.Clear()
self.pressure_text_control.AppendText(str(SortAndDecode(self.data, 'PresPsi')))
# ON TIME -- HOURS AND MINUTES --- EDITING IF YOU WANT
self.on_time_text_control.Clear()
self.on_time_text_control.AppendText(str(SortAndDecode(self.data, 'OnTime')))
# JOB ON TIME - HOURS AND MINUTES - EDITING IF YOU WANT
self.job_on_time_text_control.Clear()
self.job_on_time_text_control.AppendText(str(SortAndDecode(self.data, 'JobOnTime')))
# LAST ERROR ----- RECHECK THIS AGAIN
self.last_error_text_control.Clear()
self.last_error_text_control.AppendText(str(SortAndDecode(self.data, 'LastErr')))
# LAST ERROR COUNT --- RECHECK THIS AGAIN
self.error_count_text_control.Clear()
self.error_count_text_control.AppendText("CHECK THIS")
As you can see my READEVENT is very big and it takes a while for the GUI to take enough time to successfully do all these things. My problem here is, when my GUI is updating the values of TEXTCTRL it is going unresponsive - I cannot do anything else. I cant press a button or enter data or anything else. My question is if there is a better way for me to do this, so my GUI wont go unresponsive. I dont know how I can put this in a different thread as all widgets are in the main GUI. But that also requires keep creating threads every 100ms - which is horrible. Any suggestions would be greatly helpful.
Some suggestions:
How long does SortAndDecode take? What about the str() of the result? Those may be good candidates for keeping that processing in the worker thread instead of the UI thread, and passing the values to the UI thread pre-sorted-and-decoded.
You can save a little time in each iteration by calling ChangeValue instead of Clear and AppendText. Why do two function calls for each text widget instead of just one? Function calls are relatively expensive in Python compared to other Python code.
If it's possible that the same value will be sent that was sent on the last iteration then adding checks for the new value matching the old value and skipping the update of the widget could potentially save lots of time. Updating widget values is very expensive compared to leaving them alone.
Unless there is a hard requirement for 100ms updates you may want to try 150 or 200. Fewer updates per second may be fast enough for most people, especially since it's mostly textual. How much text can you read in 100ms?
If you are still having troubles with having more updates than the UI thread can keep up with, then you may want to use a different approach than pubsub and wx.CallAfter. For example you could have the worker thread receive and process the data and then add an object to a Queue.Queue and then call wx.WakeUpIdle(). In the UI thread you can have an EVT_IDLE event handler that checks the queue and pulls the first item out of the queue, if there are any, and then updates the widgets with that data. This will give the benefit of not flooding the pending events list with events from too many wx.CallAfter calls, and you can also do things like remove items from your data queue if there are too many items in it.
I have a generator that loops through a large list of elements and yields the ones that meet certain conditions. It can take a while to process a single element. Once I yield that element, once again it takes a while to process it in my main function.
This means that when I loop through the generator, I have to wait for the generator to find an element that meets all the conditions, then for my main function to process it, then rinse and repeat. I'd like to speed things up by having the next value available as soon as I need it.
def generate(a, b):
for stack in some_function(a, b):
# Check for multiple conditions. This
# takes a while.
# I'd like to run this code in the
# background while I process the
# previous element down below.
yield stack
for stack in generate(foo, bar):
# Process the stack. This can take
# a while too.
How can I get the generator to prepare the next value so that it's ready when next is called? Is this possible out of the box? I looked into coroutines and concurrency already, but they don't seem relevant to my problem.
This is the solution I came up with:
from queue import Queue
from threading import Thread
def generate(a, b, queue):
for stack in some_function(a, b):
# Check for multiple conditions.
queue.put(stack)
queue = Queue()
thread = Thread(target=generate, args=(foo, bar, queue))
thread.start()
while thread.is_alive() or not queue.empty():
stack = queue.get()
# Process the stack.
If stacks are processed faster than they're added to the queue, the while loop still runs because the thread is still alive. If the thread is dead, then the loop runs as long as the queue is empty. This is obviously a workaround because generate is no longer a generator, but it does the trick.