如何在不使用类的情况下自动调整QLabel pixmap大小并保持比例?

6
我们正在使用PyQt和Qt Designer制作GUI。现在,我们需要在窗口大小调整时保持比例重新调整QLabel中的图像(pixmap)。
我已经阅读了其他问题/答案,但是它们都使用了扩展类。由于我们不断更改UI,并且使用Qt Creator创建它,.ui和(相应的).py文件会自动生成,因此,如果我没有弄错的话,使用类解决方案对我们来说不是一个好选择,因为每次更新ui时我们都应手动更改类名。
有没有选项可以自动调整QLAbel中的pixmap大小并保持比例而不使用扩展类呢?
谢谢。

Dmitry,样式表?具体怎么做? - user3061177
4个回答

14

有几种方法可以做到这一点。

首先,您可以在Qt Designer中将您的QLabel提升为一个用Python编写的自定义子类。右键单击QLabel并选择“Promote to ...”,然后给该类命名(例如“ScaledLabel”)并设置头文件为自定义子类类将从中导入的Python模块(例如'mylib.classes')。

然后,自定义子类将像这样重新实现resizeEvent

class ScaledLabel(QtGui.QLabel):
    def __init__(self, *args, **kwargs):
        QtGui.QLabel.__init__(self)
        self._pixmap = QtGui.QPixmap(self.pixmap())

    def resizeEvent(self, event):
        self.setPixmap(self._pixmap.scaled(
            self.width(), self.height(),
            QtCore.Qt.KeepAspectRatio))

为了使此方法正常工作,QLabel应该将其大小策略设置为"expanding"或"minimumExpanding",并且最小大小应该设置为较小的非零值(以便图像可以被缩小)。

第二种方法避免使用子类,并使用事件过滤器来处理调整大小事件:

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        ...
        self._pixmap = QtGui.QPixmap(self.label.pixmap())
        self.label.installEventFilter(self)

    def eventFilter(self, widget, event):
        if (event.type() == QtCore.QEvent.Resize and
            widget is self.label):
            self.label.setPixmap(self._pixmap.scaled(
                self.label.width(), self.label.height(),
                QtCore.Qt.KeepAspectRatio))
            return True
        return QtGui.QMainWindow.eventFilter(self, widget, event)

我正在尝试实现这两种解决方案中的任何一种,但遇到了问题。有一个问题。 “Scaled contents”属性应该被激活还是停用? - user3061177
1
@user3061177。应将“scaledContents”属性设置为false,并根据我的答案设置大小策略和最小大小。我可以发布一个可工作的演示,但是在我的答案中已经没有太多需要添加的了。您到底遇到什么问题了? - ekhumoro

2

为您的标签设置background-image,background-repeat和background-position QSS属性。您可以通过表单编辑器或代码 QWidget :: setStyleSheet 来实现。

QSS的良好起点(附有示例)- http://doc.qt.io/qt-5/stylesheet-reference.html


所以,如果我正确设置了QSS,当调整窗口大小时,图片就会正确缩放,还是我仍然需要创建一个子类来触发resizeEvent? - user3061177
1
是的,只需设置QSS即可,无需子类化。 - Dmitry Sazonov

1
一种方法是创建一个QWidget/QLabel的子类并重新实现resizeEvent

void QWidget::resizeEvent(QResizeEvent * event) [virtual protected]

可以在子类中重新实现此事件处理程序,以接收传递给event参数的小部件调整大小事件。当调用resizeEvent()时,小部件已经具有其新几何形状。旧大小可通过QResizeEvent::oldSize()访问。

在处理调整大小事件后,小部件将被擦除并立即接收绘图事件。不需要在此处理程序内进行任何绘制(也不应该)。

这需要使用C++完成,而不是PyQt。

完成后,您可以按如下方式将自定义小部件添加到QtDesigner中:

在Qt Designer中使用自定义小部件


Laszlo,那我需要在同一项目中混合使用 py 和 cpp 文件吗?还是直接在 QtCreator 中编写 cpp 代码? - user3061177
1
@user3061177:你需要在生成的UI文件中使用该类,因此你需要将其放在项目中或者作为一个单独的库进行链接(需要进行一些修改)。 - László Papp
好的,还有一个问题。为什么我不能在Python中使用子类?这是Qt Creator或PyQT的限制吗? - user3061177
@user3061177:说实话,我觉得 Ekhumoro 比我更懂这个。 - László Papp

1

令人难以置信的是,经过七年,@ekhumoro的出色回答仍然是唯一可以找到的基本可行的Python实现;其他人告诉你该怎么做,但没有人给出实际的代码。

尽管如此,对我来说起初并没有起作用,因为我碰巧在代码的其他地方生成了像素图 - 具体来说,我的像素图是在点击按钮时才激活的函数内生成的,因此不是在窗口初始化期间生成的。

在弄清楚@ekhumoro的第二种方法如何工作之后,我进行了编辑,以适应这种差异。实际上,我将原始代码概括了,也因为我不喜欢(出于效率原因)它如何向标签添加新的_pixmap属性,这似乎只是原始像素图的副本。

以下是我的版本;请注意,我尚未完全测试它,但由于它是我的原始工作代码的较短版本,因此它也应该可以正常工作(欢迎更正):

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        # Initialize stuff here; mind that no pixmap is added to the label at this point

    def eventFilter(self, widget, event):
        if event.type() == QEvent.Resize and widget is self.label:
            self.label.setPixmap(self.label.pixmap.scaled(self.label.width(), self.label.height(), aspectRatioMode=Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation))
            return True
        return QMainWindow.eventFilter(self, widget, event)

    def apply_pixelmap(self, image):  # This is where the pixmap is added. For simplicity, suppose that you pass a QImage as an argument to this function; however, you can obtain this in any way you like
        pixmap = QPixmap.fromImage(image).scaled(new_w, new_h, aspectRatioMode=Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation)

        self.label.setPixmap(pixmap)
        self.label.pixmap = QPixmap(pixmap)  # I am aware that this line looks like a redundancy, but without it the program does not work; I could not figure out why, so I will gladly listen to anyone who knows it
        self.label.installEventFilter(self)

        return

这可以通过将 ScaledContents 属性设置为 False,并将 SizePolicy 设置为 ExpandingIgnored 来实现。请注意,如果包含图像的标签未设置为中央小部件(self.setCentralWidget(self.label),其中 self 指的是 MainWindow),则可能无法正常工作。

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