QtVCP ist eine Infrastruktur zum Erstellen von benutzerdefinierten CNC-Bildschirmen oder Bedienfeldern für LinuxCNC.
Es zeigt eine .ui
-Datei an, die mit dem Qt Designer-Bildschirmeditor erstellt wurde, und kombiniert diese mit Python-Programmierung, um einen GUI-Bildschirm für den Betrieb einer CNC-Maschine zu erstellen.
QtVCP ist vollständig anpassbar: Sie können verschiedene Schaltflächen und Status-LEDs usw. hinzufügen oder Python-Code für eine noch feinere Anpassung einfügen.
1. Schaukasten
Einige Beispiele für mit QtVCP erstellte Bildschirme und virtuelle Bedienfelder:
2. Übersicht
Zwei Dateien werden, einzeln oder in Kombination, verwendet, um Anpassungen vorzunehmen:
-
Eine UI-Datei, bei der es sich um eine XML-Datei handelt, die mit dem grafischen Editor Qt Designer erstellt wurde.
-
Eine Handler-Datei, die eine Textdatei mit Python-Code ist.
Normalerweise verwendet QtVCP die standardmäßige UI- und Handler-Datei, aber Sie können QtVCP so einstellen, dass es "lokale" UI- und Handler-Dateien verwendet.
Eine lokale Datei ist eine Datei, die sich im Konfigurationsordner befindet, der den Rest der Anforderungen des Rechners definiert.
Man ist nicht darauf beschränkt, ein benutzerdefiniertes Panel auf der rechten Seite oder eine benutzerdefinierte Registerkarte hinzuzufügen, da QtVCP den Qt Designer (den Editor) und PyQt5 (das Widget-Toolkit) nutzt.
QtVCP hat einige spezielle LinuxCNC Widgets und Aktionen hinzugefügt.
Es gibt spezielle Widgets, um Widgets von Drittanbietern mit HAL-Pins zu verbinden.
Es ist möglich, Widget-Antworten zu erstellen, indem man Signale mit Python-Code in der Handler-Datei verbindet.
2.1. QtVCP Widgets
QtVCP nutzt die PyQt5-Toolkits für die Einbeziehung von LinuxCNC.
Widget is the general name for user interface objects such as buttons and labels in PyQt5.
Es steht Ihnen frei, alle verfügbaren Standard-Widgets im Qt Designer-Editor zu verwenden.
Es gibt auch spezielle Widgets für LinuxCNC gemacht, dass die Integration einfacher zu machen.
Diese sind in drei Überschriften auf der linken Seite des Editors aufgeteilt:
-
Eine ist nur für HAL Widgets;
-
Eine ist für CNC-Steuerungs-Widgets;
-
Eine ist für Dialog-Widgets.
Es steht Ihnen frei, sie auf Ihrer Tafel beliebig zu mischen.
Ein sehr wichtiges Widget für die CNC-Steuerung ist das ScreenOptions
-Widget: Es fügt dem Bildschirm nichts Visuelles hinzu, sondern ermöglicht die Auswahl wichtiger Details, die dann in der Handler-Datei kodiert werden müssen.
2.2. INI-Einstellungen
Wenn Sie QtVCP zur Erstellung eines CNC-Bewegungssteuerungsbildschirms (und nicht eines HAL-basierten Panels) verwenden, fügen Sie in der INI-Datei im Abschnitt [DISPLAY]
eine Zeile mit folgendem Muster ein:
DISPLAY = qtvcp <Optionen> <Bildschirmname>
Anmerkung
|
Alle "<Optionen>" müssen vor "<Bildschirmname>" stehen. |
-
-d
Debugging an. -
-i
Infoausgabe aktivieren. -
-v
Aktiviert die ausführliche Debug-Ausgabe. -
-q
Aktiviert nur die Fehler-Debug-Ausgabe. -
-a
Fenster immer in Vordergrund (engl. top) setzen. -
-c NAME
Name der HAL-Komponente. Standardmäßig wird der UI-Dateiname verwendet. -
-g GEOMETRIE`Legt die Geometrie WIDTHxHEIGHT+XOFFSET+YOFFSET fest. Die Werte sind in Pixel-Einheiten, XOFFSET/YOFFSET wird vom linken oberen Bildschirmrand aus referenziert. Verwenden Sie -g WIDTHxHEIGHT, um nur die Größe zu bestimmen, oder -g +XOFFSET+YOFFSET, um nur die Position zu bestimmen. Beispiel: `-g 200x400+0+100
-
`-H DATEI`Führt HAL-Anweisungen aus DATEI mit halcmd aus, nachdem die Komponente eingerichtet und bereit ist.
-
-m
Fenster maximieren. -
-f
Vollbild des Fensters. -
-t THEME
Standard ist das Systemdesign -
-x XID
Einbindung in ein X11-Fenster, das nicht die Integration unterstützt. -
--push_xid
Sendet die X11-Fenster-Identifikationsnummer von QtVCP an die Standardausgabe; zum Einbetten. -
-u USERMOD
Dateipfad einer Ersatz-Handler-Datei. -
-o USEROPTS
Übergibt einen String an die Handler-Datei von QtVCP unter der Listenvariablenself.w.USEROPTIONS_
. Können mehrere -o sein.
<Bildschirmname>
ist der Basisname der .ui und _handler.py Dateien. Wenn <Bildschirmname>
fehlt, wird der Standardbildschirm geladen.
QtVCP nimmt an, dass die UI-Datei und die Handler-Datei den gleichen Basisnamen verwenden. QtVCP sucht zunächst im LinuxCNC-Konfigurationsverzeichnis, das gestartet wurde, nach den Dateien, dann im System-Skin-Ordner mit den Standardbildschirmen.
[DISPLAY] CYCLE_TIME = 100 GRAPHICS_CYCLE_TIME = 100 HALPIN_CYCLE = 100
Stellt die Reaktionsgeschwindigkeit der GUI-Aktualisierungen in Millisekunden ein. Standardwert ist 100, nutzbarer Bereich 50 - 200.
Die Widgets, Grafiken und die HAL-Pin-Aktualisierung können separat eingestellt werden.
Wenn die Aktualisierungszeit nicht richtig eingestellt ist, kann der Bildschirm nicht mehr reagieren oder stark ruckeln.
2.3. Qt Designer UI Datei
Eine Qt Designer-Datei ist eine Textdatei, die im XML-Standard organisiert ist und das Layout und die Widgets des Bildschirms beschreibt.
PyQt5 verwendet diese Datei, um die Anzeige zu erstellen und auf diese Widgets zu reagieren.
Der Qt Designer Editor macht es relativ einfach, diese Datei zu erstellen und zu bearbeiten.
2.4. Handler-Dateien
Eine Handler-Datei ist eine Datei, die Python-Code enthält, der zu den QtVCP-Standardroutinen hinzugefügt wird.
Eine Handler-Datei erlaubt es, Voreinstellungen zu ändern oder einem QtVCP-Bildschirm Logik hinzuzufügen, ohne den Kerncode von QtVCP zu verändern. Auf diese Weise können Sie eigene Verhaltensweisen implementieren.
Falls vorhanden, wird eine Handler-Datei geladen. Es ist nur eine Datei erlaubt.
2.5. Bibliotheken Module
QtVCP, so wie es gebaut ist, tut wenig mehr als den Bildschirm anzuzeigen und auf Widgets zu reagieren. Für weitere vorgefertigte Verhaltensweisen gibt es verfügbare Bibliotheken (zu finden in lib/python/qtvcp/lib
in RIP LinuxCNC install).
Libraries are prebuilt Python modules that add features to QtVCP. In this way you can select what features you want - yet don’t have to build common ones yourself.
Zu diesen Bibliotheken gehören:
-
audio_player
-
aux_program_loader
-
keybindings
-
message
-
preferences
-
notify
-
virtual_keyboard
-
machine_log
2.6. Themen
Designs sind eine Möglichkeit, das look and feel der Widgets auf dem Bildschirm zu ändern.
Zum Beispiel kann die Farbe oder Größe von Schaltflächen und Schiebereglern mit Hilfe von Themen geändert werden.
Das Windows-Thema ist der Standard für Bildschirme.
Das Systemthema ist der Standard für Bedienfelder.
Um die verfügbaren Themen zu sehen, können Sie sie mit dem folgenden Befehl in einem Terminal laden:
qtvcp -d -t <theme_name>
QtVCP kann auch mit Qt-Stylesheets (QSS) unter Verwendung von CSS angepasst werden.
2.7. Lokale Dateien
Falls vorhanden, werden die lokalen UI/QSS/Python-Dateien im Konfigurationsordner anstelle der Standard-UI-Dateien geladen.
Lokale UI/QSS/Python-Dateien ermöglichen es Ihnen, Ihre eigenen Designs anstelle der Standardbildschirme zu verwenden.
QtVCP sucht nach einem Ordner mit dem Namen <screen_name> (im Ordner für die Startkonfiguration, der die INI-Datei enthält).
In diesem Ordner lädt QtVCP jede der folgenden Dateien:
-
_<screen_name>_.ui
, -
<screen_name>_handler.py
, und -
_<screen_name>_.qss
.
2.8. Veränderung mitglieferter Bildschirmmasken
Es gibt drei Möglichkeiten, einen Bildschirm/Panel anzupassen.
2.8.1. Kleinere Stylesheet-Änderungen
Stylesheets können zum Setzen von Qt-Eigenschaften verwendet werden. Wenn ein Widget Eigenschaften verwendet, können diese normalerweise durch Stylesheets verändert werden.
State_LED #name_of_led{ qproperty-color: red; qproperty-diameter: 20; qproperty-flashRate: 150; }
2.8.2. Handler Patching - Subclassing Builtin Screens
We can have QtVCP load a subclassed version of the standard handler file. in that file we can manipulate the original functions or add new ones.
Subclassing just means our handler file first loads the original handler file and adds our new code on top of it - so a patch of changes.
This is useful for changing/adding behaviour while still retaining standard handler updates from LinuxCNC repositories.
You may still need to use the handler copy dialog to copy the original handler file to decide how to patch it. See custom handler file
Es sollte einen Ordner im Konfigurationsordner geben; für Bildschirme: benannt als <KONFIGURATIONSORDNER>/qtvcp/screens/<BILDSCHIRMNAME>/
Fügen Sie dort die Handler-Patch-Datei hinzu, benannt wie folgt <ORIGINAL BILDSCHIRMNAME>_handler.py,
d.h. für QtDragon würde die Datei qtdragon_handler.py genannt werden.
Hier ist ein Beispiel, um X-Achse Jogstifte zu einem Bildschirm wie QtDragon hinzuzufügen:
import sys import importlib from qtvcp.core import Path, Qhal, Action PATH = Path() QHAL = Qhal() ACTION = Action() # besorge Referenz zu Original Handler Datei, um sie zu spezialisieren (engl. subclass) sys.path.insert(0, PATH.SCREENDIR) module = "{}.{}_handler".format(PATH.BASEPATH,PATH.BASEPATH) mod = importlib.import_module(module, PATH.SCREENDIR) sys.path.remove(PATH.SCREENDIR) HandlerClass = mod.HandlerClass # return our subclassed handler object to QtVCP def get_handlers(halcomp, widgets, paths): return [UserHandlerClass(halcomp, widgets, paths)] # sub class HandlerClass which was imported above class UserHandlerClass(HandlerClass): # add a terminal message so we know this got loaded print('\nCustom subclassed handler patch loaded.\n') def init_pins(self): # Aufruf der original Handler init_pins Funktionen super().init_pins() # Hinzufügen Schnellauf pins X axis pin = QHAL.newpin("jog.axis.jog-x-plus", QHAL.HAL_BIT, QHAL.HAL_IN) pin.value_changed.connect(lambda s: self.kb_jog(s, 0, 1, fast = False, linear = True)) pin = QHAL.newpin("jog.axis.jog-x-minus", QHAL.HAL_BIT, QHAL.HAL_IN) pin.value_changed.connect(lambda s: self.kb_jog(s, 0, -1, fast = False, linear = True))
2.8.3. Kleinere Änderungen am Python-Code
Eine weitere Python-Datei kann verwendet werden, um Befehle auf dem Bildschirm hinzuzufügen, nachdem die Handler-Datei geparst wurde. Dies kann für kleinere Änderungen nützlich sein, während die Standard-Handler-Updates aus den LinuxCNC-Repositorien weiterhin berücksichtigt werden.
Anmerkung
|
Handler Patching ist ein besserer Weg, um Änderungen hinzuzufügen - Instanz-Patching ist schwarzer magischer voodoo - dies ist hier nur der Nostalgie halber dokumentiert. |
In der INI Datei unter der [DISPLAY] ` Überschrift fügen Sie hinzu *`USER_COMMAND_FILE = _PATH_
*
PATH kann jeder gültige Pfad sein. Er kann ~
für das Heimatverzeichnis oder WORKINGDIRECTORY
oder CONFIGDIRECTORY
verwenden, um QtVCPs Vorstellung von diesen Verzeichnissen zu repräsentieren, z. B.:
[DISPLAY] USER_COMMAND_FILE = CONFIGFOLDER/<Bildschirm_name_hinzugefuegte_Befehle>
Wenn kein Eintrag in der INI gefunden wird, sucht QtVCP im Standardpfad. Der Standardpfad befindet sich im Konfigurationsverzeichnis als versteckte Datei mit dem Basisnamen des Bildschirms und rc, d.h.: CONFIGURATION DIRECTORY/.<Bildschirmname>rc
.
Diese Datei wird als Python-Code im handler-Dateikontext gelesen und ausgeführt.
Only local functions and local attributes can be referenced.
Global libraries defined in the screen’s handler file can be referenced by importing the handler file.
These are usually seen as all capital words with no preceding self.
self references the window class functions
self.w typically references the widgets
Was verwendet werden kann, mag je nach Bildschirm und Entwicklungszyklus variieren.
Verweis auf das Hauptfenster, um den Titel zu ändern (wird nicht angezeigt, wenn INI-Einträge für die Titeländerung verwendet werden).
self.w.setWindowTitle('Mein Titel-Test')
Dies könnte mit der Handler-Datei des QtDragon-Bildschirms funktionieren.
Hier zeigen wir, wie man neue Funktionen hinzufügt und bestehende überschreibt.
# Benötigt für Instanz-Patch # Referenz: https://ruivieira.dev/python-monkey-patching-for-readability.html import types # importiere das Handlerfile, um einen Verweis auf dessen Bibliotheken zu erhalten. # benutze <Bildschirmname>_handler import qtdragon_handler as hdlr # Dies ist eigentlich eine unbeschränkte Funktion mit 'obj' als Parameter. # Sie rufen diese Funktion ohne das übliche vorangestellte 'self' auf. # Das liegt daran, dass sie nicht in die ursprüngliche Instanz der Handler-Klasse eingefügt wird. # Sie wird nur von Code in dieser Datei aufgerufen. def test_function(obj): print(dir(obj)) # Dies ist eine neue Funktion, die wir der bestehenden Handler-Klasseninstanz hinzufügen werden. # Beachten Sie, dass sie die unbeschränkte Funktion mit 'self' als Parameter aufruft, 'self' ist der einzige verfügbare globale Referenz. # Sie verweist auf die Fensterinstanz def on_keycall_F10(self,event,state,shift,cntrl): if state: print ('F10') test_function(self) # Dies wird verwendet, um eine bestehende Funktion in der bestehenden Handler-Klasseninstanz außer Kraft zu setzen. # Beachten Sie, dass wir auch eine Kopie der ursprünglichen Funktion aufrufen. # Dies zeigt, wie man eine bestehende Funktion um zusätzliche Funktionen erweitert. def on_keycall_F11(self,event,state,shift,cntrl): if state: self.on_keycall_F11_super(event,state,shift,cntrl) print ('Hallo') # Wir verweisen auf die KEYBIND-Bibliothek, die in der ursprünglichen Handler-Klasseninstanz instanziiert wurde # durch Hinzufügen von 'hdlr.' (vom imp). # Diese Funktion weist KEYBIND an, 'on_keycall_F10' aufzurufen, wenn F10 gedrückt wird hdlr.KEYBIND.add_call('Key_F10','on_keycall_F10') # Hier patchen wir die ursprüngliche Handler-Datei, um eine neue Funktion hinzuzufügen, # die unsere neue Funktion (mit demselben Namen) aufruft, definiert in dieser Datei. self.on_keycall_F10 = types.MethodType(on_keycall_F10, self) # Hier definieren wir eine Kopie der ursprünglichen Funktion 'on_keycall_F11'. # damit wir sie später aufrufen können. Wir können jeden gültigen, unbenutzten Funktionsnamen verwenden. # Wir müssen dies tun, bevor wir die ursprüngliche Funktion überschreiben. self.on_keycall_F11_super = self.on_keycall_F11 # Hier patchen wir die ursprüngliche Handler-Datei, um eine bestehende Funktion zu überschreiben, # und so auf unsere neue Funktion (mit demselben Namen) zu verweisen, definiert in dieser Datei. self.on_keycall_F11 = types.MethodType(on_keycall_F11, self) # fügen sie einen neuen Pin dem Bildschirm hinzu: # pin callback um den Status auszugeben def new_pin_changed(data): print(data) # Spezielle Funktion, die aufgerufen wird, bevor die HAL-Komponente bereit ist. # Here we used the function to add a bit input pin with a callback def after_override__(self): try: pin = hdlr.QHAL.newpin("new_pin", hdlr.QHAL.HAL_BIT, hdlr.QHAL.HAL_IN) pin.value_changed.connect(new_pin_changed) except Exception as e: print(e) # Hier patchen wir die ursprüngliche Handler-Datei, um eine neue Funktion hinzuzufügen, # die unsere neue Funktion (mit demselben Namen) aufruft, definiert in dieser Datei. self.after_override__ = types.MethodType(after_override__, self)
2.8.4. Volle kreative Kontrolle mit benutzerdefinierten Handler/UI-Dateien
Wenn Sie einen Standardbildschirm mit voller Kontrolle verändern möchten, kopieren Sie dessen UI und Handler-Datei in Ihren Konfigurationsordner.
Es gibt ein QtVCP-Panel, das dabei hilft:
-
Öffnen Sie ein Terminal und führen den folgenden Befehl aus:
qtvcp copy
-
Wählen Sie den Bildschirm und den Zielordner im Dialog
-
Wenn Sie Ihren Bildschirm anders benennen möchten als den Standardnamen des eingebauten Bildschirms, ändern Sie den Basisnamen im Bearbeitungsfeld.
-
Es sollte einen Ordner im Konfigurationsordner geben; für Bildschirme: mit dem Namen <CONFIG FOLDER>/qtvcp/screens/ für Panels: mit dem Namen <CONFIG FOLDER>/qtvcp/panels/ fügen Sie die Ordner hinzu, wenn sie fehlen, und kopieren Sie Ihre Ordner/Dateien hinein.
-
Bestätigen, um alle Dateien zu kopieren
-
Löschen Sie die Dateien, die Sie nicht ändern möchten, damit die Originaldateien verwendet werden.
3. VCP-Paneele
QtVCP kann verwendet werden, um Bedienfelder zu erstellen, die mit HAL verbunden sind.
3.1. Eingebaute Panels
Es sind mehrere integrierte HAL-Panels verfügbar.
Geben Sie in einem Terminal qtvcp <return>
ein, um eine Liste zu sehen:
-
test_panel
-
Sammlung nützlicher Widgets zum Testen von HAL-Komponenten, einschließlich der Anzeige des LED-Zustands.
-
cam_align
-
Ein Kameraanzeige-Widget für die Rotationsausrichtung.
-
sim_panel
-
Ein kleines Bedienfeld zur Simulation von MPG-Jogging-Steuerungen usw.
Für simulierte Konfigurationen. -
vismach_mill_xyz
-
3D-OpenGL-Ansicht einer 3-Achsen-Fräsmaschine.
Sie können diese aus dem Terminal oder aus einer HAL-Datei mit diesem einfachen Befehl laden:
loadusr qtvcp test_panel
Aber typischerweise eher so:
loadusr -Wn test_panel qtvcp test_panel
Auf diese Weise wartet HAL bis die HAL-Pins gesetzt sind, bevor es weitergeht.
3.2. Benutzerdefinierte Bedienfelder
Sie können natürlich Ihr eigenes Panel erstellen und laden.
Wenn Sie eine UI-Datei mit dem Namen my_panel.ui
und eine HAL-Datei mit dem Namen my_panel.hal
erstellt haben, würden Sie diese dann von einem Terminal aus laden mit:
halrun -I -f my_panel.hal
# Echtzeitkomponenten laden loadrt threads loadrt classicladder_rt # Nicht-Echtzeit-Programme laden loadusr classicladder loadusr -Wn my_panel qtvcp my_panel.ui # <1> # Komponenten zum Thread hinzufügen addf classicladder.0.refresh thread1 # Pins verbinden net bit-input1 test_panel.checkbox_1 classicladder.0.in-00 net bit-hide test_panel.checkbox_4 classicladder.0.hide_gui net bit-output1 test_panel.led_1 classicladder.0.out-00 net s32-in1 test_panel.doublescale_1-s classicladder.0.s32in-00 # start thread start
-
In diesem Fall laden wir
qtvcp
mit-Wn
, das wartet, bis das Panel das Laden beendet hat, bevor es mit der Ausführung des nächsten HAL-Befehls fortfährt.
Damit soll gewährleistet werden, dass die vom Panel erstellten HAL-Pins tatsächlich fertig sind, falls sie im Rest der Datei verwendet werden.
4. Erstellen eines einfachen benutzerdefinierten Bildschirms
4.1. Übersicht
So erstellen Sie ein Bedienfeld oder einen Bildschirm:
-
Verwenden Sie Qt Designer, um ein Design zu erstellen, das Ihnen gefällt, und speichern Sie es in Ihrem Konfigurationsordner unter einem Namen Ihrer Wahl, der mit
.ui
endet -
Ändern Sie die Konfigurations-INI-Datei, um QtVCP mit Ihrer neuen
.ui
-Datei zu laden. -
Dann verbinden Sie alle erforderlichen HAL-Kenntnisse in einer HAL-Datei.
4.2. Holen Sie sich Qt Designer, um LinuxCNC-Widgets einzubinden
Zuerst müssen Sie den Qt Designer installieren.
Die folgenden Befehle sollten ihn zu Ihrem System hinzufügen, oder verwenden Sie Ihren Paketmanager, um dasselbe zu tun:
sudo apt-get install qttools5-dev-tools qttools5-dev libpython3-dev
qtvcp_plugin.py
zum Qt Designer SuchpfadDann müssen Sie einen Link zu qtvcp_plugin.py
in einem der Ordner hinzufügen, in denen Qt Designer suchen wird.
In einer RIP (engl. Abkürzung von "run in place", d.h. das Programm started dort wo es durch den Quellcode auch kompiliert wurde) Version von LinuxCNC wird qtvcp_plugin.py
sein:
'~/LINUXCNC_PROJECT_NAME/lib/python/qtvcp/plugins/qtvcp_plugin.py'
Die installierte Paketversion sollte sein:
'usr/lib/python2.7/qtvcp/plugins/qtvcp_plugin.py' or
'usr/lib/python2.7/dist-packages/qtvcp/plugins/qtvcp_plugin.py'
Legen Sie einen symbolischen Link auf die obige Datei an und verschieben Sie sie an einen der Orte, an denen Qt Designer sucht.
Qt Designer sucht an diesen beiden Stellen nach Links (wählen Sie einen aus):
'/usr/lib/x86_64-linux-gnu/qt5/plugins/designer/python' or
'~/.designer/plugins/python'
Möglicherweise müssen Sie den Ordner plugins/python
erstellen.
-
Für eine RIP-Installation:
Öffnen Sie ein Terminal, setzen Sie die Umgebungsvariablen für LinuxCNC <1>, dann laden Sie Qt Designer <2> mit :. scripts/rip-environment <1> designer -qt=5 <2>
-
Für eine Paketinstallation:
Öffnen Sie ein Terminal und geben Sie ein:designer -qt=5
Wenn alles gut geht, wird Qt Designer gestartet und Sie werden die auswählbaren LinuxCNC Widgets auf der linken Seite sehen.
4.3. Erstellen Sie die .ui
-Datei des Bildschirms
MainWindow
WidgetsWenn Qt Designer zum ersten Mal gestartet wird, erscheint ein 'New Form' Dialog.
Wählen Sie 'Main Window' und drücken Sie die Schaltfläche 'Create'.
Ein MainWindow
-Widget wird angezeigt.
Wir werden diesem Fenster eine bestimmte, nicht veränderbare Größe geben:
-
Fassen Sie die Ecke des Fensters an und ändern Sie die Größe auf eine geeignete Größe, z. B. 1000x600.
-
Klicken Sie mit der rechten Maustaste auf das Fenster und klicken Sie auf Mindestgröße einstellen.
-
Wiederholen Sie dies und stellen Sie maximale Größe ein.
Unser Beispiel-Widget ist nun nicht mehr größenveränderbar.
ScreenOptions
Ziehen Sie das ScreenOptions
-Widget per Drag-and-Drop an eine beliebige Stelle im Hauptfenster.
Dieses Widget fügt visuell nichts hinzu, richtet aber einige allgemeine Optionen ein.
Es wird empfohlen, dieses Widget immer vor allen anderen hinzuzufügen.
Klicken Sie mit der rechten Maustaste auf das Hauptfenster, nicht auf das "ScreenOptions"-Widget, und stellen Sie "Layout" auf "Vertikal", um "ScreenOptions" in voller Größe anzuzeigen.
Auf der rechten Seite befindet sich ein Panel mit Registerkarten für einen Eigenschaftseditor und einen Objektinspektor.
Klicken Sie im Objektinspektor auf ScreenOptions.
Wechseln Sie dann zum Eigenschaftseditor (engl. property editor) und schalten Sie unter der Überschrift ScreenOptions die filedialog_option
um.
Ziehen Sie ein GCodeGraphics
widget und ein GcodeEditor
widget per Drag and Drop.
Platzieren Sie sie und ändern Sie die Größe, wie Sie es für richtig halten, und lassen Sie etwas Platz für Schaltflächen.
Fügen Sie dem Hauptfenster 7 Aktionsschaltflächen hinzu.
Wenn Sie auf die Schaltfläche (engl. button) doppelklicken, können Sie Text hinzufügen.
Bearbeiten Sie die Schaltflächenbeschriftungen für "Notaus" (engl. "E-stop"), "Maschine ein", Referenzpunkt (engl. "Home"), "Laden" (engl. "Load"), "Ausführen" (engl. "Run"), "Pause" und "Stopp".
Aktionsschaltflächen sind standardmäßig auf keine Aktion eingestellt, daher müssen wir die Eigenschaften für definierte Funktionen ändern. Sie können die Eigenschaften bearbeiten:
-
direkt im Eigenschaften-Editor auf der rechten Seite des Qt-Designers, oder
-
praktischerweise lassen sich durch einen Doppelklick mit der linken Maustaste auf die Schaltfläche ein Dialogfeld "Eigenschaften" aufrufen, was die Auswahl von Aktionen ermöglicht, wobei nur die für die Aktion relevanten Daten angezeigt werden.
Wir werden zunächst den bequemen Weg beschreiben:
-
Klicken Sie mit der rechten Maustaste auf die Schaltfläche Maschine ein (engl.machine on) und wählen Sie Aktionen festlegen (engl. set actions).
-
Wenn das Dialogfeld angezeigt wird, verwenden Sie die Combobox, um zu
MASCHINENSTEUERUNGEN - Maschine ein
(engl.MACHINE CONTROLS - Machine On
) zu navigieren. -
In diesem Fall gibt es keine Option für diese Aktion, also wählen Sie "OK".
Jetzt schaltet die Taste das Gerät ein, wenn sie gedrückt wird.
Und nun der direkte Weg mit dem Eigenschaftseditor von Qt Designer:
-
Wählen Sie die Schaltfläche "Maschine ein".
-
Gehen Sie zum Eigenschaftseditor auf der rechten Seite von Qt Designer.
-
Blättern Sie nach unten, bis Sie die Überschrift ActionButton finden.
-
Klicken Sie auf das Kontrollkästchen für die Aktion "Machine_on", das Sie in der Liste der Eigenschaften und Werte sehen.
Die Taste steuert nun das Ein- und Ausschalten der Maschine.
Machen Sie das Gleiche für alle anderen Schaltflächen und fügen Sie noch einen hinzu:
-
Bei der Schaltfläche "Home" müssen wir auch die Eigenschaft
joint_number
auf1
ändern.
Dadurch wird der Controller angewiesen, alle Achsen und nicht nur eine bestimmte Achse zu referenzieren. -
Mit dem Button "Pause":
-
Unter der Überschrift
Indicated_PushButton
überprüfen Sie dieIndicator_option
. -
Unter der Überschrift
QAbstactButton
markieren Siecheckable
.
-
.ui
-Datei speichernDiesen Entwurf müssen wir dann als tester.ui
im Ordner sim/qtvcp
speichern.
Wir speichern sie unter dem Namen tester, da dies ein Dateiname ist, den QtVCP erkennt und eine eingebaute Handler-Datei verwendet, um sie anzuzeigen.
4.4. Handler-Datei
Eine Handler-Datei ist erforderlich.
Er ermöglicht das Schreiben von Anpassungen in Python.
Zum Beispiel werden Tastatursteuerungen normalerweise in die Handler-Datei geschrieben.
In diesem Beispiel wird die eingebaute Datei tester_handler.py
automatisch verwendet: Sie tut das Minimum, das erforderlich ist, um den in tester.ui
definierten Bildschirm darzustellen und einfache Tastatureingaben vorzunehmen.
4.5. INI-Konfiguration
Wenn Sie QtVCP zur Erstellung eines CNC-Steuerungsbildschirms verwenden, setzen Sie unter der Überschrift INI-Datei [DISPLAY]
:
DISPLAY = qtvcp <Bildschirmname>
_<Bildschirmname>_
ist der Basisname der Dateien .ui
und _handler.py
.
In unserem Beispiel gibt es bereits eine Sim-Konfiguration namens tester, die wir zur Anzeige unseres Testbildschirms verwenden werden.
Wenn Ihr Bildschirm Widgets mit HAL-Pins verwendet, dann müssen Sie diese in einer HAL-Datei verbinden.
QtVCP sucht in der INI-Datei unter der Überschrift [HAL]
nach den folgenden Einträgen:
-
POSTGUI_HALFILE=<Dateiname>
-
Der Konvention nach wäre
<Dateiname>
als+<Bildschirm_name>_postgui.hal+
genannt, aber es kann jeder legale Dateiname sein.
Sie können mehrerePOSTGUI_HALFILE
-Zeilen in der INI haben: jede wird nacheinander in der Reihenfolge ausgeführt, in der sie erscheint.
Diese Befehle werden nach der Erstellung des Bildschirms ausgeführt, um sicherzustellen, dass die HAL-Pins des Widgets verfügbar sind. -
POSTGUI_HALCMD=<Befehl>
-
<Befehl>
wäre jeder gültige HAL-Befehl.
Sie können mehrerePOSTGUI_HALCMD
-Zeilen in der INI haben: jede wird nacheinander in der Reihenfolge ausgeführt, in der sie erscheint.
Um zu garantieren, dass die HAL-Pins des Widgets verfügbar sind, werden diese Befehle ausgeführt:-
nachdem der Bildschirm gebaut ist,
-
nachdem alle POSTGUI_HALFILEs ausgeführt wurden.
-
In unserem Beispiel gibt es keine HAL-Pins zu verbinden.
5. Handler-Datei im Detail
Handler-Dateien werden zur Erstellung von benutzerdefinierten Steuerelementen mit Python verwendet.
5.1. Übersicht
Hier ist ein Beispiel für eine Handler-Datei.
Es ist in Abschnitte unterteilt, um die Diskussion zu erleichtern.
############################ # **** IMPORT SECTION **** # ############################ import sys import os import linuxcnc from PyQt5 import QtCore, QtWidgets from qtvcp.widgets.mdi_line import MDILine as MDI_WIDGET from qtvcp.widgets.gcode_editor import GcodeEditor as GCODE from qtvcp.lib.keybindings import Keylookup from qtvcp.core import Status, Action # Set up logging from qtvcp import logger LOG = logger.getLogger(__name__) # Set the log level for this module #LOG.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL ########################################### # **** BIBLIOTHEKEN INSTANZIIEREN **** # ########################################### KEYBIND = Keylookup() STATUS = Status() ACTION = Action() ################################### # **** HANDLER CLASS SECTION **** # ################################### class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # Widgets ermöglicht den Zugriff auf Widgets aus den QtVCP-Dateien # An dieser Stelle werden die Widgets und HAL-Pins nicht instanziiert def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths ########################################## # SPECIAL FUNCTIONS SECTION # ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready # This is where you make HAL pins or initialize state of widgets etc def initialized__(self): pass def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2, QtCore.Qt.Key_F3,QtCore.Qt.Key_F5,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it is one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to G-code widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True if event.isAutoRepeat():return True # ok if we got here then try keybindings try: return KEYBIND.call(self,event,is_pressed,shift,cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format (e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print('Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key)) return False ######################## # CALLBACKS FROM STATUS # ######################## ####################### # CALLBACKS FROM FORM # ####################### ##################### # GENERAL FUNCTIONS # ##################### # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self,event,state,shift,cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: self.cmnd.abort() # Linear Jogging def on_keycall_XPOS(self,event,state,shift,cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value) ################################ # required handler boiler code # ################################ def get_handlers(halcomp,widgets,paths): return [HandlerClass(halcomp,widgets,paths)]
5.2. IMPORT Bereich
Dieser Abschnitt ist für Import der erforderlichen Bibliotheksmodule für Ihren Bildschirm.
Es wäre typisch, die QtVCP-Bibliotheken keybinding, Status und Action zu importieren.
5.3. Abschnitt INSTANTIATE BIBRARIES
Indem wir die Bibliotheken hier instanziieren, erzeugen wir eine globale Referenz.
Sie können dies an den Befehlen erkennen, denen kein "self" vorangestellt ist.
Konventionell werden die Namen von global referenzierten Bibliotheken großgeschrieben.
5.4. HANDLER CLASS-Abschnitt
Der angepasste Code wird in einer Klasse platziert, damit QtVCP ihn verwenden kann.
Dies ist die Definition der Handler-Klasse.
5.5. INITIALIZE Abschnitt
Wie alle Python-Bibliotheken wird die +__init__+
-Funktion aufgerufen, wenn die Bibliothek erstmals instanziiert wird.
Hier können Sie Standardwerte, Referenzvariablen und globale Variablen einrichten.
Die Referenzen der Widgets sind zu diesem Zeitpunkt nicht verfügbar.
Die Variablen halcomp
, widgets
und paths
ermöglichen den Zugriff auf QtVCP’s HAL-Komponenten, Widgets bzw. Pfadinformationen.
5.6. Abschnitt zu BESONDEREN FUNKTIONEN
Es gibt mehrere spezielle Funktionen, nach denen QtVCP in der Handler-Datei sucht. Wenn QtVCP diese findet, ruft es sie auf, wenn nicht, ignoriert es sie stillschweigend.
-
class_patch__(self):
-
Class patching, auch bekannt als monkey patching, erlaubt es, Funktionsaufrufe in einem importierten Modul zu überschreiben.
Klassen-Patching muss vor der Instanziierung des Moduls durchgeführt werden und verändert alle danach erstellten Instanzen.
Ein Beispiel wäre das Patchen von Schaltflächenaufrufen aus dem G-Code-Editor, um stattdessen Funktionen in der Handler-Datei aufzurufen.
-
initialized__(self):
-
Diese Funktion wird aufgerufen, nachdem die Widgets und HAL-Pins erstellt wurden.
Sie können hier die Widgets und HAL-Pins manipulieren oder weitere HAL-Pins hinzufügen.
In der Regel gibt es-
Einstellungen überprüft und eingestellt,
-
auf Widgets angewendete Stile,
-
Status von LinuxCNC verbunden mit Funktionen.
-
Tastenbelegungen würden hinzugefügt.
-
-
before_hal_init__(self):
-
This function is called before the HAL-ified widgets have their hal_init_ function called.
Some property changes need to be done before HAL_init is called on the widget. -
after_override__(self):
-
Diese Funktion wird aufgerufen, nachdem die optionale Override-Datei geladen ist,
aber bevor die optionale HAL-Datei geladen oder HAL-Komponente bereit ist. -
processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl):
-
Diese Funktion wird aufgerufen, um Tastatur-Jogging usw. zu erleichtern.
Durch die Verwendung derkeybinding
-Bibliothek kann dies verwendet werden, um einfach Funktionen hinzuzufügen, die an Tastendrücke gebunden sind. -
keypress_event__(self,receiver, event):
-
Diese Funktion liefert rohe Tastendruckereignisse.
Sie hat _Vorrang vor demverarbeiteten_Tastenereignis
. -
keyrelease_event__(receiver, event):
-
Diese Funktion gibt raw key release events aus.
Es hat Vorrang vor demprocessed_key_event
. -
before_loop__(self):
-
Diese Funktion wird kurz vor dem Eintritt in die Qt-Ereignisschleife aufgerufen. Zu diesem Zeitpunkt sind alle Widgets/Bibliotheken/Initialisierungscodes abgeschlossen und der Bildschirm wird bereits angezeigt.
-
system_shutdown_request__(self):
-
Falls vorhanden, überschreibt diese Funktion die normale Funktion, die beim vollständigen Herunterfahren des Systems aufgerufen wird.
Sie kann dazu benutzt werden, vor dem Herunterfahren Hausarbeiten zu erledigen.
-
+ Das Linux System wird nicht heruntergefahren, wenn Sie diese Funktion verwenden, Sie müssen das selbst tun.
QtVCP/LinuxCNC beendet sich ohne eine Eingabeaufforderung, sobald diese Funktion zurückkehrt.
-
-
closing_cleanup__(self):
-
Diese Funktion wird kurz vor dem Schließen des Bildschirms aufgerufen. Sie kann verwendet werden, um vor dem Schließen aufzuräumen.
5.7. STATUS CALLBACKS Abschnitt
Konventionell würden Sie hier Funktionen unterbringen, die Rückrufe von STATUS-Definitionen sind.
5.8. CALLBACKS FROM FORM Abschnitt
Konventionell würden Sie hier Funktionen ablegen, die Rückrufe von den Widgets sind, die mit dem MainWindow im Qt Designer-Editor verbunden sind.
5.9. Abschnitt mit ALLGEMEINEN FUNKTIONEN
Konventionell werden hier die allgemeinen Funktionen untergebracht.
5.10. Abschnitt zur KEY BINDING (engl. für Tastenbelegung)
Wenn Sie die Keybinding
-Bibliothek_ verwenden, platzieren Sie hier Ihre benutzerdefinierten Tastenaufrufroutinen.
Die Funktionssignatur ist:
def on_keycall_KEY(self,event,state,shift,cntrl): if state: self.do_something_function()
KEY
ist der Code (aus der Keybindings-Bibliothek) für den gewünschten Schlüssel.
5.11. CLOSING EVENT Sektion
Wenn Sie die Funktion closeEvent
hier einfügen, werden Schließungsereignisse abgefangen.
Dies replaces eine vordefinierte 'closeEvent'-Funktion von QtVCP.
def closeEvent(self, event): self.do_something() event.accept()
Anmerkung
|
Normalerweise ist es besser, die spezielle Funktion closing_cleanup__ zu verwenden. |
6. Verbinden von Widgets mit Python-Code
Es ist möglich, Widgets über Signale und Slots mit Python-Code zu verbinden.
Auf diese Weise können Sie:
-
LinuxCNC-Widgets neue Funktionen geben, oder
-
Standard Qt-Widgets zur Steuerung von LinuxCNC verwenden.
6.1. Übersicht
In the Qt Designer editor:
-
Sie erstellen Benutzerfunktions-Slots
-
Sie verbinden die Slots mit Widgets, indem Sie Signale verwenden.
In the handler file:
-
Sie erstellen die Funktionen des Slots, die im Qt Designer definiert sind.
6.2. Hinzufügen von Slots mit Qt Designer
Wenn Sie Ihren Bildschirm in Qt Designer geladen haben, fügen Sie einen einfachen PushButton
zu dem Bildschirm hinzu.
Sie könnten den Namen der Schaltfläche in etwas Interessantes wie "test_button" ändern.
Es gibt zwei Möglichkeiten, Verbindungen zu bearbeiten - Dies ist die grafische Methode.
-
In der oberen Werkzeugleiste von Qt Designer gibt es eine Schaltfläche zum Bearbeiten von Signalen. Wenn Sie auf die Schaltfläche klicken und sie gedrückt halten, wird ein Pfeil angezeigt (sieht aus wie ein Erdungssignal aus einem elektrischen Schaltplan).
-
Schieben Sie diesen Pfeil auf einen Bereich des Hauptfensters, in dem sich keine Widgets befinden.
-
Ein Dialogfeld „Verbindungen konfigurieren“ wird angezeigt.
-
Die Liste auf der linken Seite enthält die verfügbaren Signale des Widgets.
-
Die Liste auf der rechten Seite sind die verfügbaren Slots im Hauptfenster und Sie können sie ergänzen.
-
-
Wählen Sie das Signal
clicked()
- dies macht die Slot-Seite verfügbar. -
Klicken Sie in der Slotliste auf "Bearbeiten" (engl. edit).
-
Ein Dialogfeld "Slots/Signale des Hauptfensters" wird angezeigt.
-
In der Slots-Liste oben befindet sich ein "+"-Symbol - klicken Sie darauf.
-
Sie können nun einen neuen Slotnamen bearbeiten.
-
Löschen Sie den Standardnamen
slot()
und ändern Sie ihn intest_button()
. -
Drücken Sie die Taste OK.
-
Sie gelangen zurück zum Dialog Verbindungen konfigurieren.
-
Nun können Sie Ihren neuen Slot in der Slotliste auswählen.
-
Drücken Sie dann auf "OK" und speichern Sie die Datei.
6.3. Änderungen am Python-Handler
Nun müssen Sie die Funktion in die Handler-Datei einfügen.
Die Funktionssignatur lautet def slot_name(self):
.
Für unser Beispiel fügen wir etwas Code hinzu, um den Namen des Widgets zu auszugeben:
def test_button(self): name = self.w.sender().text() print(name)
Fügen Sie diesen Code unter dem Abschnitt namens:
####################### # Callbacks vom Formular #######################
Tatsächlich spielt es keine Rolle, wo in der Handler-Klasse Sie die Befehle ablegen, aber per Konvention ist dies der Ort, an dem Sie sie ablegen müssen.
Speichern der Handlerdatei.
Wenn Sie nun Ihren Bildschirm laden und die Schaltfläche drücken, sollte der Name der Schaltfläche im Terminal angezeigt werden.