Memory Management in Qt - c++

I have small doubt about Qt memory management.
Let's take an example of Listview, in listview we add each item by allocating memory dynamically. So in this case do we need to delete all the "new"ed items manually.
E.g:
Qlistview *list = new Qlistview;
QStandardItemModel *mModel = new QStandardItemModel();
list ->setModel(mModel);
for(int I =0;i<10;i++)
{
QsandardItem *item = new QsandardItem(“Hi”);
mModel->appendRow(item);
}
In this example, should item be deleted manually?

QStandardItemModel takes ownership of items, so they will be automatically deleted when model is destroyed. You still need to delete the model itself (setModel() doesn't transfer ownership of model to the view, because one model can be used by multiple views).

Agree with chalup's answer , the answer for your question is:
if you called mModel->clear();, it will help you delele all of those items, you don't need to mannually delete items one by one, what's more if you want to totally delete model, you should called delete mModel;.
Run the example code provided by ChrisW67 here, you will have a better understanding:
#include <QCoreApplication>
#include <QDebug>
#include <QStandardItemModel>
class MyItem: public QStandardItem
{
public:
MyItem(const QString & text): QStandardItem(text) {
qDebug() << "Item created" << this;
}
~MyItem() {
qDebug() << "Item destroyed" << this;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QStandardItemModel* model = new QStandardItemModel;
for (int row = 0; row < 4; ++row) {
for (int column = 0; column < 4; ++column) {
QStandardItem *item =
new MyItem(QString("row %0, column %1").arg(row).arg(column));
model->setItem(row, column, item);
}
}
qDebug() << "Finished making model";
model->clear();
qDebug() << "Model cleared";
qDebug() << "===================";
QStandardItem *newitem1 = new MyItem(QString("new item"));
qDebug()<<"create new item at"<<newitem1;
QStandardItem *newitem2 = new MyItem(QString("new item"));
QStandardItem *newitem3 = new MyItem(QString("new item"));
QStandardItem *newitem4 = new MyItem(QString("new item"));
//because we didn't delete model so far, we can still append items to model.
model->appendRow({newitem1,newitem2,newitem3,newitem4});
model->clear();
qDebug() << "Model cleared again";
//although the memoty of newitem1 has already been deallocated, but the pointer still point to that address, now newitem1 is a dangling pointer
if(newitem1){
qDebug()<<"newitem1 address is"<<newitem1;
}
else{
qDebug()<<"newitem1 address in null";
}
// delete newitem1;//this will cause program crash because newitem1 acutally has been delete, double delete should not be allowed
newitem1 = nullptr;//Instead of delete, wo could set newitem1 pointet to null, this can avoid wild pinter/dangling pointer
delete model;
qDebug()<<"deleted model";
return a.exec();
I am learing c++ too, if there is something wrong above, please tell me, i will correct it.

Related

Initialising a pointer to QListWidgetItem Qt 5.8

In my program I have a series of tabs and on each tab there is a combobox and a QListWidget. Based on the selection on the combobox the QListWidget will be populated.
Now what I am trying to achieve is that one the user presses a "APPLY" button after selecting a series of items on the "checkable" list widget for a given selection in combobox, I will read the bool status of each item on the list widget by using a pointer QListWidgetItem pointer to the list widget
This is part of my code;
void MainWindow::on_applyButton_clicked()
{
//Reset list
MainWindow::revenueList.clear();
//Processing income statement
//Select the first item on inc_st_combo
ui->inc_st_comb->setCurrentText("Revenue Growth");
//Create an iterator
QStringListIterator iter(MainWindow::revenue_labels);
//Loop through the list widget and read bool status
while(iter.hasNext())
{
//Index for the QList
int index = 0;
//Create a QListWidgetItem pointer
QListWidgetItem *listItem = new QListWidgetItem(iter.next(),Ui_MainWindow::inc_st_list);
bool status = listItem->checkState();
qDebug() << "Status: " << status << endl;
MainWindow::revenueList.append(status);
}
qDebug() << "List: " << MainWindow::revenueList << endl;
}
My problem is that when I try to initialise the QLsitWidgetItem on the following line;
QListWidgetItem *listItem = new QListWidgetItem(iter.next(),Ui_MainWindow::inc_st_list);
Qt return the following error;
/Users/Vino/Documents/My Stuff/Qt Projects/Fundemental Analysis/FundementalAnalysis/mainwindow.cpp:389: error: invalid use of non-static data member 'inc_st_list'
QListWidgetItem *listItem = new QListWidgetItem(iter.next(),Ui_MainWindow::inc_st_list);
~~~~~~~~~~~~~~~^~~~~~~~~~~
How do I initialise the QListWidgetItem pointer to point at a particular listWidget on the form?
If you want a pointer to an already existing object you won't use new, you need to assign it the address of the existing object:
int pos = 0; //determine the right position
QListWidgetItem *listItem = ui->inc_st_list->item(pos);

Dynamically & sequentially naming & instantiating widgets qt

I'm creating a new instance of QTableWidgetItem for each row the user may add, and adding it to a QVector of QTableWidgetItems.
I'd like to do soemthing like the following to name each instance in the following iteration with the row number included in the instance name:
QVector<QCheckBox> *checkBox_array;
for(int r=0;r<user_input;r++)
{
ui->tableWidget->insertRow(r);
*checkBox%1.arg(r) = new QCheckBox; //create an instance "checkBox1" here
checkBox_array->pushBack(checkBox%1.arg(r))
}
or something like the following, which does not compile in its current state:
for(int r=0;r<7;r++)
{
ui->tableWidget->insertRow(r);
checkBox_array->push_back();
checkBox_array[r] = new QCheckBox;
ui->tableWidget->setCellWidget(r,2,checkBox_array[r]);
}
is this possible? How can I work around this issue? All I need is to get the new widgets into the array without having to name them explicitly. Thanks in advance!
Thanks in advance.
Try something like this:
for(int r=0;r<7;r++)
{
ui->tableWidget->insertRow(r);
ui->tableWidget->setCellWidget(r,2,new QCheckBox(QString("checkBox%1").arg(r)));
}
It creates some widgets.
When you want change something in this widget or get data then use cellWidget() method, but don't forget cast it! For example:
for(int r=0;r<7;r++)
{
QCheckBox* curBox = qobject_cast<QCheckBox*>(ui->tableWidget->cellWidget(r,2));
if(curBox)
{
qDebug() << curBox->text() << curBox->isChecked();
curBox->setText("This is new Text");
}
else
qDebug() << "fail";
}

Qt - Access a checkbox in a table

I have a table and each row in the table has a checkbox in it's first column. I need to make it so I can detect which checkboxes are checked and delete those rows when a button is pressed.
QWidget * chkWidget = new QWidget();
QHBoxLayout *center = new QHBoxLayout();
center->setAlignment( Qt::AlignCenter );
center->addWidget( new QCheckBox );
chkWidget->setLayout( center );
ui->data_table->setCellWidget(rowCount,0, chkWidget);
Was this done right? If so how do I access the checkboxes at each row?
I talk about a QTableWidget. You can use a QList.You save your QCheckBox into this QList and use it, when there is some change
Maybe you should check out the documentation
QTableWidget: Link
QList: Link
Here is a solution. I cannot run it at the moment, so please tell me if it works. Please validate the row value. I am not sure if it's possible, that row can have the value -1 when you delete the last row ;)
#include "TestTableWidget.h"
#include "ui_TestTableWidget.h"
TestTableWidget::TestTableWidget(QWidget *parent) : QMainWindow(parent), ui(new Ui::TestTableWidget)
{
ui->setupUi(this);
tableWidget = new QTableWidget(this);
tableWidget->setColumnCount(1); // Just an example
ui->gridLayout->addWidget(tableWidget);
connect(tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotChange()));
for(int i = 1; i < 10; i++)
{
addRow("Row " + QString::number(i));
}
}
TestTableWidget::~TestTableWidget()
{
delete ui;
}
void TestTableWidget::addRow(QString text)
{
int row = tableWidget->rowCount();
qDebug() << "Current row count is " + QString::number(row);
// Add new one
QTableWidgetItem *item = new QTableWidgetItem(text);
tableWidget->insertRow(row);
tableWidget->setItem(row, 0, item);
// Add item to our list
}
void TestTableWidget::slotChange()
{
int row = tableWidget->currentRow();
qDebug() << "Change in table. Current row-index: " + QString::number(row);
// This value is zero-based, so you can use it in our list
}

Empty scrollArea QT C++

I am loading data from xml executed by timer. xml is parsed and populated in entity object.
In a loop im taking the data from entity object and populate QCommandLinkButton's. and in the end a batch of buttons are set into the verticalLayout and then in scrollArea.
but every time data is loaded its appends to the old data. How can I empty the content of the srollArea before repopulating scrollArea.
MainWindow::methodExecudedByTimer(){
foreach(int i, map.keys()){
QCommandLinkButton* buttonEmail = new QCommandLinkButton(this);
Email em = map[i];
buttonEmail->setText(em.__toString());
ui->verticalLayout->addWidget(buttonEmail);
}
ui->scrollArea->setLayout(ui->verticalLayout);
}
you can use setWidget replace setLayout.and then new data coming,you can call takeWidget to remove old data.
MainWindow::methodExecudedByTimer(){
foreach(int i, map.keys()){
QCommandLinkButton* buttonEmail = new QCommandLinkButton(this);
Email em = map[i];
buttonEmail->setText(em.__toString());
ui->verticalLayout->addWidget(buttonEmail);
}
ui->scrollArea->takeWidget();
QWidget *widget = new QWidget();
QSize size = ui->scrollArea->size();
widget->setMinimumSize(size.width(),size.height());
widget->setLayout(ui->verticalLayout);
ui->scrollArea->setWidget(widget);
}
I found the answer in the depths of Internet.
before foreach i added this:
qDebug() << "check if layout filled";
if(ui->verticalLayout->count() > 0){
qDebug() << "empty layout";
QLayoutItem *item = NULL;
while ((item = ui->verticalLayout->takeAt(0)) != 0) {
delete item->widget();
}
}else{
// on app first run
qDebug() << "layout empty";
}

Removing QWidgets from a QGridLayout

I have a QGridLayout where I add my custom QWidgets.
When I try to delete all of them they are supposedly removed from the layout (as the function layout.count() returns 0) but they are still shown in the interface and I can interact with them.
Here you have the way I add widgets:
void MyClass::addCustomWidget(CustomWidget *_widget, int r, int c)
{
layout->addWidget(_widget, r, c);
_widget->show();
}
And here the way I delete them:
void MyClass::clearLayout()
{
qDebug() << "Layout count before clearing it: " << layout->count();
int count = layout->count();
int colums = layout->columnCount();
int rows = layout->rowCount();
int i=0;
for(int j=0; j<rows; j++)
{
for(int k=0; k<colums && i<count; k++)
{
i++;
qDebug() << "Removing item at: " << j << "," << k;
QLayoutItem* item = layout->itemAtPosition(j, k);
if (!item) continue;
if (item->widget()) {
layout->removeWidget(item->widget());
} else {
layout->removeItem(item);
}
qDebug() << "Removed!";
}
}
qDebug() << "Layout count after clearing it: " << layout->count();
}
Any kind of help or tip to delete items/widgets correctly from a QGridLayout?
P.D. : I have seen on the internet that a lot of people deletes the widget directly (delete _widget) after removing them from the layout. In my case it is not possible as I need to mantain that widgets in memory.
You can also use deleteLater() to avoid issue with maintaining child
count during iterations:
for (int i = 0; i < gridLayout.count(); i++)
{
gridLayout.itemAt(i)->widget()->deleteLater();
}
Just to be clear. You didn't "delete" the widgets. You only removed them from layout. Removing from layout means only that widget will be no more managed (resized/positioned) by this layout BUT it doesn't mean that widget will be "deleted" (in C++ way). Also widget won't be magically hidden. Your widget after removing from layout still leaves in widget it was created / managed in. So owner of this layout still has this widget as child (visible child).
You have to
hide widget or if you're sure it will not be used anymore
delete widget with "delete" keyword
Also you don't need to call removeWidget(item->widget()); removeItem(item) will be enough for all layout items (even those with widget inside)
Try
QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0);
It is supposed to be safe. If for any reasons it doesn't work, you can use a collection of widgets or layoutitems, which is updated every time you add a widget. Then to delete you loop on the collection and remove each element from the layout.
Header:
class Grid : QGridLayout
{
public:
Grid(QWidget* parent);
void addElement(QWidget* element, int x, int y);
void delElement(int x, int y);
void resize(int width, int height);
void clear();
protected:
QSize size;
};
void Grid::clear(){
for(int i = 0;i<size.width();i++){
for(int j = 0;j<size.height();j++){
QLayoutItem* item = takeAt(i*size.width() + j);
if(item != NULL) delete item;
}
}
}
None of these answers worked for me. In my situation, I have several object each with their own QChartView. The idea is that the user selects which object they want to view and a common area central in the main window is updated with the QChartView of the user-selected object. This should have been straightforward, simply remove the widget from the chart area, add a new one. What ended up working for me was this:
while( QLayoutItem* item = ui->mPlotArea->layout()->takeAt(0) )
{
item->widget()->setVisible(false);
ui->mPlotArea->layout()->removeItem(item);
}
ui->mPlotArea->layout()->addWidget( pv );
pv->setVisible(true);
Where mPlotArea is a QFrame and pv is a derived class of QChartView. I can't explain why the other answers did not worked, but I spent a couple of hours trying different widgets and different ways of removing without deleting, organizing, etc...