# Modyfikacja Interfejsu qtdragon_hd w LinuxCNC Data: 2025-12-12 ## 1. Cel Głównym celem jest dodanie nowej, niestandardowej zakładki z własnymi przyciskami do interfejsu `qtdragon_hd` w LinuxCNC. Kluczowe założenia to: - Wykorzystanie wbudowanego, ukrytego przycisku `btn_user`, przeznaczonego do tego celu. - Minimalna ingerencja w oryginalne pliki systemowe LinuxCNC. - Stworzenie rozwiązania modułowego, gdzie logika i wygląd nowej zakładki są oddzielone od głównego interfejsu. ## 2. Podjęte Kroki i Stworzone Pliki 1. **Analiza i Strategia:** Zidentyfikowaliśmy, że plik `qtdragon_hd.ui` zawiera przycisk `btn_user` i `QStackedWidget` o nazwie `stackedWidget_mainTab`. Zdecydowaliśmy się nie modyfikować tego pliku, a zamiast tego dynamicznie załadować naszą zakładkę za pomocą niestandardowego handlera w Pythonie. 2. **Stworzenie plików w `~/linuxcnc/configs/mayo-mill_hd/`**: - `qtvcp/screens/qtdragon_hd/qtdragon_hd.ui`: Lokalna kopia domyślnego pliku UI, stworzona w celu uniknięcia modyfikacji pliku systemowego. - `qtvcp/screens/qtdragon_hd/my_user_tab.ui`: Plik UI zawierający *tylko zawartość* naszej nowej zakładki (jeden przycisk testowy). - `qtvcp/screens/qtdragon_hd/handler.py`: Plik Pythona z klasą `MyHandler`, która zawiera logikę ładowania zakładki i obsługi przycisków. - `custom_postgui.hal`: Plik do podłączenia pinów HAL z interfejsu do logiki maszyny. 3. **Konfiguracja:** Zmodyfikowaliśmy plik `mayo-mill.ini`, dodając w sekcji `[DISPLAY]` wpis: `HANDLER = handler.MyHandler`. ## 3. Napotkane Problemy i Diagnoza 1. **Błąd pinu HAL:** Wystąpił błąd `Pin ... does not exist`, ponieważ `custom_postgui.hal` był przetwarzany, zanim handler w Pythonie zdążył utworzyć pin. Problem nasilał się przez użycie kropki w nazwie pinu, która jest znakiem specjalnym w HAL. - **Status:** Problem tymczasowo ominięto przez zakomentowanie linii w `custom_postgui.hal`. 2. **Główny Problem: Handler nie był ładowany:** Mimo poprawnego wpisu w `.ini`, logi startowe LinuxCNC pokazały, że system ignorował nasz plik `handler.py` i ładował domyślny handler systemowy (`/usr/share/qtvcp/screens/qtdragon_hd/qtdragon_hd_handler.py`). - **Diagnoza:** Nasz plik i klasa `MyHandler` nie były powiązane z domyślnym handlerem. Przez to QTVCP nie traktował go jako rozszerzenia, a jako zupełnie osobny byt, który w tym wypadku został zignorowany na rzecz domyślnej implementacji. Próba zmiany nazwy pliku na `qtdragon_hd_handler.py` spowodowała konflikt i nadpisanie domyślnej logiki, co jest błędne. ## 4. Poprawne Rozwiązanie (do wdrożenia) Aby poprawnie rozszerzyć funkcjonalność `qtdragon_hd`, nasz niestandardowy handler musi **dziedziczyć** po klasie handlera domyślnego. W ten sposób zachowujemy całą oryginalną funkcjonalność i tylko dodajemy naszą własną. **Następne kroki do wykonania:** 1. Zastąpić zawartość pliku `handler.py` poniższym, **poprawionym kodem**, który implementuje dziedziczenie. 2. Uruchomić LinuxCNC i zweryfikować, czy zakładka "USER" się pojawiła. 3. Opcjonalnie: odkomentować linię w `custom_postgui.hal`, aby przetestować działanie pinu HAL. --- ### Docelowy Kod `handler.py` (z dziedziczeniem) ```python # -*- coding: utf-8 -*- # Plik: handler.py import os # Krok 1: Importujemy domyślny handler qtdragon_hd from qtvcp.screens.qtdragon_hd import qtdragon_hd_handler from qtvcp.core import Info from qtvcp.widgets.widget_base import WidgetBase from PyQt5.QtWidgets import QPushButton print(">>> WŁASNY HANDLER: Plik handler.py został załadowany.") PATH = Info().get_config_path() # Krok 2: Nasza klasa dziedziczy po 'HandlerClass' z domyślnego handlera class MyHandler(qtdragon_hd_handler.HandlerClass): """ Niestandardowy handler, który ROZSZERZA domyślny handler qtdragon_hd. """ def __init__(self, *args, **kwargs): # Krok 3: Wywołujemy __init__ klasy nadrzędnej super(MyHandler, self).__init__(*args, **kwargs) print(">>> WŁASNY HANDLER: __init__ wykonany.") def initialized(self): # Krok 4: Wywołujemy initialized klasy nadrzędnej, aby cała domyślna logika się wykonała super(MyHandler, self).initialized() print(">>> WŁASNY HANDLER: Metoda 'initialized' została wywołana (po domyślnym handlerze).") # --- Od tego momentu zaczyna się nasza własna logika --- # Tworzymy nasz własny pin self.my_custom_button_pin = self.hal.newpin("wlasny-przycisk_is-on", "HAL_BIT", "out") print(f">>> WŁASNY HANDLER: Pin HAL '{self.my_custom_button_pin.name}' został stworzony.") user_tab_ui_file = os.path.join(PATH, 'qtvcp', 'screens', 'qtdragon_hd', 'my_user_tab.ui') print(f">>> WŁASNY HANDLER: Sprawdzam ścieżkę: {user_tab_ui_file}") if not os.path.exists(user_tab_ui_file): print(">>> WŁASNY HANDLER: BŁĄD! Nie znaleziono pliku my_user_tab.ui.") self.widgets.btn_user.setVisible(False) return print(">>> WŁASNY HANDLER: Plik my_user_tab.ui znaleziony. Ładuję...") self.user_tab_widget = WidgetBase() self.user_tab_widget.load_ui(user_tab_ui_file) main_stack = self.widgets.stackedWidget_mainTab user_button = self.widgets.btn_user target_index = user_button.property('index') main_stack.insertWidget(target_index, self.user_tab_widget) user_button.setVisible(True) print(f">>> WŁASNY HANDLER: Zakładka wstawiona na indeks {target_index}, przycisk USER widoczny.") my_button = self.user_tab_widget.findChild(QPushButton, 'moj_wlasny_przycisk_1') if my_button: my_button.clicked.connect(self.on_my_custom_button_clicked) print(">>> WŁASNY HANDLER: Połączono sygnał z 'moj_wlasny_przycisk_1'.") def on_my_custom_button_clicked(self): current_state = self.my_custom_button_pin.get() self.my_custom_button_pin.set(not current_state) print(f">>> WŁASNY HANDLER: Klik! Nowy stan pinu: {not current_state}") ``` ### Plik `my_user_tab.ui` ```xml UserTabWidget 0 0 400 300 Form 150 50 Mój Własny Przycisk Testowy Qt::Vertical 20 40 ```