C++, Qt : deallocation of "non attribute" objects created in constructor - c++

In the following example of a Qt class, many objects are created in the constructor and in functions called by the constructor, but are not pointed at by attribute pointers and, from what I understood, cannot be deleted in the destructor (there is at not point in any of the files the keyword delete).
http://qt-project.org/doc/qt-4.8/widgets-groupbox.html
in window.h
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0);
private:
QGroupBox *createFirstExclusiveGroup();
QGroupBox *createSecondExclusiveGroup();
QGroupBox *createNonExclusiveGroup();
QGroupBox *createPushButtonGroup();
};
in window.cpp
Window::Window(QWidget *parent)
: QWidget(parent)
{
QGridLayout *grid = new QGridLayout;
grid->addWidget(createFirstExclusiveGroup(), 0, 0);
grid->addWidget(createSecondExclusiveGroup(), 1, 0);
grid->addWidget(createNonExclusiveGroup(), 0, 1);
grid->addWidget(createPushButtonGroup(), 1, 1);
setLayout(grid);
setWindowTitle(tr("Group Boxes"));
resize(480, 320);
}
QGroupBox *Window::createFirstExclusiveGroup()
{
QGroupBox *groupBox = new QGroupBox(tr("Exclusive Radio Buttons"));
QRadioButton *radio1 = new QRadioButton(tr("&Radio button 1"));
QRadioButton *radio2 = new QRadioButton(tr("R&adio button 2"));
QRadioButton *radio3 = new QRadioButton(tr("Ra&dio button 3"));
radio1->setChecked(true);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(radio1);
vbox->addWidget(radio2);
vbox->addWidget(radio3);
vbox->addStretch(1);
groupBox->setLayout(vbox);
return groupBox;
}
Am I missing something or is it a "bad" example of implementation ?
Would a correct implementation be to put the pointers as attribute of class Window and destroy what they point at in ~Window() ? Thank you.

Because all objects are added to the object tree, they are deleted automatically.
E.g. all widgets are added to the layout, and the layout itself is set in the window. This creates a tree of objects.
You can use the method QObject::dumpObjectTree() to get a visual representation of the current tree of objects.
See also Object Trees & Ownership in the Qt documentation for details.

Related

Change background color of a Qlayout containing QButtons

I have this Qt code:
QHBoxLayout *taggerBox = new QHBoxLayout;
falseBtn = new QToolButton;
falseBtn->setText(tr("False"));
voidBtn = new QToolButton;
voidBtn->setText(tr("Void"));
taggerBox->addWidget(falseBtn);
taggerBox->addWidget(voidBtn);
I would like to change the background of the QHBoxLayout (NOT the background of each button). I didn't find any way to change the background color of a QLayout.
How can I do this ?
Thanks!
QLayout is not a visual element, it's a container that adjust location of contained widgets. You can change background of QFrame or other widgets you included QLayout into.
Since QHBoxLayout is not a QWidget it hasn't its own appearance. So you can't change its color.
You'll have to add an intervening widget that you set the layout on, and change that widget's background. E.g.
auto *taggerBox = new QWidget;
auto *layout = new QHBoxLayout(taggerbox);
falseBtn = new QToolButton(tr("False"));
voidBtn = new QToolButton(tr("Void"));
layout->addWidget(falseBtn);
layout->addWidget(voidBtn);
auto palette = taggerBox->palette();
palette.setColor(QPalette::Window, Qt::blue);
taggerBox->setPalette(palette);
If you're doing this in the constructor of some class, then likely the objects have the same lifetime as the class as there's no point to dynamically allocate them. In such circumstances, the widgets and layouts should be class members instead:
class MyClass : ... {
QWidget taggerBox;
QHBoxLayout taggerLayout{&taggerBox};
QToolButton falseBtn{tr("False")};
QToolButton voidBtn{tr("Void")};
public:
MyClass(...);
};
MyClass::MyClass(...) : ... {
taggerLayout.addWidget(&falseBtn);
taggerLayout.addWidget(&voidBtn);
auto palette = taggerBox.palette();
palette.setColor(QPalette::Window, Qt::blue);
taggerBox.setPalette(palette);
...
}

How to hide a temporary search bar?

I have a window that contains a browser. Up is a toolbar. In the bottom of the window is a search bar.
Search bar has a close button [x].
When the user clicks the close button I want the bar to disappear.
I want the bar only appear when user press CTRL + F. I tried to connect the close butoon with .hide() command, but application crashes. I need help.
.cpp
DocumentationWin::DocumentationWin (QWidget * parent){
docs = new QTextBrowser( this );
//Prepare toolbar
toolbar = new QToolBar( this );
//add stuff to toolbar
//Prepare footer bar
searchlabel = new QLabel(tr("Find in page:"),this);
resultslabel = new QLabel("",this);
searchinput = new QLineEdit();
findprev = new QToolButton(this);
findprev->setArrowType(Qt::UpArrow);
connect(findprev, SIGNAL(clicked()), this, SLOT (clickFindPrev()));
findnext = new QToolButton(this);
findnext->setArrowType(Qt::DownArrow);
connect(findnext, SIGNAL(clicked()), this, SLOT (clickFindNext()));
QStyle *style = qApp->style();
QIcon closeIcon = style->standardIcon(QStyle::SP_TitleBarCloseButton);
QPushButton *closeButton = new QPushButton(this);
closeButton->setIcon(closeIcon);
closeButton->setFlat(true);
connect(closeButton, SIGNAL(clicked()), this, SLOT (clickCloseFind()));
QWidget *bottom = new QWidget;
QHBoxLayout *footer = new QHBoxLayout();
casecheckbox = new QCheckBox(tr("Case sensitive"),this);
footer->setContentsMargins(5,5,5,5);
footer->addWidget(searchlabel);
footer->addSpacing(3);
footer->addWidget(searchinput);
footer->addWidget(findprev);
footer->addWidget(findnext);
footer->addSpacing(10);
footer->addWidget(casecheckbox);
footer->addSpacing(10);
footer->addWidget(resultslabel);
footer->addStretch(1);
footer->addWidget(closeButton);
bottom->setLayout(footer);
//Prepare main layout
layout = new QVBoxLayout;
layout->setContentsMargins(0,0,0,0);
layout->setSpacing(0);
layout->addWidget(toolbar);
layout->addWidget(docs);
layout->addWidget(bottom);
this->setLayout(layout);
this->show();
}
void DocumentationWin::clickCloseFind(){
bottom->hide();
}
.h
class DocumentationWin : public QDialog
{
Q_OBJECT
public:
DocumentationWin(QWidget * parent);
protected:
virtual void keyPressEvent(QKeyEvent *);
private slots:
void clickCloseFind();
private:
QVBoxLayout* layout;
QToolBar* toolbar;
QTextBrowser* docs;
QBoxLayout* footer;
QLabel *searchlabel;
QLabel *resultslabel;
QLineEdit *searchinput;
QToolButton *findprev;
QToolButton *findnext;
QCheckBox *casecheckbox;
QWidget *bottom;
QPushButton *closeButton;
};
Ahh, the classic case of local variables hiding the members. There have been quite a few identical questions on SO about this. This is wrong:
QWidget *bottom = new QWidget;
You want:
bottom = new QWidget;
You'll run into these problems always because you dynamically allocate all the widgets - that's completely unnecessary.
Suggestions:
Hold the child widgets and layouts by value, don't dynamically allocate them.
Don't pass a parent to widgets that are managed by a layout. Every widget that is laid out will be automatically parented.
Don't redundantly call setLayout. A QLayout takes the widget to lay its children on as a constructor argument.
QWidget::hide() is a slot.
Many widgets take the text as a constructor argument.
If you don't have any arguments to pass to the constructor in a new expression, you can drop the parentheses (but we try to avoid these anyway):
searchinput = new QLineEdit; // not QLineEdit();
Widgets shouldn't usually show() themselves upon construction. No Qt widget does that. It's up to the widget's user to do it.
C++ overloads a method call syntax with construction syntax. To differentiate the two, prefer uniform initialization (Type{arg0, arg1, ...}) over old syntax that used ().
Here's how your code can look when you're using C++11. This compiles with either Qt 4 or Qt 5. If you don't target Qt 4, you should be using the new connect syntax, though.
As you can see, there isn't a single explicit dynamic allocation - that's how quite a bit of C++11 code will look, when the used types are sane.
// https://github.com/KubaO/stackoverflown/tree/master/questions/find-hide-38082794
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
class DocumentationWin : public QDialog
{
Q_OBJECT
public:
explicit DocumentationWin(QWidget * parent = 0);
private:
QVBoxLayout layout{this};
QToolBar toolbar;
QTextBrowser docs;
QWidget bottom;
QHBoxLayout footer{&bottom};
QLabel searchlabel{tr("Find in page:")};
QLabel resultslabel;
QLineEdit searchinput;
QToolButton findprev;
QToolButton findnext;
QCheckBox casecheckbox{tr("Case sensitive")};
QPushButton closeButton;
Q_SLOT void onFindPrev() {}
Q_SLOT void onFindNext() {}
};
DocumentationWin::DocumentationWin(QWidget * parent) : QDialog(parent) {
findprev.setArrowType(Qt::UpArrow);
connect(&findprev, SIGNAL(clicked()), this, SLOT(onFindPrev()));
findnext.setArrowType(Qt::DownArrow);
connect(&findnext, SIGNAL(clicked()), this, SLOT(onFindNext()));
auto style = qApp->style();
auto closeIcon = style->standardIcon(QStyle::SP_TitleBarCloseButton);
closeButton.setIcon(closeIcon);
closeButton.setFlat(true);
connect(&closeButton, SIGNAL(clicked(bool)), &bottom, SLOT(hide()));
footer.setContentsMargins(5,5,5,5);
footer.addWidget(&searchlabel);
footer.addSpacing(3);
footer.addWidget(&searchinput);
footer.addWidget(&findprev);
footer.addWidget(&findnext);
footer.addSpacing(10);
footer.addWidget(&casecheckbox);
footer.addSpacing(10);
footer.addWidget(&resultslabel);
footer.addStretch(1);
footer.addWidget(&closeButton);
layout.setContentsMargins(0,0,0,0);
layout.setSpacing(0);
layout.addWidget(&toolbar);
layout.addWidget(&docs);
layout.addWidget(&bottom);
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
DocumentationWin win;
win.show();
return app.exec();
}
#include "main.moc"

Qt4 application crash, segmentaton fault when click connected button

I've created a simple application with QGraphicsView and I have a problem with connected button.
There is a simple window with QGraphicsScene and one QPushButton and a function which should add a rectangle to my scene. Compilation is ok, it works and after I click this button application crash.
.h file:
class Canvas : public QWidget{
Q_OBJECT
public:
Canvas(QWidget *parent = 0);
private slots:
void addPoint();
private:
QGraphicsScene *scene;
QPushButton *btn;
};
.cpp file:
Canvas::Canvas(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setSpacing(1);
QPushButton *btn = new QPushButton("test", this);
QGraphicsView *view = new QGraphicsView(this);
QGraphicsScene *scene = new QGraphicsScene(this);
view->setScene(scene);
vbox->addWidget(view);
vbox->addWidget(btn);
setLayout(vbox);
connect(btn, SIGNAL(clicked()), this, SLOT(addPoint()));
}
void Canvas::addPoint()
{
scene->addRect(100,0,80,100);
}
Also debuger said:
The inferior stopped because it received a signal from the Operating System.
Signal name : SIGSEGV
Signal meaning : Segmentation fault
And points this line:
{ return addRect(QRectF(x, y, w, h), pen, brush); }
What am I doing wrong? Thanks in advance.
The following statement in your constructor is a local variable definition and initialization:
QGraphicsScene *scene = new QGraphicsScene(this);
The actual scene member variable is never initialized and anything that tries to use this->scene will crash the application.
As you want to initialize the existing scene variable, you should omit the type in front of the variable:
scene = new QGraphicsScene(this);

How to add a Menubar to a QWidget?

I am currently writing a C++ Application using the Qt framework, in which the 'main window' inherits from the QWidget class:
class Draughts : public QWidget
{
Q_OBJECT
public:
explicit Draughts(QWidget *parent = 0);
~Draughts();
private:
Ui::Draughts *ui;
};
And I attempted to add a simple menu bar to the application, using the following code:
Draughts::Draughts(QWidget *parent) :
QWidget(parent),
ui(new Ui::Draughts)
{
ui->setupUi(this);
QWidget *menuWidget = new QWidget;
QMenu *menuGame = new QMenu("Game");
menuGame->addAction("New");
menuGame->addAction("Exit");
QMenu *menuHelp = new QMenu("Help");
menuHelp->addAction("How to Play...");
menuHelp->addAction("About");
//Setup the Application Menu
QMenuBar mainMenu(this);
mainMenu.addMenu(menuGame);
mainMenu.addMenu(menuHelp);
}
Should I be using the QMainWindow class instead of the QWidget class for my application?
It would be easier to use QMainWindow, because it provides a convenient menuBar() method:
QMenuBar* mainMenu = this->menuBar();
But it is possible to add it to QWidget, just as any other widget. Just don't allocate it in the local scope, because it will be deleted after the function finishes. Instead, do it like with other widgets:
QMenuBar mainMenu = new QMenuBar(this);
You should also probably add a layout to your widget, and add the menu to the layout to have more control over where does it appear. You may find this tutorial useful.

Do I have to delete these pointers?

This is the MainWindow class which I call and use the function show() to make it visible to the user.
class MainWindow : public QMainWindow
{
Q_OBJECT
QWidget *centralWidget;
QGridLayout* gridLayout;
QGridLayout* infoBoxLayout;
QHBoxLayout* buttonGroup;
QHBoxLayout* subCategoryLayout;
//... more widgets
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
void setupUi();
void setupConnections();
private slots:
void add();
void edit();
void remove();
void find();
void clearAll();
void screenshotDesktop();
void screenshotApp();
void currentSubCategoryChanged( const QString& );
void curretCategoryChanged( const int );
void keyPressEvent( QKeyEvent * );
};
I created for each widget (those pointers after the macro Q_OBJECT) a new object on the heap with new. However, I did not delete them anywhere in the program. Does this cause a memory leak in Qt? Or does something from Qt delete them automatically when destroying the class?
If a widget has a parent set, then Qt will handle deleting the widget.
In the case of a MainWindow, when you close it, the MainWindow and its children will be cleaned up, so pass the parent to the widget's constructor: -
QHBoxLayout* buttonGroup = new QHBoxLayout(this); // where this is the parent (MainWindow)
If you create a Widget such as this: -
QHBoxLayout* buttonGroup = new QHBoxLayout;
And haven't passed in the parent, then it will not be cleaned up and you'll have to handle that yourself.
if you add them to the gui hierarchy then they will be cleaned up when the MainWindow is deleted
this is because parents assume ownership over their children (which is set with the various adds of the gui)
so a this->add(centralWidget); will call centralWidget->setParent(this); which will let centralWidget be deleted when MainWindow is deleted
you are free to delete QObjects yourself but beware dangling pointers (QPointer will help here). though I suggest using deleteLater() to ensure no strange behavior when a pointer still lives on the stack.
for more info about the object tree see here
The automatic memory management through parent-child relationships is done by the QObject. QWidget happens to be a QObject, and it so happens that widgets that have parent widgets have the same underlying QObjects as parents.
A QObject with children automatically deletes its children in its destructor.
A QObject or a QWidget may be adopted by another object. For example, adding widgets to a layout will automatically reparent them to the widget the layout is set on. Even if a layout doesn't have a widget set on it yet, the reparenting will be done at the time you add the layout to a widget (or to a layout that has a widget set). It's pretty clever and saves a lot of typing and reduces possibilities for mistakes.
The idiomatic, minimum-typing way of adding widgets to another widget is:
MyWidget() {
QLayout * layout = new QHBoxLayout(this); // set a layout on this widget
layout->addWidget(new QLabel("foo")); // the label is reparented to this
layout->addWidget(new QPushButton("bar")); // the button is reparented to this
}