wxPython: Using EVT_IDLE - python

I defined an handler for EVT_IDLE that does a certain background task for me. (That task is to take completed work from a few processes and integrate it into some object, making a visible change in the GUI.)
The problem is that when the user is not moving the mouse or doing anything, EVT_IDLE doesn't get called more than once. I would like this handler to be working all the time. So I tried calling event.RequestMore() at the end of the handler. Works, but now it takes a whole lot of CPU. (I'm guessing it's just looping excessively on that task.)
I'm willing to limit the number of times the task will be carried out per second; How do I do that?
Or do you have another solution in mind?

Something like this (executes at most every second):
...
def On_Idle(self, event):
if not self.queued_batch:
wx.CallLater(1000, self.Do_Batch)
self.queued_batch = True
def Do_Batch(self):
# <- insert your stuff here
self.queued_batch = False
...
Oh, and don't forget to set self.queued_batch to False in the constructor and maybe call event.RequestMore() in some way in On_Idle.

This sounds like a use case for wxTimerEvent instead of wxIdleEvent. When there is processing to do call wxTimerEvent.Start(). When there isn't any to do, call wxTimerEvent.Stop() and call your methods to do processing from EVT_TIMER.
(note: i use from wxWidghets for C++ and am not familiar with wxPython but I assume they have a similar API)

Related

How do I use this callback in Python?

Please mind that I am completely green in Python and I do not know how callbacks work in this language.
I have been trying for some time to solve my problem, but I cannot figure out what to do.
I have the problem with using callbacks in Python. I use the Python keyboard module to listen to keys pressed on a machine.
When I try to use the keyboard.hook() method, it asks me for a callback. I know how callbacks work, but as long as Python is not my main language I can't really figure out what to do with it.
In JavaScript it's as easy as naming a parameter in a function then printing that parameter. Easy as that.
import keyboard
keyboard.hook()
## How to print keys?
In the official documentation, it is written that the hook() method invokes a callback. How do I access this callback and most importantly print keys which are recorded from it? I just need a simple example then I will be able to remember it forever.
Any help really appreciated.
You can pass a function just like you would a variable—by passing its name to the hook() method.
Then, per the docs on keyboard.hook(), it calls your callback with a keyboard.KeyboardEvent with three fields:
name: an Unicode representation of the character (e.g. "&") or description (e.g. "space"). The name is always lower-case.
scan_code: number representing the physical key, e.g. 55.
time: timestamp of the time the event occurred, with as much precision as given by the OS.
So, putting it together, you can use it like this:
import keyboard
def my_keyboard_hook(keyboard_event):
print("Name:", keyboard_event.name)
print("Scan code:", keyboard_event.scan_code)
print("Time:", keyboard_event.time)
keyboard.hook(my_keyboard_hook)
# Block forever, so that the program won't automatically finish,
# preventing you from typing and seeing the printed output
keyboard.wait()
And each time a key is pressed, you'll print the details of the keyboard event.
Note that the keyboard.wait() call is not necessary if your program would otherwise continue running—I just wanted to make sure that you didn't run the example code, see it terminate immediately, and then think something went wrong.
Something like this appears to be what you want from the documentation.
def keyHook(info):
print(info)
keyboard.hook(keyHook)

Segmentation fault when initializing array

I am getting a segmentation fault when initializing an array.
I have a callback function from when an RFID tag gets read
IDS = []
def readTag(e):
epc = str(e.epc, 'utf-8')
if not epc in IDS:
now = datetime.datetime.now().strftime('%m/%d/%Y %H:%M:%S')
IDS.append([epc, now, "name.instrument"])
and a main function from which it's called
def main():
for x in vals:
IDS.append([vals[0], vals[1], vals[2]])
for x in IDS:
print(x[0])
r = mercury.Reader("tmr:///dev/ttyUSB0", baudrate=9600)
r.set_region("NA")
r.start_reading(readTag, on_time=1500)
input("press any key to stop reading: ")
r.stop_reading()
The error occurs because of the line IDS.append([epc, now, "name.instrument"]). I know because when I replace it with a print call instead the program will run just fine. I've tried using different types for the array objects (integers), creating an array of the same objects outside of the append function, etc. For some reason just creating an array inside the "readTag" function causes the segmentation fault like row = [1,2,3]
Does anyone know what causes this error and how I can fix it? Also just to be a little more specific, the readTag function will work fine for the first two (only ever two) calls, but then it crashes and the Reader object that has the start_reading() function is from the mercury-api
This looks like a scoping issue to me; the mercury library doesn't have permission to access your list's memory address, so when it invokes your callback function readTag(e) a segfault occurs. I don't think that the behavior that you want is supported by that library
To extend Michael's answer, this appears to be an issue with scoping and the API you're using. In general pure-Python doesn't seg-fault. Or at least, it shouldn't seg-fault unless there's a bug in the interpreter, or some extension that you're using. That's not to say pure-Python won't break, it's just that a genuine seg-fault indicates the problem is probably the result of something messy outside of your code.
I'm assuming you're using this Python API.
In that case, the README.md mentions that the Reader.start_reader() method you're using is "asynchronous". Meaning it invokes a new thread or process and returns immediately and then the background thread continues to call your callback each time something is scanned.
I don't really know enough about the nitty gritty of CPython to say exactly what going on, but you've declared IDS = [] as a global variable and it seems like the background thread is running the callback with a different context to the main program. So when it attempts to access IDS it's reading memory it doesn't own, hence the seg-fault.
Because of how restrictive the callback is and the apparent lack of a buffer, this might be an oversight on the behalf of the developer. If you really need asynchronous reads it's worth sending them an issue report.
Otherwise, considering you're just waiting for input you probably don't need the asynchronous reads, and you could use the synchronous Reader.read() method inside your own busy loop instead with something like:
try:
while True:
readTags(r.read(timeout=10))
except KeyboardInterrupt: ## break loop on SIGINT (Ctrl-C)
pass
Note that r.read() returns a list of tags rather than just one, so you'd need to modify your callback slightly, and if you're writing more than just a quick script you probably want to use threads to interrupt the loop properly as SIGINT is pretty hacky.

How do I restructure this code so Pickle 'Buffer Region' errors do not occur

I was wondering if anyone had any good solutions to the pickling error I am having at the moment. I am trying to set my code up to open several different processes in parallel, each with a fitting process to be display on a matplotlib canvas in real time. Within my main application, I have a button which activates this function:
def process_data(self):
process_list = []
for tab in self.tab_list:
process_list.append(mp.Process(target=process_and_fit, args=(tab,)))
process_list[-1].start()
process_list[-1].join()
return
As you may notice, a 'tab' (PyQt4.QtGui.QTabWidget object) is passed to the function process_and_fit, which I have noticed is not able to be pickled readily (link here) .
However, I am not certain how to change the code to get rid of the frame being passed since it needs to be called in the process_and_fit function indirectly. By indirectly I mean something like this: (psuedo code again)
def process_and_fit(tab): # this just sets up and starts the fitting process
result = lmfit.Minimizer(residual, parameters, fcn_args=(tab,))
result.prepare_fit()
result.leastsq()
def residual(params, tab):
residual_array = Y - model
tab.refreshFigure()
return residual_array
class tab(QtGui.QTabWidget):
def __init__(self, parent, spectra):
# stuff to initialize the tab widget and hold all of the matplotlib lines and canvases
# This just refreshes the GUI stuff everytime that the parameters are fit in the least squares method
def refreshFigure(self):
self.line.set_data(self.spectra.X, self.spectra.model)
self.plot.draw_artist(self.line)
self.plot.figure.canvas.blit(self.plot.bbox)
Does anyone know how to get around this pickling error since the tab associated with a process should have only one set of data associated with it? I looked at Steven Bethard's approach but I really didn't understand where to put the code or how to utilize it. (I am a chemical engineer, not a computer scientist so there's a lot that I don't understand)
Any help is greatly appreciated.
EDIT: I added the links in that I forgot about, as requested.
The main issue is that you can't make UI changes from a separate process from the main UI thread (the one that all of your Qt calls are in). You need to use a mp.Pipe or mp.Queue to communicate back to the main process.
def process_data(self):
for tab in self.tab_list:
consumer, producer = mp.Pipe()
process_list.append(mp.Process(target=process_and_fit, args=(producer,)))
process_list[-1].start()
while (true):
message = consumer.recv() # blocks
if message == 'done':
break
# tab.spectra.X, tab.spectra.model = message
tab.refreshFigure()
process_list[-1].join()
return
def process_and_fit(pipe_conn):
...
pipe_conn.send('done')
def residual(params, pipe_conn):
residual_array = Y - model
pipe_conn.send('refresh') # or replace 'refresh' with (X, model)
return residual_array
One more thing to note: blocking for the consumer.recv() will probably hang the GUI thread. There are plenty of resources to mitigate this, the question "subprocess Popen blocking PyQt GUI" will help, since you should probably switch to QThreads. (Qthread: PySide, PyQt)
The advantage of using QThreads instead of Python threads is that with QThreads, since you're already in Qt's main event loop, you can have asynchronous (non-blocking) callbacks to update the UI.

Is this a valid use case for a context manager?

I'm making a progress indicator for some long-running console process with intent to use it like this:
pi = ProgressIndicator()
for x in somelongstuff:
do stuff
pi.update()
pi.print_totals()
Basically, it should output some kind of a progress bar with dots and dashes, and something like "234234 bytes processed" at the end.
I thought it would be nice to use it as a context manager:
with ProgressIndicator() as pi:
for x in somelongstuff:
do stuff
pi.update()
However there are a few things that concern me about this solution:
extra indentation makes the indicator feature appear more important than it actually is
I don't want ProgressIndicator to handle any exceptions that might occur in the loop
Is this a valid use case for a context manager? What other solutions can you suggest?
It definitely seems a valid use case. The context manager doesn't have to handle exceptions if you don't want it to, although you would want to end the line that the progress bar is output on to prevent it being confused with the traceback, and have it not print the totals if it is exited through an exception.
With regard to indentation, I'd argue that letting the user see progress is actually a very important feature so it's fine for it to take up an indentation level.
There's a GUI application which has a very similar ProgressTask API, which you use like this:
def slow_func():
t = nuke.ProgressTask()
t.setMessage("Doing something")
for x in range(100):
do_something()
t.setProgress(x+1)
When ProgressTask.__del__ is called, the progress bar UI disappears. This works nicely for the most part, however if an exception is raised (e.g by do_something()), the traceback object keeps a reference to the ProgressTask object, so the progress-bar gets stuck (until another traceback occurs)
The ProgressTask implemented the context-manager protocol, it could use the __exit__ method to ensure the progress bar has been hidden.
For a command-line UI (which is sounds like you are writing), this may not be an issue, but you could do similar cleanup tasks, e.g to display an ######### 100% (error) type bar, and ensure the traceback output isn't messed up etc
There's no reason your progress-bar class couldn't be usable in both manners - most context-managers are perfectly usable as both regular objects and context-managers, e.g:
lock = threading.Lock()
lock.acquire()
lock.release()
# or:
with lock:
pass

pygtk's strange problem about set button's sensitive property

on one of my methods, I have the following code:
def fun():
self.button1.set_sensitive(False)
self.get_time()
However, self.button1 only becomes insensitive after get_time() return !!,use the time.sleep(n) replace the get_time() could get same result
Any clue why?
I think programmic changes to widgets applies in the next lap of event loop (gtk.main()), that is probably after finishing fun function. Does that make a problem for you? How much time self.get_time()
takes? If that takes a sensible time, you can update widgets before that:
def fun():
self.button1.set_sensitive(False)
while gtk.events_pending():
gtk.main_iteration_do(False)
self.get_time()
Uhh are you sure you want to do that?
All GUI programming events are done by message passing and so you really shouldn't block the main thread for long enough you'd ever need some workaround like this. And if you do that, you'll soon have other problems like the window manager killing your window because it's not responding to ping or reentrance problems when you do the iteration. If you have some complicated task like burning a CD or whatever that takes that long, put the actual burning into its own executable and call it by glib.spawn_async (or similar). Use gobject.child_watch_add to ask to be notified about termination.

Categories