1

I'd like a Python-accessible function show_image(...) that launches a fairly simple image viewer, where the image is specified as (for example) a NumPy array. Importantly, I'd like for this function not to block, but for the script to continue execution while maintaining interactivity of the window.

I realize that the giant design flaw and all around pain-in-the-ass known as the Global Interpreter Lock will thwart any Python-based GUI running in parallel with Python code in the main thread, so I'd obviously need to launch a thread that releases the GIL and does everything in C/C++.

I'm comfortable wrapping such a thing with Cython (or in a handwritten C extension module) but I'm looking for the right GUI solution. It should be cross-platform and easy to build in conjunction with a Cython extension (the latter of which seemingly rules out Qt/qmake/etc.)

Importantly, I'd like to be able to launch multiple windows this way, on demand. It seems that most GUI toolkits have some run()-like function that needs to be called at the end of the main thread program's main(), making it not terribly clear to me how I'd go about launching a window more than once. I guess I could launch a separate GUI event loop every time, but that seems like a recipe for disaster (as I recall, it is explicitly unsupported by GTK+, at least).

I'll also say that fork()'ing is not an option.

dwf
  • 3,503
  • 1
  • 20
  • 25
  • Newer version of `matplotlib` seem to have a non-blocking `show()`: http://stackoverflow.com/questions/458209/is-there-a-way-to-detach-matplotlib-plots-so-that-the-computation-can-continue – ev-br Jan 21 '13 at 20:19
  • 1
    You should really look into how GUI toolkits work even if you don't plan on using any of them, if only to get rid of some misconceptions. The GIL isn't really going to be your main problem, most GUI toolkits are single-threaded even in languages that have a GIL-free runtime, and for good reason. So basically pattern your code based on however the toolkits do it. – millimoose Jan 21 '13 at 20:20
  • The basic way how GUIs maintain interactivity is by being event-based, and forcing the programmer to make sure the events resolve quickly. This involves doing any operations that take a long time *off* the GUI thread. It's better to think of the GUI thread as your persistent "main" thread and of everything else as worker threads which periodically feed the GUI thread with updates. ("Open a new window" counts as a GUI update.) The GIL might make a performance impact in that worker threads will compete for it with input event handlers, but it's certainly not a showstopper. – millimoose Jan 21 '13 at 20:25
  • Zhenya: it won't help. The GUI will be unresponsive, unable to complete a redraw while the GUI thread doesn't have the GIL. – dwf Jan 21 '13 at 20:26
  • millimoose: I realize they are single-threaded. And if I have a fairly long NumPy or Theano computation going, that's going to mean an unresponsive GUI for upwards of several seconds. I've tried. Somehow, OpenCV's `highgui` module is able to accomplish what I want from C/C++ with [cvShowImage](http://opencv.willowgarage.com/documentation/user_interface.html#cvShowImage). – dwf Jan 21 '13 at 20:28
  • @dwf Ah, so your problem is that you know there are computations where a C extension explicitly holds the GIL for a long time. In that case you're screwed without splitting them off into a subprocess. Is there a reason why the `multiprocessing` module isn't an option? It's certainly more convenient than `fork()`. – millimoose Jan 21 '13 at 20:29
  • @millimoose There is no way to launch a GUI thread (using some toolkit) and have it poll for events from the compute thread that come in whenever `show_image()` is called? – dwf Jan 21 '13 at 20:32
  • @dwf You don't really "poll for events from another thread", a thread (in the classical concurrency model) doesn't own data - it's either completely private or shared between threads. You can have the GUI thread check a shared data structure for what updates to perform periodically using a timer, but that won't solve your problem where the GUI thread can't acquire the GIL to resolve the timer events. You can also usually manually post updates (usually a piece of code) to be executed on the GUI thread from any thread, but the problem remains if you can't interrupt your computation to do so. – millimoose Jan 21 '13 at 21:28
  • @dwf Basically, to perform any sort of GUI update from Python code, or based on data that's managed by the Python interpreter, the GUI thread has to be able to hold the GIL for a moment to read said data and run the event handler to perform the update. The only workaround I can think of would be at a very low level where you communicate with the GUI toolkit straight from C code, and read data from NumPy arrays using some low-level API if there even is one and if it allows for this; effectively bypassing the Python interpreter completely. – millimoose Jan 21 '13 at 21:30

1 Answers1

0

Personally I use os.system("xv foo.png &"), or any small image viewer. Depending on the details, you may also do g = os.popen("xv - &", "w") and write the image data to the file g. Again depending on the details, I sometimes make my program a web server (a few dozen lines of code, e.g. with Twisted), serve images from there, and view them in my web browser. My point is that you don't need any GUI or multithreading at all.

Armin Rigo
  • 12,048
  • 37
  • 48
  • 2
    This is what we're doing now, but it seems really hackish. It also doesn't lend itself to updating the same image later. – dwf Jan 21 '13 at 20:41
  • You might wanna have a look at this: https://github.com/musyoku/imgplot –  Jul 05 '18 at 16:48