【问题标题】:Python GTK signal handler not workingPython GTK信号处理程序不起作用
【发布时间】:2014-10-15 17:04:23
【问题描述】:

我正在编写一个使用 GTK 作为 GUI 的 Python 应用程序。我注意到从终端用 Ctrl-C 关闭它不起作用,我发现这是因为一个错误,所以我尝试手动处理信号。问题是,如果我将默认行为设置为默认行为,则会捕获信号并正确关闭应用程序,但如果我使用自定义处理程序,它就不起作用。这是我的(简化的)代码:

from gi.repository import Gtk
import signal

class MainWindow(Gtk.Window):

    def __init__(self):
        ...
        signal.signal(signal.SIGINT, self.__signal_handler)

    def __signal_handler(self, signal, frame):
        print "Caught!"

    ...

if __name__ == "__main__":
    win = MainWindow()
    win.show_all()
    Gtk.main()

如果我设置了默认行为,则信号被正确捕获:

from gi.repository import Gtk
import signal

    class MainWindow(Gtk.Window):

        def __init__(self):
            ...
            signal.signal(signal.SIGINT, signal.SIG_DFL)

        ...

    if __name__ == "__main__":
        win = MainWindow()
        win.show_all()
        Gtk.main()

我错过了什么吗?

编辑:

我尝试了更多,我注意到信号实际上被捕获了,但是窗口并没有立即关闭,而是只有在再次获得焦点时才关闭。相反,如果我运行一个

kill -9 pid

从另一个终端窗口,应用程序立即关闭。

【问题讨论】:

标签: python gtk signals pygtk gtk3


【解决方案1】:

我还记得在使用 pygtk3 学习 appindicators 时遇到了很多关于信号处理的麻烦。这是一个工作示例,演示了如何为 SIGHUP、SIGINT 和 SIGTERM 完成此操作:

#!/usr/bin/python
from gi.repository import Gtk, GLib, GObject
from gi.repository import AppIndicator3 as appindicator
import os
import signal

class Gui():
    def __init__(self):
        self.window = Gtk.Window(title="Signal example")
        self.window.set_size_request(250,150)
        self.window.connect("delete-event", Gtk.main_quit)
        self.window.show_all()

    def cleanup(self):
        print("... Cleaning up variables, etc.")

    def quit(self, widget):
        print("... Exiting main gtk loop")
        Gtk.main_quit()

def InitSignal(gui):
    def signal_action(signal):
        if signal is 1:
            print("Caught signal SIGHUP(1)")
        elif signal is 2:
            print("Caught signal SIGINT(2)")
        elif signal is 15:
            print("Caught signal SIGTERM(15)")
        gui.cleanup()
        gui.quit(None)

    def idle_handler(*args):
        print("Python signal handler activated.")
        GLib.idle_add(signal_action, priority=GLib.PRIORITY_HIGH)

    def handler(*args):
        print("GLib signal handler activated.")
        signal_action(args[0])

    def install_glib_handler(sig):
        unix_signal_add = None

        if hasattr(GLib, "unix_signal_add"):
            unix_signal_add = GLib.unix_signal_add
        elif hasattr(GLib, "unix_signal_add_full"):
            unix_signal_add = GLib.unix_signal_add_full

        if unix_signal_add:
            print("Register GLib signal handler: %r" % sig)
            unix_signal_add(GLib.PRIORITY_HIGH, sig, handler, sig)
        else:
            print("Can't install GLib signal handler, too old gi.")

    SIGS = [getattr(signal, s, None) for s in "SIGINT SIGTERM SIGHUP".split()]
    for sig in filter(None, SIGS):
        print("Register Python signal handler: %r" % sig)
        signal.signal(sig, idle_handler)
        GLib.idle_add(install_glib_handler, sig, priority=GLib.PRIORITY_HIGH)

if __name__ == "__main__":
    gui = Gui()
    InitSignal(gui)
    Gtk.main()

请注意,当接收到一个信号时,如果你不退出 gtk 循环 (Gtk.main_quit()),那么当它第二次接收到一个信号时它会自行关闭,可能是因为你提到的错误。尽管如此,在退出之前清理变量(包括使用 CTRL + C)仍然可以完美运行。

如果我没记错的话,我从 pygtk irc 频道中的某个人那里得到了解决方案,所以我不能正确地赞扬为我提供解决方案的人。

【讨论】:

  • 我通过将其适应我的代码快速尝试了它,它似乎工作。遗憾的是,您需要为“基本”信号处理做所有这些事情。希望他们在未来修复它。
  • > 当它第二次收到信号时它会自行关闭 - 我最近遇到了这个问题,我仍然想知道为什么它在收到第二个信号后会自行关闭(显然任何信号,甚至USR1)
【解决方案2】:

我尝试了几种不同的方法,包括有一个单独的线程来运行 glib 主循环并从另一个线程中捕获信号,但最终它就像使用 'try' 一样简单:

from gi.repository import GLib

main_loop = GLib.MainLoop()

try:
    main_loop.run()
except KeyboardInterrupt:
    print("How rude!")

【讨论】:

    【解决方案3】:

    我无法完全重现您的问题,因为我正在运行 GTK2(确切地说,在 Linux 上,gtk 版本:2.21.3,pygtk 版本:2.17.0)。当按下 ^C 时,我的 GTK 程序会因 KeyboardInterrupt 异常而死掉,我可以使用 try: ... except KeyboardInterrupt: 块捕获它。

    但是当我在 GTK GUI 的 __init__ 方法中设置信号处理程序时,我确实得到了与您相同的结果:即,使用默认的 signal.SIG_DFL 处理程序按预期工作,但是自定义处理程序不会。

    但是,如果我将信号处理程序 GUI 类之外设置,它确实 对我有用。这是一个在 GUI 中带有条目框的演示程序,以便 GUI 在清理时报告一些状态信息。

    #! /usr/bin/env python
    
    ''' Testing signal trapping in GTK '''
    
    import signal
    
    import pygtk
    pygtk.require('2.0')
    import gtk
    
    class Demo:
        def cleanup(self):
            print "entry text: '%s'" % self.entry.get_text()
            print 'Quitting...'
    
        def quit(self, widget=None):
            self.cleanup()
            gtk.main_quit()
    
        def entry_activated_cb(self, widget):
            print "entry activated: '%s'" % widget.get_text()
            return True
    
        def __init__(self):
            win = gtk.Window(gtk.WINDOW_TOPLEVEL)
            win.connect("destroy", self.quit)
            win.set_border_width(5)
    
            self.entry = entry = gtk.Entry()
            entry.connect("activate", self.entry_activated_cb)
            win.add(entry)
            entry.show()
    
            win.show()
    
    
    def main():
        def my_sigint_trap(signum, frame):
            print '\nSignal handler called with signal %d, %s' % (signum, frame)
            ui.quit()
    
        ui = Demo()
        signal.signal(signal.SIGINT, my_sigint_trap)
        gtk.main()
    
    
    if __name__ == "__main__":
        main()
    

    希望,这种技术也适用于 GTK3。

    【讨论】:

    • 实际上在尝试了更多之后,我注意到问题是信号被正确捕获,但窗口没有立即关闭,而是在再次获得焦点后才关闭。仅当我在启动程序的窗口中按 ctrl - C 时才会发生这种情况,但如果我运行 kill -9 pid 应用程序会立即关闭。这种行为(不幸的是)也发生在你的技术上。
    • 讨厌!哦,好吧,我想这值得一试。您可以使用window.present() 或其他方式来获取焦点吗?
    • 在退出 GUI 之前尝试了 window.present(),但没有任何变化。
    猜你喜欢
    • 2023-04-06
    • 2017-08-06
    • 2018-02-04
    • 1970-01-01
    • 2018-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    相关资源
    最近更新 更多