Why object be deleted without obvious code? - c++

In qt, I use tablewidget to store 100 rows. At first, I new tableWidgetItems to fill the rows.
As it runs, I set the items' propertities and no longer 'new'.
But I find after I use 'ui->tableWidget->setRowCount(index);', and later set back to 100, the code "ui->tableWidget->item(index, 0)->setText(...);" will crash the program. That's so bad!!! ;(
I debugged and find the new index > index set as row count before 'setting back to 100'.
Did the system delete the table items automatically when I set smaller row count???
I fear about this so much because even my code cannot determine the lifetime of the objects I created... Does anyone know how to keep them 'alive' after setting row count?(otherwise, I have to new them...).
I really appreciate it you take the patience to read my poor ELis:)
new:
//TABLE
ui->tableWidget->setColumnCount(3);
ui->tableWidget->setRowCount(100);
ui->tableWidget->setHorizontalHeaderLabels(headers);
for(int i = 0; i < 100; i++)//new
{
ui->tableWidget->setItem( i, 0 , new QTableWidgetItem(""));//time
ui->tableWidget->setItem( i, 1 , new QTableWidgetItem(""));//name
ui->tableWidget->setItem( i, 2 , new QTableWidgetItem(""));//BITS
}
Related code lines only:
{
int index = 0;
for(int queue_i = size_1; queue_i >= 0; queue_i--)
{
if(logDisplayQueue.at(queue_i).at(3) == "0" || logDisplayQueue.at(queue_i).at(3) == "2")continue;
QStringList BITList = bits2Hexs(queue_i);
ui->tableWidget->item(index, 0)->setText(logDisplayQueue.at(queue_i).at(0));//time
ui->tableWidget->item(index, 1)->setText(logDisplayQueue.at(queue_i).at(1));//name
ui->tableWidget->item(index, 2)->setText(BITList.join(""));//BITS
if(queue_i == oldRowItemNo)ui->tableWidget->selectRow(index);
index++;
}
ui->tableWidget->setRowCount(index);//set row count to be 30 more or less
}
Another function:
{
ui->tableWidget->setRowCount(100);//back to be 100 again
for(int queue_i = size_1, index = 0; queue_i >= 0; queue_i--, index++)
{
QStringList BITList = bits2Hexs(queue_i);
ui->tableWidget->item(index, 0)->setText(logDisplayQueue.at(queue_i).at(0));//time
ui->tableWidget->item(index, 1)->setText(logDisplayQueue.at(queue_i).at(1));//name
ui->tableWidget->item(index, 2)->setText(BITList.join(""));//BITS
//In debugging, when index reches the value of old row-count, "->setText" crashes the //program.
if(queue_i == oldRowItemNo)ui->tableWidget->selectRow(index);
}
}
When running, it returns message like 'instruction 0x00421727 refers to 0x00000000 memory, the memory cannot be 'read''
if I comment off this line:
'ui->tableWidget->setRowCount(index);//set row count to be 30 more or less', it runs well without crash and rows after index-referred-row show the same data as before.

setRowCount ensures that the table holds exactly that many rows. If you had more rows than index before, those rows are gone (deleted).
If you want to temporarily hide rows, you should probably use hideRow(int)/showRow rather than resetting the row count.

Related

check if a random move has already been made, and to ignore previous move in the randomisation process?

Im positioning sprites in a sliding puzzle game, but I have trouble to randomise the tile position
How can I check if a random move (arc4random) has already been made, and to ignore previous move in the randomisation process?
the tiles do randomise/reshuffle, but sometimes the repeat the random move made
eg tile 23 slides to tile 24 position and back several times, counting as a random move
(which means the board doesn't shuffle properly)
int count = 0;
int moveArray[5];
int GameTile;
int EmptySq;
//loop through the board and find the empty square
for (GameTile = 0; GameTile <25; ++GameTile) {
if (boardOcc[GameTile]== kEMPTY) {
EmptySq = GameTile;
break;
}
}
int RowEmpty = RowNumber[GameTile];
int colEmpty = ColHeight[GameTile];
if (RowEmpty <4) moveArray[count++] = (GameTile +5);//works out the current possible move
if (RowEmpty >0) moveArray[count++] = (GameTile -5);//to avoid unsolvable puzzles
if (colEmpty <4) moveArray[count++] = (GameTile +1);
if (colEmpty >0) moveArray[count++] = (GameTile -1);
int RandomIndex = arc4random()%count;
int RandomFrom = moveArray[RandomIndex];
boardOcc[EmptySq] = boardOcc[RandomFrom];
boardOcc[RandomFrom] = kEMPTY;
There are few - if not many possibilities.
One possibility would be to create a stack-like buffer array, which would contain, let's say 10 steps. (Stack - goes in from one end, goes out from other end)
For example:
NSMutableArray *_buffer = [NSMutableArray new];
So - game starts, buffer array is empty. You generate first random move, and also insert it into the buffer array:
[_buffer insertObject:[NSNumber numberWithInt:RandomIndex] atIndex:0];
Then run a check if our array contains more that 10 elements and remove last one if so:
if([_buffer count] > 10)
{
[_buffer removeObjectAtIndex:10];
}
We need to remove only one item, as we only add one object each time.
And then we add the checking, so that next 'RandomIndex' would be something else than previous 10 indexes. We set 'RandomIndex' to some neutral value (-1) and then launch a while loop (to set 'RandomIndex' to some random value, and second time check if '_buffer' contains such value. If it contains, it will regenerate 'RandomIndex' and check again.. it could do so indefinitely, but if the 'count' is a much bigger number, then it will take 2-3 while loops, tops. No worries.
int RandomIndex = -1;
while(RandomIndex == -1 || [_buffer containsObject:[NSNumber numberWithInt:RandomIndex]])
{
RandomIndex = arc4random()%count;
}
But You could add some safety, to allow it to break out of the loop if after, say, 5 cycles: (But then it will keep the repeating value..)
int RandomIndex = -1;
int safetyCounter = 0;
while(RandomIndex == -1 || [_buffer containsObject:[NSNumber numberWithInt:RandomIndex]])
{
RandomIndex = arc4random()%count;
if(safetyCounter == 5)
{
break;
}
safetyCounter++;
}
You could also decrease the buffer size to - 3 or five, then it will work perfectly in 99.9999999% cases or even 100%. Just to disable that case, when randomly it picks the same number each second time as You described. Anyways - no worries.
But still. Let's discuss another - a bit more advanced and safer way.
Other possibility would be to create two separate buffers. One - as in previous example - would be used to store last 10 values, and second would have all the other possible unique moves.
So:
NSMutableArray *_buffer = [NSMutableArray new];
NSMutableArray *_allValues = [NSMutableArray new];
At the beginning '_buffer' is empty, but for '_allValues', we add all possible moves:
for(int i = 0; i < count; i++)
{
[_allValues addObject:[NSNumber numberWithInt:i]];
}
and again - when we calculate a random value - we add it to the '_buffer' AND remove from '_allValues'
[_buffer insertObject:[NSNumber numberWithInt:RandomIndex] atIndex:0];
[_allValues removeObject:[NSNumber numberWithInt:RandomIndex]];
after that - we again check if _buffer is not larger than 10. If Yes, we remove last object, and add back to _allValues:
if([_buffer count] > 10)
{
[_allValues addObject:[_buffer objectAtIndex:10]];
[_buffer removeObjectAtIndex:10];
}
And most importantly - we calculate 'RandomIndex from the count of _allValues and take corresponding object's intValue:
RandomIndex = [[_allValues objectAtIndex:(arc4random()%[_allValues count])] intValue];
Thus - we don't need any safety checking, because in this way, each time calculated value will be unique for the last 10 moves.
Hope it helps.. happy coding!

How to delete selected row of a List Control in MFC?

I want to delete selected row of list control in MFC.
I have created a Delete Button, So If any row (it could be one or more than one row) is/are selected and I press delete button that/those rows should be deleted.
If lets say there are 100 rows and I select rows from 50-60, all the rows in this range should be deleted and rest of rows should have indexes from 1 to 90. means indexing should be proper after deletion also.
Adapted from this MSDN article:
UINT i, uSelectedCount = m_myListCtrl.GetSelectedCount();
int nItem;
if (uSelectedCount > 0)
for (i=0; i < uSelectedCount; i++)
{ nItem = m_myListCtrl.GetNextItem(-1, LVNI_SELECTED);
ASSERT(nItem != -1);
m_myListCtrl.DeleteItem(nItem);
}
When deleting a multiple selection having several items I prefer to do it like this:
int nItem = -1;
while ((nItem = m_list.GetNextItem(nItem, LVNI_SELECTED)) != -1)
{
if (m_list.DeleteItem(nItem))
nItem--;
}
Notice the important nItem--; line
UPDATE
I had to give up from this approach as the ItemData of an element gots fucked up. If I remove the nth element then the n+1 element will be my new nth. That element has a completely screwed up Itemdata.
UPDATE 2
I also tried with
int nItem = -1;
while ((nItem = m_list.GetNextItem(-1, LVNI_SELECTED)) != -1)
{
m_list.DeleteItem(nItem);
}
This approach also has the problem of screwing the Itemdata I reported before.
The following approach worked perfecly for me:
std::stack< int > items;
int nItem = -1;
while ((nItem = myListCtrl.GetNextItem(nItem, LVNI_SELECTED)) != -1)
{
items.push(nItem);
}
bool removed = false;
while (!items.empty())
{
nItem = items.top();
if (myListCtrl.DeleItem(nItem))
removed = true;
items.pop();
}
if (removed)
// update some application state;
Explanation:
When you remove things from the end to the start, you do not have to worry about the validity of positions. As the CListCtrl does not provide a GetPrevItem or any other way to get items in the reverse order, you need to store them in a collection where you can have that reverse order.
The most practical way to do it is to use a stack. Due to the way it works, you will put things in there in the normal order, and when you retrieve things they are automatically in reverse order.

How to delete all rows from QTableWidget

I am trying to delete all rows from a QTableWidget . Here is what I tried.
for ( int i = 0; i < mTestTable->rowCount(); ++i )
{
mTestTable->removeRow(i);
}
I had two rows in my table. But this just deleted a single row. A reason could be that I did not create the the table with a fixed table size. The Qt Documentation for rowCount() says,
This property holds the number of rows in the table.
By default, for a table constructed without row and column counts,
this property contains a value of 0.
So if that is the case, what is the best way to remove all rows from table?
Just set the row count to 0 with:
mTestTable->setRowCount(0);
it will delete the QTableWidgetItems automatically, by calling removeRows as you can see in QTableWidget internal model code:
void QTableModel::setRowCount(int rows)
{
int rc = verticalHeaderItems.count();
if (rows < 0 || rc == rows)
return;
if (rc < rows)
insertRows(qMax(rc, 0), rows - rc);
else
removeRows(qMax(rows, 0), rc - rows);
}
I don't know QTableWidget but your code seems to have a logic flaw. You are forgetting that as you go round the loop you are decreasing the value of mTestTable->rowCount(). After you have removed one row, i will be one and mTestTable->rowCount() will also be one, so your loop stops.
I would do it like this
while (mTestTable->rowCount() > 0)
{
mTestTable->removeRow(0);
}
AFAIK setRowCount(0) removes nothing. Objects are still there, but no more visible.
yourtable->model()->removeRows(0, yourtable->rowCount());
QTableWidget test;
test.clear();
test.setRowCount( 0);
The simple way to delete rows is to set the row count to zero. This uses removeRows() internally.
table->setRowCount(0);
You could also clear the content and then remove all rows.
table->clearContents();
table->model()->removeRows(0, table->rowCount());
Both snippets leave the headers untouched!
If you need to get rid of headers, too, you could switch from clearContents() to clear().
In order to prevent an app crash, disconnect all signals from the QTableView.
// Deselects all selected items
ui->tableWidget->clearSelection();
// Disconnect all signals from table widget ! important !
ui->tableWidget->disconnect();
// Remove all items
ui->tableWidget->clearContents();
// Set row count to 0 (remove rows)
ui->tableWidget->setRowCount(0);
Look this post : http://forum.qt.io/topic/1715/qtablewidget-how-to-delete-a-row
QList<QTableWidgetItem*> items = table.findItems(.....);
QMap<int, int> rowsMap;
for(int i = 0; i < items.count(); i++{
rowsMap[items.at(i).row()] = -1; //garbage value
}
QList<int> rowsList = rowsMap.uniqueKeys();
qSort(rowsList);
//Now go through your table and delete rows in descending order as content would shift up and hence cannot do it in ascending order with ease.
for(int i = rowList.count() - 1; i >= 0; i--){
table.removeRow(rowList.at(i));
}
You can just add empty item model (QStandardItemModel) to your QTableView (myTableView):
itemModel = new QStandardItemModel;
ui->myTableView->setModel(itemModel);
In python you can just set the rowCount to zero and that'll work!
tableWidget.setRowCount(0)
This code is tested with PySide6. Hope it will work for PyQt5 and PyQt6 too.
Your code does not delete last row.
Try this one.
int totalRow = mTestTable->rowCount();
for ( int i = 0; i < totalRow ; ++i )
{
mTestTable->removeRow(i);
}
In your code, on the first time, rowCount() have value 2 and value of the i is 0, so its delete 1st row,
But on the second time value of i incremented with 1, but rowCount() return the updated row count which is now 1, so, it does not delete the last row.
Hope now you ll be clear.
Removes all items not in the headers from the view. This will also remove all selections. The table dimensions stay the same.
void QTableWidget::clearContents()
Removes all items in the view. This will also remove all selections and headers.
void QTableWidget::clear()
This works for me:
for i in reversed(range(self.tableWidget.rowCount())):
self.tableWidget.removeRow(i)

c++ - adding new values to an array of ints

In my code below I have an array of objects - tArray.
I am trying to find the 'buyer names' that have the top five total 'num shares',
the calctotal, and calcstring arrays work in tandem to store the buyer and his total value.
However, I have stepped through the code when running and my code is essentially replacing the values that are smaller that the current 'numshares' in the loop. This means even if a buyer that was just replaced comes up again his total starts new and is not added, which is want I want.
How would I change this code so when a larger value is found that smaller value is pushed further down into the array and not replaced?
Thanks - I am bound to this 'format' of solving the problem (assignment) so achieving the functionality is the goal so I can progress.
So, essentially the second if statement is were the issue lies:
for (int i = 0; i<nTransactions; i++)
{
//compares with arrays
for(int j =0; j<sSize; j++)
{
if(tArray[i].buyerName == calcString[j])
{
calcTotal[j] += tArray[i].numShares;
break;
}
else{
//checks if shares is great then current total then replaces
if(tArray[i].numShares > calcTotal[j])
{
calcTotal[j] = tArray[i].numShares;
calcString[j] = tArray[i].buyerName;
break;
}
}
}
}
return calcString;
}
It seems like you are trying to find the largest totals only looking at 1 transaction at a time. You need to aggregate the totals for all the buyers first. Then it is a simple matter to find the 5 highest totals.

QTableWidget update items

I’m having trouble with updating QTableWidgetItems. I don’t understand what I’m doing wrong :(
code and explanation.
Step by step problem.
at first insertion = OK, all first cells are filled.
updating firstly inserted items = OK, all first cells are updated.
at second insertion = OK, all second cells are filled.
updating second inserted items = OK, all second cells are updated.
updating first inserted items = FAIL, all first cells are updated, but NEXT cell’s first table is empty. WHY?
Code:
void MainWindow::fillTable(QList<QByteArray> Info)
{
int Row = ui->clientsList->rowCount() - 1; //Starts from 0.
//Check if client row already exists.
for(int i = Row; i >= 0; i--)
{
if(ui->clientsList->item(i, 0)->text().contains(QString(Info[1])))
{
//Update row.
for(int u = 0; u < Info.count() - 1; u++)
{
ui->clientsList->setItem(i, u, new QTableWidgetItem(QString(Info[u + 1])));
}
return; //avoid new row insertion.
}
}
//Insert new row.
Row = ui->clientsList->rowCount() + 1;
ui->clientsList->setRowCount(Row);
for(int i = 0; i < Info.count() - 1; i++)
{
//Fill rows.
ui->clientsList->setItem(Row - 1, i, new QTableWidgetItem(QString(Info[i + 1])));
}
}
Not full solution yet, but few comments:
1.There may be memory leak in line
ui->clientsList->setItem(i, u, new QTableWidgetItem(QString(Info[u + 1])));
why not use
ui->clientsList->item(i, u)->setText(QString(Info[u + 1]));
which is safer and more clear.
2.My understanding is you are relying on the fact that Info has same length as the row length is, perhaps it worth to add check for that?