Mailing List Archive

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

I'm trying to use cv2 to display images created as numpy arrays, from
within a tkinter app (which does other things with the arrays before
they are displayed as images). The arrays are colour-coded
visualisations of genomes and can be over a billion elements in size,
and I've found the PIL methods to display images in tkinter are too
slow and memory-heavy.

Here is minimal code that demonstrates the problem in the subject line:

import cv2
from tkinter import *

images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths

cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN, 
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
cv2.waitKey(1)
counter[0] += 1

root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()

It works up to a point - I can cycle through the images by clicking the
button - but if I mouse-click on the displayed image (e.g. to use the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying "'Unknown'
is not responding" and giving the option of waiting or forcing close
(but sometimes these options are greyed out too). Clicking "wait", if
available, closes the popup but it comes back a few seconds later. If I
then click on the tkinter window titlebar, the popup changes to "'Tk'
is not responding". Clicking on the button still works and after a few
clicks the popup closes.

This happens under both x11 and wayland, but under wayland, I  also get
this error:

"QSocketNotifier: Can only be used with threads started with QThread
qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"

and only every second button press displays a new image ,with only
every second image displayed.

I think this particular popup is a Gnome thing, but AIUI most DEs have
something similar to detect stuck apps. But the app is not stuck.

I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and the
tkinter event loop, but it's beyond my knowledge.

Any suggestions about causes or workarounds?

Thanks

--

John


--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
On 3/14/2023 6:54 AM, John O'Hagan wrote:
> Hi list
>
> I'm trying to use cv2 to display images created as numpy arrays, from
> within a tkinter app (which does other things with the arrays before
> they are displayed as images). The arrays are colour-coded
> visualisations of genomes and can be over a billion elements in size,
> and I've found the PIL methods to display images in tkinter are too
> slow and memory-heavy.
>
> Here is minimal code that demonstrates the problem in the subject line:
>
> import cv2
> from tkinter import *
>
> images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
>
> cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
> cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
> cv2.WINDOW_FULLSCREEN)
> counter=[0]
> def show():
> cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
> cv2.waitKey(1)
> counter[0] += 1
>
> root=Tk()
> root.wm_attributes("-topmost", 1)
> Button(root, text=' Show ', command=show).pack()
> mainloop()
>
> It works up to a point - I can cycle through the images by clicking the
> button - but if I mouse-click on the displayed image (e.g. to use the
> zooming and panning features of cv2), nothing happens, and a few
> seconds later the image greys out and a popup appears saying "'Unknown'
> is not responding" and giving the option of waiting or forcing close
> (but sometimes these options are greyed out too). Clicking "wait", if
> available, closes the popup but it comes back a few seconds later. If I
> then click on the tkinter window titlebar, the popup changes to "'Tk'
> is not responding". Clicking on the button still works and after a few
> clicks the popup closes.
>
> This happens under both x11 and wayland, but under wayland, I  also get
> this error:
>
> "QSocketNotifier: Can only be used with threads started with QThread
> qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"
>
> and only every second button press displays a new image ,with only
> every second image displayed.
>
> I think this particular popup is a Gnome thing, but AIUI most DEs have
> something similar to detect stuck apps. But the app is not stuck.
>
> I suspect this is some kind of interaction between the call to
> cv2.waitKey (which is necessary but I've never understood why!) and the
> tkinter event loop, but it's beyond my knowledge.
>
> Any suggestions about causes or workarounds?
>

I don't know anything about the specifics here, but with billions of
elements you will not be able to show more than a small fraction on the
screen. So I think that a progressive disclosure approach would pay
off. Sample the arrays down to a more workable size before creating the
screen images. If you want to zoom in, resample them and recreate new
images that only cover the zoomed in region in more detail.

It would also be useful to cache the generated images so they can be
re-displayed without needing to be regenerated each time.

--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
Assuming you?re using opencv-python, I?d post query at https://github.com/opencv/opencv-python/issues.

From: Python-list <python-list-bounces+gweatherby=uchc.edu@python.org> on behalf of John O'Hagan <research@johnohagan.com>
Date: Tuesday, March 14, 2023 at 6:56 AM
To: Python list <python-list@python.org>
Subject: Tkinter and cv2: "not responding" popup when imshow launched from tk app
*** Attention: This is an external email. Use caution responding, opening attachments or clicking on links. ***

Hi list

I'm trying to use cv2 to display images created as numpy arrays, from
within a tkinter app (which does other things with the arrays before
they are displayed as images). The arrays are colour-coded
visualisations of genomes and can be over a billion elements in size,
and I've found the PIL methods to display images in tkinter are too
slow and memory-heavy.

Here is minimal code that demonstrates the problem in the subject line:

import cv2
from tkinter import *

images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths

cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
cv2.waitKey(1)
counter[0] += 1

root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()

It works up to a point - I can cycle through the images by clicking the
button - but if I mouse-click on the displayed image (e.g. to use the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying "'Unknown'
is not responding" and giving the option of waiting or forcing close
(but sometimes these options are greyed out too). Clicking "wait", if
available, closes the popup but it comes back a few seconds later. If I
then click on the tkinter window titlebar, the popup changes to "'Tk'
is not responding". Clicking on the button still works and after a few
clicks the popup closes.

This happens under both x11 and wayland, but under wayland, I also get
this error:

"QSocketNotifier: Can only be used with threads started with QThread
qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"

and only every second button press displays a new image ,with only
every second image displayed.

I think this particular popup is a Gnome thing, but AIUI most DEs have
something similar to detect stuck apps. But the app is not stuck.

I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and the
tkinter event loop, but it's beyond my knowledge.

Any suggestions about causes or workarounds?

Thanks

--

John


--
https://urldefense.com/v3/__https://mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!iHFg1AtgcwsfEeHXaH_Nasebf9SGreVlDs-DevEIQbFiwUQThx-_rah1QkSHRJEotJFyd-d6OCQ3GuQa1MxvsnGA$<https://urldefense.com/v3/__https:/mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!iHFg1AtgcwsfEeHXaH_Nasebf9SGreVlDs-DevEIQbFiwUQThx-_rah1QkSHRJEotJFyd-d6OCQ3GuQa1MxvsnGA$>
--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
On 3/14/23 06:54, John O'Hagan wrote:
> Hi list
>
> I'm trying to use cv2 to display images created as numpy arrays, from
> within a tkinter app (which does other things with the arrays before
> they are displayed as images). The arrays are colour-coded
> visualisations of genomes and can be over a billion elements in size,
> and I've found the PIL methods to display images in tkinter are too
> slow and memory-heavy.
>
> Here is minimal code that demonstrates the problem in the subject line:
>
> import cv2
> from tkinter import *
>
> images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
>
> cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
> cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
> cv2.WINDOW_FULLSCREEN)
> counter=[0]
> def show():
> cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
> cv2.waitKey(1)
> counter[0] += 1
>
> root=Tk()
> root.wm_attributes("-topmost", 1)
> Button(root, text=' Show ', command=show).pack()
> mainloop()
>
> It works up to a point - I can cycle through the images by clicking the
> button - but if I mouse-click on the displayed image (e.g. to use the
> zooming and panning features of cv2), nothing happens, and a few
> seconds later the image greys out and a popup appears saying "'Unknown'
> is not responding" and giving the option of waiting or forcing close
> (but sometimes these options are greyed out too). Clicking "wait", if
> available, closes the popup but it comes back a few seconds later. If I
> then click on the tkinter window titlebar, the popup changes to "'Tk'
> is not responding". Clicking on the button still works and after a few
> clicks the popup closes.
>
> This happens under both x11 and wayland, but under wayland, I  also get
> this error:
>
> "QSocketNotifier: Can only be used with threads started with QThread
> qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"
>
> and only every second button press displays a new image ,with only
> every second image displayed.
>
> I think this particular popup is a Gnome thing, but AIUI most DEs have
> something similar to detect stuck apps. But the app is not stuck.
>
> I suspect this is some kind of interaction between the call to
> cv2.waitKey (which is necessary but I've never understood why!) and the
> tkinter event loop, but it's beyond my knowledge.
>
> Any suggestions about causes or workarounds?
>
> Thanks
>
> --
>
> John
>
>


I don't get any of the zoom/panning behavior with waitKey(1). But a
couple notes from the web:


https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html

Note

This function should be followed by waitKey function which displays the
image for specified milliseconds. Otherwise, it won’t display the image.
For example, waitKey(0) will display the window infinitely until any
keypress (it is suitable for image display). waitKey(25) will display a
frame for 25 ms, after which display will be automatically closed. (If
you put it in a loop to read videos, it will display the video
frame-by-frame)

https://pythonexamples.org/python-opencv-imshow/

cv2.waitKey(0) is important for holding the execution of the python
program at this statement, so that the image window stays visible. If
you do not provide this statement, cv2.imshow() executes in fraction of
a second and the program closes all the windows it opened, which makes
it almost impossible to see the image on the window.




if I change waitKey to 0, I get the ability to zoom/pan, but it places
the tk window in a blocked state because it is waiting on cv2 to return.
If I hit the ESC key, it releases the wait and gives control back to the
tk window, allowing me to press show again to continue to the next image.

I can try to change waitKey to a high ms like 10000000 and have zoom/pan
for that amount of time before it gives control back to tk (not a
sensical approach).

The fact that you can still see the image after tk takes back control is
I believe just a matter of design, some examples show
cv2.destroyAllWindows() after waitKey(0) to clean that up, but of course
if you are reusing the same window, that destroys the target.

You can resolve that if you move
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

to inside the show, and destroy it after the wait, and make waitKey 0,
this allows creation/cleanup of that window per image

Hitting ESC when done zooming/panning on each image to get back to tk.
--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
On Tue, 2023-03-14 at 08:07 -0400, Thomas Passin wrote:
> On 3/14/2023 6:54 AM, John O'Hagan wrote:
> > Hi list
> >
> > I'm trying to use cv2 to display images created as numpy arrays,
> > from
> > within a tkinter app (which does other things with the arrays
> > before
> > they are displayed as images). The arrays are colour-coded
> > visualisations of genomes and can be over a billion elements in
> > size,
> > and I've found the PIL methods to display images in tkinter are too
> > slow and memory-heavy.
> >
> > Here is minimal code that demonstrates the problem in the subject
> > line:
> >
> > import cv2
> > from tkinter import *
> >
> > images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
> >
> > cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
> > cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
> > cv2.WINDOW_FULLSCREEN)
> > counter=[0]
> > def show():
> >     cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
> >     cv2.waitKey(1)
> >     counter[0] += 1
> >
> > root=Tk()
> > root.wm_attributes("-topmost", 1)
> > Button(root, text=' Show ', command=show).pack()
> > mainloop()
> >
> > It works up to a point - I can cycle through the images by clicking
> > the
> > button - but if I mouse-click on the displayed image (e.g. to use
> > the
> > zooming and panning features of cv2), nothing happens, and a few
> > seconds later the image greys out and a popup appears saying
> > "'Unknown'
> > is not responding" and giving the option of waiting or forcing
> > close
> > (but sometimes these options are greyed out too). Clicking "wait",
> > if
> > available, closes the popup but it comes back a few seconds later.
> > If I
> > then click on the tkinter window titlebar, the popup changes
> > to "'Tk'
> > is not responding". Clicking on the button still works and after a
> > few
> > clicks the popup closes.

[...]

> I don't know anything about the specifics here, but with billions of
> elements you will not be able to show more than a small fraction on
> the
> screen.  

Hi Thomas

Thanks for your reply.

In the real app I use interpolating methods to fit the whole image on
the screen. In cv2:

cv2.imshow('W', cv2.resize(array, (width, height))

It's very quick, fractions of a second even for a billion+ sized array.
The PIL/Tk equivalent:

im = ImageTk.PhotoImage(Image.fromarray(array).resize((width, height)))
canvas.create_image(width/2, height/2, image=im)

did the same thing but was very, very slow for such large arrays (15
minutes or more per image, with memory heavily swapped out).

Having said all that, the specific problem I'm having isn't related to
the size of the arrays. The code I posted above triggers the problem
even with small images.

> So I think that a progressive disclosure approach would pay
> off.  Sample the arrays down to a more workable size before creating
> the
> screen images.  If you want to zoom in, resample them and recreate
> new
> images that only cover the zoomed in region in more detail.
>
> It would also be useful to cache the generated images so they can be
> re-displayed without needing to be regenerated each time.

This is exactly the approach I took in the first draft (except the
caching idea)! I wasn't using interpolating methods and was limited to
a minimum of one pixel per array element, and therefore limited in how
much of the array could be displayed. Even this got pretty slow in
tkinter if fully zoomed out, although the caching would have helped
with that. But the project brief requires the whole genome to be
visible by default.

Thanks

--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
On Tue, 2023-03-14 at 13:52 +0000, Weatherby,Gerard wrote:
> Assuming you’re using opencv-python, I’d post query at
> https://github.com/opencv/opencv-python/issues.


Thanks Gerard

I'm using the python3-opencv package from Debian testing. Is that
github the appropriate place for this query?

Thanks

--

John

>

--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:
> On 3/14/23 06:54, John O'Hagan wrote:

[...]
> >
> > Here is minimal code that demonstrates the problem in the subject
> > line:
> >
> > import cv2
> > from tkinter import *
> >
> > images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
> >
> > cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
> > cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
> > cv2.WINDOW_FULLSCREEN)
> > counter=[0]
> > def show():
> >     cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
> >     cv2.waitKey(1)
> >     counter[0] += 1
> >
> > root=Tk()
> > root.wm_attributes("-topmost", 1)
> > Button(root, text=' Show ', command=show).pack()
> > mainloop()
> >
> > It works up to a point - I can cycle through the images by clicking
> > the
> > button - but if I mouse-click on the displayed image (e.g. to use
> > the
> > zooming and panning features of cv2), nothing happens, and a few
> > seconds later the image greys out and a popup appears saying
> > "'Unknown'
> > is not responding" and giving the option of waiting or forcing
> > close
> > (but sometimes these options are greyed out too). Clicking "wait",
> > if
> > available, closes the popup but it comes back a few seconds later.
> > If I
> > then click on the tkinter window titlebar, the popup changes
> > to "'Tk'
> > is not responding". Clicking on the button still works and after a
> > few
> > clicks the popup closes.

[...]

> > I think this particular popup is a Gnome thing, but AIUI most DEs
> > have
> > something similar to detect stuck apps. But the app is not stuck.
> >
> > I suspect this is some kind of interaction between the call to
> > cv2.waitKey (which is necessary but I've never understood why!) and
> > the
> > tkinter event loop, but it's beyond my knowledge.
> >
> > Any suggestions about causes or workarounds?
> >
> > Thanks
> >
> > --
> >
> > John
> >
> >
>
>
> I don't get any of the zoom/panning behavior with waitKey(1). But a
> couple notes from the web:
>
>
> https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html
>
> Note
>
> This function should be followed by waitKey function which displays
> the
> image for specified milliseconds. Otherwise, it won’t display the
> image.
> For example, waitKey(0) will display the window infinitely until any
> keypress (it is suitable for image display). waitKey(25) will display
> a
> frame for 25 ms, after which display will be automatically closed.
> (If
> you put it in a loop to read videos, it will display the video
> frame-by-frame)
>
> https://pythonexamples.org/python-opencv-imshow/
>
> cv2.waitKey(0) is important for holding the execution of the python
> program at this statement, so that the image window stays visible. If
> you do not provide this statement, cv2.imshow() executes in fraction
> of
> a second and the program closes all the windows it opened, which
> makes
> it almost impossible to see the image on the window.
>
> if I change waitKey to 0, I get the ability to zoom/pan, but it
> places
> the tk window in a blocked state because it is waiting on cv2 to
> return.
> If I hit the ESC key, it releases the wait and gives control back to
> the
> tk window, allowing me to press show again to continue to the next
> image.
>
> I can try to change waitKey to a high ms like 10000000 and have
> zoom/pan
> for that amount of time before it gives control back to tk (not a
> sensical approach).
>
> The fact that you can still see the image after tk takes back control
> is
> I believe just a matter of design, some examples show
> cv2.destroyAllWindows() after waitKey(0) to clean that up, but of
> course
> if you are reusing the same window, that destroys the target.
>
> You can resolve that if you move
> cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
> cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
> cv2.WINDOW_FULLSCREEN)
>
> to inside the show, and destroy it after the wait, and make waitKey
> 0,
> this allows creation/cleanup of that window per image
>
> Hitting ESC when done zooming/panning on each image to get back to
> tk.

Thanks for your reply.

I'm afraid in the real app it won't be practical to have no gui control
while viewing images.

But your suggestions have made me realise that my issue has nothing to
do with tkinter, it seems to be the way imshow is supposed to work.
This code gives the same behaviour described above:

cv2.imshow('W', array)
cv2.waitKey(100)
time.sleep(20)

I've seen the docs you mention that say the window will close if no
keypress happens within the waitKey time, but that doesn't seem to be
what happens as you say. After waitKey returns, the image remains and
everything works, but for some reason the desktop complains about an
unresponsive app.

You can use cv2 to display video with:

while 1:
frame = camera.read() #pseudocode
cv2.imshow('W', frame)
cv2.waitKey(1)

(In fact this used to work without waitKey if you called
cv2.startWindowThread() after creating the window, but that stopped
working at some point.)

The fact that this works, presumably because the image is being
replaced often enough, suggested the following workaround to my
problem, using a call to after() to re-display the current image before
the desktop starts complaining:

images=[cv2.imread(i) for i in ('a.jpg', 'b.jpg', 'c.jpg')]

cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)

counter=[0]

image = [None]

def show():
im = images[counter[0] % 3]
cv2.imshow('W', im)
cv2.waitKey(1)
image[:] = [im]
counter[0] += 1

root=Tk()
root.wm_attributes("-topmost", 1)
b=Button(root, text=' Show ', command=show)
b.pack()

def update():
if image[0] is not None:
cv2.imshow('W', image[0])
cv2.waitKey(1)
root.after(1000, update)

update()

mainloop()


Not ideal, but I'm starting to think that despite the "highgui" label,
cv2.imshow is not really suited for integration into a gui. Which is a
shame because it's so damn fast! 

Thanks

John
--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
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:
>>> It works up to a point - I can cycle through the images by clicking
>>> the
>>> button - but if I mouse-click on the displayed image (e.g. to use
>>> the
>>> zooming and panning features of cv2), nothing happens, and a few
>>> seconds later the image greys out and a popup appears saying
>>> "'Unknown'
>>> is not responding" and giving the option of waiting or forcing
>>> close
>>> (but sometimes these options are greyed out too). Clicking "wait",
>>> if
>>> available, closes the popup but it comes back a few seconds later.
>>> If I
>>> then click on the tkinter window titlebar, the popup changes
>>> to "'Tk'
>>> is not responding". Clicking on the button still works and after a
>>> few
>>> clicks the popup closes.
>
> [...]
>
>>> I think this particular popup is a Gnome thing, but AIUI most DEs
>>> have
>>> something similar to detect stuck apps. But the app is not stuck.
>>>
>>> I suspect this is some kind of interaction between the call to
>>> cv2.waitKey (which is necessary but I've never understood why!) and
>>> the
>>> tkinter event loop, but it's beyond my knowledge.
>>>
>>> Any suggestions about causes or workarounds?
>>>
>>> Thanks
>>>
>>> --
>>>
>>> John
>>>
>>>
>>
>>
>> I don't get any of the zoom/panning behavior with waitKey(1). But a
>> couple notes from the web:
>>
>>
>> https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html
>>
>> Note
>>
>> This function should be followed by waitKey function which displays
>> the
>> image for specified milliseconds. Otherwise, it won’t display the
>> image.
>> For example, waitKey(0) will display the window infinitely until any
>> keypress (it is suitable for image display). waitKey(25) will display
>> a
>> frame for 25 ms, after which display will be automatically closed.
>> (If
>> you put it in a loop to read videos, it will display the video
>> frame-by-frame)
>>
>> https://pythonexamples.org/python-opencv-imshow/
>>
>> cv2.waitKey(0) is important for holding the execution of the python
>> program at this statement, so that the image window stays visible. If
>> you do not provide this statement, cv2.imshow() executes in fraction
>> of
>> a second and the program closes all the windows it opened, which
>> makes
>> it almost impossible to see the image on the window.
>>
>> if I change waitKey to 0, I get the ability to zoom/pan, but it
>> places
>> the tk window in a blocked state because it is waiting on cv2 to
>> return.
>> If I hit the ESC key, it releases the wait and gives control back to
>> the
>> tk window, allowing me to press show again to continue to the next
>> image.
>>
>> I can try to change waitKey to a high ms like 10000000 and have
>> zoom/pan
>> for that amount of time before it gives control back to tk (not a
>> sensical approach).
>>
>> The fact that you can still see the image after tk takes back control
>> is
>> I believe just a matter of design, some examples show
>> cv2.destroyAllWindows() after waitKey(0) to clean that up, but of
>> course
>> if you are reusing the same window, that destroys the target.
>>
>> You can resolve that if you move
>> cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
>> cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
>> cv2.WINDOW_FULLSCREEN)
>>
>> to inside the show, and destroy it after the wait, and make waitKey
>> 0,
>> this allows creation/cleanup of that window per image
>>
>> Hitting ESC when done zooming/panning on each image to get back to
>> tk.
>
> Thanks for your reply.
>
> I'm afraid in the real app it won't be practical to have no gui control
> while viewing images.
>
> But your suggestions have made me realise that my issue has nothing to
> do with tkinter, it seems to be the way imshow is supposed to work.
> This code gives the same behaviour described above:
>
> cv2.imshow('W', array)
> cv2.waitKey(100)
> time.sleep(20)
>
> I've seen the docs you mention that say the window will close if no
> keypress happens within the waitKey time, but that doesn't seem to be
> what happens as you say. After waitKey returns, the image remains and
> everything works, but for some reason the desktop complains about an
> unresponsive app.
>
> You can use cv2 to display video with:
>
> while 1:
> frame = camera.read() #pseudocode
> cv2.imshow('W', frame)
> cv2.waitKey(1)
>
> (In fact this used to work without waitKey if you called
> cv2.startWindowThread() after creating the window, but that stopped
> working at some point.)
>
> The fact that this works, presumably because the image is being
> replaced often enough, suggested the following workaround to my
> problem, using a call to after() to re-display the current image before
> the desktop starts complaining:
>
> images=[cv2.imread(i) for i in ('a.jpg', 'b.jpg', 'c.jpg')]
>
> cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
> cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
> cv2.WINDOW_FULLSCREEN)
>
> counter=[0]
>
> image = [None]
>
> def show():
> im = images[counter[0] % 3]
> cv2.imshow('W', im)
> cv2.waitKey(1)
> image[:] = [im]
> counter[0] += 1
>
> root=Tk()
> root.wm_attributes("-topmost", 1)
> b=Button(root, text=' Show ', command=show)
> b.pack()
>
> def update():
> if image[0] is not None:
> cv2.imshow('W', image[0])
> cv2.waitKey(1)
> root.after(1000, update)
>
> update()
>
> mainloop()
>
>
> Not ideal, but I'm starting to think that despite the "highgui" label,
> cv2.imshow is not really suited for integration into a gui. Which is a
> shame because it's so damn fast!



Yep, it was just a solution for the extent of the snippet. I don't have
any easy fixes for the single thread blocking issue as I have not needed
to investigate any UI blocking situations yet (if anyone has experience
in that they can chime in, or you may need to ask about
blocking/threading solutions/examples in regard to imshow on a cv2
specific forum, some sort of entirely different approach may be needed).

To clarify the docs, it does not say "close if no key presses", it says
2 different things. 2nd part being, Setting the ms will close the window
after the time regardless. The wording of the "displaying the window
indefinitely until any keypress" - when 0, does not say anything about
closing, so the until a keypress is unclear as to what that means, as
any keypress when it is 0 definitely does not close it. They do activate
the pan/zoom, and ability to ESC though..

Also to clarify (just in case), waitKey is in ms, and time.sleep is in
seconds, so waitKey 100 is 0.1, the equivalent to time.sleep(20) would
be waitKey(20000)

The likely reason the desktop is complaining about an unresponsive app
is that even though you still see that image, even at 100 ms (0.1s) the
window you are clicking on by that time is technically already dead and
closed, just not cleaned up.

Additionally at least one of the error messages you were getting were in
regard to qt (one of the graphical options under the hood), my
understanding is that qt has some added controls for imshow that show up
in a toolbar above the image (I don't have them so I don't see them). A
sort of hypothetical guess, but it might be that those features are
getting launched, but when they don't have the live imshow to operate
on, they choke and the OS steps in (could be wrong).

Does the update solve your problem in practice though? That is just a
loop that makes the imshow live again for 1 ms, then pauses for 1
second, then runs recursively. Potentially indefinitely delaying
anything from being flagged as stuck. I assume that doesn't give you the
pan/zoom ability either (at least it doesn't on mine).




--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
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:
>

Doing a quick read, tkinter is not threadsafe, so diving in to a
threading solution is probably not the best approach.

But just to throw out another possible solution to see if you can find a
"good enough" work around that fits your desired behavior.

You could spawn the imshow as it's own program:

file2: sp.py

import sys
import cv2
import tkinter as tk
def show(img, root):
cv2.namedWindow(str(root), cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty(str(root), cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
cv2.imshow(str(root), cv2.imread(img))
cv2.waitKey(0)
cv2.destroyAllWindows()
exit()
root=tk.Tk()
show(sys.argv[1], root)
tk.mainloop()


file1:main.py

import cv2
import tkinter as tk
import subprocess
images=['c.jpg', 'b.jpg', 'c.jpg']
control = {
"counter" : 0,
"pid": None
}
def show(control):
if control["pid"]:
control["pid"].kill()
control["pid"] = subprocess.Popen(["python", "sp.py",
images[control["counter"] % len(images)]])
control["counter"] += 1
root=tk.Tk()
root.wm_attributes("-topmost", 1)
tk.Button(root, text=' Show ', command=lambda: show(control)).pack()
tk.mainloop()


refactor/design as needed, you track the pid, kill the existing on each
subsequent show press.

caveats would be that since it is an entirely separate program, if you
close the main window, the other window will still linger until it is
also closed. You could try to catch the close to run a .kill() on the
subprocess if you want right before the exit, etc.

But this gives control back to the main GUI since they are now
independent of each other and not within the same thread.

If they need to talk to each other in some way more than that, I am sure
deeper design solutions could be thought up.
--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
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.

Anyway, apologies for the mistake on the any key part.

--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
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





--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app [ In reply to ]
On Tue, 2023-03-14 at 21:54 +1100, John O'Hagan wrote:

[...]

> Here is minimal code that demonstrates the problem in the subject
> line:
>
> import cv2
> from tkinter import *
>
> images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
>
> cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
> cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN, 
> cv2.WINDOW_FULLSCREEN)
> counter=[0]
> def show():
> cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
> cv2.waitKey(1)
> counter[0] += 1
>
> root=Tk()
> root.wm_attributes("-topmost", 1)
> Button(root, text=' Show ', command=show).pack()
> mainloop()
>
> It works up to a point - I can cycle through the images by clicking
> the button - but if I mouse-click on the displayed image (e.g. to use
> the zooming and panning features of cv2), nothing happens, and a few
> seconds later the image greys out and a popup appears saying
> "'Unknown' is not responding" and giving the option of waiting or
> forcing close (but sometimes these options are greyed out too).
> Clicking "wait", if available, closes the popup but it comes back a
> few seconds later. If I then click on the tkinter window titlebar,
> the popup changes to "'Tk' is not responding". Clicking on the button
> still works and after a few clicks the popup closes.
>
[...]

For anyone interested (and there are a lot of questions about this type
of issue out there), here's the nearest I came to a satisfactory
solution. In the real code I put a method like:

def window_thread():
cv2.namedWindow('W') #window properties omited
while 1: # really a flag for clean exit
cv2.imshow('W', self.image)
cv2.waitKey(40)

and call this as a thread from __init__. This updates the image every
40 ms regardless of whether it has been changed or not, in the manner
of a video feed. Seems unnecessary, but surprisingly it doesn't seem to
use any significant resources and seems to be the only way to keep both
the tkinter GUI and the cv2 window responsive. What I'd really like is
cv2.wait(until_i_say_so()) - i.e. to wait for a flag that can be set
programatically, but it doesn't seem to be available in cv2.

Thanks to those who replied.

> --
>
> John
>
>

--
https://mail.python.org/mailman/listinfo/python-list