2014-10-15 3 views
4

Я пишу приложение Python, используя GTK для графического интерфейса. Я заметил, что закрытие его с помощью Ctrl-C с терминала не работает, и я обнаружил, что это из-за ошибки, поэтому я попытался вручную обработать сигнал. Проблема в том, что если я установил поведение по умолчанию на значение по умолчанию, сигнал пойман и приложение будет закрыто правильно, но если я использую пользовательский обработчик, он не работает. Вот моя (упрощенный) код:Обработчик сигнала Python GTK не работает

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() 

ли я что-то отсутствует?

EDIT:

Я попробовал еще немного, и я заметил, что сигнал действительно захвачен, но окно не немедленно выключается, но только тогда, когда основное внимание было приобретено еще раз. Если вместо этого я запустил

kill -9 pid 

из другого окна терминала, заявление закрывается немедленно.

+0

Вам нужен для ручного управления сигналом, хотя?См. [Прерывание клавиатуры с помощью python gtk?] (Http://stackoverflow.com/questions/16410852/keyboard-interrupt-with-with-python-gtk) –

+0

Да, я хотел бы установить обработчик для правильного управления данными записывается в файл, даже если программа убита, отправив SIGINT. – Pyrox

ответ

4

Я также помню, что у меня возникли проблемы с обработкой сигналов во время обучения приложению с pygtk3. Вот рабочий пример, демонстрирующий, как это может быть сделано для 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()), а затем, когда он recieves сигнал во второй раз закроется, вероятно, из-за ошибки, о которой вы говорили. Тем не менее для очистки переменных непосредственно перед выходом (в том числе с CTRL + C) все еще работает отлично.

Если я правильно помню, я получил решение от человека в канале pygtk irc, поэтому я не могу дать правильный кредит тому человеку, который предоставил мне решение.

+1

Я пробовал это быстро, адаптируя его к своему коду, и, похоже, он работает. Жаль, что вам нужно делать все это только для «базовой» обработки сигналов. Надеюсь, они исправит это в будущем. – Pyrox

1

Я не могу точно воспроизвести вашу проблему, потому что я использую GTK2 (версия gtk: 2.21.3, версия pygtk: 2.17.0, на Linux, если быть точным). Мои программы GTK умирают с исключением KeyboardInterrupt, когда нажата кнопка^C, и я могу ловить ловушку, используя блок try: ... except KeyboardInterrupt:.

Но я сделать получить те же результаты, как и вы, когда я установил обработчик сигнала в __init__ метод графического интерфейса GTK: то есть, используя по умолчанию обработчик signal.SIG_DFL работает, как ожидалось, но пользовательский обработчик не делает.

Однако делает работы для меня, если я установить обработчик сигнала вне класса 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.

+0

На самом деле, попробовав еще немного, я заметил, что проблема заключается в правильном захвате сигнала, но окно не закрывается немедленно, а только после того, как фокус снова получен. Это происходит, только если я ctrl-C в окне, где я запустил программу, но если я запустил 'kill -9 pid', приложение немедленно отключится. Такое поведение (к сожалению) происходит также с вашей техникой. – Pyrox

+0

Drats! О, ну, наверное, стоило попробовать. Можете ли вы сосредоточиться, используя 'window.present()' или что-то еще? –

+0

Пробовал window.present() перед выходом из графического интерфейса, но ничего не меняется. – Pyrox

3

Я играл с несколькими различными подходами, в том числе имеющие отдельный поток для запуска бойкий MainLoop и ловить сигнал от другого, но в конце концов, это было так просто, как с помощью «попробовать»:

from gi.repository import GLib 

main_loop = GLib.MainLoop() 

try: 
    main_loop.run() 
except KeyboardInterrupt: 
    print("How rude!") 
Смежные вопросы