https://himadawa.doorblog.jp/archives/10160498.html
あっさりと作られるのは、凄い事です。それを楽しまれているのも素晴らしいです。
Himadawaさんが制作されたカバーとELパネルを Python で “On, Off” するGUIを試作します。
PythonでUSBシリアルリレーを”On, Off”します。
GNU/Linux では、コマンドとそれを組み合わせたスクリプトで過ごしてきた自分ですが、これは『人に優しくない』ので、GUIを習作します。
GNU/Linux を使い、コマンドを組み合わせたスクリプトは何となく作れる者の、これは、Python学習メモです。まとまっていません。貴方には読みにくいと思います。ごめんなさい。
「曇りの日が多くて星空が見られないよ〜」→「天体画像処理も一通り終えたな〜」→
「Siril開発版も落ち着いてきたな〜」→「プログラムでもしよっかな」→
「天体画像処理でも使えるからPythonだ」
- USBデバイスを調べる
- リレー動作確認
- Python周り (Python 環境のこと)
- デバイスファイル名を固定する
- Python スクリプト 自動起動させる
- Python シリアル事始め
- Pythonで行いたいこと:仕様 その1 (先ずは簡単に)
- Pythonで行いたいこと:仕様 その2 (こちらを目指したい)
- Python GUI 事始め
- import文
- Pythonの型とオブジェクト
- Python ボタン事始め
- Python ボタンに処理を含ませる:シリアルにA1,A0を送る
- Tkinterを使ったクラス化手法によるGUI
- TkinterのFrame, pack, gridをためす
- 制御構文や条件分岐 if文のこと
- テキスト表示用ウィジェット
- イベントループ:「何かしてよ」— .pyより —
- 参考にしたURL その1
- 参考にしたURL その2
- /devデバイス選択、Class未使用、4ボタン、丸四角ステータスランプ
USBデバイスを調べる
https://devicehunt.com
こちらでUSBデバイスを調べます。
今回使う、USB RELAY X-RL1 のベンダーidとプロダクトidを入れると、
Microchip Technology, Inc.
CDC RS-232 Emulation Demo
リレー動作確認
コマンドを送って、USBリレーモジュールの動作確認を行います。
物は、USB RELAY X-RL1 及び、USB RELAY X-RL2
- USBモジュールを差し込み、/dev/ttyUSB0 もしくは /dev/ttyACM0 などを確認します。
- $ sudo stty -F /dev/ttyACM0 ← ボーレートの設定。設定無しでも良い。
- $ sudo echo -n “1” > /dev/ttyACM0 ← グループにユーザー追加せずに、sudoを用いる。
- $ sudo echo -n “0” > /dev/ttyACM0 ← 改行が不要な場合は、-nオプション。
USB RELAY X-RL2 は基盤プリントに “A1送信でリレーAがON” とあるので以下で動作確認します。
(これは未確認 10,2025)
- $ sudo echo -n “A1” > /dev/ttyACM0
- $ sudo echo -n “A0” > /dev/ttyACM0
- $ sudo echo -n “B1” > /dev/ttyACM0
- $ sudo echo -n “B0” > /dev/ttyACM0
Python周り (Python 環境のこと)
SirilもPythonスクリプトを多用しています。
自分もPythonコードを少しは分かるようになりたいので、Pythonを使います。
OSはGUN/Linux, Ubuntu 24 です。
GUIを扱う”Tkinter”
シリアルを扱う”pyserial”を用意します。
https://packaging.python.org/ja/latest/tutorials/installing-packages
ここを参照し、仮想環境も作ります。
$ python3 -m pip --version #バージョン確認
$ python3 -m venv tutorial_env #試作ディレクトリを作成
$ cd tutorial_env
$ source ./bin/activate #試作ディレクトリを活性化
$ python3 -m pip install tkinter
$ python3 -m pip install pyserial
Pythonのシリアル通信モジュールには、2種類あるそうです。
・pyserial ← こちらを使います。
・serial
Visual Studio Code が便利そうなので使います。
https://code.visualstudio.com/download
“.deb”ファイルをダウンロードし、$ sudo apt install code_????_amd64.deb
カレントディレクトリで、Visual Studio Code を動かします。
$ code .
日本語パッケージを使います。Japanese Language Pack
Python言語サポートする拡張機能も使います。
デバイスファイル名を固定する
頻繁に、PCに接続する機器が変わらないならば、pythonコードにデバイスを指定します。
USBデバイスを入れ替えるならば、コネクターに挿すごとにデバイスファイル名が変わるのは、困ります。
その場合は、udevコンフィグファイルを指定することで、シンボリックリンクを作ります。
USB RELAY X-RL1 は、syslogを見ると、
New USB device found, idVendor=04d8, idProduct=000a, bcdDevice= 1.01
New USB device strings: Mfr=1, Product=2, SerialNumber=3
Product: USB-RELAY1
SerialNumber: X-RL1
mtp-probe: checking bus 2, device 5: "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1"
USB端子の箇所を変えると、
mtp-probe: checking bus 2, device 6: "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2"
$ lsusb
Bus 002 Device 008: ID 04d8:000a Microchip Technology, Inc. CDC RS-232 Emulation Demo
$ udevadm info -a -n /dev/ttyACM0
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1':
KERNELS=="2-1.1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
udevのルールを記述して名前を指定します。
参考にしたのはこの方
https://qiita.com/porizou1/items/f8e23e32ed8e1be85c65
/etc/udev/rules.d ディレクトリに、指定ファイルを置きます。
https://wiki.archlinux.jp/index.php/Udev
上記を参照
/etc/udev/rules.d/99-usb-switches.rules
ACTION=="add", KERNELS=="2-1.1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", SYMLINK+="relay_X-RL1", GROUP="dialout"
# Web カメラならばこんな感じかな
# KERNEL=="video*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="YYYY", ATTR{index}=="0", SYMLINK+="usb-camera-video0", GROUP="video", MODE="0666"
ATTRS{idVendor}、ATTRS{idProduct}:デバイスに応じて適宜設定するSYMLINK:固定したい名称- GROUP:グループを指定。”Arduino”を使用したOnStep赤道儀、”Althiba”を使うのに”dialout”グループを指定しているので、これを用います。
- 実行権限を追加するには
MODE="0666"を指定する。
udevに新しいルールを適応させます。
$ sudo udevadm control --reload-rules && sudo udevadm trigger
上記の様に設定すれば、”/dev/relay_X-RL1″ と指定できます。
ここまで行って気づいたのは、プルダウンメニューで、”/dev/tty何とか”を選び、指定するのも良いかもしれません。
Python スクリプト 自動起動させる
Ubuntu + Python + venv(仮想環境)を、PC起動時にPythonスクリプトを動かします。
また、まわり道です。
https://widedeepspace.net/2024/09/03/1634
こちらの方を参考にして
Exec=gnome-terminal -- bash -c "'/home/UserName/ProjectName/venv/bin/python' '/home/UserName/ProjectName/test.py';$SHELL"
この部分を下記の様にしました。
Exec=bash -c "source /home/USERNAME/HimadawaCoverEL/venv/bin/activate && python3.13 /home/USERNAME/HimadawaCoverEL/HimadawaCoverEL.py"
/home/minmin/.config/autostart/gnome-Himadawa-terminal.desktop
以下にファイルを作ります。
[Desktop Entry]
Type=Application
Name=My Venv Script
Exec=bash -c "source /home/USERNAME/HimadawaCoverEL/venv/bin/activate && python3.13 /home/USERNAME/HimadawaCoverEL/HimadawaCoverEL.py"
Terminal=false
PC起動時に、”HimadawaCoverEL.py”も画面に表示されます。
Python シリアル事始め
https://www.python.jp/train/index.html
“プログラミング言語 Python情報サイト” こちらも参考にします。
分かったことを確認するために、ここに戻ります。
https://qiita.com/umi_mori/items/757834e0ef75f38cea19
先ずは、こちらの方から学びます。
コマンドを用いて、USBシリアルに”1″を送ります。”1″を”0″に代えると”0″を送ります。
serial-send_1.py
# serial モジュールを読み込み
import serial
# serial ポート指定
PORT ='/dev/ttyACM0'
# serial ボーレート指定
BAUDRATE = 9600
# serial を開ける
# timeout引数は、データの受信待機時間を指定
ser = serial.Serial(PORT, BAUDRATE, timeout=1)
# serial "1" 送る
data = '0'
ser.write(data.encode())
# serial 状態を受信
print(ser)
# serial を閉じる
ser.close()
他のソースでは、
import serial
print("===== USBデバイスを指定中 =====\n")
# Set Parameter USBデバイスを指定
deviceName = '/dev/ttyACM0'
baudrateNum = 9600
timeoutNum = 3
print("===== USBデバイス指定終わり =====\n")
# Read Serial
### readSer = serial.Serial('/dev/ttyACM0',9600, timeout=3)
readSer = serial.Serial(deviceName, baudrateNum, timeout=timeoutNum)
c = readSer.read() # 1 byte
### string = readSer.read(10) # 10 byte
### line = readSer.readline() # 1 line (upto '\n')
print("Read Serial:")
print(c)
readSer.close()
print("===== Serial読み込み終わり =====\n")
# Serial 書き込み
serialCommand = "1" # "0"で閉じる
writeSer = serial.Serial(deviceName, baudrateNum, timeout=timeoutNum)
writeSer.write(serialCommand.encode())
writeSer.close()
print("===== Serial書き込み終わり =====\n")
# serial 読み込み
# 文字列に変数を埋め込む f-strings、フォーマット済み文字列リテラル
print(f"シリアル状態:{readSer}")
print("===== プログラム終了 =====\n")
$ python3.13 serial-send_1.py
$ python3.13 serial-send_0.py
USB RELAY X-RL1 は、動作確認ができました。
Pythonで行いたいこと:仕様 その1 (先ずは簡単に)
- USBデバイスのパスと通信速度をソースで指定
- カバー On, Off スイッチ
- ELパネル On, OFF スイッチ
- スイッチをそれぞれに2つ
- それぞれの状態は、ステータスランプ
- カバー[開]、ELパネル[点灯]は、[緑色] → ステータスランプ
- カバー[閉]、ELパネル[消灯]は、[橙色] → ステータスランプ
Pythonで行いたいこと:仕様 その2 (こちらを目指したい)
- /dev/ を一覧表示
- /dev/一覧表示からデバイス、ボーレートを選ぶ
- デバイスを接続、若しくは切断
- カバー、ELパネル、On,Offスイッチ
- ステータスランプを設ける
- カバー[開]、ELパネル[点灯]は、ステータスランプ[緑色]
- カバー[閉]、ELパネル[消灯]は、ステータスランプ[橙色]
Python GUI 事始め
『習うよりも、慣れろ』これはプログラミングで良く言われることです。
本やサイトにあるコードを見たならば、一行でもコードを打ち込む。ですね。
tkinter を用いてGUIを表示させます。
#は、コメントになります。
“””
これもコメントになります。
複数行のコメントになります。
“””
“”で囲むと文字列
処理ブロックは、段落(インデント)ごと
文の終わりは改行
# 1. ライブラリのインポート
import tkinter as tk
# 2. メインウィンドウの作成
# tk.Tk()は、アプリケーションのメインウィンドウを作成します
# すべてのTkinterアプリケーションは、まずこのインスタンスから始まります
root = tk.Tk()
# 3. ウィンドウの設定
root.title("僕のサイズは 400 x 300")
# ウィンドウサイズと位置を指定
root.geometry("400x300+100+50") # サイズ400x300、画面上の位置(100,50)
# root.geometry("400x300") # サイズ400x300 のみ
# 4. ウィジェットの作成と配置
# ウィジェットとは、GUIを構成する部品のことです
# ボタン、テキストボックス、ラベル、メニューなどです
label = tk.Label(root, text="Hello, Tkinter!")
label.pack()
# 5. イベントループの開始
# ウィンドウを表示させ、操作を待ち続けさせる
root.mainloop() #これが無いと一瞬で消える

[カバー部分]や、[EL部分]などの様に、部分を一つのモノとして扱うには、オブジェクト指向
from tkinter import ttk
from tkinter import ttk とは、" tkinter.ttk →Tk のテーマ付きウィジェット, Ttk ” との事です。
ボタンやチェックがつく外観を使えるようです。
https://kuroro.blog/python/L9hqzwxZAx0soqSmI8zE/
import文
import 文は、利用するモジュールを次のように指定します。
次の様に指定します。
import tkinter # tkinterモジュールを使用する
モジュールをインポートしたら、
モジュール名.関数名()
の形式で、そのモジュールの関数を使えるようになります。
Pythonの型とオブジェクト
関連するデータと、それに対する操作をまとめて、これを一つのモノとして捉えます。
↓
変数と、関数をまとめて、これをオブジェクトとして捉えます。
オブジェクトの属性やこの値を「プロパティ」と呼ぶ。
関数を「メソッド」と呼ぶ。
「クラス」 → 「インスタンス化」 → 「オブジェクト」
望遠鏡の設計図 → インスタンス化 → 実際の望遠鏡
オブジェクトを使って、
・シリアルデバイスを選び、ボーレートを指定。接続、切断ボタン
・カバー開ボタン、カバー閉ボタン。EL点灯ボタン、EL消灯ボタン
これらをオブジェクトとして配置する。
Pythonでは、Pythonが操作するいろいろな種類のデータやプログラムなどのことを、まとめて オブジェクト と呼びます。
Python ボタン事始め
tkinter を用いて、GUIに「ボタン」を表示させます。
https://japan-cyber.com/archives/3685
この方から
import tkinter as tk
# メインウィンドウ作成
root = tk.Tk()
# メインウィンドウのタイトル
root.title("HimadawaCoverEL制御")
# メインウィンドウを640x480にする
root.geometry("480x280")
# 1 Cover 開くボタン
def on_button1_click():
print("Cover開ボタンがクリック")
# 1 Cover 開くボタン処理
button1 = tk.Button(root, text="Cover開 : A1", command=on_button1_click)
button1.pack()
# 2 Cover 閉じるボタン
def on_button2_click():
print("Cover閉ボタンがクリック")
# 2 Cover 閉じるボタン処理
button2 = tk.Button(root, text="Cover閉 : A0", command=on_button2_click)
button2.pack()
# 3 EL点灯ボタン
def on_button3_click():
print("EL点灯ボタンがクリック")
# 3 EL点灯ボタン処理
button3 = tk.Button(root, text="EL点灯 : B1", command=on_button3_click)
button3.pack()
# 4 EL消灯ボタン
def on_button4_click():
print("EL消灯ボタンがクリック")
# 4 EL消灯ボタン処理
button4 = tk.Button(root, text="EL消灯 : B0", command=on_button4_click)
button4.pack()
# rootを表示し無限ループ
root.mainloop()

Python ボタンに処理を含ませる:シリアルにA1,A0を送る
import tkinter as tk
import serial
# pySerialライブラリ、シリアルポートの一覧を取得します
# 後ほど、一覧取得に使います。
#import serial.tools.list_ports
# シリアルポートとボーレートの設定
# 接続しているデバイスのCOMポート名
# Windowsでは'COM3'、macOSやLinuxでは'/dev/ttyACM0'など
PORT = '/dev/ttyACM0'
BAUDRATE = 9600
# シリアル通信の初期化
# serial.Serial()を使ってシリアルポートを開きます。
# ser = serial.Serial(PORT, BAUDRATE)
# 何らかのエラーで開けなかった場合は、エラーメッセージを表示します
# try-except構文を使用して、シリアル通信中に発生する可能性のあるエラーを捉え
# 適切に処理します。
try:
ser = serial.Serial(PORT, BAUDRATE)
print(f"シリアルポート {PORT} を開きました。")
except serial.SerialException as e:
print(f"エラー: シリアルポート {PORT} を開けませんでした。{e}")
ser = None
# send_data関数: シリアルポートにデータを送信する汎用的な関数です。
# ser.write()を使って、引数で受け取った文字列を
# バイトデータにエンコード(.encode('utf-8'))をして送信します。
def send_data(data):
"""シリアル通信でデータを送信する関数"""
if ser and ser.is_open:
try:
###ser.write(data.encode('utf-8')) # エンコードして送信
ser.write(data.encode()) # エンコードしないで送信
print(f"'{data}' をシリアル送信しました。")
except serial.SerialException as e:
print(f"エラー: データ送信中に問題が発生しました。{e}")
else:
print("エラー: シリアルポートが開いていません。")
# send_A1とsend_A0関数:
# それぞれのボタンがクリックされたときに呼び出される関数(イベントハンドラ)。
# 対応する文字列をsend_dataに渡します。
def send_A1():
"""A1を送信するボタンのイベントハンドラ"""
send_data("A1")
def send_A0():
"""A0を送信するボタンのイベントハンドラ"""
send_data("A0")
def send_B1():
"""B1を送信するボタンのイベントハンドラ"""
send_data("B1")
def send_B0():
"""B0を送信するボタンのイベントハンドラ"""
send_data("B0")
# Tkinterのウィンドウを作成
root = tk.Tk()
root.title("HimadawaCoverEL制御")
#root.geometry("300x150")
root.geometry("480x280")
# tk.Button()でボタンを作成し、
# command引数にボタンがクリックされたときに実行する関数を指定します。
# button.pack()でボタンをウィンドウに配置します。
# x方向の外部間隔はpadx、y方向の外部間隔はpadyで指定します。
# A1送信ボタン
button_a1 = tk.Button(root, text="Cover開:A1", command=send_A1)
button_a1.pack(pady=10)
# A0送信ボタン
button_a0 = tk.Button(root, text="Cover閉:A0", command=send_A0)
button_a0.pack(pady=10)
# B1送信ボタン
button_b1 = tk.Button(root, text="EL点灯:B1", command=send_B1)
button_b1.pack(pady=10)
# B0送信ボタン
button_b0 = tk.Button(root, text="EL消灯:B0", command=send_B0)
button_b0.pack(pady=10)
# ウィンドウが閉じられた時に、シリアルポートも閉じるための処理をします
# ウィンドウクローズ時の処理:
# on_closing関数は、ウィンドウが閉じられたときに呼ばれます。
# この関数内でser.close()を実行し、シリアルポートを閉じます。
def on_closing():
"""ウィンドウが閉じられたときに実行する関数"""
if ser and ser.is_open:
ser.close()
print("シリアルポートを閉じました。")
root.destroy()
# ウィンドウマネージャのイベントを処理
# ウィンドウの閉じるボタン(WM_DELETE_WINDOW)が押された時の処理を指定します
root.protocol("WM_DELETE_WINDOW", on_closing)
# イベントループ
root.mainloop()
Tkinterを使ったクラス化手法によるGUI
https://qiita.com/michimichix521/items/4d8721aaa59e1c913d9a
クラス化は、
同じ手続きを繰り返さないで、つまり、同じボタンを表示するのに何度も手続きを繰り返さない為に、表示するボタンを「自分専用のボタンクラス」として定義すること。
でも、今回は操作ボタンは4つ、シリアル選択、接続&切断トグルボタンが1つなので、
まずは、オブジェクト指向を用いないで記述します。手続き型で記述します。
TkinterのFrame, pack, gridをためす
wedget (ウィジェット)
ボタン、ラベル(表示)、ラジオボタンなどの オブジェクトのこと
制御構文や条件分岐 if文のこと
制御構文とは、
プログラムの流れを分けること
プログラムをただ単に上から順に処理していくには、その限界があります。
そこで、
・条件分岐 → 処理を分岐させる → if : → if と 『コロン』
・繰り返し → 処理を繰り返させる → for と while
テキスト表示用ウィジェット
視覚が改善されたウィジェットを使用したい場合はfrom tkinter import ttk
イベントループ:「何かしてよ」— .pyより —
mainloop()メソッドは、アプリケーションを画面に表示させ続け、GUIでの入力やシステムイベントを待ち続け、イベントがあれば処理します。
- 画像の表示:ウィンドウやウィジェット(ボタン、ラベルなど)を画面に表示し続ける
- イベント待ち:マウスクリック、キーボード入力、ウィンドウの大きさ変更などを待つ
- イベントを検出:発生したイベントを取得する
- イベントの処理:ボタンを押した時やキーボード入力などの処理 (コールバック関数)を行う
- 画面の更新:必要に応じてウィジェットの表示を更新する
- ループの継続:アプリケーションが終了するまで、上記を繰り返す
参考にしたURL その1


参考にしたURL その2



youtube URL https://www.youtube.com/watch?v=nsJtQeAFeXo&t=1s
youtube URL https://www.youtube.com/watch?v=9EXt69Vlm_E&t=9s
内容を詰めずにコピー&ペーストをした部分があります。
動作部分がそれです。一番大切な部分ですね。皆さん有難うございます。
def toggle_connection():
“””接続、及び、切断のトグル”””
def send_command(command, lamp_canvas, lamp_id):
“””シリアルポートにコマンドを送信し、ランプを更新する”””
/devデバイス選択、Class未使用、4ボタン、丸四角ステータスランプ
"""
11 2025
(c) globe3.ddns.net
Himadawa Cover ELPanel USB Serial Relay Operation
"""
# モジュール読み込み tk, messagebox, serial, serial走査
import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
# シリアルポートオブジェクトを初期化
ser = None
# キャンバスのアイテムID(ステータスランプ)を保持するためのNone変数
oval_lamp_item = None
rect_lamp_item = None
"""
"= None"
初期化時に変数に値を設定しない場合
関数が明示的に値を返さない場合
"""
def get_serial_ports():
"""利用可能なシリアルポートをリストアップします"""
ports = serial.tools.list_ports.comports()
return [port.device for port in ports]
def update_ports():
"""ポートリストを更新してコンボボックスに反映させます"""
port_menu['values'] = get_serial_ports()
if get_serial_ports():
port_menu.set(get_serial_ports()[0])
def toggle_connection():
"""接続、切断のトグルボタン"""
global ser
if connect_button['text'] == '接続':
port = port_menu.get()
baudrate = int(baud_menu.get())
if not port:
messagebox.showerror("エラー", "接続ポートを選んで下さい。WindowsはCOM。LinuxはttyACM0など")
return
try:
ser = serial.Serial(port, baudrate, timeout=1)
connect_button.config(text='切断')
messagebox.showinfo("接続", f"{port}に接続しました。")
except serial.SerialException as e:
messagebox.showerror("エラー", f"接続に失敗しました: {e}")
else:
if ser and ser.is_open:
ser.close()
ser = None # シリアルを初期化
connect_button.config(text='接続')
messagebox.showinfo("切断", "シリアルポートを切断しました。")
def send_command(command, lamp_canvas, lamp_id):
"""シリアルポートにコマンドを送信し、ステータスランプを更新します"""
global ser
if ser and ser.is_open:
try:
ser.write(command.encode('utf-8'))
###response = ser.readline().decode('utf-8').strip() # 文字コード指定
response = ser.readline().decode().strip() # デコード無し
print(f"送信: {command}, 受信: {response}")
# 受信した応答に基づいてランプの色を変更
if response == "1": # USBシリアルからの戻り値"1"
#lamp_canvas.itemconfig(lamp_id, fill="green") # 開、点灯時
lamp_canvas.itemconfig(lamp_id, fill="#CCFF00") # 開、点灯時
elif response == "0": # USBシリアルからの戻り値"0"
#lamp_canvas.itemconfig(lamp_id, fill="orange") # 閉、消灯時
lamp_canvas.itemconfig(lamp_id, fill="#FFCC33") # 閉、消灯時
else:
lamp_canvas.itemconfig(lamp_id, fill="gray") # 戻り値不明な場合は灰色
except serial.SerialException as e:
messagebox.showerror("通信エラー", f"データの送受信に失敗しました: {e}")
else:
messagebox.showwarning("接続していません", "シリアルポートを選んで下さい。")
def on_closing():
"""
ウィンドウ終了時にシリアルポートを閉じます
on_closing関数は、ウィンドウが閉じられたときに呼ばれます。
"""
global ser
if ser and ser.is_open:
ser.close()
root.destroy()
# メインウィンドウの設定
root = tk.Tk()
root.title("HimadawaCover_ELパネル制御")
root.protocol("WM_DELETE_WINDOW", on_closing) # ウィンドウ閉じるボタンでシリアルを開放する
# 接続設定エリア
connection_frame = ttk.LabelFrame(root, text="接続設定", padding="10")
connection_frame.pack(padx=10, pady=10, fill="x")
# ポートを指定
port_label = ttk.Label(connection_frame, text="接続ポート:")
port_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
port_menu = ttk.Combobox(connection_frame, state="readonly")
port_menu.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
# ポートを再走査
reload_button = ttk.Button(connection_frame, text="再読込", command=update_ports)
reload_button.grid(row=0, column=2, padx=5, pady=5)
# 接続速度を指定
baud_label = ttk.Label(connection_frame, text="接続速度:")
baud_label.grid(row=1, column=0, padx=5, pady=5, sticky="w")
###baud_menu = ttk.Combobox(connection_frame, values=["9600", "19200", "38400", "57600", "115200"], state="readonly")
baud_menu = ttk.Combobox(connection_frame, values=["9600", "19200", "38400"], state="readonly")
baud_menu.set("9600") #初めは9600
baud_menu.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
# 接続ボタン表示
connect_button = ttk.Button(connection_frame, text="接続", command=toggle_connection)
connect_button.grid(row=2, column=0, columnspan=3, padx=5, pady=10, sticky="ew")
update_ports()
# スイッチエリアを設定
switch_frame = ttk.LabelFrame(root, text="鏡筒カバー ELパネル 操作", padding="10")
switch_frame.pack(padx=10, pady=10, fill="x")
# カバー開閉の操作
cover_frame = ttk.Frame(switch_frame)
cover_frame.pack(pady=5, fill="x")
# カバー開閉の丸いステータスランプ
###cover_lamp_canvas = tk.Canvas(cover_frame, width=20, height=20, bg="white", relief="sunken", bd=1)
cover_lamp_canvas = tk.Canvas(cover_frame, width=25, height=25, bg="lightgray", bd=1) # キャンバスを背景色、枠無し
cover_lamp_canvas.pack(side="right", padx=10)
oval_lamp_item = cover_lamp_canvas.create_oval(2, 2, 25, 25, fill="gray", outline="black")
# カバー開閉ボタン
a1_button = ttk.Button(cover_frame, text="カバー開(A1)", command=lambda: send_command("A1", cover_lamp_canvas, oval_lamp_item))
a0_button = ttk.Button(cover_frame, text="カバー閉(A0)", command=lambda: send_command("A0", cover_lamp_canvas, oval_lamp_item))
a1_button.pack(side="left", padx=5)
a0_button.pack(side="left", padx=5)
# ELパネル点灯/消灯の操作
el_panel_frame = ttk.Frame(switch_frame)
el_panel_frame.pack(pady=5, fill="x")
# ELパネルの四角いランプ
el_panel_lamp_canvas = tk.Canvas(el_panel_frame, width=25, height=25, bg="gray", relief="sunken", bd=1)
el_panel_lamp_canvas.pack(side="right", padx=10)
rect_lamp_item = el_panel_lamp_canvas.create_rectangle(2, 2, 25, 25, fill="gray", outline="black")
# ELパネル、点灯消灯ボタン
b1_button = ttk.Button(el_panel_frame, text="ELパネル点灯(B1)", command=lambda: send_command("B1", el_panel_lamp_canvas, rect_lamp_item))
b0_button = ttk.Button(el_panel_frame, text="ELパネル消灯(B0)", command=lambda: send_command("B0", el_panel_lamp_canvas, rect_lamp_item))
b1_button.pack(side="left", padx=5)
b0_button.pack(side="left", padx=5)
# イベントループ
root.mainloop()

Ubuntu 24 では動作しました。次は、Windows11で動作確認です。


コメント
こんばんは。
HPへのご招待ありがとうございます。
自分ならターミナルソフトでコマンドを打って使ってと、こうちゃんさんに言ってしまう所ですが、GUIで操作できると喜ばれると思います。
自分は50年位前にかじったBASICしかわかりませんので,今の言語は全くです。
何にしても自分で製作するの楽しいですね。
17日が楽しみです。
後ほどゆっくりとHPを拝見させて頂きます。
のちほ
himadawaさん、コメント有難うございます。
急ぎならば、私もターミナルソフトのショートカットをディスクトップに作り、
「カバー開くは、COM5へ”1″を送信」など書いたポストイットを貼り付けるかもしれません。
自作した物が思い通りに動くのが、楽しいですよね。