PDA

View Full Version : XML, Highlighting and Model/View



aamer4yu
19th June 2008, 11:54
Hi,
I have my data in QDomElement / QDomDocument form, and I simply display it using a web browser element.On clicking, the node expands/contracts as in internet explorer. Fine.

Problem :

Now, I have a situation where I want to right click on a xml node and be able to tell its path from the root element. say if i clicked D, I should get "Root/A/B/C/D" . Also I want to keep the xml highlighting.


I have two solutions in mind -
1) Implement my own item model and item view. Simplest would be using QStandardItem and QTreeView I guess. But problem with this solution is,,, I havent used model/view till now and it looks very comples to me. Also how do I maintain XML highting even if I manage to make a tree view out of my xml data ?

2) Inherit QTreeWidget and QTreeWidgetItem. and set a delegate for it. The delegate is supposed to do the XML highlighing. Also what functions wud I need to override ? I havent worked with delegates too till now :(

Which one of the above 2 is better ?
Is there any other way I could achieve my goal more easily and efficiently ?? I dont wanna increase my code.

Any ideas ???:confused:

caduel
19th June 2008, 14:58
To i)
Learning model/view is worth the trouble.
I am not saying you should for this problem; but take a look at it anyway!

ii) I don't see why you would need/want to subclass QTreeWidget (if at all: QTreeView, probably?)? As long as you do not have a model, a view (or delegate) won't help you.


There is an example of adapting XML to a model in Qt4's examples (look for "Simple DOM Model"). Take a look at it.
Then put that in a QTreeView.
Connect to the contextMenu event (or something like), translate the mouse pos. to the QModelIndex, get the DomNode from that, and from that, the path.

HTH

aamer4yu
20th June 2008, 04:59
Hi,
I had thought of the answer you told. And the problem is xml highlighting !
If I simply use treewidget, can I set xml highlighting on it ? I guess I need to use delegates for that, isnt it ?

Or is there some easy way out :confused:

caduel
20th June 2008, 07:40
One things that will probably work (not sure if it's the best solution) would indeed be to set a delegate on the QTreeView. No need to subclass it.
Then override the delegates paint method.

create a QTextDocument, a QSyntaxHighlighter for it and set the (fragment for the QModelIndex of the) XML as plain text (in the QTextDocument).

Will probably work. (I did something like that once.)
Perhaps not efficient, though... best give it a try (and tell us about it!)

HTH

aamer4yu
20th June 2008, 13:17
I made the treeview of xml subclassing treewidget and treewidgetitem.
Now I can extract path of the node relative to the root element. Also i can change font of the tree widget item.

But still havent started code for delegate.
Before I start it, I haev a question, are delegates called on double click ?? If so it wont help me. I want to render the xml formating on that start itself. :(

Also my code is working quite slow, I need to check whats wrong, and am worried how slow it will become even if i set delegate :confused:

caduel
20th June 2008, 17:32
The delegate does
* painting of all cells (this has nothing to to with mouse clicks)
* and the delegate is also responsible for creating editors (if the model is editable)

Use valgrind or something like it to profile your application.

aamer4yu
20th June 2008, 18:42
painting of all cells (this has nothing to to with mouse clicks)

How then in Spin box delegate example, do we have to double click to see the spin box ??
or is it because of the createEditor function ?? Can anyone explain ?

caduel
20th June 2008, 19:21
the spinBox has nothing to do with drawing; it is (as you guessed) the editor created by createEditor.
The cells that are not edited do not show a spin box!

paint: looks for a cell that is not being edited
createEditor: the editor (which often is laid over the edited cell, giving the impression of a different painting while being edited. it is just an impression. the cell is always painted by paint.)

HTH

aamer4yu
22nd June 2008, 17:32
Well, started reading Model/View architecture in Qt.. but I wanted some immediate results.
So I tried the following -
copied the simple DOM example, and modified it that I show attributes in column 0 along with tag name. So something like an XML Tree. Now moving on with delegates, made a little progress.
I overrided the drawDisplay method of QItemDelegate -

void XMLDelegate::drawDisplay ( QPainter * painter, const QStyleOptionViewItem & option, const QRect & rect, const QString & text ) const
{
//QItemDelegate::drawDisplay(painter,option,rect,tex t);

QColor colors[3] = {Qt::black,Qt::red,Qt::blue};
QColor red(Qt::red);
QColor blue(Qt::blue);
QColor black(Qt::black);

QString textToDisplay = option.fontMetrics.elidedText(text,Qt::ElideNone,r ect.width());
int len = textToDisplay.length();
int x,y;
x = rect.x();
y = rect.y()+10;
for(int i=0;i<len;i++)
{
painter->setPen(colors[i%3]);
painter->drawText(x,y,QString(textToDisplay[i]));
x += option.fontMetrics.width(textToDisplay[i]);
}
}


By this am able to display text in alternate colors. Now thing is,,, I havent handled it for Wrap mode :( . And without that one display wont look good ...

Isnt there some simple way to do this ? Currently to display xml, we use Internet explorer and Navigate function for a xml file. It does display the xml with highlighting. But I want to add context menu to sucha tree, and be able to compute xpath of the node clicked...


Can someone suggest a simpler way ?? Or do I have to do it the hard way :crying:

wysota
22nd June 2008, 22:51
Could you provide a screenshot or a mockup of what you want? I think your problem is easy to solve, but first I need to make sure what you want :)

aamer4yu
23rd June 2008, 06:21
Even I think the problem must be easy to solve, may be am moving in wrong direction , may be not :D

Well, I will restate my problem with snapshots and also give an idea as what I am able to achieve till now.

First, the problem :
I need to display xml data as it displays in internet explorer. However, additionaly I want to be able to show a context menu and be able to extract the Xpath of the node that was right clicked.
Refer : required functionality.jpg
I know I can achieve this by -
1) Make a tree out of xml
2) apply syntax highlighter to each row
3) Handle multi line
But I dont know whats the best solution to achieve above steps :crying:


Now the things I have tried till now :
1) I subclassed QTreeWidget and QTreeWidgetItem and made a Tree from XML Data. This works fine, shows the data like in explorer. I override drawBranches() of treewidget.
But problem is, I can show it only in one Color. :(

2) I modified the Simple DOM Model example, and returned text in column 0 only instead of Column 1 and 2. I also made a custom delegate where I overrided drawDisplay() and was able to show text in different colors. Problem with this solution is, I see row text in single line only, and the text is clipped if its longer than the rect width.
Refer try 2.jpg

Now how do I handle multiple line in a single cell ?? Like in iexplorer, if we resize the window, the xml data gets wrapped properly. How do I achieve this functionality ?? :confused:

caduel
23rd June 2008, 07:25
assuming you do the drawing with QTextDocument; use setTextWidth, pass the size of the rectangle passed to QAbstractItemDelegate::paint (in QStyleOptionViewItem)

HTH

aamer4yu
23rd June 2008, 07:44
Dont assume !!
Am not using QTextDocument. If only I cud get QtextDocument for a model index, i cud have set the syntax highlighter for it.

What I am doing is setting a ItemDelegate for the Tree View in Simple DOM Model example.
am ONLY overriding ItemDelegate::drawDisplay() function. The code is same as posted few posts back.

Now say I have text = "Hello, How are you";
If i need to display each word in different color, I need to first write Hello with one color, then get the width occupied by Hello, and write How with other color and so on. I just cant simply write the whole text in one go :( Can I ??
Also see drawDisplay() in QItemDelegate.cpp. There are many things to consider, which are going above my head for the time being

Also I made a small parser inside the drawDisplay() function, and am able to get xml like highlighting :)
But prob remains... single line :crying: doesnt gets wrapped..
Another prob is, values are treated as child nodes, So if i want to display text vlaue of a node like <node> nodeValue </node> , am not able to do it. I will have to look in model how to prevent adding row for #text nodes :(

caduel
23rd June 2008, 08:17
ok, how about

i) using a QItemDelegate in the QTreeWidget approach, too?

ii) pass Qt::TextWordWrap to QPainter::drawText

in YourItemDelegate::paint(...):
I would not do the algorithm for drawing colored text myself (unless I had to, that is).
Doesn't a simple

painter->translate(option.rect.topLeft());
QTextDocument doc;
QSyntaxHighlighter yourHighlighter(&doc);
// if you want wrapping text
// doc.setTextWidth(option.rect.width());
QString text=index.data().toString(); // assuming you can get the xml to display that way
doc.setHtml(text);
doc.drawContents(painter);
work for you?
(Maybe it is inefficient, but it is easy to implement. And maybe it is fast enough, too.

wysota
23rd June 2008, 08:27
I'm not sure if a tree is the best way to visualize the document this way, but since you already have most things done, let's keep it this way.

Drawing wrapped text is easy - use a variant of QPainter::drawText() that takes flags as one of its parameters and pass it a flag to wrap text. But... this way you won't be able to highlight syntax...

Another way is to use QTextLayout and QTextLine which is quite straightforward, but... no highlighting either.

Probably the best way would be to have a QTextDocument that the delegate would use to render the xml. You would apply a piece of text as the document, trigger the highlighter, render the document, apply a new piece of text, etc. Just make sure not to create a new document in each pass of the delegate - reuse the old one instead.

aamer4yu
23rd June 2008, 10:28
@ Caduel
Thanks a lot !!

doc.drawContents(painter);
Thats what I needed so badly :D

Now I can use this delegate with the Simple DOM Model example, and it works. I haven't tried multi line yet, but I guess there wont be any problem


@Wysota
Caduel also suggested what u said, except for the doc reuse :P . Well, now I guess I can make things work.



I have my QTreeWidget and QTreeWidgetItem subclasses working now. But they take time to populate the tree, and hence not efficient. In DOM model example I found the child items are created as and when the nodes are expanded. Gradually I will like to move my code to DOM Model, along with syntax highlighting :)


I need to know 2 things -
1) Is there any widget/ text edit, where I can set color of each char individually ?? Its just a thought to avoid using a highlighter.

2) With the simple DOM Model, now I can set the syntax highlighter. Only thing is nodeValues are represented as child items in tree :(. How to avoid this ?

BTW Wysota, U said -

I'm not sure if a tree is the best way to visualize the document this way, but since you already have most things done, let's keep it this way.

Can you suggest the better way ? I know filling a tree myself is inefficient. The recursion takes a long time.

caduel
23rd June 2008, 13:05
without having tried it out:


1) Is there any widget/ text edit, where I can set color of each char individually ?? Its just a thought to avoid using a highlighter.
You can do that with a QTextEdit


2) With the simple DOM Model, now I can set the syntax highlighter. Only thing is nodeValues are represented as child items in tree . How to avoid this ?
You can modify the model. Adjust the data() and rowCount() methods to
data(): include the text of the childNodes() that satisfy isText()
rowCount(): do not count child nodes that satisfy the condiftion of above

Alternative:
wrap the dom-model in another model that provides said difference.

wysota
23rd June 2008, 13:49
Can you suggest the better way ? I know filling a tree myself is inefficient. The recursion takes a long time.

Yes, QTextEdit with implemented ability to collapse and expand levels of the DOM tree. This involves some work unfortunately...

As for the tree and the lag you get, you can do it in another thread or in chunks separated by event processing.

aamer4yu
23rd June 2008, 18:11
Quote:
1) Is there any widget/ text edit, where I can set color of each char individually ?? Its just a thought to avoid using a highlighter.
You can do that with a QTextEdit

How can I do it with QTextEdit ??

Also your reply to 2nd point is good.... I have tried with simple DOM Model and got sucess...now just trying to handle comments :) .


@Wysota

Yes, QTextEdit with implemented ability to collapse and expand levels of the DOM tree. This involves some work unfortunately...
:D too much work I guess ;)

Well Now I have 2 ways -
1) Subclassing QtreeWidget/QtreeWidgetItem
2) modify simple DOM example.

As for the highlighter , it will work with both.
And common problem is resizing ROW according to content.
I tried using QTextDoc.setTextWidth(). But it works only within the rect provided by the drawDisplay function. How do I resize the row so that whole text is displayed ??

Am quite close to my goal I guess,,, thanks to you both :)

aamer4yu
24th June 2008, 07:05
I am stuck with setting row height. :( I guess if I am able to set row height for each item, I can display my contents properly.
I tried setting sizeHint() in ItemDelegate, and also tried rowHeight, and indexRowSizeHint for QtreeWidget... but the rowHeight and indexRowSizeHint are never called :(. Also I tried setting setWordWrap(true) for tree widget, but no use :(

Can someone give me idea where to move ?? or what to look for ?

caduel
24th June 2008, 07:30
Have you tried QAbstractItemModel::data with role=Qt::SizeHintRole?

aamer4yu
24th June 2008, 07:38
Not yet... But what shud I return from there ? I dont have font info in data() :(

Also how do I do it with QTreeWidget, where I am not using model ? I tried rowHeight and indexRowSizeHint, I put a breakpoint, but the breakpoint was never reached :(

caduel
24th June 2008, 08:39
Yes, the fact that stuff like FontRole, SizeHintRole, ForegroundRole etc is 'part' of the model is a bit strange now and then.

Perhaps QAbstractItemDelegate::sizeHint will help you, then.
You're rendering in the delegate, so there you should be able to access font info etc.

And make sure not to set uniformRowHeights on the TreeView (if you want rows of different heights).

wysota
24th June 2008, 08:39
Not yet... But what shud I return from there ? I dont have font info in data() :(
Implement sizeHint() for your delegate. You have the font info there in the option parameter.

aamer4yu
24th June 2008, 09:32
Ya i had implemented sizeHint for delegate, but in vain.
Actually sizeHint is called once initially. So I can set height accordingly. But when I resize the window, treewidget also resize and on this resize I want the row height to increase.

Which function should I look for ? :confused:

I also tried visualRect, but it doesnt effect too :( . Also rowHeight and indexRowSizeHint are never called :(.
Isnt there any function to update the row height on resize event ?

wysota
24th June 2008, 23:15
The item size should not be dependent on the view size. Make sure uniformRowHeight property of the view is set to false.

aamer4yu
25th June 2008, 05:42
It is false. I am getting different row heights.
I was asking how to set row heights upon resize. Say if window is reduced in width, columnwidth is also reduced. So now I want to resize row height to fit the content acc to new height.

I am able to set the row height initially once by QItemDelegate::sizeHint() function. I was having 2 errors in it previously -
I was calculating row height based on -

do
{
line++;
elided = fontMetrics.elidedText(textToRender,Qt::ElideNone, width-2);
charsRendered += elided.length();
textToRender = text.mid(charsRendered);
}while(!textToRender.isEmpty());
height = height*line;

Problem was with Qt::ElideNone. I was getting the original text and the line count was always 1 :( Is elidedText() supposed to return the original text if mode is Qt::ElideNone ??.

Second problem was with width = optio.rect.width(); inside sizeHint() function.
I found that in QTreeView::indexRowSizeHint() function, width is set to -1 for some optimization. Hence I was not getting the proper column width :(. I set the width manually and now I get the line numbers correctly.

So far so good. I can assume my application wont be resized and am taking some default column width.

Still if someone know how to set row height on resize, let me know.

Thanks for ur help Caduel and Wysota :)

aamer4yu
25th June 2008, 09:58
I landed in another problem.

The QItemdelegate::sizeHint() doesnt work same with QTreeWidget and QTreeView.
In the Simple DOM example, in the QItemdelegate::sizeHint(), I am getting proper font in option parameter. While in QItemdelegate::sizeHint(), delegate set for MyTreeWidget, I dont get the proper font. :(

Hence while calculatin row height, I get a difference in these 2 implementations. Why is it so ??

Also another doubt, if we setWordWrap(true) on QTreeView, isnt it supposed to display text in multiple lines ? One can easily check using Simple DOM model and setting word wrap to true for the treeview.

wysota
25th June 2008, 14:00
Still if someone know how to set row height on resize, let me know.

There is a sizeHintChanged() signal in QAbstractItemDelegate. You can apply an event filter to the view so that you know when its size changes and emit that signal from within the delegate and the view should re-ask the delegate for new sizes then. I think it's worth to try...


The QItemdelegate::sizeHint() doesnt work same with QTreeWidget and QTreeView.
Sure it does :)


In the Simple DOM example, in the QItemdelegate::sizeHint(), I am getting proper font in option parameter. While in QItemdelegate::sizeHint(), delegate set for MyTreeWidget, I dont get the proper font. :(
What do you consider a "proper" font?


Also another doubt, if we setWordWrap(true) on QTreeView, isnt it supposed to display text in multiple lines ?

Yes, but this works only on word-breaks, I think. I did see it work, at least.

aamer4yu
25th June 2008, 16:46
What do you consider a "proper" font?
By proper font I mean the font I set on the TreeWidget or the TreeView.

The QItemdelegate::sizeHint() doesnt work same with QTreeWidget and QTreeView.
Yes, it doesnt for me. Well I meant the overrided sizeHint. What I did was I overrided sizeHint of my delegate. Say MyDelegate::sizeHint. I am using this delegate in 2 programs.

One MyTreeWidget/MyTreeWidgetItem (P1), and other modified version of Simple DOM(P2) Model. In P1 I set MyTreeWidget->setFont(myFont) after creating the tree. In P2 I did the same.
Now in P1 , in MyDelegate::sizeHint, I get the font from option.font as system default - MS Shell Dialog2(something like that :D) . and in P2 , in MyDelegate::sizeHint, for option.font I get font as myFont. This is what I mean that sizeHint doesnt work same in both cases :(


As for Word wrap, did u try setting word wrap to true for QTreeView in simple DOM model example and shrink the window width ??

For sizeHintChanged() , I will surely try.
But its gonna fail. In MyDelegate::sizeHint() function, i get option.rect.width() as -1. This is set in QTreeView::indexRowSizeHint() function for some hack :crying:
You can verify from the QTreeView::indexRowSizeHint() function.