2014年2月9日日曜日

wxPythonで常駐ランチャをつくろう!第二回 実行中のファイルに引数を渡す

あいさつ


つかさです。今回は、実行中のプログラムへの引数の送り方です。
メインウィンドウの文字列を与えられた引数に置き換えます。

例えば、既にランチャが起動しているときに

D:\bin\homuLauncher\htest2.py H:\howm2\Python

とすれば実行中のウィンドウがこんな感じになるようにします。



hlnch02.py

# -*- encoding: utf-8 -*-

import wx,subprocess,os,sys,SocketServer,socket,threading

class MyTxtCtr(wx.PySimpleApp):
    
    def OnInit(self):
        HOST, PORT = socket.gethostname(), 61955
        argvs = sys.argv
        instance_name = u"%s-%s" % (self.GetAppName(), wx.GetUserId())
        self.instance = wx.SingleInstanceChecker(instance_name)
        if self.instance.IsAnotherRunning():
            if len(argvs) >= 2:
                self.client(HOST, PORT, argvs)
            wx.Exit()
        else:
            server = self.start_server(HOST, PORT)
        self.Frm = wx.Frame(None, -1, "homuLauncher", size=(400,60),pos=(400,400))
        self.TxtCtr = wx.TextCtrl(self.Frm, -1)
        self.Frm.Show()
        return 1

    def start_server(self,host, port):
        server = ThreadedTCPServer((host, port), ThreadedTCPRequestHandler)
        ip, port = server.server_address
        server_thread = threading.Thread(target=server.serve_forever)
        server_thread.setDaemon(True)
        server_thread.start()

    def client(self,ip, port, arg):
        message = os.path.abspath(' '.join(arg[1:]))
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((ip, port))
        sock.send(message)
        response = sock.recv(1024)
#        print "Received: %s" % response
        sock.close()

class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024)
        wx.GetApp().TxtCtr.SetValue(data)
        response = 'string length: %d' % len(data)
#        print 'responding to',data,'with',response
        self.request.send(response)
        
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

app = MyTxtCtr()
app.MainLoop()

説明


  1. 初回起動時に、本体と共にバックグラウンドでサーバーを起動する。そのサーバーが何かメッセージを受け取れば、それに応じて本体に反映させるようにしておく
  2. 二度目の起動時には、そのサーバーに接続するクライアントを作成。引数を渡し、自身は終了する

という構造です。
ポート番号を予め指定し、サーバーとクライアントを作ることで、初回起動時のプログラムと二度目以降のとをつなげるという仕組みらしい。もっと簡単にできると思ってたけどわりと大掛かりなんですね。それとも他の方法があるのかしらん。

解説


二重起動のチェック


        if self.instance.IsAnotherRunning():
            if len(argvs) >= 2:
                self.client(HOST, PORT, argvs)
            wx.Exit()
        else:
            server = self.start_server(HOST, PORT)


wx.SingleInstanceCheckerで、既に起動中のプログラムがあるかどうかで分岐。

  • 初回起動なら サーバーをバックグラウンドで起動する
  • 既に起動しているなら クライアントを作成して、既に起動中のプログラムのサーバーに接続する

という具合です。それぞれstart_serverとclientで行ってます。

サーバー側


    def start_server(self,host, port):

        server = ThreadedTCPServer((host, port), ThreadedTCPRequestHandler)
        ip, port = server.server_address
        server_thread = threading.Thread(target=server.serve_forever)
        server_thread.setDaemon(True)
        server_thread.start()


ここでサーバーを動かしてるわけですが、二つのクラスを利用してます。

1 ThreadedTCPRequestHandlerクラス

クライアントからメッセージを受け取った時の処理をThreadedTCPRequestHandlerクラスで定めます。

class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024)
        wx.GetApp().TxtCtr.SetValue(data)
        #Note to the self.server.app
        response = 'string length: %d' % len(data)
#        print 'responding to',data,'with',response
        self.request.send(response)


受け取ったデータを、SetValueでテキストボックスに貼り付けてますね。

2 ThreadedTCPServerクラス

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass


非同期的な動作をサポートするのにこれが必要らしい。参考にしたのがこうしてたのでとりあえずこうしてます。


クライアント側


    def client(self,ip, port, arg):
        message = os.path.abspath(' '.join(arg[1:]))
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((ip, port))
        sock.send(message)
        response = sock.recv(1024)
#        print "Received: %s" % response
        sock.close()


  1. ソケットを作成
  2. サーバーに接続
  3. コマンドライン引数をサーバーに送る
  4. レスポンスを受け取り
  5. 終了

という順序ですね。

0 件のコメント:

コメントを投稿