Qt program crashes after deleting last object from a vector - c++

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!

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.

Search vector of objects by object data member attribute

I'm writing a Jukebox simulator and I'm trying to search a vector of Album objects by album title and return the index for use in other functions. The function is to be used for a number of different things such as deleting an album, printing an album etc.
I have gotten it to work in a previous application when the function was in the same Class as the data member to search for. I can however for some reason not get it to work using getters. No matter what I input as search key the idx returns 3 although the vector only contains indexes 0, 1 and 2 (only 3 albums right now).
The lambda function seem to be able to access data by using the getAlbum()-getter but somehow the comparison doesn't work.
My approach might be entirely wrong and I'd be grateful for any pointers in the right direction, or suggestions on how to accomplish the desired result using some other technique.
int Jukebox::findAlbumIdx(string key)
{
// Get search predicate
auto it = find_if(albvec.begin(), albvec.end(), [key](Album const &a)
{
return (a.getAlbum() == key);
});
int idx = it - albvec.begin();
return idx;
}
void Jukebox::delAlbum()
{
cin.get();
string key;
cout << "Input title of album to delete: ";
getline(cin, key);
int idx = findAlbumIdx(key);
if(idx > albvec.size() - 1)
cout << "No hits." << endl;
else
albvec.erase(albvec.begin() + idx);
}
getAlbum is just a simple inline getter as such:
string getAlbum() const {return album_title;}
Following Jonathan Wakely's suggestion to add std::cout << a.getAlbum() << " == " << key << std::endl; in the lambda the output is this:
Input title of album to delete: Abbey Road
== Abbey Road
== Abbey Road
== Abbey RoadLonely Hearts Club Band
No hits.
Obviously the getter isn't actually getting much to use for comparison. Not sure why it only gets the last entry and on the right hand side of the comparison.
If I add this to any of the functions above it gets and displays the Album titles correctly. The vector seems to be fine just before calling findAlbumIdx(key); and also inside the findAlbumIdx function.
for(unsigned int i = 0; i < albvec.size(); ++i)
cout << albvec[i].getAlbum() << endl;
The original playlist file that is read into the vector to be searched had dos newlines, after converting it using dos2unix (since I'm running Linux) the search, and I presume a lot of other things, is working correctly.
I suppose trimming newline characters while reading the file into the vector would be the more correct approach.

Erasing Element From a Vector Erases Multiple (C++)

I've been building a small Roguelike ASCII game that uses the console to build my limited knowledge of C++. I have it set up so when the player object's coordinates are the same as an item(which is stored in a vector), it removes the item and adds 10 to the player's HP.
The program starts out with 9 items in the room. BUT, whenever I get the item, it deletes the first 5 items in the vector(But they're still being drawn on the screen for some reason), and adds 50 to the health. So I'm left with 4 items. The next time I get an item, it removes the first 2 and adds 20 to the health. Now I'm left with 2 items. The last two times, it removes one item at a time.
So, essentially, the vector is being split in half each time I get an item. I've narrowed down the problem through a lot of debugging, and I'm pretty sure it's coming from this chunk of code:
for(int i=0;i<ia.size();i++) //If the player touches an item
{
if(grid[p.getY()][p.getX()]==itm)
{
hp+=10;
cout << "Item "<<i<<" removed\n";
swap(ia[i], ia.back());
ia.pop_back();
system("pause");
}
}
I'm still trying to figure out vectors, so if someone could tell me what I'm doing wrong, I would really appreciate it. :)
There're comments suggesting erase(), but be aware that it will copy later array elements one by one to fill in the index being vacated: your swap approach may be massively faster and is better if you don't care about element order.
One issue is that you swap the back() element into the position occupied by the element you're removing, then increment the iterator to test the next ia index. This means that the items copied from the back() are never themselves tested - for that you want to avoid the i++ after doing a swap. Of course if only one item can ever occupy a grid position, you can just break as abiessu commented....
Here's some code that works as I think you want:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ia{ 10, 20, 30, 40, 50, 60, 70 };
for(size_t i=0;i<ia.size(); ) //If the player touches an item
{
if(ia[i] % 20)
{
cout << "Item "<<i<<" removed\n";
swap(ia[i], ia.back());
ia.pop_back();
continue;
}
else
++i;
}
for (size_t i = 0; i < ia.size(); ++i)
std::cout << '[' << i << "] " << ia[i] << '\n';
}
See it at http://ideone.com/JJ6CE6
That said, for some data types ia[i] = ia.back(); will be faster than swap - for others slower (if they use and benefit from C++11 style "move"). You may also want to skip any self-swap/assignment if i == size() - 1 (at least check that the array element type supports that safely).

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.

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