Accès rapide :
La vidéo
Introduction
Mise en oeuvre de ce type de positionnement
Le problème du redimensionnement de la fenêtre
Cette vidéo vous montre comment positionner au pixel près un widget dans son conteneur. Comme nous allons le constater, cette technique n'est pas forcément la meilleure et elle nous permettra d'introduire la notion de layout (de stratégie de positionnement).
Le positionnement au pixel près dans PySide6 est une méthode permettant de placer des widgets à des positions spécifiques dans une fenêtre en utilisant des coordonnées x et y, ainsi que de définir leur taille précise. Cette approche est souvent contrastée avec l'utilisation des layouts (stratégie de positionnement), qui gèrent automatiquement le positionnement et le redimensionnement des widgets selon les directives du développeur.
Cette approche, bien qu'elle offre un contrôle précis et une personnalisation poussée, présente à la fois des avantages et des inconvénients.
Avantage : très simple à mettre en oeuvre.
Inconvénients : par défaut, ne permet pas un repositionnement automatique des widgets en cas de changement de taille de la fenêtre. Et si vous cherchez à réagir à ces changements pour tout repositionner, cela peut vous coûter très cher en lignes de code à produire.
Pour mettre en oeuvre le positionnement au pixel près, il faut commencer par instancier un widget à l'associant au conteneur dans lequel il sera
affiché. Ensuite il faut utiliser les méthodes move()
et resize()
, ou setGeometry()
(cette dernière
méthode combinant les deux précédentes en un seul appel). Voici un premier exemple basique.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton class ManualPositionnning(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Positionnnement au pixel près") self.resize(500, 200) # On crée le conteneur que nous allons placer en tant que # zone centrale de la fenêtre (la QMainWindow). centralWidget = QWidget() self.setCentralWidget(centralWidget) # Un premier bouton positionné avec des appels à move et resize firstButton = QPushButton("Push me!", centralWidget) firstButton.move(10, 10) firstButton.resize(300, 30) # Un second bouton positionné avec un appel à setGeometry secondButton = QPushButton("Click me!", centralWidget) secondButton.setGeometry(10, 50, 300, 30) if __name__ == "__main__": app = QApplication([]) fenetre = ManualPositionnning() fenetre.show() app.exec() |
Et voici le visuel obtenu avec ce code.
Comme nous l'avons déjà dit, ce type de positionnement présente un inconvénient majeur : en cas de redimensionnement de la fenêtre, certains widgets peuvent ne plus être visible (partiellement ou intégralement) dans l'interface utilisateur. Voici un exemple de code montrant cet aspect : amusez-vous à changer la taille de la fenêtre.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton class ManualPositionnning(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Positionnnement au pixel près") self.resize(500, 200) # On crée le conteneur que nous allons placer en tant que zone centrale de la fenêtre centralWidget = QWidget() self.setCentralWidget(centralWidget) # Un premier bouton positionné avec des appels à move et resize firstButton = QPushButton("Push me!", centralWidget) firstButton.move(10, 10) firstButton.resize(300, 30) # Un second bouton positionné avec un appel à setGeometry secondButton = QPushButton("Click me!", centralWidget) secondButton.setGeometry(10, 50, 300, 30) # Un troisième bouton placé à la droite du premier thirdButton = QPushButton("Cropped button", self) thirdButton.setGeometry(400, 10, 300, 30) lastButton = QPushButton("Another cropped button", self) lastButton.setGeometry(400, 50, 300, 30) if __name__ == "__main__": app = QApplication([]) fenetre = ManualPositionnning() fenetre.show() app.exec() |
On se doute que les deux nouveaux boutons sont en partie visibles. Voici le résultat obtenu.
Vous pourriez imaginer prendre en charge vous-même le redimensionnement des boutons en fonction de la place disponible. Mais vous me voyez venir, cela va être un peu complexe à gérer : il va falloir faire des calculs.
Pour réagir au changement de taille de la fenêtre, il faut redéfinir la méthode resizeEvent
. Cette méthode est initialement
définie dans la classe QWidget
. Afin de permettre l'accès aux boutons dans cette méthode, il est nécessaire de les définir en
tant qu'attributs de la fenêtre (ajout du self.
devant chaque instance de bouton).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
from typing import override from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton class ManualPositionnning(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Positionnnement au pixel près") self.resize(500, 200) # On crée le conteneur que nous allons placer en tant que zone centrale de la fenêtre. centralWidget = QWidget() self.setCentralWidget(centralWidget) # Les boutons sont maintenant des attributs de la fenêtre. self.firstButton = QPushButton("Push me!", centralWidget) self.secondButton = QPushButton("Click me!", centralWidget) self.thirdButton = QPushButton("Another button", self) self.lastButton = QPushButton("Last button", self) # Cette méthode déclenche automatiquement en cas de redimensionnement de la fenêtre. @override def resizeEvent(self, event): # Appel à la méthode de base pour gérer d'autres aspects du redimensionnement. super().resizeEvent(event) # Calcul de la largeur disponible pour les boutons. # Le -30 correspond aux espaces entre les bordures et les boutons. buttonWidth = (self.width() - 30) // 2 # // => division entière # Ajustement de la taille des boutons pour occuper la nouvelle largeur disponible. self.firstButton.setGeometry(10, 10, buttonWidth, 30) self.secondButton.setGeometry(20 + buttonWidth, 10, buttonWidth, 30) self.thirdButton.setGeometry(10, 50, buttonWidth, 30) self.lastButton.setGeometry(20 + buttonWidth, 50, buttonWidth, 30) if __name__ == "__main__": app = QApplication([]) fenetre = ManualPositionnning() fenetre.show() app.exec() |
@override
a été introduit dans Python à partir de sa version 3.12.
Il sert juste, à titre indicatif et explicite, à dire qu'on redéfini la méthode resizeEvent
, mais n'a pas d'autre utilité.
Si vous utiliser une version précédente de Python, veuillez retirer l'utilisation de ce décorateur et tout devrait quand même parfaitement
fonctionner.
Et voici le résultat obtenu.
Au terme de ce chapitre, j'espère avoir éclairé les défis et les limites inhérents au positionnement manuel des widgets au pixel près. Cette approche, bien que précise et flexible dans certains cas, révèle rapidement ses limites lorsque nous abordons des interfaces plus complexes ou nécessitant une adaptabilité accrue. C'est dans ce contexte que l'importance des layouts, ou systèmes de positionnement automatique, prend tout son sens.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :