I'm trying to get my camera feed into a GTK window, and I'd like to keep the button-press-event and motion-notify-event events working.
I've found how to get the video by refreshing the image in a Gtk.image, in a GLib.idle_add, tried with a thread.Threading, tried with a GLib.timeout_add, but the loop is still blocking the events. I also tried with an OpenCV-headless in a virtual environment...
I read it: https://pygobject.readthedocs.io/en/latest/guide/threading.html
What didn't I understand? Is there a way to fix this ?
Here's my (simplified) code :
import cv2
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gtk, GdkPixbuf
#import threading
vdo_url="http://my_cam/vdo.mjpg" # simplified, without security
class Cam_GTK(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Cam GTK")
self.capture = cv2.VideoCapture(vdo_url)
self.video = Gtk.Image.new()
self.video.connect("button-press-event", self.on_video_clicked )
self.video.connect("motion-notify-event", self.on_video_hover )
# Also tried with event=Gtk.EventBox.new(), event.add(self.video), then "connect"...
page_box = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=1 )
page_box.pack_start( self.video, True, True, 0 )
self.add(page_box)
GLib.idle_add(self.show_frame)
#GLib.timeout_add(100, self.show_frame)
#tried to Thread(target=self.show_frame) w/out loop
self.connect("destroy", Gtk.main_quit)
self.show_all()
def on_video_hover( self, event, result ):
print("video hover")
def on_video_clicked( self, event, button ):
print("video clicked")
def show_frame(self):
ret, frame = self.capture.read()
#tried with a while
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pb = GdkPixbuf.Pixbuf.new_from_data(frame.tobytes(),
GdkPixbuf.Colorspace.RGB,
False,
8,
frame.shape[1],
frame.shape[0],
frame.shape[2]*frame.shape[1])
self.video.set_from_pixbuf( pb.copy() )
return True #tried changed to False to stop loop
cam = Cam_GTK()
Gtk.main()
I answer myself : I've found a working solution, coupling multiple usages :
here is the working code (I think it could be useful for someone with the same troubles) :
import cv2
from time import sleep
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, GdkPixbuf, Gdk, GObject
from threading import Thread
vdo_url="http://my_cam/vdo.mjpg" # simplified, without security
class Cam_GTK(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Cam GTK")
self.capture = cv2.VideoCapture(vdo_url)
self.video = Gtk.Image.new()
refresh_vdo = Thread(target = self.refresh_video)
refresh_vdo.daemon= True
refresh_vdo.start()
self.event_vdo = Gtk.EventBox.new() #This gives the new events processing for signals
self.event_vdo.add(self.video)
self.event_vdo.set_above_child(True)
self.event_vdo.connect("button-press-event", self.on_video_clicked )
self.event_vdo.connect("motion-notify-event", self.on_video_hover )
page_box = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=1 )
page_box.pack_start( self.event_vdo, True, True, 0 )
self.add(page_box)
self.connect("destroy", Gtk.main_quit)
self.show_all()
def on_video_clicked( self, event, button ):
print("clicked")
def on_video_hover(self, widget, event):
print("hover")
def refresh_video( self ):
while not self.done:
GLib.idle_add(self.show_frame)
sleep(0.1) #Wait for Gtk.Image refresh (I guess)
def show_frame(self):
ret, frame = self.capture.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pb = GdkPixbuf.Pixbuf.new_from_data(frame.tobytes(),
GdkPixbuf.Colorspace.RGB,
False,
8,
frame.shape[1],
frame.shape[0],
frame.shape[2]*frame.shape[1])
self.video.set_from_pixbuf(pb.copy())
return False #Important to be False not to block
camera = Cam_GTK()
Gtk.main()