I'm making a GUI to play videos using the python bindings for VLC, and I've run into a snag with the progress slider. Due to VLC's extreme inaccuracy and inconsistency when it comes to reporting a video's progress, I've been faking the QSlider by simply incrementing it + updating the rest of the UI (which includes multiple QLabels, QLineEdits, and QSpinBoxes) once per frame in a separate thread (not a QThread).
However, something strange has been happening: eventually, the entire UI "freezes"... except the video continues playing, the console continues outputting, and the UI instantly jumps back to life after manually interacting with it (such as by pressing the pause button). Qt is running and keeping track of the current state of the UI, just without painting anything.
I've tried manually updating the UI, manually repainting the UI (which always crashes...?), auto-resizing the UI, running processEvents(), running QTest.qWait(1), but nothing has worked. Using a QTimer to increment the progress bar, however, DOES prevent the UI from freezing, which confirms I'm not crazy. Of course, I can't actually use a QTimer though, since even a Qt.PreciseTimer results in... a very imprecise timer.
I've read that this is likely caused by Qt mistakenly trying to optimize my code by clumping the rapid updates into one, which would explain why the UI seemingly stops being painted despite clearly being active under the hood.
Though I have a feeling that the solution is probably something obvious (maybe even a different implementation of something I mentioned trying above) that doesn't require a code sample, here's a significantly stripped down version of what I'm currently working with:
def setup(self):
self.progress_thread = Thread(target=self.update_slider_thread, daemon=True)
self.progress_thread.start()
def update_slider_thread(self):
current_frame = self.progress_slider.value
is_playing = self.vlc.player.is_playing
update_progress = self.update_progress
while True:
start = time.time()
while not is_playing():
sleep(0.01)
start = time.time()
while is_playing() and not self.lock_progress_updates:
frame_multiplier = self.frame_rate
next_frame = current_frame() + 1
if next_frame <= self.frame_count: # don't update if we're at the end
update_progress(next_frame)
delay = self.delay # frame rate / total frames
try:
sleep(0.0001) # forces time.time() to update (otherwise we get the same "time" for several loops)
sleep(delay - (time.time() - start) - 0.00075) # 0.00075 to account for executing this line of code
except: pass
finally:
start = time.time()
continue
def update_progress(self, frame):
if self.get_player_state() == vlc.State.Ended:
if self.ready_to_restart and self.vlc.is_paused:
self.restart_video(frame=frame, pause=True)
self.current_time = round(self.duration * (frame / self.frame_count), 2)
h, m, s, ms = get_hms(self.current_time)
current_time_string = f'{m:02}:{s:02}.{ms:02}'
# these are just aliases for things like setValue() for each widget (for performance)
self.set_progress_slider(frame)
if not self.current_time_text_has_focus(): self.set_current_time_text(current_time_string)
self.set_hour_spin(h)
self.set_minute_spin(m)
self.set_second_spin(s)
self.set_frame_spin(frame)
Uhhhhhhhhhhhhhhhhh Ok so some basics. 1st of all, any interaction with widgets have to happen in main thread. So if you are changing slider value/etc. from daemon/worker thread. Then you are messing up Qt insides.
I would suggest that you use signals/slots. Here is a small example of it
class sliderUpdate(QObject):
handleUpdate = Signal(int)
def __init__(self):
print "Current thread (Should be main thread ) : " QThread.currentThread()
# Lets conect our signal to function/slot.
self.handleUpdate.connect(self.doUpdate,Qt.QueuedConnection) # We force it in to QueuedConnection so that Qt pass the data from worker thread to Main thread.
self.slider = QSlider()
def doUpdate(self,val):
print "Current thread (Should be main thread ) : " QThread.currentThread()
self.slider.setValue(val)
def processInThread(self):
print "Current thread (Should be worker thread ) : " QThread.currentThread()
self.handleUpdate.emit(10)
2nd. If VLC is sending updates A LOT per second. Then you may be bombarding Qt with refresh requests & lagging app. I would suggest implementing some sort of... delayed report... A sudo example :
timer = QElapsedTimer()
timer.start()
...
...
if timer.elapsed()>500:
emit new Value:
timer.restart()
else:
skip, emit on next call if timeout over 500 ms.
Related
I have a function that runs a few intensive commands, so I made a Spinner class, which is just a simple window that appears with a wx.Gauge widget that pulses during loading.
The problem is that, when called in Run, the window doesn't appear until several seconds after it was initialized - self.TriangulatePoints() actually finishes before the window appears. Indeed, if I don't comment out load.End() (which closes the window), the Spinner instance will appear and immediately disappear.
I assume this has something to do with threading, and the program continues to run while Spinner initiates. Is this the case? And if so, can you pause progression of Run() until the Spinner window appears?
It should also be noted that running time.sleep(n) after calling Spinner(...) does not change when in the program sequence it appears on screen.
def Run(self, event):
gis.points_packed = False
gis.triangulated = False
load = Spinner(self, style=wx.DEFAULT_FRAME_STYLE & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX) ^ (wx.RESIZE_BORDER) & (~wx.MINIMIZE_BOX))
load.Update('Circle packing points...')
gis.boundary(infile=gis.loaded_boundary)
load.Pulse()
self.GetPoints(None, show=False)
load.Update("Triangulating nodes...")
self.TriangulatePoints(None, show=True)
load.End()
########################################################
class Spinner(wx.Frame):
def __init__(self, *args, **kwds):
super(Spinner, self).__init__(*args, **kwds)
self.SetSize((300,80))
self.SetTitle('Loading')
process = "Loading..."
self.font = wx.Font(pointSize = 12, family = wx.DEFAULT,
style = wx.NORMAL, weight = wx.BOLD,
faceName = 'Arial')
self.process_txt = wx.StaticText(self, -1, process)
self.process_txt.SetFont(self.font)
self.progress = wx.Gauge(self, id=-1, range=100, pos=(10,30), size=(280,15), name="Loading")
self.Update(process)
self.Centre()
self.Show(True)
def End(self):
self.Close(True)
def Update(self,txt):
dc = wx.ScreenDC()
dc.SetFont(self.font)
tsize = dc.GetTextExtent(txt)
self.process_txt.SetPosition((300/2-tsize[0]/2,10))
self.process_txt.SetLabel(txt)
self.progress.Pulse()
def Pulse(self):
self.progress.Pulse()
There doesn't seem to be any threads in the code you show, so it's really not clear why do you think this has anything to do with threading. Quite the contrary, in fact: AFAICS this is due to not using threads. You should run your long running ("intensive") code in a worker thread, then things would work and display correctly in the UI.
You can't block the main, UI thread for any non-trivial amount of time and still expect the UI to update correctly.
By adding wx.Yield() immediately after load.Update('...'), I was able to fix the problem.
I found the solution through a post that Robin Dunn (#RobinDunn), one of the original authors of wxPython, wrote in a Google group:
As Micah mentioned the various yield functions are essentially a
nested event loop that reads and dispatches pending events from the
event queue. When the queue is empty then the yield function returns.
The reason [wx.Yield()] fixes the problem you are seeing is that your long
running tasks are preventing control from returning to the main event
loop and so the paint event for your custom widget will just sit in
the queue until the long running task completes and control is
allowed to return to the main loop. Adding the yield allows those
events to be processed sooner, but you may still have problems when
the long running task does finally run because any new events that
need to be processed during that time (for example, the user clicks a
Cancel button) will still have to wait until the LRT is finished.
Another issue to watch out for when using a yield function is that it
could lead to an unexpected recursion. For example you have a LRT
that periodically calls yield so events can be processed, but one of
the events that happens is one whose event handler starts the LRT
again.
So usually it is better to use some other way to prevent blocking of
events while running a the LRT, such as breaking it up into chunks
that are run from EVT_IDLE handlers, or using a thread.
i am building a small gui apllication that allowd the user to
download file from the server. it got a use in socket and tkinter mostly.
but when i download a file (a movie for example) its take a time, lets say 5 minuites for example. and in that time i want a progress bar that will tart looping till the file fully downloaded. but when the client getting the file data line by line using sock.recv,
the all gui program is freezing!
so because of that the progress bar cannot move,
and i cannot push any buttons.
so my question is - how can i fix it? mean that the gui application wont be stack while getting data from the server, and then i can make the progress bar work.
thanks lot you guys.
Here I tried to describe (mostly pseudo-code) how you can implement a non-blocking download function with progress bar.
Hope it helps.
def update_progress(percentage):
# update your progress bar in GUI
def download(callback):
# implement your download function
# check the full size of the file to be downloaded.
# try to download a reasonable amount at once
# call callback with percentage that you have downloaded to update GUI
total = 1000000 # get total size of the file to be downloaded.
current = 0
block_size = 1000 # i.e., 1 KB
while True:
# do required operations to download data of block_size amount
# example: sock.recv(block_size)
current += block_size
# calculate downloaded percentage
percentage = (block_size * 100) / total # you may add precision if you prefer
# call the callback function to update GUI based on the downloaded percentage
callback(percentage)
# check if download completed
if current >= total:
break
def start_download(): # bind this function to your button's click on GUI.
# import threading
# create a thread to execute download
# see how 'update_progress' is passed as an argument
thread = threading.Thread(target=download, args=[update_progress])
thread.start()
# execution will not be blocked here as the thread runs in the background.
# so, any code here will run without waiting for download to be completed.
thanks you guys for the help and especially to you ohannes, i used in background
thread class, this is the code: (you need change the 'root' to the name of your tkinter window)
class ThreadedClient(threading.Thread):
def __init__(self, queue, fcn):
threading.Thread.__init__(self)
self.queue = queue
self.fcn = fcn
def run(self):
time.sleep(1)
self.queue.put(self.fcn())
def spawnthread(fcn):
thread = ThreadedClient(queue, fcn)
thread.start()
periodiccall(thread)
def periodiccall(thread):
if(thread.is_alive()):
root.After(100, lambda: periodiccall(thread))
#END
I'm currently trying to build a security system on my raspberry pi. I have 9 buttons to enter a code. Once the code is entered u can press "arm" to arm the system. Then a function checks a PIR sensor for movement. When movement is detected an alarm should go off, for which I need time.sleep.
So my actual problem is, with time.sleep I block the programm for the time its sleeping, thus i cant disarm the system during alarm mode.
My idea so far was just to put everything into threads. But without success so far. Are there any better solutions to time.sleep?
You don't need to use threads. With tkinter you can easily schedule a function to run every couple of seconds in the main thread to check the sensor or do any other work that you want.
Here's a short contrived example, which will work just fine assuming that the sensor check doesn't take more than a couple hundred milliseconds. It's not exactly how I would do it, but it illustrates how you can have some function run periodically without having to put the UI to sleep.
import tkinter as tk
class App():
def __init__(self):
self._job_id = None
self.init_gui()
def init_gui(self):
self.root = tk.Tk()
self.button = tk.Button(self.root, width=6, text="Arm", command=self.arm)
self.button.pack(padx=20, pady=20)
def start(self):
self.root.mainloop()
def arm(self):
self.button.configure(text="Disarm", command=self.disarm)
self.poll()
def disarm(self):
self.button.configure(text="Arm", command=self.arm)
if self._job_id:
self.root.after_cancel(self._job_id)
def poll(self):
# ... check the sensor here ...
movement = True
if movement:
print("beep!")
self._job_id = self.root.after(2000, self.poll)
app = App()
app.start()
If your problem is simply looking for a better method to use then time.sleep, you could consider using time.time rather than time.sleep and then use checks to see what actions should occur. This would avoid using time.sleep which blocks all events (including your GUI). As a quickly written example to demonstrate this idea:
from time import time
millis = lambda: int(time() * 1000)
def updateAlarm(lastTime, beepRate, currentState):
now = millis()
if now > (lastTime + beepRate):
return (not(currentState), now)
return (currentState, now)
last = millis() #set the first time for the alarm
rate = 2000 # change state every two seconds
state = False # the alarm is currently off
while True: # just for demonstration purposes, while True: won't work inside of a tkinter GUI
change = updateAlarm(last, rate, state)
if change[0] != state: # if the state has changed, update it and print
state = change[0]
last = change[1]
print(state)
Depending on your implementation, this may make more sense, especially if you are not using tkinter. Personally, I think Bryan's solution is much more elegant, especially since it doesn't require constantly checking the alarm to see if it needs to update.
I'm using PyQt for Python, and am building a gui. I had a problem a few weeks ago where I had a function outside of the gui module modifying widgets within the gui (advanced progress bar, updating strings, etc.), and those modifications were not reflected in the gui until the function that had made the changes finished running.
The solution to this was to simply call app.processEvents() after doing whatever modifications I wanted, which would immediately update the graphics of the window.
But now I am wondering, is there a way to do this everytime the window is brought forward?
Let's say I have called a function that will be modifying the progress bar, but this function takes quite a while to run. Inbetween calling the function, and the progress bar modification, the app processes no events. So, it during this time, I pull up a Chrome window (or anything else), and then close it, my gui window is blank, just gray, until app.processEvents() is called again.
Is ther functionality in PyQt that allows me to detect whenever the window is brought to the front of all current windows?
You should look into QThread.
Threads allow you to run long, complicated tasks in a worker thread while a background thread keeps the GUI responsive, such as updating a QProgressBar, ensuring it responds to motion events.
The basic idea is this:
# load modules
import time
from PySide import QtCore, QtGui
# APPLICATION STUFF
# -----------------
APP = QtGui.QApplication([])
# THREADS
# -------
class WorkerThread(QtCore.QThread):
'''Does the work'''
def __init__(self):
super(WorkerThread, self).__init__()
self.running = True
def run(self):
'''This starts the thread on the start() call'''
# this goes over 1000 numbers, at 10 a second, will take
# 100 seconds to complete, over a minute
for i in range(1000):
print(i)
time.sleep(0.1)
self.running = False
class BackgroundThread(QtCore.QThread):
'''Keeps the main loop responsive'''
def __init__(self, worker):
super(BackgroundThread, self).__init__()
self.worker = worker
def run(self):
'''This starts the thread on the start() call'''
while self.worker.running:
APP.processEvents()
print("Updating the main loop")
time.sleep(0.1)
# MAIN
# ----
def main():
# make threads
worker = WorkerThread()
background = BackgroundThread(worker)
# start the threads
worker.start()
background.start()
# wait until done
worker.wait()
if __name__ == '__main__':
main()
The output you get is something like this, showing how it takes turns at doing the long calculation and updating the main loop:
0
Updating the main loop
1
Updating the main loop
2
Updating the main loop
3
Updating the main loop
4
Updating the main loop
5
Updating the main loop
6
Updating the main loop
Updating the main loop7
8
Updating the main loop
9
This along with a QFocusEvent override should allow you to do whatever you wish. But it's better to separate updating the GUI and running your desired long thread.
As for overriding the QFocusEvent you can do something as follows:
def focusInEvent(self, event):
event.accept()
# insert your code here
And if you choose to implement threads to avoid GUI blocking, you should read about the basics of threading (as threads have a lot of nuances unless you know about their potential pitfalls).
The code below is a stripped down version (for clarity reasons) of a small application I am working on; an application for spelling words for children.
The problem
The problem I am having is in the function flash_correct(); its purpose is to show a word for 5 seconds, then hide again.
I must have a silly blind spot, but no matter where I put the time.sleep(5), the function starts with the break of 5 seconds, while the entry: self.entry2 never shows up:
Without the time.sleep(5) however, it shows up correctly:
Where is my blind spot?
The (stripped down) code:
#!/usr/bin/env python3
from gi.repository import Gtk, Pango, Gdk
import subprocess
import time
class InterFace(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Woorden raden")
maingrid = Gtk.Grid()
self.add(maingrid)
maingrid.set_border_width(10)
self.entry2 = Gtk.Entry()
self.entry2.set_size_request(500,60)
self.entry2.set_child_visible(False)
self.entry2.modify_font(Pango.FontDescription('Ubuntu 30'))
maingrid.attach(self.entry2, 0, 4, 4, 1)
quitbutton = Gtk.Button("Stop", use_underline=True)
quitbutton.modify_font(Pango.FontDescription('Ubuntu 20'))
quitbutton.connect("clicked", self.on_close_clicked)
maingrid.attach(quitbutton, 3, 7, 1, 1)
showword_button = Gtk.Button("↺", use_underline=True)
showword_button.modify_font(Pango.FontDescription('Ubuntu 25'))
showword_button.connect("clicked", self.flash_correct)
showword_button.set_size_request(60,20)
maingrid.attach(showword_button, 0, 6, 1, 1)
def flash_correct(self, button):
# the time.sleep(5) seems to take place at the beginning
# no matter in which order I set the commands
self.entry2.set_text("Monkey")
self.entry2.set_child_visible(True)
time.sleep(5)
self.entry2.set_child_visible(False)
def on_close_clicked(self, button):
Gtk.main_quit()
window = InterFace()
window.connect("delete-event", Gtk.main_quit)
window.set_default_size(330, 330)
window.set_resizable(False)
window.show_all()
Gtk.main()
You can use time.time to hide for roughly 5 seconds calling Gtk.main_iteration() in the loop to avoid your app becoming unresponsive.
def hide(self, time_lapse):
start = time.time()
end = start + time_lapse
while end > time.time():
Gtk.main_iteration()
def flash_correct(self, button):
# the time.sleep(5) seems to take place at the beginning
# no matter in which order I set the commands
self.entry2.set_text("Monkey")
self.entry2.set_child_visible(True)
self.hide(5)
self.entry2.set_child_visible(False)
There is a good explanation in the pygtk faq 7. How can I force updates to the application windows during a long callback or other internal operation?
If you have a long-running callback or internal operation that tries to modify the application windows incrementally during its execution, you will notice that this doesn't happen; the windows of your app freeze for the duration.
This is by design: all gtk events (including window refreshing and updates) are handled in the mainloop, and while your application or callback code is running the mainloop can't handle window update events. Therefore nothing will happen in the application windows.
The trick here is to realize where your operation can take a while to return, or where it is dynamically changing the window contents, and add a code fragment like this wherever you want an update forced out:
while gtk.events_pending():
gtk.main_iteration(False)
This tells gtk to process any window events that have been left pending. If your handler has a long loop, for instance, inserting this snippet as part of the loop will avoid it hanging the window till the callback has finished.
More eloquently, in the words of the great Malcolm Tredinnick, 'this requires using what should be called "Secret Technique #1 For Making Your Application Look Responsive"(tm):
Adding while gtk.events_pending(): may be no harm also.
It would be better to use a timer that integrates with the main loop, rather than busy-waiting until the time has elapsed. Luckily there is just such a facility in GLib:
def flash_correct(self, button):
self.entry2.set_text("Monkey")
self.entry2.set_child_visible(True)
GLib.timeout_add_seconds(5, self.flash_end)
def flash_end(self):
self.entry2.set_child_visible(False)
return GLib.SOURCE_REMOVE