[Python] Monitorowanie portów COM

Tutaj umieszczamy tematy związane z językami programowania niepasującymi do innych działów.
Regulamin forum
Temat prosimy poprzedzić nazwą języka umieszczonego w nawiasach kwadratowych np. [Pascal].
Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

[Python] Monitorowanie portów COM

Postautor: GrumpyRez » piątek 30 maja 2025, 12:54

Ktoś, coś...

Potrzebuje prostego programu powiadomień w zasobniku systemowym.. ewentualnie jako widżet, albo po prostu małe okienko które można zakotwiczyć np. gdzieś w rogu (to samo w sumie mogło by być jako ikona powiadomień systemowych).
Do monitorowania podpiętych/aktywnych portów COM (coś ala FT232 Watcher z MKavrCalkulatora).

jako że pythona raczej znam tylko z zoo, a i programowanie w środowisku windows nie jest moją mocną stroną.
Zapytałem chataGPT

Niestety nie do końca to działa.

Kod: Zaznacz cały

import serial.tools.list_ports
import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
import threading
import time

def get_com_ports():
    ports = serial.tools.list_ports.comports()
    return [(port.device, port.description) for port in ports]

def create_image():
    # Tworzenie prostej ikony (16x16 px)
    image = Image.new('RGB', (64, 64), "white")
    draw = ImageDraw.Draw(image)
    draw.rectangle((16, 16, 48, 48), fill="blue")
    return image

def show_ports(icon):
    ports = get_com_ports()
    if ports:
        msg = "\n".join([f"{port}: {desc}" for port, desc in ports])
    else:
        msg = "Brak podłączonych portów COM."
    icon.notify(msg, "Lista portów COM")

def exit_action(icon, item):
    icon.stop()

def build_tray():
    icon = pystray.Icon("com_monitor")
    icon.icon = create_image()
    icon.title = "Monitor portów COM"

    icon.menu = pystray.Menu(
        item('Pokaż porty COM', lambda: show_ports(icon)),
        item('Wyjście', lambda: exit_action(icon, None))
    )

    icon.run()

# Uruchomienie w osobnym wątku, by nie blokować GUI
threading.Thread(target=build_tray).start()
Ostatnio zmieniony piątek 30 maja 2025, 13:13 przez GrumpyRez, łącznie zmieniany 1 raz.

Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » piątek 30 maja 2025, 12:58

PS. jest jeszcze możliwość w C#
za pomocą
System.IO.Ports.SerialPort.GetPortNames() oraz ManagementObjectSearcher z System.Management do pobrania pełnych nazw urządzeń.

Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » piątek 30 maja 2025, 13:15

Ok. soft po uruchomieniu sypie błędami

Kod: Zaznacz cały

An error occurred when calling message handler
Traceback (most recent call last):
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_win32.py", line 412, in _dispatcher
    return int(icon._message_handlers.get(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_win32.py", line 224, in _on_notify
    descriptors[index - 1](self)
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_base.py", line 328, in inner
    callback(self)
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_base.py", line 453, in __call__
    return self._action(icon, self)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_base.py", line 548, in wrapper0
    return action()
           ^^^^^^^^
  File "C:\Skrypty\comy.py", line 36, in <lambda>
    item('Pokaż porty COM', lambda: show_ports(icon)),
                                    ^^^^^^^^^^^^^^^^
  File "C:\Skrypty\comy.py", line 25, in show_ports
    icon.notify(msg, "Lista portów COM")
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_base.py", line 292, in notify
    self._notify(message, title)
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_win32.py", line 87, in _notify
    self._message(
  File "C:\Users\sylwq\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pystray\_win32.py", line 337, in _message
    win32.Shell_NotifyIcon(code, win32.NOTIFYICONDATAW(
                                 ^^^^^^^^^^^^^^^^^^^^^^
ValueError: string too long (267, maximum length 256)


co do ostatniego.... to ja pythonie zmiescić dłuższy string? Możliwe że nazwy urządzeń są za długie?

Awatar użytkownika
phill2k
User
User
Posty: 288
Rejestracja: niedziela 18 paź 2015, 00:12

Re: [Python] Monitorowanie portów COM

Postautor: phill2k » piątek 30 maja 2025, 23:35

GrumpyRez pisze:Ktoś, coś...
Niestety nie do końca to działa.

Działa ale musisz mieć zainstalowane pakiety:
serial
serial.tool
pystray

Instalacja w konsoli > pip install nazwa_pakietu np. pip install serial

Wynik działania poniżej

Tray1.jpg
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » sobota 31 maja 2025, 22:13

Dzięki, pomogło. Dodatkowo dodałem (znaczy się chatGPT dodał :P) kilka funkcjonalności.... jak możliwość zamknięcia programu ;).
Oraz automatyczne odświeżanie listy COM

Kod: Zaznacz cały

import serial.tools.list_ports
import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
import threading
import tkinter as tk
import os
import sys
import winreg
tray_icon = None
com_window = None

def get_com_ports():
    ports = serial.tools.list_ports.comports()
    return [(port.device, port.description) for port in ports]

def create_image():
    # Tworzy ikonę z kształtem przypominającym wtyczkę DSUB
    image = Image.new('RGB', (64, 64), "white")
    draw = ImageDraw.Draw(image)
    draw.rectangle((16, 16, 48, 48), fill="gray")  # obudowa wtyczki
    for i in range(3):
        for j in range(5):
            draw.rectangle((20 + j*5, 20 + i*5, 22 + j*5, 22 + i*5), fill="black")  # piny
    return image

def show_ports_window():
    global com_window
    if com_window is not None and com_window.winfo_exists():
        com_window.lift()
        return

    com_window = tk.Tk()
    com_window.title("Podłączone porty COM")
    com_window.geometry("480x200")

    label_frame = tk.Frame(com_window)
    label_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

    def update_ports():
        for widget in label_frame.winfo_children():
            widget.destroy()
        ports = get_com_ports()
        if not ports:
            label = tk.Label(label_frame, text="Brak dostępnych portów COM.")
            label.pack()
        else:
            for port, desc in ports:
                label = tk.Label(label_frame, text=f"{port}: {desc}")
                label.pack(anchor='w')
        com_window.after(5000, update_ports)

    update_ports()

    def on_close():
        global com_window
        com_window.destroy()
        com_window = None

    com_window.protocol("WM_DELETE_WINDOW", on_close)
    com_window.mainloop()



def exit_action(icon, item):
    global com_window
    if com_window is not None and com_window.winfo_exists():
        com_window.destroy()
    icon.stop()
    sys.exit()


def add_to_startup():
    exe_path = os.path.realpath(sys.argv[0])
    if exe_path.endswith(".exe"):
        key = r"Software\Microsoft\Windows\CurrentVersion\Run"
        app_name = "portChecker"
        try:
            with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_SET_VALUE) as regkey:
                winreg.SetValueEx(regkey, app_name, 0, winreg.REG_SZ, exe_path)
        except Exception as e:
            print(f"Błąd autostartu: {e}")

def build_tray():
    global tray_icon
    tray_icon = pystray.Icon("port_checker")
    tray_icon.icon = create_image()
    tray_icon.title = "Monitor portów COM"
    tray_icon.menu = pystray.Menu(
        item('Pokaż porty COM', show_ports_window),
        item('Zamknij', exit_action)
    )
    tray_icon.run()


if __name__ == '__main__':
    add_to_startup()
    threading.Thread(target=build_tray).start()


port_checker.rar


Przydał by się jeszcze dymek z powiadomieniem o zmianach...
Ale po dodatniu kilku opcji soft przestał się uruchamiać :mrgreen:

Kod: Zaznacz cały

import serial.tools.list_ports
import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
import threading
import tkinter as tk
import os
import sys
import time
import winreg

# Globalne zmienne
tray_icon = None
com_window = None
last_ports = set()

# ========== Funkcje główne ==========

def get_com_ports():
    return [(port.device, port.description) for port in serial.tools.list_ports.comports()]

def create_image():
    image = Image.new('RGB', (64, 64), "white")
    draw = ImageDraw.Draw(image)
    draw.rectangle((16, 16, 48, 48), fill="gray")
    for i in range(3):
        for j in range(5):
            draw.rectangle((20 + j*5, 20 + i*5, 22 + j*5, 22 + i*5), fill="black")
    return image

def show_ports_window():
    global com_window
    if com_window is not None and com_window.winfo_exists():
        com_window.lift()
        return

    com_window = tk.Tk()
    com_window.title("Podłączone porty COM")
    com_window.geometry("480x200")

    label_frame = tk.Frame(com_window)
    label_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

    def update_ports():
        for widget in label_frame.winfo_children():
            widget.destroy()
        ports = get_com_ports()
        if not ports:
            label = tk.Label(label_frame, text="Brak dostępnych portów COM.")
            label.pack()
        else:
            for port, desc in ports:
                label = tk.Label(label_frame, text=f"{port}: {desc}")
                label.pack(anchor='w')
        if com_window.winfo_exists():
            com_window.after(5000, update_ports)

    update_ports()

    def on_close():
        global com_window
        com_window.destroy()
        com_window = None

    com_window.protocol("WM_DELETE_WINDOW", on_close)
    com_window.mainloop()

def exit_action(icon, item):
    global com_window
    if com_window is not None and com_window.winfo_exists():
        com_window.destroy()
    icon.stop()
    sys.exit()

def add_to_startup():
    exe_path = os.path.realpath(sys.argv[0])
    if exe_path.endswith(".exe"):
        key = r"Software\Microsoft\Windows\CurrentVersion\Run"
        app_name = "portChecker"
        try:
            with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_SET_VALUE) as regkey:
                winreg.SetValueEx(regkey, app_name, 0, winreg.REG_SZ, exe_path)
        except Exception as e:
            print(f"Błąd autostartu: {e}")

def monitor_new_ports():
    global last_ports, tray_icon
    while True:
        current_ports_

Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Awatar użytkownika
phill2k
User
User
Posty: 288
Rejestracja: niedziela 18 paź 2015, 00:12

Re: [Python] Monitorowanie portów COM

Postautor: phill2k » niedziela 01 cze 2025, 12:25

GrumpyRez pisze:Przydał by się jeszcze dymek z powiadomieniem o zmianach...
Ale po dodatniu kilku opcji soft przestał się uruchamiać :mrgreen:

Chyba niepełny kod wkleiłeś. Ciężko cokolwiek wywnioskować.

Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » poniedziałek 02 cze 2025, 08:28

To mnie gptchat zrobił w bambuko ;).
Poszukam czy gdzieś mam resztę. Jak nie, to niestety ja tego nie ogarniam na wystarczajaco wysokim poziomie.

Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » poniedziałek 02 cze 2025, 09:46

Ok. Tu jest pełny kod

Kod: Zaznacz cały

import serial.tools.list_ports
import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
import threading
import tkinter as tk
import os
import sys
import time
import winreg

# Globalne zmienne
tray_icon = None
com_window = None
last_ports = set()

# ========== Funkcje główne ==========

def get_com_ports():
    return [(port.device, port.description) for port in serial.tools.list_ports.comports()]

def create_image():
    image = Image.new('RGB', (64, 64), "white")
    draw = ImageDraw.Draw(image)
    draw.rectangle((16, 16, 48, 48), fill="gray")
    for i in range(3):
        for j in range(5):
            draw.rectangle((20 + j*5, 20 + i*5, 22 + j*5, 22 + i*5), fill="black")
    return image

def show_ports_window():
    global com_window
    if com_window is not None and com_window.winfo_exists():
        com_window.lift()
        return

    com_window = tk.Tk()
    com_window.title("Podłączone porty COM")
    com_window.geometry("300x200")

    label_frame = tk.Frame(com_window)
    label_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

    def update_ports():
        for widget in label_frame.winfo_children():
            widget.destroy()
        ports = get_com_ports()
        if not ports:
            label = tk.Label(label_frame, text="Brak dostępnych portów COM.")
            label.pack()
        else:
            for port, desc in ports:
                label = tk.Label(label_frame, text=f"{port}: {desc}")
                label.pack(anchor='w')
        if com_window.winfo_exists():
            com_window.after(5000, update_ports)

    update_ports()

    def on_close():
        global com_window
        com_window.destroy()
        com_window = None

    com_window.protocol("WM_DELETE_WINDOW", on_close)
    com_window.mainloop()

def exit_action(icon, item):
    global com_window
    if com_window is not None and com_window.winfo_exists():
        com_window.destroy()
    icon.stop()
    sys.exit()

def add_to_startup():
    exe_path = os.path.realpath(sys.argv[0])
    if exe_path.endswith(".exe"):
        key = r"Software\Microsoft\Windows\CurrentVersion\Run"
        app_name = "portChecker"
        try:
            with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_SET_VALUE) as regkey:
                winreg.SetValueEx(regkey, app_name, 0, winreg.REG_SZ, exe_path)
        except Exception as e:
            print(f"Błąd autostartu: {e}")

def monitor_new_ports():
    global last_ports, tray_icon
    while True:
        current_ports = set(get_com_ports())
        new_ports = current_ports - last_ports
        if tray_icon and new_ports:
            for port, desc in new_ports:
                try:
                    tray_icon.notify(f"Nowy port COM: {port}", desc)
                except:
                    pass  # ignoruj błędy notyfikacji
        last_ports = current_ports
        time.sleep(5)

def build_tray():
    global tray_icon, last_ports
    tray_icon = pystray.Icon("port_checker")
    tray_icon.icon = create_image()
    tray_icon.title = "Monitor portów COM"
    tray_icon.menu = pystray.Menu(
        item('Pokaż porty COM', show_ports_window),
        item('Zamknij', exit_action)
    )

    # Ustaw początkową listę portów
    last_ports = set(get_com_ports())

    # Uruchom monitorowanie portów w tle
    threading.Thread(target=monitor_new_ports, daemon=True).start()

    tray_icon.run()

# ========== Start aplikacji ==========

if __name__ == '__main__':
    add_to_startup()
    threading.Thread(target=build_tray, daemon=True).start()


Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » poniedziałek 02 cze 2025, 10:20

wcześniej źle skopiowałem kod... sytuacja jak /w/w czyli brak reakcji na próbę uruchomienia w/w programu. Kompiluje się prawidłowo i bez żadnych błędów.

Awatar użytkownika
phill2k
User
User
Posty: 288
Rejestracja: niedziela 18 paź 2015, 00:12

Re: [Python] Monitorowanie portów COM

Postautor: phill2k » poniedziałek 02 cze 2025, 15:05

Pogadałem z CGPT i tak, doinstaluj sobie pakiety winotify jeśli nie masz i spróbuj tego poniżej, teraz wyświetla dymki w tray'u po dołączeniu i odłączeniu portu com, reszta bez zmian.

Kod: Zaznacz cały

import serial.tools.list_ports
import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
import threading
import tkinter as tk
import os
import sys
import time
import winreg
from winotify import Notification

# Globalne zmienne
tray_icon = None
com_window = None
label_frame = None
last_ports = {}

# ========== Funkcje ==========
def get_com_ports():
    return {port.device: port.description for port in serial.tools.list_ports.comports()}

def create_image():
    image = Image.new('RGB', (64, 64), "white")
    draw = ImageDraw.Draw(image)
    draw.rectangle((16, 16, 48, 48), fill="gray")
    for i in range(3):
        for j in range(5):
            draw.rectangle((20 + j * 5, 20 + i * 5, 22 + j * 5, 22 + i * 5), fill="black")
    return image

def show_ports_window():
    global com_window, label_frame
    if com_window is not None and com_window.winfo_exists():
        com_window.lift()
        return

    com_window = tk.Tk()
    com_window.title("Podłączone porty COM")
    com_window.geometry("300x200")
    label_frame = tk.Frame(com_window)
    label_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

    update_ports()
    com_window.protocol("WM_DELETE_WINDOW", on_close)
    com_window.mainloop()

def update_ports():
    global com_window, label_frame
    if not com_window or not label_frame:
        return

    for widget in label_frame.winfo_children():
        widget.destroy()

    ports = get_com_ports()
    if not ports:
        label = tk.Label(label_frame, text="Brak dostępnych portów COM.")
        label.pack()
    else:
        for port, desc in ports.items():
            label = tk.Label(label_frame, text=f"{port}: {desc}")
            label.pack(anchor='w')

    if com_window and com_window.winfo_exists():
        com_window.after(5000, update_ports)

def on_close():
    global com_window
    if com_window:
        com_window.destroy()
        com_window = None

def exit_action(icon, item):
    global com_window
    if com_window is not None and com_window.winfo_exists():
        com_window.destroy()
    icon.stop()
    sys.exit()

def add_to_startup():
    exe_path = os.path.realpath(sys.argv[0])
    if exe_path.endswith(".exe"):
        key = r"Software\Microsoft\Windows\CurrentVersion\Run"
        app_name = "portChecker"
        try:
            with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_SET_VALUE) as regkey:
                winreg.SetValueEx(regkey, app_name, 0, winreg.REG_SZ, exe_path)
        except Exception as e:
            print(f"Błąd autostartu: {e}")

def notify_new_port(port, desc):
    toast = Notification(
        app_id="Port Checker",
        title="Nowy port COM wykryty",
        msg=f"{port}: {desc}",
        duration="short"
    )
    toast.show()

def notify_removed_port(port, desc):
    toast = Notification(
        app_id="Port Checker",
        title="Port COM odłączony",
        msg=f"{port}: {desc}",
        duration="short"
    )
    toast.show()

def monitor_ports():
    global last_ports
    while True:
        current_ports = get_com_ports()

        # Nowe porty
        new_ports = set(current_ports.keys()) - set(last_ports.keys())
        for port in new_ports:
            try:
                notify_new_port(port, current_ports[port])
            except Exception as e:
                print(f"Błąd powiadomienia (dodanie): {e}")

        # Usunięte porty
        removed_ports = set(last_ports.keys()) - set(current_ports.keys())
        for port in removed_ports:
            try:
                notify_removed_port(port, last_ports[port])
            except Exception as e:
                print(f"Błąd powiadomienia (usunięcie): {e}")

        last_ports = current_ports
        time.sleep(5)

def build_tray():
    global tray_icon, last_ports
    tray_icon = pystray.Icon("port_checker")
    tray_icon.icon = create_image()
    tray_icon.title = "Monitor portów COM"
    tray_icon.menu = pystray.Menu(
        item('Pokaż porty COM', show_ports_window),
        item('Zamknij', exit_action)
    )

    last_ports = get_com_ports()
    threading.Thread(target=monitor_ports, daemon=True).start()
    tray_icon.run()

# ========== Start aplikacji ==========
if __name__ == '__main__':
    add_to_startup()
    build_tray()


Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » wtorek 03 cze 2025, 14:55

Co prawda nie ma dymka powiadomień... Ale zaczął się uruchamiać.
Dzięki.

W sumie ciekawe dlaczego nie ma dymka...
PS. dla chcących potestować, warto sobie ustawić większe okientko w

Kod: Zaznacz cały

com_window.geometry("480x200") # domyślnie jest 300x200 i to jednak ciut za mało przy długich nazwach.
 


Dobra, działa... Bo to nie jest w win11 w postaci dymka, tylko w obszarze powiadomień...

Awatar użytkownika
GrumpyRez
User
User
Posty: 258
Rejestracja: poniedziałek 04 cze 2018, 09:19

Re: [Python] Monitorowanie portów COM

Postautor: GrumpyRez » środa 04 cze 2025, 13:26

Po dniu testów, może i toporny program. Ale działa zgodnie z założeniami.

Nie trzeba grzebać w menedżerze urządzeń za każdym razem, jak podpinamy coś co tworzy nowy COM.
Odpięcie powoduje zniknięcie z listy COM

Przydało by się jeszcze rozwinąć program o możliwość edycji nazw (by były krótsze). Ale to już przerasta moje umiejętności pythonowe.

Awatar użytkownika
xor
User
User
Posty: 175
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: [Python] Monitorowanie portów COM

Postautor: xor » piątek 04 lip 2025, 19:59

Windows 11 informuje o dołączanych/odłączanych urzadzeniach COM bez potrzeby instalowania czegokolwiek czy grzebania w device manager. Ustawienia -> Bluetooth i urządzenia -> wyświetl więcej urządzeń.


Wróć do „Inne języki programowania”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 6 gości