Participer au site avec un Tip
Rechercher
 

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 :

Positionnement au pixel près

Mise en oeuvre d'un menu contextuel Les stratégies QHBoxLayout et QVBoxLayout


Accès rapide :
La vidéo
Introduction
Mise en oeuvre de ce type de positionnement
Le problème du redimensionnement de la fenêtre

La vidéo

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


Positionnement de widgets au pixel près.

Introduction

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.

Mise en oeuvre de ce type de positionnement

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()
Positionnement au pixel près

Et voici le visuel obtenu avec ce code.

Exemple de positionnement au pixel près

Le problème du redimensionnement de la fenêtre

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()
Problème de visibilité

On se doute que les deux nouveaux boutons sont en partie visibles. Voici le résultat obtenu.

Exemple de positionnement au pixel près avec débordement en dehors de la fenêtre

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()
le décorateur @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.

Exemple de repositionnement manuel des widgets

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.



Mise en oeuvre d'un menu contextuel Les stratégies QHBoxLayout et QVBoxLayout