QCombobox doesn't select when changing currentIndex - c++

In my constructor I connect to a Sqlite Database and read "Categories" (QStrings) from it.
I store them in a QList. I checked via debugger if they're empty, but everything is fine.
The int currCategoryIndex is set to 0 in my initializer list. And since QComboboxes start indexing from 0 it should be the first item.
An extract from my constructor:
dm.readDB_C(c_list); //reads into c_list
if(c_list.count() > 0) //has 1 or more items
updateCategories();
This is the part where I read the database, check if it's empty and if not call a function which adds those categories to a QComboBox.
updateCategories() function :
void MainWindow::updateCategories()
{
for(int i = 0; i < c_list.count(); i++){
if(ui->cmbCategory->findText(c_list[i]) != -1){ //-1 means "not found"
continue;
} else {
ui->cmbCategory->addItem(c_list[i]); //Add to QCombobox
}
}
ui->cmbCategory->setCurrentIndex(currCategoryIndex); //Should be the first item
}
I have all items in my QCombobox but none is selected. I have to click the box and select one myself. That's not supposed to happen.
What is wrong? Why doesn't it select one itself?
Edit:
currentIndexChanged signal:
void MainWindow::on_cmbCategory_currentIndexChanged(int index){
currCategoryIndex = index;
}

Perhaps the first item is empty?
Since you only add items to QComboBox, it is possible that index 0 is (from the beginning) an empty string.
try putting
ui->cmbCategory->removeItem(0);
in the beginning of updateCategories to check if it is the case
also, if currCategoryIndex is an index that does not exist (for example -1) QComboBox will also be empty (even if there is no empty string to choose) - in this case you can try to hardcode 0 in the function (if you want the item to always be the first one), or add additional check, for example:
if (0 > currentCategoryIndex || currentCategoryIndex > ui->cmbCategory->count())
currentCategoryIndex = 0

Related

How to set a new item in tabview Qt, and save the previous ones

When I use Qt tableView, I can just set item in the first row. When I click add, it will cover the former, but not set a new item. Maybe my slot function is incorrect. But I don't know how to handle it.
void Widget::on_addButton_clicked()
{ int i = 0;
EditDialog editDialog(this);
if(editDialog.exec() == 1)
{
model->setItem(i,0,new QStandardItem(editDialog.getID()));
model->setItem(i,1,new QStandardItem(editDialog.getPriority()));
model->setItem(i,2,new QStandardItem(editDialog.getTime()));
}
i++;
}
Please, have a look into doc. QStandardItemModel::setItem():
Sets the item for the given row and column to item. The model takes ownership of the item. If necessary, the row count and column count are increased to fit the item. The previous item at the given location (if there was one) is deleted.
(Emphasizing is mine.)
If a row shall be inserted before end of table (e.g. as first), then it's necessary to do this explicitly.
This can be achieved by calling QStandardItemModel::insertRow() before setting the items.
This could e.g. look like this:
// fills a table model with sample data
void populate(QStandardItemModel &tblModel, bool prepend)
{
int row = tblModel.rowCount();
if (prepend) tblModel.insertRow(0);
for (int col = 0; col < NCols; ++col) {
QStandardItem *pItem = new QStandardItem(QString("row %0, col %1").arg(row).arg(col));
tblModel.setItem(prepend ? 0 : row, col, pItem);
}
}
I took this from an older post of mine. The complete sample can be found in my answer to SO: Stop QTableView from scrolling as data is added above current position.

Accessing a Combobox inside a dataGridView Column?

I'm working on a scheduling program, and inside the dataGridView, we have a few ComboBox Columns that are populated by 3 entries upon creation, but I wanted to be able to add more as the user creates them, but I have no idea how you would access the combobox data. Any help is appreciated!
// this is initialized in a separate part.
/* System::Windows::Forms::DataGridView^ dataGridView;*/
System::Windows::Forms::DataGridViewComboBoxColumn^ newCol =
(gcnew System::Windows::Forms::DataGridViewComboBoxColumn());
dataGridView->Columns->AddRange(gcnew cli::array< System::Windows::Forms::DataGridViewComboBoxColumn^ >(1) {newCol});
// add the choices to the boxes.
newCol->Items->AddRange("User inputted stuff", "More stuff", "Add New...");
Solution
If you have access to the data from the user entry and you know the column index for the DataGridViewComboBoxColumn, you should be able to just do the following wherever needed:
DataGridViewComboBoxColumn^ comboboxColumn = dataGridView->Columns[the_combobox_column_index];
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
}
Comments Response
how could you change the selected index of that combobox (the one that
the edit was triggered on)? [...] we want it so that when the new item
is added the selected index is set to that new item).
Couple of ways come to mind.
Add a single line within the if-statement of the above code. This will set the default displayed value for each DataGridViewComboBoxCell in the DataGridViewComboBoxColumn.
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
comboboxColumn->DefaultCellStyle->NullValue = "the new user entry";
}
Pros: Clean, efficient. Previous user-selected values are left intact. The cell's FormattedValue will display the new user value by default if no other selection has been made.
Cons: Doesn't actually set a cell's selected value, so Value will return null on cells not explicitly user-selected.
Actually set the value of certain cells (based on your criteria) to the user-added value.
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
for (int i = 0; i < dataGridView->Rows->Count; i++)
{
DataGridViewComboBoxCell^ cell = dataGridView->Rows[i]->Cells[the_combobox_column_index];
if ( cell != nullptr /* and your conditions are met */ )
{
cell->Value = "the new user entry";
}
}
}
Pros: The Value of targeted cells is actually set to the new user value.
Cons: Logic deciding which cells should be affected is more complicated.

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)

How to move an item up and down in a wxListCtrl (wxwidgets)

This should be pretty easy but I'm having a heck of a time doing it. Basically I want to move a row in my wxListCtrl up or down. I posted this to wxwidgets forum and got the following code.
m_list->Freeze();
wxListItem item;
item.SetId(item_id); // the one which is selected
m_list->GetItem(item); // Retrieve the item
m_list->DeleteItem(item_id); // Remove it
item.SetId(item_id - 1); // Move it up
m_list->SetItem(item); // Apply it's new pos in the list
m_list->Thaw();
which doesn't work. The element is deleted but not moved up (I guess the setitem line is not working). Then I thought to just switch the text and the image but I can't even get the text from the row reliably. I have
int index = m_right->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
wxString label = m_right->GetItemText(index);
if(index == 0)
return;
wxListItem item;
item.SetId(index);
bool success = m_right->GetItem(item);
wxString text = item.GetText();
but text is blank even though there is text and the index is correct. So, I'm stuck not even being able to do the most basic task. Anybody know how to do this? The code runs in a button callback (the user presses a little up arrow and my code executes to try to move it). I'm using 2.9.1 on windows.
I made it work like this with wxWidgets 2.9.3 :
void FileSelectionPanel::OnMoveUp( wxCommandEvent& WXUNUSED(evt) )
{
int idx = _listCtrl->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
if( idx == 0) idx = _listCtrl->GetNextItem( 0, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
_listCtrl->Freeze();
while( idx > -1 ) {
wxListItem item;
item.SetId(idx); _listCtrl->GetItem(item);
item.SetId(idx-1); _listCtrl->InsertItem(item);
_listCtrl->SetItemData( idx-1, _listCtrl->GetItemData( idx+1 ));
for( int i = 0; i < _listCtrl->GetColumnCount(); i++ ) {
_listCtrl->SetItem( idx-1, i, _listCtrl->GetItemText( idx+1, i ));
}
_listCtrl->DeleteItem( idx + 1 );
idx = _listCtrl->GetNextItem( idx-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
}
_listCtrl->Thaw();
}
The thing I noticed it that wxListItem is more of a convenience struct, for storing state of the view and help pass values into the wxListCtrl "nicely". It is in no way bound to what is actually inside of the wxListCtrl.
Hope this still helps anyone !
Even there is already an answer that is checked. I have the same problem here, but my list is unordered. By looking into wxWidgets' code I found out there is another important information inside the wxListItem object - the mask. I got my reordering to work correctly by setting the mask value to -1, which means that all data shall be copied. This includes the item text as well as other information, like the item data (which was important in my case).
wxListItem item;
item.SetId(item_id); // set needed id
item.SetMask(-1); // set needed data
m_list->GetItem(item); // actually retrieve the item
m_list->DeleteItem(item_id); // remove old copy
item.SetId(item_id - 1); // move item up
m_list->InsertItem(item); // insert copy of item
I also had to use "InsertItem" instead of "SetItem". Otherwise, there was no new item inserted, but an existing one overwritten (see also tomcat31's answer).
Is the list ordered? if it is auto ordering it may be ignoring the order you are trying to apply.
From recollection the internal order was not necessarily sequential, you might have to get the index of the previous item and go one before it.