なんか、いまいちマルチスレッド化した各種情報表示処理がうまく終了してくれません。
一応改良版です。
ついでに、「ホスト名」「IPアドレス(V4)(インターフェース毎)」を表示するように修正しました。
手持ちの小さなLCDに表示するため、2行から1行に修正したりしています。
次は、何を表示しようか。
Pythonプログラミング
# -*- coding: utf-8 -*- # 日本語コメントをエラーとしないように、UTF-8で保存 # 使用するライブラリのインポート import datetime import threading import tkinter as Tk from time import sleep import psutil import smbus import socket class Frame(Tk.Frame): """ フレームを拡張する """ def __init__(self, master = None): # ウィンドウを作成 Tk.Frame.__init__(self, master) def setSize(self, width, height, isCenter = True): """ サイズを指定、ディスプレイ中央に表示する """ # サイズを指定した文字列 geometry = "{0}x{1}".format(width, height) if isCenter: # ディスプレイ中央に表示する # ディスプレイの幅と高さを取得 s_width = self.winfo_screenwidth() s_height = self.winfo_screenheight() # 表示する左上の座標を算出 win_left = int((s_width - width) / 2) win_top = int((s_height - height) / 2) # 表示位置を指定した文字列を追加 geometry += "+{0}+{1}".format(win_left, win_top) # サイズと表示位置を中央に指定(幅 x 高さ + 左位置 + 上位置) self.master.geometry(geometry) def setFullScreen(self, isFullScreen = True): """ ウィンドウのフルスクリーンを設定 """ self.master.fullScreenState = isFullScreen self.master.attributes("-fullscreen", self.master.fullScreenState) def setTitle(self, title): """ タイトルを指定する """ self.master.title(title) def disableMaximum(self): """ 最大化を無効化 """ self.master.resizable(False, False) class label(Tk.Label): """ labelクラスを拡張する """ def setFormat(self, value): """ フォーマット書式を指定する """ self.__dict__['format'] = value def disp_formated(self, *args): """ 指定されているフォーマット書式で表示する """ self['text'] = str(self.__dict__['format']).format(*args) class guiParts(): """ GUI部品を追加表示する すべてグリッド、左寄せで表示 """ def addLabel(self, row, column, text = '', format = '', isLeft = True): """ ラベルを追加表示する """ # 初期表示テキストが設定されておらず、フォーマットが設定されている場合は、フォーマット文字列を表示 if (text == '' and format != ''): lbl = label(self, text = format) else: lbl = label(self, text = text) # 表示フォント設定 lbl['font'] = ("", 20) # 表示フォーマットを設定 lbl.setFormat(format) if isLeft: lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.W) else: lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.E) return lbl def addButton(self, row, column, text, width = None): """ ボタンを追加表示する """ btn = Tk.Button(self, text = text, width = width) btn.grid(row = row, column = column, sticky = Tk.W) # 表示フォント設定 btn['font'] = ("", 15) return btn class raspiinfo(): @staticmethod def tempelature_humidity(): """ 温度と湿度を取得する """ ADR = 0x44 # I2Cの取得 ic2 = smbus.SMBus(1) # 測定頻度の設定 ic2.write_byte_data(ADR, 0x23, 0x34) sleep(0.5) # 測定値の読出し ic2.write_byte_data(ADR, 0xe0, 0x0) sleep(0.5) # 6バイト取得 d = ic2.read_i2c_block_data(ADR, 0x0, 6) # 0~1バイトが温度 # 測定データ(生データ)を取得 t = ((d[0]) << 8) | (d[1]) # 換算する t = -45 + (175 * t / 65535) # 3~4バイトが湿度 # 測定データ(生データ)を取得 h = ((d[3]) << 8) | (d[4]) # 換算する h = 100 * h / 65535 # 結果を返却 return t, h @staticmethod def cpu_temp(): """ CPU温度の取得 """ f = open("/sys/class/thermal/thermal_zone0/temp","r") cpu_temp = 0.0 for t in f: cpu_temp = t[:2]+"."+t[2:] f.close() return float(cpu_temp) @staticmethod def cpu_percent(): """ CPU使用率の取得 """ cpu_percent = psutil.cpu_percent(interval=1) return round(cpu_percent) @staticmethod def memory_info(): """ メモリ情報(使用量[MB]、総量[MB])の取得 """ mem = psutil.virtual_memory() mem_used = round(mem.used / 1024 / 1024) mem_total = round(mem.total / 1024 / 1024) return (mem_used, mem_total) @staticmethod def IP_info(): """ ホスト名、IPアドレスの取得(IPアドレスはIF:IP(V4)アドレスkジェイ式で複数) """ host = socket.gethostname() ip = [] ipinfo = psutil.net_if_addrs() for _if in ipinfo.keys(): # if毎の属性値 for ifattr in str(ipinfo[_if]).split(','): # ifの属性値 if ifattr.strip().startswith('address='): if ifattr.strip().split('=')[1] == "'127.0.0.1'": # Localアドレスははじく break else: ip.append('{0}:{1}'.format(_if,ifattr.strip().split("=")[1].strip("'"))) break return (host, ip) class dispSub(): @staticmethod def disp_clock(): """ 現在時刻を表示 """ # 表示情報の取得 now = datetime.datetime.now() # 表示 lblClock.disp_formated(now) @staticmethod def disp_temp(): """ 室温と湿度を表示 """ try: # 表示情報の取得 templature , humidity = raspiinfo.tempelature_humidity() # 表示 lblTemp.disp_formated(templature, humidity) except Exception as e: print(e) lblTemp.disp_formated(0, 0) @staticmethod def disp_cpu(): """ CPU温度とCPU使用率、メモリ情報を表示 """ # 表示情報の取得 cpu_info = (raspiinfo.cpu_temp(), raspiinfo.cpu_percent()) mem_info = raspiinfo.memory_info() # 表示 lblCpuTemp.disp_formated(cpu_info[0], cpu_info[1]) lblMem.disp_formated(mem_info[0], mem_info[1], (mem_info[0] / mem_info[1]) * 100) @staticmethod def disp_if(): """ ホスト名とIPアドレスを表示 """ # 表示情報の取得 ip_info = raspiinfo.IP_info() # 表示 lblHost.disp_formated(ip_info[0]) lblIP.disp_formated(ip_info[1]) @staticmethod def execute_disp_clock(): """ 別スレッドで現在時刻表示処理実行 """ while True: # 現在時刻を表示する dispSub.disp_clock() # 500mS待ち受ける global runThread for t in range(0, 5): if runThread: # 待つ(100mS) sleep(0.1) else: return @staticmethod def execute_disp_temp(): """ 別スレッドで室温と温度を表示処理実行 """ while True: # 現在室温、湿度を表示する dispSub.disp_temp() # 2000mS待ち受ける global runThread for t in range(0, 20): if runThread: # 待つ(100mS) sleep(0.1) else: return @staticmethod def execute_disp_cpu(): """ 別スレッドでCPU情報とメモリ情報を表示処理実行 """ while True: # 現在室温、湿度を表示する dispSub.disp_cpu() # 2000mS待ち受ける global runThread for t in range(0, 20): if runThread: # 待つ(100mS) sleep(0.1) else: return @staticmethod def execute_disp_IF(): """ 別スレッドでホスト名とIPアドレスを表示処理実行 """ while True: # 現在室温、湿度を表示する dispSub.disp_if() # 10S待ち受ける global runThread for t in range(0, 100): if runThread: # 待つ(100mS) sleep(0.1) else: return def clickQuit(event): """ 終了する """ # 別スレッド終了フラグをOFFに global runThread runThread = False # 別スレッドを終了させる global threads for thread in threads: thread._stop() # ウィンドウを閉じる global w w.master.destroy() # アプリケーション終了 print('finished !!') if __name__ == "__main__": global w # メインウィンドウを生成、各設定を行う w = Frame() w.setTitle('Raspberry Pi 情報') # サイズは指定せず、フルスクリーンで表示する w.setFullScreen(True) w.pack() # 項目ラベル guiParts.addLabel(w, 0, 0, '日時 時刻:' , isLeft = False) guiParts.addLabel(w, 1, 0, '室温 / 湿度:' , isLeft = False) guiParts.addLabel(w, 2, 0, 'CPU温度 / 使用率:' , isLeft = False) guiParts.addLabel(w, 3, 0, 'メモリ情報:' , isLeft = False) guiParts.addLabel(w, 4, 0, 'host:' , isLeft = False) guiParts.addLabel(w, 5, 0, 'IP(V4):' , isLeft = False) # 項目値 lblClock = guiParts.addLabel(w, 0, 1, format = '{0:%Y/%m/%d(%a) %H:%M:%S}') lblTemp = guiParts.addLabel(w, 1, 1, format = '{0:>5.1f} [°C] / {1:>5.1f} [%]') lblCpuTemp = guiParts.addLabel(w, 2, 1, format = '{0:>5.1f} [°C] / {1:>5.1f} [%]') lblMem = guiParts.addLabel(w, 3, 1, format = '{0:>4} / {1:>4} [MB] ({2:>5.1f} [%])') lblHost = guiParts.addLabel(w, 4, 1, format = '{0}') lblIP = guiParts.addLabel(w, 5, 1, format = '{0}') btnQuit = guiParts.addButton(w, 6, 1, '終了', 10) # 別スレッドでの実行フラグをONに global runThread runThread = True # 別スレッドで再表示処理を実行 global threads threads = [] threads.append(threading.Thread(target=dispSub.execute_disp_clock)) threads.append(threading.Thread(target=dispSub.execute_disp_temp)) threads.append(threading.Thread(target=dispSub.execute_disp_cpu)) threads.append(threading.Thread(target=dispSub.execute_disp_IF)) for thread in threads: thread.start() # 終了ボタンに終了処理をバインド btnQuit.bind('<1>', clickQuit) # メインウィンドウをメインスレッドとして実行 w.mainloop()