how to get the end position of drawItemText in Qt - qt

I am trying to customize a QPUshButton by subclassing it and overriding the paintEvent. I am writing the text, followed by the icon as below:
paintEvent(QPaintEvent *paint)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
//Draw the base
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
//Draw the text
style()->drawItemText(&p,this->rect(),Qt::AlignCenter,(this->palette()), true, this->text());
//How do I make the image immediately follow the text
if(!this->icon().isNull())
//Draw the icon at 75% button height
style()->drawItemPixmap(&p, this->rect(),Qt::AlignRight|Qt::AlignVCenter, this->icon().pixmap(this->rect().height() * 0.75));
}
I am center aligning the text, and right aligning the icon. However, this causes a gap between the text and the icon. Is there any way for me to draw the icon immediately after the text, instead of aligning?
In other words, is there any way to get the position where the drawItemText finished?

QStyle::itemTextRect() will tell you where the text will be laid down with given rectangle, font metrics and alignment.

QFontMetrics will tell you how wide your text is. It doesn't know about your rectangle, so you will have to do the alignment calculation yourself.

Related

Center text vertically when drawing with QPainter's drawText()

My strategy when centering text on images is to get bounding rectangle for that text and divide width or height by two. I did the same in this case. This is example I have created:
void CanvasWidget::paintEvent(QPaintEvent*)
{
//Create image:
QImage image(rect().width(), rect().height(), QImage::Format_RGB32);
QPainter paint(&image);
// White background
image.fill(QColor("#FFF"));
// set some metrics, position and the text to draw
QFontMetrics metrics = paint.fontMetrics();
int yposition = 100;
QString text = "Hello world.";
// Draw gray line to easily see if centering worked
paint.setPen(QPen(QColor("#666"), 1, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin));
paint.drawLine(0, yposition, image.width(), yposition);
// Get rectangle
QRect fontRect = metrics.boundingRect(text);
// Black text
paint.setPen(QPen(QColor("#000"), 1, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin));
// Add half the height to position (note that Qt has [0,0] coordinates at the bottom of the image
paint.drawText(4, yposition+round(((double)fontRect.height())/2.0), text);
QPainter p(this);
p.drawImage(rect(), image, image.rect());
p.end();
}
This is the result - the text is under line instead centered on the line:
Android:
Windows:
I used lines to draw frame around the text based on metrics rectangle:
Intended result was to center visible text exactly around the given point/line:
To put you in perspective, this is the actual problem I am having:
The numbers should be in the middle of the lines, not so much below.
The function I am using returns size including accents and other big characters that aren't there. How do I get the rectangle in pixels only for characters that are there?
Not quite sure what you're asking, but if it's why does the bounding rect appear wrong, it's because you're not taking into account characters that have accents in the font such as é, å etc. The bounding rect returned from font metrics includes these.
As it states in the boundingRect documentation
The height of the bounding rectangle is at least as large as the value returned by height().
This is not the case for tightBoundingRect that will, I expect, provide the right result.

How can I wrap text in QGraphicsItem?

1) How can I wrap text in a QGraphicsTextItem to fit a fixed rectangle, with width and height ?
Right now I am experimenting with creating a text, getting its bounding rectangle, and resizing it to fit the box - but I can't get wrapping.
class TTT: public QGraphicsTextItem {
TTT() {
{
setPlainText("abcd");
qreal x = m_itemSize.width()/boundingRect().width();
qreal y = m_itemSize.height()/boundingRect().height();
scale(x, y);
}
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
// experiment with clip regions
// text gets covered by hole in clip
QRegion r0(boundingRect().toRect());
QRegion r1(QRect(5, 5, 10, 10), QRegion::Ellipse);
QRegion r2 = r0.subtracted(r1);
painter->setClipRegion(r2);
painter->setBrush(Qt::yellow);
painter->drawRect(boundingRect());
QGraphicsTextItem::paint(painter, option, widget);
}
}
What makes wrapping happen, how can I trigger it ?
Right now, as I keep typing, the box is automatically expanding.
2) Is it possible to wrap the text in a QGraphicsItem / QGraphicTextItem subclass in a shape that is not a rectangle ?
(Something like in the image above)
I tried to use clipRegion, see code above, but I guess it is not the right way to go, clipping cuts the text but did not wrap.
Maybe it would... If I could figure out how to wrap text in the first place ?
Qt 4.8
You did not specify Qt version but try:
void QGraphicsTextItem::setTextWidth(qreal width)
Sets the preferred width for the item's text. If the actual text is wider than >the specified width then it will be broken into multiple lines.
If width is set to -1 then the text will not be broken into multiple lines >unless it is enforced through an explicit line break or a new paragraph.
The default value is -1.
In answer to 1) I'd opt not to use the QGraphicsTextItem, but draw the text directly in your QGraphicsItem's paint function using the drawText overloaded function, which takes a QTextOption parameter.
Using this, you can set the WrapMode, for example, with a call to
QTextOption::setWrapMode(QTextOption:: WordWrap)
As for 2) with a non-rectangular shape, I don't think Qt will do this for you.
Doing it yourself you can use QFontMetrics, to work out just how much text would fit in each line, depending upon where it lies within its bounding item.
Alternatively, you could adapt the concept of a text-to-path method.

Preventing font scale in QGraphicsItem

I am using QGraphicsTextItem to paint the text on the scene. Text is painted along the path (QGraphicsPathItem), wich is parent of my QGraphicsTextItem - so the text rotation is changed to be along the path element and is sticked to it while zooming the view. But the font size of QGraphicsTextItem is also changing while zooming the view - this is what I am trying to avoid. Of I set QGraphicsItem::ItemIgnoresTransformations flag to the QGraphicsTextItem it stops rotating while it's parent (QGraphicsPathItem) does.
I do understand that I have to re-implement QGraphicsTextItem::paint function, but I am stuck with the coordination system. Here is the code (Label class inherits public QGraphicsTextItem):
void Label::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
{
// Store current position and rotation
QPointF position = pos();
qreal angle = rotation();
// Store current transformation matrix
QTransform transform = painter->worldTransform();
// Reset painter transformation
painter->setTransform( QTransform() );
// Rotate painter to the stored angle
painter->rotate( angle );
// Draw the text
painter->drawText( mapToScene( position ), toPlainText() );
// Restore transformation matrix
painter->setTransform( transform );
}
The position (and rotation) of my text on the screen is unpredictable :(
What am I doing wrong? Thank you very much in advance.
I solved a problem this way - for drawing a line/circle/rectangle/path, which I want to be transformed, I use an appropriate QGraphicsLine/Ellipse/Rect/PathItem. For drawing the text (which I do NOT want to be transformed) I use QGraphicsSimpleTextItem. I set text's flag to ignore transormations and set it's parent to Line/Ellipse/Rect/Path item. The Line/Ellipse/Rect/Path item transforms, but text does not - this is what I wanted. I can also rotate text and set it's position.
Thank you very much for answers.
The following solution worked perfectly for me:
void MyDerivedQGraphicsItem::paint(QPainter *painter, const StyleOptionGraphicsItem *option, QWidget *widget)
{
double scaleValue = scale()/painter->transform().m11();
painter->save();
painter->scale(scaleValue, scaleValue);
painter->drawText(...);
painter->restore();
...
}
We can also multiply the scaleValue by other mesures we want to keep its size constant outside the save/restore environment.
QPointF ref(500, 500);
QPointF vector = scaleValue * QPointF(100, 100);
painter->drawLine(ref+vector, ref-vector);
I had this issue once. Instead of ignoring transformations, you need to scale down the items you don't want to be zoomed in in your zoom-in function.
When you zoom in, if you change the scale by ds for example, scale the items by 1.0 / ds
You might need to change their positions though.
I hope this helps.
Edit: I hope I understood the question right.

increase selection outline width in QGraphicsTextitem

i have a QGraphicsTextitem with text interaction where user can edit the current text and add the new text . but my new requirement is to increase the selection outline width and can be controlled by a QSlider. is it possible to increase the dotted selection width of the QGraphicsTextItem.
i wanted to increase the pen thickness or size of the selection box coming around the text ..
in the image a dotted lines bound the text . is it possible to increase the dotted line pen size or thickness.
This question is ancient, but I'll do my civic duty to try an answer:
There are a few things you must do.
Subclass your QGraphicsTextItem
Override the paint method, and inside it remove the default selection style, and draw your own
void TextItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
QStyleOptionGraphicsItem opt(*option);
// Remove the selection style state, to prevent the dotted line from being drawn.
opt.state = QStyle::State_None;
// Draw your fill on the item rectangle (or as big as you require) before drawing the text
// This is where you can use your calculated values (as member variables) from what you did with the slider
painter->setPen(Qt::NoPen);
painter->setBrush(Qt::green);
painter->drawRect(whateverRectangle());
// Call the parent to do the actual text drawing
QGraphicsTextItem::paint(painter, &opt, widget);
// You can use these to decide when you draw
bool textEditingMode = (textInteractionFlags() & Qt::TextEditorInteraction);
bool isSelected = (option->state & QStyle::State_Selected);
// Draw your rectangle - can be different in selected mode or editing mode if you wish
if (option->state & (QStyle::State_Selected))
{
// You can change pen thickness for the selection outline if you like
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(Qt::magenta);
painter->drawRect(whateverRectangle());
}
}
You may have to override the boundingRect, opaqueArea and shape functions to account for your increased size.

QPushButton when subclassed draws box around icon

I am trying to customize a QPUshButton by subclassing it and overriding the paintEvent. I am writing the text, followed by the icon as below:
paintEvent(QPaintEvent *paint)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
//Draw the base
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
//Draw the text
style()->drawItemText(&p,this->rect(),Qt::AlignCenter,(this->palette()), true, this->text());
if(!this->icon().isNull())
//Draw the icon at 75% button height
style()->drawItemPixmap(&p, this->rect(),Qt::AlignRight|Qt::AlignVCenter, this->icon().pixmap(this->rect().height() * 0.75));
}
However, the button I get is as below:
If you can see, there is a box around the icon image. How do I remove that? (The icon size appears bigger because I took the snapshot before I reduced the size. However, the box appears even if the image is much smaller and fits into the image).

Resources