Here's my use case (very simplified).
I have some data from DB in QSQLTableModel and I need to transform it: merge few fields into one (and display as such) or split one field into few. How and where this should be done in Model/View?
Notes:
I tried using AbstractProxyModel to do this, but I guess, it's suitable only for filtering or sorting. I could allocate new data and return QModelIndex (which carries the pointer to the data) but which object should deallocate the data later?
Modifying the View object also wouldn't help because it processes every table cell separately.
Mind that I can't alter database in any way.
How to do this in Qt? I already spend two whole days on this only to run into one wall after another.
Using QAbstractProxyModel isn't the wrong approach, you can use it to map any kind of source model to restructure the data. But it will be a lot of work, you have to re-implement several methods, including columnCount, data, flags, index, and more. In effect, you have to make sure that the indexes of this model map to the correct data of the source model. Also, if you have a dynamic model, then you will want to handle signals coming from the source model, modify the values and re-emit them. If you want the view to edit the data as well, then you'll have to re-implement setData, etc.
For example, if you have a column that has two values appended, and you want to display them in two separate columns, then columnCount should return one extra, data should retrieve the original data and return only one part based on the column in the index, index must be modified to check the new bounds of this model, etc.
If you want more specific help with this, then please post some example code.
Related
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.
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.
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.
I'm fairly new to Tableau, and I'm struggling in building some routines that could be easily implemented in Excel (though it would take forever for big sets of data).
So here is the deal, consider a dataset with the following fields:
int [id_order] -> id of the sales order (deepest level, there are only unique entries of id_order)
int [id_client] -> as I want to know who bought it
date [purchase_date] -> when the customer bought the product
What I want to know is, for each order, when was the last time (if ever) the client has bought something. In order words, what is the highest purchase_date for that user that is smaller than current purchase_date.
In excel, approach is simple (but again, not efficient)
{=max(if(id_client=B1,if(purchase_order
Is there a way to do this kind of calculation in Tableau?
You can do this in Tableau using table calculations. They take a little time to understand how to use well, but are very powerful and flexible. I posted a sample Tableau workbook for a similar question in an answer for SO question Find first time a condition is met
Your situation is similar, but with the extra complication that you want to repeat the analysis for each client id, so you might want to try a recursive approach using the Previous_Value() function instead of the approach used in that example - though I'm not certain that previous_value() will fit your situation.
Still, it might be helpful to download the example workbook I mentioned to get an idea how table calculations can address similar problems.
Just to register the solution, in case someone has the same doubt.
So, basically the solution I found use table calculation, which is not calculated until it's called on a sheet (and is only calculated on the context of the sheet). That's a little bit limiting, so what I do is create a sheet with all the fields I need (+ what is necessary for the table calculation) then export the data (to mdb) and connect to this new file.
So, for my example, the right table calculation is (let's name it last_order_date):
LOOKUP(MAX([purchase_date]),-1)
Explanations. The MAX() is necessary because Lookup (and all table calculations) does not work with data directly, only with aggregations. You can use sum, avg, max, attr, whatever suits you. As in my case there will be only 1 correspondence, any function will do just fine and return the same value.
The -1 indicates that I'm looking for the element immediately before the current entry (of the table, as you define it). If it were FIRST(), it would go for the first entry of the table, and LAST() would go for the last.
Now, I have to put it on a sheet. So I'll bring the fields id_client, id_order, purchase_date and last_order_date.
Then I have to define the parameters of my table calculation last_order_date (Edit Table Calculation). I'll go to Compute using and choose advanced. Now I'll do Partitioning: id_client, and addressing all the rest. What will that do? This mean Tableau will create temporary tables for each id_client, and table calculations will use those tables as parameter.
Additionally, I will Sort by field purchase_date, Max (again the aggregation issue) and ascending, to guarantee my entries are in chronological order.
Now, what will it do? For each entry it will access the table of the id_client, and check what was the purchase_date that is immediately before the current entry (that is being assessed), exactly what I need.
To avoid spending Tableau processing in Visualization, I often put all the fields in details (and leave nothing on screen), use Bar chart (it's good because it allows me to see the data). Then I export it to mdb, then connect to it again. Unfortunately Tableau doesn't directly export to tde.
I have a series of objects I have created:
Item
Order
Song
etc.
Each object has a reasonable number of properties, and I use a datareader where I pass it "SELECT * FROM .objectname." and then I fill a collection of objects, and return the collection. This works as: GetOrdersCollection(), GetSongsCollection(), etc.
I understand SELECT * to be a performance problem, and additionally, sometimes I prefer to include additional columns in the select statement which do not exist in the object, and have those all returned as well.
So my question is, what is the best way to approach this problem?
Should I create a new object for every query type?
I tried performing a check to see if column is in datareader before storing it, but this presents perf. issues. Is there a negligible perf. way to avoid IndexOutOfRange?
Should I just use Datatable and read right from the table?
I understand SELECT * to be a
performance problem,
It's not a performance problem if there are only a few columns, or you need all of the columns anyway.
1.Should I create a new object for every query type?
You should create a new object for each table, and a new method for each query type.
2.I tried performing a check to see if column is in datareader before storing
it, but this presents perf. issues. Is
there a negligible perf. way to avoid
IndexOutOfRange?
If you are referring to your fields by name rather than index, there shouldn't be any IndexOutOfRange problems. If you are referring to your fields by index, you can loop thru them where your index is less than the column Count(), and there shouldn't be any IndexOutOfRange problems.
3.Should I just use Datatable and read right from the table?
That's a perfectly good approach to start out with. Consider spending some time to learn a simple ORM as others have suggested. Subsonic is a good "first" ORM.
Performance-wise reading from a forward only data structure like DataReader is going to net you the best performance and resource conservation.
On the other hand populating object (like a OR/M does) can be negligible so long as you are not returning more than a handful of objects.
Your first step should be to profile your database and ensure that you have proper indexes. Write some tests to see where your largest time expense is in the process and optimize the target areas that cost you the most.
Are there any reasons you can't use a simple ORM generator like SubSonic? This will allow you to very easily access these types of collections, and they'll be strongly typed. You also won't have to worry about the SQL since the queries will be built by SubSonic.