Pythonでコマンドラインランチャを作ろう!第六回−craftlaunch型と辞書

前置き

今日でもう6回目ですね。
craftlaunch型でのリスト表示とコマンドの実行をやります。
今回で、6回にわたって行ったつかにゃん講師によるPythonコマンドラインランチャを作ろう!基本編講座は終了いたします。ご静聴ありがとうございました^(ノ゚ー゚)ノ☆パチパチ☆ヾ(゚ー゚ヾ)^

概観

craftlaunch型ということでfenrir型と変わってくるのは、表示されたり絞り込まれたりするのがコマンドであって、実際に実行するパスとは異なる、ということですね。
コマンドとパスという、ふたつのペアになっている要素があるわけです。
だから、この二つを結びつけ、表示されるのはコマンドで実行するのはパス、というようなつながりを作る必要がある。
このために辞書を使います。辞書というのは、二つ以上の要素を一緒に扱えるリスト、という認識でいいと思います。
そこで、まず辞書の説明をした後に、具体的なやり方を書きます。

辞書の説明

辞書の定義は、次のようにします。

SampleDict = {"tukasa":"hiiragitukasa","kagami":"hiiragikagami","konata":"izumikonata"}

"tukasa":"hiiragitukasa"というのが一つの要素です。そして、それがカンマ(,)で区切られて、並んでいるんですね。
一つ一つの要素の中は、コロン(:)で区切られています。その左側がキーで、右側が値ですね。この場合だと、tukasaがキーで、hiiiragitukasaが値です。

利用の仕方ですが、

  1. キーの一覧の取得の仕方
  2. それぞれのキーから値を取り出す仕方
  3. 登録の仕方

の三つを知っていればいいと思います。

キーの一覧の取得

SampleDict.keys()

でできます。そうすると、キーの一覧がリストで出てきます。

['kagami', 'konata', 'tukasa']

みたいに。

キーから値を取り出す方法

SampleDict["opera"]

みたいにします。[]の中にキーを入れるんですね。そうすると、キーに対応する値が出てきます。この場合だと、C:\Program Files\Opera\Opera.exeですね。

登録の仕方

一つ一つ辞書に登録する方法です。
SampleDict["afx"] = "D:\bin\afx\AFX.EXE"
みたいにすると登録できます。[]の中に登録したいキーを入れて、右側にそれと結びつける値、ということですね。

解説

辞書は、だいたいリストと似ています。リストと違う点は、二つの要素がくっついている場合に利用しやすいというところです。今回の場合だと、登録コマンドと実行パスとが結びついていることになるんですね。

CmdDict = {'opera': 'C:\\Program Files\\Opera\\Opera.exe',
           'billy': 'D:\\bin1\\billy\\Billy.exe',
           'lightwire': 'D:\\bin1\\LightWrite\\LightWrite.exe', 
           'afx': 'D:\\bin\\afxw\\AFXWCMD.EXE',
           'firefox': 'C:\\Program Files\\Mozilla Firefox\\firefox.exe'}

というような辞書を作ることになります。

コマンドファイルの記述

では、本題に入ります。
以前、パスファイルを用意したように、今回はコマンドファイルを作ります。書式は以下です。

billy =D:\bin1\billy\Billy.exe
lightwire = D:\bin1\LightWrite\LightWrite.exe
afx = D:\bin\afxw\AFXWCMD.EXE
firefox = C:\Program Files\Mozilla Firefox\firefox.exe
opera = C:\Program Files\Opera\Opera.exe

左が登録コマンドで、右が実行パスですね。"="で結びつけるのがポイントです。
上みたいなのをテキストファイルで作って、dataフォルダに保存します。名前は、instant.txtにしましょう。dataフォルダというのは、第四回で作った、エクスカリパーと同じフォルダにあるフォルダです。

読み込みと辞書の作成

次に、これを読み込みます。
手順は、

  1. ファイルを開く
  2. 内容を取り出す
  3. 辞書への登録
  4. ファイルを閉じる

です。

    def OnInit(self):
        PATH_FILE = "data/instant.txt"
        self.Dict = {}
        f = open(PATH_FILE, "r")
        for x in f.readlines():
            line = x.split("=")
            key = line[0].strip()
            path = line[1].strip()
            self.Dict[key] = path
        f.close()
        self.List = self.Dict.keys()
        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

読み込みはOnInitで行います。少し長いので、関係する一部分のみ取り出し、そっちのほうで説明しましょう。

        PATH_FILE = "data/instant.txt"
        self.Dict = {}
        f = open(PATH_FILE, "r")
        for x in f.readlines():
            line = x.split("=")
            key = line[0].strip()
            path = line[1].strip()
            self.Dict[key] = path
        f.close()

3行目で、先に書いたコマンドファイルを開いています。これと、forによる繰り返しは前回やったのと同じですね。
forの中で繰り返す処理が、前回までと異なります。まず、split("=")で取り出した行を分割しています。行の中に"="があったら、それの前後で分割する、ということですね。その結果はリストになります。このとき、スプリットしたのを格納したlineは、

["billy","D:\bin1\billy\Billy.exe"]

となっています。コマンドとパスを取得するには、だから

            key = line[0].strip()
            path = line[1].strip()

とすればいいんですね。
keyには、lineの一番目の要素"billy"が。pathには二番目の要素"D:\bin1\billy\Billy.exeが代入されてます。
それを、辞書に登録します。

self.Dict[key] = path

がそれです。
で、これをf.readlines()の要素がなくなるまで続けるわけですね。
二回目のループのときには、lineの中身は

['lightwire ', ' D:\\bin1\\LightWrite\\LightWrite.exe\n']

に。三回目の時のlineの中身は

['afx ', ' D:\\bin\\afxw\\AFXWCMD.EXE\n']

になります。
これで、コマンドファイルからキーとパスとを合わせた辞書を作成しました。
ちなみに、中身は次のようになってます。

CmdDict = {'opera': 'C:\\Program Files\\Opera\\Opera.exe',
           'billy': 'D:\\bin1\\billy\\Billy.exe',
           'lightwire': 'D:\\bin1\\LightWrite\\LightWrite.exe', 
           'afx': 'D:\\bin\\afxw\\AFXWCMD.EXE',
           'firefox': 'C:\\Program Files\\Mozilla Firefox\\firefox.exe'}

リストボックスへのセット

次に、これをリストボックスにセットします。
リストボックスに表示したいのは、登録コマンドですよね。先に、辞書のキーとしたものです。だから、その一覧をリストとして取得し、それをセットすればいいわけです。
ここで、最初に説明したkeys()を使います。これはキーの一覧をリストとして取得するものです。

        self.List = self.Dict.keys()

self.Listの中身は、コマンドがリストになったものです。

['opera', 'billy', 'lightwire', 'afx', 'firefox']

ですね。
それを、ListBoxのchoicesで最初に表示する項目にセットします。

        self.LBox = wx.ListBox(self.lbFrame, -1, choices = self.List, size=(415,200))
        Frm.Show()


これで、コマンドをリストボックスに表示させることはできました。次は、実行時の話です。

パスの実行

リストに表示されているのは、コマンドです。これを取得し、それをパスに変換し、実行という手続きになります。この、コマンドのパスへの変換というのが前回との違いです。

では、説明を。キーから値を取り出す方法を利用します。
実行するのは、前回見たようにOnKeyDownですね。

    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):
            cmd_name = self.LBox.GetStringSelection()
            path_file = self.Dict[cmd_name]
            subprocess.Popen(path_file)
            wx.Exit()
            return
        else: event.Skip()

関係する一部分のみを。

        elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
            cmd_name = self.LBox.GetStringSelection()
            path_file = self.Dict[cmd_name]
            subprocess.Popen(path_file)
            wx.Exit()

まず、2行目でリストボックスの選択している箇所からGetStringSelection()で文字列を取り出します。それを、cmd_nameに代入しています。
次に、辞書を利用して、3行目でそこからパスを取り出します。self.Dict[cmd_name]により、cmd_nameで取り出したキーに対応したパスをpath_fileに代入している訳ですね。
そうすれば、もう実行パスは取り出されたのだから、4行目のsubprocess.Popenでそのパスを実行すればいいだけです。

まとめ

これで、fenrir型にしてもcraftlaunch型にしろ、ランチャとしての最低限のとこまではたどり着けたわけですね。あとは改良あるのみ!
たぶん、ここまでできたのだったらあとのほうはリファレンスを調べるなりなんなりして、なんとかなるのではないでしょうか。
というわけでこれにてPythonコマンドラインランチャを作ろう!基本編は終了です。しばらくの充電期間をおいてのち、Pythonコマンドラインランチャを作ろう!〜セカンドシーズン 花咲く乙女たちのかげに〜が連載される予定です。もう、まとまって書くこともないと思うので、10週書いて1年間休むという不定期連載になるやもです。それはさておき、Pythonコマンドラインランチャを作ろう関係のネタはまだ残っているのでそのうち7回目書きます。
つかにゃん先生の次回作にご期待くださいo(_ _*)o

exp03

import wx,subprocess

class MyExcalibur(wx.PySimpleApp):
    
    def OnInit(self):
        PATH_FILE = "data/instant.txt"
        self.Dict = {}
        f = open(PATH_FILE, "r")
        for x in f.readlines():
            line = x.split("=")
            key = line[0].strip()
            path = line[1].strip()
            self.Dict[key] = path
        f.close()
        self.List = self.Dict.keys()
        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):
            cmd_name = self.LBox.GetStringSelection()
            path_file = self.Dict[cmd_name]
            subprocess.Popen(path_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)
        self.LBox.SetSelection(0)
        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()