Manual pyglet loop freezes after a few iterations - python

I am testing out pyglet for usage in a larger project, and apparently pyglet recommends/wants you to use it's own loop (with pyglet.app.run())
This is a something I don't want, for reasons of compatibility of other packages and also to not have to rewrite the entire program structure.
Here I have prototype code stuck together from different parts and tutorials and docs.
It runs for 5-15 iterations and then just freezes, not printing anything and also not doing any draw updates.
from __future__ import division, print_function
import sys
import pyglet
window = pyglet.window.Window(800, 800, resizable=True)
window.set_caption('Pyglet Testing')
window.flip()
image = pyglet.resource.image('Sprites/scout.png')
def draw(dt):
image.blit(700-dt, 400)
while not window.has_exit:
dt = pyglet.clock.tick()
window.dispatch_events()
window.clear()
draw(dt)
window.flip()
print(dt)
My suspicion is that I have done nothing to catch events and handle them, so at a certain point it just overflows with events and blocks the whole thing. I couldn't understand how to do this however, and getting overflowed with events in under 1 second seems a bit much.
Any help?

Basically what you are doing is sending as many image.blit(...) commands to the window, until the pc probably can't handle it anymore.
For instance, if you change your code like this:
add this code:
import time
from time import sleep
change code:
def draw(dt):
image.blit(700-dt, 400)
sleep(0.1) #insert this line
When executing the modified code, you will notice that it does not freeze and that the output dt is around 0.11 seconds, which is the number of seconds since the last "tick" = the time slept (0.1 second) + the remainder time (clear window, display new frame ...)

Related

Python Prevent the screen saver

I need to run a code for hours, and the computer I am working with has a (forced and unchangeable) screensaver policy. (It locks after 10 minutes). What can I do to prevent it? Is there any line of code to prevent that?
This worked for me. I'll just leave it here so people can use it.
import ctypes
ctypes.windll.kernel32.SetThreadExecutionState(0x80000002) #this will prevent the screen saver or sleep.
## your code and operations
ctypes.windll.kernel32.SetThreadExecutionState(0x80000000) #set the setting back to normal
You can prevent screen saver by moving mouse cursor on a fixed period of time, below function can handle cursor moving.
import win32api
import random
def wakeup():
win32api.SetCursorPos((random.choice(range(100)),random.choice(range(100))))
This is a coded solution that you can place in your program (also works for Mac users):
pip3 install pyautogui
https://pypi.org/project/PyAutoGUI/ (Reference)
import pyautogui
import time
def mouse_move():
while True:
pyautogui.moveTo(100, 100, duration = 1) # move the mouse
time.sleep(60) # Every 1 min
pyautogui.moveTo(50, 100, duration = 1) # move the mouse
mouse_move()
Or, without the while loop, run it when required if your program is already within a while loop:
def mouse_move():
pyautogui.moveTo(50, 100, duration = 1) # move the mouse
mouse_move()

Issues launching headless soundboard script on boot

I'm building a headless soundboard using Raspberry Pi, and as such need a way to launch the script I'm using on boot. The program was edited and tested using the default editor Pi shot up, Thonny, and everything seems to run as intended. The buttons I'm using all play the sounds I expect them to, no issues.
I went ahead and edited rc.local to run the script as soon as the Pi boots (specifically, I added sudo python /filepath/soundboard.py & above exit 0), which it does. It seems to run identically to the way it did using Thonny, but sound cuts off after about 5 seconds, even if no buttons are pressed. When I run it directly through the command line, the same issue occurs.
The code here has been compressed, as there is more than one button, but they all use the same line.
import pygame
import random
import glob
from gpiozero import Button
import time
pygame.init()
while True:
n = glob.glob('/filepath/*.wav')
btn_0 = Button(8)
btn_0.when_pressed = pygame.mixer.stop
btn_0.when.held = lambda: pygame.mixer.Sound(random.choice(n)).play()
As far as I can tell, the while loop continues to run the program, but pressing buttons does nothing. Also, since adding the loop, the code dumps a Traceback, showing the error
gpiozero.exc.GPIOPinInUse: pin 8 is already in use by <gpiozero.Button objext on pin GPIO8, pull_up=True, is_active=False>
which might have something to do with my issue? btn_0 isn't the only button to have two functions assigned to it, but the only one to throw up this error, no matter what pin I use. The error doesn't appear if I remove the loop from the code.
You create btn_0 in an infinit while loop again and again. In the second iteration btn_0 is probably the first button that is created again. But pin 8 (which should be used for the button) has been assigned to the old instance of btn_0 in the last iteration.
You should move the glob.glob statement and the button initialization outside of the While loop. If the while loop is necessary to keep you program running place it below the initialization code and iterate over nop ore pause statements (whatever works).
If pygame.init starts it own looped thread you do not need a while loop at the end at all.
I don't know anything about pygame, so the last statement is just a guess.
Example:
import pygame
import random
import glob
from gpiozero import Button
import time
pygame.init()
n = glob.glob('/filepath/*.wav')
btn_0 = Button(8)
btn_0.when_pressed = pygame.mixer.stop
btn_0.when.held = lambda: pygame.mixer.Sound(random.choice(n)).play()
while True:
nop

wxPython hanging at random points in sequential code

I have a simple app that presents you with a button, when the button is pressed, some long running computations are done(not in a separate thread, there is no need for a separate thread as these computations are the sole reason of the app). These computations take place in a for loop and at every iteration, the for look has a callback to the main frame that updates it so it shows the current iteration(same with a small gauge underneath that fills up).
def _delay(self):
for i in range(15000):
self.update_stats(f'{i}', i)
def on_button_click(self, event):
self.init_completion_gauge(15000)
self._delay()
self.set_state_comparison_done()
def init_completion_gauge(self, maxval):
self.gauge.SetRange(maxval)
self.Layout()
self.Update()
def update_stats(self, current_iter, count):
self.label_current_iter.SetLabelText(
'\n'.join((f'Iter:', current_iter)))
self.gauge.SetValue(count)
self.label_percent_complete.SetLabelText(
f'{(count + 1) * 100 // self.gauge.GetRange()}%')
self.Layout()
self.Update()
When this runs, everything goes smoothly for a few seconds before the app shows Not responding in the title bar and hangs until it is complete. I don't mean is just hands and shows no error. Since the computations are done in the main loop it is intended for the app to block the main loop until they are done, but I doubt it is normal to randomly show not responding without being interacted with. After the computations are done the app behaves normally but it will stop responding at completely random points.
Any advice as to why this happens and what should be done to fix this?
GUI updates are queued.
Let's suppose your code looks like:
myLongTask
{
loop:
... do something
UpdateGUI()
end loop
}
Each time you call UpdateGUI() a message is sent to the OS, and the control returns inmediately and continues with the loop.
When has the OS the oportunity to handle that message? It's too busy running that loop.
You could use wx.Yield or better set a thread for the long task, that sends messages to the OS requesting update.
See more here

Memory leak when embedding and updating a matplotlib graph in a PyQt GUI

I am trying to embed a matplotlib graph that updates every second into a PyQt GUI main window.
In my program I call an update function every second using threading.Timer via the timer function shown below. I have a problem: my program grows bigger every second - at a rate of about 1k every 4 seconds. My initial thoughts are that the append function (that returns a new array in update_figure) does not delete the old array? Is it possible this is the cause of my problem?
def update_figure(self):
self.yAxis = np.append(self.yAxis, (getCO22()))
self.xAxis = np.append(self.xAxis, self.i)
# print(self.xAxis)
if len(self.yAxis) > 10:
self.yAxis = np.delete(self.yAxis, 0)
if len(self.xAxis) > 10:
self.xAxis = np.delete(self.xAxis, 0)
self.axes.plot(self.xAxis, self.yAxis, scaley=False)
self.axes.grid(True)
self.i = self.i + 1
self.draw()
This is my timer function - this is triggered by the click of a button in my PyQt GUI and then calls itself as you can see:
def timer(self):
getCH4()
getCO2()
getConnectedDevices()
self.dc.update_figure()
t = threading.Timer(1.0, self.timer)
t.start()
EDIT: I cant post my entire code because it requires a lot of .dll includes. So i'll try to explain what this program does.
In my GUI I want to show the my CO2 value over time. My get_co22 function just returns a float value and I'm 100% sure this works fine. With my timer, shown above, I want to keep append a value to a matplotlib graph - the Axes object is available to me as self.axes. I try to plot the last 10 values of the data.
EDIT 2: After some discussion in chat, I tried putting the call to update_figure() in a while loop and using just one thread to call it and was able to make this minimal example http://pastebin.com/RXya6Zah. This changed the structure of the code to call update_figure() to the following:
def task(self):
while True:
ui.dc.update_figure()
time.sleep(1.0)
def timer(self):
t = Timer(1.0, self.task())
t.start()
but now the program crashes after 5 iterations or so.
The problem is definitely not with how you are appending to your numpy array, or truncating it.
The problem here is with your threading model. Integrating calculation loops with a GUI control loop is difficult.
Fundamentally, you need your GUI threading to have control of when your update code is called (spawning a new thread to handle it if necessary) - so that
your code does not block the GUI updating,
the GUI updating does not block your code executing and
you don't spawn loads of threads holding multiple copies of objects (which might be where your memory leak comes from).
In this case, as your main window is controlled by PyQt4, you want to use a QTimer (see a simple example here)
So - alter your timer code to
def task(self):
getCH4()
getCO2()
getConnectedDevices()
self.dc.update_figure()
def timer(self):
self.t = QtCore.QTimer()
self.t.timeout.connect(self.task)
self.t.start(1000)
and this should work. Keeping the reference to the QTimer is essential - hence self.t = QtCore.QTimer() rather than t = QtCore.QTimer(), otherwise the QTimer object will be garbage collected.
Note:
This is a summary of a long thread in chat clarifying the issue and working through several possible solutions. In particular - the OP managed to mock up a simpler runnable example here: http://pastebin.com/RXya6Zah
and the fixed version of the full runnable example is here: http://pastebin.com/gv7Cmapr
The relevant code and explanation is above, but the links might help anyone who wants to replicate / solve the issue. Note that they require PyQt4 to be installed
if you are creating a new figure for every time this is quite common.
matplotlib do not free the figures you create, unless you ask it, somethink like:
pylab.close()
see How can I release memory after creating matplotlib figures

Measuring the delay of background changing and sound playing using Timeit (python)

I need to figure out the delay between sending a command to change the background color or playing a sound and these events actually occurring using timeit.(I'm on windows, Python 2.73)
I'm doing a reaction time test where I'll record the time(using time.clock()) before either changing the background color or playing a sound. Then, when the subject presses a key I record the time again and take difference to find the reaction time.
For the sound playing, here's what I did:
import timeit
t = timeit.Timer(stmt = "winsound.PlaySound('C:\WINDOWS\media\Windows XP Error.wav', winsound.SND_FILENAME)",setup = "import winsound")
n = t.timeit(number = 100)
print n/100 -0.999
The 0.999 is the duration of the Windows XP Error.wav in seconds.
This gave me something like 56ms. I'm not sure if its reasonable and if its the right way to do it as well as should I be enabling the garbage collection or not?
For the background changing I'm having more problems. Since I'm doing the test in fullscreen mode I tried to put all of these into the setup parameter:
from Tkinter import Tk
root=Tk()
root.overrideredirect(True)
root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(),root.winfo_screenheight()))
root.mainloop()
Even though I separate them all with ; I still get syntax errors. When I try it not in full screen
setup = 'from Tkinter import Tk; root=Tk(); root.mainloop()' the window actually opens, yet nothing happens and if I close it I see other errors.Invalid command name "."
The statement that I'm actually measuring is root.configure(background='red').
Here's an example of a way to create a multi-line setup string for use with timeit:
setup = """
import random
l1 = [random.randrange(100) for _ in xrange(100)]
l2 = [random.randrange(100) for _ in xrange(10)]
"""
Here's another tip. To get an accurate measurement, it's important to time things following this basic pattern:
time = min(timeit.repeat(statements, setup=setup, repeat=R, number=N))
With an R of at least 3 (R = 3). This takes the fastest value obtained by doing everything 3 times, which will eliminate differences due to the many other things running on your system in the background.
This doesn't answer your whole question, but may be helpful in your quest.

Categories