I'd like to build a completion for a QLineEdit which can take several completion models and organizes them as categories.
I have a working solution based on a QSortFilterProxyModel and a tree model for the items:
The root items in the model show up as category, and the children of each root-item show up as filtered items. Then there's a customized QTreeView so this is displayed properly, tabbing ignores categories, etc.
This works fine - however it makes it very hard to write new completions (e.g. for the possible values for a setting).
Each completion needs to be a tree model with all categories in it. I'd prefer to write simple list models for each category, and then being able to combine them to a QTreeModel, i.e. something like this (pseudo-ish Python code):
commands = CommandListModel()
settings = SettingListModel()
completion.setModels([('commands', commands), ('settings', settings)])
There are some different solutions I had in mind, but I'm not sure what route to go, as all of them feel non-trivial to implement:
Write a QAbstractProxyModel-like class (or a custom model) which combines several list models to one tree model, and leave the view part as-is.
Writing custom tree models in Python is hard however (and typically segfaults if you do anything wrong), and I've had performance issues with it in the past (compared to a QStandardItemModel, as there are some thousand items in the underlying models).
Write functions for each model which fill a tree model (given a category and a list of items).
This makes it harder to write dynamic models rather than just having a list of static items.
Use several list models, and adjust the view to be a QVBoxLayout of QListViews.
This sounds the most promising to me so far. However, implementing tabbing through the completion and filtering so it works right might be troublesome as well, and resizing the sub-views appropriately too.
Is there some easier way to do this I haven't considered? Which approach is likely to be the least painful down the road?
Related
I could have a simple QVector, QList, etc. of QObject to present in ListView, GridView and TableView and add/edit/remove items in/from those View. To me using QVector, QList, etc. of QObject* is the easiest way to accomplish the ultimate goal. What's the real benefit of inheriting QAbstractTableModel, QAbstractListModel, etc.? In WPF we've VirtualizingStackPanel and some properties to boost performance in such type of View. Does inheriting those Abstract..Model serve the same purpose or either way (inheriting or using plain QVector/QList), performance will be same?
The advantages of QAbstractItemModel and subclasses are mainly increased performance when handling larger lists, and increased flexibility by using proxy models:
Partial updates: A model can tell the view that single rows or ranges of rows were inserted, moved, removed or updated which then the view can act upon in a smarter way than throwing away and rebuilding everything. The cost of inserting an item in the middle of 10k other items is cheap, for example.
Lower overhead: A large list of, say, 50k QObject*s would have a major overhead, both in cost for the objects themselves and in handling all the signal/slot connections for handling the property updates.
A model can act as an adapter to an already existing data structure, without the need to hold all the data in the model (although that can be tricky in practice, to guarantee correctness/consistency)
Lazy-loading: The model can load data on demand as the user is navigating the view, without having to have all data already available (see fetchMore()/canFetchMore()).
Flexibility through proxies: QAbstractProxyModel and subclasses (in particular, QSortFilterProxyModel) allow stacking of models to filter/sort/modify the data in the original model. With QSortFilterProxyModel sorting and filtering is usually much simpler to implement (and without changing the original data/model) than implementing it manually for a QList. Simple modifications like changing the returned data for a certain role can be implemented by subclassing QIdentityProxyModel.
More complex data structures: QAbstractItemModel also allows for trees (tricky to implement though) and tables. These are more often used in "traditional" (QWidget-based) desktop UIs than in embedded/mobile touch UIs though.
I'm using Qt creator to write c++ GUI application. I want to have a list to show the some guides to user and prefer to have a list with Scroll Bar. what widget can I use to this?
For displaying lists, trees and tables, Qt has a set of Model/View classes.
In model/view, a subclass of QAbstractItemModel provides the data, which is then displayed by a QAbstractItemView. Models can be "stacked" for rather flexible filtering, sorting and mapping of data (see e.g. QSortFilterProxyModel).
There's basically now three ways to use them, of varying complexity, power and scalability:
So if you want full flexibility or have a large number of list entries (say, in the thousands), implement a QAbstractListModel with a QListView. While this is the most powerful solution, it's also the most complex one, and implementing a correct and efficient model has some pitfalls.
A simpler way would be to use QStandardItemModel (or for an even simpler list of just strings, QStringListModel) and set that as model of a QListView. That might be far less daunting for a beginner, compared with having to implement your own models. It's not the most scalable version, but if you display e.g. 50 items, that just doesn't matter.
You can still combine it with sorting and filtering via proxy models.
Then, the third and most simple way is to display a list is to use QListWidget. It's a subclass of QListView that allows you to pass in the list items via QListWidgetItem, similar to QStandardItemModel. It basically "merges" the model functionality into the view class so that you don't have to touch models at all. While this is the simplest way, it's also the most inflexible one: You cannot use sort/filter proxy models when using a QListWidget.
To get started with Model/View, especially to people who are used to manage item-based lists, I recommend (2) and then move up to (1). If you just want a simple and immutable list of a couple items, (3) might as well do. If you find out later that you need sort/filter functionality, converting (3) into (2) later is usually rather easy, while converting (3) to (1) is a far more intrusive change.
I am trying to understand how one would choose whether to use a QAbstractListModel or a QObject with a QQmlListProperty.
Given that the QQmlListProperty handles the "roles" functionality that would have to be written using the QAbstractListModel, it seems like the less tedious route.
I can't tell if most people suggest using QAbstractListModel simply because it has been around longer or if it is the better choice for some reason.
Nor have I been able to find any discussion of the trade-offs between the two options. This question was brought up during a Qt Developer Days talk discussing QAbstractListModel, but the answer was along the lines of "that would also work".
A model implementation will generally be more efficient to work with a view. When you expose a "dumb list" to use a model, every time the model changes the whole view is reconstructed, whereas with a model only the changes are updated. If you have a lot of items there will be tangible performance overhead.
You could use a list for a model, and you could use a model for a list, but when you want optimal performance, you should use a list for a list, and a model for a model.
I want to implement in my program a tree with nested sub-levels, and I'm looking for which of those two kind(View/Widget) is best suited for my goal.
I have a list of days with task that are either done/missed/failed, each task has a count of how many times it was done/missed/failed and lastly a score for that day.
I want to display them like so:
I made this example in QtCreator using a QTreeWidget, but I'm worried that it would be hard to modify the elements since they are stored somewhere else.
Are my worries rational and should I go to the model/view structure, or can I easily get going with the QTreeWidget? The tree will be logging the task and thus will be constantly changing. Elements will only be added to it, not removed. And the days will be sorted from highest-lowest(day 2 is first, then day 1)
If your data is stored in a database model or if you want to have a single data model and show it in some views in different ways, then you are definitely better to go with QTreeView.
But QTreeWidget has it's internal model in some way along with the methods to deal with model in context of indexes. In general if you just want something that is simple to work, you can use the widget way.
But the Model/View approach is more general and flexible IMO. You can create your own subclasses of model and view which enables you to do whatever you like.
I have a simple list:
mylist = ["foo", "bar", "baz"]
and a QtTableWidget with 1 column. I would like to keep the data in mylist in sync with that in the QtTableWidget, including allowing drag-and-drop reordering (i.e., the user can do that, and mylist gets updated) and programmatic changes (i.e., I update mylist and the table gets updated).
What is the right way to do this?
Note that in general there's no way in Qt/C++ proper to link a generic data structure with a view. Models are such instrumented data structures that allow trivial linking with views -- generic lists, like say QStringList can't be used like that.
In Python it should be possible to inject attributes into an existing instance of any list to turn it into a model that notifies its views of changes done to it. I don't know if any Qt-to-Python bridges do that sort of a thing, I can only give C++ perspective on this. Someone who knows PyQt or PySide is welcome to chime in and edit as appropriate.
Use a QStringListModel for the model. Enable drag-drop reordering on the view as follows:
view.setDragEnabled(true);
view.setDropIndicatorShown(true);
view.setDragDropMode(QAbstractItemView::InternalMove);
My other answer has a full example that uses the convenience widget QListWidget with its buit-in model. That may be better as a starting point, and for lists that can be reasonably manipulated with a mouse, it's a good choice. Generally if a model has 100s of items, it can't be readily manipulated with just a mouse without a way of doing real-time finds or pruning of some sort -- scrolling so many items is a usability nightmare. So don't do it.
Qt's documentation would be a good plate to start learning. Read about the model/view framework. Then look at QStringListModel and your chosen view, say QTableView or QListView.