QTableWidget update items - c++

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?

Related

How to efficiently copy contents between two list controls

I want to copy rows from one List Control to another List Control. I am only able to copy them by sub item. I think this is not very efficient. There must be a method to copy the content by rows. The following is my code that copies content one sub item at a time.
CString CurItem, tem, copystr;
int j = 0;
m_combo_list.GetLBText(m_combo_list.GetCurSel(), CurItem);
for (int i = 0; i < m_list.GetItemCount(); i++) {
tem = m_list.GetItemText(i, 0);
if (CurItem == tem) {
m_report_list.InsertItem(j, _T(""));
for (int k = 0; k < 14; k++) { // 14 items per row.
copystr = m_list.GetItemText(i, k);// get one item per time from one list control.
m_report_list.SetItemText(j, k, copystr); // this is another list control. Copy the item to this list control.
}
j++;
}
}
Could anyone give a method to replace the for loop by copy a row from m_list to m_report_list directly? I think such a way must save a lot of time.

How to fill a datarow->ItemArray from a DataGridView?

I try to fill my DataTable with Values from a DataGridView.
Code:
Data::DataTable^ dataTable = gcnew Data::DataTable;
int rows = dataGridView1->RowCount;
int cols = dataGridView1->ColumnCount;
for (int iZeilen= 0; iZeilen < rows; iZeilen++ ){
Data::DataRow^ row = dataTable->Rows->Add() ;
row->ItemArray[dataGridView1->RowCount];
for (int iSpalten= 1; iSpalten = cols; iSpalten++) {
row->ItemArray[iSpalten] = (dataGridView1[iZeilen, iSpalten]) ;
}
dataTable->Rows->Add(row);
}
The Compiler has no problem, but during execution there is an Exception, which says:
Index is out of range array
In Line:
row->ItemArray[iSpalten] = (dataGridView1[iZeilen, iSpalten]) ;
Can someone tell me which array is meant?
There are a few issues happening here.
Your nested loop will only exit if row->ItemArray[iSpalten] is out of range. Otherwise it will never end because your conditional statement is an assignment: iSpalten = cols. Probably a simple mistake or copy-paste error. If dataGridView1 has more columns than rows, you would get the exact error that you have.
Example:
Data::DataTable^ dataTable = gcnew Data::DataTable;
int rows = dataGridView1->RowCount; // Let's say this is 3.
int cols = dataGridView1->ColumnCount; // Let's say this is 5.
for (int iZeilen= 0; iZeilen < rows; iZeilen++ )
{
Data::DataRow^ row = dataTable->Rows->Add();
row->ItemArray[dataGridView1->RowCount]; // 3 items in row->ItemArray
for (int iSpalten= 1; iSpalten = cols; iSpalten++) // iSpalten = 5, always.
{
row->ItemArray[iSpalten] = (dataGridView1[iZeilen, iSpalten]); // Access row->ItemArray[5] = error, index out of range
}
dataTable->Rows->Add(row);
}
Though unrelated to your runtime error, you will find your dataTable not filled as you'd like. When accessing arrays, array[row, col] is generally correct. But for a DataGridView it's the opposite: dgv[col, row]. So this line row->ItemArray[iSpalten] = (dataGridView1[iZeilen, iSpalten]); should actually be row->ItemArray[iSpalten] = (dataGridView1[iSpalten, iZeilen]);. I don't know why that's the convention for dgv's, nor do I like it, but it is what it is.
This line row->ItemArray[dataGridView1->RowCount]; should probably be row->ItemArray[dataGridView1->ColumnCount]; because you are adding all the values of a single row of the dgv to a single row of the dataTable. Which means it has an item for each column.
This should get you the results you are wanting. Hope it helps.

Selective Infinite Loop in Making a Tournament Tree

I want to write a program that randomly generates a tournament tree using only the number of challengers. I read into another such problem, but the answer described how ranks would take part and seeding the players, which went a little over head.
The problem I am facing is that my algorithm produces an infinite loop for values between 1 and 4 inclusively. For all values otherwise, the program runs as desired.
My approach was to take in an array of strings for the competitors' names. Then, I would iterate over each position and randomly select a competitor's name to take that spot. Because I am swapping the names, I have to check for duplicates in the array. I believe this is where my code is experiencing issues.
Here is the snippet that actually determines the tree
for(int i = 0; i < no_players;) {
int index = rand() % ((no_players - i) + i);
// randomly choose an element from the remainder
string temp = players[index];
bool unique = true;
// check all the elements before the current position
for(int j = 0; j < i; j++) {
// if the element is already there, it is not unique
if(players[j] == temp)
unique = false;
}
// only if the element is unique, perform the swap
if(unique) {
players[index] = players[i];
players[i] = temp;
i++;
}
}
Any help is much appreciated!

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.

Why object be deleted without obvious code?

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.