PySide Intermediate Examples/fr: Difference between revisions

From FreeCAD Documentation
(Created page with "=== Déclaration d'importation === La déclaration d'importation obligatoire")
No edit summary
 
(83 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<languages/>
<languages/>
==Introduction==

{{TOCright}}
{{TOCright}}

==Introduction==


Cette page couvre des exemples de niveau moyen du gestionnaire d'interface graphique [[PySide/fr|PySide]] (les pages d'accompagnement couvrent des aspects à la fois moins ou plus avancés, [[PySide_Beginner_Examples/fr|Exemples PySide pour débutants]] et [[PySide_Advanced_Examples/fr|Exemples PySide pour confirmés]]). Dans cette page, un exemple de programme est utilisé pour couvrir les différents sujets PySide. L'intention est de présenter du code PySide fonctionnel afin que toute personne ayant besoin d'utiliser PySide puisse copier la section correspondante, la modifier et l'adapter à ses propres fins.
Cette page couvre des exemples de niveau moyen du gestionnaire d'interface graphique [[PySide/fr|PySide]] (les pages d'accompagnement couvrent des aspects à la fois moins ou plus avancés, [[PySide_Beginner_Examples/fr|Exemples PySide pour débutants]] et [[PySide_Advanced_Examples/fr|Exemples PySide pour confirmés]]). Dans cette page, un exemple de programme est utilisé pour couvrir les différents sujets PySide. L'intention est de présenter du code PySide fonctionnel afin que toute personne ayant besoin d'utiliser PySide puisse copier la section correspondante, la modifier et l'adapter à ses propres fins.


<span id="Notes"></span>
===Remarques===
===Remarques===


Line 14: Line 15:
* Il convient de garder à l'esprit que PySide fonctionne avec des chaînes lorsqu'il s'agit de la saisie de l'utilisateur, ce qui apparaît à l'écran sous forme de nombre est en fait une représentation textuelle d'un nombre
* Il convient de garder à l'esprit que PySide fonctionne avec des chaînes lorsqu'il s'agit de la saisie de l'utilisateur, ce qui apparaît à l'écran sous forme de nombre est en fait une représentation textuelle d'un nombre


<span id="Code_Based_Discussion_-_Declarative_Portion"></span>
== Discussion basée sur le code - Partie déclarative ==
== Discussion basée sur le code - Partie déclarative ==


Line 21: Line 23:


[[File:PySideScreenSnapshot3.jpg]]
[[File:PySideScreenSnapshot3.jpg]]



La majeure partie du reste de cette section décrira le contenu de la définition de classe qui apparaît à la fin de cette section. Nous couvrirons d'abord les éléments déclaratifs qui définissent comment les choses fonctionnent et comment l'interface graphique est assemblée, puis nous couvrirons les sections opérationnelles (c'est-à-dire le code qui s'exécutera lorsque les interactions de l'utilisateur se produiront). Cette fenêtre est basée sur la classe QDialog et est donc modale - ce qui signifie qu'aucune activité ne peut être effectuée en dehors de la fenêtre lorsqu'elle est ouverte.
La majeure partie du reste de cette section décrira le contenu de la définition de classe qui apparaît à la fin de cette section. Nous couvrirons d'abord les éléments déclaratifs qui définissent comment les choses fonctionnent et comment l'interface graphique est assemblée, puis nous couvrirons les sections opérationnelles (c'est-à-dire le code qui s'exécutera lorsque les interactions de l'utilisateur se produiront). Cette fenêtre est basée sur la classe QDialog et est donc modale - ce qui signifie qu'aucune activité ne peut être effectuée en dehors de la fenêtre lorsqu'elle est ouverte.


<span id="Import_Statement"></span>
=== Déclaration d'importation ===
=== Déclaration d'importation ===

La déclaration d'importation obligatoire
La déclaration d'importation obligatoire

{{Code|code=
{{Code|code=
from PySide import QtGui, QtCore
from PySide import QtGui, QtCore
}}
}}
This is best placed at the top of the Python file.


Il est préférable de le placer en haut du fichier Python.
===Class Definition===

<span id="Class_Definition"></span>
=== Définition de classe ===

{{Code|code=
{{Code|code=
class ExampleModalGuiClass(QtGui.QDialog):
class ExampleModalGuiClass(QtGui.QDialog):
Line 41: Line 48:
def initUI(self):
def initUI(self):
}}
}}
This code is best copied out verbatim and altered. The gist of the code is that we are sub-classing the QDialog Class of PySide. In adapting this code you will want to change the class name "ExampleModalGuiClass" - make sure to change it in both locations (e.g. lines 1 & 4).


Ce code est mieux copié textuellement et modifié. L'essentiel du code est que nous sous-classons la classe QDialog de PySide. En adaptant ce code, vous voudrez changer le nom de la classe "ExampleModalGuiClass" - assurez-vous de le changer dans les deux endroits (par exemple les lignes 1 et 4).
===Window Return Status===

<span id="Window_Return_Status"></span>
=== État de retour de la fenêtre ===

{{Code|code=
{{Code|code=
self.result = userCancelled
self.result = userCancelled
}}
}}
This is not mandatory but rather a good programming practice, this sets a default return status for the window which will be there regardless of what the user does. Later in the code this may be changed by the Python code to indicate different options the user may have selected.


Ce n'est pas obligatoire mais plutôt une bonne pratique de programmation, cela définit un statut de retour par défaut pour la fenêtre qui sera là indépendamment de ce que fait l'utilisateur. Plus tard dans le code, cela peut être modifié par le code Python pour indiquer les différentes options que l'utilisateur peut avoir sélectionnées.
===Window Creation===

<span id="Window_Creation"></span>
=== Création de fenêtres ===

{{Code|code=
{{Code|code=
# create our window
# create our window
Line 57: Line 70:
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
}}
}}
Remembering that screen dimensions are measured from the upper-left corner, on the 3rd line the values refer to:
* the number of pixels the upper-left corner will be to the right of the lefthand screen edge (250)
* the number of pixels the upper-left corner will be below the upper screen edge (250)
* the width of the screen in pixels (400)
* the height of the screen in pixels (350)
The title of the window is set and the final line simply means that this window will never be obscured by another window - if this is not desired then simply place a Python comment character ('#') as the first character of the line.


En vous rappelant que les dimensions de l'écran sont mesurées à partir du coin supérieur gauche, sur la 3ème ligne, les valeurs se réfèrent à:
===Label Creation===
* le nombre de pixels dans le coin supérieur gauche sera à droite du bord gauche de l'écran (250)
* le nombre de pixels dans le coin supérieur gauche sera sous le bord supérieur de l'écran (250)
* la largeur de l'écran en pixels (400)
* la hauteur de l'écran en pixels (350)
Le titre de la fenêtre est défini et la dernière ligne signifie simplement que cette fenêtre ne sera jamais obscurcie par une autre fenêtre - si cela n'est pas souhaité, placez simplement un caractère de commentaire Python ('#') comme premier caractère de la ligne.

<span id="Label_Creation"></span>
=== Création d'étiquettes ===

{{Code|code=
{{Code|code=
# create some Labels
# create some Labels
Line 78: Line 94:
self.label4.move(20, 170)
self.label4.move(20, 170)
}}
}}
In PySide labels serve two purposes, static labels (as the name implies) as well as read-only (i.e. display-only) text fields. So unchanging instructions to the user such as "Don't push the red button" as well as dynamic calculation results such as "42" can be communicated to the user. The 2nd line declares a Label and sets it's initial value (which is blank in this case). The 3rd line specifies the font, any font (on the system) can be specified, if not specified the default font is used. In this case the font is specified as a non-proportional one. The label is moved to it's location in the window - it's coordinates specify it's position with respect to the window (not the screen).


Dans PySide, les étiquettes ont deux objectifs: les étiquettes statiques (comme leur nom l'indique) et les champs de texte en lecture seule (c'est-à-dire en affichage uniquement). Ainsi, des instructions inchangées à l'utilisateur telles que "Don't push the red button" ainsi que des résultats de calcul dynamiques tels que "42" peuvent être communiquées à l'utilisateur. La 2ème ligne déclare une étiquette et définit sa valeur initiale (qui est vide dans ce cas). La troisième ligne spécifie la police, n'importe quelle police (sur le système) peut être spécifiée, si elle n'est pas spécifiée, la police par défaut est utilisée. Dans ce cas, la police est spécifiée comme non proportionnelle. L'étiquette est déplacée vers son emplacement dans la fenêtre - ses coordonnées spécifient sa position par rapport à la fenêtre (pas à l'écran).
===Checkbox Creation===

<span id="Checkbox_Creation"></span>
=== Création de cases à cocher ===

{{Code|code=
{{Code|code=
# checkboxes
# checkboxes
Line 92: Line 111:
self.checkbox2.move(210,30)
self.checkbox2.move(210,30)
}}
}}
Checkboxes can be off and on in any combination (unlike radio buttons). Line 2 declares one and set's it initial Value. Line 3 specifies which method will be executed when the Checkbox is clicked (in this case the method 'onCheckBox1'). If the 4th line did not have the Python comment character ('#') as the first character, then it would be executed and it would mark the checkbox as checked. Finally the 5th line moves the Checkbox into position.


Les cases à cocher peuvent être désactivées et activées dans n'importe quelle combinaison (contrairement aux boutons radio). La ligne 2 en déclare un et définit sa valeur initiale. La ligne 3 spécifie quelle méthode sera exécutée lorsque la case à cocher est cliquée (dans ce cas, la méthode 'onCheckBox1'). Si la 4ème ligne n'avait pas le caractère de commentaire Python ('#') comme premier caractère, alors elle serait exécutée et il marquerait la case comme cochée. Enfin, la 5ème ligne place la case à cocher en position.
===Radio Button Creation===

<span id="Radio_Button_Creation"></span>
=== Création de boutons radio ===

{{Code|code=
{{Code|code=
# radio buttons
# radio buttons
Line 105: Line 127:
self.radioButton2.move(210,80)
self.radioButton2.move(210,80)
}}
}}
The creation of the Radio BUttons is very similar to the Checkboxes. The only difference really is the behaviour of the Radio Buttons in that only one of them can be 'on' at a time.


La création des boutons radio est très similaire aux cases à cocher. La seule différence est vraiment le comportement des boutons radio en ce qu'un seul d'entre eux peut être 'activé' à la fois.
===Pop-Up Menu Creation===

<span id="Pop-Up_Menu_Creation"></span>
=== Création d'un menu contextuel ===

{{Code|code=
{{Code|code=
# set up lists for pop-ups
# set up lists for pop-ups
Line 118: Line 143:
self.popup1.move(210, 115)
self.popup1.move(210, 115)
}}
}}
In line 2 a list is built up of what will be the user choices. An alternative is to build up a Dictionary but only use the Keys for the list of menu choices. Line 4 creates the pop-up menu (known as a ComboBox to PySide), the user options are added in line 5.


À la ligne 2, une liste est constituée de ce que seront les choix de l'utilisateur. Une alternative consiste à créer un dictionnaire mais à n'utiliser les touches que pour la liste des choix de menu. La ligne 4 crée le menu contextuel (appelé ComboBox vers PySide), les options utilisateur sont ajoutées à la ligne 5.
As a side note, if the Dictionary was used then the lines would appear as:

Si le Dictionnaire était utilisé, les lignes apparaîtraient comme suit :

{{Code|code=
{{Code|code=
self.popupItems1 = OrderedDict([("2","widget"),("pink","foobar"),("4","galopsis")])
self.popupItems1 = OrderedDict([("2","widget"),("pink","foobar"),("4","galopsis")])
Line 126: Line 153:
self.popup1.addItems(self.popupItems1.keys())
self.popup1.addItems(self.popupItems1.keys())
}}
}}
Returning to the main code sample for this section, line 6 sets the default choice, this line may be omitted, also the value of the default choice could be loaded into the corresponding Label (once again if appropriate). And finally the move into position at line 8.


Revenant à l'exemple de code principal de cette section, la ligne 6 définit le choix par défaut, cette ligne peut être omise, la valeur du choix par défaut peut également être chargée dans l'étiquette correspondante (encore une fois si nécessaire). Et enfin le passage en position à la ligne 8.
===Button Creation Part 1===

<span id="Button_Creation_Part_1"></span>
=== Création de bouton, partie 1 ===

{{Code|code=
{{Code|code=
# toggle visibility button
# toggle visibility button
Line 136: Line 166:
pushButton1.move(210, 165)
pushButton1.move(210, 165)
}}
}}
The button is created in line 2 with it's name, the handler for it's signal when clicked is specified in line 3. Line 4 is there to prevent the button from becoming the 'default button' - the button that will be clicked if the user simply presses the {{KEY|Return}} key. And a move to position finished up the code segment.


Le bouton est créé à la ligne 2 avec son nom, le gestionnaire de son signal lorsque l'utilisateur clique est spécifié à la ligne 3. La ligne 4 est là pour empêcher le bouton de devenir le 'bouton par défaut' - le bouton sur lequel l'utilisateur clique simplement si l'utilisateur appuie sur la touche {{KEY|Valider}}. Et un passage à la position a terminé le segment de code.
===Text Input Creation===

<span id="Button_Creation_Part_2"></span>
=== Création de bouton, partie 2 ===

{{Code|code=
# cancel button
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.setAutoDefault(True)
cancelButton.move(150, 280)
# OK button
okButton = QtGui.QPushButton('OK', self)
okButton.clicked.connect(self.onOk)
okButton.move(260, 280)
}}

Les deux boutons sont créés avec un nom (qui apparaîtra comme leur étiquette), associé à une méthode qui s'exécutera quand ils seront cliqués et déplacés en position. La seule exception est la ligne 4 qui spécifie le bouton 'Annuler' comme bouton par défaut - cela signifie qu'il sera "cliqué" si l'utilisateur appuie sur la touche {{KEY|Valider}}.

<span id="Text_Input_Creation"></span>
=== Création d'une entrée de texte ===

{{Code|code=
{{Code|code=
# text input field
# text input field
Line 146: Line 196:
self.textInput.move(20, 220)
self.textInput.move(20, 220)
}}
}}
The QLineEdit widget is probably the most common for user textual input, in this example the code section after this one will set up a contextual menu to operate on it. This code section creates (line 2), sets an initial value (line 3), sets a width to the field (line 4) and moves the widget into place (line 5).


Le widget QLineEdit est probablement le plus courant pour la saisie textuelle de l'utilisateur, dans cet exemple, la section de code après celle-ci mettra en place un menu contextuel pour l'utiliser. Cette section de code crée (ligne 2), définit une valeur initiale (ligne 3), définit une largeur pour le champ (ligne 4) et met le widget en place (ligne 5).
===Contextual Menu Creation===

<span id="QuantitySpinBox_Creation"></span>
===Création de QuantitySpinBox===

{{Code|code=
# QuantitySpinBox
from FreeCAD import Units
ui = FreeCADGui.UiLoader()
quantityInput = ui.createWidget("Gui::QuantitySpinBox")
self.quantityInput.setProperty( 'minimum', 0.0)
potential = 2.87
unit = "V"
# only set the value
self.quantityInput.setProperty('rawValue', potential )
# set quantity (value + unit)
quantity = Units.Quantity("{} {}".format(potential , unit))
self.quantityInput.setProperty('value', quantity)
# read value from the spinbox
quantity = self.quantityInput.property('value')
}}

Le widget Gui::QuantitySpinBox est un spécial FreeCAD, conçu pour afficher et manipuler des valeurs avec leurs [[Expressions/fr#Unit.C3.A9s|unités]]. Il est dérivé de la classe [https://doc.qt.io/qt-5/qabstractspinbox.html QAbstractSpinBox] de Qt. Pour toutes ses propriétés, voir la liste dans le fichier de code source [https://github.com/FreeCAD/FreeCAD/blob/master/src/Gui/QuantitySpinBox.h#L42 QuantitySpinBox.h].

<span id="Contextual_Menu_Creation"></span>
=== Création du menu contextuel ===

{{Code|code=
{{Code|code=
# set contextual menu options for text editing widget
# set contextual menu options for text editing widget
Line 174: Line 249:
self.textInput.addAction(popMenuAction3)
self.textInput.addAction(popMenuAction3)
}}
}}
This code has numerous repetitions as the same action is performed with different values - this is part of what makes GUI code so lengthy (no matter what the system). First a QAction is created - it is a pairing (or linkage) of the text that the user will see as their selectable option along with the method that will execute if they select that option. It is basically a pairing of a user choice with a piece of code. Line 3 creates it, line 4 defines the user option (as they will see it) and line 5 specifies which piece of Python code will execute.


Ce code a de nombreuses répétitions car la même action est effectuée avec des valeurs différentes - cela fait partie de ce qui rend le code GUI si long (quel que soit le système). Tout d'abord, une QAction est créée - c'est un appariement (ou un lien) du texte que l'utilisateur verra comme son option sélectionnable avec la méthode qui s'exécutera s'il sélectionne cette option. Il s'agit essentiellement d'un couplage d'un choix d'utilisateur avec un morceau de code. La ligne 3 le crée, la ligne 4 définit l'option utilisateur (comme ils le verront) et la ligne 5 spécifie quel morceau de code Python sera exécuté.
Skipping to line 19 (the line with "self.textInput.setContextMenuPolicy") a ActionsContextMenu is created which is holder for all the separate QAction linkages between user choice and code to execute. Each widget can only have a single Contextual Menu (i.e. the menu associated with the right-click) so line 19 defines that menu. The following 4 lines add the linkages created at the beginning of this code section. Order is significant here, the user will see the menu options in the order they are added. Notice that the 3rd menu option is really a bit of nothing, it's code is null but it serves to separate 2 groups of options on the Contextual Menu.

En sautant à la ligne 19 (la ligne avec "self.textInput.setContextMenuPolicy"), un ActionsContextMenu est créé, qui contient tous les liens QAction séparés entre le choix de l'utilisateur et le code à exécuter. Chaque widget ne peut avoir qu'un seul menu contextuel (c'est-à-dire le menu associé au clic droit) donc la ligne 19 définit ce menu. Les 4 lignes suivantes ajoutent les liens créés au début de cette section de code. L'ordre est important ici, l'utilisateur verra les options du menu dans l'ordre dans lequel elles sont ajoutées. Notez que la 3ème option de menu est vraiment un peu rien, son code est nul mais il sert à séparer 2 groupes d'options sur le menu contextuel.

<span id="Numeric_Input_Creation"></span>
=== Création d'une entrée numérique ===


===Numeric Input Creation===
{{Code|code=
{{Code|code=
# numeric input field
# numeric input field
Line 187: Line 265:
self.numericInput.move(250, 220)
self.numericInput.move(250, 220)
}}
}}
The creation of the field for numeric input really follows that for Text Input earlier. In fact the code is identical with exception of the 3rd and 4th lines. The 3rd line sets the Mask as defined by PySide, which in this case specifies up to 3 digits (which may include 0). A full list of the InputMask codes can be found at [http://doc.qt.io/qt-5/qlineedit.html#inputMask-prop QLineEdit InputMask]


La création du champ pour la saisie numérique suit vraiment celle de la saisie de texte plus tôt. En fait le code est identique à l'exception des 3ème et 4ème lignes. La troisième ligne définit le masque tel que défini par PySide, qui dans ce cas spécifie jusqu'à 3 chiffres (qui peuvent inclure 0). Une liste complète des codes InputMask peut être trouvée sur [http://doc.qt.io/qt-5/qlineedit.html#inputMask-prop QLineEdit InputMask]
===Button Creation Part 2===

{{Code|code=
<span id="Window_Display"></span>
# cancel button
=== Affichage de la fenêtre ===
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.setAutoDefault(True)
cancelButton.move(150, 280)
# OK button
okButton = QtGui.QPushButton('OK', self)
okButton.clicked.connect(self.onOk)
okButton.move(260, 280)
}}
Both buttons are created with a name (which will appear as their label), associated with a method which will execute when they are clicked, and moved into position. The one exception is line 4 which specifies the 'Cancel' button as the default button - that means it will be "clicked" if the user preses the {{KEY|Return}} key.


===Window Display===
{{Code|code=
{{Code|code=
# now make the window visible
# now make the window visible
self.show()
self.show()
}}
}}
There is only one line and it causes the GUI to be displayed after the setup.


Il n'y a qu'une seule ligne et cela provoque l'affichage de l'interface graphique après l'installation.
==Code Based Discussion - Operative Portion==


<span id="Code_Based_Discussion_-_Operative_Portion"></span>
We now move onto the operative portion of the GUI definition which is the code that executes in response to user interactions with the GUI. The order of statement groups is not very relevant - with the caveat that something must be declared before it can be referenced. Some people put all the handlers of a certain type (e.g. handlers for buttons) in one group, others list the handlers alphabetically. For specific application there may be a problem related reason that all handlers relating to a specific aspect be gathered together
== Discussion basée sur le code - Partie opérative ==


Nous passons maintenant à la partie opérationnelle de la définition de l'interface graphique qui est le code qui s'exécute en réponse aux interactions de l'utilisateur avec l'interface graphique. L'ordre des groupes d'instructions n'est pas très pertinent - avec la mise en garde que quelque chose doit être déclaré avant de pouvoir être référencé. Certaines personnes mettent tous les gestionnaires d'un certain type (par exemple, les gestionnaires de boutons) dans un groupe, d'autres répertorient les gestionnaires par ordre alphabétique. Pour une application spécifique, il peut y avoir une raison liée au problème que tous les gestionnaires relatifs à un aspect spécifique soient rassemblés
There is a high degree of similarity between the handlers. Most do not receive a parameter, the fact they are executing is realy the only parameter (or signal) they get. Others like "onPopup1" and "mousePressEvent" accept a parameter.


Il existe un degré élevé de similitude entre les gestionnaires. La plupart ne reçoivent pas de paramètre, le fait qu'ils s'exécutent est vraiment le seul paramètre (ou signal) qu'ils reçoivent. D'autres comme "onPopup1" et "mousePressEvent" acceptent un paramètre.
There must be a one to one correspondance between the handlers specified in the declarative section and the handler declared in this, the operative section. There may be extra handlers declared which are never invoked but there may not be any missing.


Il doit y avoir une correspondance un à un entre les gestionnaires spécifiés dans la section déclarative et le gestionnaire déclaré dans cette section opérationnelle. Il peut y avoir des gestionnaires supplémentaires déclarés qui ne sont jamais appelés, mais il se peut qu'il n'y en ait aucun manquant.
===Generic Handler===


<span id="Generic_Handler"></span>
In this code example, generic handlers handle the following events:
=== Gestionnaire générique ===

Dans cet exemple de code, les gestionnaires génériques gèrent les événements suivants:


* onCheckbox1
* onCheckbox1
Line 234: Line 304:
* onOk
* onOk


La forme générale des gestionnaires est:
The general form for the handlers is:

{{Code|code=
{{Code|code=
def handlerName(self):
def handlerName(self):
Line 240: Line 311:
lineOfCode2
lineOfCode2
}}
}}
The first line has the keyword "def" followed by the handler name. The handler name must match the name from the earlier declarative section exactly. The parameter "self" is part of the standard syntax as are the enclosing parenthesis and the final colon character. Once the first line is finished then there are no requirements of the following code, it is purely application specific.


La première ligne contient le mot-clé "def" suivi du nom du gestionnaire. Le nom du gestionnaire doit correspondre exactement au nom de la section déclarative précédente. Le paramètre "self" fait partie de la syntaxe standard, tout comme la parenthèse englobante et le caractère deux-points final. Une fois la première ligne terminée, il n'y a aucune exigence du code suivant, il est purement spécifique à l'application.
===Pop-Up Menu Handler===

<span id="Pop-Up_Menu_Handler"></span>
=== Gestionnaire de menu contextuel ===

{{Code|code=
{{Code|code=
def onPopup1(self, selectedText):
def onPopup1(self, selectedText):
}}
}}
The Pop-Up menu handler is the same as the generic handler with exception that a second parameter, the text selected by the user, is passed in. Remember that everything is text coming from the Pop-Up menu and even if the user has selected the number 3, it will be passed in as the string "3".


Le gestionnaire du menu contextuel est le même que le gestionnaire générique, à l'exception du fait qu'un second paramètre, le texte sélectionné par l'utilisateur, est transmis. Rappelez-vous que tout est du texte provenant du menu Pop-Up et que même si l'utilisateur a sélectionné le chiffre 3, il sera transmis comme la chaîne "3".
===Mouse Event Handler===

<span id="Mouse_Event_Handler"></span>
=== Gestionnaire d'événements de la souris ===

{{Code|code=
{{Code|code=
def mousePressEvent(self, event):
def mousePressEvent(self, event):
# print mouse position, X & Y
# print mouse position, X & Y
print "X = ", event.pos().x()
print("X = ", event.pos().x())
print "Y = ", event.pos().y()
print("Y = ", event.pos().y())
#
#
if event.button() == QtCore.Qt.LeftButton:
if event.button() == QtCore.Qt.LeftButton:
print "left mouse button"
print("left mouse button")
if self.label1.underMouse():
if self.label1.underMouse():
print "over the text '"+self.label1.text()+"'"
print("over the text '"+self.label1.text()+"'")
}}
}}
The Mouse Event handler is the same as the generic handler with exception that a second parameter, the mouse event (e.g. left-click, right-click) from the user is passed in. The name of the handler, "mousePressEvent", is reserved and if it is changed then the handler will no longer receive the event from the mouse presses.


Le gestionnaire d'événements de la souris est le même que le gestionnaire générique à l'exception du fait qu'un deuxième paramètre, l'événement de la souris (par exemple, clic gauche, clic droit) de l'utilisateur est transmis. Le nom du gestionnaire, "mousePressEvent", est réservé et s'il est modifié, le gestionnaire ne recevra plus l'événement des pressions de la souris.
The X and Y coordinates of the mouse press are given by the reference "event.pos().x()" and "event.pos().y()". The constants "QtCore.Qt.LeftButton" and "QtCore.Qt.RightButton" are used to determine which mouse button was pressed.


Les coordonnées X et Y de la pression de la souris sont données par la référence "event.pos().x()" et "event.pos().y()". Les constantes "QtCore.Qt.LeftButton" et "QtCore.Qt.RightButton" sont utilisées pour déterminer quel bouton de la souris est enfoncé.
A reference to a widget can be made of the form "self.widgetName.underMouse()" which will return {{TRUE}} or {{FALSE}} as to whether the mouse cursor is over the widget "widgetName". Although presented in the same code excerpt the "underMouse()" handler is not tied to the "mousePressEvent" handler and can be used at any time.


Une référence à un widget peut être faite de la forme "self.widgetName.underMouse()" qui retournera {{TRUE}} ou {{FALSE}} pour savoir si le curseur de la souris est sur le widget "widgetName". Bien que présenté dans le même extrait de code, le gestionnaire "underMouse()" n'est pas lié au gestionnaire "mousePressEvent" et peut être utilisé à tout moment.
==Code Based Discussion - Main Routine==

<span id="Code_Based_Discussion_-_Main_Routine"></span>
== Discussion basée sur le code - Routine principale ==

La plupart du volume de code est dans la définition de la classe GUI, il n'y a pas grand-chose dans la procédure principale.


Most of the volume of code is in the GUI Class definition, there is not much in the main procedure.
{{Code|code=
{{Code|code=
# Constant definitions
# Constant definitions
global userCancelled, userOK
global userCancelled, userOK
userCancelled = "Cancelled"
userCancelled = "Cancelled"
userOK = "OK"
userOK = "OK"
}}
}}

Lines 2,3 & 4 deal with coordinating the status of the user interaction with the GUI - e.g. Cancelled, OK, or any other application defined status. The handler routines On Cancel and OnOk earlier also set these statuses.
Les lignes 2, 3 et 4 traitent de la coordination de l'état de l'interaction de l'utilisateur avec l'interface graphique - par ex. Annulé, OK ou tout autre statut défini par l'application. Les routines de gestionnaire On Cancel et OnOk précédemment définissent également ces statuts.

{{Code|code=
{{Code|code=
form = ExampleGuiClass()
form = ExampleGuiClass()
Line 289: Line 371:
localVariable4 = form.label4.text()
localVariable4 = form.label4.text()
}}
}}
Lines 1 and 2 show the method for invoking the GUI. There may be multiple GUI definitions for a program and also the GUI need not be invoked as the first thing in the Python file, it may be invoked at any point. The Name of the GUI Class is specified in line 1 ("ExampleGuiClass" in this case) but the rest of the 2 lines are to be copied verbatim.


Les lignes 1 et 2 montrent la méthode pour appeler l'interface graphique. Il peut y avoir plusieurs définitions d'interface graphique pour un programme et l'interface graphique n'a pas besoin d'être invoquée en premier lieu dans le fichier Python, elle peut être appelée à tout moment. Le nom de la classe GUI est spécifié à la ligne 1 ("ExampleGuiClass" dans ce cas) mais le reste des 2 lignes doit être copié textuellement.
Lines 4 and 6 use the result field to determine the appropriate action. The last 4 lines simply show the copying of the data in the GUI object to variables local to the executing main procedure.


Les lignes 4 et 6 utilisent le champ de résultat pour déterminer l'action appropriée. Les 4 dernières lignes montrent simplement la copie des données de l'objet GUI vers des variables locales de la procédure principale en cours d'exécution.
==Complete Modal Code Example==

<span id="Complete_Modal_Code_Example"></span>
== Exemple de code modal complet ==

Voici l'exemple complet de code (développé sur FreeCAD v0.14):


This is the complete code example (developed on FreeCAD v0.14):
{{Code|code=
{{Code|code=
# import statements
# import statements
Line 448: Line 533:
def mousePressEvent(self, event):
def mousePressEvent(self, event):
# print mouse position, X & Y
# print mouse position, X & Y
print "X = ", event.pos().x()
print("X = ", event.pos().x())
print "Y = ", event.pos().y()
print("Y = ", event.pos().y())
#
#
if event.button() == QtCore.Qt.LeftButton:
if event.button() == QtCore.Qt.LeftButton:
print "left mouse button"
print("left mouse button")
if self.label1.underMouse():
if self.label1.underMouse():
print "over the text '"+self.label1.text()+"'"
print("over the text '"+self.label1.text()+"'")
if self.label2.underMouse():
if self.label2.underMouse():
print "over the text '"+self.label2.text()+"'"
print("over the text '"+self.label2.text()+"'")
if self.label3.underMouse():
if self.label3.underMouse():
print "over the text '"+self.label3.text()+"'"
print("over the text '"+self.label3.text()+"'")
if self.label4.underMouse():
if self.label4.underMouse():
print "over the text '"+self.label4.text()+"'"
print("over the text '"+self.label4.text()+"'")
if self.textInput.underMouse():
if self.textInput.underMouse():
print "over the text '"+self.textInput.text()+"'"
print("over the text '"+self.textInput.text()+"'")
if event.button() == QtCore.Qt.RightButton:
if event.button() == QtCore.Qt.RightButton:
print "right mouse button"
print("right mouse button")

# Class definitions
# Class definitions


Line 471: Line 555:


# Constant definitions
# Constant definitions
userCancelled = "Cancelled"
userCancelled = "Cancelled"
userOK = "OK"
userOK = "OK"


# code ***********************************************************************************
# code ***********************************************************************************
Line 487: Line 571:
localVariable3 = form.label3.text()
localVariable3 = form.label3.text()
localVariable4 = form.label4.text()
localVariable4 = form.label4.text()
print localVariable1
print localVariable2
print localVariable3
print localVariable4
#
#
#OS: Mac OS X
#OS: Mac OS X
Line 504: Line 584:
#
#
}}
}}
The best way to use this code is to copy it into an editor or FreeCAD macro file and play around with it.


La meilleure façon d'utiliser ce code est de le copier dans un éditeur ou un fichier macro FreeCAD et de jouer avec.
==Code Based Discussion - Nonmodal Code Example==


<span id="Code_Based_Discussion_-_Nonmodal_Code_Example"></span>
All of the widget specific from the previous modal example transfer to use in a nonmodal window. The main difference is that the nonmodal window does not restrict the user from interacting with other windows. Basically, a nonmodal window is one that can be opened and left open for as long as needed without it placing any restrictions on other application windows. There are a small number of code differences between the two which will be highlighted, consequently this code example is quite brief. Anything that is the same as the previous modal example will be left out in the interests of keeping this overview brief. This is the nonmodal GUI screen the PySide Class generates:
== Discussion basée sur le code - Exemple de code non modal ==

Tous les widgets spécifiques de l'exemple modal précédent sont transférés à utiliser dans une fenêtre non modale. La principale différence est que la fenêtre non modale n'empêche pas l'utilisateur d'interagir avec d'autres fenêtres. Fondamentalement, une fenêtre non modale est une fenêtre qui peut être ouverte et laissée ouverte aussi longtemps que nécessaire sans imposer de restrictions sur les autres fenêtres d'application. Il existe un petit nombre de différences de code entre les deux qui seront mises en évidence, par conséquent, cet exemple de code est assez bref. Tout ce qui est identique à l'exemple modal précédent sera omis dans l'intérêt de garder cette vue d'ensemble brève. Voici l'écran GUI non modal généré par la classe PySide:


[[File:PySideScreenSnapshot4.jpg]]
[[File:PySideScreenSnapshot4.jpg]]


<span id="Import_Statement"></span>
===Import Statement===
=== Déclaration d'importation ===
The mandatory Import statement

La déclaration d'importation obligatoire

{{Code|code=
{{Code|code=
from PySide import QtGui, QtCore
from PySide import QtGui, QtCore
}}
}}
This is best placed at the top of the Python file.


Il est préférable de le placer en haut du fichier Python.
===Class Definition===

<span id="Class_Definition"></span>
=== Définition de classe ===

{{Code|code=
{{Code|code=
class ExampleNonmodalGuiClass(QtGui.QMainWindow):
class ExampleNonmodalGuiClass(QtGui.QMainWindow):
Line 528: Line 616:
def initUI(self):
def initUI(self):
}}
}}
This code is best copied out verbatim and altered. The gist of the code is that we are sub-classing the QMainWindow Class of PySide. In adapting this code you will want to change the class name "ExampleNonmodalGuiClass" - make sure to change it in both locations (e.g. lines 1 & 4).


Ce code est mieux copié textuellement et modifié. L'essentiel du code est que nous sous-classons la classe QMainWindow de PySide. En adaptant ce code, vous voudrez changer le nom de la classe "ExampleModalGuiClass" - assurez-vous de le changer dans les deux endroits (par exemple les lignes 1 et 4).
===Window Creation===

<span id="Window_Creation"></span>
=== Création de fenêtres ===

{{Code|code=
{{Code|code=
# create our window
# create our window
Line 539: Line 630:
self.setMouseTracking(True)
self.setMouseTracking(True)
}}
}}
Obviously our window dimensions and title are different. The main point to note is the last line which lets PySide know that it is to send out mouse position events as they happen. Note that these events will not be sent out when the mouse is over a widget like a button as the widget will capture the events.


Évidemment, les dimensions et le titre de nos fenêtres sont différents. Le point principal à noter est la dernière ligne qui permet à PySide de savoir qu'il doit envoyer les événements de position de la souris au fur et à mesure qu'ils se produisent. Notez que ces événements ne seront pas envoyés lorsque la souris sera sur un widget comme un bouton car le widget capturera les événements.
===Mouse Move Event Handler===

<span id="Mouse_Move_Event_Handler"></span>
=== Gestionnaire d'événements de déplacement de la souris ===

{{Code|code=
{{Code|code=
def mouseMoveEvent(self,event):
def mouseMoveEvent(self,event):
self.label6.setText("X: "+str(event.x()) + " Y: "+str(event.y()))
self.label6.setText("X: "+str(event.x()) + " Y: "+str(event.y()))
}}
}}
This handler receives the event of a Mouse Move and displays the formatted form of it. Test what happens when it is over widgets or outside of the window.


Ce gestionnaire reçoit l'événement d'un déplacement de la souris et en affiche la forme formatée. Testez ce qui se passe lorsqu'il s'agit de widgets ou en dehors de la fenêtre.
===Invoking the Window===

<span id="Invoking_the_Window"></span>
===Appeler la fenêtre===

{{Code|code=
{{Code|code=
form = ExampleNonmodalGuiClass()
form = ExampleNonmodalGuiClass()
}}
}}
Invoking the window is another area of difference from the previous example. This time only 1 line is needed for invoking the GUI.


L'appel de la fenêtre est un autre domaine de différence par rapport à l'exemple précédent. Cette fois, une seule ligne est nécessaire pour appeler l'interface graphique.
==Complete Nonmodal Code Example==

<span id="Complete_Nonmodal_Code_Example"></span>
== Exemple de code non modal complet ==

{{Code|code=
{{Code|code=
from PySide import QtGui, QtCore
from PySide import QtGui, QtCore
Line 636: Line 736:
}}
}}


<span id="Misc_Additional_Topics"></span>
==Misc Additional Topics==
== Divers sujets supplémentaires ==


There are 3 concepts to the screen real estate in a GUI environment:
Il existe 3 concepts à l'écran immobilier dans un environnement GUI:
* espace physique sur l'écran
* physical space on the screen
* frame
* cadre
* géométrie
* geometry
Dans le logiciel, tous sont mesurés en pixels. PySide a la fonction de mesurer dans des unités du monde réel, mais celles-ci ne sont pas fiables car les fabricants n'ont pas de norme pour la taille des pixels ou le rapport hauteur/largeur.
Within the software all are measured in pixels. PySide has function to measure in real world units but these are undependable as the manufacturers have no standard for pixel size or aspect ratio.


Le cadre a la taille d'une fenêtre, y compris ses barres latérales, la barre supérieure (éventuellement avec un menu) et la barre inférieure. La géométrie est l'espace situé à l'intérieur du cadre et est donc toujours inférieure ou égale au cadre. À son tour, le cadre est toujours inférieur ou égal à la taille d'écran disponible.
The Frame is the size of a window including it's side bars, top bar (possibly with a menu in it) and bottom bar. The Geometry is the space lying within the Frame and so is always less than or equal to the Frame. In turn the Frame is always less than or equal to the available screen size.

<span id="Available_Screen_Size"></span>
=== Taille d'écran disponible ===


===Available Screen Size===
{{Code|code=
{{Code|code=
# get screen dimensions (Available Screen Size)
# get screen dimensions (Available Screen Size)
Line 655: Line 758:
availableHeight = QtGui.QDesktopWidget().availableGeometry().height()
availableHeight = QtGui.QDesktopWidget().availableGeometry().height()
}}
}}
Generally the "availableHeight" should be less than the "screenHeight" by the height of the menu bar. These 4 values are based on the hardware environment and will change from computer to computer. They are not dependent on any application window size.


En général, "availableHeight" doit être inférieur à "screenHeight" de la hauteur de la barre de menus. Ces 4 valeurs sont basées sur l'environnement matériel et changeront d'un ordinateur à l'autre. Ils ne dépendent d'aucune taille de fenêtre d'application.
===Frame Size and Geometry===

(Depuis Python 3.9, cet avertissement apparaît lorsque le code ci-dessus est exécuté: '''DeprecationWarning: QDesktopWidget.screenGeometry(int screen) const is deprecated'''. Un remplacement semble être nécessaire à partir de Python 3.10.)

<span id="Frame_Size_and_Geometry"></span>
=== Taille et géométrie du cadre ===

{{Code|code=
{{Code|code=
# set up a variable to hold the Main Window to save some typing...
# set up a variable to hold the Main Window to save some typing...
Line 684: Line 792:
mainWin.frameGeometry()
mainWin.frameGeometry()
}}
}}

These same commands can be executed on a user generated window, the syntax does not change.
Ces mêmes commandes peuvent être exécutées sur une fenêtre générée par l'utilisateur, la syntaxe ne change pas.



{{Powerdocnavi{{#translation:}}}}
{{Powerdocnavi{{#translation:}}}}
[[Category:Developer Documentation{{#translation:}}]]
[[Category:Developer Documentation{{#translation:}}]]
[[Category:Python Code{{#translation:}}]]
[[Category:Python Code{{#translation:}}]]
{{clear}}

Latest revision as of 10:49, 18 February 2023

Other languages:

Introduction

Cette page couvre des exemples de niveau moyen du gestionnaire d'interface graphique PySide (les pages d'accompagnement couvrent des aspects à la fois moins ou plus avancés, Exemples PySide pour débutants et Exemples PySide pour confirmés). Dans cette page, un exemple de programme est utilisé pour couvrir les différents sujets PySide. L'intention est de présenter du code PySide fonctionnel afin que toute personne ayant besoin d'utiliser PySide puisse copier la section correspondante, la modifier et l'adapter à ses propres fins.

Remarques

  • Cette page n'est pas destinée à couvrir le langage Python ou à servir d'instructions en Python.
  • Les noms des variables ne sont pas descriptifs mais ont été conservés dans l'ordre pour mieux organiser les explications
  • Il existe de nombreuses conventions de dénomination pour les composants de l'interface graphique, dont aucune n'est «bonne» ou «mauvaise»
  • Il existe une variété de séquences différentes des déclarations pour les widgets, les signaux, les méthodes, encore une fois, aucune n'est "bonne" ou "mauvaise"
  • Il convient de garder à l'esprit que PySide fonctionne avec des chaînes lorsqu'il s'agit de la saisie de l'utilisateur, ce qui apparaît à l'écran sous forme de nombre est en fait une représentation textuelle d'un nombre

Discussion basée sur le code - Partie déclarative

Le "programme d'exemple" est en fait une grande définition de classe, la définition d'une classe PySide GUI et a plus de 150 lignes de code (y compris les commentaires). Il n'y a pas de but fonctionnel pour la classe ou son comportement, le seul but est de démontrer les actions possibles de l'interface graphique et de présenter du code qui, espérons-le, peut être utilisé par d'autres utilisateurs de FreeCAD.

La définition de classe et le petit nombre de lignes de code qui invoquent sont décrits dans l'ordre d'apparition dans le fichier. Cet ordre est basé sur la disposition de l'écran qui est plutôt arbitraire et uniquement destinée à démontrer les fonctionnalités. Voici l'écran GUI modal généré par la classe PySide:

La majeure partie du reste de cette section décrira le contenu de la définition de classe qui apparaît à la fin de cette section. Nous couvrirons d'abord les éléments déclaratifs qui définissent comment les choses fonctionnent et comment l'interface graphique est assemblée, puis nous couvrirons les sections opérationnelles (c'est-à-dire le code qui s'exécutera lorsque les interactions de l'utilisateur se produiront). Cette fenêtre est basée sur la classe QDialog et est donc modale - ce qui signifie qu'aucune activité ne peut être effectuée en dehors de la fenêtre lorsqu'elle est ouverte.

Déclaration d'importation

La déclaration d'importation obligatoire

from PySide import QtGui, QtCore

Il est préférable de le placer en haut du fichier Python.

Définition de classe

class ExampleModalGuiClass(QtGui.QDialog):
	""""""
	def __init__(self):
		super(ExampleModalGuiClass, self).__init__()
		self.initUI()
	def initUI(self):

Ce code est mieux copié textuellement et modifié. L'essentiel du code est que nous sous-classons la classe QDialog de PySide. En adaptant ce code, vous voudrez changer le nom de la classe "ExampleModalGuiClass" - assurez-vous de le changer dans les deux endroits (par exemple les lignes 1 et 4).

État de retour de la fenêtre

self.result = userCancelled

Ce n'est pas obligatoire mais plutôt une bonne pratique de programmation, cela définit un statut de retour par défaut pour la fenêtre qui sera là indépendamment de ce que fait l'utilisateur. Plus tard dans le code, cela peut être modifié par le code Python pour indiquer les différentes options que l'utilisateur peut avoir sélectionnées.

Création de fenêtres

# create our window
# define window		xLoc,yLoc,xDim,yDim
self.setGeometry(	250, 250, 400, 350)
self.setWindowTitle("Our Example Program Window")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)

En vous rappelant que les dimensions de l'écran sont mesurées à partir du coin supérieur gauche, sur la 3ème ligne, les valeurs se réfèrent à:

  • le nombre de pixels dans le coin supérieur gauche sera à droite du bord gauche de l'écran (250)
  • le nombre de pixels dans le coin supérieur gauche sera sous le bord supérieur de l'écran (250)
  • la largeur de l'écran en pixels (400)
  • la hauteur de l'écran en pixels (350)

Le titre de la fenêtre est défini et la dernière ligne signifie simplement que cette fenêtre ne sera jamais obscurcie par une autre fenêtre - si cela n'est pas souhaité, placez simplement un caractère de commentaire Python ('#') comme premier caractère de la ligne.

Création d'étiquettes

# create some Labels
self.label1 = QtGui.QLabel("                       ", self)
self.label1.setFont('Courier') # set to a non-proportional font
self.label1.move(20, 20)
self.label2 = QtGui.QLabel("sample string number two", self)
self.label2.move(20, 70)
self.label3 = QtGui.QLabel("                        ", self)
self.label3.setFont('Courier') # set to a non-proportional font
self.label3.move(20, 120)
self.label4 = QtGui.QLabel("can you see this?", self)
self.label4.move(20, 170)

Dans PySide, les étiquettes ont deux objectifs: les étiquettes statiques (comme leur nom l'indique) et les champs de texte en lecture seule (c'est-à-dire en affichage uniquement). Ainsi, des instructions inchangées à l'utilisateur telles que "Don't push the red button" ainsi que des résultats de calcul dynamiques tels que "42" peuvent être communiquées à l'utilisateur. La 2ème ligne déclare une étiquette et définit sa valeur initiale (qui est vide dans ce cas). La troisième ligne spécifie la police, n'importe quelle police (sur le système) peut être spécifiée, si elle n'est pas spécifiée, la police par défaut est utilisée. Dans ce cas, la police est spécifiée comme non proportionnelle. L'étiquette est déplacée vers son emplacement dans la fenêtre - ses coordonnées spécifient sa position par rapport à la fenêtre (pas à l'écran).

Création de cases à cocher

# checkboxes
self.checkbox1 = QtGui.QCheckBox("Left side", self)
self.checkbox1.clicked.connect(self.onCheckbox1)
#self.checkbox1.toggle() # will set an initial value if executed
self.checkbox1.move(210,10)
#
self.checkbox2 = QtGui.QCheckBox("Right side", self)
self.checkbox2.clicked.connect(self.onCheckbox2)
self.checkbox2.move(210,30)

Les cases à cocher peuvent être désactivées et activées dans n'importe quelle combinaison (contrairement aux boutons radio). La ligne 2 en déclare un et définit sa valeur initiale. La ligne 3 spécifie quelle méthode sera exécutée lorsque la case à cocher est cliquée (dans ce cas, la méthode 'onCheckBox1'). Si la 4ème ligne n'avait pas le caractère de commentaire Python ('#') comme premier caractère, alors elle serait exécutée et il marquerait la case comme cochée. Enfin, la 5ème ligne place la case à cocher en position.

Création de boutons radio

# radio buttons
self.radioButton1 = QtGui.QRadioButton("random string one",self)
self.radioButton1.clicked.connect(self.onRadioButton1)
self.radioButton1.move(210,60)
#
self.radioButton2 = QtGui.QRadioButton("owt gnirts modnar",self)
self.radioButton2.clicked.connect(self.onRadioButton2)
self.radioButton2.move(210,80)

La création des boutons radio est très similaire aux cases à cocher. La seule différence est vraiment le comportement des boutons radio en ce qu'un seul d'entre eux peut être 'activé' à la fois.

Création d'un menu contextuel

# set up lists for pop-ups
self.popupItems1 = ("pizza","apples","candy","cake","potatoes")
# set up pop-up menu
self.popup1 = QtGui.QComboBox(self)
self.popup1.addItems(self.popupItems1)
self.popup1.setCurrentIndex(self.popupItems1.index("candy"))
self.popup1.activated[str].connect(self.onPopup1)
self.popup1.move(210, 115)

À la ligne 2, une liste est constituée de ce que seront les choix de l'utilisateur. Une alternative consiste à créer un dictionnaire mais à n'utiliser les touches que pour la liste des choix de menu. La ligne 4 crée le menu contextuel (appelé ComboBox vers PySide), les options utilisateur sont ajoutées à la ligne 5.

Si le Dictionnaire était utilisé, les lignes apparaîtraient comme suit :

self.popupItems1 = OrderedDict([("2","widget"),("pink","foobar"),("4","galopsis")])

self.popup1.addItems(self.popupItems1.keys())

Revenant à l'exemple de code principal de cette section, la ligne 6 définit le choix par défaut, cette ligne peut être omise, la valeur du choix par défaut peut également être chargée dans l'étiquette correspondante (encore une fois si nécessaire). Et enfin le passage en position à la ligne 8.

Création de bouton, partie 1

# toggle visibility button
pushButton1 = QtGui.QPushButton('Toggle visibility', self)
pushButton1.clicked.connect(self.onPushButton1)
pushButton1.setAutoDefault(False)
pushButton1.move(210, 165)

Le bouton est créé à la ligne 2 avec son nom, le gestionnaire de son signal lorsque l'utilisateur clique est spécifié à la ligne 3. La ligne 4 est là pour empêcher le bouton de devenir le 'bouton par défaut' - le bouton sur lequel l'utilisateur clique simplement si l'utilisateur appuie sur la touche Valider. Et un passage à la position a terminé le segment de code.

Création de bouton, partie 2

# cancel button
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.setAutoDefault(True)
cancelButton.move(150, 280)
# OK button
okButton = QtGui.QPushButton('OK', self)
okButton.clicked.connect(self.onOk)
okButton.move(260, 280)

Les deux boutons sont créés avec un nom (qui apparaîtra comme leur étiquette), associé à une méthode qui s'exécutera quand ils seront cliqués et déplacés en position. La seule exception est la ligne 4 qui spécifie le bouton 'Annuler' comme bouton par défaut - cela signifie qu'il sera "cliqué" si l'utilisateur appuie sur la touche Valider.

Création d'une entrée de texte

# text input field
self.textInput = QtGui.QLineEdit(self)
self.textInput.setText("cats & dogs")
self.textInput.setFixedWidth(190)
self.textInput.move(20, 220)

Le widget QLineEdit est probablement le plus courant pour la saisie textuelle de l'utilisateur, dans cet exemple, la section de code après celle-ci mettra en place un menu contextuel pour l'utiliser. Cette section de code crée (ligne 2), définit une valeur initiale (ligne 3), définit une largeur pour le champ (ligne 4) et met le widget en place (ligne 5).

Création de QuantitySpinBox

# QuantitySpinBox
from FreeCAD import Units
ui = FreeCADGui.UiLoader()
quantityInput = ui.createWidget("Gui::QuantitySpinBox")
self.quantityInput.setProperty( 'minimum', 0.0)
potential = 2.87
unit = "V"
# only set the value
self.quantityInput.setProperty('rawValue', potential )
# set quantity (value + unit)
quantity = Units.Quantity("{} {}".format(potential , unit))
self.quantityInput.setProperty('value', quantity)
# read value from the spinbox
quantity = self.quantityInput.property('value')

Le widget Gui::QuantitySpinBox est un spécial FreeCAD, conçu pour afficher et manipuler des valeurs avec leurs unités. Il est dérivé de la classe QAbstractSpinBox de Qt. Pour toutes ses propriétés, voir la liste dans le fichier de code source QuantitySpinBox.h.

Création du menu contextuel

# set contextual menu options for text editing widget
# set text field to some dogerel
popMenuAction1 = QtGui.QAction(self)
popMenuAction1.setText("load some text")
popMenuAction1.triggered.connect(self.onPopMenuAction1)
# make text uppercase
popMenuAction2 = QtGui.QAction(self)
popMenuAction2.setText("uppercase")
popMenuAction2.triggered.connect(self.onPopMenuAction2)
# menu dividers
popMenuDivider = QtGui.QAction(self)
popMenuDivider.setText('---------')
popMenuDivider.triggered.connect(self.onPopMenuDivider)
# remove all text
popMenuAction3 = QtGui.QAction(self)
popMenuAction3.setText("clear")
popMenuAction3.triggered.connect(self.onPopMenuAction3)
# define menu and add options
self.textInput.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.textInput.addAction(popMenuAction1)
self.textInput.addAction(popMenuAction2)
self.textInput.addAction(popMenuDivider)
self.textInput.addAction(popMenuAction3)

Ce code a de nombreuses répétitions car la même action est effectuée avec des valeurs différentes - cela fait partie de ce qui rend le code GUI si long (quel que soit le système). Tout d'abord, une QAction est créée - c'est un appariement (ou un lien) du texte que l'utilisateur verra comme son option sélectionnable avec la méthode qui s'exécutera s'il sélectionne cette option. Il s'agit essentiellement d'un couplage d'un choix d'utilisateur avec un morceau de code. La ligne 3 le crée, la ligne 4 définit l'option utilisateur (comme ils le verront) et la ligne 5 spécifie quel morceau de code Python sera exécuté.

En sautant à la ligne 19 (la ligne avec "self.textInput.setContextMenuPolicy"), un ActionsContextMenu est créé, qui contient tous les liens QAction séparés entre le choix de l'utilisateur et le code à exécuter. Chaque widget ne peut avoir qu'un seul menu contextuel (c'est-à-dire le menu associé au clic droit) donc la ligne 19 définit ce menu. Les 4 lignes suivantes ajoutent les liens créés au début de cette section de code. L'ordre est important ici, l'utilisateur verra les options du menu dans l'ordre dans lequel elles sont ajoutées. Notez que la 3ème option de menu est vraiment un peu rien, son code est nul mais il sert à séparer 2 groupes d'options sur le menu contextuel.

Création d'une entrée numérique

# numeric input field
self.numericInput = QtGui.QLineEdit(self)
self.numericInput.setInputMask("999")
self.numericInput.setText("000")
self.numericInput.setFixedWidth(50)
self.numericInput.move(250, 220)

La création du champ pour la saisie numérique suit vraiment celle de la saisie de texte plus tôt. En fait le code est identique à l'exception des 3ème et 4ème lignes. La troisième ligne définit le masque tel que défini par PySide, qui dans ce cas spécifie jusqu'à 3 chiffres (qui peuvent inclure 0). Une liste complète des codes InputMask peut être trouvée sur QLineEdit InputMask

Affichage de la fenêtre

# now make the window visible
self.show()

Il n'y a qu'une seule ligne et cela provoque l'affichage de l'interface graphique après l'installation.

Discussion basée sur le code - Partie opérative

Nous passons maintenant à la partie opérationnelle de la définition de l'interface graphique qui est le code qui s'exécute en réponse aux interactions de l'utilisateur avec l'interface graphique. L'ordre des groupes d'instructions n'est pas très pertinent - avec la mise en garde que quelque chose doit être déclaré avant de pouvoir être référencé. Certaines personnes mettent tous les gestionnaires d'un certain type (par exemple, les gestionnaires de boutons) dans un groupe, d'autres répertorient les gestionnaires par ordre alphabétique. Pour une application spécifique, il peut y avoir une raison liée au problème que tous les gestionnaires relatifs à un aspect spécifique soient rassemblés

Il existe un degré élevé de similitude entre les gestionnaires. La plupart ne reçoivent pas de paramètre, le fait qu'ils s'exécutent est vraiment le seul paramètre (ou signal) qu'ils reçoivent. D'autres comme "onPopup1" et "mousePressEvent" acceptent un paramètre.

Il doit y avoir une correspondance un à un entre les gestionnaires spécifiés dans la section déclarative et le gestionnaire déclaré dans cette section opérationnelle. Il peut y avoir des gestionnaires supplémentaires déclarés qui ne sont jamais appelés, mais il se peut qu'il n'y en ait aucun manquant.

Gestionnaire générique

Dans cet exemple de code, les gestionnaires génériques gèrent les événements suivants:

  • onCheckbox1
  • onCheckbox2
  • onRadioButton1
  • onRadioButton2
  • onPushButton1
  • onPopMenuAction1
  • onPopMenuAction2
  • onPopMenuDivider
  • onPopMenuAction3
  • onCancel
  • onOk

La forme générale des gestionnaires est:

def handlerName(self):
	lineOfCode1
	lineOfCode2

La première ligne contient le mot-clé "def" suivi du nom du gestionnaire. Le nom du gestionnaire doit correspondre exactement au nom de la section déclarative précédente. Le paramètre "self" fait partie de la syntaxe standard, tout comme la parenthèse englobante et le caractère deux-points final. Une fois la première ligne terminée, il n'y a aucune exigence du code suivant, il est purement spécifique à l'application.

Gestionnaire de menu contextuel

def onPopup1(self, selectedText):

Le gestionnaire du menu contextuel est le même que le gestionnaire générique, à l'exception du fait qu'un second paramètre, le texte sélectionné par l'utilisateur, est transmis. Rappelez-vous que tout est du texte provenant du menu Pop-Up et que même si l'utilisateur a sélectionné le chiffre 3, il sera transmis comme la chaîne "3".

Gestionnaire d'événements de la souris

def mousePressEvent(self, event):
	# print mouse position, X & Y
	print("X = ", event.pos().x())
	print("Y = ", event.pos().y())
	#
	if event.button() == QtCore.Qt.LeftButton:
		print("left mouse button")
	if self.label1.underMouse():
		print("over the text '"+self.label1.text()+"'")

Le gestionnaire d'événements de la souris est le même que le gestionnaire générique à l'exception du fait qu'un deuxième paramètre, l'événement de la souris (par exemple, clic gauche, clic droit) de l'utilisateur est transmis. Le nom du gestionnaire, "mousePressEvent", est réservé et s'il est modifié, le gestionnaire ne recevra plus l'événement des pressions de la souris.

Les coordonnées X et Y de la pression de la souris sont données par la référence "event.pos().x()" et "event.pos().y()". Les constantes "QtCore.Qt.LeftButton" et "QtCore.Qt.RightButton" sont utilisées pour déterminer quel bouton de la souris est enfoncé.

Une référence à un widget peut être faite de la forme "self.widgetName.underMouse()" qui retournera true ou false pour savoir si le curseur de la souris est sur le widget "widgetName". Bien que présenté dans le même extrait de code, le gestionnaire "underMouse()" n'est pas lié au gestionnaire "mousePressEvent" et peut être utilisé à tout moment.

Discussion basée sur le code - Routine principale

La plupart du volume de code est dans la définition de la classe GUI, il n'y a pas grand-chose dans la procédure principale.

# Constant definitions
global userCancelled, userOK
userCancelled = "Cancelled"
userOK = "OK"

Les lignes 2, 3 et 4 traitent de la coordination de l'état de l'interaction de l'utilisateur avec l'interface graphique - par ex. Annulé, OK ou tout autre statut défini par l'application. Les routines de gestionnaire On Cancel et OnOk précédemment définissent également ces statuts.

form = ExampleGuiClass()
form.exec_()

if form.result==userCancelled:
	pass # steps to handle user clicking Cancel
if form.result==userOK:
	# steps to handle user clicking OK
	localVariable1 = form.label1.text()
	localVariable2 = form.label2.text()
	localVariable3 = form.label3.text()
	localVariable4 = form.label4.text()

Les lignes 1 et 2 montrent la méthode pour appeler l'interface graphique. Il peut y avoir plusieurs définitions d'interface graphique pour un programme et l'interface graphique n'a pas besoin d'être invoquée en premier lieu dans le fichier Python, elle peut être appelée à tout moment. Le nom de la classe GUI est spécifié à la ligne 1 ("ExampleGuiClass" dans ce cas) mais le reste des 2 lignes doit être copié textuellement.

Les lignes 4 et 6 utilisent le champ de résultat pour déterminer l'action appropriée. Les 4 dernières lignes montrent simplement la copie des données de l'objet GUI vers des variables locales de la procédure principale en cours d'exécution.

Exemple de code modal complet

Voici l'exemple complet de code (développé sur FreeCAD v0.14):

# import statements
from PySide import QtGui, QtCore

# UI Class definitions

class ExampleModalGuiClass(QtGui.QDialog):
	""""""
	def __init__(self):
		super(ExampleModalGuiClass, self).__init__()
		self.initUI()
	def initUI(self):
		self.result = userCancelled
		# create our window
		# define window		xLoc,yLoc,xDim,yDim
		self.setGeometry(	250, 250, 400, 350)
		self.setWindowTitle("Our Example Modal Program Window")
		self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
		# create some Labels
		self.label1 = QtGui.QLabel("                       ", self)
		self.label1.setFont('Courier') # set to a non-proportional font
		self.label1.move(20, 20)
		self.label2 = QtGui.QLabel("sample string number two", self)
		self.label2.move(20, 70)
		self.label3 = QtGui.QLabel("                        ", self)
		self.label3.setFont('Courier') # set to a non-proportional font
		self.label3.move(20, 120)
		self.label4 = QtGui.QLabel("can you see this?", self)
		self.label4.move(20, 170)
		# checkboxes
		self.checkbox1 = QtGui.QCheckBox("Left side", self)
		self.checkbox1.clicked.connect(self.onCheckbox1)
		#self.checkbox1.toggle() # will set an initial value if executed
		self.checkbox1.move(210,10)
		#
		self.checkbox2 = QtGui.QCheckBox("Right side", self)
		self.checkbox2.clicked.connect(self.onCheckbox2)
		self.checkbox2.move(210,30)
		# radio buttons
		self.radioButton1 = QtGui.QRadioButton("random string one",self)
		self.radioButton1.clicked.connect(self.onRadioButton1)
		self.radioButton1.move(210,60)
		#
		self.radioButton2 = QtGui.QRadioButton("owt gnirts modnar",self)
		self.radioButton2.clicked.connect(self.onRadioButton2)
		self.radioButton2.move(210,80)
		# set up lists for pop-ups
		self.popupItems1 = ("pizza","apples","candy","cake","potatoes")
		# set up pop-up menu
		self.popup1 = QtGui.QComboBox(self)
		self.popup1.addItems(self.popupItems1)
		self.popup1.setCurrentIndex(self.popupItems1.index("candy"))
		self.popup1.activated[str].connect(self.onPopup1)
		self.popup1.move(210, 115)
		# toggle visibility button
		pushButton1 = QtGui.QPushButton('Toggle visibility', self)
		pushButton1.clicked.connect(self.onPushButton1)
		pushButton1.setAutoDefault(False)
		pushButton1.move(210, 165)
		# text input field
		self.textInput = QtGui.QLineEdit(self)
		self.textInput.setText("cats & dogs")
		self.textInput.setFixedWidth(190)
		self.textInput.move(20, 220)
		# set contextual menu options for text editing widget
		# set text field to some dogerel
		popMenuAction1 = QtGui.QAction(self)
		popMenuAction1.setText("load some text")
		popMenuAction1.triggered.connect(self.onPopMenuAction1)
		# make text uppercase
		popMenuAction2 = QtGui.QAction(self)
		popMenuAction2.setText("uppercase")
		popMenuAction2.triggered.connect(self.onPopMenuAction2)
		# menu dividers
		popMenuDivider = QtGui.QAction(self)
		popMenuDivider.setText('---------')
		popMenuDivider.triggered.connect(self.onPopMenuDivider)
		# remove all text
		popMenuAction3 = QtGui.QAction(self)
		popMenuAction3.setText("clear")
		popMenuAction3.triggered.connect(self.onPopMenuAction3)
		# define menu and add options
		self.textInput.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
		self.textInput.addAction(popMenuAction1)
		self.textInput.addAction(popMenuAction2)
		self.textInput.addAction(popMenuDivider)
		self.textInput.addAction(popMenuAction3)
		# numeric input field
		self.numericInput = QtGui.QLineEdit(self)
		self.numericInput.setInputMask("999")
		self.numericInput.setText("000")
		self.numericInput.setFixedWidth(50)
		self.numericInput.move(250, 220)
		# cancel button
		cancelButton = QtGui.QPushButton('Cancel', self)
		cancelButton.clicked.connect(self.onCancel)
		cancelButton.setAutoDefault(True)
		cancelButton.move(150, 280)
		# OK button
		okButton = QtGui.QPushButton('OK', self)
		okButton.clicked.connect(self.onOk)
		okButton.move(260, 280)
		# now make the window visible
		self.show()
		#
	def onCheckbox1(self):
		text = self.label1.text()
		if text[0]==' ':
			self.label1.setText('left'+text[4:])
		else:
			self.label1.setText('    '+text[4:])
	def onCheckbox2(self):
		text = self.label1.text()
		if text[-1]==' ':
			self.label1.setText(text[:-5]+'right')
		else:
			self.label1.setText(text[:-5]+'     ')
	def onRadioButton1(self):
		self.label2.setText(self.radioButton1.text())
	def onRadioButton2(self):
		self.label2.setText(self.radioButton2.text())
	def onPopup1(self, selectedText):
		if self.label3.text().isspace():
			self.label3.setText(selectedText)
		else:
			self.label3.setText(self.label3.text()+","+selectedText)
	def onPushButton1(self):
		if self.label4.isVisible():
			self.label4.hide()
		else:
			self.label4.show()
	def onPopMenuAction1(self):
		# load some text into field
		self.textInput.setText("Lorem ipsum dolor sit amet")
	def onPopMenuAction2(self):
		# set text in field to uppercase
		self.textInput.setText(self.textInput.text().upper())
	def onPopMenuDivider(self):
		# this option is the divider and is really there as a spacer on the menu list
		# consequently it has no functional code to execute if user selects it
		pass
	def onPopMenuAction3(self):
		# clear the text from the field
		self.textInput.setText('')
	def onCancel(self):
		self.result			= userCancelled
		self.close()
	def onOk(self):
		self.result			= userOK
		self.close()
	def mousePressEvent(self, event):
		# print mouse position, X & Y
		print("X = ", event.pos().x())
		print("Y = ", event.pos().y())
		#
		if event.button() == QtCore.Qt.LeftButton:
			print("left mouse button")
		if self.label1.underMouse():
			print("over the text '"+self.label1.text()+"'")
		if self.label2.underMouse():
			print("over the text '"+self.label2.text()+"'")
		if self.label3.underMouse():
			print("over the text '"+self.label3.text()+"'")
		if self.label4.underMouse():
			print("over the text '"+self.label4.text()+"'")
		if self.textInput.underMouse():
			print("over the text '"+self.textInput.text()+"'")
		if event.button() == QtCore.Qt.RightButton:
			print("right mouse button")
# Class definitions

# Function definitions

# Constant definitions
userCancelled = "Cancelled"
userOK = "OK"

# code ***********************************************************************************

form = ExampleModalGuiClass()
form.exec_()

if form.result==userCancelled:
	pass # steps to handle user clicking Cancel
if form.result==userOK:
	# steps to handle user clicking OK
	localVariable1 = form.label1.text()
	localVariable2 = form.label2.text()
	localVariable3 = form.label3.text()
	localVariable4 = form.label4.text()
#
#OS: Mac OS X
#Word size: 64-bit
#Version: 0.14.3703 (Git)
#Branch: releases/FreeCAD-0-14
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
#Python version: 2.7.5
#Qt version: 4.8.6
#Coin version: 3.1.3
#SoQt version: 1.5.0
#OCC version: 6.7.0
#

La meilleure façon d'utiliser ce code est de le copier dans un éditeur ou un fichier macro FreeCAD et de jouer avec.

Discussion basée sur le code - Exemple de code non modal

Tous les widgets spécifiques de l'exemple modal précédent sont transférés à utiliser dans une fenêtre non modale. La principale différence est que la fenêtre non modale n'empêche pas l'utilisateur d'interagir avec d'autres fenêtres. Fondamentalement, une fenêtre non modale est une fenêtre qui peut être ouverte et laissée ouverte aussi longtemps que nécessaire sans imposer de restrictions sur les autres fenêtres d'application. Il existe un petit nombre de différences de code entre les deux qui seront mises en évidence, par conséquent, cet exemple de code est assez bref. Tout ce qui est identique à l'exemple modal précédent sera omis dans l'intérêt de garder cette vue d'ensemble brève. Voici l'écran GUI non modal généré par la classe PySide:

Déclaration d'importation

La déclaration d'importation obligatoire

from PySide import QtGui, QtCore

Il est préférable de le placer en haut du fichier Python.

Définition de classe

class ExampleNonmodalGuiClass(QtGui.QMainWindow):
	""""""
	def __init__(self):
		super(ExampleNonmodalGuiClass, self).__init__()
		self.initUI()
	def initUI(self):

Ce code est mieux copié textuellement et modifié. L'essentiel du code est que nous sous-classons la classe QMainWindow de PySide. En adaptant ce code, vous voudrez changer le nom de la classe "ExampleModalGuiClass" - assurez-vous de le changer dans les deux endroits (par exemple les lignes 1 et 4).

Création de fenêtres

# create our window
# define window	xLoc,yLoc,xDim,yDim
self.setGeometry(	250, 250, 400, 150)
self.setWindowTitle("Our Example Nonmodal Program Window")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setMouseTracking(True)

Évidemment, les dimensions et le titre de nos fenêtres sont différents. Le point principal à noter est la dernière ligne qui permet à PySide de savoir qu'il doit envoyer les événements de position de la souris au fur et à mesure qu'ils se produisent. Notez que ces événements ne seront pas envoyés lorsque la souris sera sur un widget comme un bouton car le widget capturera les événements.

Gestionnaire d'événements de déplacement de la souris

def mouseMoveEvent(self,event):
	self.label6.setText("X: "+str(event.x()) + " Y: "+str(event.y()))

Ce gestionnaire reçoit l'événement d'un déplacement de la souris et en affiche la forme formatée. Testez ce qui se passe lorsqu'il s'agit de widgets ou en dehors de la fenêtre.

Appeler la fenêtre

form = ExampleNonmodalGuiClass()

L'appel de la fenêtre est un autre domaine de différence par rapport à l'exemple précédent. Cette fois, une seule ligne est nécessaire pour appeler l'interface graphique.

Exemple de code non modal complet

from PySide import QtGui, QtCore

# UI Class definitions

class ExampleNonmodalGuiClass(QtGui.QMainWindow):
	""""""
	def __init__(self):
		super(ExampleNonmodalGuiClass, self).__init__()
		self.initUI()
	def initUI(self):
		self.result = userCancelled
		# create our window
		# define window		xLoc,yLoc,xDim,yDim
		self.setGeometry(	250, 250, 400, 150)
		self.setWindowTitle("Our Example Nonmodal Program Window")
		self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
		self.setMouseTracking(True)
		# create Labels
		self.label4 = QtGui.QLabel("can you see this?", self)
		self.label4.move(20, 20)
		self.label5 = QtGui.QLabel("Mouse position:", self)
		self.label5.move(20, 70)
		self.label6 = QtGui.QLabel("               ", self)
		self.label6.move(135, 70)
		# toggle visibility button
		pushButton1 = QtGui.QPushButton('Toggle visibility', self)
		pushButton1.clicked.connect(self.onPushButton1)
		pushButton1.setMinimumWidth(150)
		#pushButton1.setAutoDefault(False)
		pushButton1.move(210, 20)
		# cancel button
		cancelButton = QtGui.QPushButton('Cancel', self)
		cancelButton.clicked.connect(self.onCancel)
		cancelButton.setAutoDefault(True)
		cancelButton.move(150, 110)
		# OK button
		okButton = QtGui.QPushButton('OK', self)
		okButton.clicked.connect(self.onOk)
		okButton.move(260, 110)
		# now make the window visible
		self.show()
		#
	def onPushButton1(self):
		if self.label4.isVisible():
			self.label4.hide()
		else:
			self.label4.show()
	def onCancel(self):
		self.result			= userCancelled
		self.close()
	def onOk(self):
		self.result			= userOK
		self.close()
	def mouseMoveEvent(self,event):
		self.label6.setText("X: "+str(event.x()) + " Y: "+str(event.y()))
# Class definitions

# Function definitions

# Constant definitions
global userCancelled, userOK
userCancelled		= "Cancelled"
userOK			= "OK"

# code ***********************************************************************************

form = ExampleNonmodalGuiClass()
#
#OS: Mac OS X
#Word size: 64-bit
#Version: 0.14.3703 (Git)
#Branch: releases/FreeCAD-0-14
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
#Python version: 2.7.5
#Qt version: 4.8.6
#Coin version: 3.1.3
#SoQt version: 1.5.0
#OCC version: 6.7.0

Divers sujets supplémentaires

Il existe 3 concepts à l'écran immobilier dans un environnement GUI:

  • espace physique sur l'écran
  • cadre
  • géométrie

Dans le logiciel, tous sont mesurés en pixels. PySide a la fonction de mesurer dans des unités du monde réel, mais celles-ci ne sont pas fiables car les fabricants n'ont pas de norme pour la taille des pixels ou le rapport hauteur/largeur.

Le cadre a la taille d'une fenêtre, y compris ses barres latérales, la barre supérieure (éventuellement avec un menu) et la barre inférieure. La géométrie est l'espace situé à l'intérieur du cadre et est donc toujours inférieure ou égale au cadre. À son tour, le cadre est toujours inférieur ou égal à la taille d'écran disponible.

Taille d'écran disponible

# get screen dimensions (Available Screen Size)
screenWidth		= QtGui.QDesktopWidget().screenGeometry().width()
screenHeight		= QtGui.QDesktopWidget().screenGeometry().height()
# get dimensions for available space on screen
availableWidth		= QtGui.QDesktopWidget().availableGeometry().width()
availableHeight		= QtGui.QDesktopWidget().availableGeometry().height()

En général, "availableHeight" doit être inférieur à "screenHeight" de la hauteur de la barre de menus. Ces 4 valeurs sont basées sur l'environnement matériel et changeront d'un ordinateur à l'autre. Ils ne dépendent d'aucune taille de fenêtre d'application.

(Depuis Python 3.9, cet avertissement apparaît lorsque le code ci-dessus est exécuté: DeprecationWarning: QDesktopWidget.screenGeometry(int screen) const is deprecated. Un remplacement semble être nécessaire à partir de Python 3.10.)

Taille et géométrie du cadre

# set up a variable to hold the Main Window to save some typing...
mainWin = FreeCAD.Gui.getMainWindow()

mainWin.showFullScreen() # no menu bars, every last pixel is given over to FreeCAD
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()

mainWin.showMaximized() # show maximised within the screen, window edges and the menu bar will be displayed
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()

mainWin.showNormal() # show at the last non-maximised or non-minimised size (and location)
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()

mainWin.setGeometry(50, 50, 800, 800) # specifically set FreeCAD main window's size and location, this will become the new setting for 'showNormal()'

mainWin.showMinimized() # FreeCAD will disappear from view after this command...
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()

Ces mêmes commandes peuvent être exécutées sur une fenêtre générée par l'utilisateur, la syntaxe ne change pas.