If-else condition in loop: future iterations overwrite results of previous iterations - c++

I try to highlight a selectedItem and its children among a list of items.
const QList<Item *> items = /* ... */;
Item *selectedItem = /* ... */;
Q_FOREACH( Item *item, items ) {
if ( selectedItem == item ) {
item->setHighlightEnabled(true); // Highlight selected item
} else {
item->setHighlightEnabled(false); // De-highlight other items
}
}
The item->setHighlightEnabled method does the same for children recursively:
void Item::setHighlightEnabled(bool enabled)
{
if (enabled) {
/* highlight item */
} else {
/* de-highlight item */
}
// Go through all children and highlight them too
Q_FOREACH (Item *child, children())
child->setHighlightEnabled(enabled);
}
Works fine but there is a bug. We loop over all items. When a parent is selected, parent and its children are highlighted. But then loop continues iterating over children. Since children are NOT selected, therefore they are de-highlighted (overwriting highlight in previous loop iterations). I wonder what is the best practice to fix it.

As far as i understand your problem, you could make to for-loops. In the first one you de-highlight all elements. And the second one you leve as is and just stop it with a break; statement, once it found the selected item.

De-highlight everything first, then highlight the current selection.
If selectedItem already is a pointer to the only element you want to highlight, you don't need to search for it, you only need:
Q_FOREACH (Item *item, items)
item->setHighlightEnabled(false);
selectedItem->setHighlightEnabled(true)

Related

CTabCtrl DeleteItem does not work in every case

i have a function which loops through map (the map contains the index of my CTabCtrl-Tab and the ID of the Document which is shown in the Tab) and if the ID of the current selected Tab is not similar to the ID of the current looped tab, the tab should be removed.
int deleteTab = -1;
for (auto i : tabIndexToFBNR)
{
deleteTab = i.first;
if (i.second == m_pDlgSubFBs.at(m_AktTab)->m_pRecFB->m_ID)
deleteTab = -1;
if (deleteTab != -1)
m_tabSubFB.DeleteItem(deleteTab);
}
Problem is: Some tabs are removed, some are not. The return code of DeleteItem is always "1".
Any ideas?

QListView: how to automatically scroll the view and keep current selection on correct items in view when removing items from the top

I have a list view with a custom model. The model allows me to add text to the bottom of the list (using 'addText(const QString&)') and to remove items from the top of the list (using 'removeItemsFromTop(int _iCount)').
What is the best way to add text to the view and keep the model size under some maximum (lets say 'MAX_LIST_SIZE'), while always maintaining the view (i.e. current selection and items in view should not change when items are removed).
The solution should preferably be a function that I can use wherever I'm using my custom model.
I have looked at indexAt(...), scrollTo(...), currentIndex(...) and setCurrentIndex(...) methods on QListView, but I can't figure out how to put all of this together.
So far I have (for auto scrolling the view)
// add items here ...
// cleanup
QModelIndex indexViewTop = listView->indexAt(QPoint(8, 8));
if (listModel->rowCount() > MAX_SIZE)
{
int iRemoveCount = (listModel->rowCount() - MAX_SIZE) + MAX_SIZE/10;
listModel->clearTextFromFront(iRemoveCount);
listView->scrollTo(indexViewTop.sibling(indexViewTop.row() - iRemoveCount, 0), QAbstractItemView::PositionAtTop);
}
This is supposed to scroll the list view as items are removed to keep the view consistent, but indexAt(...) always returns an invalid index.
For keeping the selection consistent I tried:
// add items her ...
// cleanup
if (listModel->rowCount() > MAX_SIZE)
{
int iCurrentViewIndex = listView->currentIndex().row();
int iRemoveCount = (listModel->rowCount() - MAX_SIZE) + MAX_SIZE/10;
listModel->clearTextFromFront(iRemoveCount);
listView->setCurrentIndex(listModel->index(iCurrentViewIndex - iRemoveCount, 0));
}
This seems to work, but I'm still stuck on the auto scrolling.
I did a queue-like table model implementation. I think it is similar for QAbstractItemModel. Best way would be to use QQueue to store data.
Now, this is a snipped for QAbstractTableModel (which is subclass of QAbstractItemModel so it should work; mEvents is QQueue):
// custom table for inserting events
void EventPreviewTableModel::insertEvent(const DeviceEvent &event) {
beginInsertRows(QModelIndex(), 0, 0);
mEvents.enqueue(event);
endInsertRows();
if (mEvents.size() > SIZE) {
beginRemoveRows(QModelIndex(), mEvents.size(), mEvents.size());
mEvents.dequeue();
endRemoveRows();
}
}
And also override data() and rowCount() to serve correct data.
For second part using ItemIsSelected flag for items you want to have selected is done through: Qt::ItemFlags QAbstractItemModel::flags(const QModelIndex & index)
This is my current approach and it seems to work well:
void addTitlesToList(Model *model, QListView *view, std::vector<Object*> &items)
{
QScrollBar *pVerticalScrollBar = view->verticalScrollBar();
bool bScrolledToBottom = pVerticalScrollBar->value() == pVerticalScrollBar->maximum();
QModelIndex indexViewTop = view->indexAt(QPoint(8, 8));
// add to model
model->pushItems(items);
// cleanup if model gets too big
if (model->rowCount() > model->maxListSize())
{
int iCurrentViewIndex = view->currentIndex().row();
int iRemoveCount = (int)(model->rowCount() - model->maxListSize()) + (int)model->maxListSize()/10;
model->removeItemsFromFront(iRemoveCount);
// scrolls to maintain view on items
if (bScrolledToBottom == false)
{
_pView->scrollTo(indexViewTop.sibling(indexViewTop.row() - iRemoveCount, 0), QAbstractItemView::PositionAtTop);
}
// maintain selection
if (iCurrentViewIndex >= iRemoveCount)
{
view->setCurrentIndex(_pModel->index(iCurrentViewIndex - iRemoveCount, 0));
}
else
{
view->setCurrentIndex(QModelIndex());
}
}
// move scroll bar to keep new items in view (if scrolled to the bottom)
if (bScrolledToBottom == true)
{
view->scrollToBottom();
}
}
One issue I had with indexAt(QPoint(...)) is that I was calling it after adding the items to the list, which seems to cause it to always return an invalid index. Calling indexAt before anything is added to the model seems to work.
I also added automatic 'scroll to bottom' if already there (i.e. the view either stays fixed on specific items or sticks to the latest items if scrolled all the way to the bottom).

How to select an item in QTreeWidget?

I am trying to make a functionality, that will select the last item in the QTreeView, if there are no items selected. I don't know how to select an item within the program. So far I have tried this, but it doesn't work.
if (selectedItemList.length() == 0) // no items selected
{
QItemSelectionModel *selection = new QItemSelectionModel(treeWidget->model());
QModelIndex index = treeWidget->model()->index(treeWidget->model()->rowCount() - 1,
0, QModelIndex());
selection->select(index, QItemSelectionModel::Select);
treeWidget->setSelectionModel(selection);
return;
}
treeWidget is a QTreeWidget object and selectedItemList is the list of selected items in it. I would appreciate all help.
if (treeWidget->selectedItems().size() == 0 && treeWidget->topLevelItemCount())
{
treeWidget->topLevelItem(treeWidget->topLevelItemCount() - 1)->setSelected(true);
}
You can interact with the selection directly using the items.
QList<QTreeWidgetItem*> selectedItemList = tree->selectedItems();
if (selectedItemList.length() == 0) // no items selected
{
tree->topLevelItem(tree->topLevelItemCount()-1)->setSelected(true);
}

Remove selected items from listWidget

How to remove selected items from a QListWidget?
I have tried write the following code, but it does not work.
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
foreach(QListWidgetItem item, items){
ui->listWidget->removeItemWidget(item);
}
One way to remove item from QListWidget is to use QListWidget::takeItem which removes and returns the item :
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
foreach(QListWidgetItem * item, items)
{
delete ui->listWidget->takeItem(ui->listWidget->row(item));
}
Another way is to qDeleteAll :
qDeleteAll(ui->listWidget->selectedItems());
To give a solution with removeItemWidget:
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
foreach(QListWidgetItem* item, items){
ui->listWidget->removeItemWidget(item);
delete item; // Qt documentation warnings you to destroy item to effectively remove it from QListWidget.
}

Hide the checkbox from a QListView item

I wave a QListView that is backed by a QStandardItemModel. Under certain circonstances, the QStandardItem are made checkable. A checkbox gets displayed besides the item's display. At some point, I want to remove hide the QStandardItem checkbox. I set its checkable state to false but it doesn't hide the checkbox (though it cannot be checked anymore).
The only way I have found of hiding the checkbox is to replace the item with a new one. This doesn't seem the proper way to preceed.
This is the code:
MyModel::MyModel(QObject *parent):QStandardItemModel(parent){}
void MyModel::createItem(int row, const QString &text)
{
setItem(row, new QStandardItem(text));
}
void MyModel::setCheckable(int row)
{
item(row)->setCheckState(Qt::Unchecked);
item(row)->setCheckable(true); // A checkbox appears besides the text
}
void MyModel::hideCheckBox(int row)
{
item(row)->setCheckState(Qt::Unchecked);
item(row)->setCheckable(false); // does not work
// I need to completely replace the item for the checkbox to disapear.
// This doesn't seem the proper way to proceed
setItem(row, new QStandardItem(item(row)->data(Qt::DisplayRole).toString()));
}
Is there better way to proceed?
When you call setCheckState or setCheckable, the qt will update the data of list item by adding or setting a Qt::CheckStateRole data. If the Qt::CheckStateRole data is existed, the check icon will be shown. So you need remove it from the data map of the list item.
Finally, the code of hideCheckBox should be:
void MyModel::hideCheckBox(int row)
{
// check the item pointer
QStandardItem* pitem = item(row);
if (pitem == NULL) return;
// find and delete the Qt::CheckStateRole data
QMap<int, QVariant> mdata = itemData(pitem->index());
if (mdata.remove(Qt::CheckStateRole))
{
setItemData(pitem->index(), mdata);
}
}
Hope it useful. :)
I think the presence of the check boxes in items defined by item flags, so that I would write the function in the following way:
void MyModel::hideCheckBox(int row)
{
// Does not set the Qt::ItemIsUserCheckable flag.
item(row)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}