wxPython: frame class loads several commands AFTER it is called - python

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.

Related

Preventing multiple instances of a thread in GTK

Upon pressing a listbox row, a thread is started which:
Clears the treeview (its liststore)
Then, adds items to the treeview (or rather its liststore).
The row can be pressed multiple times, meaning it can spawn multiple threads, which may end up running simultaneously.
The issue is that if the row is pressed very quickly multiple times, the treeview ends up with duplicate entries. This is caused by the multiple threads running in parallel, all trying to add the same set of items the treeview.
The solution to this issue would be to only ever allow one instance of the thread running at any time. This could be done by either:
Preventing new thread instances from starting if the thread is currently running
Stop the already running instance of the thread, and only then starting a new one
The problem is that I do not know how to implement either of those solutions.
Code:
class Main(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.connect("destroy", Gtk.main_quit)
self.liststore = Gtk.ListStore(str)
self.treeview = Gtk.TreeView()
self.treeview.set_model(self.liststore)
cellrenderertext = Gtk.CellRendererText()
treeviewcolumn = Gtk.TreeViewColumn("Items", cellrenderertext, text=0)
self.treeview.append_column(treeviewcolumn)
self.button = Gtk.Button(label='Button')
box = Gtk.Box()
box.set_orientation(Gtk.Orientation.VERTICAL)
box.add(self.button)
box.add(self.treeview)
self.add(box)
self.button.connect('clicked', self.button_clicked)
def button_clicked(self, widget):
def threaded():
self.liststore.clear()
self.liststore.append(['first_item'])
time.sleep(0.1)
self.liststore.append(['second_item'])
time.sleep(0.1)
self.liststore.append(['third_item'])
threading.Thread(target=threaded, daemon=True).start()
if __name__ == '__main__':
window = Main()
window.show_all()
Gtk.main()
I added the time.sleep(0.1) to simulate the delay between each item being added.
Pressing the button multiple times will result in duplicate entries appearing in the treeview. The expected output is:
first_item
second_item
third_item
However, pressing the button really quickly instead results in:
first_item
third_item
second_item
third_item
You should use variable to keep thread and check this variable.
In __init__ set variable
self.thread = None
and when you press button then check this variable
#if (self.thread in None) or (self.thread.is_alive() is False):
if not self.thread or not self.thread.is_alive():
self.thread = threading.Thread(...)
self.thread.start()
Instead of checking is_alive() you could set self.thread = None at the end of thread.
Note that all of the comments or answers that tell you how do deal with threading in Python might be partially correct for the narrow question on multi-threading, but they're not really what you want here.
You should realize that almost all the GTK/GLib API (like Gtk.ListStore) is not thread-safe unless explicitly mentioned. If you're going to call those functions from different threads than the main one, you should be prepared to encounter random and hard-to-debug issues.
In this case, if you want to update your Gtk.ListStore from a different thread, you want to make sure you use a function like GLib.idle_add() to update the store in the main thread.
You might also want to read up on the PyGObject tutorial on threading and concurrency

PyQt5 UI "freezes" when updating it too quickly from another thread

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.

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

time .sleep() taking place at incorrect order in commands; always at the beginning of the function

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

wxPython: Threading GUI --> Using Custom Event Handler

I am trying to learn how to run a thread off the main GUI app to do my serial port sending/receiving while keeping my GUI alive. My best Googling attempts have landed me at the wxpython wiki on: http://wiki.wxpython.org/LongRunningTasks which provides several examples. I have settled on learning the first example, involving starting a worker thread when the particular button is selected.
I am having trouble understanding the custom-event-definition:
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""
def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
Primarily the
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
I think EVT_RESULT is placed outside the classes so as to make it call-able by both classes (making it global?)
And.. the main GUI app monitors the thread's progress via:
# Set up event handler for any worker thread results
EVT_RESULT(self,self.OnResult)
I also notice that in a lot of examples, when the writer uses
from wx import *
they simply bind things by
EVT_SOME_NEW_EVENT(self, self.handler)
as opposed to
wx.Bind(EVT_SOME_NEW_EVENT, self.handler)
Which doesn't help me understand it any faster.
Thanks,
That's the old style of defining custom events. See the migration guide for more information.
Taken from the migration guide:
If you create your own custom event
types and EVT_* functions, and you
want to be able to use them with the
Bind method above then you should
change your EVT_* to be an instance of wx.PyEventBinder instead of a
function. For example, if you used to
have something like this:
myCustomEventType = wxNewEventType()
def EVT_MY_CUSTOM_EVENT(win, id, func):
win.Connect(id, -1, myCustomEventType, func)
Change it like so:
myCustomEventType = wx.NewEventType()
EVT_MY_CUSTOM_EVENT = wx.PyEventBinder(myCustomEventType, 1)
Here is another post that I made with a couple of example programs that do exactly what you are looking for.
You can define events like this:
from wx.lib.newevent import NewEvent
ResultEvent, EVT_RESULT = NewEvent()
You post the event like this:
wx.PostEvent(handler, ResultEvent(data=data))
Bind it like this:
def OnResult(event):
event.data
handler.Bind(EVT_RESULT, OnResult)
But if you just need to make a call from a non-main thread in the main thread you can use wx.CallAfter, here is an example.
Custom events are useful when you don't want to hard code who is responsible for what (see the observer design pattern). For example, lets say you have a main window and a couple of child windows. Suppose that some of the child windows need to be refreshed when a certain change occurs in the main window. The main window could directly refresh those child windows in such a case but a more elegant approach would be to define a custom event and have the main window post it to itself (and not bother who needs to react to it). Then the children that need to react to that event can do it them selves by binding to it (and if there is more than one it is important that they call event.Skip() so that all of the bound methods get called).
You may want to use Python threads and queues and not custom events. I have a wxPython program (OpenSTV) that loads large files that caused the gui to freeze during the loading. To prevent the freezing, I dispatch a thread to load the file and use a queue to communicate between the gui and the thread (e.g., to communicate an exception to the GUI).
def loadBallots(self):
self.dirtyBallots = Ballots()
self.dirtyBallots.exceptionQueue = Queue(1)
loadThread = Thread(target=self.dirtyBallots.loadUnknown, args=(self.filename,))
loadThread.start()
# Display a progress dialog
dlg = wx.ProgressDialog(\
"Loading ballots",
"Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots),
parent=self.frame, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
)
while loadThread.isAlive():
sleep(0.1)
dlg.Pulse("Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots))
dlg.Destroy()
if not self.dirtyBallots.exceptionQueue.empty():
raise RuntimeError(self.dirtyBallots.exceptionQueue.get())

Categories