Python 当固定纵横比QWidget无法填充整个窗口时创建填充
自定义小部件(类名MyLabel,继承QLabel)具有固定的纵横比16:9 当我调整窗口大小时,标签是左上对齐的,除非窗口正好是16:9,在这种情况下,标签会完全填充窗口 如何使标签居中?我已经研究了大小策略、对齐、使用spaceitems和拉伸,但我似乎无法让它按预期工作 以下是一个最小的可复制示例:Python 当固定纵横比QWidget无法填充整个窗口时创建填充,python,pyqt5,Python,Pyqt5,自定义小部件(类名MyLabel,继承QLabel)具有固定的纵横比16:9 当我调整窗口大小时,标签是左上对齐的,除非窗口正好是16:9,在这种情况下,标签会完全填充窗口 如何使标签居中?我已经研究了大小策略、对齐、使用spaceitems和拉伸,但我似乎无法让它按预期工作 以下是一个最小的可复制示例: import sys from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow from PyQt5.QtCore impor
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow
from PyQt5.QtCore import QSize, Qt
from PyQt5.Qt import QVBoxLayout, QWidget
class MyLabel(QLabel):
def __init__(self, text, parent=None):
super().__init__(text, parent)
self.setStyleSheet("background-color: lightgreen") # Just for visibility
def resizeEvent(self, event):
# Size of 16:9 and scale it to the new size maintaining aspect ratio.
new_size = QSize(16, 9)
new_size.scale(event.size(), Qt.KeepAspectRatio)
self.resize(new_size)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__(None)
# Main widget and layout, and set as centralWidget
self.main_layout = QVBoxLayout()
self.main_widget = QWidget()
self.main_widget.setLayout(self.main_layout)
self.setCentralWidget(self.main_widget)
# Add button to main_layout
label = MyLabel("Hello World")
self.main_layout.addWidget(label)
self.show()
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
预期结果的例子:
实际结果的例子:
不幸的是,Qt没有为需要固定纵横比的小部件提供直接的解决方案 旧文档中有一些痕迹,但主要问题是:
- 与小部件、布局和大小策略的纵横比(
etc)相关的所有函数仅用于大小提示,因此,如果通过布局手动调整小部件的大小,则没有可用的约束李>hasHeightForWidth()
- 因为在
或moveEvent()
中更改小部件的几何结构可能会导致无限递归李>resizeEvent()
- 在保持纵横比的同时,不可能(正确地)控制尺寸的增长或收缩李>
paintEvent
,但出于性能目的,使用带有子QLabel的容器小部件可能会稍微好一点:理论上这会使事情变得更快,因为所有计算都完全在Qt中完成:
class ParentedLabel(QWidget):
def __init__(self, pixmap=None):
super().__init__()
self.child = QLabel(self, scaledContents=True)
if pixmap:
self.child.setPixmap(pixmap)
def setPixmap(self, pixmap):
self.child.setPixmap(pixmap)
self.updateGeometry()
def updateChild(self):
if self.child.pixmap():
r = self.child.pixmap().rect()
size = self.child.pixmap().size().scaled(
self.size(), Qt.KeepAspectRatio)
r = QRect(QPoint(), size)
r.moveCenter(self.rect().center())
self.child.setGeometry(r)
def hasHeightForWidth(self):
return bool(self.child.pixmap())
def heightForWidth(self, width):
return width * self.child.pixmap().height() / self.child.pixmap().width()
def sizeHint(self):
if self.child.pixmap():
return self.child.pixmap().size()
return QSize(160, 90)
def moveEvent(self, event):
self.updateChild()
def resizeEvent(self, event):
self.updateChild()
最后,另一种可能是使用QGraphicsView,这可能是所有方法中最快的方法,但有一个小缺点:基于给定大小提示显示的图像可能比原始图像稍小(几个像素),因此由于调整大小,它看起来有点“失焦”
class ViewLabel(QGraphicsView):
def __init__(self, pixmap=None):
super().__init__()
self.setStyleSheet('ViewLabel { border: 0px solid none; }')
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scene = QGraphicsScene()
self.setScene(scene)
self.pixmapItem = QGraphicsPixmapItem(pixmap)
self.pixmapItem.setTransformationMode(Qt.SmoothTransformation)
scene.addItem(self.pixmapItem)
def setPixmap(self, pixmap):
self.pixmapItem.setPixmap(pixmap)
self.updateGeometry()
self.updateScene()
def updateScene(self):
self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
def hasHeightForWidth(self):
return not bool(self.pixmapItem.pixmap().isNull())
def heightForWidth(self, width):
return width * self.pixmapItem.pixmap().height() / self.pixmapItem.pixmap().width()
def sizeHint(self):
if not self.pixmapItem.pixmap().isNull():
return self.pixmapItem.pixmap().size()
return QSize(160, 90)
def resizeEvent(self, event):
self.updateScene()
要正确实现一个可调整大小的小部件来保持Qt上的纵横比并不容易(实际上,有时几乎是不可能的)。还要记住,在
resizeEvent()
中更改大小是非常困难的,因为它可能导致无限递归。问题是:您是否仅出于示例目的使用QLabel,而实际上您将使用自定义小部件,或者您绝对需要该类?或者你想展示一个保持纵横比的缩放图像?谢谢你对@musicamante的评论。在我的实际问题中,我使用opencv在一个确实继承了qlabel的小部件中显示视频。我愿意接受这里的任何其他建议。我有一个实现,我实际上对resizeevent进行了裁剪,这样整个主窗口都会保持纵横比,但这不是很好,在捕捉到侧面(顶部或底部)时会变得有点奇怪。Mmh。这可能会变得棘手,因为这取决于opencv实际如何使用QLabel。除了定位问题之外,您的实现工作正常吗?我的意思是,视频是否正确显示(位置和大小),或者是否被裁剪(可能在其左侧/底部)?事实上,我读取了一帧并将其放在pixmap中,所以没有问题。如果我们解决了这个问题中的例子,我们就可以开始了。惊人的答案,谢谢!
class ViewLabel(QGraphicsView):
def __init__(self, pixmap=None):
super().__init__()
self.setStyleSheet('ViewLabel { border: 0px solid none; }')
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scene = QGraphicsScene()
self.setScene(scene)
self.pixmapItem = QGraphicsPixmapItem(pixmap)
self.pixmapItem.setTransformationMode(Qt.SmoothTransformation)
scene.addItem(self.pixmapItem)
def setPixmap(self, pixmap):
self.pixmapItem.setPixmap(pixmap)
self.updateGeometry()
self.updateScene()
def updateScene(self):
self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
def hasHeightForWidth(self):
return not bool(self.pixmapItem.pixmap().isNull())
def heightForWidth(self, width):
return width * self.pixmapItem.pixmap().height() / self.pixmapItem.pixmap().width()
def sizeHint(self):
if not self.pixmapItem.pixmap().isNull():
return self.pixmapItem.pixmap().size()
return QSize(160, 90)
def resizeEvent(self, event):
self.updateScene()