Layout issues within GraphicsItem
-
Hi,
I 'm having trouble with layouts in general and was wondering if I'm just missing something or theres a bug. The setup is quite complex so I'll do my best to describe the issue.
So I'm using QGraphicsView to create a node editor. Each node is a
QGraphicsWidget
that contains layouts and otherQGraphicsWidgets
.The structure of a node looks like this
I want node to always resize to its content - thats why I'm using layouts.
Now, lets say theres a custom text edit that can resize based on it's content. The longer the string, the longer the widget.
When I put this text edit into my node (in inputLayout), the node grows accordingly so it contains entire text edit BUT when the text edit shrinks, node does not. It seems like it's all depending onQSizePolicy
I set on HeaderLayout.When Header layout (
QGraphicsLinearLayout(Qt::Horizontal)
) size policy is set toQSizePolicy::Preferred
it has both Shrink and Grow flags but it doesnt shrink - it just grows.
When I set header layout size policy toQSizePolic::Maximum
, it works as expected - node shrinks and grows BUT second header element is no longer aligned to the right, which is what I want to achieve.To better illustrate the issue take a look at this
There are two nodes on graphics view here.
Each node has exactly the same layout - the only difference is between header size policy
Header contains label (with size policy printed out) and two buttons used to resize underlying text edit.What I want is to have, is buttons in header to be right aligned (like in second node) and entire node to shrink/grow accordingly to its contents (like first node).
Below you can find the code behind the example:
#include "QtLayoutIssues.h" #include <QtWidgets/QApplication> #include "qpushbutton.h" #include "qtextedit.h" #include "qgraphicswidget.h" #include "qgraphicsproxywidget.h" #include "qgraphicslinearlayout.h" #include "qlabel.h" #include "qpainter.h" #include "qgraphicsview.h" #include "QStyleOptionGraphicsItem" class TestTextEdit : public QTextEdit { public: static const int SIZE_DELTA = 25; TestTextEdit(QWidget* parent = nullptr) : QTextEdit(parent) {} void shrink() { updateSize({ -SIZE_DELTA, 0 }); } void grow() { updateSize({ SIZE_DELTA, 0 }); } void updateSize(QSize delta) { const QSize s = size(); // this doesn't work for some reason... //setFixedSize(s + delta); setFixedHeight(s.height() + delta.height()); setFixedWidth(s.width() + delta.width()); updateGeometry(); } }; class TestItem : public QGraphicsWidget { public: QGraphicsProxyWidget* MakeProxyWithContent(QWidget* content) { auto proxy = new QGraphicsProxyWidget(this); proxy->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); proxy->setWidget(content); return proxy; } TestItem(QSizePolicy::Policy headerSizePolicy, QGraphicsItem* parent = nullptr) : QGraphicsWidget(parent) { setFlags(ItemIsSelectable | ItemIsMovable); QGraphicsLinearLayout* mainLayout = new QGraphicsLinearLayout(Qt::Vertical); QGraphicsLinearLayout* headerLayout = new QGraphicsLinearLayout(Qt::Horizontal); headerLayout->setSizePolicy(headerSizePolicy, QSizePolicy::Maximum); headerLayout->addItem(MakeProxyWithContent(new QLabel(QVariant::fromValue(headerSizePolicy).toString()))); headerLayout->addStretch(); auto minusButton = new QPushButton("-"); auto plusButton = new QPushButton("+"); headerLayout->addItem(MakeProxyWithContent(minusButton)); headerLayout->addItem(MakeProxyWithContent(plusButton)); mainLayout->addItem(headerLayout); QGraphicsLinearLayout* bodyLayout = new QGraphicsLinearLayout(Qt::Horizontal); bodyLayout->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); QGraphicsLinearLayout* inputLayout = new QGraphicsLinearLayout(Qt::Vertical); QGraphicsLinearLayout* outputLayout = new QGraphicsLinearLayout(Qt::Vertical); bodyLayout->addItem(inputLayout); bodyLayout->addStretch(); bodyLayout->addItem(outputLayout); auto textEdit = new TestTextEdit; connect(minusButton, &QPushButton::clicked, textEdit, &TestTextEdit::shrink); connect(plusButton, &QPushButton::clicked, textEdit, &TestTextEdit::grow); inputLayout->addItem(MakeProxyWithContent(textEdit)); mainLayout->addItem(bodyLayout); setLayout(mainLayout); } void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override { QGraphicsWidget::paint(painter, option, widget); painter->save(); painter->setBrush(Qt::darkBlue); painter->setPen(Qt::black); painter->drawRect(option->rect); painter->setBrush(Qt::darkRed); painter->drawRect(layout()->contentsRect()); painter->restore(); } }; class GraphicsView : public QGraphicsView { public: GraphicsView(QWidget* parent = nullptr) : QGraphicsView(parent) { QGraphicsScene* scene = new QGraphicsScene(this); scene->setSceneRect(-200, -200, 400, 400); setScene(scene); scene->setBackgroundBrush(Qt::darkGray); auto item1 = new TestItem(QSizePolicy::Maximum); item1->moveBy(-300, -250); scene->addItem(item1); auto item2 = new TestItem(QSizePolicy::Preferred); item2->moveBy(-300, 0); scene->addItem(item2); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); GraphicsView* view = new GraphicsView; QMainWindow w; w.resize(800, 600); w.setCentralWidget(view); w.show(); return a.exec(); }
I have tried all other size policy combinations with no luck and tbh it looks like a bug to me.
Any help or advice is greatly appreciated!
Cheers
-
Hi,
I 'm having trouble with layouts in general and was wondering if I'm just missing something or theres a bug. The setup is quite complex so I'll do my best to describe the issue.
So I'm using QGraphicsView to create a node editor. Each node is a
QGraphicsWidget
that contains layouts and otherQGraphicsWidgets
.The structure of a node looks like this
I want node to always resize to its content - thats why I'm using layouts.
Now, lets say theres a custom text edit that can resize based on it's content. The longer the string, the longer the widget.
When I put this text edit into my node (in inputLayout), the node grows accordingly so it contains entire text edit BUT when the text edit shrinks, node does not. It seems like it's all depending onQSizePolicy
I set on HeaderLayout.When Header layout (
QGraphicsLinearLayout(Qt::Horizontal)
) size policy is set toQSizePolicy::Preferred
it has both Shrink and Grow flags but it doesnt shrink - it just grows.
When I set header layout size policy toQSizePolic::Maximum
, it works as expected - node shrinks and grows BUT second header element is no longer aligned to the right, which is what I want to achieve.To better illustrate the issue take a look at this
There are two nodes on graphics view here.
Each node has exactly the same layout - the only difference is between header size policy
Header contains label (with size policy printed out) and two buttons used to resize underlying text edit.What I want is to have, is buttons in header to be right aligned (like in second node) and entire node to shrink/grow accordingly to its contents (like first node).
Below you can find the code behind the example:
#include "QtLayoutIssues.h" #include <QtWidgets/QApplication> #include "qpushbutton.h" #include "qtextedit.h" #include "qgraphicswidget.h" #include "qgraphicsproxywidget.h" #include "qgraphicslinearlayout.h" #include "qlabel.h" #include "qpainter.h" #include "qgraphicsview.h" #include "QStyleOptionGraphicsItem" class TestTextEdit : public QTextEdit { public: static const int SIZE_DELTA = 25; TestTextEdit(QWidget* parent = nullptr) : QTextEdit(parent) {} void shrink() { updateSize({ -SIZE_DELTA, 0 }); } void grow() { updateSize({ SIZE_DELTA, 0 }); } void updateSize(QSize delta) { const QSize s = size(); // this doesn't work for some reason... //setFixedSize(s + delta); setFixedHeight(s.height() + delta.height()); setFixedWidth(s.width() + delta.width()); updateGeometry(); } }; class TestItem : public QGraphicsWidget { public: QGraphicsProxyWidget* MakeProxyWithContent(QWidget* content) { auto proxy = new QGraphicsProxyWidget(this); proxy->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); proxy->setWidget(content); return proxy; } TestItem(QSizePolicy::Policy headerSizePolicy, QGraphicsItem* parent = nullptr) : QGraphicsWidget(parent) { setFlags(ItemIsSelectable | ItemIsMovable); QGraphicsLinearLayout* mainLayout = new QGraphicsLinearLayout(Qt::Vertical); QGraphicsLinearLayout* headerLayout = new QGraphicsLinearLayout(Qt::Horizontal); headerLayout->setSizePolicy(headerSizePolicy, QSizePolicy::Maximum); headerLayout->addItem(MakeProxyWithContent(new QLabel(QVariant::fromValue(headerSizePolicy).toString()))); headerLayout->addStretch(); auto minusButton = new QPushButton("-"); auto plusButton = new QPushButton("+"); headerLayout->addItem(MakeProxyWithContent(minusButton)); headerLayout->addItem(MakeProxyWithContent(plusButton)); mainLayout->addItem(headerLayout); QGraphicsLinearLayout* bodyLayout = new QGraphicsLinearLayout(Qt::Horizontal); bodyLayout->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); QGraphicsLinearLayout* inputLayout = new QGraphicsLinearLayout(Qt::Vertical); QGraphicsLinearLayout* outputLayout = new QGraphicsLinearLayout(Qt::Vertical); bodyLayout->addItem(inputLayout); bodyLayout->addStretch(); bodyLayout->addItem(outputLayout); auto textEdit = new TestTextEdit; connect(minusButton, &QPushButton::clicked, textEdit, &TestTextEdit::shrink); connect(plusButton, &QPushButton::clicked, textEdit, &TestTextEdit::grow); inputLayout->addItem(MakeProxyWithContent(textEdit)); mainLayout->addItem(bodyLayout); setLayout(mainLayout); } void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override { QGraphicsWidget::paint(painter, option, widget); painter->save(); painter->setBrush(Qt::darkBlue); painter->setPen(Qt::black); painter->drawRect(option->rect); painter->setBrush(Qt::darkRed); painter->drawRect(layout()->contentsRect()); painter->restore(); } }; class GraphicsView : public QGraphicsView { public: GraphicsView(QWidget* parent = nullptr) : QGraphicsView(parent) { QGraphicsScene* scene = new QGraphicsScene(this); scene->setSceneRect(-200, -200, 400, 400); setScene(scene); scene->setBackgroundBrush(Qt::darkGray); auto item1 = new TestItem(QSizePolicy::Maximum); item1->moveBy(-300, -250); scene->addItem(item1); auto item2 = new TestItem(QSizePolicy::Preferred); item2->moveBy(-300, 0); scene->addItem(item2); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); GraphicsView* view = new GraphicsView; QMainWindow w; w.resize(800, 600); w.setCentralWidget(view); w.show(); return a.exec(); }
I have tried all other size policy combinations with no luck and tbh it looks like a bug to me.
Any help or advice is greatly appreciated!
Cheers
This could be a case for a
QSplitter
. It will push your buttons to the right and shrink with your widget, so your buttons move left again while resizing.
https://doc.qt.io/qt-5/qsplitter.htmlEdit:
Place your splitter left to your buttons inside your header layout. -
@Pl45m4 thanks for reply, unfortunately using
QSplitter
dosn't change anything. Buttons are kept on the right but entire node is not shrinking.I've tried it like this
QGraphicsLinearLayout* headerLayout = new QGraphicsLinearLayout(Qt::Horizontal); headerLayout->setSizePolicy(headerSizePolicy, QSizePolicy::Maximum); auto splitter = new QSplitter(Qt::Horizontal); splitter->addWidget(new QLabel(QVariant::fromValue(headerSizePolicy).toString())); splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); headerLayout->addItem(MakeProxyWithContent(splitter)); auto minusButton = new QPushButton("-"); auto plusButton = new QPushButton("+"); headerLayout->addItem(MakeProxyWithContent(minusButton)); headerLayout->addItem(MakeProxyWithContent(plusButton));
and also tried to add buttons to splitter but with no luck.
-
@Pl45m4 thanks for reply, unfortunately using
QSplitter
dosn't change anything. Buttons are kept on the right but entire node is not shrinking.I've tried it like this
QGraphicsLinearLayout* headerLayout = new QGraphicsLinearLayout(Qt::Horizontal); headerLayout->setSizePolicy(headerSizePolicy, QSizePolicy::Maximum); auto splitter = new QSplitter(Qt::Horizontal); splitter->addWidget(new QLabel(QVariant::fromValue(headerSizePolicy).toString())); splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); headerLayout->addItem(MakeProxyWithContent(splitter)); auto minusButton = new QPushButton("-"); auto plusButton = new QPushButton("+"); headerLayout->addItem(MakeProxyWithContent(minusButton)); headerLayout->addItem(MakeProxyWithContent(plusButton));
and also tried to add buttons to splitter but with no luck.
-
@Pl45m4 mainLayout has default size policy so I believe its
QSizePolicy::Preferred, QSizePolicy::Preferred
.
Setting it toMaximum, Maximum
doesn't change anythingThe problem is that header layout shrinks when its size policy is set to
Maximum
(it has shrink flag on) but not when it's set toPreferred
(when it has Shrink AND grow flag)