dynamic qtreeview with custom delegates - c++

I'm using Qt 5.6 and my goal is to develop a dynamic tree using QTreeView that needs to populate items such as combo boxes, edit box, checkbox, buttons..etc depending on the settings loaded and the tree data can change as new data is loaded. There will be many nodes to populate in the tree with parent and child nodes.
I've read about different options to implement this type of treeview such as populating a QTreeView and implementing my own delegate by inheriting from QITemDelegate or QStyledItemDelegate or using Qt Quick's QML treeview.
For my purposes I've chosen the first option to dynamically populate a tree and modify parameters but I'm having trouble setting the delegate i want for a selected cell only as the only options to set the delegate in the tree is to setItemDelegateForRow, setItemDelegateForColumn or setItemDelegate. The tree will look something like the below. How should i set a different delegate for each of the child nodes?
by (col, row)
Parent(cell 0,0)
->Child(cell 0,1) -combo box(1,1)
->Child(cell 0,2) -button(1,2)
->Child(cell 0,3) -check box(1,3)
...
...etc
Parent(cell 0,10)
->Child(cell 0,11) - edit box(1,11)
->Child(cell 0,12) - edit box(1,12)
->Child(cell 0,13) - check box(1,13)
...
...etc
Edit
This is a simplified version of how I have setup my tree view so far. Found out the answer to why my child node column 1 was not showing was because i did not set the column count of the model. I have updated the code below and it works now.
enum EditType
{
TYPE_Text,
TYPE_CheckBox,
TYPE_ComboBox,
TYPE_Button
};
QTreeView* tree = new QTreeView(this);
QStandardItemModel* model = new QStandardItemModel;
model->setColumnCount(2);
tree->setModel(model);
//set custom delegate for column 1 of the tree
CustomDelegate *customDelegate = new CustomDelegate(this);
tree->setItemDelegateForColumn(1, customDelegate);
// Add parent nodes
QStandardItem* parent1 = new QStandardItem(tr("parent1"));
QStandardItem* parent2 = new QStandardItem(tr("parent2"));
model->appendRow(parent1);
model->appendRow(parent2);
// add a child
QList<QStandardItem*> childrow;
QStandardItem* child = new QStandardItem(tr("child1"));
childrow.append(child);
QStandardItem* child_value = new QStandardItem();
child_value->setData(TYPE_ComboBox, Qt::UserRole);
childrow.append(child_value);
parent1->appendRow(it_child);

As you can see from QAbstractItemView documentation, there is only methods for set delegate for
whole view, for columns or for rows. As you noticed, this methods are: setItemDelegate, setItemDelegateForColumn, setItemDelegateForRow. That's why you can not set delegate for certain cell.
There is another way, you can use for showing different widgets in QTreeView - method SetIndexWidget of QAbstractItemView. But you must keep in mind that:
This function should only be used to display static content within the
visible area corresponding to an item of data. If you want to display
custom dynamic content or implement a custom editor widget, subclass
QItemDelegate instead.
Use SetIndexWidget method is very "expensive" and if you have many rows in you QTreeView, you can see performance degradation.
Addition:
If you need widgets in QTreeView only for edit data items from your model, the best way to solve you problem will be reimplement QStyledItemDelegate. Look for method createEditor.
Addition:
Example of reimplement QStyledItemDelegate

Related

qt - how to permanently sort a qstandarditemmodel

I have a program where I am trying to implement sorting on a qstandarditemmodel that is displayed in a table view. However, the method I am using doesn't seem to actually sort the model itself, but only the view. I need it to be able to sort the source model because I save the data to a .csv file using a delegate that passes the items from the model into an object of a class, and if the view is the only thing that is sorted it causes data loss due to the positions of the items in the view being changed but not in the model itself.
This is the code I use in the mainwidget constructor to connect the headerview clicked signal to a method that sorts the model:
currentStudentsModel = new QStandardItemModel(this);
ui->currentTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->currentTableView->setModel(currentStudentsModel);
ui->currentTableView->setItemDelegate(currentStudentsDelegate);
currentTableHeader = ui->currentTableView->horizontalHeader();
connect(currentTableHeader, SIGNAL(sectionClicked(int)), this, SLOT(on_sectionClicked(int)));
Here is on_sectionClicked():
void mainWidget::on_sectionClicked(int index)
{
currentStudentsModel->sort(index,Qt::AscendingOrder);
}
As I previously stated, this appears to only sort the items in the view as when I try to output all of the records stored in the model it has not changed from when they were initially entered. How do I get the model to be sorted itself and that order to be saved?
QStandardItemModel does not implements sort.
From Qt documentation:
void QAbstractItemModel::sort(int column, Qt::SortOrder order =
Qt::AscendingOrder)
Sorts the model by column in the given order. The base class
implementation does nothing.
You need to sort through QSortFilterProxyModel
currentStudentsProxyModel = new QSortFilterProxyModel;
currentStudentsModel->setSourceModel( currentStudentsProxyModel );
currentStudentsProxyModel->sort( 0, Qt::AscendingOrder );
void mainWidget::on_sectionClicked(int index)
{
currentStudentsProxyModel->sort(index,Qt::AscendingOrder);
}

How to disable filtering option for a QTreeView or Model

I am having a piece of code which deals with two QTreeView, where the items are added from left tree view to the right tree view. Here is a part of code:
MasterModel *model = new MasterModel;
leftProxyFilterModel *leftModel = new leftProxyFilterModel;
rightSelectedRowsFiletrModel *rightModel = new rightSelectedRowsFilterModel;
QTreeView *leftTreeView;
QTreeView *rightTreeView;
leftModel->setSourceModel(model);
rightModel->setSourceModel(leftModel);
leftTreeView->setModel(leftModel);
rightTreeView->setModel(rightModel);
Now for filtering some strings are set like this:
leftModel->setFilterWildcard(LineEdit->text());
This line is Filtering the contents of both the tree views, what I want is filtering option should be applicable only for left tree view.
Can someone help me to achieve this???
If rightModel's source is leftModel, then any filters you apply to leftModel will also affect rightModel.
Change rightModel->setSourceModel(leftModel) to rightModel->setSourceModel(model)
UPDATE: Or, you can add another layer of QSortFilterProxyModel so that your filter doesn't propagate to rightModel:
leftModel->setSourceModel(model);
rightModel->setSourceModel(leftModel);
auto leftFilteredModel = new QSortFilterProxyModel;
leftFilteredModel->setSource(leftModel);
leftFilteredModel->setFilterWildcard(LineEdit->text());
leftTreeView->setModel(leftFilteredModel);
rightTreeView->setModel(rightModel);

Qt use same model for QListView and QTableView

I'm trying to use the same model to display pieces of information on 2 different kind of view.
I need to show information about 150 objects that have a description and can be either on or off.
One of the view is a summary of the on/off states of my objects in a table (15 by 10) of icons.
The other view is a list of 150 rows that display the state of the objects and their descriptions.
I tried to subclass a QAbstractTableModel and return a different rowCount and columnCount value depending of the view used and it works (my 150 objects are all shown in the table and in the list) but it remove the clear separation between model and view given that I need to specifically tell the model which view is using it and it doesn't work as expected when I try to link the 2 selection models of the views together because QModelIndex changes when the row and columns counts change.
Any idea of what could be a better approach to my problem or a way to solve the selection issue?
EDIT due to #ramtheconqueror response:
The response from ramtheconqueror led me to the right direction but surprisingly I discovered that the ProxyModel takes the overriden columnCount and rowCount function into account for the widget size calculation but do not actually use them for the widget indexes.
I mean that regarding my example above, the ListView using the new ProxyModel was a widget with enough space for 150 rows but was only showing the first 10 (rowCount in the source model).
Therefore, I did it the other way around and used a QAbstractListModel as the main model an set the QTableView to use the Proxy model but again the table was correctly a 15 by 10 widget but with content only on the first column.
I had to change the return value of ColumnCount to 15 on my QAbstractListModel subclass to get the correct result (even if it's really a list and therefore only contains one column).
Am I doing something wrong? I just don't understand why it behaves like that...
As you already have QAbstractTableModel, create a proxy model for the list view. Simple implementation would be like
TableModel* tableModel = new TableModel();
.....
tableView->setModel(tableModel);
class ListProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
virtual int columnCount(const QModelIndex& idx) const { return 1; }
virtual QVariant data(const QModelIndex& idx, int role) const {
... get the actual model index
... ask the tableModel for the actual data
... construct the data (string / int / bool etc)
return the data;
}
}
QListView listView = new QListView();
ListProxyModel* listModel = new ListProxyModel();
listModel->setModel(tableModel);
listView->setModel(listModel);

How to dynamically add Item to QComboBox with QSqlQueryModel

I am new to QT and learning how to populate QComboBox. I know about addItem() method of QComboBox to add new item. But in my case it is not working. Below is relevant part my code.
QSqlQuery q;
q.prepare("SELECT fname from Person");
if(q.exec())
{
QSqlQueryModel *model = new QSqlQueryModel(this);
model->setQuery(q);
ui->comboFName->setModel(model);
db.close();
}
QString item = "XYZ";
ui->comboFName->addItem(item);
My QComboBox is getting populated with data from QSqlQueryModel but manual insertion of addItem() is not working.
You should decide whether to use a model or the manual way to add items, since using both leads to a strange behaviour http://www.qtcentre.org/threads/15391-QComboBox-gt-setModel-gt-Strange-behaviour.
Once you set the model of UIComponent it becomes the source for items, it would be populated from this model.
QSqlQueryModel is read only so you can't modify it (class reference page http://qt-project.org/doc/qt-5/qsqlquerymodel.html).
You can't Add Items to this SQL result Model.
To Add your items you must clear (function clear()) the model, then use function AddItem.
Hope this would help.

(Qt) Losing contact with items added to an scene. Cannot get positions

I'm trying to set up a configuring widget for an editable structure form widget so that the positions of the "to be filled LineEdits" are chose arbitrary by the user.
My idea is to gather the wanted positions of the LineEdits by using a widget made of a QGraphicsScene scene, a QGraphicsView view, and some references to the LineEdits items with proper name: QTextItems.
I have managed to add the reference items to the scene, have shown the view and have been able to put the items on the positions I wanted. The problem is once the items are added to the scene, I lose all contact with them and I can't know which item is in which position.
Here is a little example:
scene = new QGraphicsScene(myWidgetSize_QRect);
view = new QGraphicsView(scene);
view->setAcceptDrops(true);
for(int i=0, i<number_of_input_fields, i++)
{
GraphicsTextItem *item=new GraphicsTextItem();
//Configure Item
item->setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);
item->setAcceptedMouseButtons(Qt::LeftButton);
item->setAcceptDrops(true);
item->setCursor(Qt::OpenHandCursor);
QString txt("");
txt = textForItem(i);//some function that defines the text.
//Suppose it have returned for some items: "Name", "Address"
item->setPos(20+20*i,20+20*i);//load the items on different positions
scene->addItem(item);
}
view->show();
Well, having done a code alike I don't know how to get the positions back once they were moved to the desired location.
Let me be more specific if I have placed the items with the text "Name" and "Adress" on the positions I wanted, I don't know how to get the position of the "Name" item.
The only access I have to the items after adding them to the scene is:
scene->items();
I don't how the items on that list are ordered. And don't know how to know which is which...
I'm having a very hard time doing this, I have been putting off this while concentrating in some other things... but I have reached the time I cannot continue coding other things because this is a main feature of my program
Any help will be very appreciated... Thank you.