Question

J'ai actuellement un QScrollArea défini par:

self.results_grid_scrollarea = QScrollArea()
self.results_grid_widget = QWidget()
self.results_grid_layout = QGridLayout()
self.results_grid_layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
self.results_grid_widget.setLayout(self.results_grid_layout)
self.results_grid_scrollarea.setWidgetResizable(True)
self.results_grid_scrollarea.setWidget(self.results_grid_widget)
self.results_grid_scrollarea.setViewportMargins(0,20,0,0)

qui se trouve assez bien imbriqué dans d'autres mises en page / widgets, se redimensionne comme prévu, etc.

Pour fournir des en-têtes pour les colonnes de la grille, j'utilise un autre QGridLayout positionné directement au-dessus de la zone de défilement - cela fonctionne ... mais semble un peu étrange, même si le style est approprié, en particulier lorsque la barre de défilement à la demande (verticale) apparaît ou disparaît selon les besoins et les en-têtes ne s'alignent plus correctement avec les colonnes de la grille. C'est une chose esthétique que je sais ... mais je suis un peu pointilleux;)

D'autres widgets sont ajoutés / supprimés au self.results_grid_layout par programme ailleurs. La dernière ligne ci-dessus que j'ai récemment ajoutée car je pensais qu'il serait facile d'utiliser la zone de marge créée, le docs pour l'état de setViewportMargins:

Définit les marges autour de la zone de défilement. Ceci est utile pour les applications telles que les feuilles de calcul avec des lignes et des colonnes «verrouillées». L'espace marginal est laissé vide; mettez les widgets dans la zone inutilisée.

Mais je ne peux pas pour la vie de moi savoir comment y parvenir, et soit mon GoogleFu m'a abandonné aujourd'hui, soit il y a peu d'informations / d'exemples sur la façon d'y parvenir.

Ma tête me dit que je peux attribuer un seul widget, contrôlé par une mise en page (contenant n'importe quel nombre d'autres widgets) à la zone de défilement - comme je l'ai fait. Si j'ajoute par exemple un QHeaderview à la ligne 0 de la grille, il apparaîtra juste sous la marge de la fenêtre et défilera avec le reste de la mise en page? Ou est-ce que je manque quelque chose et je ne vois tout simplement pas le bois des arbres?

J'apprends juste Python / Qt, donc toute aide, pointeurs et / ou exemples (de préférence avec Python mais pas essentiels) seraient appréciés!


Edit: Ayant suivi les conseils donnés jusqu'à présent (je pense), j'ai mis au point le petit programme de test suivant pour essayer des choses:

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setMinimumSize(640, 480)

        self.container_widget = QWidget()
        self.container_layout = QVBoxLayout()
        self.container_widget.setLayout(self.container_layout)
        self.setCentralWidget(self.container_widget)

        self.info_label = QLabel(
            "Here you can see the problem.... I hope!\n"
            "Once the window is resized everything behaves itself.")
        self.info_label.setWordWrap(True)

        self.headings_widget = QWidget()
        self.headings_layout = QGridLayout()
        self.headings_widget.setLayout(self.headings_layout)
        self.headings_layout.setContentsMargins(1,1,0,0)

        self.heading_label1 = QLabel("Column 1")
        self.heading_label1.setContentsMargins(16,0,0,0)
        self.heading_label2 = QLabel("Col 2")
        self.heading_label2.setAlignment(Qt.AlignCenter)
        self.heading_label2.setMaximumWidth(65)
        self.heading_label3 = QLabel("Column 3")
        self.heading_label3.setContentsMargins(8,0,0,0)
        self.headings_layout.addWidget(self.heading_label1,0,0)
        self.headings_layout.addWidget(self.heading_label2,0,1)
        self.headings_layout.addWidget(self.heading_label3,0,2)
        self.headings_widget.setStyleSheet(
            "background: green; border-bottom: 1px solid black;" )

        self.grid_scrollarea = QScrollArea()
        self.grid_widget = QWidget()
        self.grid_layout = QGridLayout()
        self.grid_layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
        self.grid_widget.setLayout(self.grid_layout)
        self.grid_scrollarea.setWidgetResizable(True)
        self.grid_scrollarea.setWidget(self.grid_widget)
        self.grid_scrollarea.setViewportMargins(0,30,0,0)
        self.headings_widget.setParent(self.grid_scrollarea)
        ### Add some linedits to the scrollarea just to test
        rows_to_add = 10
        ## Setting the above to a value greater than will fit in the initial
        ## window will cause the lineedits added below to display correctly,
        ## however - using the 10 above, the lineedits do not expand to fill
        ## the scrollarea's width until you resize the window horizontally.
        ## What's the best way to fix this odd initial behaviour?
        for i in range(rows_to_add):
            col1 = QLineEdit()
            col2 = QLineEdit()
            col2.setMaximumWidth(65)
            col3 = QLineEdit()
            row = self.grid_layout.rowCount()
            self.grid_layout.addWidget(col1,row,0)
            self.grid_layout.addWidget(col2,row,1)
            self.grid_layout.addWidget(col3,row,2)
        ### Define Results group to hold the above sections
        self.test_group = QGroupBox("Results")
        self.test_layout = QVBoxLayout()
        self.test_group.setLayout(self.test_layout)
        self.test_layout.addWidget(self.info_label)
        self.test_layout.addWidget(self.grid_scrollarea)
        ### Add everything to the main layout
        self.container_layout.addWidget(self.test_group)


    def resizeEvent(self, event):
        scrollarea_vpsize = self.grid_scrollarea.viewport().size()
        scrollarea_visible_size = self.grid_scrollarea.rect()
        desired_width = scrollarea_vpsize.width()
        desired_height = scrollarea_visible_size.height()
        desired_height =  desired_height - scrollarea_vpsize.height()
        new_geom = QRect(0,0,desired_width+1,desired_height-1)
        self.headings_widget.setGeometry(new_geom)


def main():
    app = QApplication(sys.argv)
    form = MainWindow()
    form.show()
    app.exec_()

if __name__ == '__main__':
   main()

Est-ce que quelque chose de ce genre est la méthode vers laquelle vous vouliez pointer? Tout fonctionne comme prévu, exactement ce que je recherchais, sauf pour un comportement initial étrange avant que la fenêtre ne soit redimensionnée par l'utilisateur, une fois qu'elle est redimensionnée, tout s'aligne et va bien. Je réfléchis probablement à nouveau ou du moins j'oublie quelque chose ... des pensées?

Était-ce utile?

La solution

Vous pensez peut-être un peu trop.

Tout ce que vous avez à faire est d'utiliser la géométrie de la fenêtre de la zone de défilement et les marges actuelles pour calculer la géométrie de tous les widgets que vous souhaitez placer dans les marges.

La géométrie de ces widgets devrait également être mise à jour dans le resizeEvent de la zone de défilement.

Si vous regardez le code source de QTableView, je pense que vous trouverez qu'il utilise cette méthode pour gérer ses vues d'en-tête (ou quelque chose de très similaire).

< EDIT

Pour résoudre les problèmes mineurs de redimensionnement dans votre cas de test, je vous conseille de lire le Coordonnées dans la documentation pour QRect (en particulier, au troisième paragraphe).

J'ai pu obtenir un redimensionnement plus précis en réécrivant votre scénario de test comme ceci:

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setMinimumSize(640, 480)
        self.container_widget = QWidget()
        self.container_layout = QVBoxLayout()
        self.container_widget.setLayout(self.container_layout)
        self.setCentralWidget(self.container_widget)
        self.grid_scrollarea = ScrollArea(self)
        self.test_group = QGroupBox("Results")
        self.test_layout = QVBoxLayout()
        self.test_group.setLayout(self.test_layout)
        self.test_layout.addWidget(self.grid_scrollarea)
        self.container_layout.addWidget(self.test_group)

class ScrollArea(QScrollArea):
    def __init__(self, parent=None):
        QScrollArea.__init__(self, parent)
        self.grid_widget = QWidget()
        self.grid_layout = QGridLayout()
        self.grid_widget.setLayout(self.grid_layout)
        self.setWidgetResizable(True)
        self.setWidget(self.grid_widget)
        # save the margin values
        self.margins = QMargins(0, 30, 0, 0)
        self.setViewportMargins(self.margins)
        self.headings_widget = QWidget(self)
        self.headings_layout = QGridLayout()
        self.headings_widget.setLayout(self.headings_layout)
        self.headings_layout.setContentsMargins(1,1,0,0)
        self.heading_label1 = QLabel("Column 1")
        self.heading_label1.setContentsMargins(16,0,0,0)
        self.heading_label2 = QLabel("Col 2")
        self.heading_label2.setAlignment(Qt.AlignCenter)
        self.heading_label2.setMaximumWidth(65)
        self.heading_label3 = QLabel("Column 3")
        self.heading_label3.setContentsMargins(8,0,0,0)
        self.headings_layout.addWidget(self.heading_label1,0,0)
        self.headings_layout.addWidget(self.heading_label2,0,1)
        self.headings_layout.addWidget(self.heading_label3,0,2)
        self.headings_widget.setStyleSheet(
            "background: green; border-bottom: 1px solid black;" )
        rows_to_add = 10
        for i in range(rows_to_add):
            col1 = QLineEdit()
            col2 = QLineEdit()
            col2.setMaximumWidth(65)
            col3 = QLineEdit()
            row = self.grid_layout.rowCount()
            self.grid_layout.addWidget(col1,row,0)
            self.grid_layout.addWidget(col2,row,1)
            self.grid_layout.addWidget(col3,row,2)

    def resizeEvent(self, event):
        rect = self.viewport().geometry()
        self.headings_widget.setGeometry(
            rect.x(), rect.y() - self.margins.top(),
            rect.width() - 1, self.margins.top())
        QScrollArea.resizeEvent(self, event)

if __name__ == '__main__':

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

Autres conseils

J'ai eu un problème similaire et je l'ai résolu un peu différemment. Au lieu d'utiliser un QScrollArea j'utilise deux et avance un mouvement du défilement inférieur zone vers le haut. Ce que fait le code ci-dessous est

  1. Il crée deux widgets QScrollArea dans un QVBoxLayout.
  2. Il désactive la visibilité des barres de défilement du QScrollArea supérieur et lui attribue une hauteur fixe.
  3. Utilisation du signal valueChanged de la barre de défilement horizontale du QScrollArea inférieur, il est possible de "transférer" la valeur de la barre de défilement horizontale du QScrollArea inférieur vers celui du haut, résultant en un en-tête fixe en haut de la fenêtre.

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

        widget = QWidget()
        self.setCentralWidget(widget)

        vLayout = QVBoxLayout()
        widget.setLayout(vLayout)

        # TOP
        scrollAreaTop = QScrollArea()
        scrollAreaTop.setWidgetResizable(True)
        scrollAreaTop.setFixedHeight(30)
        scrollAreaTop.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scrollAreaTop.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scrollAreaTop.setWidget(QLabel(" ".join([str(i) for i in range(100)])))

        # BOTTOM
        scrollAreaBottom = QScrollArea()
        scrollAreaBottom.setWidgetResizable(True)
        scrollAreaBottom.setWidget(QLabel("\n".join([" ".join([str(i) for i in range(100)]) for _ in range(10)])))
        scrollAreaBottom.horizontalScrollBar().valueChanged.connect(lambda value: scrollAreaTop.horizontalScrollBar().setValue(value))

        vLayout.addWidget(scrollAreaTop)
        vLayout.addWidget(scrollAreaBottom)

 Fenêtre résultant du code ci-dessus.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top