Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 2

Qt-interest Archive, December 2006
[ModelView] item delegate rendering a widget


Message 1 in thread

Hi folks,

In a model/view of my own, I would need to have interactive QWidget for
various QModelIndexes, eg :
- draw a progressbar
- have an interactive button which icon depends on the QModelIndex's data
- etc


I am quite sure there is a **standard** way of having interactive QWidget in
the wiew for QModelIndexes, but... I am not able to figure it out from the
doc or the example.



I may use QAbstractItemModel::setIndexWidget(), but... since the content to
render is dynamic (depends on the model's data, cf. the doc for
QAbstractItemView::setIndexWidget()), and since I have many rows() in my
model (ie it would require allocating many QWidgets), it is clearly not the
correct solution...



I think the correct way to achieve this is using a QItemDelegate.
Am I wrong ?
But how ?


From that point, I see two possibilities :

POSSIBILITY 1 :
In MyItemDelegate::paint(), "emulate" the widget, ie draw the widget by
using a QStylePainter, without alocating a widget dynamically in memory.

However, in that case, how may I emulate the correct behavior for the
widget, eg :
- highlight the buttons when they are entered, or pushed
- etc.
?



POSSIBILITY 2 :
When the QModelIndex is "not interactive", use the POSSIBILITY 1 mean.

Now, when the mouse enters a QModelIndex (ie : the modelindex's widget may
be under the mouse), make the widget interactive by doing the following :
1/ start editing the QModelIndex by calling QAbstractItemModel::edit().
2/ In MyItemDelegate::createEditor(), allocate the needed QWidget, and
return it.

Now, for example, if the widget is a QPushButton, it is correctly displayed
and behaves correctly.
Problems : 
1/ this requires allocating a QWidget each time the mouse moves to a new
QModelIndex
2/ there is no leave(QModelIndex) signal in QAbstractItemView, so... how
should I close and delete the editor ?



I am quite sure there are some example arround that let the ItemDelegate
emulate some QWidgets...
Would be grateful if anyone could post it, or provide some hints !

Best-
Nicolas

--
 [ signature omitted ] 

Message 2 in thread

On 07.12.06 14:24:20, Nicolas Castagne wrote:
> Now, when the mouse enters a QModelIndex (ie : the modelindex's widget may
> be under the mouse), make the widget interactive by doing the following :
> 1/ start editing the QModelIndex by calling QAbstractItemModel::edit().
> 2/ In MyItemDelegate::createEditor(), allocate the needed QWidget, and
> return it.
> 
> Now, for example, if the widget is a QPushButton, it is correctly displayed
> and behaves correctly.
> Problems : 
> 1/ this requires allocating a QWidget each time the mouse moves to a new
> QModelIndex
> 2/ there is no leave(QModelIndex) signal in QAbstractItemView, so... how
> should I close and delete the editor ?

Could you clarify wether you want that widget yo be displayed _always_
or just when the user edits the item?

If its just for editing you won't have a problem with "too many
widgets", the widgets will be deleted once the editing is done.

In case you always want to display a widget: What exactly should that
widget do? Pushbuttons could work, about progress bars I'm not so sure.
I mean seeing 20 different progress bars on the screen is distracting,
emitting dataChanged for a few hundred (or thousand) indexes will also
probably not perform very well.

Andreas

-- 
 [ signature omitted ] 

Message 3 in thread

Hi Andreas,

> Could you clarify wether you want that widget yo be displayed _always_
> or just when the user edits the item?

The widget should, indeed, be displayed _always_, 
and it should be interactive, if it is an interactive widget,
when the mouse is over it (ie : as a any interactive QWidget does).

That's why I have problems with the "too many widgets" implicated by
QAbstractItemView::setIndexWidget.

Now, I mainly need Buttons, you are right.


I am, in fact, quite close to the required behavior with the following :

1/ in MyItemDelegate::paint, do a QPixmap::grabWidget() and render it

2/ let the QModelIndex be editable in the model
(QAbstractItemModel::flags())

3/ connect the signal QAbstractItemView::entered() to the slot
QAbstractItemView::edit()

4/ in MyItemDelegate::createEditor(), allocate the needed widget and returns
it



However, I still have some problems I am working on.

My main question now is : am going toward a correct solution ?

If yes, I may post later on a sample code, to let pple know and to detail
the remaining problems.




Now, concerning :
> In case you always want to display a widget: What exactly should that
> widget do? Pushbuttons could work, about progress bars I'm not so sure.
> I mean seeing 20 different progress bars on the screen is distracting,
> emitting dataChanged for a few hundred (or thousand) indexes will also
> probably not perform very well.

Each rows corresponds with a threaded processing.
Only a few can be active at a moment.
The progressbar is available only for these rows.
The rows are updated only on a per-second basis : not so costly in fact.


Though, I mainly need interactive buttons, as said previously.

But, more generally, I am looking for a generic mean I may reuse in other
model/view I am about to design. 
I am quite sure there is one, thanks to this huuuuuuuuuuuuuuuuge Qt
library :=)

Best-
Nicolas


Andreas Pakulat wrote:

> On 07.12.06 14:24:20, Nicolas Castagne wrote:
>> Now, when the mouse enters a QModelIndex (ie : the modelindex's widget
>> may be under the mouse), make the widget interactive by doing the
>> following : 1/ start editing the QModelIndex by calling
>> QAbstractItemModel::edit(). 2/ In MyItemDelegate::createEditor(),
>> allocate the needed QWidget, and return it.
>> 
>> Now, for example, if the widget is a QPushButton, it is correctly
>> displayed and behaves correctly.
>> Problems :
>> 1/ this requires allocating a QWidget each time the mouse moves to a new
>> QModelIndex
>> 2/ there is no leave(QModelIndex) signal in QAbstractItemView, so... how
>> should I close and delete the editor ?
> 
> Could you clarify wether you want that widget yo be displayed _always_
> or just when the user edits the item?
> 
> If its just for editing you won't have a problem with "too many
> widgets", the widgets will be deleted once the editing is done.
> 
> In case you always want to display a widget: What exactly should that
> widget do? Pushbuttons could work, about progress bars I'm not so sure.
> I mean seeing 20 different progress bars on the screen is distracting,
> emitting dataChanged for a few hundred (or thousand) indexes will also
> probably not perform very well.
> 
> Andreas
> 

--
 [ signature omitted ] 

Message 4 in thread

On 07.12.06 18:16:31, Nicolas Castagne wrote:
> Hi Andreas,
> 
> > Could you clarify wether you want that widget yo be displayed _always_
> > or just when the user edits the item?
> 
> The widget should, indeed, be displayed _always_, 
> and it should be interactive, if it is an interactive widget,
> when the mouse is over it (ie : as a any interactive QWidget does).
> 
> That's why I have problems with the "too many widgets" implicated by
> QAbstractItemView::setIndexWidget.

I think this is the time I would start to think about subclassing one of
the Q*View classes (I guess QTableView for you) and hook into the
scrolling. That way you could only create widgets for the indexes that
are currently visible, and if a widget goes "out of sight" delete the
widgets for it (or even reuse them!).

Andreas

-- 
 [ signature omitted ] 

Message 5 in thread

Nicolas,

The problem with showing actual widgets "always" is that isn't not 
scalable.  A 1000x10 table is 10000 widgets.  Not to mention Windows has 
a hard system wide limit of ~32K objects.  So what you really want is 
10,000 fake widgets drawn with one real widget created to edit a cell.  
A user cannot physically edit more than one field at a time.  If done 
right  the switching from fake to real widgets is transparent because 
the pixels line up exactly. Here is what you want to do:

1)In paint()
Create a QStyleOptions for the widget you want to draw
Fill out the structure.
Get a pointer to the platform style and call something like
style->drawControl(CE_PushButton, opts, painter, 0);

2) In createEditor
Return a newly allocated "real" widget like:
return new QPushButton;

The style code is the exact same code that the widget uses to draw 
itself, so the look is exactly the same.  You can now paint thousands of 
"widgets" and still edit transparently with a "real" widget.

--Justin

Nicolas Castagne wrote:
> Hi Andreas,
>
>   
>> Could you clarify wether you want that widget yo be displayed _always_
>> or just when the user edits the item?
>>     
>
> The widget should, indeed, be displayed _always_, 
> and it should be interactive, if it is an interactive widget,
> when the mouse is over it (ie : as a any interactive QWidget does).
>
> That's why I have problems with the "too many widgets" implicated by
> QAbstractItemView::setIndexWidget.
>
> Now, I mainly need Buttons, you are right.
>
>
> I am, in fact, quite close to the required behavior with the following :
>
> 1/ in MyItemDelegate::paint, do a QPixmap::grabWidget() and render it
>
> 2/ let the QModelIndex be editable in the model
> (QAbstractItemModel::flags())
>
> 3/ connect the signal QAbstractItemView::entered() to the slot
> QAbstractItemView::edit()
>
> 4/ in MyItemDelegate::createEditor(), allocate the needed widget and returns
> it
>
>
>
> However, I still have some problems I am working on.
>
> My main question now is : am going toward a correct solution ?
>
> If yes, I may post later on a sample code, to let pple know and to detail
> the remaining problems.
>
>
>
>
> Now, concerning :
>   
>> In case you always want to display a widget: What exactly should that
>> widget do? Pushbuttons could work, about progress bars I'm not so sure.
>> I mean seeing 20 different progress bars on the screen is distracting,
>> emitting dataChanged for a few hundred (or thousand) indexes will also
>> probably not perform very well.
>>     
>
> Each rows corresponds with a threaded processing.
> Only a few can be active at a moment.
> The progressbar is available only for these rows.
> The rows are updated only on a per-second basis : not so costly in fact.
>
>
> Though, I mainly need interactive buttons, as said previously.
>
> But, more generally, I am looking for a generic mean I may reuse in other
> model/view I am about to design. 
> I am quite sure there is one, thanks to this huuuuuuuuuuuuuuuuge Qt
> library :=)
>
> Best-
> Nicolas
>
>
> Andreas Pakulat wrote:
>
>   
>> On 07.12.06 14:24:20, Nicolas Castagne wrote:
>>     
>>> Now, when the mouse enters a QModelIndex (ie : the modelindex's widget
>>> may be under the mouse), make the widget interactive by doing the
>>> following : 1/ start editing the QModelIndex by calling
>>> QAbstractItemModel::edit(). 2/ In MyItemDelegate::createEditor(),
>>> allocate the needed QWidget, and return it.
>>>
>>> Now, for example, if the widget is a QPushButton, it is correctly
>>> displayed and behaves correctly.
>>> Problems :
>>> 1/ this requires allocating a QWidget each time the mouse moves to a new
>>> QModelIndex
>>> 2/ there is no leave(QModelIndex) signal in QAbstractItemView, so... how
>>> should I close and delete the editor ?
>>>       
>> Could you clarify wether you want that widget yo be displayed _always_
>> or just when the user edits the item?
>>
>> If its just for editing you won't have a problem with "too many
>> widgets", the widgets will be deleted once the editing is done.
>>
>> In case you always want to display a widget: What exactly should that
>> widget do? Pushbuttons could work, about progress bars I'm not so sure.
>> I mean seeing 20 different progress bars on the screen is distracting,
>> emitting dataChanged for a few hundred (or thousand) indexes will also
>> probably not perform very well.
>>
>> Andreas
>>
>>     
>
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with "unsubscribe" in the subject or the body.
> List archive and information: http://lists.trolltech.com/qt-interest/
>   

begin:vcard
begin:vcard
fn:Justin Noel
n:Noel;Justin
org:ICS;Engineering
adr:;;54B Middlesex Trpk;Bedford;MA;01730;USA
email;internet:justin@xxxxxxx
title:Sr. Consulting Engineer / Certified Qt Instructor
tel;work:(617) 621-0060
url:http://www.ics.com
version:2.1
end:vcard


Message 6 in thread

Thx you both for your answer.

Andreas, I ll think about your porposal closely tomorrow !

Justin :
you got precisely what I was thinking about !

Now, two refined problems I am into are :

PB 1 :
the true QWidget to be used is not the "edited widget" in the sense of the
view.
Especially, the widget should be "not fake" but "true" when the mose is over
the QModelIndex.
The view does not provide a mean trigger editing when the "mouse is over".
Hence, I have to set up one.

The best I found so far is :
connecting the signal QAbstractItemView::entered() to the slot
QAbstractItemView::edit()

But it has some drawbacks...


PB 2 : dynamic allocation
Since my "true" QWidget is about to be displayed often (each time the mouse
is over it), I consider it would be much better to let MyItemDelegate
possess the widget (as an attribute) and return it in createEditor without
the need to alocate it.

However, QItemDelegate is not designed to do this.
Especially, it seems that Qt silently delete the editor widget once editing
is finished.
Is there a mean to avoid this ?


I think I will be able to post a sample code by tomorrow.

Best-
Nicolas






Justin Noel wrote:

> Nicolas,
> 
> The problem with showing actual widgets "always" is that isn't not
> scalable.  A 1000x10 table is 10000 widgets.  Not to mention Windows has
> a hard system wide limit of ~32K objects.  So what you really want is
> 10,000 fake widgets drawn with one real widget created to edit a cell.
> A user cannot physically edit more than one field at a time.  If done
> right  the switching from fake to real widgets is transparent because
> the pixels line up exactly. Here is what you want to do:
> 
> 1)In paint()
> Create a QStyleOptions for the widget you want to draw
> Fill out the structure.
> Get a pointer to the platform style and call something like
> style->drawControl(CE_PushButton, opts, painter, 0);
> 
> 2) In createEditor
> Return a newly allocated "real" widget like:
> return new QPushButton;
> 
> The style code is the exact same code that the widget uses to draw
> itself, so the look is exactly the same.  You can now paint thousands of
> "widgets" and still edit transparently with a "real" widget.
> 
> --Justin
> 
> Nicolas Castagne wrote:
>> Hi Andreas,
>>
>>   
>>> Could you clarify wether you want that widget yo be displayed _always_
>>> or just when the user edits the item?
>>>     
>>
>> The widget should, indeed, be displayed _always_,
>> and it should be interactive, if it is an interactive widget,
>> when the mouse is over it (ie : as a any interactive QWidget does).
>>
>> That's why I have problems with the "too many widgets" implicated by
>> QAbstractItemView::setIndexWidget.
>>
>> Now, I mainly need Buttons, you are right.
>>
>>
>> I am, in fact, quite close to the required behavior with the following :
>>
>> 1/ in MyItemDelegate::paint, do a QPixmap::grabWidget() and render it
>>
>> 2/ let the QModelIndex be editable in the model
>> (QAbstractItemModel::flags())
>>
>> 3/ connect the signal QAbstractItemView::entered() to the slot
>> QAbstractItemView::edit()
>>
>> 4/ in MyItemDelegate::createEditor(), allocate the needed widget and
>> returns it
>>
>>
>>
>> However, I still have some problems I am working on.
>>
>> My main question now is : am going toward a correct solution ?
>>
>> If yes, I may post later on a sample code, to let pple know and to detail
>> the remaining problems.
>>
>>
>>
>>
>> Now, concerning :
>>   
>>> In case you always want to display a widget: What exactly should that
>>> widget do? Pushbuttons could work, about progress bars I'm not so sure.
>>> I mean seeing 20 different progress bars on the screen is distracting,
>>> emitting dataChanged for a few hundred (or thousand) indexes will also
>>> probably not perform very well.
>>>     
>>
>> Each rows corresponds with a threaded processing.
>> Only a few can be active at a moment.
>> The progressbar is available only for these rows.
>> The rows are updated only on a per-second basis : not so costly in fact.
>>
>>
>> Though, I mainly need interactive buttons, as said previously.
>>
>> But, more generally, I am looking for a generic mean I may reuse in other
>> model/view I am about to design.
>> I am quite sure there is one, thanks to this huuuuuuuuuuuuuuuuge Qt
>> library :=)
>>
>> Best-
>> Nicolas
>>
>>
>> Andreas Pakulat wrote:
>>
>>   
>>> On 07.12.06 14:24:20, Nicolas Castagne wrote:
>>>     
>>>> Now, when the mouse enters a QModelIndex (ie : the modelindex's widget
>>>> may be under the mouse), make the widget interactive by doing the
>>>> following : 1/ start editing the QModelIndex by calling
>>>> QAbstractItemModel::edit(). 2/ In MyItemDelegate::createEditor(),
>>>> allocate the needed QWidget, and return it.
>>>>
>>>> Now, for example, if the widget is a QPushButton, it is correctly
>>>> displayed and behaves correctly.
>>>> Problems :
>>>> 1/ this requires allocating a QWidget each time the mouse moves to a
>>>> new QModelIndex
>>>> 2/ there is no leave(QModelIndex) signal in QAbstractItemView, so...
>>>> how should I close and delete the editor ?
>>>>       
>>> Could you clarify wether you want that widget yo be displayed _always_
>>> or just when the user edits the item?
>>>
>>> If its just for editing you won't have a problem with "too many
>>> widgets", the widgets will be deleted once the editing is done.
>>>
>>> In case you always want to display a widget: What exactly should that
>>> widget do? Pushbuttons could work, about progress bars I'm not so sure.
>>> I mean seeing 20 different progress bars on the screen is distracting,
>>> emitting dataChanged for a few hundred (or thousand) indexes will also
>>> probably not perform very well.
>>>
>>> Andreas
>>>
>>>     
>>
>> --
>> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with
>> "unsubscribe" in the subject or the body. List archive and information:
>> http://lists.trolltech.com/qt-interest/
>>

--
 [ signature omitted ] 

Message 7 in thread

Hi folks,

As "promised", I enclose in this post a sample code featuring a View
displaying "virtual" interactive QWidgets.


As far as I checked, no bug in this code. And I did not notice slow down.
But... I am not sure it is the best solution to achieve the specs.

So, ANY comment, advice, etc. welcomed !




In my code, all the rendering of widget are made by drawing allocated
widget. No use of QStylePainter. Though, one need only a single allocated
widget per column - see MyItemDelegate.

Note that In the view open a persistent editor to display the interactive,
QPushButton (ie : when mouse is over the corresponding index).
This is because this "pseudo-editor" may be opened while another index is
edited (in row 0 for example), and because the view cannot have mutliple
editors opened unless they are "persistent" (AFAIK).


Each row in the model corresponds with a "pseudo-process" MyModelRowData
which processing is emulated by a QTimer. 

The model has three colums.
Column 0 is the row name.
Column 1 is the row's completion percentage. It is renderd through a
ProgressBar.
Column 2 is the mean to control the state of the row. It is renderd through
a PushButton.




My own comment on this sample code is :

Now that I came to this code, 
I think that, probably, it would be better (especially more efficient)
not to use any allocated widget,
but to do ALL the paintings by drawing a "virtual" widget using a
QStylePainter.

However, drawing should reflect all the usual interactive widget's behavior.

I am not sure that this is possible by using the QStyleOptionViewItem 
options provided in ItemDelegate::paint()...
Especially, shall we have in QStyleOptionViewItem "on mouse press", "on
mouse over" options, etc ?
And what if the widget is not a simple QWidget, but a complex, layouted
widget ?

Anyhow, it seems a bit painful.
"Analysing" the QStyleOptionViewItem  in order to build the specific
QStyleOption corresponding with the needed widget seems a bit tricky.

What do you think ?

End of comment.


Your turn ! ;=)


Best-
Nicolas

Attachment: widgets_in_view.tar
Description: Unix tar archive