Linking a tree view to a table view? [Qt] - c++

How do I link a tree view to a table view using Qt? For example, I have a treeView with a list of items in it. I also have a tableView with a model/view implementation so that data from different files populate the table. So let's say the filename of each file (in this case, all files are CSVs) is listed as an item in the treeView. What I'd like to do is link each .csv item in the treeView to show the parsed contents of the .csv file that is selected in the treeView. I'd like that data to show in the tableView. I have implemented both the tree and table separately - and they work - I just don't know how to link them together. How do I make my parsed data show in the table only after I select the corresponding item in the treeview?

The Qt itemviews system won't give you this functionality automatically, but it's easy to get the behavior using a signal/slot connection.
When the user selects a different row in the QTreeView, the QTableView should repopulate itself with the contents of the .CSV file that row in the QTreeView represents. This can be accomplished by connecting the selectionChanged() method of your QTreeView's SelectionModel object to a slot that will perform the table-repopulation operation. i.e. something like:
connect(myTreeWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), someObjectInYourProgram, SLOT(RepopulateTableView()));
... and then have your RepopulateTableView() slot-method look at which row(s) are currently selected in the QTreeView object, and repopulate its contents based on them.
(side note: you can have RepopulateTableView() use the arguments directly from the selectionChanged() signal, but I often find it more useful to have it examine the QTreeView's selectionModel object using a pointer that is supplied separately, since that way RepopulateTableView() can be easily called from other contexts besides this signal)

Related

Update of QTreeView without changing selection

I have a model that retrieves data from a table in a database from a certain SQL query, and shows the items in a QTreeView. The characteristics are:
the data comes from a table, but has an underlying tree structure (some rows are parents that have rows below them as children)
this tree structure is shown in the QTreeView
the children are selectable in the QTreeView (not so the parents)
the table in the database gets updated continuously
in the updates, a children can be added to any existing parent
periodically (with a QTimer) the QTreeView is updated with the contents of the table
Since the children are added at any time to any parent, the first silly approach when updating the QTreeView is clearing it all, and append all the rows again, in form of parent or children, to the QTreeView. This is a 0-order approximation, and it is indeed terrible inefficient. In particular, the following problems appear:
Any existing selection is gone
Any expanded parent showing its children is collapsed (unless ExpandAll is active)
The view is reset to show the very first row.
What is the best solution to this problem? I mean, the first solution I will try will be not to clear the QTreeView, but instead parse all the returned rows from the table, and check for each of them whether the corresponding item in the QTreeView exists, and add it if not. But I wonder if there is a trickiest solution to engage a given table in a database with a QTreeView (I know this exists for a QTableView, but then the tree structure is gone).
This thread mentions a general approach, but this might get tricky quickly, but I am not sure how this would work if the underlying model is changing constantly (i.e. the QModelIndex becoming invalid).
Worst case is that you will have to write your own mechanism to remember the selection before updating and then re-applying it.
I assume you use some model/view implementation? You could enhance your model with a safe selection handling, in case the example mentioned above does not work for you.
I guess this is the case for a self-answer.
As I presumed, after a careful analysis of what data is retrieved from the database, I had to do the following "upgrades" to the retrieval code:
I retrieve, along with the fields I want to show in the view, two identifiers, one for grouping rows and one for sorting items into groups
I also retrieve the internal record ID (an increasing integer number) from the table in the database, in order to ask only for new records in the next retrieval.
In the model population code, I added the following:
I first scan the initial records that may belong to existing groups in the model
When, in the scanning, I reach the last group in the model, this implies that the rest of retrieved records belong to new groups (remember the records are retrieved sorted such that items that belong to the same group are retrieved together)
Then start creating groups and adding items to each group, until we use all the records retrieved.
Finally, it is very important:
the use beginInsertRows() and endInsertRows() before and after inserting new items in the model
capture the sorting status of the view (with sortIndicatorSection() and sortIndicatorOrder()) and re-apply this sorting status after updating the model (with sortByColumn())
Doing that the current position and selection in the QTreeView receiving the model updates are preserved, and the items in the view are added and the view updated transparently for the user.

Simple document switcher functionality?

I'm writing an application that will allow a user to drag/drop specific files onto the application window, parse those files, put the contents into a table (via a QStandardItemModel), and add each file's name (or alias) to a separate tree view (which acts as the document switcher).
I'll use NotePad++ as a simple example.
When I click any of the new files in the leftmost "Doc Switcher," it shows the contents in the right pane. Imagine that right pane is a table. And for instance, imagine that the list on the left is a list of .csv files that were imported into the application.
What I want to do is, upon clicking each item in the list, I want the corresponding parsed .csv file to show up in the table pane on the right.
My table is just a QTableView that displays the contents of the .csv files in a QStandardItemModel. Everything works when it comes to implementing the table and parsing the files.
I also set up a QTreeWidget as the "document switcher." Now, I need to link the document switcher selection to the table so that each file's respective contents will be shown in the table view.
I can have the application populate the tableView with the model contents when the QTreeView's top level item selection changes. That's no problem. The problem is with what I should be checking for when that selection changes and how.
I'm unsure of how to implement this. How do I store a bunch of QStandardItemModel objects and then link them to their names in the document switcher? Should I even be doing that? Do I have to create a new QStandardItemModel for each file that is imported? Should I create one QStandardItemModel, then somehow save it to be pulled back up later and re-use that same table model object for each file that is added? I'm just unsure how how this is supposed to work and feel like I am missing a fundamental part of all of this.
I would suggest two approaches to solve your problem:
You can watch document switcher signal (selection changed) and create new model for the currently selected data. Your table view in the right should show the data, when you set the model. When new file item selected, delete existing model and create new one with new data,
The same as first approach, but instead of recreating model for each data change you can use a single model, but reset its data each time you switch the file.

Qt QTreeWidgetItem text contents vs widget item vs data

upon construction of QTreeWidgetItem you can pass a list of strings, so when you insert it in a table(QTreeWidget), you get the strings listed on a row. However, from the methods of the table you can also call setItemWidget and set a text widget or any sort of widget to be in that row, but it seems incompatible with having a string list, since the widget is drawn over the strings. There is also a setData method for the QTreeWidgetItem, which sets some data that can be retreived, but isn't visible to the user. Is there a cookie-cutter way of properly using all three data storage methods? Are they even compatible or must I stick to only one?
The Constructor of QTreeWidgetItem is convenient to immediately list the desired content.
When inserting a custom widget in a cell, you need to change its autoFillBackgroundproperty to true, so that it is not transparent. See the QTreewidget::setItemWidget description:
The given widget's autoFillBackground property must be set to true,
otherwise the widget's background will be transparent, showing both
the model data and the tree widget item.
QTreeWidgetItem::setData can be used when already having an item and you want to change one of its contents.
Of course you can combine any of these methods, but it is hard to say, which approach is best without knowing your use case. Just one more hint: If you just need a plain stupid representation of data that does not change, using QTreeWidget is fine. But if your displayed data can change, e.g. objects get deleted, added, changed in various locations of your code, a QTreeView with a custom data model might be a better choice.

QSortFilterProxyModel and the slot setFilterFixedString

I currently have a search button and I would like to search a specific column of my model.Thus I just want the matching rows to be displayed in my tableview.
I have attached a QSortFilterProxyModel* object as a source to the table view and have set a QStandardItemModel* as its source. Then with my search button I made the following connection
QObject::connect(ui.lineEditSearch,SIGNAL(textChanged(QString)),proxyModelFilter,SLOT(setFilterFixedString(QString)));
Now I was under the impression that upon typing the relevant rows will be returned. Then I realized that I havent specified as to which columns I want the filter proxy model to search.
I understand that I could implement a class that inherits from QSortFilterProxyModel and re implement its filterAcceptsRow. I wanted to know if there was a way for me to avoid creating a class that inherits from QSortFilterProxyModel and simply use the QSortFilterProxyModel class only to tell which columns to search on when the Slot setFilterFixedString is called ?
The column to filter by can be set via QSortFilterProxyModel::filterKeyColumn.
It allows to specify a single column, or all of them (-1, the default).
Alternatively, one can define a custom filter role returning a concatenation of all string to search for, and set it via setFilterRole().

QTableWidget - combobox delegate how do I allow different options per cell

Aloha
I have a QTableWidget with two columns that are currently using a ComboboxDelegate (my subclass of QItemDelegate) to present options to the user. I'd like the choice in the first column to effect the options available in the second, for the current row only.
E.g have a list of cars in the first column, and in the second a list of colours which are available for that car. Other rows to have different cars selected and thus different colour choices available.
From what I can see, I can only set an item delegate per row or column, so I can't see how to change the options in the second column's delegate without affecting all the other rows.
Is this possible? I'd really like to avoid going to a full view/model separation as I have quite a bit of code looking at this QTableWidget already (and I'm under time pressure)
Well for those interested; I went back to my pre-delegate approach, which was to use QTableWidget::setItemWidget() to provide a combobox widget for each cell.
I subclassed qcombobox to take a reference to the table, and connected the combobox CurrentIndexChanged with a slot to update the table data.
(setting a widget in a cell does not affect the tablewidget data unless you do this).
Using a full combobox like this is more expensive than an itemdelegate, but my tables are very small so I can get away with it. The rendering of the combobox is not as nice as the delegate (the combobox is visible all the time instead of only during editing in the delegate's case), but with time I'm sure I can improve on this.