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































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


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






管理人へMAIL

プライバシーポリシー

Raspberry Pi から IRKitを操作するGUIを作ってみた(2)

そろそろちゃんとしたプログラム名も付けたいな


 Raspberry PiからIRKitを操作するGUIプログラムに、GET(最後に受信した赤外線信号を保存する)とDelete(保存してある赤外線信号を削除する)機能を追加してみました。
 GET機能もIRKit操作コマンド "irkit_com.py" で作成済みの get関数にGUIの皮を被せる形にするのですが、get関数には一つ問題があります。それはIRKitに受信した信号が無かった場合、標準エラー出力に "No data" と出力するだけで、呼び出した側には何も通知されないという事です。GUIプログラムの場合には、エラーのダイアログを出すようにすべきですよね。と言う事で、今回もIRKit操作コマンド "irkit_com.py" を少し変更する事から始めました。

def print_message(msg):
    '''Output error message to stderr'''
    if __name__ == '__main__':
        print(msg, file=sys.stderr)

def get(address, port, signal_name):
    '''GET sub-command'''
    irkit = IRKit(address, port)
    data = irkit.get()
    if data:
        ir_data = json.loads(data)
        file_name = make_filename(signal_name)
        if verbose:
            print('{0} <= {1}'.format(file_name, ir_data))
        with open(file_name, mode='wt', encoding='utf-8') as f:
            json.dump(ir_data, f, indent=2, separators=(',', ': '))
        return True
    else:
        print_message('No data')
        return False

 先ずコマンドとして実行された際に標準エラー出力にエラーメッセージを出す関数 "print_message()" を追加しました。この関数はモジュールとしてimportされた時には何もしません。
 次に "get()" の結果を呼び出し元へ通知する方法ですが、例外を上げる方法と復帰値で返却する2つの方法が考えられます。今回は簡単にBoolean型(True or False)の復帰値で返却するようにしてみました。

 これでGUIプログラムから "get()"関数を呼び出す準備はできましたが、その前に "irkit_gui.py" なんて適当な名前ではなくて、ちゃんとしたプログラム名も付けておきたいですね。しかし、こういう命名って結構難しかったりするんです。特にワタシにはそういうセンスが無いものですから・・・数日考えた揚げ句 "QuickIR" と言う名前にしてみました。これは、その昔Appleのフレームワークやコンポーネントの名前に "Quickほにゃらら" という物が多かった(QuickDraw, QuickTimeなど)のをヒントにしたのと、"QuickIR" でググッてもヒットするものが無かったという理由からです。
 と言う訳で、今まで作ってきたGUIプログラムの名前は、今日から "QuickIR.py" です(^^;)。

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

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

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

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

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

def about_QuickIR():
    '''About message'''
    showinfo(title='about', message='QuickIR Ver.0.2.0')

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

def confirm(msg):
    '''Confirm dialog'''
    return askyesno(title='QuickIR confirming', message=msg, default='no')

class Application(Frame):
    '''IRKit application class'''

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

    def get_signal(self):
        '''Get IR signal Dialog'''
        global get_win

        def save(*args):
            '''Save IR signal'''
            signame = self.en.get()
            if signame:
                info = get_irkit_info()
                if get(info['IPaddress'], info['Port'], signame):  # irkit_com.get()
                    self.insert2list()
                else:
                    show_error('受信した赤外線信号がありません')
                get_win.destroy()     # GET dialogを閉じる
            else:
                show_error('信号名を入力してください')

        if get_win == None or not get_win.winfo_exists():
            get_win = Toplevel()      # GET dialogを作る
            get_win.resizable(0, 0)   # リサイズの禁止
            # ラベル, Entry(入力フィールド), ボタンを作る
            Label(get_win, text='最後に受信した赤外線信号を保存します。').grid(column=0, row=0, columnspan=3)
            Label(get_win, text='信号名:').grid(column=0, row=1, sticky='w')
            self.en = en = Entry(get_win, width=24)
            en.grid(column=1, row=1, padx=8, columnspan=2)
            Button(get_win, text='Save',   command=save).grid(column=1, row=2)
            Button(get_win, text='Cancel', command=get_win.destroy).grid(column=2, row=2)
            #
            en.focus_set()            # 入力フィールドにフォーカス
            en.bind('<Return>', save) # リターンキーでも保存

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

    def delete_signal(self):
        '''赤外線信号の削除'''
        signame = self.lb.get('anchor')
        if signame:
            if confirm('"{0}"を削除します。よろしいですか?'.format(signame)):
                delete(signame)      # irkit_com.delete()
                self.insert2list()
        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=self.get_signal)
        irkit_menu.add_command(label='POST',   under=0, command=self.send_signal)
        irkit_menu.add_command(label='Delete', under=0, command=self.delete_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_QuickIR)

    def insert2list(self):
        '''Insert IR signal list to Listbox'''
        self.lb.delete(0, END)
        for file in sorted(os.listdir(save_dir + '/signals')):
            if file[0] != '.':
                self.lb.insert(END, os.path.splitext(file)[0])

    def createWidgets(self):
        '''画面の作成'''
        # Listbox
        self.lb = lb = Listbox(self, width=40, height=10, selectmode=SINGLE, bd=4, relief=FLAT)
        # Scrollbar
        sb_y = Scrollbar(self, orient=VERTICAL, command=lb.yview)
        lb.configure(yscrollcommand=sb_y.set)
        # Listbox, ScrollbarをGridderで配置
        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に表示
        self.insert2list()
        # バインディングの設定
        lb.bind('<Double-1>', self.send_signal)  # ダブルクリックで送信


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

if __name__ == '__main__':
    main()

 少しリファクタリングして冗長な処理を整理して、GET機能とDelete機能を追加しています。行数は約160行で前回のものから少し増えていますが、まぁこの程度ならまだホームページに載せられる規模ですかね。
 しかし、この QuickIR はまだまだ未完成で基本機能がやっと出来たと言った感じかな。特に異常系は全然で、ちょっとした異常事象(例えばIRKitデバイスが見つからない等)で簡単に落ちると思います。まぁTkinterでのGUIプログラミングも少しづつ面白くなってきたし、ボチボチやっていきましょうかね。


< 過去の記事 [ 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.