Files
mayo-mil-hd/custom_qtdragon.md

7.2 KiB

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)

# -*- 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 version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>UserTabWidget</class>
 <widget class="QWidget" name="UserTabWidget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QPushButton" name="moj_wlasny_przycisk_1">
     <property name="minimumSize">
      <size>
       <width>150</width>
       <height>50</height>
      </size>
     </property>
     <property name="text">
      <string>Mój Własny Przycisk Testowy</string>
     </property>
    </widget>
   </item>
   <item>
    <spacer name="verticalSpacer">
     <property name="orientation">
      <enum>Qt::Vertical</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>20</width>
       <height>40</height>
      </size>
     </property>
    </spacer>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>