0

I'm trying to build a topBar to put in other widgets layout but I don't know why the `QPixmap is not rescaling as we change the application window size. Here's the code:

QPixmap is within QLabel within a QHBoxLayout of a QWidget that is the centralWidget of a QMainWindow

QT 5.8 - Python 3.6

I've updated this code and deleted the previous version on March 24, 2017.

0 - Dependencies

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

1 - Main Window

class MainWindow(QMainWindow):
    def __init__(self):
        print("I've been in main window")
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("I'm here, the main window")

2 - Top Bar

class topBar(QWidget):
    def __init__(self):
        print("I've been in topBar")
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setObjectName("topBar")
        self.setStyleSheet("""
        QWidget {background-color: pink;}
        QLabel {background-color: green; }""")

    def resizeEvent(self,event):
        resizeHandler(self,event) # You'll see this little dude right next

3 - The resizeEvent Handler, that's were I believe the issue is

def resizeHandler(self,event):
    print(" I've been in resizeHandler")
    if self.objectName() == "topBar":
        print("I've been resizing the topBar")
        logo = QPixmap('some_Image')
        # Debug
        # You'll be able to see that it does re-scale, but it's not updating the Pixmap.
        logo.scaled(event.size(),Qt.KeepAspectRatio).save("pixmap.png")
        # ...
        logoContainer = QLabel()
        logoContainer.setPixmap(logo.scaled(event.size(),Qt.KeepAspectRatio,Qt.FastTransformation))  
        logoContainer.setMaximumSize(logo.width(),logo.height())

        containerLayout = QHBoxLayout()
        containerLayout.addWidget(logoContainer,0)

        container = QWidget()
        container.setLayout(containerLayout)

        # Layout
        layout = QHBoxLayout()
        layout.addWidget(container,0)

        self.setLayout(layout)

        main.setCentralWidget(self)

4 - Testing

if __name__ == '__main__':
    print("I've been in __main__")
    app = 0
    app = QApplication(sys.argv)
    app.aboutToQuit.connect(app.deleteLater)
    app.setWindowIcon(QIcon('someIcon'))

    main = MainWindow() 
    main.layout().setSizeConstraint(QLayout.SetNoConstraint)

    bar = topBar()
    main.setCentralWidget(bar)
    main.show()
    app.exec_()

If it's possible I'd also like to limit topBar itself to not exceed 20% of the current screen size vertically (setMaximumHeight? But based on what?) but I'm not sure how.

Thanks!

EduGord
  • 139
  • 2
  • 13
  • What makes you think `container` isn't scaling? Change `self.setStyleSheet("""` to `container.setStyleSheet("""` and you'll see `container` scaling until it reaches the maximum size that you've set. Is the real problem not that `logo` isn't scaling? – G.M. Mar 23 '17 at 10:07
  • For a moment I thought container was not being resized as the current screen shrinks or expands. I've been trying many approaches for the past 3 days on this very simple problem but I was unable to solve it. Latest approach I removed `setScaledContents(True)` and tried re-defining the `resizeEvent` to update the pixmap to `pixmap.scaled(main.width(),main.height(),Qt.KeepAspectRatio)` but that didn't work either =/. (*main being `QMainWindow` and `topBar` being the `CentralWidget`*) – EduGord Mar 23 '17 at 22:45
  • @G.M. I've updated the question and the code, it seems like the real problem is that `QPixmap` is not updating its size. – EduGord Mar 24 '17 at 08:14

3 Answers3

0

To get a widget to fill out the container, you would want to set the vertical and horizontal size policy to either minimum, expanding, minimum expanding, or ignored. http://doc.qt.io/qt-5/qsizepolicy.html#Policy-enum

As far as the second question, it's not a built-in feature of Qt widgets. You might get better luck with QML or Web Engine. You could create a sub-class of QWidget that uses setGeometry() and some window calculation to constrain its size.

user2836202
  • 641
  • 3
  • 12
  • Tried both `[*].setSizePolicy([policy1],[policy2])` with policy being a pair of `QSizePolicy.MinimumExpanding`and `QSizePolicy.Expanding` but it's still not rescaling. I don't know if the `QLabel` is not rescaling or if it is the `container` `QWidget`. Thanks for your repply! – EduGord Mar 22 '17 at 21:18
0

I think you're looking at the wrong thing here when trying to diagnose the problem.

Your last comment states...

the real problem is that QPixmap is not updating its size

That's because the QLabel that's displaying it isn't being resized. Going back to your original code I think all you need to do is insert a layout between container and the QLabel logo...

class topBar(QWidget):
def __init__(self,parent):
    super().__init__()

    container = QWidget()
    container.setMaximumSize(587,208)
    container.setMinimumSize(0,0)

    ## Logo
    logo = QLabel(container)
    #logo = QLabel(container_layout)
    logo.setPixmap(QPixmap('.some_image_in_the_current_working_dir.png'))
    logo.setScaledContents(1)

    # G.M.
    container_layout = QHBoxLayout(container)
    container_layout.addWidget(logo)

    # Layout
    ## to center content horizontally in wrapper w/o preventing rescale
    layout = QHBoxLayout(self)
    layout.addWidget(container)

    self.setStyleSheet("""
    QWidget {background-color: red;}
    QLabel {background-color: green; Qt::KeepAspectRatio;}""")

if __name__ == '__main__':
    app = 0
    app = QApplication(sys.argv)
    app.aboutToQuit.connect(app.deleteLater)
    test = topBar(None)
    test.show()
    app.exec_()

(Look for the G.M. comment)

The code above simply creates a layout container_layout for container and makes logo a child of it. That appears to solve the problem I think you're describing.

G.M.
  • 12,232
  • 2
  • 15
  • 18
  • I've managed to achieve the same output of your code before, but it's still not keeping the aspect ratio, I guess `Qt::KeepAspectRatio` on the `StyleSheet` isn't helping with that. – EduGord Mar 24 '17 at 11:27
  • As for the *new* code, I've tried `logoContainer.setStyleSheet(".Qlabel {background-color: purple;}")` on it and I've seen that the `QLabel` is resizing, `QPixmap` still not. – EduGord Mar 24 '17 at 11:35
0

After a lot of debugging and reading here and there I came up with the following solution (using numpy to help with the rescaling):

def resizeHandler(self,event):
    if self.objectName() == "topBar":
        # Wiping the old layout
        temp = QWidget()
        temp.setLayout(self.layout())

        # Pixmap
        logoPixmap = QPixmap('./img/exampleImage.png')

        # Label
        logoLabel = QLabel()
        logoLabel.setPixmap(logoPixmap)
        logoLabel.setScaledContents(True)

        ## Label Container Layout
        containerLayout = QHBoxLayout()
        containerLayout.addWidget(logoLabel,0)

        # Label Container
        logoContainer = QWidget()
        logoContainer.setLayout(containerLayout)

        # Finding the width and height of the scaled box
        # Image unit vectors
        imageSize = np.array((logoPixmap.width(),logoPixmap.height()))
        screenSize = np.array((event.size().width(),event.size().height()))

        # Proportion of each dimension in relation to the smallest side
        # Note that one will always be the unit vector and the other greater than a unit
        screenUVec = screenSize / screenSize.min()
        imageUVec = imageSize / imageSize.min()


        # minBorder 11 is the distance between the Application vertical border and the central widget
        # 22 is the minimum height where the CentralWidget begins to appear 
        # Which should vary according to the height of menubar and statsbar of the QMainWindow
        minBorder = np.array([11,22]) *2
        screenSize -= minBorder
        for axis,size in enumerate(screenSize):
            if size < 0:
                screenSize[axis] = 0
        maxSize = np.zeros(2)

        # Ideal ratio based on the maxSide
        ratio =  int(round(screenSize[imageUVec.argmax()] / imageUVec.max() - 0.49999999))

        if ratio >=1 and 0 not in screenSize: # Image is scalable on the current screen
            maxSize[imageUVec.argmin()] = min(screenSize[imageUVec.argmin()],ratio) # We should add imageSize[imageUVec.argmin()] if we want to limit the maxSize to the maximum size of the image
            maxSize[imageUVec.argmax()] = maxSize[imageUVec.argmin()] * imageUVec.max()
            sizeUVec = maxSize / maxSize.min()


        # Layout
        layout = QHBoxLayout()
        layout.addWidget(logoContainer,0)
        logoLabel.setMaximumSize(QSize(maxSize[0],maxSize[1]))
        self.setLayout(layout)

A special thanks to @alexisdm, he showed me HERE that we should first wipe the old layout. When I started watching the globals I saw that several layouts were stacked.

As for the rescaling part, I still went through an unconventional path, but it's behaving the way I want.

Community
  • 1
  • 1
EduGord
  • 139
  • 2
  • 13