Drag and drop between different views of a heterogeneous data hierarchy - qt

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.

Related

How to design a QAbstractItemModel to support multiple object types and different views

I need to write a QAbstractItemModel class that represents a hierarchy of different object types. I want to be able at some point, show a table/list containing only level 1 elements, only level 2, and so on.
I am working on a network protocol analyzer tool, something like wireshark. I am capturing socket.recv and socket.send events from a process. In my model those events are called NetworkEvent. Each network event may contain one or more Packet. Each packet has one or more Message, where a message, based on its code, is parsed as a defined struct.
Here is an image of the program class hierarchy:
The main window has a list and a tree. I expect to be able to show:
a table/list containing only network events including its attributes.
a table/list containing only packets including its attributes.
a table/list containing only packets based on a network event.
a tree containing a packet/message hierarchy (with fields and sub structures)
a table/list containing only messages
a table/list containing only messages based on a packet
a tree containing a message hierarchy (with fields and sub structures).
So I thought the best idea was to model the QAbstractItemModel as a tree. First problem I encountered is that while each class has the concept of "children", each one has a different field that represents childrens, so I have to take care of that inside the QAbstractItemModel.
Also, because a table/list of EventNetworkdoesn't have same columns as table/list of Packet, nor Message, I can't properly use the same model to define all possible ways to show the data. I suppose the correct way to do this would be defining proxy models for each kind of view.
Is there any better or easy way to approach this? Or is this the way to go?
So you create a common base family of polymorphic classes, and use a base pointer as the data source for the model. One single role - the data object, from then individual delegates can access their specific data fields without having to bother implementing everything using roles. The role-centric use-case is really only applicable for isomorphic data sets.
Then you can customize the visual representation based on the actual individual object and view type.
I wouldn't marry to any particular representation. I'd only implement a list interface, this gives more flexibility how to represent the structure, you can draw simple lists as list view or table view, and you can also have tables or trees that are made of lists of lists.
In programming, it is always a tree, which is very obvious if you structure your application well, so it is a simple manner of choosing how you visualize each node and its contents. It is common to even have different ways of visualizing the same data, both in terms of visual structure and actual delegates.
You will have a tremendously easier time implementing this in QML, especially the actual visual representation, using the generic object model outlined here. Although if your object count is very high, you might want to implement it as a single abstract item model rather than have every object be a list model to avoid the overhead. But unless you deal with millions and millions of items, the overhead is well worth the return.
I think you are on the right course with thinking of using multiple proxy models, one for each table. I would suggest starting with QSortFilterProxyModel and implement your own filtering algorithm.
I will also suggest you might want to identify the type of data with using custom Qt::ItemDataRole enumerations, one for each type. I think it will make the filtering easier.
You may want to experiment with List, Table, and Tree Views to see which suits your purpose the best. The beauty of the Model-View system is you can easily change the View once you have the model(s) down.

How to split a complex hierarchical Model into two partial Views?

I'm learning the Model/View paradigm of Qt because it seems very well suited to edit the data structures I have to deal with, such as this one:
Addition
|_QuadraticFunction
| |_intercept=0.2
| |_slope=0.0
| |_quadratic=1.2
|_Multiplication
|_LinearFunction
| |_intercept=0.0
| |_slope=-8.9
|_Gaussian
|_center=0.6
|_sigma=0.4
My data structure is made up of a combination of functions, each function has its own properties. However, I don't want to display the whole data structure in a single TreeView because it can get too long for complicated structures. Instead, I want to show one view including only the function names, and other view showing only the properties of the function selected in the previous view by the user with a click of the mouse, like this:
(FunctionsView, the first View)
Addition
|_QuadraticFunction
|_Multiplication
|_**LinearFunction**
|_Gaussian
(selectedFunctionView, the second View)
intercept 0.0
slope -8.9
In this example, the user clicked on LinearFunction in the first View, and the second View automatically showed its properties.
My question is: can I hold all my data structure (function names and function properties) under a single model and then have two Views that display only parts of the model like above? If not, do I have to create one model for each partial View, each model indexing different parts of the data structure? Please help, I'm inexperienced with this.
.Jose
Yes, you can absolutely keep it all in one model with two different views. You'll probably want to look into a QSortFilterProxyModel; you would have one of these for each view. The proxy applies sorting and filtering--and filtering is what you're doing here--to a more complete model. When you select something in the main view, you'll want to issue a signal that's picked up by the other proxy model (or the other view and passed to its proxy), and then refilter based on the newly selected item. It's actually very easy to use overall. The easiest mistake to make is getting confused over which model index to use because you'll have model indexes for the proxies, and model indexes for the complete model, and sometimes you have to convert between the two. The documentation is pretty clear on where that's necessary, but it helps to be extra aware of it.
Take a look at the documentation and if you have more questions, please ask.

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.

how to properly implement different views options of the same data set

I'm currently building a model view architecture and came across an issue I can't find information on across the internet.
I have one set of complex data, that I want to show to the user in two (or more) different fashions :
full data is shown
only selected (partial) information in shown
The way this data is printed is to me irrelevant, but if this help it's either in a table view (basic information) or a column view (full information). those two clases comes from QT model / view framework.
Now I though about two option to implement this and wonder the one I should use
Option 1
I build my data structure,
include it in a custom model
specialize (subclass) view classes in order to only print what I'm interrested in.
Option 2
I build my data structure,
specialize my models to only provide access to relevant data
use standart view to print it on screen.
I would honestly go for option 2, but seeing the amount of case over the internet where option 1 is used I started to wonder if I'm doing it right. (I never found any example of dual model of a data when multiple view of a model appears to be quite frequent)
Placing data relevant handling inside view classes seem wrong to me, but duplicating models of a data leads to either duplicated data (which also seems wrong) or shared data (and then model no longer 'hold' the data)
I also had a look on QT delegates, but those class are mostly meant to change the appearence of data. I didn't find a way using delegates to ignore the data that is not relevant for one view.
You are completely right thinking that it's wrong to use views for filtering data. The only reasons for reimplementing a view is to have a different view of the same data or special processing of user events.
So there are two ways to filter out a data:
1.Create two models which will share the data. It's a standard recommended approach - not to keep data in models.
2.Create one model providing all the data and create a proxy model inherited from QSortFilterProxyModel to filter out the data.
You will need to reimplement filterAcceptsColumn method to filter out columns and filterAcceptsRow to filter out rows.
Then use View-Model to show all the data or View-Proxy-Model to show some data.

How to handle signals when a widget has many child widgets

I am designing a game that includes a world map widget; this widget has 100+ region widgets as its children. There is also a separate information panel widget. When I click on a region, I want that information to be sent and displayed on the information panel. My thoughts so far give me 2 options:
Connect each of the 100+ regions to the information panel. This seems ugly to me as I feel like the information panel should only know about the world map widget, not its internal workings (such as children).
Connect each of the 100+ regions to the map widget, and then have the map
send another signal to be picked up by other widgets in the game such as the information panel. This seems nicer as far as having independent widgets goes, but requires 2 signals instead of 1.
Is one of these methods preferable to the other? Or is there another solution I am missing entirely?
I used to work for a company which made its own geospatial information system (GIS) product and we had this exact use case using Qt as well.
From my experience we would use option #2 as it encapsulated the details and relationships between the information panel (i.e. a view) and the world map (i.e. a model).
In the future maybe your world map will contain more than selectable regions and the information panel may not want to have to start knowing about all of the different entities in your world map to display them. So yes while #2 results in some additional signals it can be better from an encapsulation and expansion standpoint.
For example connect the information panel to a signal from your world map regarding a selected entity rather than a region. Sure a region is an entity and using region directly could work but again in the future maybe your world map will have selectable buildings, markers, vehicles, etc...
i.e.
connect( mapWidget, SIGNAL( selectedEntityChanged( MapEntity* ) ),
infoPanel, SLOT( onSelectedEntityChanged( MapEntity* ) ) );
This way your information panel could be designed to generically display information about an entity, again from which a region is derived. If you want to add say a vehicle to your map and make it so the information panel can display it, all you'll have to do is derive your vehicle from entity and you'll be good to go.
Great question welcome to StackOverflow!
Remember that each QObject comes with a property system - a general purpose, Python-like key-value store. The keys are strings, and the values are variants. Since QWidget is a QObject, you can leverage that. Just by itself this makes QSignalMapper quite redundant.
You can also use the undocumented, but slightly faster userData mechanism - it uses integer keys instead of string keys. The unique key ids are obtained through registerUserData(), with semantics otherwise identical to QEvent::registerEventType.

Resources