如何让QLabel的大小与其显示的QPixmap大小完全一致?

5

我需要关于QT/PyQt中布局的一些建议。

我的目标是显示一张图片,居中并按比例缩放,尽可能地占据空间。可以通过以下方式轻松实现:

class Demo(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowState(Qt.WindowMaximized)
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        self.button = QPushButton("Next image")
        self.label = QLabel()
        self.label.setStyleSheet("QLabel { background-color : red; }")
        self.label.setAlignment(Qt.AlignCenter)

        self.layout.addWidget(self.label)
        self.layout.addWidget(self.button)

        self.button.clicked.connect(self.showImage)
        self.show()


    def showImage(self):
        self.pixmap = QPixmap("image.jpg")
        scaled = self.pixmap.scaled(self.label.size(), Qt.KeepAspectRatio)
        self.label.setPixmap(scaled)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Demo()
    sys.exit(app.exec_())

这样做的问题是,图片将会居中显示在标签上,周围会有很多“过剩的标签”,如截图所示,红色部分。

enter image description here

但我需要标签的大小与图像完全相同。我想在图像上进行一些橡皮筋选择(选择框以供稍后在retinanet训练中使用),并计算正确的框大小,我需要映射到全局和从全局映射回来。我无法从Pixmap本身执行此操作(因为它不是小部件),当我从Label执行此操作时,我得到的值是错误的,因为它比实际图像大。
现在,我可以使用GridLayout或HBoxLayout与SpacerItems。问题在于逻辑。Label的大小(以及SpacerItems)在UI加载时确定。当我稍后动态添加图像时,即使它可以更大,它也会缩放到Label的大小,只要SpacerItems“让路”给Label。结果是,我不是左右有“太多的Label”,而是上下有“太多的Label”。我希望你明白我的意思。
我目前所做的是:
  1. 将窗口调整为屏幕分辨率
  2. 添加(缩放的)图像
  3. 调整大小(0,0)
应用程序随后被调整大小,以便Label的大小与图像完全相同(这是我想要的),但这是一种邪恶的、hacky的、闪烁的、疯狂的方法来做到这一点;-)
编辑: 非常感谢您的回答,@eyllanesc :-) 我尝试了一下,对于第一张图片它确实做到了我想要的效果。我忘了提到的是,当第一张图片被正确地"rubberbanded"之后,点击"Next"按钮,会显示另一张图片。如果这张图片比实际的Label小(或者横向而不是纵向),则标签会缩小。看哪,经过一些具有不同分辨率和方向的图像之后,标签继续缩小...

https://pastebin.com/ZMMw20Z7

https://giphy.com/gifs/xlou5RpQoX805fUymR

1个回答

4

一个可能的解决方案是将大小的水平策略更改为QSizePolicy::Maximum,并将小部件居中放置在布局中:

from PyQt5 import QtCore, QtGui, QtWidgets

class Demo(QtWidgets.QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowState(QtCore.Qt.WindowMaximized)
        layout = QtWidgets.QVBoxLayout(self)
        self.button = QtWidgets.QPushButton("Next image")
        self.label = QtWidgets.QLabel()
        self.label.setStyleSheet("QLabel { background-color : red; }")
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self.button.clicked.connect(self.showImage)
        self.show()

    def showImage(self):
        self.pixmap = QtGui.QPixmap("image.jpg")
        scaled = self.pixmap.scaled(self.label.size(), QtCore.Qt.KeepAspectRatio)
        self.label.setPixmap(scaled)
        sp = self.label.sizePolicy()
        sp.setHorizontalPolicy(QtWidgets.QSizePolicy.Maximum)
        self.label.setSizePolicy(sp)
        self.layout().setAlignment(self.label, QtCore.Qt.AlignCenter)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ex = Demo()
    sys.exit(app.exec_())

enter image description here

更新:

之前解决方案的问题在于以前的QLabel大小被用作参考来确定新的大小,而它应该基于它可能拥有的最大大小。为此,我实现了一个自定义类来跟踪最大尺寸:

from PyQt5 import QtCore, QtGui, QtWidgets
from itertools import cycle
from glob import glob

class Label(QtWidgets.QLabel):
    def resizeEvent(self, event):
        if not hasattr(self, 'maximum_size'):
            self.maximum_size = self.size()
        else:
            self.maximum_size = QtCore.QSize(
                max(self.maximum_size.width(), self.width()),
                max(self.maximum_size.height(), self.height()),
            )
        super(Label, self).resizeEvent(event)

    def setPixmap(self, pixmap):
        scaled = pixmap.scaled(self.maximum_size, QtCore.Qt.KeepAspectRatio)
        super(Label, self).setPixmap(scaled)

class Demo(QtWidgets.QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowState(QtCore.Qt.WindowMaximized)
        layout = QtWidgets.QVBoxLayout(self)
        self.button = QtWidgets.QPushButton("Next image")
        self.label = Label()
        self.label.setStyleSheet("QLabel { background-color : red; }")
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self.button.clicked.connect(self.showImage)
        self.show() 
        self.images = cycle(glob("images/*"))

    def showImage(self):
        try:
            filename = next(self.images)
            self.label.setPixmap(QtGui.QPixmap(filename))
            sp = self.label.sizePolicy()
            sp.setHorizontalPolicy(QtWidgets.QSizePolicy.Maximum)
            self.label.setSizePolicy(sp)
            self.layout().setAlignment(self.label, QtCore.Qt.AlignCenter)
        except StopIteration:
            pass

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ex = Demo()
    sys.exit(app.exec_())

1
非常感谢您的回答 :-) 我尝试了一下,它可以做到我想要的_对于第一张图片_。我忘记提到的是,在第一张图像被正确地“橡皮筋绑定”之后,单击“下一个”按钮,会显示另一张图像。如果此图像小于实际标签(或横向而不是纵向),则标签会缩小。神奇的是,在一些具有不同分辨率和方向的图像之后,标签会不断缩小... - Plotin
这是用你上面的代码精确复制得到的,同样的两张图像一遍又一遍。 https://pastebin.com/ZMMw20Z7 https://giphy.com/gifs/xlou5RpQoX805fUymR - Plotin
我希望应用程序全屏显示,并将图像按比例缩放到最大尺寸。这样,橡皮筋效果会更好。它不需要最大化,适应屏幕分辨率的(固定)缩放也可以。 - Plotin
运行得非常好!谢谢你的千个感谢 :-) - Plotin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接