【问题标题】:Auto resize pyqt widget without putting it in a layout自动调整 pyqt 小部件的大小而不将其放入布局中
【发布时间】:2021-05-19 04:11:48
【问题描述】:

我正在尝试覆盖两个 QWidget,并让它们在窗口大小改变时自动调整大小。

背景 QWidget 显示一个带有坐标的网格,前景 QWidget 包含其他小部件(按钮等)。这些都设置为同一个 QWidget 的子级。如果我不使用布局,我可以让它们重叠,但是当父小部件调整大小时,我会失去有用的子小部件自动调整大小。

我的总体目标是创建一个编辑器,让用户可以为仪表板设计自己的小部件。网格背景将是网格布局,其小部件位于前台。

import sys

from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QWidget, \
        QGridLayout


class Grid(QWidget):
    def __init__(self, rows, columns, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.columns = columns
        self.rows = rows
        self.layout = QGridLayout()
        self.layout.setContentsMargins(0,0,0,0)
        self.layout.setSpacing(0)
        for ii in range(self.rows):
                for jj in range(self.columns):
                        label = QLabel('{}, {}'.format(ii,jj))
                        label.setStyleSheet("border: 1px solid grey; color: grey")
                        self.layout.addWidget(label, ii, jj, 1, 1)
        self.setLayout(self.layout)

class Overlay(QWidget):
    def __init__(self, rows, columns, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.rows = rows
        self.columns = columns
        self.layout = QGridLayout()
        # self.layout.setContentsMargins(0,0,0,0)
        # self.layout.setSpacing(0)
        button_1 = QPushButton('yep')
        button_2 = QPushButton('nope')
        top_left_label = QLabel('tl')
        bottom_right_label = QLabel('br')
        self.layout.addWidget(button_1, 1, 1, 1, 4)
        self.layout.addWidget(button_2, 4, 4, 2, 2)
        self.layout.addWidget(top_left_label, 0, 0, 1, 1)
        self.layout.addWidget(bottom_right_label, self.rows, self.columns, 1, 1)
        self.setLayout(self.layout)

class Parent_Dashboard_Template(QWidget):
    def __init__(self, rows, columns, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.rows = rows
        self.columns = columns
        self.setMinimumSize(400, 200)
        self.layout = QGridLayout()
        self.layout_2 = QGridLayout()
        self.grid = Grid(self.rows, self.columns, parent=self)
        # self.layout.addWidget(self.grid)
        # self.setLayout(self.layout)
        # self.grid.showMaximized()
        self.overlay = Overlay(self.rows, self.columns, parent=self)
        # self.layout_2.addWidget(self.overlay)
        # self.setLayout(self.layout_2)



if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Parent_Dashboard_Template(10, 10)
    window.show()
    app.exec_()

【问题讨论】:

    标签: python python-3.x pyqt pyqt5


    【解决方案1】:

    QGridLayout 具有“隐藏”功能,可以重叠占据相同“单元格”的项目,因此您可以将这两个小部件添加到同一行/列中:

    class Parent_Dashboard_Template(QWidget):
        def __init__(self, rows, columns, *args, **kwargs):
            # ...
            layout = QGridLayout(self)
            self.grid = Grid(self.rows, self.columns)
            self.overlay = Overlay(self.rows, self.columns)
            layout.addWidget(self.grid, 0, 0)
            layout.addWidget(self.overlay, 0, 0)
    

    由于这些覆盖的小部件需要正确地与网格对齐,更好的可能性是为主小部件创建一个网格布局并在那里添加所有内容,然后使用上述“技巧”添加覆盖的小部件。考虑到,由于 Qt 按钮有一个最小尺寸提示和一个固定的垂直策略,您还应该根据可能的最小尺寸显式覆盖尺寸提示,并设置一个Preferred 垂直尺寸策略。

    class Parent_Dashboard_Template(QWidget):
        def __init__(self, rows, columns, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.rows = rows
            self.columns = columns
            self.setMinimumSize(400, 200)
            layout = QGridLayout(self)
    
            for ii in range(self.rows):
                for jj in range(self.columns):
                    label = QLabel('{}, {}'.format(ii,jj))
                    label.setStyleSheet("border: 1px solid grey; color: grey")
                    layout.addWidget(label, ii, jj)
    
            button_1 = QPushButton('yep')
            button_2 = QPushButton('nope')
    
            # just a reference based on the last created label
            minSize = label.sizeHint()
            button_1.sizeHint = lambda: minSize
            button_2.sizeHint = lambda: minSize
            button_1.setSizePolicy(
                QSizePolicy.Preferred, QSizePolicy.Preferred)
            button_2.setSizePolicy(
                QSizePolicy.Preferred, QSizePolicy.Preferred)
    
            top_left_label = QLabel('tl')
            bottom_right_label = QLabel('br')
            layout.addWidget(button_1, 1, 1, 1, 4)
            layout.addWidget(button_2, 4, 4, 2, 2)
            layout.addWidget(top_left_label, 0, 0)
            layout.addWidget(
                bottom_right_label, self.rows - 1, self.columns - 1)
    

    最后,考虑到网格可能无论如何都需要在一个单独的类中,您可以覆盖主小部件的调整大小事件,然后根据它们跨越的单元格矩形设置覆盖小部件的几何形状,映射到顶级小部件。

    class Parent_Dashboard_Template(QWidget):
        def __init__(self, rows, columns, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.rows = rows
            self.columns = columns
            self.setMinimumSize(400, 200)
            layout = QVBoxLayout(self)
    
            self.grid = Grid(self.rows, self.columns)
            layout.addWidget(self.grid)
    
            self.button_1 = QPushButton('yep', self)
            self.button_2 = QPushButton('nope', self)
            self.top_left_label = QLabel('tl', self)
            self.bottom_right_label = QLabel('br', self)
            self.widgetCells = {
                self.button_1: (1, 1, 1, 4), 
                self.button_2: (4, 4, 2, 2), 
                self.top_left_label: (0, 0, 1, 1), 
                self.bottom_right_label: (
                    self.rows - 1, self.columns - 1, 1, 1), 
            }
    
        def resizeEvent(self, event):
            super().resizeEvent(event)
            layout = self.grid.layout
            # required for first show
            layout.activate()
            for w, (row, column, rSpan, cSpan) in self.widgetCells.items():
                geo = layout.cellRect(row, column)
                geo |= layout.cellRect(
                    row + rSpan - 1, column + cSpan - 1)
                geo.moveTopLeft(self.grid.mapTo(self, geo.topLeft()))
                w.setGeometry(geo)
    

    注意:

    • layout() 是所有 QWidget 类的现有 动态 属性,您不应覆盖它;
    • 如果它们只是1,则不需要显式添加行/列跨度,因为这是默认值;
    • 您可以通过在布局构造函数中指定 QLayout 来自动在小部件上“安装”它(就像我对 QGridLayout(self) 所做的那样);

    【讨论】:

      猜你喜欢
      • 2017-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-29
      • 1970-01-01
      相关资源
      最近更新 更多