Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Créer des Widgets complexes avec PyQt5 et les intégrer dans une fenêtre

+3 votes

Je cherche à créer une application qui nécessite des widgets relativement complets, chacun d'eux composés de plusieurs boutons, checkbox, etc...

Voici un des widgets qu j'ai créé, les autres sont sur le même modèle :

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QWidget, QApplication,
                             QLabel, QCheckBox, 
                             QRadioButton, QVBoxLayout)

class Options(QWidget):
    """Classe représentant les options de configuration de GRUB"""

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        # Création des éléments
        # Label
        label = QLabel("Options", self)
        label.setAlignment(Qt.AlignHCenter)
        # Radio Buttons
        restart_nowCB = QRadioButton("Redémarrer maintenant", self)
        restart_laterCB = QRadioButton("Redémarrer plus tard", self)
        restart_nowCB.toggled.connect(self.setRestart)
        restart_laterCB.toggle()
        # Check Box
        permanentCB = QCheckBox("Permanent", self)
        permanentCB.stateChanged.connect(self.setPermanent)

        # Layouts
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        vbox.addWidget(restart_nowCB)
        vbox.addWidget(restart_laterCB)
        vbox.addWidget(permanentCB)


        # Création des variables
        self.permanent = False
        self.restart_now = False

        # Affichage de l'interface
        self.setLayout(vbox)

    def setPermanent(self, state):
        if state == Qt.Checked:
            self.permanent = True
        else:
            self.permanent = False

    def setRestart(self, state):
        self.restart_now = state

    def getPermanent(self):
        """Renvoie l'état du """
        return self.permanent

    def getRestart(self):
        return self.restart_now

if __name__ == "__main__":

    app = QApplication(sys.argv)

    win = Options()
    win.setWindowTitle("Options")
    win.show()

    sys.exit(app.exec_())

Le problème c'est que quand je tente de les intégrer tous dans la même fenêtre, il n'y a rien.
Juste une fenêtre vide.

Voici le code de cette fenêtre :

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import sys
import editor
import grub
import options
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QMainWindow, QApplication,
                             QAction, qApp,
                             QHBoxLayout, QVBoxLayout,
                             QPushButton)

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        # Création des éléments
        # Left
        self.grubList = grub.GrubList(self)
        self.options = options.Options(self)
        # Right
        self.editeur = editor.Editor(self)
        valid = QPushButton("Valider")
        cancel = QPushButton("Quitter")
        # Top
        menubar = self.menuBar()

        # Créations des Actions
        # Fichier
        exit = QAction("Quitter", self)
        exit.setShortcut('Ctrl+Q')
        exit.triggered.connect(qApp.quit)
        openISO = QAction("Sélectionner une ISO", self)
        openISO.triggered.connect(self.editeur.open_iso)
        openLoop = QAction("Ouvrir un fichier Loopback", self)
        openLoop.triggered.connect(self.editeur.open_loopback)
        fileMenu = menubar.addMenu("Fichier")
        fileMenu.addAction(openISO)
        fileMenu.addAction(openLoop)
        fileMenu.addAction(exit)
        # Grub
        openDir = QAction("Sélectionner un dossier", self)
        openDir.triggered.connect(self.grubList.add_item)
        scan = QAction("Scanner", self)
        scan.setStatusTip("Peut être long")
        scan.triggered.connect(self.grubList.scan)
        genLoop = QAction("Générer le fichier Loopback", self)
        genLoop.triggered.connect(self.editeur.gen_loopback)
        grubMenu = menubar.addMenu("Grub")
        grubMenu.addAction(openDir)
        grubMenu.addAction(scan)
        grubMenu.addAction(genLoop)

        # Création des Layouts
        # Buttons
        buttons = QHBoxLayout()
        buttons.addWidget(valid)
        buttons.addWidget(cancel)
        # Right
        right = QVBoxLayout()
        right.addWidget(self.grubList)
        right.addWidget(self.options)
        # Left
        left = QVBoxLayout()
        left.addWidget(self.editeur)
        left.addLayout(buttons)
        # Window
        window = QHBoxLayout()
        window.addLayout(right)
        window.addLayout(left)

        self.setLayout(window)
        self.setWindowTitle("GrubEnhancer")

if __name__ == "__main__":

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Dans la console, j'obtiens ce message :

QWidget::setLayout: Attempting to set QLayout "" on MainWindow "", which already has a layout

J'imagine donc que ce problème est lié à l'utilisation de Layout dans les Widgets. Cependant si je supprime le passage les concernant dans la définitions des widgets, tous les éléments se superposent sur la fenêtre finale, ce qui n'est pas terrible.
Lancer les widgets séparément fonctionne très bien.

Je voulais donc savoir comment créer des widgets complexes de ce genre de manière à pouvoir ensuite les intégrer dans une fenêtre.

demandé 3-Mai-2015 par Laërte (346 points)

1 Réponse

+4 votes
 
Meilleure réponse

Je n'ai pas tous les morceaux pour vérifier ton code, la mauvaise version de PyQt et la mauvaise version de Python... néanmoins d'après la doc de QMainWindow:

QMainWindow has its own layout

donc pas la peine d'en redéclarer un.

De plus:

The layout has a center area that can be occupied by any kind of widget [...]. Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.

Partant de là, avec quelques modifs dans le code principal ça a l'air de marcher (ne pas oublier d'importer QWidget):

    # window
    centWidget = QWidget()
    window = QHBoxLayout()
    window.addLayout(right)
    window.addLayout(left)
    centWidget.setLayout(window)

    self.setCentralWidget(centWidget)
    self.setWindowTitle("GrubEnhancer")

En passant, il vaut mieux éviter d'utiliser des mots clefs comme nom de variable (ici exit).

répondu 4-Mai-2015 par furankun (1,398 points)
sélectionné 4-Mai-2015 par Laërte

Ça marche effectivement très bien chez moi aussi, et tous les autres widgets également.

Je prend note pour le nom des variables et je vire exit tout de suite.

...