Handle crash looping through aspects of object in vector - c++

I have created a number of 'question' objects and stored them within a vector.
I want to loop through the vector and do something at the index if the object contains a certain feature. These are the sort of things I have, and some objects will have a different arrangement of these.
QLabel *titleLabel;
QTextEdit *textEdit;
QLineEdit *lineEdit;
QLabel *commentsLabel;
QTextEdit *commentsEdit;
QLineEdit *option;
QLabel *scaleLabel;
QLabel *label;
QLineEdit *scaleFrom;
QLineEdit *scaleTo;
My code crashes if the object at the index doesn't have the specific thing.
Question *question;
for(int i = 0; i< question_vector.size(); i++){
question = question_vector[i];
if(question->scaleFrom)
{
qDebug() << question->scaleFrom->text();
}
else
{
qDebug() << "no";
}
}
The object at index 0 doesnt have a 'scaleFrom' so my program crashes. How do I handle this and skip over it?

You're dereferencing a pointer. It needs to point to a valid memory address. Make it so if your objects don't have something, their pointers are set to NULL or nullptr (C++11), so then you can check whether they are == to null. You can then check whether or not the pointer is null before dereferencing it.
Instead of
qDebug() << question->scaleFrom->text();
you'd have:
if (question != nullptr && question->scaleFrom != nullptr)
qDebug() << question->scaleFrom->text();

Related

Why my delete button didn't delete all widgets on QHBoxLayout

I have problem when deleting my widgets on QHBoxLayout.
i use QList for listing my layout, because i add layout at runtime.
this is my QList
QList<QHBoxLayout*> hBoxLayoutParent;
this my code when i add my widgets
hBoxLayoutParent.push_back(createNewHBoxParent());
hBoxLayoutParent.last()->addWidget(label);
hBoxLayoutParent.last()->addWidget(cmbBox);
hBoxLayoutParent.last()->addWidget(cmbJurusan);
hBoxLayoutParent.last()->addWidget(listButton.last());
ui->formLayout_2->addLayout(hBoxLayoutParent.last());
and this how i delete them
for(int i = 0; i < hBoxLayoutParent[index]->count(); i++)
{
delete hBoxLayoutParent[index]->takeAt(0)->widget();
qDebug() << "Widget Number: " << hBoxLayoutParent[index]->count();
}
hBoxLayoutParent.removeAt(index);
when i click on delete button, not all been deleted.
cmbJurusan still exists.
The problem is that your for loop isn't counting in quite the way you think it is. You have...
for (int i = 0; i < hBoxLayoutParent[index]->count(); i++)
So you're incrementing i on each iteration. But...
delete hBoxLayoutParent[index]->takeAt(0)->widget();
will remove an item from hBoxLayoutParent[index]. So you're modifying the QHBoxLayout over whose items you're iterating -- each iteration increases i by one but also decreases the number of items in the layout by one.
Instead, try...
while (!hBoxLayoutParent[index]->isEmpty()) {
delete hBoxLayoutParent[index]->takeAt(0)->widget();
qDebug() << "Widget Number: " << hBoxLayoutParent[index]->count();
}
Note also that if this code is run within the context of the event loop then you might want to use QObject::deleteLater rather than delete.

Cleaning up QList and QGraphicsScene to avoid memory leaks

I want to be thorough and clean up to avoid memory and object leaks.
I had the misunderstanding that Qt cleans automatically objects that go out of scope, except pointers need to be deleted manually.
In my code I have a number of QList... example:
void someFunction()
{
QList<int> x = QList<int>() << 0 << 0 << 0 << 0;
QList<QImage> y; // filled in some way
QList<QGraphicsScene*> s;
s = otherFunction(x, y);
}
QList<QGraphicsScene*> otherFunction(QList<int>& x, const QList<QImage>& y)
{
QList<QGraphicsScene*> s;
s.push_back(this->scene()); // a few times; of course there is some processing...
return s;
}
void thirdFunction()
{
QList<int> x = QList<int>() << 0 << 0 << 0 << 0;
QList<QImage> y; // filled in some way
QList<QGraphicsScene*> s;
s = otherFunction(x, y);
for (int i = 0; i < s.size(); ++i)
{
view[i]->setScene(s.at(i)); // view is a QList<QGraphicsView*>
}
}
Called multiple times, I can see the memory increasing (seen from task manager).
Obviously when items go out of scope, they are not being deleted... I immediately suspected the list of scene pointers.
1) What is the best way to delete them ? Will
qDeleteAll(s);
in someFunction() be enough ? Will it delete the scene pointers, as well as all the QGraphicItems inside the scenes ? or do I need to iterate through the list of scenes, and delete all items ? Do I have to do this :
for (int i = 0; i < s.size(); ++i)
{
s.at(i).clear();
} // then
qDeleteAll(s);
2) Do I need to delete the lists of simple variables (int, bool) ?
What about lists of objects, like QImage ?
3) I assumed that removing an item from the scene deletes its pointer. But now I read that, after removing an item from the scene, it needs to be deleted manually. So does
scene().clear();
delete the QGraphicItem pointers that have been added to the scene ? Or should the items be deleted as well using a delete call ?
4) In the thirdFunction, do I have a memory leak ? It seems like I do !!
I can't delete the scenes since I set them onto views... but what happens to the scenes that were assigned to the views already ?
How do I clean that correctly ?
1.
qDeleteAll deletes items in a container using the C++ delete operator. It's also stated in the Qt documentation about QGraphicsScene destructor:
Removes and deletes all items from the scene object before destroying
the scene object
So it's enough to call qDeleteAll(s).
2.
There is no need to delete lists of basic or Qt types like QList<int> or QList<QImage> as they are deleted when the list is deleted.
3.
QGraphicsScene::clear() removes and deletes all items from the scene. So calling scene().clear(); is enough. It's equivalent to calling :
qDeletaAll( scene()->items() );
4.
Since a view does not take ownership of scene, you should delete the previously assigned scenes using deleteLater() :
for (int i = 0; i < s.size(); ++i)
{
view[i]->scene()->deleteLater();
view[i]->setScene(s.at(i)); // view is a QList<QGraphicsView*>
}

QDir::remove() always causing a crash when called in specific SLOT

Everytime I call QDir::removeRecursively() my application crashes AFTER having removed the folder containing the files correctly.
Having done some testing I found out that it depends on how I call the function. This is my code:
Recorder::Recorder(ParentClass *parent): QObject(parent){
connect(this,SIGNAL(finishedRec(QString,QString)),this,SLOT(finishedRecording(QString,QString)));
}
void Recorder::start(){
if (!recording){
recording=true;
recThread.reset(new std::thread(&Recorder::recordThread, this));
}
}
void Recorder::stop(){
recording = false;
recThread->join(); recThread.reset();
}
void Recorder::recordThread(){
QString picDir;
QString filename;
//....
while(recording){
//writing frames to folder
}
emit finishedRec(picDir,filename);
}
void Recorder::finishedRecording(QString picDir, QString filename){
QProcess* proc = new QProcess();
vecProcess.push_back(proc);
vecString.push_back(picDir);
proc->start("C:\\ffmpeg.exe", QStringList() <<"-i"<< picDir << "-r"<< "30" << "-vcodec"<< "ffv1" << filename);
connect(proc,SIGNAL(finished(int)),this,SLOT(finishedProcess()));
}
void Recorder::finishedProcess(){
for (int i=0; i<vecProcess.size();i++){
if(vecProcess.at(i)->state()==QProcess::NotRunning){
delete vecProcess.at(i);
vecProcess.erase(vecProcess.begin() + i);
QString folderToRemove=vecString.at(i);
folderToRemove.chop(12);
qDebug() << folderToRemove;
QDir dir(folderToRemove);
dir.removeRecursively();
vecString.erase(vecString.begin() + i);
}
}
}
Only if I leave dir.removeRecursively() in, my application will always crash. Without it everything works as intended. Even deleting all the files with
QDir dir(path);
dir.setNameFilters(QStringList() << "*.*");
dir.setFilter(QDir::Files);
foreach(QString dirFile, dir.entryList()){
dir.remove(dirFile);
}
will cause a crash AFTRER all the files were deleted.
I'm running my recordThead as a std::unique_ptr<std::thread>. I did try to run the thread as a QThread but that gave me the exact same result. If dir.removeRecursively() was called the program will crash after finishing the event in finishedProcess()
Calling removeRecursively() in a different event loop works. Why doesn't it work when using it in a SLOT like shown in my example?
vector erase effectively reduces the container size by the number of elements removed, which are destroyed.
// one suspect
vecProcess.erase(vecProcess.begin() + i);
// another suspect
vecString.erase(vecString.begin() + i);
And you call that in a loop where 'i' gets incremented? Should eventually attempt to erase something beyond the vector size. I would just release entire container if possible after the loop finished or used list. And maybe you don't need to store pointers in those containers but values (?). Storing pointers to objects allocated by you makes you to release the one by one and sometimes, yes, justified but with C++ 11 and move semantics it is not always the case.

How to run a loop using gui object names in qt?

I have a gui form, where multiple text boxes are present. I want to put their values inside an array. One way of doing it is by writing something like this
{array element } = ui->text_1->text();
and repeat it for text_2,text_3 upto n.
What I want is to run a loop and replace number portion of text box name in each cycle.
something like this {array element } = ui->text_{This number getting changed }->text();
How can it be done in qt?
There are two ways of doing this.
When you create the UI, instead of using text1, text2, etc. you create an array of QLineEdits (eg. std::vector<QLineEdit>) and then when you want to retrieve their values then simply iterate over this array
Iterate over the children of the container widget. You can get the list of the children using the following (documentation):
QList<QObject *> list = parentWidget->children();
Another option to those listed would be to create an array using an initializer list. Depending on how big the array is (and how often it changes), this might be workable.
QLineEdit* entries[] = { ui->text_0, ui->text_1, ui=>text_2 };
QStringList answers;
for ( int i = 0; i < 3; ++i )
{
answers += entries[i]->text();
}
here is an expanded version of Matyas' solution:
class MyClass : public QWidget
{
QStringList answers;
void FillAnswersList(QObject *base)
{
QLineEdit *lineEdit = qobject_cast<QLineEdit>(base);
if(lineEdit)answers.append(lineEdit->text());
else
{
foreach(QObject *child, base->children())
FillAnswersList(child);
}
}
};
If it is just the number changing, and always incrementing, there is another possible solution using QObject::findChild, which takes a name as a parameter.
QString name_template("text_%1");
QStringList answers;
for(int i = 0; i < MAX_TEXTS; ++i)
{
QLineEdit *edit = ui->findChild<QLineEdit *>(name_template.arg(i));
answers += edit->text();
}

Qt 4.6 - C++ - QTreeWidgetItem Iterator

I have a QTreeWidget with items in it. The first column contains a unique number. This is set via item->setData(0, 0, unique_number);. The second column contains a checkbox, set via item->setCheckState(1, Qt::Unchecked);. The user can select the items (s)he would like to work with and presses a button. The slot for this button will run a loop on the checked items. In the Qt documentation an example is given. You use the QTreeWidgetItemIterator.
QTreeWidgetItemIterator it(treeWidget);
while (*it) {
if ((*it)->text(0) == itemText)
(*it)->setSelected(true);
++it;
}
It also says that you can specify an argument in the constructor to only iterate over the checked items. The flag is : QTreeWidgetItemIterator::Checked. My slightly customized loop is as follows:
QTreeWidgetItemIterator it(treeWidget, QTreeWidgetItemIterator::Checked);
while (*it)
{
QVariant w;
w = (*it)->data(0, 0);
std::cout << "Selected item # " << w.toInt() << "\n";
it++;
}
This code will compile fine but won't work when you actually run the program. It doesn't print any values.
Any tips? Thanks!
The one caveat missing from the Qt documentation is that the QTreeWidgetItemIterator::Checked flag only tests the check state for column 0 in each of your QTreeWidgetItems. Using column 0 for your checkbox and column 1 for your unique number should cause the loop to print values.
Thanks Roneel!
Another way to do this is like this (I just figured it out).
QTreeWidgetItemIterator it(ui->treeWidget);
while (*it)
{
if ((*it)->checkState(1) == 2)
{
QVariant w;
w = (*it)->data(4, 0);
std::cout << "Selected item # " << w.toString().toStdString() << "\n";
}
++it;
}
checkState takes a int column argument and the == 2 only allows checked items to be processed.
Just one thing... I think it's not a good habit to compare enum to int, as enum "int value" may change... Compare to Qt::Checked instead... It's just "for sure" and much more clean for reading