Subclass of QPlainText does not expand to fill the layout - c++

I do not understand why the CodeEditor example from the Qt website does not appear to work as expected. Every time I run the code it displays it like this, really small and not expanding to take up all the available space. Does anyone have any idea why? I have even tried to set a fixed size, sizepolicy and minimum sizing. Not sure what I am missing here.
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include "codeeditor.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
// controls
auto *widget_main = new QWidget(this);
auto *lay_main = new QVBoxLayout(widget_main);
auto *label = new QLabel("Test");
auto *editor = new CodeEditor();
// layout
lay_main->addWidget(label);
lay_main->addWidget(editor);
lay_main->setContentsMargins(5, 5, 5, 5);
}
MainWindow::~MainWindow()
{
}
codeeditor.h:
#ifndef CODEEDITOR_H
#define CODEEDITOR_H
#include <QPlainTextEdit>
#include <QObject>
class QPaintEvent;
class QResizeEvent;
class QSize;
class QWidget;
class LineNumberArea;
// Main text editor
class CodeEditor : public QPlainTextEdit
{
Q_OBJECT
public:
CodeEditor(QWidget *parent = 0);
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
protected:
void resizeEvent(QResizeEvent *event) override;
private slots:
void updateLineNumberAreaWidth(int newBlockCount);
void highlightCurrentLine();
void updateLineNumberArea(const QRect &, int);
private:
QWidget *lineNumberArea;
};
// Line number gutter
class LineNumberArea : public QWidget
{
public:
LineNumberArea(CodeEditor *editor) : QWidget(editor) {
codeEditor = editor;
}
QSize sizeHint() const override {
return QSize(codeEditor->lineNumberAreaWidth(), 0);
}
protected:
void paintEvent(QPaintEvent *event) override {
codeEditor->lineNumberAreaPaintEvent(event);
}
private:
CodeEditor *codeEditor;
};
#endif
codeeditor.cpp:
#include "codeeditor.h"
#include <QtWidgets>
#include <QFontMetrics>
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lineNumberArea = new LineNumberArea(this);
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
updateLineNumberAreaWidth(0);
highlightCurrentLine();
setFixedSize(200, 200);
setMinimumSize(200,200);
}
int CodeEditor::lineNumberAreaWidth()
{
int digits = 1;
int max = qMax(1, blockCount());
while (max >= 10) {
max /= 10;
++digits;
}
//int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
int space = 3 + 12 * digits;
return space;
}
void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}
void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
{
if (dy)
lineNumberArea->scroll(0, dy);
else
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
if (rect.contains(viewport()->rect()))
updateLineNumberAreaWidth(0);
}
void CodeEditor::resizeEvent(QResizeEvent *e)
{
QPlainTextEdit::resizeEvent(e);
QRect cr = contentsRect();
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
void CodeEditor::highlightCurrentLine()
{
QList<QTextEdit::ExtraSelection> extraSelections;
if (!isReadOnly()) {
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::yellow).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
}
setExtraSelections(extraSelections);
}
void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
{
QPainter painter(lineNumberArea);
painter.fillRect(event->rect(), Qt::lightGray);
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
painter.setPen(Qt::black);
painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
++blockNumber;
}
}

QMainWindow is a special widget since it has a preset layout
So you must set the main widget through setCentralWidget():
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto *widget_main = new QWidget;
auto *lay_main = new QVBoxLayout(widget_main);
auto *label = new QLabel("Test");
auto *editor = new CodeEditor();
setCentralWidget(widget_main); // <-- +++
// layout
lay_main->addWidget(label);
lay_main->addWidget(editor);
lay_main->setContentsMargins(5, 5, 5, 5);
}
On the other hand if you are going to use layouts then you should not set a fixed size to the widget, in your case remove setFixedSize(200, 200) on the other hand it is recommended that you make the connections with the new syntax:
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lineNumberArea = new LineNumberArea(this);
connect(this, &QPlainTextEdit::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
connect(this, &QPlainTextEdit::updateRequest, this, &CodeEditor::updateLineNumberArea);
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
updateLineNumberAreaWidth(0);
highlightCurrentLine();
// setFixedSize(200, 200); <-- ---
setMinimumSize(200,200);
}

Related

Customized widget in scroll area cannot be scrolled up or down after setting setWidgetResizable to true in qt

I have a problem when working with QScrollArea. Mainly, I want to add a customized widget into a scroll area to reach:
scroll widget if widget's size is larger than parent(scroll)
customized widget can automatically resize its size to fill all space of scroll if it is smaller than scroll
But I failed. Setting setWidgetResizable to true can resize widget but cannot scroll. Otherwise, can scroll widget not resize.
I'm using qt-5.15.x. Here is the minimal example, I can scroll it if I comment out line scroll->setWidgetResizable(true);:
customwidget.h
#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H
#include <QWidget>
#include <QSize>
#include <QResizeEvent>
class CustomWidget : public QWidget
{
Q_OBJECT
public:
explicit CustomWidget(QWidget *parent = nullptr);
~CustomWidget()=default;
protected:
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
QSize sizeHint() const override;
private:
QVector<QRectF> _rects;
QSize _size;
};
#endif // CUSTOMWIDGET_H
customwidget.cpp
#include "customwidget.h"
#include <QPainter>
CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent)
{
_size = QSize(120, 200);
for(int i = 0; i < 10; i++)
{
QRectF rect(QPointF(10, 60*i+2),QSize(100, 50));
_rects.push_back(rect);
}
}
void CustomWidget::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
// background
painter.setBrush(QColor(130,130,130));
painter.drawRect(QRectF(QPointF(0,0), _size));
// items
for(int i = 0; i < 10; i++)
{
painter.setBrush(QColor(43,43,43));
painter.drawRect(_rects[i]);
painter.setPen(QColor(255,255,255));
painter.drawText(_rects[i], QString("ITEM %1").arg(QString::number(i)));
}
}
void CustomWidget::resizeEvent(QResizeEvent *e)
{
_size = e->size();
}
QSize CustomWidget::sizeHint() const
{
int rows = _rects.size();
int height = rows * 60+2;
height = std::max(height, parentWidget()->height());
int width = 120;
return QSize(width, height);
}
Add the mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QScrollArea>
#include "customwidget.h"
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setGeometry(0,0,300,200);
QVBoxLayout *layout = new QVBoxLayout(centralWidget());
layout->setContentsMargins(0,0,0,0);
CustomWidget *cw = new CustomWidget();
QScrollArea *scroll = new QScrollArea(this);
scroll->setWidgetResizable(true);
scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll->setStyleSheet("QScrollArea{background:rgb(0,0,0);border:none;}");
scroll->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
scroll->setWidget(cw);
layout->addWidget(scroll);
}
MainWindow::~MainWindow()
{
delete ui;
}
A possible solution is to set the sizePolicy to Minimum:
CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent)
{
_size = QSize(120, 200);
for(int i = 0; i < 10; i++)
{
QRectF rect(QPointF(10, 60*i+2),QSize(100, 50));
_rects.push_back(rect);
}
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
}

Qt QGraphicsView Scrolling causes items to randomly disappear

I am having issues with a weird bug which occurs after you adjust the size of a QGraphicsItem.
Here is a YouTube video showing the issue: https://youtu.be/gp1lQTkPf54
In my application, a slider is used to adjust the horizontal zoom of all the regions on the QGraphicsScene. I have made sure to call prepareGeometryChange(); when I am changing the geometry of a region and call update(); but that has not helped. It seems to affect regions that are being rendered out of view from the user.
Code:
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene *scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
RegionGraphicsItem *rgi = new RegionGraphicsItem(ui->graphicsView->scene());
itemList.append(rgi);
ui->graphicsView->scene()->addItem(rgi);
rgi->setHScaleFactor(ui->horizontalSlider->value());
}
void MainWindow::test() {
QWidget test;
test.show();
}
void MainWindow::on_horizontalSlider_valueChanged(int value)
{
for (int i = 0; i < itemList.size(); i++) {
itemList[i]->setHScaleFactor(value);
}
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "regiongraphicsitem.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QList<RegionGraphicsItem*> itemList;
private slots:
void on_pushButton_clicked();
void on_horizontalSlider_valueChanged(int value);
private:
void test();
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
regiongraphicsitem.cpp
#include "regiongraphicsitem.h"
RegionGraphicsItem::RegionGraphicsItem(QGraphicsScene *_scene) : QGraphicsItem()
{
regionColor = QColor::fromRgb(255,255,255);
setFlags(ItemIsMovable);
waveFormColor = regionColor.darker(80);
outlineColor = QColor("#0f0f0f");
selectedColor = selectedColor.lighter(30);
hScaleFactor = 100;
mainBrush = QBrush(regionColor);
mainPen = QPen(outlineColor, 1);
gridLength = 5;
height = 56;
oldPos = pos();
scene = _scene;
this->prepareGeometryChange();
gridLocation = 5;
setY((0 * 60) + 1);
}
QRectF RegionGraphicsItem::boundingRect() const
{
return QRectF(0, 0, float(gridLength * hScaleFactor), float(height));
}
void RegionGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
QRectF rect = boundingRect();
painter->setBrush(mainBrush);
painter->setPen(mainPen);
painter->drawRoundedRect(rect, 5, 5);
painter->setPen(mainPen);
if (selected) {
painter->setBrush(QBrush(mainPen.color()));
painter->drawRoundedRect(QRect(rect.x(), rect.y(), rect.width(), 20), 5, 5);
painter->drawRect(QRect(rect.x(), rect.y() + 5, rect.width(), 15));
}
QFont font = scene->font();
font.setPixelSize(10);
font.setBold(true);
QFontMetricsF fontMetrics(font);
QString text = "TEST";
int heightFont = fontMetrics.boundingRect(text).height();
if (selected) {
painter->setPen(QPen(mainBrush.color(), 1));
}
painter->drawText(5, heightFont + 3, text);
if (pressed == false ) {
setX((gridLocation - 1) * hScaleFactor);
}
}
void RegionGraphicsItem::setHScaleFactor(int value) {
prepareGeometryChange();
hScaleFactor = value;
update();
}
regiongraphicsitem.h
#ifndef RegionGraphicsItem_H
#define RegionGraphicsItem_H
#include <QGraphicsItem>
#include <QColor>
#include <QBrush>
#include <QPen>
#include <QtGui/QPainter>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <stdint.h>
class RegionGraphicsItem : public QGraphicsItem
{
public:
RegionGraphicsItem(QGraphicsScene *_scene);
float getGridLocation();
void setHScaleFactor(int value);
protected:
QColor outlineColor;
QColor selectedColor;
QColor regionColor;
QColor waveFormColor;
bool selected;
int penWidth;
int rounded;
QBrush mainBrush;
QBrush waveformBrush;
QPen mainPen;
int height;
float gridLength;
bool pressed = false;
QPointF oldPos, oldMousePos;
int oldTrackIndex;
float gridLocation;
QGraphicsScene *scene;
int oldHScaleFactor;
virtual QRectF boundingRect() const override;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
private:
int hScaleFactor;
};
#endif // RegionGraphicsItem_H
Steps to reproduce:
Add a RegionGraphicItem to the QGraphicsScene
Increase the slider until the region is off screen
Decrease the slider and the region will not be visible. <-- This is the issue.

QDockWidget does not resize properly when interacting with GUI commands of QMainWindow

I have a problem regarding the the non correct resize of the QDockWidget. Specifically when I launch the GUI, the QDockWidget appears like in the image below which is wrong. Also I adjust the size of the QDockWidget during the use of the .ui, however as soon as I interact with the .ui (e.g. using a QPushButton or using a QCheckBox) the QDockWidget gets bigger again:
The expected behavior is the one below, which it does not increase dimension abruptly during the interaction with the .ui but rather remains in position as below:
Below is the most important part of the code I am using for this project and I signed the 3 debugging errors notified by the compiler with // <-- ERROR HERE if that can be useful:
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mDockWidget_A = new QDockWidget(QLatin1String("Command Log"));
mDockWidget_A->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
mNewText = new QPlainTextEdit;
mNewText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mDockWidget_A->setWidget(mNewText);
mDockWidget_A->installEventFilter(new QDockResizeEventFilter(mNewText,dynamic_cast<QFluidGridLayout*>(mNewText->layout())));
addDockWidget(Qt::BottomDockWidgetArea, mDockWidget_A);
}
qdockresizeeventfilter.h
#include <QObject>
#include <QLayout>
#include <QEvent>
#include <QDockWidget>
#include <QResizeEvent>
#include <QCoreApplication>
#include <QMouseEvent>
#include "qfluidgridlayout.h"
#include "mainwindow.h"
class QDockResizeEventFilter : public QObject
{
public:
friend QMainWindow;
friend QLayoutPrivate;
QDockResizeEventFilter(QWidget* dockChild, QFluidGridLayout* layout, QObject* parent = nullptr)
: QObject(parent), m_dockChild(dockChild), m_layout(layout)
{
}
protected:
bool eventFilter(QObject *p_obj, QEvent *p_event)
{
if (p_event->type() == QEvent::Resize)
{
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(p_event);
QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(p_obj->parent());
QDockWidget* dock = static_cast<QDockWidget*>(p_obj);
// determine resize direction
if (resizeEvent->oldSize().height() != resizeEvent->size().height())
{
// vertical expansion
QSize fixedSize(m_layout->widthForHeight(m_dockChild->size().height()), m_dockChild->size().height()); // <-- ERROR HERE
if (dock->size().width() != fixedSize.width())
{
m_dockChild->setFixedWidth(fixedSize.width());
dock->setFixedWidth(fixedSize.width());
// cause mainWindow dock layout recalculation
QDockWidget* dummy = new QDockWidget;
mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dummy);
mainWindow->removeDockWidget(dummy);
// adding dock widgets causes the separator move event to end
// restart it by synthesizing a mouse press event
QPoint mousePos = mainWindow->mapFromGlobal(QCursor::pos());
mousePos.setY(dock->rect().bottom());
QCursor::setPos(mainWindow->mapToGlobal(mousePos));
QMouseEvent* grabSeparatorEvent = new QMouseEvent(QMouseEvent::MouseButtonPress,mousePos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
qApp->postEvent(mainWindow, grabSeparatorEvent);
}
}
if (resizeEvent->oldSize().width() != resizeEvent->size().width())
{
// Do nothing
}
}
return false;
}
private:
QWidget* m_dockChild;
QFluidGridLayout* m_layout;
};
#endif // QDockResizeEventFilter_h_
and finally qfluidgridlayout.h
#ifndef QFluidGridLayout_h_
#define QFluidGridLayout_h_
#include <QLayout>
#include <QGridLayout>
#include <QRect>
#include <QStyle>
#include <QWidgetItem>
class QFluidGridLayout : public QLayout
{
public:
enum Direction { downToUp, UpToDown };
QFluidGridLayout(QWidget *parent = nullptr)
: QLayout(parent)
{
setContentsMargins(8,8,8,8);
setSizeConstraint(QLayout::SetMinAndMaxSize);
}
~QFluidGridLayout() {
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
void addItem(QLayoutItem *item) {
itemList.append(item);
}
Qt::Orientations expandingDirections() const {
return nullptr;
}
bool hasHeightForWidth() const {
return false;
}
int heightForWidth(int width) const {
int height = doLayout(QRect(0, 0, width, 0), true, true);
return height;
}
bool hasWidthForHeight() const {
return true;
}
int widthForHeight(int height) const { // <-- ERROR HERE
int width = doLayout(QRect(0, 0, 0, height), true, false);
return width;
}
int count() const {
return itemList.size();
}
QLayoutItem *itemAt(int index) const {
return itemList.value(index);
}
QSize minimumSize() const {
QSize size;
QLayoutItem *item;
foreach (item, itemList)
size = size.expandedTo(item->minimumSize());
size += QSize(2*margin(), 2*margin());
return size;
}
void setGeometry(const QRect &rect) {
QLayout::setGeometry(rect);
doLayout(rect);
}
QSize sizeHint() const {
return minimumSize();
}
QLayoutItem *takeAt(int index) {
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
return nullptr; }
private:
int doLayout(const QRect &rect, bool testOnly = false, bool width = false) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom); // <-- ERROR HERE
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
int lineWidth = 0;
QLayoutItem* item;
foreach(item,itemList)
{
QWidget* widget = item->widget();
if (y + item->sizeHint().height() > effectiveRect.bottom() && lineWidth > 0) {
y = effectiveRect.y();
x += lineWidth + right;
lineWidth = 0;
}
if (!testOnly) {
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
}
y += item->sizeHint().height() + top;
lineHeight = qMax(lineHeight, item->sizeHint().height());
lineWidth = qMax(lineWidth, item->sizeHint().width());
}
if (width) {
return y + lineHeight - rect.y() + bottom;
}
else {
return x + lineWidth - rect.x() + right;
}
}
QList<QLayoutItem *> itemList;
Direction dir;
};
#endif // QFluidGridLayout_h_
I have been reading about this issue frequently here and in this post. However I have been reading about the possibility that this specific object may have some bugs and it was advised to overwrite a resiveEvent. However none of this worked.
I finally, after doing a good amount of research, found this useful post which almost replicates the problem I have and that carries the majority of the two classes above class QFluidGridLayout and class QDockResizeEventFilter.
Although I am using the same approach I am still not able to achieve a normal behavior of this object.
I am also including a snapshot of the debugger:
Can someone explain what am I doing wrong? Thanks so much for shedding light on this issue.
#Emanuele, The post you saw it is mainly for sub-classing the QDockWidget as a child and therefore that solution had to be implemented manually. I think that if you look at this alternative solution you will find it useful.
Try to modify on your constructor adding resizeDocks({dock}, {100}, Qt::Horizontal); as in the post so to have:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mDockWidget_A = new QDockWidget(QLatin1String("Command Log"));
mDockWidget_A->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
mNewText = new QPlainTextEdit;
mNewText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mDockWidget_A->setWidget(mNewText);
mDockWidget_A->installEventFilter(new QDockResizeEventFilter(mNewText,dynamic_cast<QFluidGridLayout*>(mNewText->layout())));
addDockWidget(Qt::BottomDockWidgetArea, mDockWidget_A);
resizeDocks({mDockWidget_A}, {100}, Qt::Horizontal);
}

Clearing and repopulating a QFormLayout

I'm trying to set up a QFormLayout so that some buttons show or hide the part below them, and using setHidden on the widgets I want to hide is resulting in a bad layout due to the form padding still showing.
So I tried this:
#include <QApplication>
#include <QCheckBox>
#include <QLabel>
#include <QFormLayout>
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
QCheckBox checkA;
QFormLayout * m_formLayout;
QWidget * m_widget;
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent),
m_formLayout(0L),
m_widget(0L)
{
populate();
connect(&checkA, &QCheckBox::stateChanged, this, &MainWindow::populate);
}
virtual ~MainWindow() { clear(); }
bool doesOwnObject(void * it) const
{
return (uintptr_t)this <= (uintptr_t)it && (uintptr_t)it < (uintptr_t)(this+1);
}
void clear()
{
if(m_formLayout)
{
QLayoutItem *child;
while ((child = m_formLayout->takeAt(0)) != 0)
{
QLayout * layout = child->layout();
QSpacerItem * spacer = child->spacerItem();
QWidget * widget = child->widget();
if(layout && !doesOwnObject(layout)) delete layout;
if(spacer && !doesOwnObject(spacer)) delete spacer;
if(widget && !doesOwnObject(widget)) delete widget;
}
delete m_formLayout;
m_formLayout = 0L;
}
}
void populate()
{
if(m_widget) { clear(); return; }
m_widget = new QWidget(this);
setCentralWidget(m_widget);
m_formLayout = new QFormLayout(m_widget);
m_formLayout->addRow(tr("Show Check Box B:"), &checkA);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
But that doesn't seem to work as clear() doesn't seem to be actually depopulating the form layout. The widgets remain where they were before the QFormLayout was deleted. And if m_widget is deleted then the program will crash as it tries to delete checkA, because checkA has not been removed from the now deleted form.
How do I fix this?
I have come to a solution that has the desirable (or what I understood) behavior. However, I've moved everything to pointers (since is what I am used to with Qt).
Header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QCheckBox>
#include <QLayoutItem>
#include <QFormLayout>
#include <QMainWindow>
#include <QLabel>
class MainWindow : public QMainWindow
{
Q_OBJECT
QCheckBox *checkA, *checkB;
QLabel *labelA, *labelB, *labelC;
QFormLayout * m_formLayout;
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
void clear();
public slots:
void populate();
};
#endif // MAINWINDOW_H
cpp file:
#include "mainWindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QWidget * centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
m_formLayout = new QFormLayout(centralWidget);
checkA = new QCheckBox;
labelA = new QLabel("Show Check Box B:");
connect(checkA, &QCheckBox::stateChanged, this, &MainWindow::populate);
int row = 0;
m_formLayout->insertRow(row++, labelA, checkA);
checkB = new QCheckBox;
labelB = new QLabel("Check Box B:");
m_formLayout->insertRow(row++, labelB, checkB);
labelC = new QLabel("Bottom label");
m_formLayout->insertRow(row++,labelC);
populate();
}
MainWindow::~MainWindow() {
}
void MainWindow::clear()
{
if (!checkA->isChecked())
{
checkB->hide();
labelB->hide();
}
}
void MainWindow::populate()
{
clear();
if(checkA->isChecked())
{
checkB->show();
labelB->show();
}
}
Solution:
void clear()
{
QLayoutItem *child;
while ((child = m_formLayout->takeAt(0)) != 0)
{
QLayout * layout = child->layout();
QSpacerItem * spacer = child->spacerItem();
QWidget * widget = child->widget();
if(layout && !doesOwnObject(layout)) delete layout;
if(spacer && !doesOwnObject(spacer)) delete spacer;
if(widget)
{
if(doesOwnObject(widget)) widget->setParent(0L);
else delete widget;
}
}
}
setParent had to be called.

QOpenGLWidget problems under Fullscreen

I'm using Qt 5.4.0.
I added two widgets in QMainWindow: QOpenGLWidget and QQuickView. But my problem is when I clicked on fullscreen button, QQuickView would be disappeared and then when I maximized it, QQuickView would be appeared on the screen. what did i do wrong?
my code:
helper.h
#include <QBrush>
#include <QFont>
#include <QPen>
#include <QWidget>
class Helper
{
public:
Helper();
public:
void paint(QPainter *painter, QPaintEvent *event, int elapsed);
private:
QBrush background;
QBrush circleBrush;
QFont textFont;
QPen circlePen;
QPen textPen;
};
helper.cpp
#include <QPainter>
#include <QPaintEvent>
#include <QWidget>
Helper::Helper()
{
QLinearGradient gradient(QPointF(50, -20), QPointF(80, 20));
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));
background = QBrush(QColor(64, 32, 64));
circleBrush = QBrush(gradient);
circlePen = QPen(Qt::black);
circlePen.setWidth(1);
textPen = QPen(Qt::white);
textFont.setPixelSize(50);
}
void Helper::paint(QPainter *painter, QPaintEvent *event, int elapsed)
{
painter->fillRect(event->rect(), background);
painter->translate(100, 100);
painter->save();
painter->setBrush(circleBrush);
painter->setPen(circlePen);
painter->rotate(elapsed * 0.030);
qreal r = elapsed / 1000.0;
int n = 30;
for (int i = 0; i < n; ++i) {
painter->rotate(30);
qreal factor = (i + r) / n;
qreal radius = 0 + 120.0 * factor;
qreal circleRadius = 1 + factor * 20;
painter->drawEllipse(QRectF(radius, -circleRadius,
circleRadius * 2, circleRadius * 2));
}
painter->restore();
painter->setPen(textPen);
painter->setFont(textFont);
painter->drawText(QRect(-50, -50, 100, 100), Qt::AlignCenter, QStringLiteral("Qt"));
}
glwidget.h
#include <QOpenGLWidget>
class Helper;
class GLWidget : public QOpenGLWidget
{
Q_OBJECT
public:
GLWidget(Helper *helper, QWidget *parent);
public slots:
void animate();
protected:
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
private:
Helper *helper;
int elapsed;
};
glwidget.cpp
#include "glwidget.h"
#include "helper.h"
#include <QPainter>
#include <QTimer>
GLWidget::GLWidget(Helper *helper, QWidget *parent)
: QOpenGLWidget(parent), helper(helper)
{
elapsed = 0;
setFixedSize(200, 200);
setAutoFillBackground(false);
}
void GLWidget::animate()
{
elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
update();
}
void GLWidget::paintEvent(QPaintEvent *event)
{
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
helper->paint(&painter, event, elapsed);
painter.end();
}
mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
bool _showFullScreen;
Helper helper;
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QQuickView>
#include <QQuickItem>
#include <QTimer>
#include "glwidget.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
_showFullScreen = false;
QQuickView *view = new QQuickView();
QWidget *container = QWidget::createWindowContainer(view, this);
container->setMinimumSize(200, 200);
container->setMaximumSize(200, 200);
container->setFocusPolicy(Qt::TabFocus);
view->setSource(QUrl("QquickView.qml"));
ui->verticalLayout->addWidget(container);
GLWidget *openGL = new GLWidget(&helper, this);
ui->verticalLayout_2->addWidget(openGL);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), openGL, SLOT(animate()));
timer->start(50);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
if (_showFullScreen == false) {
showFullScreen();
_showFullScreen = true;
} else {
showMaximized();
_showFullScreen = false;
}
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}