自动调整pyqt小部件的大小,而不将其放在布局中



我正在尝试覆盖两个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_()

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类的现有动态属性,不应覆盖它
  • 如果行/列span只是1,则不需要显式添加,因为这是默认值
  • 你可以自动地";安装";通过在布局构造函数中指定小部件上的QLayout(就像我对QGridLayout(self)所做的那样(

最新更新