Ythotha74
Ythotha74

Reputation: 83

Keep aspect ratio of image in a QLabel whilst resizing window

Say i have a layout like this:

enter image description here

I want the ratio of the left object and the right object to always be like this: enter image description here

So that i can add a small image in the top left corner that wont be gigantic or too small, but always in nice relation to the size of the window. How can i do this, either in the designer, or in the code?

I already found that you seem to be able to do this via selecting the layout and changing the LayoutStretch to something like 1,3 - this worked in the designer, however, when i inserted my image in the code, it did not respect it and blew the layout out of proption again.

I added a stretcher and used a QLabel to display the image, and then added the file via self.LogoLabel.setPixmap(QtGui.QPixmap('res/image.png')) , so i do not understand what i need to change in order to get the image to always be nice and small in the top left corner.

A test example, in case the question wasnt clear enough - the image i need is 1000x710px large.

from PyQt5 import QtCore, QtWidgets, QtGui
import sys

class Ui_ZEBRA(object):

    def setupUi(self, ZEBRA):
        ZEBRA.setObjectName("ZEBRA")
        ZEBRA.resize(315, 134)
        self.centralwidget = QtWidgets.QWidget(ZEBRA)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setContentsMargins(-1, -1, -1, 0)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.LogoLabel = QtWidgets.QLabel(self.centralwidget)
        self.LogoLabel.setText("")
        self.LogoLabel.setScaledContents(True)
        self.LogoLabel.setObjectName("LogoLabel")
        self.verticalLayout_3.addWidget(self.LogoLabel)
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout_3.addItem(spacerItem)
        self.ComboBox_InputType = QtWidgets.QComboBox(self.centralwidget)
        self.ComboBox_InputType.setObjectName("ComboBox_InputType")
        self.ComboBox_InputType.addItem("")
        self.ComboBox_InputType.addItem("")
        self.ComboBox_InputType.addItem("")
        self.ComboBox_InputType.addItem("")
        self.verticalLayout_3.addWidget(self.ComboBox_InputType)
        self.horizontalLayout.addLayout(self.verticalLayout_3)
        self.TextInput_Devices = QtWidgets.QPlainTextEdit(self.centralwidget)
        self.TextInput_Devices.setObjectName("TextInput_Devices")
        self.horizontalLayout.addWidget(self.TextInput_Devices)
        self.horizontalLayout.setStretch(0, 1)
        self.horizontalLayout.setStretch(1, 3)
        self.verticalLayout_4.addLayout(self.horizontalLayout)
        ZEBRA.setCentralWidget(self.centralwidget)
        self.menuBar_EnterToken = QtWidgets.QAction(ZEBRA)
        self.menuBar_EnterToken.setObjectName("menuBar_EnterToken")
        self.menuBar_TestToken = QtWidgets.QAction(ZEBRA)
        self.menuBar_TestToken.setObjectName("menuBar_TestToken")
        self.menuBar_About = QtWidgets.QAction(ZEBRA)
        self.menuBar_About.setObjectName("menuBar_About")

        self.retranslateUi(ZEBRA)
        QtCore.QMetaObject.connectSlotsByName(ZEBRA)

    def retranslateUi(self, ZEBRA):
        _translate = QtCore.QCoreApplication.translate
        ZEBRA.setWindowTitle(_translate("ZEBRA", "ZEBRA"))
        self.ComboBox_InputType.setItemText(0, _translate("ZEBRA", "ip"))
        self.ComboBox_InputType.setItemText(1, _translate("ZEBRA", "Use all devices"))
        self.ComboBox_InputType.setItemText(2, _translate("ZEBRA", "displayName"))
        self.ComboBox_InputType.setItemText(3, _translate("ZEBRA", "id"))
        self.menuBar_EnterToken.setText(_translate("ZEBRA", "Enter Accesstoken"))
        self.menuBar_TestToken.setText(_translate("ZEBRA", "Test Accesstoken"))
        self.menuBar_About.setText(_translate("ZEBRA", "About..."))


class Test(QtWidgets.QMainWindow, Ui_ZEBRA):
    def __init__(self, parent=None):
        super(Test, self).__init__(parent)
        self.setupUi(self)
        self.LogoLabel.setPixmap(QtGui.QPixmap('res/image.png'))

def main():
    app = QtWidgets.QApplication(sys.argv)
    form = Test()
    form.show()
    app.exec_()



if __name__ == '__main__':
    main()

EDIT: Weirdly enough, i could not find a single working example on how to use an image in a QLabel and scale its size while changing the window size, while also keeping the aspect ratio. Such a basic thing that is nowhere to be found?

Upvotes: 2

Views: 3711

Answers (2)

Flying Thunder
Flying Thunder

Reputation: 940

To scale the image down as you wanted and keep the ratio between the two elements, you need to set setScaledContents(True) like this:

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(559, 289)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setScaledContents(True)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.comboBox = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox.setObjectName("comboBox")
        self.verticalLayout.addWidget(self.comboBox)
        self.horizontalLayout.addLayout(self.verticalLayout)
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        self.textBrowser.setObjectName("textBrowser")
        self.horizontalLayout.addWidget(self.textBrowser)
        self.horizontalLayout.setStretch(0, 1)
        self.horizontalLayout.setStretch(2, 3)
        self.verticalLayout_2.addLayout(self.horizontalLayout)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "TextLabel"))

import sys
class Test(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(Test, self).__init__(parent)
        self.setupUi(self)
        pixmap = QtGui.QPixmap('res/image.png')
        pixmap = pixmap.scaled(256, 128, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
        self.label.setPixmap(pixmap)
        self.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)


def main():
    app = QtWidgets.QApplication(sys.argv)
    form = Test()
    form.show()
    app.exec_()



if __name__ == '__main__':
    main()

In order to keep the aspect ratio - i think you have to use a resizeEvent of the Widget containing the Label which changes the size to the correct aspect ratio whenever the event is triggered.

Upvotes: 1

ekhumoro
ekhumoro

Reputation: 120578

You firstly need to change the layout so that it uses alignment rather than expanding spacers to keep the label at the top left corner. Also, some properties of the label need adjustment so that it can resize itself freely. This can all be done in Qt Designer, but your example code can also be fixed manually like this:

self.LogoLabel = QtWidgets.QLabel(self.centralwidget)
self.LogoLabel.setText("")
self.LogoLabel.setObjectName("LogoLabel")

# new stuff
self.LogoLabel.setScaledContents(False)
self.LogoLabel.setMinimumSize(1, 1)
self.LogoLabel.setSizePolicy(
    QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.LogoLabel.setAlignment(QtCore.Qt.AlignTop)
self.verticalLayout_3.setAlignment(QtCore.Qt.AlignTop)

self.verticalLayout_3.addWidget(self.LogoLabel)

The dynamic resizing can then be handled using an event-filter, like this:

class Test(QtWidgets.QMainWindow, Ui_ZEBRA):
    def __init__(self, parent=None):
        super(Test, self).__init__(parent)
        self.setupUi(self)
        self._logo = QtGui.QPixmap('res/image.png')
        self.LogoLabel.setPixmap(self._logo)
        self.LogoLabel.installEventFilter(self)

    def eventFilter(self, widget, event):
        if event.type() == QtCore.QEvent.Resize and widget is self.LogoLabel:
            self.LogoLabel.setPixmap(self._logo.scaled(
                self.LogoLabel.width(), self.LogoLabel.height(),
                QtCore.Qt.KeepAspectRatio))
            return True
        return super(Test, self).eventFilter(widget, event)

Upvotes: 3

Related Questions