Tkinter and cv2: "not responding" popup when imshow launched from tk app

John O'Hagan research at johnohagan.com
Fri Mar 17 22:56:04 EDT 2023


On Thu, 2023-03-16 at 04:21 -0400, aapost wrote:
> On 3/15/23 07:37, John O'Hagan wrote:
> > On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:
> > > On 3/14/23 06:54, John O'Hagan wrote:
> > 
> > [...]
> > > > 
> 
> 
> Read an alternative description of the waitKey behavior
> 
>  >For example, waitKey(0) will display the window infinitely until
> any 
> keypress (it is suitable for image display). waitKey(25) will display
> a 
> frame and wait approximately 25 ms for a key press (suitable for 
> displaying a video frame-by-frame). To remove the window, use 
> cv::destroyWindow.
> 
> I went back to double check and I stand corrected on the "any
> keypress" 
> part. Any keypress on the 'keyboard' does break the wait (as I 
> incorrectly concluded and assumed only ESC did).
> 
> It is still slightly ambiguous in explaining that when using 25ms, 
> keypress or not, the wait breaks at 25ms (or before that if you press
> a 
> keyboard key). For my setup the window is stale at that point, no 
> controls, just a stale frame behind the tkinter window that needs to
> be 
> destroyed or reused.
> 
> Whether that is the exact behavior on all set-ups isn't clear, the
> note 
> further uses the ambiguous phrasing "might".
> 
>  >Note: This function should be followed by a call to cv::waitKey or 
> cv::pollKey to perform GUI housekeeping tasks that are necessary to 
> actually show the given image and make the window respond to mouse
> and 
> keyboard events. Otherwise, it won’t display the image and the window
> might lock up.
> 
> It seems with the several variations, behavior varies between them,
> like 
> one comment saying startWindowThread when using c++ and gtk allows
> you 
> to not use waitKey (not the case here.. it seems -- changing my
> language 
> to not misspeak, lol).
> 
> I haven't come across any examples beyond imshow running stand-alone
> as 
> in my solution suggestion 2. But waitKey does return a keyvalue for
> the 
> keypess to allow extending the functionality, so you can grab it, do 
> something, and go right back to waiting, I haven't seen any use of
> this 
> though.
> 
> You can also leave out reference to tkinter all together when using 
> startWindowThread:
> 
> import sys
> import cv2
> 
> cv2.startWindowThread()
> cv2.namedWindow("W", cv2.WND_PROP_FULLSCREEN)
> cv2.setWindowProperty("W", cv2.WND_PROP_FULLSCREEN,
> cv2.WINDOW_FULLSCREEN)
> cv2.imshow("W", cv2.imread(sys.argv[1]))
> while(1):
>      a = cv2.waitKey(0)
>      if a == 27:#ESC
>          break
>      #elif a == something else, do something
> cv2.destroyAllWindows()
> exit()
> 
> But it still blocks if integrated in to the main tkinter thread, and 
> appears to use tkinter under the hood. And as tkinter is considered 
> thread-unsafe, the startWindowThread would only be ok when spawned as
> a 
> separate process like the subprocess example.
> 

Thanks for this and your other detailed replies, which I won't go into
because your comment above led me to a simple workaround using
threading, with a caveat. 

This may be controversial, but AIUI it's fine to use threads in tkinter
as long as you don't touch the same widget from different threads. I do
it all the time!  So I tried opening and waiting for the window in a
thread like this:

def window_thread():
   cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
   cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
   cv2.WINDOW_FULLSCREEN)
   cv2.waitKey(0)

threading.Thread(target=window_thread, daemon=True).start()


This works - the images can be displayed by calling imshow() via
tkinter buttons in the main thread, and all the cv2 panning and zooming
works too. 

But the fly in the ointment is that any keypress while the focus is on
the cv2 window exits the thread. I think this would apply to your
subprocess example too.

I tried:

- putting the waitKey call in a while loop as in your example above,
hoping it would just keep waiting, but the second call to waitKey
doesn't respond to keypresses
- putting the whole window_thread code in a while loop with a call to
destroyAllWindows, hoping that this would re-create the window, but it
doesn't
- restarting the window thread after waitKey, which has the same
results as above and
- using a new name for the namedWindow each time

In all cases, any subsequent calls to imshow block. I have no idea
why. 

For now it looks like I'll have to tell my users not to touch the
keyboard while the image is in focus! Not ideal.

If there is another nice fast pythonic way to display an image from a
very large array _programatically_, I'd love to know about it.

--

John







More information about the Python-list mailing list