I know there are a lot of questions regarding qt memory management but i couldn't find an answer on mine.
So for the qt widgets, if you pass this(which is the parent widget) as parameter at the object creation, the parent widget takes ownership of it and deletes it when the parent dies.
The same thing happens when you add a widget to a layout and set that layout on the widget.
But what happens if i declare a pointer to a QColor object for example? QColor can't be added as child for another widget or can't be added to a layout. Will the memory be released when the widget dies or i need to delete it manually in the destructor?
Here an example...what happens with m_pColor when Widget will be destroyed? Will it be destroyed or there will be a memory leak?
Sorry for any mistakes in the code, i didn't compile it, i just wrote it here as example.
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class QHBoxLayout;
class QPushButton;
class QColor;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget( QWidget *parent = NULL );
public:
QHBoxLayout *m_pLayout;
QPushButton *m_pButton;
QColor *m_pColor;
};
#endif // WIDGET_H
#include "widget.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QColor>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_pLayout = new QHBoxLayout;
m_pButton = new QPushButton( tr( "Button" ) );
m_pLayout->addWidget( m_pButton );
m_pColor = new QColor(0, 0, 0, 255);
setLayout( m_pLayout );
}
What you suggested is valid only for classes inheriting QObject (ie having QObject as one of their superclass). These properties don't apply to other classes (even built-inQt ). As QColor not a subclass of QObject, the object referenced by m_pColor will not be destroyed when Widget is destroyed. You will have to do it manually.
There is no reference for parent widget to destroy the m_pColor. you can use Valgrind to check for memory leak on this executable.
Related
I have been using QGraphicsScene and QGraphicsVideoItem as my canvas. And to control them, I've chosen to use qml and QQuickWidget to develop custom objects easily for a different module. However, I quickly ran into an issue where the QGraphicsVideoItem would not render in the QGraphicsScene but rather inside the QQuickWidget (both when the widget is empty or with a qml source attached). And the issue seems to be reproducible using a fresh project as well just by placing an empty QQuickWidget(through qt designer) anywhere inside the main ui.
Here is the reproducible code:
#include "QtGuiApplication1.h"
QtGuiApplication1::QtGuiApplication1(QWidget *parent): QMainWindow(parent)
{
ui.setupUi(this);
QGraphicsView* view = new QGraphicsView(ui.widget);
QGraphicsScene* scene = new QGraphicsScene();
QGraphicsVideoItem* video = new QGraphicsVideoItem();
QMediaPlayer* player = new QMediaPlayer();
QUrl path = QUrl::fromLocalFile("D:/My Documents/Videos/XIII.mp4");
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(view);
ui.widget->setLayout(layout);
video->setFlags(QGraphicsVideoItem::ItemIsMovable | QGraphicsVideoItem::ItemIsFocusable | QGraphicsVideoItem::ItemIsSelectable);
video->setPos(100, 100);
//view->setSceneRect(QRectF(QPointF(100, 100), QPointF(800, 600)));
view->setScene(scene);
player->setMedia(path);
player->setVideoOutput(video);
scene->addItem(video);
player->play();
view->show();
}
#pragma once
#include "ui_QtGuiApplication1.h"
#include <QtCore>
#include <QDebug>
#include <QGraphicsVideoItem>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QMediaPlayer>
#include <QUrl>
#include <QString>
class QtGuiApplication1 : public QMainWindow
{
Q_OBJECT
public:
QtGuiApplication1(QWidget *parent = Q_NULLPTR);
private:
Ui::QtGuiApplication1Class ui;
};
The issue immediately went away when I removed the widget from the ui file as well. So am I missing something here?
When you call ui.widget->setLayout(layout); you break the layout that was set in Qt Designer.
Instead of programmatically creating the QGraphicsView and QVBoxLayout in your *.cpp file, add them all in Qt Designer.
(If the issue still persists, please edit your original post and include your *.ui file)
I want to display the content I wrote in the QLineEdit widget after clicking on the QPushButton with the function ShowMessage(). How can I access that content outside of the constructor?
Tried putting the QLineEdit object I created in to a private variable.
My CPP file
#include "manualwidget.h"
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QMessageBox>
ManualWidget::ManualWidget(QWidget *parent) : QWidget(parent)
{
QLabel *label = new QLabel(this);
QLineEdit *lineEdit = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
QHBoxLayout *layout = new QHBoxLayout();
label->setText("Enter text:");
pushButton->setText("Ok");
layout->addWidget(label);
layout->addWidget(lineEdit);
layout->addWidget(pushButton);
setLayout(layout);
connect(pushButton,SIGNAL(clicked()),this ,SLOT(showMessage()));
connect(lineEdit, SIGNAL(returnPressed()),this, SLOT(showMessage()));
}
void ManualWidget::showMessage(){
QMessageBox::information(this, "Message", "The text entered in the "
"manual widget window is:\n" + m_lineEdit->text());
}
My header file
#ifndef MANUALWIDGET_H
#define MANUALWIDGET_H
#include <QWidget>
#include <QLineEdit>
class ManualWidget : public QWidget
{
Q_OBJECT
public:
explicit ManualWidget(QWidget *parent = nullptr);
signals:
public slots:
private slots:
void showMessage();
private:
QLineEdit m_lineEdit;
};
#endif // MANUALWIDGET_H
#eyllanesc suggestion might work, but it should not be a prefered approach. Qt has its' own memory model, and usage of it should be prefered. Thus, "QLineEdit m_lineEdit" should be changed to e.g. "QLineEdit* m_lineEdit", and in constructor you should initialize it in the following way:
// Instance of the QLineEdit will be owned by the ManualWidget which is part of Qt memory management now.
m_lineEdit = new QLineEdit(this);
Then, the following line:
layout->addWidget(lineEdit);
Can be changed to:
layout->addWidget(m_lineEdit);
Why is it bad to use "QLineEdit m_lineEdit"? Because Qt might want to destroy this object for some reason (you still can call m_lineEdit.deleteLater()) and you might end up in the "double destruction" situation which would result in app being crashed. You can say that in this way conflicting memory models would interact.
I am a QT and C++ newbie and I am trying to bind QTreeView to a QStandardItemModel. The code below compiles, but the widget doesn't get populated with the childItem. Anyone know why?
#include <QStandardItemModel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QTreeView *qtree = ui->treeView;
QStandardItemModel model;
QStandardItem *item = model.invisibleRootItem();
QStandardItem *childItem = new QStandardItem(QString("child item"));
item->appendRow(childItem);
qtree->setModel( &model );
}
You create the instance of the model, and then promptly destroy it when the MainWindow constructor returns. Your model is a local variable!
You want the model to be a value member in the widget. I also suggest to disregard the silly Qt Creator template code and not hold the ui member by pointer, but directly by value. The extra pointer indirection is pointless and a premature pessimization. It made sense 15 years ago when compilers and disks were 1-2 orders of magnitude slower than they are now.
// mainwindow.h
#include <QMainWindow>
#include "ui_mainwindow.h"
class MainWindow : public QMainWindow {
Ui::MainWindow ui; // by value!
QStandardItemModel model; // by value!
public:
explicit MainWindow(QWidget * parent == nullptr);
// Let the compiler do the hard work: the default destructor is just fine!
}
// mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget * parent) :
QMainWindow{parent}
{
ui.setupUi(this);
auto root = model.invisibleRootItem();
root->appendRow(new QStandardItem{QStringLiteral("child item")});
ui.treeView->setModel(&model);
}
The code in my posting doesn't work because the model variable is destroyed when the constructor of the MainWindow class returns.
I am a newbie in Qt-programming. I have read a book about GUI-programming with Qt. I have an trouble in creating a dialog. Here is sample code:
// gotocell.h
#ifndef GOTOCELL_H
#define GOTOCELL_H
#include <QDialog>
#include <QtWidgets>
#include "ui_gotocell.h"
class GoToCellDialog : public QDialog, public Ui::GoToCellDialog
{
Q_OBJECT
public:
GoToCellDialog (QWidget *parent = 0);
private slots:
void on_lineEdit_textChanged();
};
#endif // GOTOCELL_H
// gotocell.cpp
#include <QtWidgets>
#include "gotocell.h"
#include <QtWidgets>
GoToCellDialog::GoToCellDialog (QWidget *parent):
QDialog (parent)
{
setupUi(this);
QRegExp regExp ("[A-Za-z][1-9][0-9]{0,2}");
lineEdit->setValidator(new QRegExpValidator(regExp, this));
connect (okButton, SIGNAL(clicked()), this, SLOT(accept()));
connect (cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
}
void GoToCellDialog::on_lineEdit_textChanged()
{
okButton->setEnabled(lineEdit->hasAcceptableInput());
}
// main.cpp
#include "gotocell.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GoToCellDialog *dialog = new GoToCellDialog;
dialog->show();
return a.exec();
}
but when I compiled, there is an error: no known conversion for argument 1 from 'GoToCellDialog* const' to 'QMainWindow*'at setupUi() function. I think because the designer in Qt Creator created a QMainWindow, not a QDialog. So I changed GoToCellDialog class to QMainWindow. But there is no slots whose name is "accepted", "rejected" in QMainWindow. Can anyone help me?
If you want to display a Dialog as main window you have two choices:
1. make the whole main window QDialog based
2. design the Dialog separately and set it as the main windows central Widget (QMainWindow->setCentralWidget()).
In both cases you still have the problem what semantics you give to the OK and Cancel buttons. Generally it may be a better idea to consider what the main window of the application should contain, and design the dialogs later.
I have built an app in Qt that contains two buttons: an exit button and an import button. When the import button is pushed, a list of buttons is shown in a scrollarea on the screen (the file loggers.csv contains the data 1;2;3;4;5;).
It all works fine, but when I push the exit button (which of course should close everything), the app is not stopped properly (the stop button of Qt is still active, and the play button isn't). When I run the debugger and push the exit button it gives an error: Invalid address specified to RtlFreeHeap( 0ADF0000, 0028FE40 ). Can anybody help me?
main
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.showFullScreen();
return a.exec();
}
Mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtGui>
#include "logger.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QPushButton exit_btn;
QPushButton import_btn;
private slots:
void createMenus();
void exit();
void import();
private:
int window_width;
int window_height;
int numLoggers;
int numSelected;
QVector<Logger*> loggers;
QScrollArea * scroll_area;
QVBoxLayout scrollLayout;
QWidget viewport;
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QtGui"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
window_width = QApplication::desktop()->width();
window_height = QApplication::desktop()->height();
createMenus();
connect(&exit_btn,SIGNAL(clicked()),this,SLOT(exit()));
connect(&import_btn,SIGNAL(clicked()),this,SLOT(import()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::createMenus()
{
import_btn.setParent(ui->centralWidget);
import_btn.setGeometry(400,300,100,100);
import_btn.setText("IMPORT");
exit_btn.setText("EXIT");
exit_btn.setParent(ui->centralWidget);
exit_btn.setGeometry(window_width-50,12,32,32);
viewport.setLayout(&scrollLayout);
viewport.resize(0,0);
scroll_area = new QScrollArea(ui->centralWidget);
scroll_area->setGeometry(0,66,317,window_height-116);
scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setWidget(&viewport);
scroll_area->setGeometry(0,97,317,window_height-228);
scrollLayout.setMargin(0);
scrollLayout.setSpacing(0);
}
void MainWindow::exit()
{
close();
qApp->quit();
}
void MainWindow::import()
{
numSelected=0;
QFile f("Loggers3.csv");
if (f.open(QIODevice::ReadOnly))
{
numLoggers=0;
QString data;
data = f.readAll();
QStringList vals = data.split(';');
while(vals.size()>=1)
{
Logger * logger = new Logger;
logger->setNumber(vals[0].toInt());
vals.removeAt(0);
loggers<<logger;
numLoggers++;
}
f.close();
for(int i=0; i<numLoggers;i++)
{
loggers[i]->createButtons();
scrollLayout.addWidget(loggers[i]->button);
}
viewport.resize(367,numLoggers*60);
}
}
logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <QtGui>
class Logger : public QWidget
{
Q_OBJECT
public:
explicit Logger(QWidget *parent = 0);
~Logger();
int number;
QLabel num;
QToolButton * button;
bool checked;
signals:
public slots:
void setNumber(int number);
void createButtons();
};
#endif // LOGGER_H
logger.cpp
#include "logger.h"
#include <QtGui>
Logger::Logger(QWidget *parent) :
QWidget(parent)
{
button = new QToolButton;
button->setCheckable(true);
button->setMinimumSize(317,60);
button->setStyleSheet("QToolButton{background-image: url(images/btn_bg); border:none}");
}
Logger::~Logger()
{
}
void Logger::setNumber(int logNumber)
{
number=logNumber;
}
void Logger::createButtons()
{
QLayout * layout = new QHBoxLayout;
QSpacerItem *spacer = new QSpacerItem(120, 31, QSizePolicy::Maximum, SizePolicy::Maximum);
num.setStyleSheet("color: white; font: bold 16px");
num.setText(QString::number(number));
layout->addWidget(&num);
layout->addItem(spacer);
button->setLayout(layout);
}
I'm not entirely certain about what you are trying to achieve... but your problem lies with these two lines:
viewport.setLayout(&scrollLayout);
viewport.resize(0,0);
In the documentation for the QWidget class it states that:
If there already is a layout manager installed on this widget, QWidget
won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.
This is where your problem lies. Don't believe me, add this check before those two lines of code.
if(layout()){
qDebug() << "Another layout exists";
}
Source: QVBoxLayout Class Reference
The QVBoxLayout class lines up widgets vertically.
This class is used to construct vertical box layout objects. See QBoxLayout for details.
The simplest use of the class is like this:
QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
window->show();
First, we create the widgets we want in the layout. Then, we create the QVBoxLayout object and add the widgets into the layout. Finally, we call QWidget::setLayout() to install the QVBoxLayout object onto the widget. At that point, the widgets in the layout are reparented to have window as their parent.
Critical source of error in your project:
Widgets should be constructed on the heap because they will be deleted automatically when their parents are deleted. You have a custom widget class that you instantiate on the heap. The members should also go on the heap. Also, you should consider using the parent /child hierarchy in your GUI code to ensure proper memory management and proper deletion.
In my experience, if your program stops in RtlFreeHeap it is a good sign of memory corruption.
When calling
import_btn.setParent(ui->centralWidget);
centralWidget takes ownership of import_btn. That means, when centralWidget is deleted (which happens as part of delete ui;in your MainWindow's destructor), it will call delete on your member variable!
This leads to the reported memory corruption.
You need to allocate your QPushButton's dynamically, not as a plain member variable. So make them QPushButton*.
Here's how I did it from mainwindow.cpp, thanks to and this question: How to create a correct exit button in qt
QPushButton * quit_btn = new QPushButton(this);
quit_btn->setGeometry(540,440,93,27);
quit_btn->setText("Exit");
QObject::connect(quit_btn,SIGNAL(clicked()),qApp,SLOT(quit()));
Works flawlessly :D