Using custom role in QTreeView instead of DisplayRole - python

A simple question, (hopefully with a simple answer).
QTreeView will pass Qt.DisplayRole to the model's data function when fetching rows for display, by default.
But say I wanted to pass 'MyAwesomeTreeDisplayRole' instead of Qt.DisplayRole, what would I need to do?
I can't seem to find anything about where the view decides to use displayrole or how to override it.
Just before you ask why I want to do such awful things..
Basically, my QAbstractItemModel is intended to be usable both for a treeview (1 column) and a tableview (multiple columns, based on parent).
Using the same DisplayRole for both types of views doesn't really work, since then I'm forced to return the same data for both. This results in me only seeing the first column in the treeview when I want to return a concatenation of a couple of columns.
I think the simplest way would be to just use custom roles (TreeDisplayRole and TableDisplayRole).
Thanks in advance.

I don't know, how to do it in python, but you have to create your own delegate to render items data. Delegate is actually the object, that requests any data from indices and renders it
Note, that in common case QTreeView is able to show multiple columns, you should only have columnCount set incorrectly for top-level index

I've used QTreeView to view a simple table with many columns - it works well - you can adjust the column count by sending it the columnCountChanged signal. So I'm not sure you need to do what you are asking.
But I've used ItemDelegates too: use my_treeview.setItemDelegate() to point the treeview to your item delegate. If this subclasses QStyledItemDelegate then it doesn't need to do much work - it can mostly just pass it all through to super.
The user roles then are just agreed between your custom ItemDelegate and your ItemModel and can be anything you like as long as they are >= than QtCore.Qt.UserRole.
(Edit: Sorry, I put this in as a new answer but it's really just an elaboration on what Lol4t0 said - apart from the columnCountChanged thing)

Related

How to keep multiple QAbstractItemModel classes in sync

I've been working really hard trying to find a solution to this for the past few weeks. I have committed to a direction now, but I am still not entirely satisfied with what I have come up with. Asking this now purely out of curiosity and for hope of a more proper solution for next time.
How on earth do I keep multiple QAbstractItemModel classes in sync that are referring to the same source data but displaying in different ways in the tree view?
One of the main reasons for using model/view is to keep multiple views in sync with one another. However, if each of my views requires different data being displayed at the same column, as far as I can tell I need to then subclass my model to two different models with different implementations that will then cater to each of those unique view displays of the same items.
Underlying source items are the same, but data displayed is different. Maybe the flags are different as well, so that the user can only select top level items in one view and then can only select child items in the other view.
I'll try to give an example:
Lets say my TreeItem has three properties: a, b, c.
I have two tree views: TreeView1, TreeView2. Each has two columns.
TreeView1 displays data as follows: column1 -> a, column2 -> b
TreeView2 displays data as follows: column1 -> a, column2 -> c
I then need to create two different models, one for TreeView1 and one for TreeView2, and override the data and flags methods appropriately for each.
Since they are now different models, even though they are both referring to the same TreeItem in the background, they are no longer staying in sync. I have to manually call the refresh on TreeView2 whenever I change data on TreeView1, and vice versa.
Consider that column1, or property a, is editable and allows the user to set the name of the TreeItem. Desired behaviour would be for the edit that is done in TreeView1 to instantly be reflected in TreeView2.
I feel like I am missing some important design pattern or something when approaching this. Can anyone out there see where I am going wrong and correct me? Or is this a correct interpretation?
Thanks!
One way to do it is to use viewmodels. Have one QAbstractItemModel adapter to your underlying data model. All interaction must pass through that model. When you need to further adapt the data to a view, simply use a proxy view model class that refers to the adapter above and reformats/adapts the data for a view. All the view models will then be automagically synchronized. They can derive from QAbstractProxyModel, although that's not strictly necessary.
There is no other way of doing it if the underlying source of data doesn't provide change notification both for the contents and for the structure. If the underlying data source provides relevant notifications, it might as well be a QAbstractItemModel to begin with :)

Same Kind Entities Groups

Let's take an example on which I run a blog that automatically updates its posts.
I would like to keep an entity of class(=model) BlogPost in two different "groups", one called "FutureBlogPosts" and one called "PastBlogPosts".
This is a reasonable division that will allow me to work with my blog posts efficiently (query them separately etc.).
Basically the problem is the "kind" of my model will always be "BlogPost". So how can I separate it into two different groups?
Here are the options I found so far:
Duplicating the same model class code twice (once FutureBlogPost class and once PastBlogPost class (so their kinds will be different)) -- seems quite ridiculous.
Putting them under different anchestors (FutureBlogPost, "SomeConstantValue", BlogPost, #id) but this method also has its implications (1 write per second?) and also the whole ancestor-child relationship doesn't seem fit here. (and why do I have to use "SomeConstantValue" if I choose that option?)
Using different namespaces -- seems too radical for such a simple separation
What is the right way to do it?
Well seems like I finally found the relevant article.
As I understand it, pulling all entities by a specific kind and pulling them by a specific property would make no difference, both will require the same type of work on the background.
(However, querying by a specific full-key, is still faster)
So basically adding a property named "Type" or any other property you want to use to split your specific entities into groups is just as useful as giving it a certain kind.
Read more here: https://developers.google.com/appengine/articles/storage_breakdown
As you see, both EntitiesByKind and EntitiesByProperty are nothing but index tables to the original key.
Finally, an answer.
Why not just put a boolean in your "BlogPost" Entity, 0 if it's past, 1 if it's future? will let you query them separately easily.

How do to explicitly define the query used in subqueryload_all?

I'm using subqueryload/subqueryload_all pretty heavily, and I've run into the edge case where I tend to need to very explicitly define the query that is used during the subqueryload. For example I have a situation where I have posts and comments. My query looks something like this:
posts_q = db.query(Post).options(subqueryload(Post.comments))
As you can see, I'm loading each Post's comments. The problem is that I don't want all of the posts' comments, I need to also take into account a deleted field, and they need to be ordered by create time descending. The only way I have observed this being done, is by adding options to the relationship() declaration between posts and comments. I would prefer not to do this, b/c it means that that relationship cannot be reused everywhere after that, as I have other places in the app where those constraints may not apply.
What I would love to do, is explicitly define the query that subqueryload/subqueryload_all uses to load the posts' comments. I read about DisjointedEagerLoading here, and it looks like I could simply define a special function that takes in the base query, and a query to load the specified relationship. Is this a good route to take for this situation? Anyone ever run into this edge case before?
The answer is that you can define multiple relationships between Posts and Comments:
class Post(...):
active_comments = relationship(Comment,
primary_join=and_(Comment.post_id==Post.post_id, Comment.deleted=False),
order_by=Comment.created.desc())
Then you should be able to subqueryload by that relationship:
posts_q = db.query(Post).options(subqueryload(Post.active_comments))
You can still use the existing .comments relationship elsewhere.
I also had this problem and it took my some time to realize that this is an issue by design. When you say Post.comments then you refer to the relationship that says "these are all the comments of that post". However, now you want to filter them. If you'd now specify that condition somewhere on subqueryload then you are essentially loading only a subset of values into Post.comments. Thus, there will be values missing. Essentially you have a faulty representation of your data in the model.
The question here is how to approach this then, because you obviously need this value somewhere. The way I go is building the subquery myself and then specify special conditions there. That means you get two objects back: The list of posts and the list of comments. That is not a pretty solution, but at least it is not displaying data in a wrong way. If you were to access Post.comments for some reason, you can safely assume it contains all posts.
But there is room for improvement: You might want to have this attached to your class so you don't carry around two variables. The easy way might be to define a second relationship, e.g. published_comments which specifies extra parameters. You could then also control that no-one writes to it, e.g. with attribute events. In these events you could, instead of forbidding manipulation, handle how manipulation is allowed. The only problem might be when updates happen, e.g. when you add a comment to Post.comments then published_comments won't be updated automatically because they are not aware of each other. Again, I'd take events for this if this is a required feature (but with the above ugly solution you would not have that either).
As a last, hybrid, solution you could take the first approach and then just assign those values to your object, e.g. Post.deleted_comments = deleted_comments.
The thing to keep in mind here is that it is generally not a clever idea to manipulate the query the ORM makes as this could lead to problems later on. I have taken this approach and manipulated the queries (with contains_eager this is easily possible) but it has created problems on some points (while generally being functional) so I dropped that approach.

Iterating over controls in wxPython in order to save session data

I have a GUI written in wxPython (with boa constructor).
I would like to save a user's session to a file, to be loaded the next time the application starts.
I would like to avoid saving each value 'by hand' by iterating over the controls and saving their values to a dictionary.
Is there a way to get a hold of all the wxIDs used in the application, and their corresponding widgets?
You don't need the IDs at all, just start from the top level window and recursively enumerate all the children using wxWindow::GetChildren() method. Then, for each child, you will need to dynamically determine its type (this is simpler if you only use controls of a few types) and save its value. You may also find it useful to specify the names (not labels) for your controls when creating them to have a more convenient unique identifier for each of them than a numeric ID.
IMHO you are going at this wrong. The state of a user's session is best not stored in the values of the controls. The state should be stored in a 'model'. The 'view' should query the model when it needs to display the state of the model, and when it wants to save the state to a file. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller.
This makes lots of things easier, even trivial, including your problem.
I would look at the PersistenceManager mechanism in wx.lib.agw. Here are the original docs for it: http://xoomer.virgilio.it/infinity77/AGW_Docs/persist.persistencemanager.PersistenceManager.html
And here are the newer docs:
https://docs.wxpython.org/wx.lib.agw.persist.persistencemanager.PersistenceManager.html#wx.lib.agw.persist.persistencemanager.PersistenceManager
Alternatively, you can probably use the frame or panel's GetChildren() method to grab all the widgets and pull the values from them, but I think the PersistenceManager would make more sense.

QAbstractTableModel and QComboBox together

I have a subclass of a QAbstractTableModel and I want to use QComboBox to display a dropbox for this table.
But by default it shows just the first column of my table, which is not what I want. I've tried setModelColumn to shows the column 1 instead of 0 but it did not work.
After having a look around in the internet, I've found 2 solutions:
Create another QAbstractListModel from the same data in the QAbstractTableModel
Use a QSortFilterProxyModel to hide other columns
Which is the best?
Thanks
Using setModelColumn should work. I would try to fix that. Perhaps you set the modelColumn at the wrong time, docs have this to say about it:
If set prior to populating the combo box, the pop-up view will not be affected and will show the first column (using this property's default value).
If this really doesn't work, and I'm quite sure it should, then use a QSortFilterProxyModel. This will be a lot less work and should be almost trivial.

Categories

Resources