Two different QTreeViews with one shared QStandardItemModel - python

I have a simple task but am struggling with the proper way to implement it:
I want one shared QStandardItemModel containing a tree structure to be displayed in two different QTreeViews.
The first QTreeView shows the model as-is and allows the user to add, remove and rearrange the items. Clicking an item shows an item-associated QWidget within a QStackedWidget below the QTreeView. Some item types can also be renamed.
The second QTreeView shows the same tree model, however all items are greyed out by default. A Processor will then move along the tree structure and process each item. Whenever an item is processed, it should not longer be greyed out. Those items not greyed out should be clickable to display the item-corresponding processed image in a viewer. Whenever a change is made in the first QTreeView, the Processor starts processing from the changed position onwards again.
How do I implement the different look and functionality of the two QTreeViews? Do I assign custom QItemDelegates to each QTreeView? Should I use a QIdentityProxyModel for the second QTreeView?

It is only necessary to use a QStandardItemModel for both models since they store the same information so if you want them to look different then you must use delegates.
If you want some action when the view is clicked then that does not depend on the model but you have to use the view clicked signal that is independent of the other view.
For the second view if a delegate is required to change the color, the logic is to use a role to indicate if that item has been processed or not, and then change the color based on that role. When you say "a change in the first QTreeView" you should actually say "a change in the model" so it is only necessary to use the dataChanged signal, process the item by changing the role status before and after processing.

Related

Creating draggable icons in a listview in PyQt

I'm working on a PyQt program where I want to be able to have some objects, say shapes, in a 'toolbar' of sorts. I would like the user to be able to click and drag an instance of one of these shapes from the toolbar to a main canvas (a QGraphicsView).
For the toolbar I was thinking of using a QListView which I could populate using a QAbstractListModel. However, I'm not sure how I can make the QListView hold only the icons of the shapes that I want... and second of all, how I can make the object icons draggable onto my main canvas. Any ideas?
If this question contained some specific code examples with an existing direction, I could comment more specifically, but here is just some general points:
Your QListView data does not need to specifically relate to the drag and drop issue, so you can populate it however you want: QListWidget, QListView + model, ../
The important aspect is setting up drag methods on your QListX widget to set up an appropriate QMimeData in the QDrag. This is the important part that transfers the data to the drop site. You can have it send an image, or some reference to some data, for which the receiver can make use of.
Set up a drop event on the graphics view to receive and check the mime type. Lets say you are just sending an image. You can receive it and add a pixmap item to your scene. Or maybe you are sending some internal reference like an id to a dictionary. It can be looked up and the data can be added to the scene in the form you want.
Refer to the general Qt Drag and Drop docs. They are pretty much what you want.

Turn off PyQt Event Loop While Editing Table

I'm developing a GUI with PyQt. The GUI has a qListWidget, a qTableWidget, and a plot implemented with Mayavi. The list refers to shapes that are plotted (cylinders and cones for example). When a shape is selected in the list, I want the shape's properties to be loaded into the table (from a dictionary variable) and the shape to be highlighted in the plot. I've got the Mayavi plotting working fine. Also, if the table is edited, I need the shape to be re-plotted, to reflect the new property value (like for a cylinder, if the radius is changed).
So, when a list item is selected -> update the table with the item's properties (from a dictionary variable), highlight the item on the plot
When the table is edited -> update the dictionary variable and re-plot the item
The Problem: when I select a list item and load data into the table, the qTableWidget ItemChanged signal fires every time a cell is updated, which triggers re-plotting the shape numerous times with incomplete data.
Is there a typical means of disabling the GUI event loop while the table is being programmatically updated? (I have experience with Excel VBA, in that context setting Application.EnableEvents=False will prevent triggering a WorksheetChange event every time a cell is programmatically updated.)
Should I have a "table update in progress" variable to prevent action from being taken while the table is being updated?
Is there a way to update the Table Widget all at once instead of item by item? (I'll admit I'm intentionally avoiding Model-View framework for the moment, hence the qListWIdget and qTableWidget).
Any suggestions?
I'm a first time poster, but a long time user of StackOverflow, so I just want to say thanks in advance for being such an awesome community!
blockSignals(bool) is intended for suppressing QObjects and their subclasses from emitting signals, thus preventing any other objects from receiving them in slots. But this is a QObject method. If you are specifically trying to prevent one object from emitting signals in response to changes that you are making, which might trigger calculations or some other expensive processing in a slot, then this is what you want.
But if your situation is that making repeated changes is causing expensive paint operations over and over (or other expensive events being generated on the widget), then you have the ability to disable updates with updatesEnabled(bool). A benefit of this method is that it recursively disables the children of the target widget, preventing them from being updated as well. So nothing in the hierarchy will receive updates until you enable again.
mainWidget.setUpdatesEnabled(False)
# do a bunch of operations that would trigger expensive events
# like repaints
mainWidget.setUpdatesEnabled(True)
Ultimately it depends on whether the source of your problem comes from triggering signals, or triggering widget events. Blocking the signals will still allow the widget to process its events, but just not notify any other listeners about it. updatesEnabled is a common way to wrap a number of list/table/tree updates. When it is enabled again afterwards, a single post update will be performed.
Signals can be temporarily blocked for any object that inherits QObject:
self.tableWidget.blockSignals(True)
# perform updates, etc
self.tableWidget.blockSignals(False)
If you disable the entire event loop, the app becomes unresponsive. And, even if the user doesn't notice, the OS might, and put up some kind of "hang" notification (like OS X's brightly-colored spinning beachball, which no user will ever miss).
You might want to disable repaints without disabling the event loop entirely. But even that's probably too drastic.
All you're really trying to do is make sure the table stops redrawing itself (without changing the way you've implemented your table view, which you admit isn't ideal, but you have reasons for).
So, just disable the ItemChanged updates. The easiest way to do this, in almost every case, is to call blockSignals(True) on the widget.
In the rare cases where this won't work (or when you're dealing with ancient code that's meant to be used in both Qt4-based and earlier projects), you can still get the handler(s) for the signal, stash them away, and remove them, then do your work, then restore the previous handler(s).
You could instead create a flag that the handlers can access, and change them so they do nothing if the flag is set. This is the traditional C way of doing things, but it's usually not what you want to do in Python.

Customize QTreeView items

I'm new to PySide and Qt at all, and now need to create an application which has a tree view with styled items. Each item needs two lines of text (different styles), and a button. Many items are supposed to be in the view, so I chose QTreeView over QTreeWidget. Now I managed to add simple text items (non-styled) to the QTreeView and have almost no idea about how to place several widgets on one item. Could you please give me an example of how to create such design?
I've found some samples on the Internet, that are similar to what I want, but they all are in C++, and it's not obvious how to convert delegates and other things to Python. I'm now really confused about it all...
I'd recomend you use simple QTreeWidget and insert complex widgets with setItemWidget. While Qt's widhets are alien, they are not so heavy to draw, but:
You shouldn't create delegates.
You shouldn't handle events (If you are going to place button in view and draw it using delegates, you had to handle all its events, such as mouseOver, focus changing, etc. It is a lot of work.

The order of wxPython events is causing me problems

My GUI consists of a wx.ListCtrl on the left, with a list of objects to edit, and a set of wx.TextCtrls on the right, for editing the selected object.
My strategy for implementing this was:
On a textbox's wx.EVT_KILL_FOCUS , update the relevant attribute of the currently selected object
On the list's wx.EVT_LIST_ITEM_DESELECTED, either hide the textboxes or blank them out and disable them (needed for when the user clicks the blank space in the list control)
On the list's wx.EVT_LIST_ITEM_SELECTED, populate the text controls with the values of the selected object's attributes
With this setup, there are 3 use cases, and 2 of them work:
When the user is clicking/tabbing between textboxes, the correct wx.EVT_KILL_FOCUSs occur, and the attributes get updated.
When the user clicks from a textbox into the blank space in the list, that's OK as well: first wx.EVT_KILL_FOCUS causes the attribute to update, and then wx.EVT_LIST_ITEM_DESELECTED hides the textboxes.
The problem is when the user clicks directly from a textbox to another object in the list control. The order of events in this case is wx.EVT_LIST_ITEM_DESELECTED, wx.EVT_LIST_ITEM_SELECTED, and then finally wx.EVT_KILL_FOCUS. You can probably see the problem: by the time the method that updates attributes is called, a new object has already been selected and the textboxes have been populated with new values.
So I know exactly what the problem is, but I can't come up with a nice, clean way to fix it. Ideally I'd like to be able to change the order of the wx events (putting wx.EVT_KILL_FOCUS at the front), but I doubt that's possible. Is there some other obvious solution I'm missing?
wx.EVT_LIST_ITEM_DESELECTED will only fire when the user changes the selected object in the list box. This serves the same purpose as losing focus on the text box. Call the update routines from that event as well. To skip the subsequent wx.EVT_KILL_FOCUS from the text box set a "isDirty" attribute in the parent object after you update the attributes. You can check the isDirty value anytime to confirm there are changes to commit. This attribute would have to be reset when you populate the text boxes for the new selection and then set during other textbox events.
It looks to me like you are trying to re-implement from scratch the functionality of wxListbook. It seems like a lot of work, perhaps you can use wxListbook
to do what you need.
http://docs.wxwidgets.org/2.9.4/classwx_listbook.html

gtk logic behind treeviewcolumns needing cell renderers

From what I understand about GTK, if I have a TreeView, I can't just use any widget I want to display information about a column. For text, you need a gtk.CellRendererText. For toggle buttons, a gtk.CellRendererToggle. For anything else, it seems you have to implement yourself, which, from a sample one for buttons that I saw, doesn't look straightforward.
Firstly, is this the case? Is there an easy way to set up whatever widget you want to be used to display some text? If not, then why is it implemented this way? If I were designing GTK i would just create some sort of system where when a row was added and when some data model information changes, user-specified callbacks would be called which would add the appropriate widget or change it, respectively.
To write a custom CellRenderer (copy-pasted from this link!):
Register some new properties that your
renderer needs with the type system
and write your own set_property and
get_property functions to set and get
your new renderer's properties.
Write your own cell_renderer_get_size
function and override the parent
object's function (usually the parent
is of type GtkCellRenderer. Note that
you should honour the standard
properties for padding and cell
alignment of the parent object here.
Write your own cell_renderer_render
function and override the parent
object's function. This function does
the actual rendering.
And there is a good/simple example for pyGTK.
Writing a custom CellRenderer is not too hard, the hardness is that how to write a custom widget. If you have learned how to write a custom widget, then writing a custom CellRenderer is easy.
The logic behind this design is flexibility. A TreeViewColumn indicates how the data (from a TreeModel) should be displayed by a CellRenderer, thus a TreeViewColumn which represents a value of boolean type, can be displayed as a text (CellRendererText) or can be displayed as a check box (CellRendererToggle). e.g. a TreeViewColumn which represents a value of integer type, can be displayed as a text (CellRendererText) or can be displayed as a progress bar (CellRendererProgress) or can be displayed as a spin button (CellRendererSpin) or can be displayed as everything that we want.

Categories