9

I want to make my widget always have square size. Following this answer, I have overridden QWidget::heightForWidth(), and I also call setHeightForWidth(true) in the constructor, as suggested by @peppe. The size policy is set to Preferred,Preferred (for both horizontal size and vertical size).

However, heightForWidth() is not being called. Is there anything I am doing wrong?

This is the declaration of heightForWidth() in my Widget class:

virtual int heightForWidth(int) const;

This happens on Linux and Windows.

Community
  • 1
  • 1
sashoalm
  • 75,001
  • 122
  • 434
  • 781
  • 1
    Have you also set the corresponding flag on the widget's QSizePolicy? X11 on its own does support height-for-width flags. – peppe Jul 17 '13 at 19:39
  • Which is this flag? The size policy of my widget is set to `Preferred`. – sashoalm Jul 18 '13 at 05:54
  • It's in QSizePolicy: `QSizePolicy p = sizePolicy(); p.setHeightForWidth(true); setSizePolicy(p);`. Note that also layouts may interfere with your heightForWidth (they have same flags as well). – peppe Jul 18 '13 at 07:30
  • Right now I'm creating the widget directly, as a top-level window, so there should be no interference there. I tried `p.setHeightForWidth(true); ` as you suggested, but `heightForWidth()` was still not called (and this time I'm trying on Windows). – sashoalm Jul 18 '13 at 10:38
  • It's a good idea to at least post the function member declaration here, just to make sure it's not some silly error (like a typo, etc.) – Nikos C. Jul 22 '13 at 14:06

2 Answers2

2

Your widget needs to be in a layout. The below works on both Qt 4 and 5.

In Qt 4, it will only force the toplevel window's minimum size if it's in a layout.

In Qt 5, it doesn't force the toplevel window size. There may be a flag for that or it's a bug but I don't recall at the moment.

screenshot

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QDebug>
#include <QVBoxLayout>
#include <QFrame>

class Widget : public QWidget {
    mutable int m_ctr;
public:
    Widget(QWidget *parent = 0) : QWidget(parent), m_ctr(0) {
        QSizePolicy p(sizePolicy());
        p.setHeightForWidth(true);
        setSizePolicy(p);
    }
    int heightForWidth(int width) const {
        m_ctr ++;
        QApplication::postEvent(const_cast<Widget*>(this), new QEvent(QEvent::UpdateRequest));
        return qMax(width*2, 100);
    }
    QSize sizeHint() const {
        return QSize(300, heightForWidth(300));
    }
    void paintEvent(QPaintEvent *) {
        QPainter p(this);
        p.drawRect(rect().adjusted(0, 0, -1, -1));
        p.drawText(rect(), QString("h4w called %1 times").arg(m_ctr));
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    QVBoxLayout * l = new QVBoxLayout(&w);
    l->addWidget(new Widget);
    QFrame * btm = new QFrame;
    btm->setFrameShape(QFrame::Panel);
    btm->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    l->addWidget(btm);
    w.show();
    return a.exec();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thanks. I just tested it, it works. It forces the top-level window's size on Windows, but on Linux, it doesn't force it, may be it's just not supported there. – sashoalm Sep 21 '13 at 11:49
  • That's due to the awkward design of the window manager protocol on X11 :( Hopefully once Wayland gains ground it will be working again. – Kuba hasn't forgotten Monica Sep 21 '13 at 14:58
  • Hm, it works with your code, but when I try to make it work with a QMainWindow, it doesn't work. I tried setting the QVBoxLayout to the centralWidget(), but that didn't work. – sashoalm Sep 28 '13 at 23:40
  • @sashoalm: That's a wholly separate problem. Post a separate question. – Kuba hasn't forgotten Monica Sep 29 '13 at 01:09
  • Just [posted](http://stackoverflow.com/questions/19076544/qwidgetheightforwidth-and-qmainwindow) a new question about it :) – sashoalm Sep 29 '13 at 09:44
  • Does posting the layout request not end with an endless loop because it activates the parent widget's layoutwhich will call sizeHint, and which calls heightForWidth again? Posting a layout request is equivalent to calling updateGeometry except that the latter is a no-op for hidden widgets right? – Johannes Schaub - litb Jul 18 '17 at 13:13
1

To stay square if the widget is in a layout you must reimplement

bool hasHeightForWidth() const{ return true; }
int heightForWidth(int w) const { return w; }

functions of the layout class.

Mattia
  • 173
  • 1
  • 1
  • 7