How to maintain widgets aspect ratio in Qt?

99封情书 提交于 2019-11-26 15:19:49
Marc Mutz - mmutz

You don't have to implement your own layout manager. You can do with inheriting QWidget and reimplementing

int QWidget::heightForWidth( int w ) { return w; }

to stay square. However, heightForWidth() doesn't work on toplevel windows on X11, since apparently the X11 protocol doesn't support that. As for centering, you can pass Qt::AlignCenter as the third parameter of QBoxLayout::addWidget() or the fifth parameter of QGridLayout::addWidget().

Note: In newer versions of Qt at least, QWidget does not have the heightForWidth or widthForHeight anymore (so they cannot be overriden), and therefore setWidthForHeight(true) or setHeightForWidth(true) only have an effect for descendants of QGraphicsLayout.

The right answer is to create your custom layout manager. That is possible by subclassing QLayout.

Methods to implement when subclassing QLayout

void addItem(QLayoutItem* item);
Adds item to layout.
int count() const;
Returns the item count.
QLayoutItem* itemAt(int index) const;
Returns item reference at index or 0 if there's none.
QLayoutItem* takeAt(int index);
Takes and returns item from the layout from index or returns 0 if there is none.
Qt::Orientations expandingDirections() const;
Returns the layouts expanding directions.
bool hasHeightForWidth() const;
Tells if the layout handles height for width calculation.
QSize minimumSize() const;
Returns the layouts minimum size.
void setGeometry(const QRect& rect);
Sets the geometry of the layout and the items inside it. Here you have to maintain the aspect ratio and do the centering.
QSize sizeHint() const;
Returns the preferred size for the layout.

Further reading

Calling resize() from within resizeEvent() has never worked well for me -- at best it will cause flickering as the window is resized twice (as you have), at worst an infinite loop.

I think the "correct" way to maintain a fixed aspect ratio is to create a custom layout. You'll have to override just two methods, QLayoutItem::hasHeightForWidth() and QLayoutItem::heightForWidth().

In my case overriding heightForWidth() doesn't work. And, for someone, it could be helpful to get working example of using resize event.

At first subclass qObject to create filter. More about event filters.

class FilterObject:public QObject{
public:
    QWidget *target = nullptr;//it holds a pointer to target object
    int goalHeight=0;
    FilterObject(QObject *parent=nullptr):QObject(parent){}//uses QObject constructor
    bool eventFilter(QObject *watched, QEvent *event) override;//and overrides eventFilter function
};

Then eventFilter function. It's code should be defined outside of FilterObject definition to prevent warning. Thanks to this answer.

bool FilterObject::eventFilter(QObject *watched, QEvent *event) {
    if(watched!=target){//checks for correct target object.
        return false;
    }
    if(event->type()!=QEvent::Resize){//and correct event
        return false;
    }

    QResizeEvent *resEvent = static_cast<QResizeEvent*>(event);//then sets correct event type

    goalHeight = 7*resEvent->size().width()/16;//calculates height, 7/16 of width in my case
    if(target->height()!=goalHeight){
        target->setFixedHeight(goalHeight);
    }

    return true;
};

And then in main code create FilterObject and set it as EventFilter listener to target object. Thanks to this answer.

FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();//let it be target object
filter->target=targetWidget;
targetWidget->installEventFilter(filter);

Now filter will receive all targetWidget's events and set correct height at resize event.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!