Как я могу получить QLabel точно такого размера, как QPixmap, который он показывает? - PullRequest
3 голосов
/ 28 марта 2019

Мне нужен совет относительно раскладок в 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

Но мне нужно, чтобы Ярлык был точно размера изображения. Я хочу сделать несколько резиновых полос на изображении, чтобы выбрать их части (выбрать блоки для последующего использования в обучении ретинанетам) и рассчитать правильный размер блоков, которые мне нужны для mapToGlobal и mapFromGlobal. Я не могу сделать это из самого Pixmap (так как это не виджет), и когда я делаю это из Label, я получаю неправильные значения, так как он больше, чем фактическое изображение.

Теперь я мог бы использовать GridLayout или HBoxLayout с SpacerItems. Проблема в логике с этим. Размер метки (и SpacerItems определяется при загрузке пользовательского интерфейса. Когда я позже динамически добавляю изображение, оно масштабируется до размера метки, даже если оно может быть больше, если только SpacerItems «уступит дорогу»). сделайте ярлык. В результате вместо «слишком большого количества ярлыков» слева и справа у меня будет «слишком много ярлыков» сверху и снизу. Надеюсь, вы понимаете, о чем я.

Что я сейчас делаю:

  1. Изменение размера окна до разрешения экрана
  2. Добавить (масштабированное) изображение
  3. изменить размер (0,0)

После этого размер приложения изменяется так, что Labe точно соответствует размеру изображения (что я хочу), но это злой, хакерский, мерцающий, безумный способ сделать это; -)

EDIT: Большое спасибо за ответ, @eyllanesc :-) Я попробовал это, и это делает то, что я хочу для первой картины. Что я забыл упомянуть, так это то, что, когда первое изображение правильно «резиновой», после нажатия кнопки «Далее» отображается другое изображение. Если это изображение меньше, чем фактическая метка (или пейзаж вместо портрета), метка сжимается. И вот, после некоторых изображений с разным разрешением и ориентацией, Метка продолжает уменьшаться и уменьшаться ...

https://pastebin.com/ZMMw20Z7

https://giphy.com/gifs/xlou5RpQoX805fUymR

1 Ответ

3 голосов
/ 28 марта 2019

Одним из возможных решений является изменение горизонтальной политики размера на 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_())
...