How to select an item in QTreeWidget? - c++

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);
}

Related

How to select item in QComboBox with QTreeView

I was trying to select item "leaf2" from QComboBox with QTreeView in the below code.
I just want to select items with no child from text by code. (if there is a child, it won't be selectable)
How can I select item or index with no child item?
Can anyone help me with this problem?
Thank you.
void Main::initView()
{
QStandardItemModel *model = new QStandardItemModel;
QStandardItem *root_item = model->invisibleRootItem();
// Build Model Items
QStandardItem *node_item = NULL;
node_item = new QStandardItem("Node");
node_item->setFlags(Qt::ItemIsEnabled);
root_item->appendRow(node_item);
QStandardItem *leaf_item = new QStandardItem("leaf1");
leaf_item ->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
node_item->appendRow(leaf_item );
leaf_item = new QStandardItem("leaf2");
leaf_item ->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
node_item->appendRow(leaf_item );
// Set Model to TreeViewComboBox
ui.cb_treevew->setModel(model);
ui.cb_treeview->setCurrentIndex(0); // "Node" is selected.
ui.cb_treeview->setCurrentIndex(1); // Nothing is selected.
ui.cb_treeview->setCurrentIndex(2); // Nothing is selected.
ui.cb_treeview->setCurrentIndex(3); // Nothing is selected.
}
Here is my code for CTreeViewComboBox.
CTreeViewComboBox::CTreeViewComboBox(QWidget *parent) : QComboBox(parent)
{
QTreeView* treeView = new QTreeView(this);
treeView->setEditTriggers(QTreeView::NoEditTriggers);
treeView->setSelectionBehavior(QTreeView::SelectRows);
treeView->setSelectionMode(QTreeView::SingleSelection);
treeView->setItemsExpandable(true);
treeView->header()->setVisible(false);
treeView->setWordWrap(true);
setView(treeView);
}
PS: I tried to select items as following, but not working. :(
ui.cb_treeview->treeView()->setCurrentIndex(getModelIndexFromText("leaf2"));
If the nodes with children will never have the same text with the nodes without children then the following method is appropriate.
QModelIndexList modelIndexes = model->match(
model->index(0, 0),
Qt::DisplayRole,
"leaf2",
-1,
Qt::MatchRecursive);
QModelIndex index = modelIndexes.first();
ui.cb_treeview.setRootModelIndex(index.parent());
ui.cb_treeview.setCurrentIndex(index.row());
On the other hand, if the nodes with children can have the same text as the nodes without children, you should use the following method.
QModelIndexList modelIndexes = model->match(
model->index(0, 0),
Qt::DisplayRole,
"leaf2",
-1,
Qt::MatchRecursive);
QModelIndexList::iterator tstIt = std::find_if(modelIndexes.begin(),
modelIndexes.end(),
[] (const QModelIndex & index) {
return !index.model()->hasChildren(index);
});
ui.cb_treeview.setRootModelIndex(tstIt->parent());
ui.cb_treeview.setCurrentIndex(tstIt->row());
In both cases I am assuming that nodes without children always have different texts. If nodes without children match the name, choose one of them.

Selecting a row in QTableWidget with conditions to disable pushButton

I'm not sure if the title is correct but this is the situation:
When a row is selected and contains Application Status of FULL_MEMBER,
A pushButton is enabled else it is disabled.
You have to get the text of the item (cell) in the fifth column of the selected row, each time the selection changes.
Have a slot like this in your widget:
private slots:
void tableSelectionChanged();
In the widget constructor, connect it to the table widget signal itemSelectionChanged:
connect(ui->tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(tableSelectionChanged()));
The slot definition is like this:
//retrieve a list of all selected rows
QModelIndexList list = ui->tableWidget->selectionModel()->selectedRows();
if(list.size() > 0)
{
//retrieve the index of the first (and only, maybe?) selected row
int row = list[0].row();
const int col = 4; //Application Status column id
QTableWidgetItem * item = ui->tableWidget->item(row, col); //the item we want to inspect
ui->pushButton->setEnabled( item->text() == "FULL_MEMBER" );
}

How to access childWidgets in a QTreeView using QModelIndex?

I'm working on an application using the Qt library (version 4.8).
I have a QTreeView with a QStandardItemModel. My widget looks like that:
Item1
subitem11
subitem12
Item2
subitem21
subitem22
Item3
subitem31
subitem32
Here is how I add the items to my QTreeView:
model->setItem(0, 0, item1);
item1->setChild(0, 0, subitem12);
I want to take an action only when the user double cliked an item (and do nothing when he clicked a subitem). So I use the doubleClicked(const QModelIndex & index) signal.
I want to process the information about the item/subitem which was double cliked by the user. So I get the row of my item/subitem:
index.row();
But every time I try to reference to the item/subitem to display its name or check if it has children, I can only access the items:
index.model()->item(row)->text();
My question is: how can I acces the subitems (vbetween items abd subitems) in my slot? Or how can I prevent them from emitting the signal? I can't disable them - it would be too confusing for the user.
Edit: The problem is that every time I click on an item or subitem and execute:
index.model()->item(row)->hasChildren();
or:
index.model()->item(row)->parent() == 0;
I get true as result. So I can reference only the items.
My question is: What is the correct way to reference to subitems?
When you are trying to access model items by row index, the model returns top-level item at that row. Use itemFromIndex instead:
auto item = index.model()->itemFromIndex(index);
if (item && item->hasChildren()){
// item is not a leaf
}
EDIT index.model() returns QAbstractItemModel*, so a cast is also necessary here (or, better, storing a pointer to the standard model somehwere in the code).
I would do it like this:
// Define your custom role to store item type.
enum MyRoles
{
ItemTypeRole = Qt::UserRole + 1
};
// Define item types.
enum ItemType
{
Primary,
Secondary
};
Than set the proper item type for all your items:
QStandardItem* item = new QStandardItem("Item1");
item->setData(Primary, ItemTypeRole);
QStandardItem* subItem = new QStandardItem("SubItem1");
subItem->setData(Secondary, ItemTypeRole);
And in your slot connected to the doubleClicked signal acces the type like this:
ItemType type = static_cast<ItemType>(index.data(ItemTypeRole).toInt());
if (type == Primary)
std::cout << "It's a Primary item!" << std::endl;
else if (type == Secondary)
std::cout << "It's a Secondary item!" << std::endl;
You can't disable the signal for the subitems. However, you can check if an item has a parent. It it doesn't, it is an item and if it does, it's a subitem.
if (item->parent() != 0)
.. //subitem
else
.. //item
An alternative would be to use the data() function to set some special value to distinguish between the two.
item1->setData(QVariant("item"));
subitem1->setData(QVariant("subitem"));
Then query the value in your doubleclick handler:
QVariant var = item->data();
if (var.toString() == "item")
...
else if (var.toString() == "subitem")
...

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).

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);
}