試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

PythonでUNIXシグナルのハンドラを扱う(前半)

Pythonスクリプトの実行中にCtrl+Cを押すと

Traceback (most recent call last):
  File "[スクリプトの場所]", line xxx, in <module>
    app.main()
  File "[スクリプトの場所]", line xxx, in main
    gtk.main()
KeyboardInterrupt

のようにKeyboardInterrupt例外が発生してメッセージが表示され、PyGTKを用いたGUIアプリケーションでもこのようにメッセージが表示される。C言語やVala言語などでGTK+を用いたプログラムを作成した場合にはこのようにはならないが、PyGTKでこのメッセージが出るのが邪魔な場合、抑制することはできる。

PythonにおけるUNIXシグナルの処理

一般的に、GNU/Linux上のアプリケーションは、UNIXシグナルを受け取ったときに任意のハンドラ関数を呼び出すように関連付けることができる。
これはOSの機能(システムコール)のsignal()にシグナルの識別番号とハンドラ関数の場所を指定することで行うが、Pythonではsignalというモジュールのsignal.signal()を用いて同様の要領でハンドラを指定できる。
ハンドラは2つの引数を受け取るようにしておく。1つ目の引数にシグナルの識別値が入る。

# シグナルSIGINT(Ctrl+Cなどで発生)を受け取ったときに関数func()を呼ぶ
signal.signal(signal.SIGINT, func)

...

def func(num, frame):
  """
  UNIXシグナルのハンドラ
  引数は2つ・番号とフレームオブジェクト
  """
  print 'func(): %d, %s' % (num, str(frame))  # ここではSIGINTの数値がnumに入る

テスト

下のコードを端末上で実行しCtrl+Cを押すと

func(): 2, <frame object at 0x122dbc0>

のような出力をして終了する。

#! /usr/bin/python
# -*- encoding: utf-8 -*-

import signal
import sys
import os
try:
  import pygtk
  pygtk.require('2.0')
except:
  pass
try:
  import gtk
except:
  sys.exit(1)


class MainWindow(gtk.Window):
  """
  メインウィンドウ
  """
  def __init__(self, *args, **kwargs):
    gtk.Window.__init__(self, *args, **kwargs)
    self.set_title('test')
    self.set_size_request(320, 200)
    self.__accelgroup = gtk.AccelGroup()
    self.add_accel_group(self.__accelgroup)
    self.__item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.__accelgroup)
    self.__menu_file = gtk.Menu()
    self.__menu_file.add(self.__item_quit)
    self.__item_file = gtk.MenuItem('_File')
    self.__item_file.set_submenu(self.__menu_file)
    self.__menubar = gtk.MenuBar()
    self.__menubar.append(self.__item_file)
    self.__button = gtk.Button()
    self.__button.set_label('Test')
    self.__statusbar = gtk.Statusbar()
    self.__vbox = gtk.VBox(False, 0)
    self.__vbox.pack_start(self.__menubar, expand=False, fill=False)
    self.__vbox.pack_start(self.__button)
    self.__vbox.pack_start(self.__statusbar, expand=False, fill=False)
    self.add(self.__vbox)
    self.__button.connect('clicked', self.__on_button_clicked)
    self.__item_quit.connect('activate', gtk.main_quit)
    self.connect('delete_event', gtk.main_quit)
  def __on_button_clicked(self, widget):
    """
    ボタンが押されたときの処理
    """
    print '__on_button_clicked()'

class PyGTKUnixSigHandlerTest:
  """
  UNIXシグナルハンドラのテスト
  """
  def main(self):
    """
    ウィンドウを作成してGTK+のメインループを呼ぶ
    """
    # シグナルSIGINT(Ctrl+Cなどで発生)を受け取ったときに関数func()を呼ぶ
    signal.signal(signal.SIGINT, func)
    win = MainWindow()
    win.show_all()
    gtk.main()


def func(num, frame):
  """
  UNIXシグナルSIGINTのハンドラ
  引数は2つ・番号とフレームオブジェクト
  """
  print 'func(): %d, %s' % (num, str(frame))  # ここではSIGINTの数値がnumに入る
  # メインループを抜けて終了することにする
  gtk.main_quit()

if __name__ == '__main__':
  app = PyGTKUnixSigHandlerTest()
  app.main()

(2010/2/9)後半の形に合わせて書き方を微調整(動作は同じ)

(「PythonでUNIXシグナルのハンドラを扱う(後半)」に続く)

関連記事:

参考URL: