Qt: set columns in treeView - c++

How can I implement this code I have for a qTreeWidget for a qTreeView?
for (const auto & i : names) {
QTreeWidgetItem * item = new QTreeWidgetItem(ui->treeWidget);
item->setText(0, QString::fromStdString(i));
ui->treeWidget->addTopLevelItem(item);
const std::unordered_map<std::string, double> map = m_reader.getMapFromEntry(i);
for (const auto & j : map) {
QTreeWidgetItem * item2 = new QTreeWidgetItem();
item2->setText(0,QString::fromStdString(j.first));
item2->setText(1,QString::number(j.second));
item->addChild(item2);
}
}
I have a model and a treeView, like this:
m_model = new QStandardItemModel(m_reader.getAllNames().size(),2,this);
ui->treeView->setModel(m_model);
I tried this, but that only shows one column:
QStandardItem * parentItem = m_model->invisibleRootItem();
for (const auto & i : names) {
QStandardItem * item = new QStandardItem(QString::fromStdString(i));
parentItem->appendRow(item);
const std::unordered_map<std::string, double> map = m_reader.getMapFromEntry(i);
for (const auto & j : map) {
QList<QStandardItem *> rowItems;
rowItems << new QStandardItem(QString::fromStdString(j.first));
rowItems << new QStandardItem(QString::number(j.second));
item->appendRow(rowItems);
}
}
With the treeWidget, I had so set the columnCount, like this:
ui->treeWidget->setColumnCount(2);
But treeView does not have a method like this.
So, to summarize: How can I implement a TreeView with more than one column?
EDIT:
To clarify, I want something like this:
|-A
| |-B-C
| |-D-E
where A is the parent and B,C,D,E the children, with B,D being in column 0 and C,E in column 1.
Hope this helps!

To support multiple columns, the model must contain data for multiple columns.
So in some sense, columns are a property of the model, not the view. Views then can decide to hide or rearrange certain columns (For example, a QListView always only shows the first column, while one can hide or reorder columns in a QTableView).
As you use QStandardItemModel, its documentation should give a few hints how to create multiple columns.
E.g., look at this example from the documentation:
QStandardItemModel model(4, 4);
for (int row = 0; row < 4; ++row) {
for (int column = 0; column < 4; ++column) {
QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
model.setItem(row, column, item);
}
}
It creates a model with 4 initial rows and columns each, and then fills it with items via setItem().
Alternatively, you can pass a list of items to QStandardItemModel::appendRow(), with an item for each column:
QList<QStandardItem*> items;
items.append(new QStandardItem(tr("One"));
items.append(new QStandardItem(tr("Two"));
model->appendRow(items);
This adds a new row with "One' in the first column and "Two" in the second. For even more ways to deal with multiple columns, see the QStandardItemModel docs.
Note: QTreeView expects the same number of columns on all levels of the hierarchy, so one should fill rows with empty items for the unused columns if need be.

Just an addition to answer by Frank Osterfeld:
QTreeView displays all columns of subtables inserted into top level QStandardItems. You just have to "force" it to show additional columns by inserting dummy QStandardItems into top-level table. Example:
QStandardItemModel *objectTreeModel = new QStandardItemModel(NULL);
QStandardItem *mainItem = new QStandardItem(tr("Main Item"));
QStandardItem *subItem1 = new QStandardItem(tr("Sub-Item 1"));
QStandardItem *subItem2 = new QStandardItem(tr("Sub-Item 2"));
mainItem->appendRow(QList<QStandardItem *>() << subItem1 << subItem2);
QStandardItem *dummyItem = new QStandardItem();
objectTreeModel->appendRow(QList<QStandardItem *>() << mainItem << dummyItem );
Now you will be able to see 2 columns and if you expand mainItem, both subitems will be visible.

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.

Adjusting QSqlTableModel for a QTreeView

I'm trying to put a MySQL table into a treeView.
Each entry has three values in the database - id, text, parentId.
This treeView needs to be editable so I really like the QSqlTableModel approach, as the functionality to save back to database is already built in.
Now, the treeView is showing all entries in the same line, of course, and I need a hierarchy. What would be the best way to build this hierarchy, while maintaining the editing and saving capabilities?
(I'm using MySQL.)
mainwindow.h
private: QSqlTableModel* goalModel;
mainwindow.cpp
(this makes a flat table hierarchy. the table is filtered based on which entry is clicked in another table)
void MainWindow::on_tableViewGoals_clicked(const QModelIndex &index)
{
goalModel = new QSqlTableModel(this);
goalModel->setTable("goals");
//Gets the id from the clicked entry to use as filter
QString idGoals = index.sibling(row,0).data().toString();
goalModel->setFilter( "id_goals=" + idGoals );
goalModel->setEditStrategy(QSqlTableModel::OnRowChange);
goalModel->select();
ui->treeView->setModel(goalModel);
...
I tried this. It's wrong, but I don't really know why. (the third column contains the parentId value, if the entry has it)
for (int row = 0; row < goalModel->rowCount(); ++row)
{
//if the entry has a value over 0 in the parentId column
if (goalModel->index(row,2).data().toInt() > 0)
{
//get number in column 2
int parentId;
parentId = goalModel->index(row,2).data().toInt();
QModelIndex newChild = goalModel->index(row,0);
//find QModelIndex row with matching number in column 0
for (int row = 0; row < goalModel->rowCount(); ++row)
{
if (goalModel->index(row,0).data().toInt() == parentId )
{
//make this entry that entry's child
QModelIndex newParent = goalModel->index(row,0);
newChild = goalModel->index(0,0,newParent);
}
}
}
}
All indexes are already made, so no need to make new ones, just assign a parent to all those who have one. How best to do that?

Add items to columns in QStandardItemModel

I am currently adding rows to my QTableView as such
QStandardItem* itm;
QStandardItemModel* model = new QStandardItemModel(this);
model->setColumnCount(2);
model->appendRow(new QStandardItem("Some Text in Column1");
How do I add items to column 2 dynamically by appending?
In the above example column 2 is empty. How do I add item to column 2?
Calling appendRow(QStandardItem *) only adds a single item to the first column. You would need to pass in a QList to appendRow() to add items to each column, e.g.:
QList<QStandardItem *> items;
items.append(new QStandardItem("Column 1 Text"));
items.append(new QStandardItem("Column 2 Text"));
QStandardItemModel* model = new QStandardItemModel(this);
model->setColumnCount(2);
model->appendRow(items);
See http://doc.qt.io/qt-5/qstandarditemmodel.html#appendRow for more detail.

Qt QTreeWidget alternative to IndexFromItem?

I have derived the class QTreeWidget and creating my own QtPropertyTree. In order to populate the tree with widgets (check boxes, buttons, etc) I am using the following code:
// in QtPropertyTree.cpp
QTreeWidgetItem topItem1 = new QTreeWidgetItem(this);
QTreeWidgetItem subItem = new QTreeWidgetItem(this);
int column1 = 0
int Column2 = 1;
QPushButton myButton = new QPushButton();
this->setIndexWidget(this->indexFromItem(this->subItem,column1), myButton);
QCheckBox myBox = new QCheckBox();
this->setIndexWidget(this->indexFromItem(this->subItem,column2), myBox);
This works fine, but the problem is that i want to avoid using the "indexFromItem" function since it is protected, and there are other classes that are populating the tree and need access to that funcion. Do you know any alternative to using that function?
You can try to use your QTreeWidget's model (QAbstractItemModel) to get the right index by the column and row numbers:
// Row value is 1 because I want to take the index of
// the second top level item in the tree.
const int row = 1;
[..]
QPushButton myButton = new QPushButton();
QModelIndex idx1 = this->model()->index(row, column1);
this->setIndexWidget(idx1, myButton);
QCheckBox myBox = new QCheckBox();
QModelIndex idx2 = this->model()->index(row, column2);
this->setIndexWidget(this->indexFromItem(idx2, myBox);
UPDATE
For sub items, the same approach can be used.
QModelIndex parentIdx = this->model()->index(row, column1);
// Get the index of the first child item of the second top level item.
QModelIndex childIdx = this->model()->index(0, column1, parentIdx);
The obvious solution is to de-protect indexFromItem like this:
class QtPropertyTree {
...
public:
QModelIndex publicIndexFromItem(QTreeWidgetItem * item, int column = 0) const
return indexFromItem (item, column) ;
}
} ;

How to remove all rows and child rows from QTreeview

I don't know why I have trouble removing all rows and sub rows from qtreeview
I'm using QStandardItemModel as the model. Now here is my code that doesn't work.
What could be the problem?
QModelIndex FirstQModelIndex;
QModelIndex parentQModelIndex;
int iMdlChidCound = m_model->hasChildren();
if(iMdlChidCound > 0)
{
// only if there at list 1 row in the view
FirstQModelIndex = m_model->item(0,0)->index();
QStandardItem* feedItem = m_model->itemFromIndex(FirstQModelIndex);
// get the parent of the first row its the header row
QStandardItem* parentItem = feedItem->parent();
// here im getting exception
int parent_rows= parentItem->hasChildren();
parentQModelIndex = m_model->indexFromItem(parentItem);
// now i like to delete all the rows under the header , and its dosnt work
if(parent_rows>0)
{
bool b = feedItem->model()->removeRows(0,y,parentQModelIndex);
}
}
It seems like a lot of what you're doing is superfluous. If your only goal is to remove all the rows from the model, you could probably just use QStandardItemModel::clear
In your code you're jumping between the model and the items in a way you don't have to.
if(m_model->hasChildren()) {
m_model->removeRows(0, m_model->rowCount());
}
That should do what you're seeking.
QStandardItemModel::clear()
Which clears all the items including the header rows.