提问人:s4mdf0o1 提问时间:10/5/2023 最后编辑:s4mdf0o1 更新时间:10/5/2023 访问量:53
在 GTK 中线程 OpenCV 视频并保留事件?
Threading OpenCV video in GTK and keep events?
问:
我正在尝试将我的相机馈送到 GTK 窗口中, 我想保持按钮按下事件和运动通知事件的工作。
我已经找到了如何通过刷新Gtk.image中的图像来获取视频, 在GLib.idle_add中,尝试使用线程。线程,尝试使用GLib.timeout_add, 但循环仍然阻塞了事件。 我还尝试在虚拟环境中使用 OpenCV-headless......
我读过:https://pygobject.readthedocs.io/en/latest/guide/threading.html
我有什么不明白的?有没有办法解决这个问题?
这是我的(简化)代码:
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()
答:
capture.read()
将阻止,直到有新帧可用。这至少会将您的事件处理循环限制到视频源可以提供的任何帧速率。你不想要那样。
您需要生成一个循环线程,然后将新读取的帧发送到您的 GUI。这完全将 GUI 事件处理与读取视频分离。capture.read()
您还必须使用某种形式的消息传递。任何 GUI 函数的直接调用都可能被阻塞,因为它们可能会隐式发布某些事件并等待它被处理......如果您当前正在执行的函数是由同一个事件循环执行的,则不会发生这种情况,该事件循环仍在忙于执行该函数,因此它无法执行其他操作,但您的 API 调用需要它才能完成......所以这就是阻止的方式。Qt(信号和插槽)是这样,GTK也很可能是这样。
看起来很有前途的随机搜索结果:GTK 中的信号和插槽
我回答自己:我找到了一个可行的解决方案,耦合了多种用法:
- 线程非阻塞GLib.idle_add
- 用Gtk.EventBox分离事件调用
这是工作代码(我认为它可能对有同样麻烦的人有用):
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()
下一个:如何找出忍者失败的子命令?
评论