猿问

PyGObject - 如何在执行期间中断 GLib 生成器函数?

我正在开发一个简单的 python GUI,用于使用 PyGObject/Gtk + Glade 来引导 gphoto2。我改编了 Gnome 教程,该教程使用生成器创建伪线程,允许将更新传递给 Gtk 主线程。一个子进程运行 gphoto2 并将命令传递给它以从 DSLR 相机拍摄一系列照片。我已经设法让程序在两次拍摄之间更新 GUI。但是,我计划做更长的镜头,实现一个取消生成器中间任务的选项会很有用。我将如何以最简单的方式实现这一点?


我使用了这个[教程]中的例子:https ://wiki.gnome.org/Projects/PyGObject/Threading


下面是正在编写的代码的简单示例。实际应用要大得多。


import gi

import re

import time

import subprocess


gi.require_version("Gtk", "3.0")

from gi.repository import Gio, GLib, Gtk, GObject



def app_main():


    builder = Gtk.Builder()

    builder.add_from_file("example.glade")


    window = builder.get_object("GUI")

    window.connect("destroy", Gtk.main_quit)


    capture_button = builder.get_object("btn_capture")


    def capture():

        cmd = ['gphoto2', '--auto-detect']


        process = subprocess.Popen(cmd, stdout=subprocess.PIPE)

        output = process.stdout.read().decode('utf-8')

        process.wait()


        usb_devices = re.findall('usb:001,' + '[0-9][0-9][0-9]', output)

        working_directory = "/home/richard"


        for i in range(1, 5):

            new_port = '--port=' + usb_devices[0]

            cmd = ['gphoto2', new_port, '--capture-image', '--keep']

            process = subprocess.Popen(cmd, cwd=working_directory)

            process.wait()

            update_progress(f"Photo {i} of 5 taken!\n")


            yield True


        capture_button.set_sensitive(True)


    def update_progress(text):

        output = builder.get_object("txtview_console")

        textviewbuffer = output.get_buffer()

        start_iter = textviewbuffer.get_start_iter()

        textviewbuffer.insert(start_iter, text)

        return False


    def on_capture_clicked(button):

        capture_button.set_sensitive(False)

        time.sleep(1)

        run_generator(capture)


    def on_cancel_clicked(button):

        print("You pressed cancel!")

        # I would like this to cancel the generator some how



守着星空守着你
浏览 144回答 2
2回答

呼唤远方

为了回答我自己的问题,我通过如下示例解决了这个问题:import osimport signalimport timeimport gigi.require_version("Gtk", "3.0")from gi.repository import GLib, Gtk, GObjectfrom multiprocessing import Pipe, Processproc = 0class GUI(Gtk.Window):&nbsp; &nbsp; def run_process(self, target, **kwargs):&nbsp; &nbsp; &nbsp; &nbsp; iterator = kwargs.get('iterator')&nbsp; &nbsp; &nbsp; &nbsp; option = kwargs.get('option')&nbsp; &nbsp; &nbsp; &nbsp; pid = kwargs.get('pid')&nbsp; &nbsp; &nbsp; &nbsp; parent_conn, child_conn = Pipe(duplex=False)&nbsp; &nbsp; &nbsp; &nbsp; if target == capture:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Process(target=target, args=[child_conn, iterator], daemon=True).start()&nbsp; &nbsp; &nbsp; &nbsp; if target == manager:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Process(target=target, args=[child_conn, option, pid], daemon=True).start()&nbsp; &nbsp; &nbsp; &nbsp; child_conn.close()&nbsp; &nbsp; &nbsp; &nbsp; def read_data(source, condition):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; assert parent_conn.poll()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i = parent_conn.recv()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except EOFError as E:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(E)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if isinstance(i, list) and not isinstance(i[0], str):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.update_progress(f"Photo {i[0]} of {i[1]} taken!\n")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; elif isinstance(i, str):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.update_progress(i)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; elif isinstance(i, list) and i[0] == "PID":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; global proc&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; proc = i[1]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return True&nbsp; &nbsp; &nbsp; &nbsp; GLib.io_add_watch(parent_conn.fileno(), GObject.IO_IN, read_data)&nbsp; &nbsp; def update_progress(self, text):&nbsp; &nbsp; &nbsp; &nbsp; output = builder.get_object("txtview_console")&nbsp; &nbsp; &nbsp; &nbsp; textviewbuffer = output.get_buffer()&nbsp; &nbsp; &nbsp; &nbsp; start_iter = textviewbuffer.get_start_iter()&nbsp; &nbsp; &nbsp; &nbsp; textviewbuffer.insert(start_iter, text)&nbsp; &nbsp; &nbsp; &nbsp; return False&nbsp; &nbsp; def on_capture_clicked(self, button):&nbsp; &nbsp; &nbsp; &nbsp; btn_capture = builder.get_object("start_capture_button")&nbsp; &nbsp; &nbsp; &nbsp; btn_capture.set_sensitive(False)&nbsp; &nbsp; &nbsp; &nbsp; self.run_process(capture, iterator=[1, 11])&nbsp; &nbsp; def on_cancel_clicked(self, button):&nbsp; &nbsp; &nbsp; &nbsp; global proc&nbsp; &nbsp; &nbsp; &nbsp; btn_capture = builder.get_object("start_capture_button")&nbsp; &nbsp; &nbsp; &nbsp; btn_capture.set_sensitive(True)&nbsp; &nbsp; &nbsp; &nbsp; if proc == 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.update_progress("No capture running!\n")&nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.run_process(manager, option=1, pid=proc)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; proc = 0def capture(c_conn, iterator):&nbsp; &nbsp; proc = os.getpid()&nbsp; &nbsp; c_conn.send(['PID', proc])&nbsp; &nbsp; for i in range(iterator[0], iterator[1]):&nbsp; &nbsp; &nbsp; &nbsp; # blocking method or subprocess&nbsp; &nbsp; &nbsp; &nbsp; time.sleep(2)&nbsp; &nbsp; &nbsp; &nbsp; c_conn.send([i, iterator[1]-1])&nbsp; &nbsp; c_conn.send(['PID', 0])def manager(c_conn, option, pid):&nbsp; &nbsp; if pid == 0:&nbsp; &nbsp; &nbsp; &nbsp; c_conn.send("No capture running!\n")&nbsp; &nbsp; &nbsp; &nbsp; return False&nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; if option == 1:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; os.kill(pid, signal.SIGKILL)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c_conn.send("Capture cancelled!\n")if __name__ == "__main__":&nbsp; &nbsp; builder = Gtk.Builder()&nbsp; &nbsp; builder.add_from_file("example.glade")&nbsp; &nbsp; builder.connect_signals(GUI())&nbsp; &nbsp; win = builder.get_object("GUI")&nbsp; &nbsp; win.connect("delete-event", Gtk.main_quit)&nbsp; &nbsp; win.show_all()&nbsp; &nbsp; Gtk.main()使用 glade 文件提供 GUI,如下所示:<?xml version="1.0" encoding="UTF-8"?><!-- Generated with glade 3.22.1 --><interface>&nbsp; <requires lib="gtk+" version="3.20"/>&nbsp; <object class="GtkWindow" id="GUI">&nbsp; &nbsp; <property name="can_focus">False</property>&nbsp; &nbsp; <child>&nbsp; &nbsp; &nbsp; <placeholder/>&nbsp; &nbsp; </child>&nbsp; &nbsp; <child>&nbsp; &nbsp; &nbsp; <object class="GtkGrid">&nbsp; &nbsp; &nbsp; &nbsp; <property name="visible">True</property>&nbsp; &nbsp; &nbsp; &nbsp; <property name="can_focus">False</property>&nbsp; &nbsp; &nbsp; &nbsp; <property name="row_homogeneous">True</property>&nbsp; &nbsp; &nbsp; &nbsp; <property name="column_homogeneous">True</property>&nbsp; &nbsp; &nbsp; &nbsp; <child>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <object class="GtkButton" id="start_capture_button">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="label" translatable="yes">Capture</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="visible">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="can_focus">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="receives_default">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <signal name="clicked" handler="on_capture_clicked" swapped="no"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </object>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <packing>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="left_attach">0</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="top_attach">0</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </packing>&nbsp; &nbsp; &nbsp; &nbsp; </child>&nbsp; &nbsp; &nbsp; &nbsp; <child>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <object class="GtkButton" id="cancel_capture_button">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="label" translatable="yes">Cancel</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="visible">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="can_focus">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="receives_default">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <signal name="clicked" handler="on_cancel_clicked" swapped="no"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </object>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <packing>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="left_attach">1</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="top_attach">0</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </packing>&nbsp; &nbsp; &nbsp; &nbsp; </child>&nbsp; &nbsp; &nbsp; &nbsp; <child>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <object class="GtkScrolledWindow">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="visible">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="can_focus">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="shadow_type">in</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <child>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <object class="GtkTextView" id="txtview_console">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="visible">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="can_focus">True</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </object>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </child>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </object>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <packing>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="left_attach">0</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="top_attach">1</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="width">2</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="height">2</property>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </packing>&nbsp; &nbsp; &nbsp; &nbsp; </child>&nbsp; &nbsp; &nbsp; </object>&nbsp; &nbsp; </child>&nbsp; </object></interface>上面的示例创建了一个带有捕获和取消按钮的窗口。如果捕获进程正在运行,则捕获按钮的灵敏度变为False并且阻塞进程使用管道中的进程运行以提供打印到文本视图中的反馈。在捕获过程中的任何时候,如果按下取消按钮,它将尝试获取捕获过程的全局 pid 号并将其终止。

浮云间

以下是一些关于如何停止线程的示例: https:&nbsp;//www.google.at/amp/s/www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/amp/
随时随地看视频慕课网APP

相关分类

Python
我要回答