絖綛 N@i.jp  昨日:00022399
 今日:00031302
 総計:00053701
keywords
管理者専用
  Post   Add link   Control Panel 































新しいトピック
最新:04/16 19:55


新しいコメント
最新:07/28 16:47






管理人へMAIL

プライバシーポリシー

Raspberry Pi から IRKitを操作するGUIを作ってみた。

一番簡単に作れそうとの理由で、PythonのTcl/Tk実装であるTkinterを使ってみた


 これまでPythonプログラムでIRKit操作コマンドを作ってきました。(全然進みが遅いけど・・・)コマンドでの操作でもワタシは慣れているから良いけど、より一般的に使えるようにするにはGUIも欲しいよね。Pythonで一番簡単にGUI作るとしたら何だろうと思ってググッてみたら、PythonからTcl/Tkを使うTkinterってのが一番簡単そう。勉強と思って作り始めてみたのだけど、正直甘かった。Tkinterの入門的な日本語ホームページは色々見つかったのだけど、そこはやはり入門用。あまり深くは書いてなかったり、前程としてある程度Tcl/Tkの知識が必要で詳細はTcl/Tkのmanページを参照しなさいという感じだったりで、あちこちのホームページの記事をつまみ食いしながら作っていった感じ。Tkinterを網羅的に説明してくれている日本語のホームページか書籍が欲しいよぉ。

 そんな訳で、まだ全然未完成で保存してある赤外線信号を送信するだけの機能しかないけど、何とか動くものはできましたので、備忘録として書いておこうと思います。

1. 今まで作ってきたIRKit操作コマンドをモジュールとして使えるようにする

 GUIは今まで作ってきたIRKit操作コマンドに一皮被せたような感じにします。そのためIRKit操作コマンドをimportして使えるよう少し変えます。具体的にはimportした途端に argparse が動くと都合が悪いので、コマンドとして呼び出された時だけ argparse が動くようにしました。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
〜略〜
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Control IRKit')
    parser.add_argument('-v', '--version', action='version', version='%(prog)s 0.2.1')
    parser.add_argument('-V', '--verbose', action='store_true', default=False, help='verbose mode')
    subparsers = parser.add_subparsers(dest='command')
    # create the parser for the "get" command
    parser_get = subparsers.add_parser('get', aliases=['read'], help='GET command')
    parser_get.add_argument('ir_signal_name', action='store', help='IR signal name')
    parser_get.add_argument('-a', '--address', nargs=1, action='store', default=None, dest='host',
                            metavar='address', help='IP address of IRKit device')
    # create the parser for the "post" command
    parser_post = subparsers.add_parser('post', aliases=['send'], help='POST command')
    parser_post.add_argument('ir_signal_name', action='store', help='IR signal name')
    parser_post.add_argument('-a', '--address', nargs=1, action='store', default=None, dest='host',
                             metavar='address', help='IP address of IRKit device')
    # create the parser for the "show" command
    parser_show = subparsers.add_parser('show', aliases=['view'], help='Show IR signal data')
    parser_show.add_argument('ir_signal_name', action='store', help='IR signal name')
    # create the parser for the "list" command
    parser_list = subparsers.add_parser('list', help='Print list saved IR signal')
    # create the parser for the "delete" command
    parser_delete = subparsers.add_parser('delete', aliases=['remove'], help='Delete saved IR signal')
    parser_delete.add_argument('ir_signal_name', action='store', help='IR signal name')
    #
    args = parser.parse_args()
    verbose = args.verbose
else:
    verbose = False
〜略〜

 変更箇所は赤字の部分で、コマンドとして呼び出された時しか argpase を呼び出さないようにif文を入れています。これで importした時には argparse を呼び出さないはず。このように変更してファイル名"irkit_com.py"として保存しました。

2. GUIプログラム

 次はGUIプログラムの方。先ずは簡単に、保存してあるjsonファイルの一覧を表示して、その中の一つを選んで送信するだけのGUIプログラムを作ってみました。先の "irkit_com.py" と同じフォルダに "irkit_gui.py" という名前で以下を保存します。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
from tkinter import *
from tkinter.messagebox import showerror, showinfo
from irkit_com import get_irkit_info, get, post   # IRKit操作コマンド

save_dir = os.environ['HOME'] + '/' + '.irkit.d'  # Getした赤外線信号の保存先ディレクトリ名
help_win = None

def help_window():
    '''Help'''
    global help_win
    if help_win == None or not help_win.winfo_exists():
        help_win = Toplevel()           # ヘルプ用の新しいウィンドウを作る
        help_win.title('IRKit Help')    # ウィンドウのタイトル
        help_text = '''IRKitの使用方法

IRKitメニュー
  [GET...]	受信した赤外線信号を保存
  [POST]	選択した赤外線信号を送信
  [Quit]	終了

Helpメニュー
  [Help...]	ヘルプの表示
  [About...]	IRKitについて'''
        msg = Message(help_win, text=help_text, width=320, justify='left')
        msg.pack(side='left')
        help_win.resizable(0, 0)  # リサイズの禁止

def about_irkit():
    '''About message'''
    showinfo(title='about', message='IRKit GUI Ver.0.1.0')

def show_error(msg):
    '''Print error message'''
    showerror(title='IRKit error', message=msg)

class Application(Frame):
    '''application class'''
    def __init__(self, master):
        master.title('IRKit')     # ウィンドウのタイトル
        master.minsize(240, 200)  # ウィンドウサイズを制限
        Frame.__init__(self, master, borderwidth=4, bg='#d0d0d0')
        self.pack(expand=True, fill='both')
        self.createMenu(master)
        self.createWidgets()

    def send_signal(self):
        '''赤外線信号の送信'''
        signame = self.lb.get('anchor')
        if signame:
            info = get_irkit_info()
            address, port = info['IPaddress'], info['Port']
            post(address, port, signame)
        else:
            show_error('赤外線信号が選択されていません')

    def createMenu(self, master):
        '''メニューの作成'''
        # メニューバー
        menubar = Menu(master)
        master.config(menu=menubar)
        # IRKitメニュー
        irkit_menu = Menu(menubar, tearoff=False)
        menubar.add_cascade(label='IRKit', underline=0, menu=irkit_menu)
        # IRKitメニュー項目
        irkit_menu.add_command(label='GET...', under=0, command=None)    # 未作成
        irkit_menu.add_command(label='POST', under=0, command=self.send_signal)
        irkit_menu.add_separator()
        irkit_menu.add_command(label='Quit', under=0, command=sys.exit)

        # HELPメニュー
        help_menu = Menu(menubar, tearoff=False)
        menubar.add_cascade(label='Help', underline=0, menu=help_menu)
        # HELPメニュー項目
        help_menu.add_command(label='Help...', under=0, command=help_window)
        help_menu.add_command(label='Abount...', under=0, command=about_irkit)

    def createWidgets(self):
        '''画面の作成'''
        def post_signal(event):
            self.send_signal()

        # Listbox
        self.lb = Listbox(self, width=40, height=10, selectmode=SINGLE, bd=4, relief=FLAT)
        # Scrollbar
        sb_y = Scrollbar(self, orient=VERTICAL, command=self.lb.yview)
        self.lb.configure(yscrollcommand=sb_y.set)
        # Listbox, ScrollbarをGridderで配置
        self.lb.grid(column=0, row=0, sticky='news')
        sb_y.grid(column=1, row=0, sticky='ns')
        # リサイズ設定
        self.grid_columnconfigure(index=0, weight=1)
        self.grid_rowconfigure(index=0, weight=1)
        # IR信号ファイルの一覧をListboxに表示
        for file in sorted(os.listdir(save_dir + '/signals')):
            if file[0] != '.':
                self.lb.insert(END, os.path.splitext(file)[0])
        # バインディングの設定
        self.lb.bind('<Double-1>', post_signal)


def main():
    root = Tk()
    app = Application(master=root)
    app.mainloop()

if __name__ == '__main__':
    main()

 例によって「¥」は実際には半角の「\」(バックスラッシュ)です。このプログラムを、UTF-8、改行コードLFで保存します。たった115行のプログラムなんですけど、いったい何日かかったことやら。Tcl/Tkの知識もないこともあって、やりたい事をどのように実現すれば良いのか調べるのにも、まともに動かない原因を調べるのにもすごく時間がかかりました。たぶん詳しい人から見たらボロボロのプログラムなんだろうなぁ・・・しかし、たった115行のプログラムで、それなりのGUIアプリが作れるってのはスゴイことでもありますね。

3. 動かしてみる

 このプログラムを実行すると、Raspberry Pi (Linux)では

こんな具合になります。左のがメインウィンドウで、"~/.irkit.d/signals/"フォルダ配下に保存されているjsonファイルの一覧が表示されているだけのシンプルなものです。一覧から送信したい赤外線信号の名前を一つ選んでダブルクリックするか、

IRKitメニューから「POST」を選ぶと送信されます。メニュー項目には「GET...」がありますが、この機能は未実装です。

 因に、このプログラムはMacのPython3上でも動いていて、MacOS X 10.9 (Mavericks)で動かすと、こんな感じになります。

 MacOS X上ではちゃんとMacのルック&フィールに従った感じになるんですね。メニューはちゃんと画面上端のメニューバーに表示されますし、スクロールバーもMacのデザインのものになってます。また、Helpメニューには

何もしていないのに検索メニューが追加され、これがまたちゃんと機能しているようなのです。これはちょっとした驚きでした。


< 過去の記事 [ 9月の 全てのカテゴリ リスト ] 新しい記事 >

2015 calendar
9月
12345
6789101112
13141516171819
20212223242526
27282930


掲示板
最新:08/15 17:19


GsBlog was developed by GUSTAV, Copyright(C) 2003, Web Application Factory All Rights Reserved.