How to render a a complex widget inside a tree view item in QT?
Asked Answered
I

2

13

I have the following problem with QT model/view framework. I want to render a widget inside a table view item.

First my thought was to use

void QAbstractItemView::setIndexWidget( const QModelIndex & index, QWidget * widget )

But the documentation for this function explicitly states:

This function should only be used to display static content within the visible area corresponding to an item of data. If you want to display custom dynamic content or implement a custom editor widget, subclass QItemDelegate instead.

So they propose to use delegates here. Well, so far so good. I know that the delegates may be used to create an editor, which may be basically any QT widget. But here is the problem - I don't want this widget to be an editor - I want to render the item with this widget always. And not just "render", I need it to have the exact behavior of the widget.

Now the widget I want to use is a custom widget, which is a container of some other widgets (few check-boxes, few buttons with some layout).

The solution I consider is like this:

  1. Grab the look of my custom widget to a pixmap.
  2. Let the delegate paint itself using this pixmap.
  3. When the mouse is over an item cause the view to automatically start editing (I don't
    know how to do it yet, but I suppose it's possible)
  4. Let the delegate create my widget as the editor for an item.

This solution seems to work, but it smells bad for me. Can anyone thing about more elegant solution for this problem?

Thanks.

Isometrics answered 26/1, 2010 at 19:53 Comment(3)
What do you mean "I need it to have the exact behavior of the widget?Chelseachelsey
I mean to interact with the user - to react on its actionsIsometrics
What kind of interactions do you want though? Each cell is exactly that, a cell. It's not a widget so I'm having a hard time imagining what you really want in the way of interactions.Chelseachelsey
C
8

Delegates are in charge of creating editors as well as doing all necessary display. They may use styles to do much of the painting, like drawing a progress bar, or paint manually.

A delegate, however, is not a widget. Unless an editor has been invoked, it doesn't have access to most things a widget would. The two are very different, have different purposes, and accomplish different things.

One of the most troublesome aspect of delegates is that they are static. Unless something in the model triggers an update (or the widget is configured to watch hover events), the delegate won't be used to redraw whatever data is present -- the buffered representation will be drawn to the screen.

You have some control over when an editor is invoked with edit triggers, although you can definitely handle it with some custom code, such as through mouse tracking.

Chelseachelsey answered 26/1, 2010 at 20:6 Comment(2)
Agree. So basically you say, that I am going to the right direction? By the way, hover event will not help me here, since the widget is created by the delegate only when editing is triggered (before that the widget doesn't exist).Isometrics
You can use composition, hold a reference to the widget, and then use QWidget::render() to draw to a pixmap that's painted to in the delegate's paint method.Chelseachelsey
L
3

You may call QAbstractItemView.openPersistentEditor(index) for every cell you need to permanently have a complex widget for. The two keys to make this work:

  1. QStyledItemDelegate.sizeHintChanged.emit(index) needs to be called anytime the size of the editor widget has changed.
  2. Implementing QStyledItemDelegate.sizeHint() can be tricky and tedious (or you can do index.internalPointer().editor_widget.sizeHint() assuming you saved a reference of the editor to the internal pointer during QStyledItemDelegate.createEditor()

I recently used the openPersistentEditor method to render tables within tree views, however it should be mentioned that opening editors is costly, so if you have thousands of indexes and they are all loaded at once, it can take a while. There are many ways to mitigate this issue:

  1. Load the model incrementally using a thread
  2. Use Qt's fetchMore() mechanism
  3. call openPersistentEditor incrementally (using a timer, or as they come into view for the first time)
  4. call openPersistentEditor when the parent is expanded and closePersistentEditor when the parent is collapsed, and possibly restrict the use of expand-all on nodes with many children.
Ligation answered 22/10, 2018 at 19:29 Comment(2)
This is useful, thank you. But I have an additional challenge -- the editing can change the tree structure. Do you have a suggestion how to open/close the child editors of an index when when that index's value changes? (I want to always show the editor for the "parent" node -- the one that is being expanded and collapsed, and show the editors for all children when expanded.). If I just use open/closePersistentEditor as you suggest, then the editors of children are not removed when the editing causes them to disappear.Fray
Can you use QAbstractItemModel.moveRow() or the signal rowsAboutToBeMoved(), from QT doc: "Note: Components connected to this signal use it to adapt to changes in the model's dimensions.", to close the editor's of the children?Ligation

© 2022 - 2024 — McMap. All rights reserved.