Как добавить фиксированный заголовок в QScrollArea? - PullRequest
2 голосов
/ 21 февраля 2012

В настоящее время у меня есть QScrollArea, определяемый как:

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)

, который довольно удачно расположен в других макетах / виджетах, изменяет размеры, как ожидается, и т. Д.

Для предоставления заголовков для столбцов сеткиЯ использую другой QGridLayout, расположенный прямо над областью прокрутки - это работает ... но выглядит немного странно, даже при правильном оформлении, особенно когда полоса прокрутки по требованию (вертикальная) появляется или исчезает по мере необходимости и заголовки больше не нужныправильно выровняйте по столбцам сетки.Это эстетическая вещь, которую я знаю ... но я немного придирчив;)

Другие виджеты добавляются / удаляются в self.results_grid_layout программно в других местах.Последняя строка выше, которую я только недавно добавил, поскольку я думал, что будет легко использовать созданную область поля, документы для состояния setViewportMargins:

Устанавливает поля вокруг области прокрутки.Это полезно для таких приложений, как электронные таблицы с «заблокированными» строками и столбцами.Краевое пространство оставлено пустым; поместите виджеты в неиспользуемую область.

Но я не могу на всю жизнь понять, как на самом деле этого добиться, и либо мой GoogleFu дезертировалмне сегодня, или есть немного информации / примеров о том, как на самом деле добиться этого.

Моя голова говорит мне, что я могу назначить только один виджет, управляемый макетом (содержащим любое количество других виджетов) дляscrollarea - как я сделал.Если я добавлю, скажем, QHeaderview, например, в строку 0 сетки, он просто появится под полем области просмотра и прокрутится с остальной частью макета?Или я что-то упускаю и просто не вижу дрова для деревьев?

Я просто изучаю Python / Qt, поэтому любая помощь, указатели и / или примеры (желательно с Python, но не обязательно) будутБудьте благодарны!


Редактировать: Следуя совету, данному до сих пор (я думаю), я придумал следующую небольшую тестовую программу, чтобы опробовать вещи:

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

Isчто-то в этом духе метод, на который вы указалиВсе работает, как и ожидалось, именно так, как я и делал, за исключением некоторого странного начального поведения до изменения размера окна пользователем, после того, как оно было изменено, все выстраивается и работает нормально.Скорее всего, я слишком обдумываю или, по крайней мере, что-то упускаю из виду ... есть мысли?

Ответы [ 2 ]

1 голос
/ 29 сентября 2017

У меня была похожая проблема, и я решил ее немного по-другому.Вместо использования одного QScrollArea я использую два и передвигаю движение нижней области прокрутки на верхнюю.Код ниже:

  1. Создает два виджета QScrollArea в QVBoxLayout.
  2. Он отключает видимость полос прокрутки верхнего QScrollArea и назначает ему фиксированную высоту.
  3. Используя сигнал valueChanged горизонтальной полосы прокрутки нижней QScrollArea, можно «переместить» значение горизонтальной полосы прокрутки от нижней QScrollArea к верхней, что приведет к фиксированному заголовку наверхняя часть окна.

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)

Window resulting from above code.

1 голос
/ 22 февраля 2012

Возможно, вы немного обдумываете вещи.

Все, что вам нужно сделать, это использовать геометрию области просмотра scrollarea и текущие поля для расчета геометрии любых виджетов, которые вы хотите разместить на полях.

Геометрия этих виджетов также должна быть обновлена ​​в resizeEvent прокрутки.

Если вы посмотрите на исходный код QTableView, я думаю, вы обнаружите, что он использует этот метод для управления своими представлениями заголовков (или чем-то очень похожим).

EDIT

Чтобы справиться с незначительными проблемами изменения размера в вашем тестовом примере, я бы посоветовал вам прочитать раздел Координаты в документации для QRect (в частности, третий абзац и далее).

Мне удалось получить более точное изменение размера, переписав ваш тестовый пример следующим образом:

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