How to access childWidgets in a QTreeView using QModelIndex? - c++

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

Related

Show Last element in QListView

It sounds trivial, but I could not find the function to show the last added element in a QListView.
It works with a model
// Create model
model = new QStringListModel(this);
// Make data
QStringList List;
// Populate our model
model->setStringList(List);
// Glue model and view together
listView->setModel(model);
Elements are added with
void WidgetMessageList::addString(const QString & message)
{
if(model->insertRow(model->rowCount())) {
QModelIndex index = model->index(model->rowCount() - 1, 0);
model->setData(index, message);
}
}
In this function the shown element should also be the last.
QAbstractItemView::scrollTo
Scrolls the view if necessary to ensure that the item at index is
visible. The view will try to position the item according to the given
hint.
http://doc.qt.io/archives/qt-4.8/qabstractitemview.html#scrollTo
Create a class attibute to hold the last index
Connect QAbstractItemModel::rowsInserted to a slot in your application
In the slot update the index accordingly

Qt5 : Get value of item clicked in a listview

I'm making a Qt5.7 application where I am populating a QListView after reading stuff from a file. Here's the exact code of it.
QStringListModel *model;
model = new QStringListModel(this);
model->setStringList(stringList); //stringList has a list of strings
ui->listView->setModel(model);
ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers); //To disable editing
Now this displays the list just fine in a QListView that I have set up. What I need to do now is to get the string that has been double clicked and use that value elsewhere. How do I achieve that?
What I tried doing was to attach a listener to the QListView this way
... // the rest of the code
connect(ui->listView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(fetch()));
...
And then I have the function fetch
void Window::fetch () {
qDebug() << "Something was clicked!";
QObject *s = sender();
qDebug() << s->objectName();
}
However the objectName() function returns "listView" and not the listView item or the index.
The signal already provides you with a QModelIndex which was clicked.
So you should change your slot to this:
void Window::fetch (QModelIndex index)
{
....
QModelIndex has now a column and a row property. Because a list has no columns you are interessted in the row. This is the index of the item clicked.
//get model and cast to QStringListModel
QStringListModel* listModel= qobject_cast<QStringListModel*>(ui->listView->model());
//get value at row()
QString value = listModel->stringList().at(index.row());
You should add the index as parameter of your slot. You can use that index to access the list
Your code should be some thing like this.
void Window::fetch (QModelIndex index) {
/* Do some thing you want to do*/
}

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

QTreeWidgetItem data

I have a following data:
1.0 2.0 3.1 4.1 5.2
Where Index.SubIndex
But I want to show in QTreeWidget as following:
Index 1
Index 3
Index 4
Index 2
Index 5
But I did as following:
QTreeWidgetItem *child = new QTreeWidgetItem();
child->setText("Index " + QString::number(index));
But I want to save hidden data, which I should to get when user selected any item of tree.
How I can do this? Which signal/slot I can use for it? I mean I should get not "Index 1" or "Index 2" and etc, I should get 1,2,3,4,5,6 aand etc. And parse text is NOT solution.
I don't understand half of your question. But for storing custom data in a QTreeWidgetItem I would subclass it:
class my_tree_item : public QTreeWidgetItem
{
public:
my_tree_item(custom_data* d, QString& str) { this->dat=d; QTreeWidgetItem(str); }
~my_tree_item();
inline custom_data* get_data { return this->dat; }
private:
custom_data* dat;
};
If you then receive a QTreeWidgetItem from a signal you can simply retrieve the data
custom_data* c = static_cast<my_tree_item*>(some_qtreewidgetitem)->get_data();
Use the Qt::UserRole data field to store your custom data and retrieve it when a QTreeItemWidget is selected:
QTreeWidgetItem* child = new QTreeWidgetItem();
child->setText(0, "Index " + QString::number(index));
// save internal data to retrieve later on
child->setData(0, Qt::UserRole, QVariant(index));

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