Dialog creation/it: Difference between revisions

From FreeCAD Documentation
(Created page with "=== Progettare la finestra === Nelle applicazioni CAD, il disegno di una buona UI (interfaccia utente) è molto importante. L'utente esegue quasi tutte le operazioni tramite ...")
No edit summary
 
(162 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<languages/>
In questa pagina vi mostreremo come creare una semplice finestra di dialogo di Qt con [http://qt-project.org/doc/qt-4.8/designer-manual.html Qt Designer], lo strumento ufficiale di Qt per la progettazione di interfacce, quindi come convertirla in codice Python e poi utilizzarla all'interno di FreeCAD.


{{Docnav/it
Nell'esempio si assume che sappiate già come modificare ed eseguire gli script di Python, e sappiate anche fare delle cose semplici in una finestra di terminale, come navigare, ecc.
|[[Interface_creation/it|Creare l'interfaccia]]
|[[License/it|Licenza]]
}}


{{TOCright}}
Naturalmente, è necessario che PyQt sia installato.


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

In questa pagina mostreremo come creare una semplice interfaccia grafica con [http://qt-project.org/doc/qt-4.8/designer-manual.html Qt Designer], lo strumento ufficiale di Qt per la progettazione di interfacce; la finestra di dialogo verrà convertita in codice [[Python/it|Python]], quindi verrà utilizzata all'interno di FreeCAD. Si assume che l'utente sappia come scrivere ed eseguire [[Python/it|Python]] in generale.

In questo esempio, l'intera interfaccia è definita in [[Python/it|Python]]. Anche se questo è possibile per interfacce piccole, per interfacce più grandi la raccomandazione è di caricare i file {{FileName|.ui}} creati direttamente nel programma.

[[File:FreeCAD_creating_interfaces.svg|600px]]
{{Caption|Due metodi generali per creare delle interfacce, includendo l'interfaccia nel file Python o usando i file {{incode|.ui}}.}}

<span id="Designing_the_dialog"></span>
=== Progettare la finestra ===
=== Progettare la finestra ===


Nelle applicazioni CAD, il disegno di una buona UI (interfaccia utente) è molto importante. L'utente esegue quasi tutte le operazioni tramite qualche componente dell'interfaccia: legge le finestre di dialogo, preme i pulsanti, sceglie tra le icone, ecc. Quindi è molto importante pensare attentamente a ciò che si intende fare, a come si desidera che l'utente si comporti, e a quale sarà il flusso di lavoro delle proprie azioni.
Nelle applicazioni CAD, il disegno di una buona UI (interfaccia utente) è molto importante. L'utente esegue quasi tutte le operazioni tramite qualche componente dell'interfaccia: legge le finestre di dialogo, preme i pulsanti, sceglie tra le icone, ecc. Quindi è molto importante pensare attentamente a ciò che si intende fare, a come si desidera che l'utente si comporti, e a quale sarà il flusso di lavoro delle proprie azioni.


Quando si progetta l'interfaccia, è bene tenere presenti alcune cose:
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.
* 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.
* 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.


* [https://it.wikipedia.org/wiki/Finestra_modale Finestre di dialogo modali e non-modali]: una finestra di dialogo modale appare sullo schermo in primo piano, blocca l'azione della finestra principale e costringe l'utente a rispondere alla finestra di dialogo, mentre un dialogo non-modale permette di continuare a lavorare sulla finestra principale. In alcuni casi è meglio usare la prima soluzione, in altri casi no.
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:
* Identificare ciò che è necessario e ciò che è facoltativo. Accertarsi che l'utente sappia esattamente quello che deve fare. Etichettare tutto con una descrizione adeguata, realizzare dei suggerimenti per l'uso degli strumenti, ecc.
* Separare i comandi dei parametri. Questo solitamente si ottiene con pulsanti e campi per inserire i testi. L'utente sa che cliccando su un pulsante si produce una azione, e che, invece, sostituendo un valore all'interno di un campo di testo si modifica un parametro da qualche parte. In genere, oggi gli utenti sanno bene che cosa è un pulsante, che cosa è un campo di input, ecc. Il toolkit Qt, che stiamo per usare, è il più avanzato strumento di costruzione di interfacce. Non dovrete preoccuparvi molto di fare le cose chiare, dal momento che sarà già esso stesso molto chiaro.

Ora che abbiamo definito con precisione quello che faremo, è il momento di aprire Qt Designer.

Disegneremo una finestra di dialogo molto semplice, simile a questa:


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


Dopo utilizzeremo questa finestra di dialogo all'interno di FreeCAD per produrre un bel piano rettangolare. Forse pensate che produrre dei bei piani rettangolari non è particolarmente utile, però, in un secondo tempo, sarà facile apportarvi delle modifiche e creare delle cose più complesse.
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:

Quando viene aperto, Qt Designer ha questo aspetto:


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


== Creazione della finestra di dialogo ==
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 è molto semplice da usare. Sulla barra di sinistra ci sono gli elementi, che possono essere trascinati sul tuo widget. Sul lato destro ci sono i pannelli delle proprietà, che mostrano tutti i tipi di proprietà modificabili degli elementi selezionati. Quindi, si inizia con la creazione di un nuovo widget.
# Selezionare "Dialogo senza pulsanti", poiché non vogliamo i pulsanti {{KEY|OK}}/{{KEY|Annulla}} predefiniti.
# Avremo bisogno di '''Etichette'''. Le etichette sono semplici stringhe di testo che appaiono sul tuo widget per informare l'utente finale. Quando si seleziona un'etichetta, si nota che sul lato destro appariranno diverse proprietà, che puoi modificare, come: stile del carattere, altezza, ecc... Quindi trasciniamo 3 etichette separate sul nostro widget:
#* Un'etichetta per il titolo
#* Un'altra etichetta per scrivere "'''Altezza'''"
#* E un'altra etichetta per scrivere "'''Larghezza'''"
# Ora abbiamo bisogno di LineEdit (2 di loro in realtà). Trascinane due sul widget. '''LineEdits''' sono campi di testo, che l'utente finale può compilare. Quindi abbiamo bisogno di un LineEdit per ''Height'' e uno per ''Width''. Anche qui possiamo modificare le proprietà. Ad esempio, perché non impostare un valore predefinito? Diciamo ad esempio: 1,00 per ciascuno. In questo modo, quando l'utente vedrà la finestra di dialogo, entrambi i valori saranno già compilati. Se l'utente finale è soddisfatto, può premere direttamente il pulsante, risparmiando tempo prezioso.
# Poi aggiungiamo un '''PushButton'''. Questo è il pulsante che l'utente finale dovrà premere dopo aver compilato entrambi i campi.


Notare che qui si sono scelti dei comandi molto semplici, ma Qt dispone di molte altre opzioni, ad esempio, è possibile utilizzare '''Spinboxes''' invece di '''LineEdits''', ecc .. Date un'occhiata a ciò che è disponibile, esplorate, vi verranno sicuramente altre idee.
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.


Questo è tutto quello che si deve fare in Qt Designer.
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.
Un'ultima cosa, però, rinominare tutti i propri elementi con nomi più adeguati, così negli script sarà più facile identificarli:

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 ==
=== Convertire il dialogo in Python ===

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:
Ora, salviamo il nostro widget da qualche parte. Esso verrà salvato come un file .ui, che potremo facilmente convertire in script di Python tramite pyuic. Su Windows, il programma pyuic è incluso con PyQt (da verificare), su Linux probabilmente è necessario installarlo separatamente tramite il proprio gestore di pacchetti (su sistemi debian-based è parte del pacchetto di strumenti PyQt4-dev-tools). Per fare la conversione, è necessario aprire una finestra di terminale (o una finestra del prompt dei comandi in Windows), portarsi nella cartella in cui si è salvato il file .ui, e digitare:
{{Code|code=
<syntaxhighlight>
pyuic mywidget.ui > mywidget.py
pyuic mywidget.ui > mywidget.py
}}
</syntaxhighlight>
In Windows pyuic.py si trova in "C:\Python27\Lib\site-packages\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:
Per creare il file batch "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
}}
Nella console Dos digitare senza estensione
{{Code|code=
compQt4 myUiFile
}}

In macOS, puoi recuperare la versione appropriata (la stessa utilizzata internamente in FreeCAD 0.19) di QT e Pyside con questi comandi (pip richiesto)
{{Code|code=
python3 -m pip install pyqt5
python3 -m pip install pySide2
}}
Questo installerà uic nella cartella "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/uic" e Designer in "/Library/Frameworks/Python.framework/Versions /3.7/lib/python3.7/site-packages/PySide2/Designer.app".
Per comodità puoi creare un link di uic in /usr/local/bin per poterlo richiamare semplicemente con uic -g python... invece di digitare tutto il percorso del programma, e un link a Designer per recuperarlo in la cartella Applicazioni del mac con
{{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 : da fare

Dato che, dopo la versione 0.13, FreeCAD si è progressivamente allontanato da PyQt a favore di [http://qt-project.org/wiki/PySide PySide] (Choice your PySide install [http://pyside.readthedocs.org/en/latest/building/ building PySide]), per costruire il file basato su PySide ora è necessario utilizzare:

{{Code|code=
pyside-uic mywidget.ui -o mywidget.py
}}
In Windows uic.py si trova in "C:\Python27\Lib\site-packages\PySide\scripts\uic.py"
Per creare il file batch "compSide.bat":
{{Code|code=
@"C:\Python27\python" "C:\Python27\Lib\site-packages\PySide\scripts\uic.py" %1.ui > %1.py
}}
Nella console Dos digitare senza estensione
{{Code|code=
compSide myUiFile
}}
In Linux : da fare

Su alcuni sistemi il programma si chiama pyuic4 invece di pyuic. Questa operazione converte semplicemente il file .ui in uno script Python. Se apriamo il file mywidget.py, è molto facile capire il suo contenuto:
{{Code|code=
from PySide import QtCore, QtGui


class Ui_Dialog(object):
class Ui_Dialog(object):
Line 59: Line 125:
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>
Come potete vedere ha una struttura molto semplice: viene creata una classe denominata Ui_Dialog, che memorizza gli elementi dell'interfaccia del nostro widget. Questa classe ha due metodi, uno per la creazione del widget, e uno per la traduzione del suo contenuto, questo fa parte del meccanismo generale di Qt per tradurre gli elementi dell'interfaccia. Il metodo di installazione crea semplicemente, uno per uno, i widget come li abbiamo definiti in Qt Designer, e imposta le loro opzioni come abbiamo deciso in precedenza. Poi, viene tradotta l'intera interfaccia e, infine, vengono collegati gli slot (di cui parleremo più avanti).
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:
Ora possiamo creare un nuovo widget, e utilizzare questa classe per creare la sua interfaccia. A questo punto, possiamo già vedere in azione il nostro widget e, per provarlo, mettiamo il nostro file mywidget.py in un luogo dove FreeCAD possa trovarlo (nella directory bin di FreeCAD, o in una qualsiasi delle sottodirectory Mod), e, nell'interprete Python di FreeCAD, digitiamo:
{{Code|code=
<syntaxhighlight>
from PyQt4 import QtGui
from PySide import QtGui
import mywidget
import mywidget
d = QtGui.QWidget()
d = QtGui.QWidget()
Line 70: Line 136:
d.ui.setupUi(d)
d.ui.setupUi(d)
d.show()
d.show()
}}
</syntaxhighlight>
Ecco apparire la nostra finestra di dialogo! Notare che il nostro interprete Python sta ancora funzionando in quanto stiamo usando un dialogo non-modale. Per chiudere la finestra, (ovviamente, oltre a cliccare sulla sua icona di chiusura) possiamo digitare:
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...


=== Utilizzare la finestra di dialogo ===
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:

<syntaxhighlight>
Ora che siamo in grado di mostrare e nascondere la nostra finestra di dialogo, basta solo aggiungere una ultima parte: per fargli fare qualcosa!

Provando per un po' Qt Designer, scoprirete presto un'intera sezione chiamata "signals and slots" (segnali e porte di ingresso dei segnali).

In pratica, funziona così: i componenti dei widget (nella terminologia Qt, questi elementi sono a loro volta dei widget) sono in grado di inviare dei segnali. Tali segnali differiscono a seconda del tipo widget. Ad esempio, un pulsante può inviare un segnale quando viene premuto e quando viene rilasciato. Questi segnali possono essere collegati agli slot. Gli slot possono essere una funzionalità speciale di altri widget (ad esempio una finestra di dialogo ha uno slot "close" al quale è possibile collegare il segnale di un pulsante di chiusura), o possono essere funzioni personalizzate.

La [http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/classes.html Documentazione di riferimento di PyQt] elenca tutti i widget Qt, che cosa possono fare, quali segnali possono inviare, ecc..

Qui, come esempio, creiamo una nuova funzione che genera un piano basato su altezza e larghezza, e colleghiamo tale funzione al segnale "pressed" (premuto) emesso dal pulsante "Create!".

Allora, cominciamo con l'importazione dei nostri moduli FreeCAD, inserendo la seguente riga all'inizio dello script, dove importiamo già QtCore e QtGui:
{{Code|code=
import FreeCAD, Part
import FreeCAD, Part
}}
</syntaxhighlight>
Then, let's add a new function to our Ui_Dialog class:
Dopo, aggiungiamo una nuova funzione alla nostra classe Ui_Dialog:
{{Code|code=
<syntaxhighlight>
def createPlane(self):
def createPlane(self):
try:
try:
Line 90: Line 166:
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 102: Line 178:
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):
Poi, bisogna dire a Qt di collegare il pulsante alla funzione, inserendo la seguente riga appena prima di QtCore.QMetaObject.connectSlotsByName (Dialog):
{{Code|code=
<syntaxhighlight>
QtCore.QObject.connect(self.create,QtCore.SIGNAL("pressed()"),self.createPlane)
QtCore.QObject.connect(self.create,QtCore.SIGNAL("pressed()"),self.createPlane)
}}
</syntaxhighlight>
Questo, come vedete, collega il segnale "pressed()" del nostro oggetto create (il pulsante "Create!"), allo slot chiamato createPlane, che abbiamo appena definito. Questo è tutto!
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:

<syntaxhighlight>
Ora, come tocco finale, possiamo aggiungere una piccola funzione per creare il dialogo, così sarà più facile chiamarlo.

Fuori dalla classe Ui_Dialog, aggiungiamo questo codice:
{{Code|code=
class plane():
class plane():
def __init__(self):
def __init__(self):
Line 115: Line 195:
self.ui.setupUi(self.d)
self.ui.setupUi(self.d)
self.d.show()
self.d.show()
}}
</syntaxhighlight>
(Promemoria di Python: ogni volta che viene creato un nuovo oggetto il metodo __init__ di una classe viene eseguito automaticamente!)
(Python reminder: the __init__ method of a class is automatically executed whenever a new object is created!)

Then, from FreeCAD, we only need to do:
Poi, da FreeCAD, basta solo fare:
<syntaxhighlight>
{{Code|code=
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.
Questo è tutto amici ... Ora è possibile provare diverse cose, come ad esempio inserire il widget nell'interfaccia di FreeCAD (vedere la pagina [[Code snippets/it|Modelli di codici]]), oppure creare strumenti personalizzati molto più avanzati, utilizzando altri elementi nel proprio widget.


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


Questo è lo script completo di riferimento:
{{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 167: Line 247:


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 179: Line 259:
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 192: Line 272:


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
==Altri esempi ==


* [[Dialog_creation_with_various_widgets/it|Creare una finestra di dialogo con diversi widget]] con {{incode|QPushButton}}, {{incode|QLineEdit}}, {{incode|QCheckBox}}, {{incode|QRadioButton}} e altri.
try:
* [[Dialog_creation_reading_and_writing_files/it|Creare una finestra di dialogo per leggere e scrivere file]] con {{incode|QFileDialog}}.
_fromUtf8 = QtCore.QString.fromUtf8
* [[Dialog_creation_setting_colors/it|Creare una finestra di dialogo per la regolazione del colore]] con {{incode|QColorDialog}}.
except AttributeError:
* [[Dialog_creation_image_and_animated_GIF/it|Creare una finestra di dialogo per immagini e GIF animate]] con {{incode|QLabel}} e {{incode|QMovie}}.
def _fromUtf8(s):
* [[PySide_usage_snippets/it|Frammenti di codice per l'utilizzo di PySide]].
return s
* [[Qt_Example/it|Esempio di Qt]]


<span id="Relevant_links"></span>
try:
== Link utili ==
_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/it|Manuale:Creare interfacce di strumenti]]


class Ui_MainWindow(object):


{{Docnav/it
def __init__(self, MainWindow):
|[[Interface_creation/it|Creare l'interfaccia]]
self.window = MainWindow
|[[License/it|Licenza]]
}}


{{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 07:10, 7 April 2024

Introduzione

In questa pagina mostreremo come creare una semplice interfaccia grafica con Qt Designer, lo strumento ufficiale di Qt per la progettazione di interfacce; la finestra di dialogo verrà convertita in codice Python, quindi verrà utilizzata all'interno di FreeCAD. Si assume che l'utente sappia come scrivere ed eseguire Python in generale.

In questo esempio, l'intera interfaccia è definita in Python. Anche se questo è possibile per interfacce piccole, per interfacce più grandi la raccomandazione è di caricare i file .ui creati direttamente nel programma.

Due metodi generali per creare delle interfacce, includendo l'interfaccia nel file Python o usando i file .ui.

Progettare la finestra

Nelle applicazioni CAD, il disegno di una buona UI (interfaccia utente) è molto importante. L'utente esegue quasi tutte le operazioni tramite qualche componente dell'interfaccia: legge le finestre di dialogo, preme i pulsanti, sceglie tra le icone, ecc. Quindi è molto importante pensare attentamente a ciò che si intende fare, a come si desidera che l'utente si comporti, e a quale sarà il flusso di lavoro delle proprie azioni.

Quando si progetta l'interfaccia, è bene tenere presenti alcune cose:

  • Finestre di dialogo modali e non-modali: una finestra di dialogo modale appare sullo schermo in primo piano, blocca l'azione della finestra principale e costringe l'utente a rispondere alla finestra di dialogo, mentre un dialogo non-modale permette di continuare a lavorare sulla finestra principale. In alcuni casi è meglio usare la prima soluzione, in altri casi no.
  • Identificare ciò che è necessario e ciò che è facoltativo. Accertarsi che l'utente sappia esattamente quello che deve fare. Etichettare tutto con una descrizione adeguata, realizzare dei suggerimenti per l'uso degli strumenti, ecc.
  • Separare i comandi dei parametri. Questo solitamente si ottiene con pulsanti e campi per inserire i testi. L'utente sa che cliccando su un pulsante si produce una azione, e che, invece, sostituendo un valore all'interno di un campo di testo si modifica un parametro da qualche parte. In genere, oggi gli utenti sanno bene che cosa è un pulsante, che cosa è un campo di input, ecc. Il toolkit Qt, che stiamo per usare, è il più avanzato strumento di costruzione di interfacce. Non dovrete preoccuparvi molto di fare le cose chiare, dal momento che sarà già esso stesso molto chiaro.

Ora che abbiamo definito con precisione quello che faremo, è il momento di aprire Qt Designer.

Disegneremo una finestra di dialogo molto semplice, simile a questa:

Dopo utilizzeremo questa finestra di dialogo all'interno di FreeCAD per produrre un bel piano rettangolare. Forse pensate che produrre dei bei piani rettangolari non è particolarmente utile, però, in un secondo tempo, sarà facile apportarvi delle modifiche e creare delle cose più complesse.

Quando viene aperto, Qt Designer ha questo aspetto:

Qt Designer è molto semplice da usare. Sulla barra di sinistra ci sono gli elementi, che possono essere trascinati sul tuo widget. Sul lato destro ci sono i pannelli delle proprietà, che mostrano tutti i tipi di proprietà modificabili degli elementi selezionati. Quindi, si inizia con la creazione di un nuovo widget.

  1. Selezionare "Dialogo senza pulsanti", poiché non vogliamo i pulsanti OK/Annulla predefiniti.
  2. Avremo bisogno di Etichette. Le etichette sono semplici stringhe di testo che appaiono sul tuo widget per informare l'utente finale. Quando si seleziona un'etichetta, si nota che sul lato destro appariranno diverse proprietà, che puoi modificare, come: stile del carattere, altezza, ecc... Quindi trasciniamo 3 etichette separate sul nostro widget:
    • Un'etichetta per il titolo
    • Un'altra etichetta per scrivere "Altezza"
    • E un'altra etichetta per scrivere "Larghezza"
  3. Ora abbiamo bisogno di LineEdit (2 di loro in realtà). Trascinane due sul widget. LineEdits sono campi di testo, che l'utente finale può compilare. Quindi abbiamo bisogno di un LineEdit per Height e uno per Width. Anche qui possiamo modificare le proprietà. Ad esempio, perché non impostare un valore predefinito? Diciamo ad esempio: 1,00 per ciascuno. In questo modo, quando l'utente vedrà la finestra di dialogo, entrambi i valori saranno già compilati. Se l'utente finale è soddisfatto, può premere direttamente il pulsante, risparmiando tempo prezioso.
  4. Poi aggiungiamo un PushButton. Questo è il pulsante che l'utente finale dovrà premere dopo aver compilato entrambi i campi.

Notare che qui si sono scelti dei comandi molto semplici, ma Qt dispone di molte altre opzioni, ad esempio, è possibile utilizzare Spinboxes invece di LineEdits, ecc .. Date un'occhiata a ciò che è disponibile, esplorate, vi verranno sicuramente altre idee.

Questo è tutto quello che si deve fare in Qt Designer. Un'ultima cosa, però, rinominare tutti i propri elementi con nomi più adeguati, così negli script sarà più facile identificarli:

Convertire il dialogo in Python

Ora, salviamo il nostro widget da qualche parte. Esso verrà salvato come un file .ui, che potremo facilmente convertire in script di Python tramite pyuic. Su Windows, il programma pyuic è incluso con PyQt (da verificare), su Linux probabilmente è necessario installarlo separatamente tramite il proprio gestore di pacchetti (su sistemi debian-based è parte del pacchetto di strumenti PyQt4-dev-tools). Per fare la conversione, è necessario aprire una finestra di terminale (o una finestra del prompt dei comandi in Windows), portarsi nella cartella in cui si è salvato il file .ui, e digitare:

pyuic mywidget.ui > mywidget.py

In Windows pyuic.py si trova in "C:\Python27\Lib\site-packages\PyQt4\uic\pyuic.py" Per creare il file batch "compQt4.bat:

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

Nella console Dos digitare senza estensione

compQt4 myUiFile

In macOS, puoi recuperare la versione appropriata (la stessa utilizzata internamente in FreeCAD 0.19) di QT e Pyside con questi comandi (pip richiesto)

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

Questo installerà uic nella cartella "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PySide2/uic" e Designer in "/Library/Frameworks/Python.framework/Versions /3.7/lib/python3.7/site-packages/PySide2/Designer.app". Per comodità puoi creare un link di uic in /usr/local/bin per poterlo richiamare semplicemente con uic -g python... invece di digitare tutto il percorso del programma, e un link a Designer per recuperarlo in la cartella Applicazioni del mac con

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 : da fare

Dato che, dopo la versione 0.13, FreeCAD si è progressivamente allontanato da PyQt a favore di PySide (Choice your PySide install building PySide), per costruire il file basato su PySide ora è necessario utilizzare:

pyside-uic mywidget.ui -o mywidget.py

In Windows uic.py si trova in "C:\Python27\Lib\site-packages\PySide\scripts\uic.py" Per creare il file batch "compSide.bat":

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

Nella console Dos digitare senza estensione

compSide myUiFile

In Linux : da fare

Su alcuni sistemi il programma si chiama pyuic4 invece di pyuic. Questa operazione converte semplicemente il file .ui in uno script Python. Se apriamo il file mywidget.py, è molto facile capire il suo contenuto:

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))
        ...

Come potete vedere ha una struttura molto semplice: viene creata una classe denominata Ui_Dialog, che memorizza gli elementi dell'interfaccia del nostro widget. Questa classe ha due metodi, uno per la creazione del widget, e uno per la traduzione del suo contenuto, questo fa parte del meccanismo generale di Qt per tradurre gli elementi dell'interfaccia. Il metodo di installazione crea semplicemente, uno per uno, i widget come li abbiamo definiti in Qt Designer, e imposta le loro opzioni come abbiamo deciso in precedenza. Poi, viene tradotta l'intera interfaccia e, infine, vengono collegati gli slot (di cui parleremo più avanti).

Ora possiamo creare un nuovo widget, e utilizzare questa classe per creare la sua interfaccia. A questo punto, possiamo già vedere in azione il nostro widget e, per provarlo, mettiamo il nostro file mywidget.py in un luogo dove FreeCAD possa trovarlo (nella directory bin di FreeCAD, o in una qualsiasi delle sottodirectory Mod), e, nell'interprete Python di FreeCAD, digitiamo:

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

Ecco apparire la nostra finestra di dialogo! Notare che il nostro interprete Python sta ancora funzionando in quanto stiamo usando un dialogo non-modale. Per chiudere la finestra, (ovviamente, oltre a cliccare sulla sua icona di chiusura) possiamo digitare:

d.hide()

Ora che siamo in grado di mostrare e nascondere la nostra finestra di dialogo, basta solo aggiungere una ultima parte: per fargli fare qualcosa!

Provando per un po' Qt Designer, scoprirete presto un'intera sezione chiamata "signals and slots" (segnali e porte di ingresso dei segnali).

In pratica, funziona così: i componenti dei widget (nella terminologia Qt, questi elementi sono a loro volta dei widget) sono in grado di inviare dei segnali. Tali segnali differiscono a seconda del tipo widget. Ad esempio, un pulsante può inviare un segnale quando viene premuto e quando viene rilasciato. Questi segnali possono essere collegati agli slot. Gli slot possono essere una funzionalità speciale di altri widget (ad esempio una finestra di dialogo ha uno slot "close" al quale è possibile collegare il segnale di un pulsante di chiusura), o possono essere funzioni personalizzate.

La Documentazione di riferimento di PyQt elenca tutti i widget Qt, che cosa possono fare, quali segnali possono inviare, ecc..

Qui, come esempio, creiamo una nuova funzione che genera un piano basato su altezza e larghezza, e colleghiamo tale funzione al segnale "pressed" (premuto) emesso dal pulsante "Create!".

Allora, cominciamo con l'importazione dei nostri moduli FreeCAD, inserendo la seguente riga all'inizio dello script, dove importiamo già QtCore e QtGui:

import FreeCAD, Part

Dopo, aggiungiamo una nuova funzione alla nostra classe Ui_Dialog:

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()

Poi, bisogna dire a Qt di collegare il pulsante alla funzione, inserendo la seguente riga appena prima di QtCore.QMetaObject.connectSlotsByName (Dialog):

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

Questo, come vedete, collega il segnale "pressed()" del nostro oggetto create (il pulsante "Create!"), allo slot chiamato createPlane, che abbiamo appena definito. Questo è tutto!

Ora, come tocco finale, possiamo aggiungere una piccola funzione per creare il dialogo, così sarà più facile chiamarlo.

Fuori dalla classe Ui_Dialog, aggiungiamo questo codice:

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

(Promemoria di Python: ogni volta che viene creato un nuovo oggetto il metodo __init__ di una classe viene eseguito automaticamente!)

Poi, da FreeCAD, basta solo fare:

import mywidget
myDialog = mywidget.plane()

Questo è tutto amici ... Ora è possibile provare diverse cose, come ad esempio inserire il widget nell'interfaccia di FreeCAD (vedere la pagina Modelli di codici), oppure creare strumenti personalizzati molto più avanzati, utilizzando altri elementi nel proprio widget.

Lo script completo

Questo è lo script completo di riferimento:

# 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()

Altri esempi

Link utili