QTreeWidgetItem data - c++

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

Related

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

Filter with QComboBox C++

I want to create an editable QComboBox which filters results according to the search query and updates the dropdown entries accordingly.
After reading How do I Filter the PyQt QCombobox Items based on the text input? I tried to implement something similar in C++.
But I can't store anything inside the QComboBox now. Even after adding new entries through addItem() the total count remains 0.
What is the reason for this and how do I insert entries inside the QComboBox with QSortFilterProxyModel?
Here is the relevant snippet of the code:
SearchBox = new QComboBox(this);
SearchBox->setEditable(true);
// Try adding a few entries and check if they persist after changing the model
SearchBox->addItem(QString("hi"));
SearchBox->addItem(QString("bye"));
int count = SearchBox->count(); // count = 2
ProxyModel = new QSortFilterProxyModel;
ProxyModel->setSourceModel(SearchBox->model());
ProxyModel->setFilterCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
SearchBox->setModel(ProxyModel);
// Check count again
count = SearchBox->count(); // count = 0 <- Why?
// Try adding new entries
SearchBox->addItem(QString("Hi again"));
count = SearchBox->count(); // count = 0 .. So new entries don't get stored
Completer = new QCompleter(ProxyModel,this);
Completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
SearchBox->setCompleter(Completer);
QObject::connect(SearchBox->lineEdit(), SIGNAL(textChanged(const QString)), ProxyModel, SLOT(setFilterFixedString(const QString)));
QObject::connect(Completer, SIGNAL(activated(const QString &)), this, SLOT(onCompleterActivated(const QString &)));
Use QStringListModel to store items. Application crashes if proxy model have no items (if filter string filters out all items)(this needs further investigation - is this completer issue or combobox). This can be fixed by not applying such filter (onTextChanged(QString text) slot). Completer completes input if theres only one item (not sure if it's ok). And sometimes checkbox doubles all items (don't know why). If this issues is critical, I think you need to write custom ComboBox from scratch and this is serious work.
{
SearchBox = new QComboBox(this);
SearchBox->setEditable(true);
QStringList Items;
Items << "hi" << "bye";
StringListModel = new QStringListModel();
StringListModel->setStringList(Items);
ProxyModel = new QSortFilterProxyModel;
ProxyModel->setSourceModel(StringListModel);
ProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
SearchBox->setModel(ProxyModel);
// Check count again
int count = SearchBox->count(); // count = 2
// Try adding new entries
QStringList Items_ = StringListModel->stringList();
Items_ << "hi again";
StringListModel->setStringList(Items_);
count = SearchBox->count(); // count = 3
Completer = new QCompleter(ProxyModel,this);
Completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
SearchBox->setCompleter(Completer);
QObject::connect(SearchBox->lineEdit(), SIGNAL(textChanged(const QString)), this, SLOT(onTextChanged(QString)));
QObject::connect(Completer, SIGNAL(activated(const QString &)), this, SLOT(onCompleterActivated(const QString &)));
}
void MainWindow::onTextChanged(QString Text) {
QStringList Items = StringListModel->stringList();
QString Item;
foreach(Item,Items) {
if (Item.indexOf(Text) > -1) {
ProxyModel->setFilterFixedString(Text);
return;
}
}
}

C++ QListView with icons And Signals on items clicked

I have a QListview where I have set a model. The model contains the QList. Now I want to get the contents of QListview on clicking the item. But I don't know how to do this? Through some tutorials I followed I am able to get two items appear in the QListview as follows. But I dont know how to make it work?? Please anyone help. The code which I am working as follows.
listviewmodel =new QListView;
listviewmodel->setModel( createModel() );
listviewmodel->setViewMode(QListView::IconMode);
listviewmodel->setIconSize(QSize(size().width()/8, size().height()/8));
connect(listviewmodel,SIGNAL(clicked(const QModelIndex)),this,SLOT(ItemClicked(QModelIndex)));
QAbstractItemModel *MainWindow::createModel()
{
QStandardItemModel *model = new QStandardItemModel();
QList<QStandardItem *> listItem;
QStandardItem *item2 = new QStandardItem();
item2->setIcon(QIcon(QPixmap::fromImage(qimages2)));
listItem << item2;
QStandardItem *item1 = new QStandardItem();
item1->setIcon(QIcon(QPixmap::fromImage(qimages1)));
listItem << item1;
model->appendColumn(listItem);
return model;
}
void MainWindow::ItemClicked (QModelIndex index )
{
textEdit->setText(index.data().toString());
}
I checked your code in QtCreator and it works as you described. So what is the problem? Are you getting some errors? Maybe did you not put declaration of ItemClicked(QModelIndex) below the public slots:? Are you sure that connect returns true? Try to check it:
bool success = connect(listviewmodel,SIGNAL(clicked(const QModelIndex)),this,SLOT(ItemClicked(QModelIndex)));
Q_ASSERT(success);
EDIT: If you want to display only icons at QListView and get some text informations after clicking on specified item, you can do it in the following way:
item2->setIcon(QIcon(QPixmap::fromImage(qimages2)));
item2->setData("informations about item2", Qt::UserRole);
(...)
void MainWindow::ItemClicked (QModelIndex index )
{
QString data = index.data(Qt::UserRole).value<QString>();
ui->textEdit->setText(data);
}

Getting variable from widget in a QListWidget

I have a custom QWidget class called VideoWidget. Its source file looks something like this:
VideoWidget::VideoWidget(QWidget *parent, string test) :
QWidget(parent)
{
pathname=test;
QLabel *label= new QLabel(pathname.c_str(), this);
//...
}
string VideoWidget::getFilePath(){
return pathname;
}
In my MainWindow class I add the VideoWidget to a QListWidget through looping through a xml file and getting the string argument from that file like this:
QDomNode node = rootXML.firstChild();
while( !node.isNull() )
{
if( node.isElement() )
{
QDomElement element = node.toElement();
VideoWidget* mytest = new VideoWidget(this, element.attribute( "Pathname", "not set").toStdString());
QListWidgetItem* item = new QListWidgetItem;
item->setSizeHint(QSize(150,100));
ui->myList->addItem(item);
ui->myList->setItemWidget(item,mytest);
}
node = node.nextSibling();
}
This correctly fills my QListWidget with the VideoWidget where all the labels have a different value.
Now I'd like to get the pathname variable everytime I doubleclick on a item in the QListWidget like this:
connect(ui->myList,SIGNAL(doubleClicked(QModelIndex)),this,SLOT(playClip(QModelIndex)));
void MainWindow::playClip(QModelIndex index){
QListWidgetItem* item = ui->myList->itemAt(0,index.row());
VideoWidget* widget = dynamic_cast<VideoWidget*>(ui->myList->itemWidget(item));
cout << widget->getFilePath() << endl;
}
My problem is that widget->getFilePath() always returns the same value for every clicked widget. It is the value of the first time I set pathname=test;. What am I missing here?
This is probably mistake:
QListWidgetItem* item = ui->myList->itemAt(0,index.row());
Method "itemAt" takes x and y coordinates, not indexes. Use "takeItem" instead.
Next thing I want to say is that this part:
ui->myList->itemWidget(item)
is useless. You can convert "item" directly.
And last - use qobject_cast since you use Qt. And never use dynamic_case (especially when you anyway do not check result against NULL).