PDA

View Full Version : Editable text in QGraphicsView



wysota
22nd February 2007, 12:37
Hi people!

I have two issues to discuss. They are related to QGraphicsView and displaying text.

My problem is that I want to have an item composed of a shape item (let's say a rectangle) and an editable text inside it. The trivial implementation would be to have a QGraphicsRectItem with a QGraphicsTextItem child. The problem is, that when you click the text to edit it, the text item gets a rectangle border around it which I don't like.

A solution would be to subclass and reimplement paint(), making sure the frame is not drawn. But here we encounter the problem - there is no method to render the text! That makes it impossible to modify the way QGraphicsTextItem works, you can only extend it.

Extend:

void MyItem::paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget){
// do something here
QGraphicsTextItem::paint(painter, option, widget);
// or here to extend
}

Original (simplified):

void QGraphicsTextItem::paint(...)
{
if (dd->control) {
//...
dd->control->drawContents(painter, r); // draws text
//...
}

if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus)) {
painter->setPen(QPen(Qt::black, 1));
painter->setBrush(Qt::NoBrush);
painter->drawRect(dd->boundingRect); // draws the frame I don't want
}
}

As you see the text is rendered directly from within QTextControl, which is not part of the public API (and this is the second issue I wanted to discuss - why is it not part of the API?). The problem would be solved if we had a protected method available that calls the text control for us. Then I could do:


void MyItem::paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget){
// do something here
renderText(painter); // this would render the text
// or here to extend
}

Unfortunately this is not possible right now (4.2.2). To be honest I don't need and I don't want the text item at all. I'd like to subclass the rect item and add text capabilities to it. It would be simple if we had access to QTextControl. Right now I can only try to emulate it using QTextLayout which seems very complicated to me (I want the cursor to be shown - that makes it complicated). Do you have a better idea how to do it or maybe you have a working snippet of code that uses QTextLayout to achieve what I want?

I think the Trolls made a simmilar mistake here as they did with the proxy model and they corrected it in 4.1 by introducing QAbstractProxyModel. I think they should do the same for QGraphicsTextItem (introducing QAbstractTextItem). What do you think?

camel
22nd February 2007, 13:16
if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus))



Just a quick idea for the rect. What happens when you edit the state, to tell the style that it was not selected at all? (You probably could do that via extension?)

Or would it then stop drawing the cursor too?

wysota
22nd February 2007, 14:03
What happens when you edit the state, to tell the style that it was not selected at all? (You probably could do that via extension?)

Or would it then stop drawing the cursor too?

Yes, this seems to work:

class MyGraphicsTextItem : public QGraphicsTextItem {
public:
MyGraphicsTextItem(QGraphicsItem *parent=0, QGraphicsScene *scene=0)
: QGraphicsTextItem(parent, scene){}
void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0 ){
QStyleOptionGraphicsItem option2 = *option;
option2.state = 0;
QGraphicsTextItem::paint(painter, &option2, widget);
}
};

Still, it's kind of a hack and I'd prefer to do it without the text item at all (otherwise I'd have to subclass the text item and reimplement the shape functionality, which of course is an option too but I'd like to avoid it).

There is another terrible hack that just might work. I might cut out the QTextControl declaration and put it into my project. As the class is present in Qt, it'll work, at least under non-Windows systems (AFAIK Windows need the symbols to be exported and QTextControl is not).

Bitto
22nd February 2007, 18:24
The code where you copy the style option and remove the state will always work; it's not a hack. It's better if you only remove the state flags that affect focus rect drawing, though.



option.state &= ~(QStyle::State_Selected | QStyle::State_HasFocus);


If you rip out QTextControl, you're on your own ;-). I don't know it's really worth it if all you want is to remove a rectangle.

wysota
22nd February 2007, 21:10
The code where you copy the style option and remove the state will always work; it's not a hack.

I meant that it's a hacky way to extend a class. There should be a way to obtain this without having to modify the state. This is one of few places in Qt where you can't modify the behaviour of the object just by directly calling some methods.

To be honest I "love" situations when I want to do something what wasn't meant to do by the class designer and I find out that the respective method is part of the class pimpl :cool:


It's better if you only remove the state flags that affect focus rect drawing, though.


option.state &= ~(QStyle::State_Selected | QStyle::State_HasFocus);

True, but in this case it completely doesn't matter.


If you rip out QTextControl, you're on your own ;-).
Oh yes, I'm aware of that, but why didn't QTextControl make it into the public API? I'm sure people would like to put their hands on it.


I don't know it's really worth it if all you want is to remove a rectangle.
Maybe I wasn't clear. I'd like to have access to QTextControl regardless of the rectangle (and the graphicsview).

Bitto
24th February 2007, 11:27
QProxyModel is dangerous, because it maps indices without mapping their positions. I can't see the parallell to QProxyModel from QGraphicsTextItem, could you explain?

QGraphicsTextItem is just as powerful as QTextEdit, but it's a graphics item. You have complete QTextDocument access. I understand from your first post that you want a way to remove the focus rectangle, and you found a way. What other limitations have you run into? I understand that you want QTextControl to be public, but I don't understand why. ;-)

wysota
24th February 2007, 13:34
QProxyModel is dangerous, because it maps indices without mapping their positions. I can't see the parallell to QProxyModel from QGraphicsTextItem, could you explain?
That you couldn't correct it without breaking binary compatibility and so another class had to be introduced. And that class is an abstract class, that is easy to extend. It's just a confusing way to say I'd like to see QGraphicsAbstractTextItem (or QAbstractGraphicsTextItem or whatever) that would have a protected method rendering the text on a specified rectangle of the item.

The drawback I mentioned is not the only one I think the item has. For instance you can limit the width of the item, but you can't limit its height (of course you can clip the painter, but that's not the same). There is QGraphicsSimpleTextItem, but it only allows you to display plain text in a rectangle, so it's quite limited compared to QGraphicsTextItem.

Let me make myself clear - I don't deny your (or whoever implemented the class) work on the item or the whole framework. It would just be nice to have that separate method to easily extend the item. It doesn't need to be virtual, so maybe it wouldn't break binary compatibility?



QGraphicsTextItem is just as powerful as QTextEdit, but it's a graphics item.
Yes, I don't deny that.

You have complete QTextDocument access.
True, but the document only holds the logical structure of its contents, not the way it is rendered. As you know the text will be laid out only when it needs to be rendered (though Q*TextLayout if I remember correctly). I know you can use the layout to get the same capabilities QTextControl has, but it is against the paradigm of reusing already written code.


I understand from your first post that you want a way to remove the focus rectangle, and you found a way.
I want to be able to have editable text without using a separate item. I iterate over item's children and each time I'd have to filter out the text item from the list of children. It's ok when you have to do it once, but such little quirks add to themselves and one ends up with quite a complex code. It has nothing to do with the graphics view of course, as it gives me a possibility to do what I want by adding that child item and connecting appropriate signals and slots.


What other limitations have you run into?
I'm just beginning to have fun with the framework :)

I find it a bit annoying that the QStyle::State_MouseOver is set for an item if the mouse is over one of its children, regardless of the boundingRect() or shape() of the parent item. I can work it around by testing against the shape (if the parent and child shapes are distinct) or testing colliding items. Right now I'm reimplementing hover enter and hover leave events (as they don't "propagate" to parent items) and set my own "hover" flag there, but it won't work if the child's shape is part of its parent shape as the parent will also receive its hover enter event (provided that it accepts hover events). Of course I could implement a method that corrects the hover flag for the parent (and its parent, etc.) when you enter or leave the child item, but again it makes the code complex. How about having a flag that enables/disables propagation of child events? The behaviour is a bit different than with widgets, as widgets are always inside their parents, so such propagation makes more sense there.


I understand that you want QTextControl to be public, but I don't understand why. ;-)
Because I want to be able to render complex text without a QTextEdit or QGraphicsTextItem and without the need to construct everything from scratch using QTextLayout. The class is already present in the code, so why not make it public? It won't compromise the framework in any way, right?

Thanks for your input, I appreciate that and hope for more. I know I can be very annoying sometimes :)

Bitto
24th February 2007, 15:15
What's wonderful about the Graphics View framework is that everyone wants to do everything with it. That's what I call a true success indicator. It's so easy to learn, and very inspiring to use, and Qt developers can only see more and more possibilities. That's when you know the API is a success. Now, that some parts of the API don't behave exactly like you would want, I can only see that as natural. Actually I see that as a good thing. It's a sign that you're working with the framework, instead of fighting it.

Now, the trick is to find out how to extend the API, without bloating it. We knew in advance that the text item would be a target of endless customization requests, and we are just extending it as we go along. QTextControl might be made public at one point, but as it is today, it probably can't do half of what you'd expect it to do... But then again, we need to see exactly what it is you want to do (as you wrote just now). Hover event behavior: you will probably notice that parents and children propagate hover events, whereas siblings do not. That's by design, and works for most cases, but not all. Maybe we can do something to help you out, if this doesn't fit your application well.

The best approach is to file a suggestion, or report an inconvenience in the API, by posting to http://www.trolltech.com/bugreport-form. With a proper line of argumentation, I'm sure you'll see that APIs will evolve in your direction.

wysota
24th February 2007, 15:30
QTextControl might be made public at one point, but as it is today, it probably can't do half of what you'd expect it to do... But then again, we need to see exactly what it is you want to do (as you wrote just now).
True. Exposing the class would freeze its API seizing its evolution.


Hover event behavior: you will probably notice that parents and children propagate hover events, whereas siblings do not. That's by design, and works for most cases, but not all. Maybe we can do something to help you out, if this doesn't fit your application well.

Oh, I'm sure it's by design and in most cases it makes sense. Although it seems to be a faulty behaviour when a child is outside its parent's shape (or even bounding rect).


The best approach is to file a suggestion, or report an inconvenience in the API, by posting to http://www.trolltech.com/bugreport-form. With a proper line of argumentation, I'm sure you'll see that APIs will evolve in your direction.

Yes, I already filed some suggestions some time ago (regarding Interview), but they had to wait because implementing them directly would cause binary compatibility to be lost (virtual methods were needed). We'll see if they make it into 4.3 :) I'm just beginning with the graphics view framework so I want to find ways of doing things instead of blaiming Qt right away and sending bug reports or suggestions.