Dialog creation/de: Difference between revisions

From FreeCAD Documentation
(Created page with "Auf dieser Seite zeigen wir, wie man einen einfachen Qt Dialog aufbaut mit [http://qt-project.org/doc/qt-4.8/designer-manual.html Qt-Designer], das offizielle Werkzeug von Qt,...")
No edit summary
 
(204 intermediate revisions by 6 users not shown)
Line 1: Line 1:
<languages/>
Auf dieser Seite zeigen wir, wie man einen einfachen Qt Dialog aufbaut mit [http://qt-project.org/doc/qt-4.8/designer-manual.html Qt-Designer], das offizielle Werkzeug von Qt, um Schnittstellen zu entwerfen. Das Ergebnis wandeln wir dann um in Python-Code, dann verwenden Sie es innerhalb FreeCAD. Ich werde in diesem Beispiel davon ausgehen, dass Sie bereits wissen, wie man Python-Skripte bearbeitet und ausführt, und dass Sie einfache Dinge in einem Terminal-Fenster wie navigieren, etc. ebenfalls können. Ebenso muss natürlich auch pyqt installiert sein.

{{Docnav/de
|[[Interface_creation/de|Erstellung von Benutzeroberflächen]]
|[[License/de|Lizenz]]
}}

{{TOCright}}

<span id="Introduction"></span>
==Einleitung==

Auf dieser Seite werden wir zeigen, wie man mit [http://qt-project.org/doc/qt-4.8/designer-manual.html Qt Designer], Qt's offiziellem Werkzeug für die Gestaltung von Oberflächen, eine einfache grafische Oberfläche erstellt; der Dialog wird in [[Python/de|Python]] Code umgewandelt und dann innerhalb von FreeCAD verwendet. Wir gehen davon aus, dass der Benutzer weiß, wie man [[Python/de|Python]] allgemein bearbeitet und ausführt.

In diesem Beispiel ist die gesamte Oberfläche in [[Python/de|Python]] definiert. Obwohl dies bei kleinen Schnittstellen möglich ist, empfiehlt es sich bei größeren Schnittstellen, die erstellten {{FileName|.ui}} Dateien direkt in das Programm zu laden.

[[File:FreeCAD_creating_interfaces.svg|600px]]
{{Caption|Zwei allgemeine Methoden zur Erstellung von Oberflächen, durch Einfügen der Schnittstelle in die Python Datei oder durch die Verwendung von {{incode|.ui}} Dateien.}}

<span id="Designing_the_dialog"></span>
== Gestalten des Dialogs ==


In CAD Anwendungen ist es sehr wichtig, eine gute UI (Benutzeroberfläche) zu entwerfend. Beinahe alles, das der Benutzer tun wird, wird über ein Teil der Oberfläche passieren: das Lesen von Dialog Kästen, drücken von Knöpfen, die Auswahl zwischen Piktogrammen, usw. Es ist also sehr wichtig, sich genau zu überlegen, was du tun willst, wie der Benutzer sich verhalten soll und wie der Ablauf deiner Aktion aussehen soll.
== Designing the dialog ==
In CAD applications, designing a good UI (User Interface) is very important. About everything the user will do will be through some piece of interface: reading dialog boxes, pressing buttons, choosing between icons, etc. So it is very important to think carefully to what you want to do, how you want the user to behave, and how will be the workflow of your action.


Ein paar Konzepte sollte man bei der Gestaltung der Oberfläche kennen:
There are a couple of concepts to know when designing interface:
* [http://en.wikipedia.org/wiki/Modal_window Modal/non-modal dialogs]: A modal dialog appears in front of your screen, stopping the action of the main window, forcing the user to respond to the dialog, while a non-modal dialog doesn't stop you from working on the main window. In some case the first is better, in other cases not.
* [http://de.wikipedia.org/wiki/Dialogfenster Dialoge]: Ein modaler Dialog erscheint auf Ihrem Schirm, unterbricht die Handlung des Hauptfensters, zwingt den Benutzer auf den Dialog zu antworten, während ein nichtmodaler Dialog Sie nicht hindert, am Hauptfenster weiter zu arbeiten. In einigen Fällen ist der erste besser, in anderen Fällen nicht.
* Identifying what is required and what is optional: Make sure the user knows what he must do. Label everything with proper description, use tooltips, etc.
* Erkennen, was erforderlich ist und was optional: Stellen Sie sicher, dass der Benutzer weiß, was er tun muss. Etikettieren Sie alles mit der richtigen Beschreibung, verwenden Sie tooltips usw.
* Die Trennung von Befehlen und Parametern: Das wird gewöhnlich mit Knöpfen und Texteingabefeldern getan. Der Benutzer weiß, dass das Klicken eines Knopfs eine Handlung erzeugen wird, indem er einen Wert innerhalb eines Textfeldes ändert, wird irgendwo ein Parameter geändert. Heutzutage aber wissen Benutzer gewöhnlich gut, was ein Knopf ist, was ein Eingangsfeld usw ist. Das Schnittstellen-Werkzeug Qt, das wir verwenden, ist eines der modernsten Werkzeuge und wir werden uns nicht viel darum sorgen müssen, Dinge verständlich zu machen, da sie bereits in sich selbst sehr klar sein werden.
* Separating commands from parameters: This is usually done with buttons and text input fields. The user knows that clicking a button will produce an action while changing a value inside a text field will change a parameter somewhere. Nowadays, though, users usually know well what is a button, what is an input field, etc. The interface toolkit we are using, Qt, is a state-of-the-art toolkit, and we won't have to worry much about making things clear, since they will already be very clear by themselves.


Nachdem wir gut definiert haben, was wir tun werden, ist es Zeit, den Qt-Designer zu öffnen. Lassen Sie uns einen sehr einfachen Dialog entwerfen:
So, now that we have well defined what we will do, it's time to open the qt designer. Let's design a very simple dialog, like this:


[[Image:Qttestdialog.jpg]]
[[Image:Qttestdialog.jpg]]


We will then use this dialog in FreeCAD to produce a nice rectangular plane. You might find it not very useful to produce nice rectangular planes, but it will be easy to change it later to do more complex things. When you open it, Qt Designer looks like this:
Wir werden dann mit diesem Dialog in FreeCAD eine schöne rechteckige Ebene erzeugen. Vielleicht finden Sie es nicht sehr nützlich, nette rechteckige Fläche zu produzieren, aber es wird leicht sein, sie später zu ändern, um komplexere Dinge zu tun. Wenn Sie es öffnen, sieht Qt-Designer wie folgt aus:


[[Image:Qtdesigner-screenshot.jpg]]
[[Image:Qtdesigner-screenshot.jpg]]


== Erstellen des Dialogs ==
It is very simple to use. On the left bar you have elements that can be dragged on your widget. On the right side you have properties panels displaying all kinds of editable properties of selected elements. So, begin with creating a new widget. Select "Dialog without buttons", since we don't want the default Ok/Cancel buttons. Then, drag on your widget '''3 labels''', one for the title, one for writing "Height" and one for writing "Width". Labels are simple texts that appear on your widget, just to inform the user. If you select a label, on the right side will appear several properties that you can change if you want, such as font style, height, etc.
Qt Designer ist sehr einfach zu benutzen. Auf der linken Leiste hast du Elemente, die auf dein Widget gezogen werden können. Auf der rechten Seite hast du Eigenschaftspaneels, die alle Arten von editierbaren Eigenschaften der ausgewählten Elemente anzeigen. Beginne also mit der Erstellung eines neuen Widgets.
# Wähle "Dialog ohne Schaltflächen", da wir die Standardschaltflächen {{KEY|OK}}/{{KEY|Abbrechen}} nicht wollen.
# Wir brauchen '''Labels''' (Beschriftungen). Labels sind einfache Textzeichenfolgen, die auf deinem Widget erscheinen, um den Endbenutzer zu informieren. Wenn du ein Label auswählst, beachte, dass auf der rechten Seite verschiedene Eigenschaften erscheinen, die du ändern kannst, wie z.B.: Schriftstil, Höhe, usw... Lass uns also 3 separate Label auf unser Widget ziehen:
#* Ein Label für den Titel
#* Ein weiteres Label zum Schreiben von "'''Height'''"
#* Ein weiteres Label zum Schreiben von "'''Width'''"
# Wir brauchen jetzt LineEdits (eigentlich 2 davon). Ziehe zwei davon auf das Widget. '''LineEdits''' sind Textfelder, die der Endbenutzer ausfüllen kann. Wir brauchen also einen LineEdit für die ''Höhe'' und einen für die ''Breite''. Auch hier können wir Eigenschaften bearbeiten. Warum nicht z.B. einen Standardwert setzen, sagen wir z.B.: 1,00 für jedes. Auf diese Weise werden beide Werte bereits ausgefüllt sein, wenn der Benutzer den Dialog sieht. Wenn der Endbenutzer zufrieden ist, kann er direkt auf die Schaltfläche drücken und so wertvolle Zeit sparen.
# Als nächstes können wir einen '''PushButton''' (Druckknopf) hinzufügen. Dies ist die Schaltfläche, die der Endbenutzer drücken muss, nachdem er beide Felder ausgefüllt hat.


'''Hinweis:''' dass wir hier sehr einfache Bedienelemente gewählt haben. Qt hat viel mehr Optionen, zum Beispiel könnte man '''Spinboxes''' anstelle von '''LineEdits''' verwenden, usw... Schaudir an, was verfügbar ist, erkunde... Du wirst sicher andere Ideen haben.
Then, add '''2 LineEdits''', which are text fields that the user can fill in, one for the height and one for the width. Here too, we can edit properties. For example, why not set a default value? For example 1.00 for each. This way, when the user will see the dialog, both values will be filled already and if he is satisfied he can directly press the button, saving precious time. Then, add a '''PushButton''', which is the button the user will need to press after he filled the 2 fields.


Das ist soweit alles, was wir im Qt Designer tun müssen. Eine letzte Sache noch: Benennen wir alle unsere Elemente mit einfacheren Namen um, damit sie in unseren Skripten leichter zu identifizieren sind:
Note that I choosed here very simple controls, but Qt has many more options, for example you could use Spinboxes instead of LineEdits, etc... Have a look at what is available, you will surely have other ideas.

That's about all we need to do in Qt Designer. One last thing, though, let's rename all our elements with easier names, so it will be easier to identify them in our scripts:


[[Image:Qtpropeditor.jpg]]
[[Image:Qtpropeditor.jpg]]


<span id="Converting_our_dialog_to_python"></span>
== Converting our dialog to python ==
== Umwandeln unseres Dialogs zu Python ==

Jetzt werden wir unser Widget irgendwo speichern. Er wird als .ui Datei gespeichert, die wir einfach mittels pyuic zu einem Python Skript wandeln werden. Unter Windows ist das pyuic Programm meistens mit pyqt gebündelt (zu überprüfen), auf Linux weirst du es wahrscheinlich extra mit deinem Paketmanager installieren müssen (auf Debian-basierten Systemen, ist es ein Teil des pyqt4-dev-tools Pakets). Für die Konvertierung musst du ein Terminalfenster (oder ein Fenster der Eingabeaufforderung unter Windows) öffnen, in welchem du zu deiner .ui Datei navigierst und dort dann folgendes eingibst:
Now, let's save our widget somewhere. It will be saved as an .ui file, that we will easily convert to python script with pyuic. On windows, the pyuic program is bundled with pyqt (to be verified), on linux you probably will need to install it separately from your package manager (on debian-based systems, it is part of the pyqt4-dev-tools package). To do the conversion, you'll need to open a terminal window (or a command prompt window on windows), navigate to where you saved your .ui file, and issue:
{{Code|code=
<syntaxhighlight>
pyuic mywidget.ui > mywidget.py
pyuic mywidget.ui > mywidget.py
}}
</syntaxhighlight>
Unter Windows befindet sich pyuic.py in "C:\Python27\Lib\site-Pakete\PyQt4\uic\pyuic.py".
On some systems the program is called pyuic4 instead of pyuic. This will simply convert the .ui file into a python script. If we open the mywidget.py file, its contents are very easy to understand:
Erstelle für die Umwandlung eine Batchdatei mit dem Namen "compQt4.bat":
<syntaxhighlight>
{{Code|code=
from PyQt4 import QtCore, QtGui
@"C:\Python27\python" "C:\Python27\Lib\site-packages\PyQt4\uic\pyuic.py" -x %1.ui > %1.py
}}
In der DOS Konsole tippe ohne Erweiterung
{{Code|code=
compQt4 myUiFile
}}

Unter macOS kannst du mit diesen Befehlen (pip erforderlich) die entsprechende Version von Qt bzw. Pyside ermitteln (die gleiche, die intern in FreeCAD 0.19 benutzt wird)
{{Code|code=
python3 -m pip install pyqt5
python3 -m pip install pySide2
}}
Dies wird uic im Verzeichnis "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/uic" installieren und Designer in "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/Designer.app".
Der Einfachheit halber kannst du einen Link auf uic in /usr/local/bin erstellen, um es einfach per uic -g python ... aufzurufen anstatt den ganzen Pfad zum Programm einzutippen und einen Link zu Designer, um ihn im Mac-Applikationsverzeichnis aufzurufen mit
{{Code|code=
sudo ln -s /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/uic /usr/local/bin
ln -s /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/Designer.app /Applications
}}

In Linux: Zu Tun

Nachdem sich FreeCAD nach v0.13 zunehmend von PyQt zugunsten von [http://qt-project.org/wiki/PySide PySide] entfernt hat (Wähle deine PySide Installation [http://pyside.readthedocs.org/en/latest/building/ PySide bauen]), um die Datei auf der Basis von PySide zu erstellen, musst du jetzt verwenden:

{{Code|code=
pyside-uic mywidget.ui -o mywidget.py
}}
Unter Windows ist uic.py in "C:\Python27\Lib\site-packages\PySide\scripts\" zu finden.
Zum Erstellen der Batchdatei "compSide.bat":
{{Code|code=
@"C:\Python27\python" "C:\Python27\Lib\site-packages\PySide\scripts\uic.py" %1.ui > %1.py
}}
In die DOS Konsole tippe ohne Erweiterung
{{Code|code=
compSide myUiFile
}}
In Linux: Zu Tun

Auf einigen Systemen wird das Programm pyuic4 statt pyuic genannt. Dadurch wird die .ui Datei einfach in ein Python Skript umgewandelt. Wenn wir die Datei mywidget.py öffnen, ist ihr Inhalt sehr leicht zu verstehen:
{{Code|code=
from PySide import QtCore, QtGui


class Ui_Dialog(object):
class Ui_Dialog(object):
Line 54: Line 119:
self.title.setText(QtGui.QApplication.translate("Dialog", "Plane-O-Matic", None, QtGui.QApplication.UnicodeUTF8))
self.title.setText(QtGui.QApplication.translate("Dialog", "Plane-O-Matic", None, QtGui.QApplication.UnicodeUTF8))
...
...
}}
</syntaxhighlight>
Wie du siehst, hat es eine sehr einfache Struktur: eine Klasse genannt Ui_Dialog wird erstellt, die die Elemente der Benutzeroberfläche unseres Widgets speichert. Diese Klasse hat zwei Methoden, eine für die Einrichtung des Widgets und eine für die Übersetzung ihres Inhalts, was ein Teil des allgemeinen Qt Mechanismus für die Übersetzung von Oberflächenelementen ist. Das Einrichtungsverfahren erzeugt einfach eins nach dem anderen die Widgets, wie wir sie in Qt Designer festgelegt haben und setzt ihre Optionen, die wir ebenfalls gewählt haben. Dann wird die ganze Schnittstelle schließlich übersetzt, die Slots werden verbunden (wir werden darüber später sprechen).
As you see it has a very simple structure: a class named Ui_Dialog is created, that stores the interface elements of our widget. That class has two methods, one for setting up the widget, and one for translating its contents, that is part of the general Qt mechanism for translating interface elements. The setup method simply creates, one by one, the widgets as we defined them in Qt Designer, and sets their options as we decided earlier. Then, the whole interface gets translated, and finally, the slots get connected (we'll talk about that later).


We can now create a new widget, and use this class to create its interface. We can already see our widget in action, by putting our mywidget.py file in a place where FreeCAD will find it (in the FreeCAD bin directory, or in any of the Mod subdirectories), and, in the FreeCAD python interpreter, issue:
Wir können jetzt ein neues Widget erstellen und diese Klasse verwenden, um seine Oberfläche zu schaffen. Wir können bereits unser Widget in Aktion sehen, indem wir unsere mywidget.py Datei an einem Ort speichern, wo FreeCAD es findet (im FreeCAD bin Verzeichnis, oder in einem der Mod Unterverzeichnisse), und dann im FreeCAD Python Interpreter eintippen:
{{Code|code=
<syntaxhighlight>
from PyQt4 import QtGui
from PySide import QtGui
import mywidget
import mywidget
d = QtGui.QWidget()
d = QtGui.QWidget()
Line 65: Line 130:
d.ui.setupUi(d)
d.ui.setupUi(d)
d.show()
d.show()
}}
</syntaxhighlight>
Und unser Dialog wird erscheinen! Beachte, dass unser Python Interpreter immer noch arbeitet, wir haben einen nichtmodalen Dialog. Um es zu abzuschließen, können wir (abgesehen von einem Klick auf das Schließen Symbol, natürlich) eingeben:
And our dialog will appear! Note that our python interpreter is still working, we have a non-modal dialog. So, to close it, we can (apart from clicking its close icon, of course) issue:
{{Code|code=
<syntaxhighlight>
d.hide()
d.hide()
}}
</syntaxhighlight>
== Making our dialog do something ==
Now that we can show and hide our dialog, we just need to add one last part: To make it do something! If you play a bit with Qt designer, you'll quickly discover a whole section called "signals and slots". Basically, it works like this: elements on your widgets (in Qt terminology, those elements are themselves widgets) can send signals. Those signals differ according to the widget type. For example, a button can send a signal when it is pressed and when it is released. Those signals can be connected to slots, which can be special functionality of other widgets (for example a dialog has a "close" slot to which you can connect the signal from a close button), or can be custom functions. The [http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/classes.html PyQt Reference Documentation] lists all the qt widgets, what they can do, what signals they can send, etc...


== Unseren Dialog etwas tun lassen ==
What we will do here, is to create a new function that will create a plane based on height and width, and to connect that function to the pressed signal emitted by our "Create!" button. So, let's begin with importing our FreeCAD modules, by putting the following line at the top of the script, where we already import QtCore and QtGui:
Nun, da wir unseren Dialog ein- und ausblenden können, müssen wir nur noch einen letzten Teil hinzufügen: Damit er etwas bewirkt! Wenn du ein wenig mit dem Qt Designer spielst, wirst du schnell einen ganzen Abschnitt namens "Signals and Slots" entdecken. Im Grunde funktioniert es so: Elemente auf deinen Widgets (in der Qt Terminologie sind diese Elemente selbst Widgets) können Signale senden. Diese Signale unterscheiden sich je nach Widget Typ. Zum Beispiel kann eine Taste ein Signal senden, wenn sie gedrückt wird und wenn sie losgelassen wird. Diese Signale können mit Slots verbunden werden, was eine spezielle Funktionalität anderer Widgets sein kann (z.B. hat ein Dialog einen "Schließ" Slot, mit dem du das Signal von einer Schaltfläche zum Schließen verbinden kannst), oder es können benutzerdefinierte Funktionen sein. Die [http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/classes.html PyQt Referenzdokumentation] listet alle qt Widgets auf, was sie können, welche Signale sie senden können, usw..
<syntaxhighlight>

Was wir hier tun werden, ist eine neue Funktion zu erstellen, die eine Ebene erzeugt, beruhend auf Höhe und Breite, und in Verbindung steht diese Funktion mit dem gedrückt Signal, das unser "Create" Knopf abgibt. Also lass uns mit dem Importieren unserer FreeCAD Module beginnen, durch setzen der folgenden Zeile an den Anfang des Skripts, wo wir bereits QtCore und QtGui importieren:
{{Code|code=
import FreeCAD, Part
import FreeCAD, Part
}}
</syntaxhighlight>
Then, let's add a new function to our Ui_Dialog class:
Dann lass uns eine neue Funktion zu unserer Ui_Dialog Klasse hinzufügen:
{{Code|code=
<syntaxhighlight>
def createPlane(self):
def createPlane(self):
try:
try:
Line 85: Line 151:
h = float(self.height.text())
h = float(self.height.text())
except ValueError:
except ValueError:
print "Error! Width and Height values must be valid numbers!"
print("Error! Width and Height values must be valid numbers!")
else:
else:
# create a face from 4 points
# create a face from 4 points
Line 97: Line 163:
Part.show(myface)
Part.show(myface)
self.hide()
self.hide()
}}
</syntaxhighlight>
Then, we need to inform Qt to connect the button to the function, by placing the following line just before QtCore.QMetaObject.connectSlotsByName(Dialog):
Dann müssen wir Qt mitteilen, dass die Schaltfläche mit der Funktion verbunden werden soll, indem wir die folgende Zeile unmittelbar vor QtCore.QMetaObject.connectSlotsByName(Dialog) setzen:
{{Code|code=
<syntaxhighlight>
QtCore.QObject.connect(self.create,QtCore.SIGNAL("pressed()"),self.createPlane)
QtCore.QObject.connect(self.create,QtCore.SIGNAL("pressed()"),self.createPlane)
}}
</syntaxhighlight>
This, as you see, connects the pressed() signal of our create object (the "Create!" button), to a slot named createPlane, which we just defined. That's it! Now, as a final touch, we can add a little function to create the dialog, it will be easier to call. Outside the Ui_Dialog class, let's add this code:
Dies verbindet, wie du siehst, das pressed() Signal unseres create Objekts (die Schaltfläche "Create!") mit einem Slot namens createPlane, den wir gerade definiert haben. Das war's! Als letzten Schliff können wir nun noch eine kleine Funktion zum Erstellen des Dialogs hinzufügen, die einfacher aufzurufen sein wird. Außerhalb der Klasse Ui_Dialog fügen wir diesen Code hinzu:
{{Code|code=
<syntaxhighlight>
class plane():
class plane():
def __init__(self):
def __init__(self):
Line 110: Line 176:
self.ui.setupUi(self.d)
self.ui.setupUi(self.d)
self.d.show()
self.d.show()
}}
</syntaxhighlight>
(Python reminder: the __init__ method of a class is automatically executed whenever a new object is created!)
(Python Erinnerung: Die __init__ Methode einer Klasse wird automatisch ausgeführt, wenn ein neues Objekt erzeugt wird!)
Then, from FreeCAD, we only need to do:
Dann brauchen wir von FreeCAD aus nur folgendes zu tun:
{{Code|code=
<syntaxhighlight>
import mywidget
import mywidget
myDialog = mywidget.plane()
myDialog = mywidget.plane()
}}
</syntaxhighlight>
That's all Folks... Now you can try all kinds of things, like for example inserting your widget in the FreeCAD interface (see the [[Code snippets]] page), or making much more advanced custom tools, by using other elements on your widget.
Das ist alles, Leute... Jetzt kannst du alle möglichen Dinge ausprobieren, wie z.B. dein Widget in die FreeCAD Oberfläche einzufügen (siehe die [[Code snippets/de|Code Schnipsel]] Seite), oder viel fortgeschrittenere, eigene Werkzeuge zu erstellen, indem du andere Elemente auf deinem Widget verwendest.

== The complete script ==
This is the complete script, for reference:
<syntaxhighlight>
# -*- coding: utf-8 -*-


== Das komplette Skript ==
Dies ist das vollständige Skript, als Referenz:
{{Code|code=
# Form implementation generated from reading ui file 'mywidget.ui'
# Form implementation generated from reading ui file 'mywidget.ui'
#
#
# Created: Mon Jun 1 19:09:10 2009
# Created: Mon Jun 1 19:09:10 2009
# by: PyQt4 UI code generator 4.4.4
# by: PyQt4 UI code generator 4.4.4
# Modified for PySide 16:02:2015
#
# WARNING! All changes made in this file will be lost!
# WARNING! All changes made in this file will be lost!


from PyQt4 import QtCore, QtGui
from PySide import QtCore, QtGui
import FreeCAD, Part
import FreeCAD, Part


Line 162: Line 226:


def retranslateUi(self, Dialog):
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
Dialog.setWindowTitle("Dialog")
self.title.setText(QtGui.QApplication.translate("Dialog", "Plane-O-Matic", None, QtGui.QApplication.UnicodeUTF8))
self.title.setText("Plane-O-Matic")
self.label_width.setText(QtGui.QApplication.translate("Dialog", "Width", None, QtGui.QApplication.UnicodeUTF8))
self.label_width.setText("Width")
self.label_height.setText(QtGui.QApplication.translate("Dialog", "Height", None, QtGui.QApplication.UnicodeUTF8))
self.label_height.setText("Height")
self.create.setText(QtGui.QApplication.translate("Dialog", "Create!", None, QtGui.QApplication.UnicodeUTF8))
self.create.setText("Create!")
print("tyty")

def createPlane(self):
def createPlane(self):
try:
try:
Line 174: Line 238:
h = float(self.height.text())
h = float(self.height.text())
except ValueError:
except ValueError:
print "Error! Width and Height values must be valid numbers!"
print("Error! Width and Height values must be valid numbers!")
else:
else:
# create a face from 4 points
# create a face from 4 points
Line 187: Line 251:


class plane():
class plane():
def __init__(self):
def __init__(self):
self.d = QtGui.QWidget()
self.d = QtGui.QWidget()
self.ui = Ui_Dialog()
self.ui = Ui_Dialog()
self.ui.setupUi(self.d)
self.ui.setupUi(self.d)
self.d.show()
self.d.show()
</syntaxhighlight>
==Creation of a dialog with buttons==


}}
===Method 1===
An example of a dialog box complete with its connections.
<syntaxhighlight>
# -*- coding: utf-8 -*-
# Create by flachyjoe


<span id="More_examples"></span>
from PyQt4 import QtCore, QtGui
==Weitere Beispiele ==


* [[Dialog_creation_with_various_widgets/de|Dialogerstellung mit verschiedenen Widgets]] mit {{incode|QPushButton}}, {{incode|QLineEdit}}, {{incode|QCheckBox}}, {{incode|QRadioButton}}, und anderen.
try:
* [[Dialog_creation_reading_and_writing_files/de|Dialogerstellung Lesen und Schreiben von Dateien]] mit {{incode|QFileDialog}}.
_fromUtf8 = QtCore.QString.fromUtf8
* [[Dialog_creation_setting_colors/de|Dialogerstellung Farben festlegen]] mit {{incode|QColorDialog}}.
except AttributeError:
* [[Dialog_creation_image_and_animated_GIF/de|Dialogerstellung Bild und animierte GIF]] mit {{incode|QLabel}} und {{incode|QMovie}}.
def _fromUtf8(s):
* [[PySide_usage_snippets/de|PySide Verwendungsschnipsel]].
return s
* [[Qt_Example/de|Qt Beispiel]]


<span id="Relevant_links"></span>
try:
== Relevante Verweise ==
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)


* [[Manual:Creating interface tools/de|Handbuch: Erstellen von Oberflächenwerkzeugen]]


class Ui_MainWindow(object):


{{Docnav/de
def __init__(self, MainWindow):
|[[Interface_creation/de|Erstellung von Benutzeroberflächen]]
self.window = MainWindow
|[[License/de|Lizenz]]
}}


{{Powerdocnavi{{#translation:}}}}
MainWindow.setObjectName(_fromUtf8("MainWindow"))
[[Category:Developer Documentation{{#translation:}}]]
MainWindow.resize(400, 300)
[[Category:Python Code{{#translation:}}]]
self.centralWidget = QtGui.QWidget(MainWindow)
self.centralWidget.setObjectName(_fromUtf8("centralWidget"))

self.pushButton = QtGui.QPushButton(self.centralWidget)
self.pushButton.setGeometry(QtCore.QRect(30, 170, 93, 28))
self.pushButton.setObjectName(_fromUtf8("pushButton"))
self.pushButton.clicked.connect(self.on_pushButton_clicked) #connection pushButton

self.lineEdit = QtGui.QLineEdit(self.centralWidget)
self.lineEdit.setGeometry(QtCore.QRect(30, 40, 211, 22))
self.lineEdit.setObjectName(_fromUtf8("lineEdit"))
self.lineEdit.returnPressed.connect(self.on_lineEdit_clicked) #connection lineEdit

self.checkBox = QtGui.QCheckBox(self.centralWidget)
self.checkBox.setGeometry(QtCore.QRect(30, 90, 81, 20))
self.checkBox.setChecked(True)
self.checkBox.setObjectName(_fromUtf8("checkBoxON"))
self.checkBox.clicked.connect(self.on_checkBox_clicked) #connection checkBox

self.radioButton = QtGui.QRadioButton(self.centralWidget)
self.radioButton.setGeometry(QtCore.QRect(30, 130, 95, 20))
self.radioButton.setObjectName(_fromUtf8("radioButton"))
self.radioButton.clicked.connect(self.on_radioButton_clicked) #connection radioButton

MainWindow.setCentralWidget(self.centralWidget)

self.menuBar = QtGui.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 26))
self.menuBar.setObjectName(_fromUtf8("menuBar"))
MainWindow.setMenuBar(self.menuBar)

self.mainToolBar = QtGui.QToolBar(MainWindow)
self.mainToolBar.setObjectName(_fromUtf8("mainToolBar"))
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)

self.statusBar = QtGui.QStatusBar(MainWindow)
self.statusBar.setObjectName(_fromUtf8("statusBar"))
MainWindow.setStatusBar(self.statusBar)

self.retranslateUi(MainWindow)

def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
self.pushButton.setText(_translate("MainWindow", "OK", None))
self.lineEdit.setText(_translate("MainWindow", "tyty", None))
self.checkBox.setText(_translate("MainWindow", "CheckBox", None))
self.radioButton.setText(_translate("MainWindow", "RadioButton", None))

def on_checkBox_clicked(self):
if self.checkBox.checkState()==0:
App.Console.PrintMessage(str(self.checkBox.checkState())+" CheckBox KO\r\n")
else:
App.Console.PrintMessage(str(self.checkBox.checkState())+" CheckBox OK\r\n")
# App.Console.PrintMessage(str(self.lineEdit.setText("tititi"))+" LineEdit\r\n") #write text to the lineEdit window !
# str(self.lineEdit.setText("tititi")) #écrit le texte dans la fenêtre lineEdit
App.Console.PrintMessage(str(self.lineEdit.displayText())+" LineEdit\r\n")

def on_radioButton_clicked(self):
if self.radioButton.isChecked():
App.Console.PrintMessage(str(self.radioButton.isChecked())+" Radio OK\r\n")
else:
App.Console.PrintMessage(str(self.radioButton.isChecked())+" Radio KO\r\n")

def on_lineEdit_clicked(self):
# if self.lineEdit.textChanged():
App.Console.PrintMessage(str(self.lineEdit.displayText())+" LineEdit Display\r\n")

def on_pushButton_clicked(self):
App.Console.PrintMessage("Terminé\r\n")
self.window.hide()

MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow(MainWindow)
MainWindow.show()
</syntaxhighlight>
Here the same window but with an icon on each button.
<syntaxhighlight>
# -*- coding: utf-8 -*-

from PyQt4 import QtCore, QtGui

try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s

try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)


class Ui_MainWindow(object):

def __init__(self, MainWindow):
self.window = MainWindow
# path = FreeCAD.ConfigGet("UserAppData")
path = FreeCAD.ConfigGet("AppHomePath")

MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(400, 300)
self.centralWidget = QtGui.QWidget(MainWindow)
self.centralWidget.setObjectName(_fromUtf8("centralWidget"))

self.pushButton = QtGui.QPushButton(self.centralWidget)
self.pushButton.setGeometry(QtCore.QRect(30, 170, 93, 28))
self.pushButton.setObjectName(_fromUtf8("pushButton"))
self.pushButton.clicked.connect(self.on_pushButton_clicked) #connection pushButton

self.lineEdit = QtGui.QLineEdit(self.centralWidget)
self.lineEdit.setGeometry(QtCore.QRect(30, 40, 211, 22))
self.lineEdit.setObjectName(_fromUtf8("lineEdit"))
self.lineEdit.returnPressed.connect(self.on_lineEdit_clicked) #connection lineEdit

self.checkBox = QtGui.QCheckBox(self.centralWidget)
self.checkBox.setGeometry(QtCore.QRect(30, 90, 100, 20))
self.checkBox.setChecked(True)
self.checkBox.setObjectName(_fromUtf8("checkBoxON"))
self.checkBox.clicked.connect(self.on_checkBox_clicked) #connection checkBox

self.radioButton = QtGui.QRadioButton(self.centralWidget)
self.radioButton.setGeometry(QtCore.QRect(30, 130, 95, 20))
self.radioButton.setObjectName(_fromUtf8("radioButton"))
self.radioButton.clicked.connect(self.on_radioButton_clicked) #connection radioButton

MainWindow.setCentralWidget(self.centralWidget)

self.menuBar = QtGui.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 26))
self.menuBar.setObjectName(_fromUtf8("menuBar"))
MainWindow.setMenuBar(self.menuBar)

self.mainToolBar = QtGui.QToolBar(MainWindow)
self.mainToolBar.setObjectName(_fromUtf8("mainToolBar"))
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)

self.statusBar = QtGui.QStatusBar(MainWindow)
self.statusBar.setObjectName(_fromUtf8("statusBar"))
MainWindow.setStatusBar(self.statusBar)

self.retranslateUi(MainWindow)

# Affiche un icône sur le bouton PushButton
# self.image_01 = "C:\Program Files\FreeCAD0.13\icone01.png" # adapt the icon name
self.image_01 = path+"icone01.png" # adapt the name of the icon
icon01 = QtGui.QIcon()
icon01.addPixmap(QtGui.QPixmap(self.image_01),QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton.setIcon(icon01)
self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) # This command reverses the direction of the button

# Affiche un icône sur le bouton RadioButton
# self.image_02 = "C:\Program Files\FreeCAD0.13\icone02.png" # adapt the name of the icon
self.image_02 = path+"icone02.png" # adapter le nom de l'icône
icon02 = QtGui.QIcon()
icon02.addPixmap(QtGui.QPixmap(self.image_02),QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.radioButton.setIcon(icon02)
# self.radioButton.setLayoutDirection(QtCore.Qt.RightToLeft) # This command reverses the direction of the button

# Affiche un icône sur le bouton CheckBox
# self.image_03 = "C:\Program Files\FreeCAD0.13\icone03.png" # the name of the icon
self.image_03 = path+"icone03.png" # adapter le nom de l'icône
icon03 = QtGui.QIcon()
icon03.addPixmap(QtGui.QPixmap(self.image_03),QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.checkBox.setIcon(icon03)
# self.checkBox.setLayoutDirection(QtCore.Qt.RightToLeft) # This command reverses the direction of the button


def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "FreeCAD", None))
self.pushButton.setText(_translate("MainWindow", "OK", None))
self.lineEdit.setText(_translate("MainWindow", "tyty", None))
self.checkBox.setText(_translate("MainWindow", "CheckBox", None))
self.radioButton.setText(_translate("MainWindow", "RadioButton", None))

def on_checkBox_clicked(self):
if self.checkBox.checkState()==0:
App.Console.PrintMessage(str(self.checkBox.checkState())+" CheckBox KO\r\n")
else:
App.Console.PrintMessage(str(self.checkBox.checkState())+" CheckBox OK\r\n")
# App.Console.PrintMessage(str(self.lineEdit.setText("tititi"))+" LineEdit\r\n") # write text to the lineEdit window !
# str(self.lineEdit.setText("tititi")) #écrit le texte dans la fenêtre lineEdit
App.Console.PrintMessage(str(self.lineEdit.displayText())+" LineEdit\r\n")

def on_radioButton_clicked(self):
if self.radioButton.isChecked():
App.Console.PrintMessage(str(self.radioButton.isChecked())+" Radio OK\r\n")
else:
App.Console.PrintMessage(str(self.radioButton.isChecked())+" Radio KO\r\n")

def on_lineEdit_clicked(self):
# if self.lineEdit.textChanged():
App.Console.PrintMessage(str(self.lineEdit.displayText())+" LineEdit Display\r\n")

def on_pushButton_clicked(self):
App.Console.PrintMessage("Terminé\r\n")
self.window.hide()

MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow(MainWindow)
MainWindow.show()
</syntaxhighlight>
Here the code to display the icon on the '''pushButton''', change the name for another button, ('''radioButton, checkBox''') and the path to the icon.
<syntaxhighlight>
# Affiche un icône sur le bouton PushButton
# self.image_01 = "C:\Program Files\FreeCAD0.13\icone01.png" # the name of the icon
self.image_01 = path+"icone01.png" # the name of the icon
icon01 = QtGui.QIcon()
icon01.addPixmap(QtGui.QPixmap(self.image_01),QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton.setIcon(icon01)
self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) # This command reverses the direction of the button
</syntaxhighlight>
The command
'''UserAppData''' gives the user path
'''AppHomePath''' gives the installation path of FreeCAD
<syntaxhighlight>
# path = FreeCAD.ConfigGet("UserAppData")
path = FreeCAD.ConfigGet("AppHomePath")
</syntaxhighlight>
This command reverses the horizontal button, right to left.
<syntaxhighlight>
self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) # This command reverses the direction of the button
</syntaxhighlight>

===Method 2===
Another method to display a window, here by creating a file '''QtForm.py''' which contains the header program (module called with '''import QtForm'''), and a second module that contains the code window all these accessories, and your code (the calling module).

This method requires two separate files, but allows to shorten your program using the file ' ' QtForm.py ' ' import. Then distribute the two files together, they are inseparable.

The file '''QtForm.py'''
<syntaxhighlight>

# -*- coding: utf-8 -*-
# Create by flachyjoe
from PyQt4 import QtCore, QtGui

try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s

try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)

class Form(object):
def __init__(self, title, width, height):
self.window = QtGui.QMainWindow()
self.title=title
self.window.setObjectName(_fromUtf8(title))
self.window.setWindowTitle(_translate(self.title, self.title, None))
self.window.resize(width, height)

def show(self):
self.createUI()
self.retranslateUI()
self.window.show()
def setText(self, control, text):
control.setText(_translate(self.title, text, None))
</syntaxhighlight>
The appellant, file that contains the window and your code.

The file my_file.py

The connections are to do, a good exercise.
<syntaxhighlight>

# -*- coding: utf-8 -*-
# Create by flachyjoe
from PyQt4 import QtCore, QtGui
import QtForm

class myForm(QtForm.Form):
def createUI(self):
self.centralWidget = QtGui.QWidget(self.window)
self.window.setCentralWidget(self.centralWidget)
self.pushButton = QtGui.QPushButton(self.centralWidget)
self.pushButton.setGeometry(QtCore.QRect(30, 170, 93, 28))
self.pushButton.clicked.connect(self.on_pushButton_clicked)
self.lineEdit = QtGui.QLineEdit(self.centralWidget)
self.lineEdit.setGeometry(QtCore.QRect(30, 40, 211, 22))
self.checkBox = QtGui.QCheckBox(self.centralWidget)
self.checkBox.setGeometry(QtCore.QRect(30, 90, 81, 20))
self.checkBox.setChecked(True)
self.radioButton = QtGui.QRadioButton(self.centralWidget)
self.radioButton.setGeometry(QtCore.QRect(30, 130, 95, 20))
def retranslateUI(self):
self.setText(self.pushButton, "Fermer")
self.setText(self.lineEdit, "essai de texte")
self.setText(self.checkBox, "CheckBox")
self.setText(self.radioButton, "RadioButton")
def on_pushButton_clicked(self):
self.window.hide()

myWindow=myForm("Fenêtre de test",400,300)
myWindow.show()
</syntaxhighlight>
==Some useful commands==
<syntaxhighlight>
# Here the code to display the icon on the '''pushButton''',
# change the name to another button, ('''radioButton, checkBox''') as well as the path to the icon,

# Displays an icon on the button PushButton
# self.image_01 = "C:\Program Files\FreeCAD0.13\icone01.png" # he name of the icon
self.image_01 = path+"icone01.png" # the name of the icon
icon01 = QtGui.QIcon()
icon01.addPixmap(QtGui.QPixmap(self.image_01),QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton.setIcon(icon01)
self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) # This command reverses the direction of the button


# path = FreeCAD.ConfigGet("UserAppData") # gives the user path
path = FreeCAD.ConfigGet("AppHomePath") # gives the installation path of FreeCAD

# This command reverses the horizontal button, right to left
self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) # This command reverses the horizontal button

# Displays an info button
self.pushButton.setToolTip(_translate("MainWindow", "Quitter la fonction", None)) # Displays an info button

# This function gives a color button
self.pushButton.setStyleSheet("background-color: red") # This function gives a color button

# This function gives a color to the text of the button
self.pushButton.setStyleSheet("color : #ff0000") # This function gives a color to the text of the button

# combinaison des deux, bouton et texte
self.pushButton.setStyleSheet("color : #ff0000; background-color : #0000ff;" ) # combination of the two, button, and text

# replace the icon in the main window
MainWindow.setWindowIcon(QtGui.QIcon('C:\Program Files\FreeCAD0.13\View-C3P.png'))

# connects a lineEdit on execute
self.lineEdit.returnPressed.connect(self.execute) # connects a lineEdit on "def execute" after validation on enter
# self.lineEdit.textChanged.connect(self.execute) # connects a lineEdit on "def execute" with each keystroke on the keyboard

# display text in a lineEdit
self.lineEdit.setText(str(val_X)) # Displays the value in the lineEdit (convert to string)

# extract the string contained in a lineEdit
val_X = self.lineEdit.text() # extract the (string) string contained in lineEdit
val_X = float(val_X0) # converted the string to an floating
val_X = int(val_X0) # convert the string to an integer

# This code allows you to change the font and its attributes
font = QtGui.QFont()
font.setFamily("Times New Roman")
font.setPointSize(10)
font.setWeight(10)
font.setBold(True) # same result with tags "<b>your text</b>" (in quotes)
self.label_6.setFont(font)
self.label_6.setObjectName("label_6")
self.label_6.setStyleSheet("color : #ff0000") # This function gives a color to the text
self.label_6.setText(_translate("MainWindow", "Select a view", None))
</syntaxhighlight>

By using the characters with accents, where you get the error :

Several solutions are possible.

<FONT COLOR="#FF0000">'''UnicodeDecodeError: 'utf8' codec can't decode bytes in position 0-2: invalid data'''</FONT>
<syntaxhighlight>
# conversion from a lineEdit
App.activeDocument().CopyRight.Text = str(unicode(self.lineEdit_20.text() , 'ISO-8859-1').encode('UTF-8'))
DESIGNED_BY = unicode(self.lineEdit_01.text(), 'ISO-8859-1').encode('UTF-8')
</syntaxhighlight>
or with the procedure
<syntaxhighlight>
def utf8(unio):
return unicode(unio).encode('UTF8')
</syntaxhighlight>

<FONT COLOR="#FF0000">'''UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 9: ordinal not in range(128)'''</FONT>

<syntaxhighlight>
# conversion
a = u"Nom de l'élément : "
f.write('''a.encode('iso-8859-1')'''+str(element_)+"\n")
</syntaxhighlight>
or with the procedure
<syntaxhighlight>
def iso8859(encoder):
return unicode(encoder).encode('iso-8859-1')
</syntaxhighlight>
or
<syntaxhighlight>
iso8859(unichr(176))
</syntaxhighlight>
or
<syntaxhighlight>
unichr(ord(176))
</syntaxhighlight>
or
<syntaxhighlight>
uniteSs = "mm"+iso8859(unichr(178))
print unicode(uniteSs, 'iso8859')
</syntaxhighlight>


{{docnav|Line drawing function|Licence}}

[[Category:Poweruser Documentation]]
[[Category:Python Code]]

{{clear}}
<languages/>

Latest revision as of 14:28, 5 April 2024

Einleitung

Auf dieser Seite werden wir zeigen, wie man mit Qt Designer, Qt's offiziellem Werkzeug für die Gestaltung von Oberflächen, eine einfache grafische Oberfläche erstellt; der Dialog wird in Python Code umgewandelt und dann innerhalb von FreeCAD verwendet. Wir gehen davon aus, dass der Benutzer weiß, wie man Python allgemein bearbeitet und ausführt.

In diesem Beispiel ist die gesamte Oberfläche in Python definiert. Obwohl dies bei kleinen Schnittstellen möglich ist, empfiehlt es sich bei größeren Schnittstellen, die erstellten .ui Dateien direkt in das Programm zu laden.

Zwei allgemeine Methoden zur Erstellung von Oberflächen, durch Einfügen der Schnittstelle in die Python Datei oder durch die Verwendung von .ui Dateien.

Gestalten des Dialogs

In CAD Anwendungen ist es sehr wichtig, eine gute UI (Benutzeroberfläche) zu entwerfend. Beinahe alles, das der Benutzer tun wird, wird über ein Teil der Oberfläche passieren: das Lesen von Dialog Kästen, drücken von Knöpfen, die Auswahl zwischen Piktogrammen, usw. Es ist also sehr wichtig, sich genau zu überlegen, was du tun willst, wie der Benutzer sich verhalten soll und wie der Ablauf deiner Aktion aussehen soll.

Ein paar Konzepte sollte man bei der Gestaltung der Oberfläche kennen:

  • Dialoge: Ein modaler Dialog erscheint auf Ihrem Schirm, unterbricht die Handlung des Hauptfensters, zwingt den Benutzer auf den Dialog zu antworten, während ein nichtmodaler Dialog Sie nicht hindert, am Hauptfenster weiter zu arbeiten. In einigen Fällen ist der erste besser, in anderen Fällen nicht.
  • Erkennen, was erforderlich ist und was optional: Stellen Sie sicher, dass der Benutzer weiß, was er tun muss. Etikettieren Sie alles mit der richtigen Beschreibung, verwenden Sie tooltips usw.
  • Die Trennung von Befehlen und Parametern: Das wird gewöhnlich mit Knöpfen und Texteingabefeldern getan. Der Benutzer weiß, dass das Klicken eines Knopfs eine Handlung erzeugen wird, indem er einen Wert innerhalb eines Textfeldes ändert, wird irgendwo ein Parameter geändert. Heutzutage aber wissen Benutzer gewöhnlich gut, was ein Knopf ist, was ein Eingangsfeld usw ist. Das Schnittstellen-Werkzeug Qt, das wir verwenden, ist eines der modernsten Werkzeuge und wir werden uns nicht viel darum sorgen müssen, Dinge verständlich zu machen, da sie bereits in sich selbst sehr klar sein werden.

Nachdem wir gut definiert haben, was wir tun werden, ist es Zeit, den Qt-Designer zu öffnen. Lassen Sie uns einen sehr einfachen Dialog entwerfen:

Wir werden dann mit diesem Dialog in FreeCAD eine schöne rechteckige Ebene erzeugen. Vielleicht finden Sie es nicht sehr nützlich, nette rechteckige Fläche zu produzieren, aber es wird leicht sein, sie später zu ändern, um komplexere Dinge zu tun. Wenn Sie es öffnen, sieht Qt-Designer wie folgt aus:

Erstellen des Dialogs

Qt Designer ist sehr einfach zu benutzen. Auf der linken Leiste hast du Elemente, die auf dein Widget gezogen werden können. Auf der rechten Seite hast du Eigenschaftspaneels, die alle Arten von editierbaren Eigenschaften der ausgewählten Elemente anzeigen. Beginne also mit der Erstellung eines neuen Widgets.

  1. Wähle "Dialog ohne Schaltflächen", da wir die Standardschaltflächen OK/Abbrechen nicht wollen.
  2. Wir brauchen Labels (Beschriftungen). Labels sind einfache Textzeichenfolgen, die auf deinem Widget erscheinen, um den Endbenutzer zu informieren. Wenn du ein Label auswählst, beachte, dass auf der rechten Seite verschiedene Eigenschaften erscheinen, die du ändern kannst, wie z.B.: Schriftstil, Höhe, usw... Lass uns also 3 separate Label auf unser Widget ziehen:
    • Ein Label für den Titel
    • Ein weiteres Label zum Schreiben von "Height"
    • Ein weiteres Label zum Schreiben von "Width"
  3. Wir brauchen jetzt LineEdits (eigentlich 2 davon). Ziehe zwei davon auf das Widget. LineEdits sind Textfelder, die der Endbenutzer ausfüllen kann. Wir brauchen also einen LineEdit für die Höhe und einen für die Breite. Auch hier können wir Eigenschaften bearbeiten. Warum nicht z.B. einen Standardwert setzen, sagen wir z.B.: 1,00 für jedes. Auf diese Weise werden beide Werte bereits ausgefüllt sein, wenn der Benutzer den Dialog sieht. Wenn der Endbenutzer zufrieden ist, kann er direkt auf die Schaltfläche drücken und so wertvolle Zeit sparen.
  4. Als nächstes können wir einen PushButton (Druckknopf) hinzufügen. Dies ist die Schaltfläche, die der Endbenutzer drücken muss, nachdem er beide Felder ausgefüllt hat.

Hinweis: dass wir hier sehr einfache Bedienelemente gewählt haben. Qt hat viel mehr Optionen, zum Beispiel könnte man Spinboxes anstelle von LineEdits verwenden, usw... Schaudir an, was verfügbar ist, erkunde... Du wirst sicher andere Ideen haben.

Das ist soweit alles, was wir im Qt Designer tun müssen. Eine letzte Sache noch: Benennen wir alle unsere Elemente mit einfacheren Namen um, damit sie in unseren Skripten leichter zu identifizieren sind:

Umwandeln unseres Dialogs zu Python

Jetzt werden wir unser Widget irgendwo speichern. Er wird als .ui Datei gespeichert, die wir einfach mittels pyuic zu einem Python Skript wandeln werden. Unter Windows ist das pyuic Programm meistens mit pyqt gebündelt (zu überprüfen), auf Linux weirst du es wahrscheinlich extra mit deinem Paketmanager installieren müssen (auf Debian-basierten Systemen, ist es ein Teil des pyqt4-dev-tools Pakets). Für die Konvertierung musst du ein Terminalfenster (oder ein Fenster der Eingabeaufforderung unter Windows) öffnen, in welchem du zu deiner .ui Datei navigierst und dort dann folgendes eingibst:

pyuic mywidget.ui > mywidget.py

Unter Windows befindet sich pyuic.py in "C:\Python27\Lib\site-Pakete\PyQt4\uic\pyuic.py". Erstelle für die Umwandlung eine Batchdatei mit dem Namen "compQt4.bat":

@"C:\Python27\python" "C:\Python27\Lib\site-packages\PyQt4\uic\pyuic.py" -x %1.ui > %1.py

In der DOS Konsole tippe ohne Erweiterung

compQt4 myUiFile

Unter macOS kannst du mit diesen Befehlen (pip erforderlich) die entsprechende Version von Qt bzw. Pyside ermitteln (die gleiche, die intern in FreeCAD 0.19 benutzt wird)

python3 -m pip install pyqt5
python3 -m pip install pySide2

Dies wird uic im Verzeichnis "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/uic" installieren und Designer in "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/Designer.app". Der Einfachheit halber kannst du einen Link auf uic in /usr/local/bin erstellen, um es einfach per uic -g python ... aufzurufen anstatt den ganzen Pfad zum Programm einzutippen und einen Link zu Designer, um ihn im Mac-Applikationsverzeichnis aufzurufen mit

sudo ln -s /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/uic /usr/local/bin
ln -s /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/Designer.app /Applications

In Linux: Zu Tun

Nachdem sich FreeCAD nach v0.13 zunehmend von PyQt zugunsten von PySide entfernt hat (Wähle deine PySide Installation PySide bauen), um die Datei auf der Basis von PySide zu erstellen, musst du jetzt verwenden:

pyside-uic mywidget.ui -o mywidget.py

Unter Windows ist uic.py in "C:\Python27\Lib\site-packages\PySide\scripts\" zu finden. Zum Erstellen der Batchdatei "compSide.bat":

@"C:\Python27\python" "C:\Python27\Lib\site-packages\PySide\scripts\uic.py" %1.ui > %1.py

In die DOS Konsole tippe ohne Erweiterung

compSide myUiFile

In Linux: Zu Tun

Auf einigen Systemen wird das Programm pyuic4 statt pyuic genannt. Dadurch wird die .ui Datei einfach in ein Python Skript umgewandelt. Wenn wir die Datei mywidget.py öffnen, ist ihr Inhalt sehr leicht zu verstehen:

from PySide import QtCore, QtGui

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(187, 178)
        self.title = QtGui.QLabel(Dialog)
        self.title.setGeometry(QtCore.QRect(10, 10, 271, 16))
        self.title.setObjectName("title")
        self.label_width = QtGui.QLabel(Dialog)
        ...

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

   def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
        self.title.setText(QtGui.QApplication.translate("Dialog", "Plane-O-Matic", None, QtGui.QApplication.UnicodeUTF8))
        ...

Wie du siehst, hat es eine sehr einfache Struktur: eine Klasse genannt Ui_Dialog wird erstellt, die die Elemente der Benutzeroberfläche unseres Widgets speichert. Diese Klasse hat zwei Methoden, eine für die Einrichtung des Widgets und eine für die Übersetzung ihres Inhalts, was ein Teil des allgemeinen Qt Mechanismus für die Übersetzung von Oberflächenelementen ist. Das Einrichtungsverfahren erzeugt einfach eins nach dem anderen die Widgets, wie wir sie in Qt Designer festgelegt haben und setzt ihre Optionen, die wir ebenfalls gewählt haben. Dann wird die ganze Schnittstelle schließlich übersetzt, die Slots werden verbunden (wir werden darüber später sprechen).

Wir können jetzt ein neues Widget erstellen und diese Klasse verwenden, um seine Oberfläche zu schaffen. Wir können bereits unser Widget in Aktion sehen, indem wir unsere mywidget.py Datei an einem Ort speichern, wo FreeCAD es findet (im FreeCAD bin Verzeichnis, oder in einem der Mod Unterverzeichnisse), und dann im FreeCAD Python Interpreter eintippen:

from PySide import QtGui
import mywidget
d = QtGui.QWidget()
d.ui = mywidget.Ui_Dialog()
d.ui.setupUi(d)
d.show()

Und unser Dialog wird erscheinen! Beachte, dass unser Python Interpreter immer noch arbeitet, wir haben einen nichtmodalen Dialog. Um es zu abzuschließen, können wir (abgesehen von einem Klick auf das Schließen Symbol, natürlich) eingeben:

d.hide()

Unseren Dialog etwas tun lassen

Nun, da wir unseren Dialog ein- und ausblenden können, müssen wir nur noch einen letzten Teil hinzufügen: Damit er etwas bewirkt! Wenn du ein wenig mit dem Qt Designer spielst, wirst du schnell einen ganzen Abschnitt namens "Signals and Slots" entdecken. Im Grunde funktioniert es so: Elemente auf deinen Widgets (in der Qt Terminologie sind diese Elemente selbst Widgets) können Signale senden. Diese Signale unterscheiden sich je nach Widget Typ. Zum Beispiel kann eine Taste ein Signal senden, wenn sie gedrückt wird und wenn sie losgelassen wird. Diese Signale können mit Slots verbunden werden, was eine spezielle Funktionalität anderer Widgets sein kann (z.B. hat ein Dialog einen "Schließ" Slot, mit dem du das Signal von einer Schaltfläche zum Schließen verbinden kannst), oder es können benutzerdefinierte Funktionen sein. Die PyQt Referenzdokumentation listet alle qt Widgets auf, was sie können, welche Signale sie senden können, usw..

Was wir hier tun werden, ist eine neue Funktion zu erstellen, die eine Ebene erzeugt, beruhend auf Höhe und Breite, und in Verbindung steht diese Funktion mit dem gedrückt Signal, das unser "Create" Knopf abgibt. Also lass uns mit dem Importieren unserer FreeCAD Module beginnen, durch setzen der folgenden Zeile an den Anfang des Skripts, wo wir bereits QtCore und QtGui importieren:

import FreeCAD, Part

Dann lass uns eine neue Funktion zu unserer Ui_Dialog Klasse hinzufügen:

def createPlane(self):
    try:
        # first we check if valid numbers have been entered
        w = float(self.width.text())
        h = float(self.height.text())
    except ValueError:
        print("Error! Width and Height values must be valid numbers!")
    else:
        # create a face from 4 points
        p1 = FreeCAD.Vector(0,0,0)
        p2 = FreeCAD.Vector(w,0,0)
        p3 = FreeCAD.Vector(w,h,0)
        p4 = FreeCAD.Vector(0,h,0)
        pointslist = [p1,p2,p3,p4,p1]
        mywire = Part.makePolygon(pointslist)
        myface = Part.Face(mywire)
        Part.show(myface)
        self.hide()

Dann müssen wir Qt mitteilen, dass die Schaltfläche mit der Funktion verbunden werden soll, indem wir die folgende Zeile unmittelbar vor QtCore.QMetaObject.connectSlotsByName(Dialog) setzen:

QtCore.QObject.connect(self.create,QtCore.SIGNAL("pressed()"),self.createPlane)

Dies verbindet, wie du siehst, das pressed() Signal unseres create Objekts (die Schaltfläche "Create!") mit einem Slot namens createPlane, den wir gerade definiert haben. Das war's! Als letzten Schliff können wir nun noch eine kleine Funktion zum Erstellen des Dialogs hinzufügen, die einfacher aufzurufen sein wird. Außerhalb der Klasse Ui_Dialog fügen wir diesen Code hinzu:

class plane():
   def __init__(self):
       self.d = QtGui.QWidget()
       self.ui = Ui_Dialog()
       self.ui.setupUi(self.d)
       self.d.show()

(Python Erinnerung: Die __init__ Methode einer Klasse wird automatisch ausgeführt, wenn ein neues Objekt erzeugt wird!) Dann brauchen wir von FreeCAD aus nur folgendes zu tun:

import mywidget
myDialog = mywidget.plane()

Das ist alles, Leute... Jetzt kannst du alle möglichen Dinge ausprobieren, wie z.B. dein Widget in die FreeCAD Oberfläche einzufügen (siehe die Code Schnipsel Seite), oder viel fortgeschrittenere, eigene Werkzeuge zu erstellen, indem du andere Elemente auf deinem Widget verwendest.

Das komplette Skript

Dies ist das vollständige Skript, als Referenz:

# Form implementation generated from reading ui file 'mywidget.ui'
#
# Created: Mon Jun  1 19:09:10 2009
#      by: PyQt4 UI code generator 4.4.4
# Modified for PySide 16:02:2015 
# WARNING! All changes made in this file will be lost!

from PySide import QtCore, QtGui
import FreeCAD, Part 

class Ui_Dialog(object):
   def setupUi(self, Dialog):
       Dialog.setObjectName("Dialog")
       Dialog.resize(187, 178)
       self.title = QtGui.QLabel(Dialog)
       self.title.setGeometry(QtCore.QRect(10, 10, 271, 16))
       self.title.setObjectName("title")
       self.label_width = QtGui.QLabel(Dialog)
       self.label_width.setGeometry(QtCore.QRect(10, 50, 57, 16))
       self.label_width.setObjectName("label_width")
       self.label_height = QtGui.QLabel(Dialog)
       self.label_height.setGeometry(QtCore.QRect(10, 90, 57, 16))
       self.label_height.setObjectName("label_height")
       self.width = QtGui.QLineEdit(Dialog)
       self.width.setGeometry(QtCore.QRect(60, 40, 111, 26))
       self.width.setObjectName("width")
       self.height = QtGui.QLineEdit(Dialog)
       self.height.setGeometry(QtCore.QRect(60, 80, 111, 26))
       self.height.setObjectName("height")
       self.create = QtGui.QPushButton(Dialog)
       self.create.setGeometry(QtCore.QRect(50, 140, 83, 26))
       self.create.setObjectName("create")

       self.retranslateUi(Dialog)
       QtCore.QObject.connect(self.create,QtCore.SIGNAL("pressed()"),self.createPlane)
       QtCore.QMetaObject.connectSlotsByName(Dialog)

   def retranslateUi(self, Dialog):
       Dialog.setWindowTitle("Dialog")
       self.title.setText("Plane-O-Matic")
       self.label_width.setText("Width")
       self.label_height.setText("Height")
       self.create.setText("Create!")
       print("tyty")
   def createPlane(self):
       try:
           # first we check if valid numbers have been entered
           w = float(self.width.text())
           h = float(self.height.text())
       except ValueError:
           print("Error! Width and Height values must be valid numbers!")
       else:
           # create a face from 4 points
           p1 = FreeCAD.Vector(0,0,0)
           p2 = FreeCAD.Vector(w,0,0)
           p3 = FreeCAD.Vector(w,h,0)
           p4 = FreeCAD.Vector(0,h,0)
           pointslist = [p1,p2,p3,p4,p1]
           mywire = Part.makePolygon(pointslist)
           myface = Part.Face(mywire)
           Part.show(myface)

class plane():
  def __init__(self):
      self.d = QtGui.QWidget()
      self.ui = Ui_Dialog()
      self.ui.setupUi(self.d)
      self.d.show()

Weitere Beispiele

Relevante Verweise