Why my delete button didn't delete all widgets on QHBoxLayout - c++

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.

Related

Qt program crashes after deleting last object from a vector

I have an application I'm making for my work. It basically takes in employees and their availability and makes a schedule. With that aside, I have a page called "edit employee" in which I can edit information from any employee. I have a "delete" button in which when you press it, you get the current row from the qlistwidget and it deletes that element. It all works, until I only have one element left in the vector.
I have tried the different forms of deleting from a vector. I tried .clear(), .erase(vec.begin(), vec.end()), .erase(vec.begin() + position), etc.
I also had a for loop displaying the contents of the vector, it showed only 1 element in the vector, which was correct. The vector did not have empty spaces or anything that you could consider "corrupted".
void MainWindow::deleteEmployee(int position)
{
// employeeList.erase(employeeList.begin() + position);
if(employeeList.size() == 1)
{
// fails only when trying to delete from last index
employeeList.clear();
}
else
{
employeeList.erase(employeeList.begin() + position);
}
}
void MainWindow::on_editEmployeeDelete_pushButton_clicked()
{
QMessageBox::StandardButton reply;
reply =
QMessageBox::question(this, "Delete Employee",
"Are you sure you want to delete " +
employeeList.at(ui->editEmployee_listWidget->currentRow()).getFirstName() +
" " +
employeeList.at(ui->editEmployee_listWidget->currentRow()).getLastName() +
"?",
QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
QMessageBox::information(nullptr, "Success",
employeeList.at(ui->editEmployee_listWidget->currentRow()).getFirstName() +
" " +
employeeList.at(ui->editEmployee_listWidget->currentRow()).getLastName() +
" has been deleted successfully.");
deleteEmployee(ui->editEmployee_listWidget->currentRow());
// clears the qlistwidget once again to add all employees left in the vector
ui->editEmployee_listWidget->blockSignals(true);
ui->editEmployee_listWidget->clear();
ui->editEmployee_listWidget->blockSignals(false);
// adds all the employees names to the qlistwidget
for(unsigned long long i = 0; i < employeeList.size(); i++)
{
ui->editEmployee_listWidget->addItem(employeeList.at(i).getLastName() +
", " +
employeeList.at(i).getFirstName());
}
}
}
employeeList is a vector of type employee, which is a class I created. It holds their name, ranking, etc (all QStrings and ints).
When you click on the qlistwidget, the delete button becomes enabled. When you click on the delete button, the selected employee will be deleted. This works perfectly fine until you have 1 person left, in which it crashes, stating "Program has unexpectedly finished". I have tried many different forms of deleting. None worked.
Any help is greatly appreciated, thank you!

Why cannot addItem into QGraphicsScene from a QVector<QGraphicsItem>?

I want to undo or redo actions in a QGraphicsScene that is linked to a QGraphicsView: For that, I use QGraphicsEllipse (to draw points while moving and clicking on the QGraphicsView) in my mouseMoveEvent method, I push every QGraphicsEllipse in a QVector<QGraphicsItem>, and when the QAction "undo" is triggered the program must delete the last ellipses (determined number of ellipses) drawn in my QGraphicsView thanks to my QGraphicsScene.
When I clear my QGraphicsScene and try to add all QGraphicsItems that were pushed in my QVector<QGraphicsItem>, I got an error: my app goes down!
if(index < historyIndex.size()){
for (int i = 0; i < scHistory.size() - historyIndex[index]; i++){
scene->addItem((QGraphicsItem*)scHistory[i]);
}
index++;
}
QVector<QGraphicsItem *> scHistory;
QGraphicsScene::addItem takes the ownership of the element added, check the doc. It means that it is now in charge of destructing the element, which happens when you clear the scene with QGraphicsScene::clear. From that point on, your vector is full of dangling pointers.
One quick fix is to replace the call to QGraphicsScene::clear with a manual removal of items through QGraphicsScene::removeItem, which doesn't destroy the item (it returns the ownership to the caller). Then, destroy only those elements that are actually out of the scene, and add back the rest. Another option, more efficient, is to remove only the elements you need, keeping the rest, so you also increase performance by avoiding add back a large number of items from the history.
Without a full knowledge of your code, the second options may be something like:
if(_indexHistoryRoam < _indexHistory.size()){
// Remove only items beyond history
const int range = _sceneHistory.size() - _indexHistory[_indexHistoryRoam];
for (int i = range; i < _sceneHistory.size(); i++){
scene->removeItem((QGraphicsItem*)_sceneHistory[i]);
delete _sceneHistory[i]; // item must be destroyed to avoid a leak
}
_indexHistoryRoam++;
} else { // force removal of all elements
scene->clear();
}
scene->update();

Handle crash looping through aspects of object in vector

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();

How to cycle through all widget in a QVBoxLayout

I have a QVBoxLayout created through the drag and drop section.
Inside of it AT RUN TIME I insert some widgets with the command
ui->verticalLayout->insertWidget() //using appropriate options.
All widgets I insert are of the same type/class.
I would like to cycle through the inserted widgets in order to perform some actions over them.
I suppose it is really simple but can't seem to find out how...
thank you all!
You can use QLayout::itemAt() to loop on the items of the layout. Then use QLayoutItem::widget() to get the widget:
for(int i = 0; i < layout->count(); ++i)
{
do_something(
layout->itemAt(i)->widget()
);
}
Note that widget() may return a null pointer.

Why do i get a repeated QStringList?

I am writing a Qt application that deals with scheduling employees. The header data for the main QTableView is a pointer to a QStringList. The headerData() function works correctly, but when i add a string to the list elsewhere, it appends the entire list including the new string to the end of the list.
For example, if i have the list 1,2,3 and i append 4 to it, then iterating through the list based on the pointer gives the result 1,2,3,1,2,3,4. I don't know a better way than using pointers to have multiple classes access the same data. Does anyone know how to fix the repeating list?
Example Code
//function to save a new employee in memory
bool EmployeeViewDialog::saveEmployee(Employee *e)
{
employees->insert(e->name,e);
*employeeNames << e->name;
for (int i = 0; i < employeeNames->length(); i++) {
qDebug() << employeeNames->at(i);
}
QList<QStandardItem*> items;
items << new QStandardItem(e->name);
items << new QStandardItem(e->id);
items << new QStandardItem(e->phone);
items << new QStandardItem(e->email);
model->appendRow(items);
return true;
}
The append was just changed to the << method. It is the employeeNames << e->name; line.
The for loop iterates through the list and does the same thing as what happens in the external class.