In Qt I understand that if you create an object that inherits from QObject and set it's parent, then the parent is responsible for deleting the children. I also understand that if you create objects on the stack that they are deleted when they go out of scope.
I'm not wrapping my head around the case where I want to explicitly delete items in a QStringList ( or any QList ) and then the list itself.
Consider if I create a QStringList and populate it like below:
QStringList multiPartPortionList;
multiPartPortionList = multiPartPortion.split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
Then I do stuff with it and call this method in attempt to explicitly delete the items contained within the list and then the list itself:
void MyClass::deleteList(QStringList list) {
// Now cleanup
QStringList *delList = new QStringList(list);
int numObjects = delList->count();
for (int i=0; i < numObjects; i++)
delete (new QString(list.takeAt(0)));
delList->clear();
delete delList;
delList = 0;
}
I can't call qDeleteall(list) because it expects a pointer, and I also can't do just do:
delete list;
Since it errors with 'argument given to 'delete', expected pointer', expecting a pointer.
I get segmentation faults with:
void MyClass::deleteList(QStringList list) {
// Now cleanup
QStringList *delList = &list; // <--- Seg fault with this
int numObjects = delList->count();
for (int i=0; i < numObjects; i++)
delete (new QString(list.takeAt(0)));
delList->clear();
delete delList;
delList = 0;
}
I also get segmentation faults with:
void MyClass::deleteList(QStringList list) {
// Now cleanup
QStringList *delList = &list;
int numObjects = delList->count();
for (int i=0; i < numObjects; i++)
delete &(list.takeAt(0)); // <--- Seg fault with this
delList->clear();
delete delList;
delList = 0;
}
And then I also get segmentation faults with:
void MyClass::deleteList(QStringList list) {
// Now cleanup
QStringList *delList = new QStringList(list);
int numObjects = delList->count();
for (int i=0; i < numObjects; i++)
delete (new QString(list.takeAt(0)));
delList->clear();
delete delList;
delList = 0;
delete &list; // <--- Seg fault with this
}
I don't think this is quite, or even close, to being right. How can I best achieve what I want - that is to explicitly delete all items in a list and then the list itself?
Your QStringList list gets deleted automagically at the end of the function. No need to worry about deleting pointers here.
In the same manner,
QStringList *delList = new QStringList(list);
should be
QStringList delList = QStringList(list);
better yet:
QStringList delList = list;
You should read about automatic storage and RAII. In your code, new and delete are superfluous.
To remove an element in the list, use removeAt. This has nothing to do with deleting.
I also suggest that you get a good book before continuing in C++.
Related
Can I somehow attach one QTreeWidgetItem to two (or more) nodes at once? Just like this:
parent1
└child1
parent2
└child1
If I just do addChild() on both parents, the child appears only on the first parent. Is that even possible? Or such result can be achieved only by completely copying of descendant?
The instructions say:
If the child has already been inserted somewhere else it won't be
inserted again.
This means that only option is to copy the child completely. Roughly so:
QTreeWidgetItem* copy(QTreeWidgetItem* item)
{
if (item == nullptr)
{
return nullptr;
}
QTreeWidgetItem* out = new QTreeWidgetItem(*item);
for (int i = 0; i < item->childCount(); i++)
{
out->addChild(copy(item->child(i)));
}
return out;
}
I have a snippet of code that is used to populate a table. It opens a file on disk and throws the data into a table.
if (file.open(QIODevice::ReadOnly))
{
QDataStream stream(&file);
qint32 numRows, numColumns;
stream >> numRows >> numColumns;
QStandardItemModel* tempModel = new QStandardItemModel;
tempModel->setRowCount(numRows);
tempModel->setColumnCount(numColumns);
for (int i = 0; i < numRows ; ++i) {
for (int j = 0; j < numColumns; j++) {
QStandardItem* tempItem = new QStandardItem; // stored in heap
tempItem->read(stream);
tempModel->setItem(i, j, tempItem);
}
}
file.close();
tableView->setModel(tempModel);
...
}
This code works. But the issue I'm having is that, the more files I open, the more memory is being used and it never goes down. If I add a second file, for example, I don't need to store the previous file's model anymore. I want to delete it.
I'm guessing the memory is not being freed up because it never gets deleted since I'm using the new keyword and a pointer.
If we take the tempItem for loop as an example, I imagine I'd have to do something similar to this to fix it:
for (int i = 0; i < numRows ; ++i) {
for (int j = 0; j < numColumns; j++) {
//QStandardItem* tempItem = new QStandardItem;
QStandardItem tempItem; // store on stack and delete at end of scope
//tempItem->read(stream);
tempItem.read(stream);
tempModel->setItem(i, j, tempItem);
}
But even then, it throws an error because QStandardItemModel's setItem (seen here) takes a QStandardItem pointer.
I'd like to fix this for both tempModel and tempItem if possible. What am I doing wrong here?
The memory leak is not due to QStandardItem ownership. The setItem() method takes ownership of the QStandardItem objects, which will be freed automatically when the QStandardItemModel object is freed.
Your memory leak is due to the tableView->setModel(tempModel);statement, because that method does not take ownership. When you change the model, or free the view, you are responsible for freeing the model.
See this document for details.
For example:
QItemSelectionModel *m = tableView->selectionModel();
tableView->setModel(tempModel);
delete m;
I wound up setting a single QStandardItemModel in my header file and my main window's initialization list.
myapp.h
...
QStandardItemModel* mainModel;
...
myapp.cpp
...
MyApp::MyApp(QWidget* parent)
: QMainWindow(parent),
mainModel(new QStandardItemModel(tableView)),
...
I changed the way the application works so that I only ever need one model for my table view. Whenever I need to add a new data set to populate the table with, I clear the current table entirely, create a new, temporary model, then set the temp model to the main model.
mainTableModel->clear();
QStandardItemModel* tempModel = new QStandardItemModel;
tempModel->setRowCount(numRows);
tempModel->setColumnCount(numColumns);
for (int i = 0; i < numRows ; ++i) {
for (int j = 0; j < numColumns; j++) {
QStandardItem* tempItem = new QStandardItem();
tempItem->read(stream);
tempModel->setItem(i, j, tempItem);
}
}
file.close();
mainModel = tempModel;
tableView->setModel(mainModel);
Clearing the model and reusing it lessens, but does not fix, the memory leak.
I have a wxScrolledWindow object filled with elements (pictures) (every element add with the class ThumbNail which uses dc.paint). I would like to dynamically change the elements with new once (not the same number) (change of folder by the user).
How can I empty all the items in wxScrolledWindow object and put new once back in? And then reset the scrollbars.
_ScrolThumbs = new wxScrolledWindow(_pMainPanel);
wxGridSizer *m_swSizer = new wxGridSizer(1,1,0);
_ScrolThumbs->SetSizer(m_swSizer); // Sets the window to have the given layout sizer.
std::vector<ThumbNail*> _Thumbs;
for(int i=0;i < FilePr::Instance()->GetNumThumbs() ;i++)
{
_Thumbs.push_back(new ThumbNail(_ScrolThumbs, PicName[i]));
_ScrolThumbs ->GetSizer()->Add (_Thumbs[i], 1, wxALL|wxALIGN_CENTER_VERTICAL, 5);
}
Then I tried to do this (when a button is hit):
wxWindowList& lst = _ScrolThumbs->GetChildren();
//if (!lst.empty())
std::cout << lst.size() << '\n';
while(!lst.empty()) //for(int i = 0; i < lst.size(); i++) //lst.size()
{
wxWindow *wnd = lst.back();
wnd->Destroy();
}
But putting new elements back in, like I did above does not work...
Any idea how to do this or were to find help on the web? Thanks!
_ScrolThumbs->GetSizer()->Clear(true);
// or
_ScrolThumbs->DestroyChildren();
I have created a QTreeWidget with Qt Creator and filled it.
When i delete an item from the tree,an empty line moves down to the end of the tree.
When i then add to the tree, the item adds below the empty line.
How do i get rid of this empty line?
Image: http://picpaste.com/gpa-GNtd84Ev.PNG
void Gpa::on_removeButton_clicked()
{
QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget);
item = ui->treeWidget->currentItem();
QString txt = item->text(0);
//search vector for item
int i;
for(i = 0; i < vec.size(); i++)
if(vec.at(i)->getCode() == txt)
break;
vec.erase(vec.begin() + i);
int index = ui->treeWidget->indexOfTopLevelItem(item);
ui->treeWidget->takeTopLevelItem(index);
}
If you are removing a top level item, you should take a look at this method:
QTreeWidgetItem * QTreeWidget::takeTopLevelItem ( int index )
Otherwise, if you are removing a child you should get a pointer to the top level item (or a child of a top level item) and use:
QTreeWidgetItem * QTreeWidgetItem::takeChild ( int index )
EDIT (after OP has posted his code)
I believe the problem lies here:
QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget);
item = ui->treeWidget->currentItem();
You are allocating a new QTreeWidgetItem, and then you assign the item you want to remove to that pointer. So basically, you are creating new empty item and you remove the old one. Try to change your code to:
QTreeWidgetItem *item;
item = ui->treeWidget->currentItem();
I can't find a way to cast a QList of QObjects to another QList with another class template. I have a function that creates instances of QObjects and then return a QList. So my question is, can I cast that QList to another QList of different template class pointers ?
My code:
QList<QObject *> DbManager::listVO(QString voname)
{
DbManager::VOPropertiesJoin(DbManager::VOKeys(obj),VOJOIN_BOTH,"=");
QList<QObject *> vos;
QString voquery = "select * from %1";
voquery = voquery.arg(voname);
QSqlQueryModel *volist = DbManager::makeSelect(voquery);
for(int i = 0;i < volist->rowCount();i++){
QSqlRecord record =volist->record(i);
QObject *voinstance = DbManager::instantiateVO(voname,record.value(0),record.value(1),record.value(2),record.value(3),record.value(4),record.value(5),record.value(6),record.value(7));
vos << voinstance;
}
return vos;
}
and I want something like this:
QList<AnyClass*> list = DbManager::listVO("AnyClass");
Thanks for help in advance.
Since what you need at the end is a QList<AnyClass*>, you can try something like this (Assuming AnyClass is derived from QObject)
Note: I did some modifications to your original code to pass your list by reference since returning a huge list might not that be efficient. I didn't compile the code, but hoping you can figure out what is happening inside this snippet
void DbManager::listVO(QString voname, QList<AnyClass*>& listAnyClass)
{
DbManager::VOPropertiesJoin(DbManager::VOKeys(obj),VOJOIN_BOTH,"=");
// Initialise list with an empty list
listAnyClass = QList<AnyClass*>();
QString voquery = "select * from %1";
voquery = voquery.arg(voname);
QSqlQueryModel *volist = DbManager::makeSelect(voquery);
for(int i = 0; i < volist->rowCount(); i++)
{
QObject *voinstance = GetInstance(voname, volist->record(i));
// Trying to cast into an AnyClass
AnyClass* pAnyClass = qobject_cast<AnyClass*>(voinstance);
// If pAnyClass is a valid AnyClass* update your list
if(pAnyClass)
{
listAnyClass.append(pAnyClass);
}
}
}
QObject* DbManager::GetInstance(QString sVoname, const QSqlRecord& record)
{
return DbManager::instantiateVO
(
voname,
record.value(0),
record.value(1),
record.value(2),
record.value(3),
record.value(4),
record.value(5),
record.value(6),
record.value(7)
)
}
And after calling DbManager::listVO(QString voname, QList<AnyClass*>& listAnyClass) function, listAnyClass will get populated accordingly. This is efficient with contrast to returning a list.
Edited:
So in a nutshell you want to convert a QList<QObject*> into QList<AnyClass*>. So keep your original code as it is and implement a method like this where you wanna
do this conversion:
void ConvertToAnyClassList(const QList<QObject*>& listQObjects, QList<AnyClass*>& listAnyClass)
{
listAnyClass = QList<AnyClass*>();
for( int i = 0; i < listQObjects.length(); ++i )
{
AnyClass* pAnyClass = qobject_cast<AnyClass*>(listQObjects.at(i));
if(pAnyClass)
{
listAnyClass.append(pAnyClass)
}
}
}
And you can call it like this:
QList<QObject*> listQObjects = DbManager::listVO("AnyClass");
QList<AnyClass*> listAnyClass;
ConvertToAnyClassList(listQObjects,listAnyClass);
Suppose you have multiple types, so you may want to implement functions for each type like this:
ConvertToUserList(listQObjects,listUserObjects);
ConvertToCountryList(listQObjects,listCountryObjects);