Pythonでコマンドラインランチャを作ろう!第五回−パスの実行

前置き

Pythonコマンドラインランチャを作ろう!も5回目です。
今日は、選択したコマンドの実行をやります

概観

コマンドラインランチャは数多くの種類があるけれども、機能を削っていくと、文字の打ち込み==>絞り込み==>コマンドの実行、という三段階を持つことになるのではないでしょうか。
だとすると、今回で一応はコマンドラインランチャの体裁は整えた形になるわけですにゃ。

今回やるのは前回と同じく、fenrir型についての話です。リストボックスに表示されるのがパスだと、それをそのままうけとり実行ということで、craftlaunch型より多少説明が簡単なんです。craftlaunch型は次回から。

今日やること

今日やるのは、パスの実行です。リストの各項目を選択し、Enterを押したらそのパスを実行するようにすればいいわけですね。

リストに表示された項目から、直接パスを抜き出しそれを実行したらいいだけだから簡単ですね。テキストコントロールでエンターを押したときのイベントを、リストボックスの選択項目を実行する関数と結びつけます。

subprocessのimport

今回は、subprocessモジュールを使います。あとでみる、subprocess.Popenを使うためですね。そこで、まずこれのインポートをしておきます。
それには、一行目の

import wx

import wx,subprocess

に書き換えます。

wx.EVT_KEY_DOWNとキーによる分岐

どうやってエンターを押したときのイベントと結びつけるか、ということですがwx.EVT_KEY_DOWNを用います。これと関数を結びつけます。その関数は、以前に作っていますね。OnKeyDownです。以前は、ここから押されたキーを取得し、そのキーが上か下かで分岐させていました。今回は、そこに押されたキーがエンターかどうか、の分岐を付け加えます。そして、もしEnterが押されていたら、パスを実行すればいいわけですね。
だから、今回やるのは、OnKeyDownに、キーの分岐を付け加えるだけです。
というわけで、前回のOnKeyDownにEnterの押し分けを付け加えましょう。次のようになります。

    def OnKeyDown(self,event):
        key = event.GetKeyCode()
        if key == wx.WXK_UP:
            next = self.LBox.GetSelection() - 1
            self.LBox.SetSelection(next)
        elif key == wx.WXK_DOWN:
            next = self.LBox.GetSelection() + 1
            self.LBox.SetSelection(next)
        elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
            open_file = self.LBox.GetStringSelection()
            subprocess.Popen(open_file)
            wx.Exit()
            return
        else: event.Skip()

カーソルに関する部分をのぞき、Enterに関する部分のみにすると次のようになります。こっちのほうが見やすいでしょうか?説明はこっちのほうでしましょう。

    def OnKeyDown(self,event):
        key = event.GetKeyCode()
        if key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
            open_file = self.LBox.GetStringSelection()
            subprocess.Popen(open_file)
            wx.Exit()
            return
        else: event.Skip()

wx.WXK_RETURNかwx.WXK_NUMPAD_ENTERかが押されたら、で分岐させています。
ふたつのどちらか、という場合には、3行目のように

if key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):

と書きます。

4行目のopen_fileが、選択カーソルから取得したパスですね。そして、それをsubprocess.Popenで実行する、と。


備考

ここで書いた方法では選択しているのがファイル以外の場合にはうまく開けないようですね。エラーになっちゃいます。
あと、実行するパスが実際に存在するかどうか、というのを確かめる仕組みをつくったほうがいいですね。ちなみに、os.pathというのを使うことになります。
これらのことは、あとでちゃんと説明することになるとは思います。ちゃんと続いたら、ですが^^;
計算したところ、今のようなペースで連載を続けたら、最低でも15回までやらなきゃいけないっぽいんですねw
そこまで僕の根気は続くのでしょうか^^;

ちなみに、外部プログラムを実行する方法は、system()とかstarfile()とか他にもあるそうですが、ここで試したのが一番無難でランチャに向いているようです。気になる人はいろいろためしてください。

exp02

import wx,subprocess

class MyExcalibur(wx.PySimpleApp):
    
    def OnInit(self):
        PATH_FILE = "data/path.txt"
        self.List = []
        f = open(PATH_FILE, "r")
        for x in f.readlines():
            self.List.append(x.strip())
        f.close()
        Frm = wx.Frame(None, -1, "Excalipur", size=(400,48),pos=(400,400))
        self.TxtCtr = wx.TextCtrl(Frm, -1)
        self.TxtCtr.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText)
        self.lbFrame = wx.Frame(None, 0, "wxPython", size=(420,200),pos=(400,448),style=wx.DOUBLE_BORDER)
        self.LBox = wx.ListBox(self.lbFrame, -1, choices = self.List, size=(415,200))
        Frm.Show()
        self.LBox.SetSelection(0)
        self.lbFrame.Show()
        self.TxtCtr.SetFocus()
        return 1


    def OnKeyDown(self,event):
        key = event.GetKeyCode()
        if key ==  wx.WXK_ESCAPE:
            wx.Exit()
        elif key == wx.WXK_UP:
            count = self.LBox.GetCount()
            next = self.LBox.GetSelection() - 1
            if next >=  0:
                self.LBox.SetSelection(next)
            else: self.LBox.SetSelection(count - 1)
        elif key == wx.WXK_DOWN:
            count = self.LBox.GetCount()
            next = self.LBox.GetSelection() + 1
            if next < count:
                self.LBox.SetSelection(next)
            else: self.LBox.SetSelection(0)
        elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
            open_file = self.LBox.GetStringSelection()
            subprocess.Popen(open_file)
            wx.Exit()
            return
        else: event.Skip()

    def OnText(self,event):
        get_word = self.TxtCtr.GetValue()
        get_list = self.SearchWord(get_word)
        self.LBox.Set(get_list)
        event.Skip()

    def SearchWord(self,word):
        new_list = []
        for line in self.List:
            if word in line:
                new_list.append(line)
        return new_list


app = MyExcalibur()
app.MainLoop()