Search code examples
c++qtqwidget

Keeping the aspect ratio of a sub-classed QWidget during resize


I'm creating a new widget, by subclassing the QWidget class. I'd like to be able to set a ratio (for its height and its width) for this widget, which will always be maintained.

For this, I've always searched, using the Qt5 documentation, Google, and Stackoverflow. Obviously, I've found answers: in particular, this one. But, unfortunately, not even one is fully effective:

  • Setting the sizeIncrement does totally nothing, even if the widget is a window
  • I tried to overload resizeEvent, but I really don't know how to do this...
  • If I follow this answer, two things:

    1. If the widget is a top-level window, the ratio isn't maintained at all, I can resize it as I want.
    2. If I place this widget in a layout, if I just increase both width and height of the window, the ratio is maintained. But as soon as I increase the width or the height to much, the widget is flattened. Instead, I would like that the layout automatically adjust its size to keep the widget's ratio.

So, how could I manage to keep the aspect ratio of a subclassed QWidget?


Solution

  • Create a parent widget (e.g., AspectRatioWidget) in which to place your widget. For the parent widget, subclass QWidget and give it a QBoxLayout. Put your widget into the center, and QSpacerItems on either side. Then in the parent widget's QWidget::resizeEvent adjust the direction and stretches as needed. I've provided an example below. To use, just create an instance of AspectRatioWidget and pass the constructor a pointer to your widget and the desired aspect ratio.

    // header
    class AspectRatioWidget : public QWidget
    {
    public:
        AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent = 0);
        void resizeEvent(QResizeEvent *event);
    
    private:
        QBoxLayout *layout;
        float arWidth; // aspect ratio width
        float arHeight; // aspect ratio height
    };
    
    // cpp
    AspectRatioWidget::AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent) :
        QWidget(parent), arWidth(width), arHeight(height)
    {
        layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
    
        // add spacer, then your widget, then spacer
        layout->addItem(new QSpacerItem(0, 0));
        layout->addWidget(widget);
        layout->addItem(new QSpacerItem(0, 0));
    }
    
    void AspectRatioWidget::resizeEvent(QResizeEvent *event)
    {
        float thisAspectRatio = (float)event->size().width() / event->size().height();
        int widgetStretch, outerStretch;
    
        if (thisAspectRatio > (arWidth/arHeight)) // too wide
        {
            layout->setDirection(QBoxLayout::LeftToRight);
            widgetStretch = height() * (arWidth/arHeight); // i.e., my width
            outerStretch = (width() - widgetStretch) / 2 + 0.5;
        }
        else // too tall
        {
            layout->setDirection(QBoxLayout::TopToBottom);
            widgetStretch = width() * (arHeight/arWidth); // i.e., my height
            outerStretch = (height() - widgetStretch) / 2 + 0.5;
        }
    
        layout->setStretch(0, outerStretch);
        layout->setStretch(1, widgetStretch);
        layout->setStretch(2, outerStretch);
    }