PDA

View Full Version : Item Delegate for TreeView with multiple editor widgets



LynneV
6th January 2010, 22:29
I am writing a Qt front end to replace the current front end for a very old report writer. The current report writer has good functionality, it just needs a better design/preview interface. The current report writer returns a list of report line objects after parsing its definition. Two of the objects are complex: the query definition and the detail line layout. I have a QTreeView with a StandardItemModel that contains a node with one column for each line of the report Each line's data is stored in a QStandardItem as a variant with a class type of the old report writer line class. I have an item delegate that correctly shows one element from each line type, with just a single widget in each editor.
GOAL: I would like to have the tree nodes for each of complex line types show a set of editing widgets - QLineEdits/QComboBoxes for the query definition and a QSplitter with the column list/layout for the detail text lines with QLineEdits and QComboBoxes for individual column attributes. So now to the design questions...
1. The data is stored inside a user defined class, so I have to use the item delegate's paint function (no default available). Is there a reasonably quicker way to draw a combo box or a splitter than having to paint each element separately? I have three paint commands for a combo box...and I haven't even tried the splitter with a list of columns yet!
2. Do I need a container of some sort if I have multiple widgets in a delegate editor? If so, what container is best? Is there an 'easier' way to keep the painting and the editor display in synch?
3. I've considered a persistent editor. But my research so far indicates that it may not behave for inserting new rows/parents for new queries and detail lines. In any case, I'm off to try that, next. Opinions on this approach are welcome!
I've seen the thread in this forum on the multiple line text editor/display, and that's been very helpful, but it's one widget. Does anyone have a link to an example of multiple widgets for a single editor? Thanks in advance for any assistance!

LynneV
12th February 2010, 00:35
I can answer this for anyone else who might have a similar challenge. The answer is iactually in all the previous docs, both on this forum and also in the Qt docs ...but maybe if I state it the way I look at it, it will help others. I have a QStandardItemModel with QStandardItems as nodes displayed in a QTreeView. I use a class registered as a variant type using Q_DECLARE_METATYPE to store the data I need. The variant is stored in my model using setData(). Here is how I solved it:
1. Make an item delegate class derived from QStyledItemDelegate. You will have to implement Paint, CreateEditor, SetEditorData, SetGeometry and SetModelData. You will have to look at the QModelIndex you get when the delegate is invoked and figure out which node you need, and have a Switch statement that defines the behavior for each node type. Set this ItemDelegate on the view. If you clear and refresh the model with new data, set the item delegate again (as in, I'm opening and reading an existing report for editing, the item delegate gets re-set).
2. Yes, you have to paint every node to match the editor you will be using. Look at the source code for the widget you need to see what you have to use for painting. it's not always clear, but if you're stuck, try different DrawPrimitive combinations. Yes, you have to get the data from the model to draw in the node for display. You will have to take the rectangle you get (option.rect) and move the right and left edges around to draw your multiple controls where they will exactly match the editor you create in createEditor.
3. If you have several widgets on the same line, you don't have to use setGeometry. If you have widgets on different lines, you have to set the geometry to have multiple lines. Here is how I did it:
QSize row_height = QStyledItemDelegate::sizeHint(option, index);
row_height.setHeight( row_height.height() *3 );
return row_height;
4. In createEditor, use a QFrame to hold your multiple widgets. One of my nodes looks like this:
case ReportLine::QueryOpen:
{
QFrame *query_open = new QFrame( parent );
//children are added and accessed in this order -the order must match
QLineEdit *query_name = new QLineEdit( query_open );
QComboBox *query_table = new QComboBox( query_open );
QComboBox *query_type = new QComboBox( query_open );
QLineEdit *query_filter = new QLineEdit( query_open );
query_filter->setAcceptDrops(true); .... more properties
5. In setEditorData, get the mutliple widgets from the frame, and set the data from the model:
case ReportLine::QueryOpen:
{
QFrame *query_open = qobject_cast< QFrame *>(editor);
if (!query_open)
return;
//some custom stuff here, But the interesting part is that you get the widgets from the Frame in the order you add them in createEditor.
QObjectList theList = query_open->children();
int pos1= ((QComboBox*)theList.at( 1 ) )->findText( currentText, Qt::MatchExactly );
((QComboBox*)theList.at( 1 ) )->setCurrentIndex( pos1 );
((QComboBox*)theList.at( 2 ) )->setCurrentIndex(pos);
((QLineEdit*)theList.at( 3 ) )->setText( filter_string );
6. To setModelData, use the same pattern above to get the data from the various widgets, and set the data in the model using setData.

It took me a while to pull it all together, so if it helps...cheers!

aamer4yu
12th February 2010, 05:41
It would have been nice if you had some pics to show what you wanted.
:rolleyes: Still reading post :rolleyes: