I have a QAbstractListModel that has a bunch of custom objects stored in it, and you can access the different fields of the custom objects in the model by specifying a role (if this is an improper use of Qt roles let me know because I must be confused). I want to display this data in a user friendly QTableView. I can get things displaying using a proxy model, but the issue is I don't want to display the raw values, I want to display specific data derived from the raw data. So for instance, I don't want a column for both ItemA.foo and ItemA.bar, I want to display just ItemA.foo - ItemA.bar in a single column. And to add to that, I want the automatic update functionality you get with models where if either ItemA.foo or ItemA.bar change, I want the difference column to automatically update and recalculate.
I would think that the way to do this would be to use some kind of table proxy model that listens to the source model, and then populates its own fields with the values derived from the source model and listens for dataChanged() signals from the source model. Then you plug this proxy model in to a QTableView. But to me this sounds like something that should be done in a view. Or is this something that should be done by the delegate? I could even go so far as to do these calculations in the base model itself and add roles specific to these values that should be displayed in the table, but that sounds like I am really overloading the responsibilities of the model.
TLDR: How do you manipulate data from a model in a QTableView? Should I do the data manipulation in the base model and then send that to the QTableView? Should I use a proxy model that manipulates the base data and sends it to the QTableView? Or am I completely misunderstanding something?
and you can access the different fields of the custom objects in the model by specifying a role
If you look at the documentation for Qt::ItemDataRole, you would see that Qt models should indeed provide different data for different roles but each role means some distinguished purpose of the data corresponding to the role. For example, the most commonly used role is probably Qt::DisplayRole as the data for this role defines the content displayed in the view e.g. it's the text in the table cell. If you are satisfied with all the other aspects of the view - font, background etc - you can just return empty QVariant for corresponding roles from your model, the view would figure out these details on its own. If you are using roles as a substitute for columns i.e. to return different pieces of conceptually the same data item, it is probably not the intended use of roles.
For the other part of your question - you can customize the appearance of data displayed in the view through the use of a delegate. For example, you could subclass QStyledItemDelegate, override its displayText method to display ItemA.foo - ItemA.bar instead of just ItemA.foo and then set this delegate into the column of your view corresponding to ItemA.foo via setItemDelegateForColumn. The tricky part here would be to detect changes in both ItemA.foo and ItemA.bar columns which would affect the text displayed in the delegate. A while back I implemented a dedicated class in one of my projects which listens to changes in some column of the original model and "reroutes" the change into another column through signal emitting. I did it to solve this very issue - to catch changes in what delegate should display although technically another model column is affected into which the delegate is not set.
Related
There is a structure that holds raw data (consider int) and a gui that should visualize this data. Gui part consists of a few models and and a few standard views (table and tree). The value should be presented either as a string or as a picture depend on user choice. There are some additional difficulties: in some cases the text is different for different elements - i.e., for the same column 0 normally means "Ok", but for a particular element it should be printed as "Yes", also depend on a user choice some columns should not print text for 0 values at all, living a cell empty and don't clutter the table; next, sometimes cell should show two values - e.g., current value and requested - something like "Yes -> No". This also applies to the case when user wants to see icons.
I see two options:
The model forms string text itself and store it somewhere (in the model item may be). The model also forms composite icon from base icons if it is needed. In that scenario I can use standard delegate and just use standard roles reimplementing QAbstractItemModel::data() method.
Reimplement delegates' paint() method (actually I need to reimplement delegates anyway because I need custom editors like QComboBox). The delegate can get any information it needs through the model and custom roles in paint() method and form the string or complex icon itself. As a suboption, I suppose, it's possible for a model to compose needed information itself, like returning std::tuple<int /*value*/, int /*requested value*/, int /*source element type*/, bool /*text or icon*/> in data() method for Qt::DisplayRole.
Update: I want add one thought about second option. Presumably delegates were introduced to handle the work of representing data. One of the consequences of that is the flexibility delegates can give. If you want to see a raw number in some column, you can just leave the default delegate, if you want some additional info presented in a cell you can set a special delegate for that specific column. And it seems you don't need even touch the model or something else.
I’m trying to display some tabular data with a QTableView subclass and a QAbstractTableModel subclass. I can’t get the data to show up, but before I start really pounding on it I want to make sure that I’m using models in the way they were intended.
The data layer of my application periodically receives new data and distributes the data to the other parts of the application by calling slots like
void new_data_received(QSharedPointer<Measurement> measurement)
where Measurement is my data class. This allows the data to be passed around without being copied (some of my data classes are very large). Measurements are immutable; the table view that displays them doesn’t allow any editing.
Measurement is a subclass of QAbstractTableModel, so whenever I receive a new measurement I call set_model on my QTableView subclass instance with the new data as a parameter. (In the time before the first measurement is received there is no model set on the table view.)
Are Qt’s view classes intended to be used like this, with a new model being set every so often? Or should there be just one instance of the model class, with the same lifetime as the table view, that receives the new data and emits dataChanged? The latter seems like it adds unnecessary structure—at least in my case—but maybe that’s the way the system was designed to be used.
I don't think your Measurement class should be a subclass of QAbstractTableModel. It should represent raw data instead. So maybe a struct with some parameters or a list of structs will be a right type for your data class.
Then you should implement a custom model where incoming data are added to. So, when new data arrives that model will automatically update all the views connected to it. In this case new data affects directly your model only, not the views.
I suppose resetting view's model every time is not the right way to do what you want.
I tried to build a user interface which displays the content of a table while the data is refreshed every second.
Therefore I have a chain of models:
QSqlTableModel - to access the tables content
MyModel - inherited from QIdentityProxyModel to modify the data a bit (source is the TableModel)
SomeFilterModels - which have MyModel as source
This chain ends in a QTableView. Because the QSqlTableModel is refreshed every second,
any selection in the TableView is removed every second also. Now I had two Ideas to fix this.
Prevent the TableModel from detecting changes. Which was not working very well.
Catching some events fired before and after the model is about to change to store and restore the current selection. Sadly the QIdentityProxyModel does not forward signals like modelAboutToBeReset or modelReset or dataChanged .. it is also impossible to reemit those signals from MyModel because they are private.
I was searching for other ways to counter those refresh problems without success. But I can't imagine that I am the first person who uses a chain of proxy models combined with a periodic model refresh and selections.
Can anyone give me some tips?
Thanks in advance.
Maybe worth to note:
One QSqlTableModel is used for many TableViews. (With a different FilterProxyModel chain.) So I can't just stop refreshing because one View has a selection.
You may think that I know when I call the models refresh method. But for now it is a bit to complicated to pass this trough my ui architecture. I mean the model is updated and the TableView has already a connection to the updated model through some ProxyModels. There should be no need for another way of communication.
Hope my question makes sense.
QAbstractItemModel includes a number of signals that can help you know when the data in the model is or will be changing. In particular, it has the following signals:
dataChanged
headerDataChanged
modelAboutToBeReset
modelReset
columnsAboutToBeInserted
columnsAboutToBeMoved
columnsAboutToBeRemoved
columnsInserted
columnsMoved
columnsRemoved
rowsAboutToBeInserted
rowsAboutToBeMoved
rowsAboutToBeRemoved
rowsInserted
rowsMoved
rowsRemoved
Given that you lose the selection, I assume that the bolded signals are the ones you want to be concerned with, because the default Qt behavior is to preserve selection if they can where the columns or rows are removed/inserted and it doesn't affect the selection.
Once you are connected to these signals, in modelAboutToBeReset I suggest getting IDs for the cells that you can reuse to select them again, and in modelReset then using those IDs to get the QModelIndexs and using them to again select the same cells.
I'm currently implementing a solution using django admin, it allows users to define in the db a product, and then custom attributes and details, more details may be aggregated by a common attribute, this allows me to query with ajax a custom view that returns some JSON data to build automagically the form fields that I need directly in the same formset view (manipulating the DOM).
The current DB design follows this schema:
Catalog(name, description, photo)
Product(rel_catalog, name, base_price, photo, manufacturer_email)
ProductDetail(rel_product, rel_attribute, percentage_price, fixed_price)
ProductAttribute(rel_product, name, description)
As you may see I have a catalog, where there can be more products, a lot of details per product, aggregated by attributes. Then I simple show by default the Catalog, then the select with all available products for that catalog, then, choosing the right Product, I obtain the complete form (each row has a label with ProductAttribute.name and a select with related ProductDetail).
All works pretty dam good, but I also need to store this references in the DB when someone completes the form (making an order with choosen products). This forms are displayed as StackedInline (the ModelAdmin is for the Order).
I don't know how many options there may be per product so I was thinking to use this design to track orders:
Order(customer, status, notes, tot_price, inserted_by)
OrderItem(rel_order, catalog, product, unit_price)
But I don't know how to store the dynamic added inputs...
I was thiking to implement OrderItemProperty(rel_orderitem, rel_productdetail, rel_productattribute) to store each single input... but how do I loop over this unknown fields?
Maybe do you suggest a better design?
If you need more code just ask for it and I'll reply with a pastebin link.
Thankyou.
Finally I got a working solution,
I've created a custom view, overriding the default "add/" view, this way I can customize whatever I want to and I can read the POST data handling each validation, putting then the data in the right model.
I got a model with 2 fields: latitude and longitude. Right now they're 2 CharFields, but I want to make a custom widget to set it in admin - was thinking about displaying Google Maps, then getting the coordinates of the marker.
But can I have 1 widget (a single map) to set 2 different fields?
Define lat and long fields on your form; set them to use the HiddenInputWidget.
After the init of your form, add another field which contains your custom widget that takes two values.
In the clean method for that added field, set the values of self.cleaned_data['lat'] and self.cleaned_data['lng']
There are probably cleaner ways, but it should work
I've used overriden full_clean method for data splitting. Using clean_FIELD method is too late.
I ended up setting a custom form on my admin with an extra not required field which had a widget that controls the map and setting a maps widget of that like stevejalim. However I didn't hide the input fields since they were still left in the view and I don't mind seeing them. It would probably be cleaner to override the template for editing that model instead and putting in a map, but that seemed like to much work for something that should be simple.
One solution would be if Django allowed for complex model fields where one model field corresponds to multiple db columns but that's not yet supported (see https://code.djangoproject.com/ticket/5929)
The other would be if Django would let us use a form field that corresponds to two model fields, but there doesn't seem to be a way of accomplishing that and I can't find a ticket for it.