Qt QTreeWidget preserve sort - qt

How do you implement a preserve sort in a Qt QTreeWidget? I.e. I would like the previous order of the tree preserved as much as possible. This allows the user to do something like click the "Name" column header and then the "Date" column header, and the resulting tree shows the items in the QTreeWidget by Date and then by Name.

Unfortunately, you can't. QTreeWidget uses an inaccessible (and internal) QTreeModel for its operations, including sorting.
Normally, to do so you would want to implement a stable sort within your QAbstractItemModel subclass. A stable sort will leave items whose position doesn't need to change in the same location.

Related

QtAbstractItemModel. Using QtreeView and QtableView together

My application is a tool library manager. It has a treeview containing a list of lists and tools. It also has a tableview showing a list of tools.
When I click on a list in the treeview, I want to show the tableview with the list of children. When I click on a single tool in the tree, I switch widgets and display the editform for the tool.
How do I limit the tableview to only the children of the selected node in the treeview?
My data model is a custom qAbstractItemModel.
I have a similar situation (not quite the same!) as yours. Before I share my experience, my assumption is that you have a database of sorts which consists of the items that make up your lists/tools.
One possible solution is to create two custom models, one for the tree view (say, MyTreeViewModel -> QAbstractItemModel, associated with QTreeView) and one for the table view (MyTableViewModel -> QAbstractItemModel, associated with QTableView).
The two custom models should be fed by the same agent working with the database. The key difference is that the tree view model works with the entire database, and the table view model with only a subset, that is, the children of the chosen item in the main list. Think of it as full mapping v. partial mapping of the database.
When the user clicks on the top list of nodes in the tree view, you can emit a signal which could be picked up by MyTableViewModel, populates with only the children, and is displayed by QTableView.
Because the same agent serves both models, you should be able to maintain data integrity, in the sense changes to the underlying data by one model should be reflected in the other model. This solution has worked out really well for my application. Hope this works for you too!
I'm not sure if this is a complete answer to my own question but I'm much closer. On the tableview I can call setRootIndex() and pass in the current item from the qTreeView. This works to show only the children of the current tree item in the table list.
For me, it still shows both tools and child lists, which I want to filter, but I think that's a different issue.

Accessing a Components Property prior to the Instantiation

I have the following problem:
I have a list of Components, that contain Rectangles with a width of either 200 or 800. I'd like to filter this list, and only create objects of the Rectangles with a width of 200 as I work on a small screen.
Preferably I do not want to create all objects, check their width, and destroy those with the wrong width again. For obvious reasons I really only want to create those with a width of 200.
To do this I would need to aquire knowledge of the width, before I instantiate them.
As far as I have seen, there is no publicly available and documented way of introspecting/reflecting the Component prior to it's instantiation.
My question is: Is there a non-public way to gain knowledge about what is packaged inside of my Component? Might it be possible with C++?
Or would it at least be possible to find out what kind of Object is encapsulated? Whether it will be a CustomComponent1, a Button, a RedRectangle...
Unfortunately not. You can't even predict it, since the Component could even point to a qml file that hasn't even been downloaded yet, if it was fetched from the network.
There are a couple of things you can try though, if you have room to approach the problem from another angle:
What you can do is pass properties from outside the component into it as it gets created. Assuming you control the code within the Component, you can then adjust how the internal elements get created based on the value of the property(ies) that was(were) set from outside.
If that's not good enough, say your Component provides multiple elements and you only want to create the ones that match your criteria (possibly a combination of many), then you can introduce a second Component layer within the first Component, and have that second Component either create the actual element if it matches your criteria, or an empty Item{} if it doesn't, which is as close as it gets to not creating anything.
I hope that helps!

Changing QGraphicsScene Insertion Order Without Reloading

I'm working on a graphical shape editor that uses the QGraphicsScene/QGraphicsView as its basis. I have a lot of experience with the scene/view framework and understand it fairly well. The issue that I'm having is that QGraphicsScene::items always returns items in the insertion order (either ascending or descending) regardless of the Z-order or the use of QGraphicsItem::stackBefore call.
The issue is that, as with most graphics editors, I need to be able to move shapes forward or backward in the stacking order. At the end, to save the resulting data, I have to traverse the list of the items in the scene and save each item's data in whatever format I'm using.
The only way that I've found to do this is that I have to remove items from the scene and reinsert them in the desired stacking order. In this particular task, it's a small number of items and happens without noticeable delay, but in a related editor, it could be many thousands.
While the QGraphicsItem::zValue and QGraphicsItem::stackBefore allow me to influence the drawing order, neither of them changes the order that gets returned from QGraphicsScene::items. Since the data I ultimately save needs to reflect the drawing order, I have to remove and reinsert to get the correct ordering at the end.
Questions:
Have I've overlooked any other techniques for managing items within the scene that will influence the results of QGraphicsItem::items?
Or is there another method for traversing the items within the scene that will give me the drawing order?
I can confirm that QGraphicsScene::items() and items(sortOrder) return the item list in the original creation and stacking order, which does not at all agree with the docs.
However, I found that by using
QGraphicsScene::items( QGraphicsScene::itemsBoundingRect, Qt::IntersectsItemBoundingRect, sortOrder) I do get the items in the correct drawing order, so this function apparently takes calls to QGraphicsItem::stackBefore() into account.
i don't use the z-order feature so I can't comment on whether that works in this scenario or not.

QTreeView with columns

I have these messages received on the can bus which need to be displayed on a suitable Qt Widget (Please refer attached picture). It seems I can use QTreeView for it.
I need to show a tree which contains many messages as shown in attached picture. Each row will contain information about the received message.
It should consist of columns :
Length
Time of receiving
Message ID
Name of the message
Message content
and when I expand message it should its different signals.
How can I make a QTreeView with columns ?
There is a problem that won't allow you to make what that screenshot shows, and it is that first and second level tiers don't have the same columns layout. AFAIK, it's not possible to do that with QTableView or QTableWidget current implementations and you will have to implement your own class.
If you can adapt to having the same column layout for both levels, then keep reading what I wrote before taking another sight to your screenshot:
You can, QTreeView is only a QTableView with some art for first
column.
Add all the columns you want to your model. A simple
[QStandardItemModel][1] would suffice to do something like the
screenshow
Just remember 2 things:
Only first column item childs will be displayed.
First column cannot be hidden.
[1]: http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html

Drag and drop between different views of a heterogeneous data hierarchy

I have a hierarchical data structure that is to be visualized in several Qt views (or widgets). The data hierarchy consists of heterogeneous element types, like:
House
|- Floor
| |- Room
| | |- Window
| | |- ...
| |- Room
| | |- ...
|- ...
All elements (House, Floor, Room, ...) have attributes that can be displayed. Note that this is a simplified example. The hierarchy is drawn by various views. E.g. just the identifier of a room in a list for templates (QListView/Widget), a custom view (hierarchy of QWidget subclasses for each element), a detailed view to edit the properties of e.g. a floor (QWidget subclass or QWizard).
It is also possible to drag and drop elements between several instances. E.g. moving rooms to different floors. It is possible to declare a specific floor as a template and to drag a floor from the template list in e.g. the custom view (where a house is composed).
Qt uses the Model/View architecture to separate the data, model, and view. Because I have totally different types of views, I assume that for each view a corresponding model is required. In the case of my custom hierarchy view, each element has its own visualization, and therefore the hierarchy exists (but should not exist) three times: the data hierarchy, the model hierarchy, and the view hierarchy. This gets really messy, because every hierarchy has to be updated, if an element is dragged and dropped, removed, or copied. A better approach would be the Presentation-Abstraction-Control pattern. However, PAC is not applicable, since the parent of a QWidget has to be set, to embed children in their parent view. Therefore, the QWidget cannot reference an agent that is responsible for modeling the hierarchy.
It seems to me that Qt is very good in representing lists, tables, and trees of homogeneous data types (like strings). In my case, every element has an individual set of attributes that can not simply be expressed in the form of a table. In this discussion it is discouraged to force a square peg into a round hole. Meaning, not to force any design in a table representation.
The core of my problem is to unite the following features in one design concept: Visualization of hierarchical data with different levels of detail. Supporting drag and drop between views, which copies the data and generates appropriate Model/View components. Supporting drag and drop in a view, which effects the data, model, and view hierarchy (which I would like to avoid implementing three times). I cannot provide one model for a house with all it subcomponents, since floors and rooms are too complex. I find it clumsy to manage three (or more) hierarchies for one drag and drop, delete, or copy action.
Is there a best practice, a design pattern, or maybe a different approach to my problem? Is this problem solvable with Qt?
I am thankful for every suggestion.
I had a similar issue for a while with my hierarchical structure. Everything in Qt's model-view-delegate architecture hinges on how you organize your data and how complex you're willing to get. For very simple applications, it makes sense to implement things from an item-based approach, editing item display at the View level. This gets very messy very fast.
Sounds like you're willing to get pretty complex, so I'd recommend taking a model-based approach. You can control nearly everything at the model level, including some basic display elements, data organization, and (most importantly) heirarchies.
I found it helpful when getting started to have my objects inherit QStandardItem and subclassing QStandardItemModel, since QStandardItem already had the parent-child hierarchy and indexing set up for me. I like incrementing Qt::UserRole and assigning an enum value to each of my custom data types. For example:
enum FloorProperties
{
DAT_CARPETING = Qt::UserRole +100,
DAT_AREA = Qt::UserRole +101,
DAT_FLOORNUM = Qt::UserRole +102
}
Even if you don't want to associate a stored value with every data role (using the convenient QStandardItem::setData()), you can customize QStandardItemModel::data() to return a computed value, stored value, whatever. Customizing the model allows you to think about each item as an object, rather than a single cell in the table/list.
The standard views should give you most of what you need on a general basis. Customize display for certain data types (Floors, Windows, integers, strings, etc) using delegates. Hope that makes some sense.

Resources