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.
Related
I've progress bar to display the instantaneous value of real-time sensor.
the range for the QProgressBar is set as
ui->progressBar->setRange(0, 0.5*100); // to make range integer
ui->progressBar->setValue(sensor_read*100);
It is working ok. But I need to set a "threshold" value using a doubleSpinBox , as shown below:
The position of the dotted line (which is a QLabel) is the threshold that can be set using a doubleSpinBox.
My requirement is to change the height of the dotted line with respect to the threshold value.
the top and bottom y coordinates of the QProgressBar is 250 and 450
How do I get proportional y coordinate for the QLabel (dotted line) when I set a threshold value using doubleSpinBox?
Using a QLabel is possible, but it might be hard to get the exact position of the line inside the QLabel itself to adjust correctly. If your QLabel is only 1px high, this should work fine, otherwise you would need to adjust a bit more in detail.
So if the QLabel has the QProgressBar set as parent, then you can set the y coordinate of the QLabel inside the QProgressBar accordingly with something like below.
Call this function every time the threshold is modified, or when the widget (i.e. the progress bar) is resized. This is done by connecting on the signal:
connect(spinBox, &QDoubleSpinBox::valueChanged, this, &MyClass::updateThreshold);
void MyClass::updateThreshold()
{
const double threshold = spinBox->value();
int labelY = qRound(threshold / progressBar->maximum() * progressBar->height());
label->move(0, progressBar->height() - labelY); // y is inverted
}
void MyClass::resizeEvent(QResizeEvent *event)
{
updateThreshold();
}
Another way is to create a new class that inherits QProgressBar and to override the paintEvent() function. Inside the paintEvent, you could draw a horizontal line and the text using the QPainter at the correct position, using the same calculation than above. There you would not need a QLabel.
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.
I would like to have certain things drawn on QGraphicsScene, but not be QGraphicsItem (it would interfere with the processing of the QGraphicsItem collection).
Example: a scene bounding rectangle, a grid
I am overriding the drawBackground(QPainter *painter, const QRectF &rect) for that purpose. (I should subclass the scene... )
void MyView::showHideBounds()
{
m_showBackgroundBounds = !m_showBackgroundBounds;
// can't triger an update ???
update(); // neither does anything
viewport()->update();
}
void MyView::drawBackground(QPainter *painter, const QRectF &rect)
{
QPen pen;
if(m_showBackgroundBounds)
pen = QPen(QColor(0, 0, 0), 10, Qt::PenStyle(Qt::SolidLine));
else
pen = QPen(QColor(255, 255, 255), 10, Qt::PenStyle(Qt::SolidLine));
painter->setPen(pen);
painter->drawRect(QRect(QPoint(-scene()->sceneRect().size().toSize().width()/2,
-scene()->sceneRect().size().toSize().height()/2),
scene()->sceneRect().size().toSize()));
}
I would like the option to show/hide either the bounding rectangle or the grid.
The only thing I can think of is paint over them with the color of the background brush ? Is there any other option ?
As I have written it above, it works - except I need user action on items (or a zoom or some other scene changing action) to trigger refresh, or call an update... (the function showHideBounds doesn't - not sure how to make it force a refresh)
I would call the drawBackground from the showHideBounds function - but I don't know how to get the painter
[Also, the drawBackground seems to be drawn automatically... how can I give it the rect argument it needs ? (it seems if I draw the rect it does draw the scene rectangle but I only see the right and bottom edges)]
In order to redraw a particular section of scene, you can call
QGraphicsScene->invalidate(rect_to_redraw, Backgroundlayer)
Note that if drawBackground(*painter, rect) paints over area outside rect, it will not update automatically. In that case invalidate has to be called with appropriate rect parameters.
I have some kind of histogram drawn with QGraphicsRectItem; some of these rectangles are long, some short. While it is no problem to select a long rectangle, one might have difficulties with the short ones.
So I was wondering if there is a way to specify custom area that would trigger mousePressEvent for the item, so rectangles would have the same size selection area.
Apart from rectangles I also draw some text on the same line. Would it be helpful to group them somehow and write mousePressEvent for the group instead?
Thank you!
It would be useful to see an example image of what you're asking, but I think I know what mean.
Personally, I'd just create my own class, inherited from QGraphicsItem (or QGraphicsObject, if you want signals and slots). This class can then provide a boundingRect() of the full area that you want to represent the area to be selected, but the paint() function only draw the visible part of the bar. Something like this: -
class Bar: public QGraphicsItem
{
Q_OBJECT
public:
Bar(int x, int y, int width, int height, int visibleBarHeight);
// returns the area of the object
QRectF boundingRect() const;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
};
In the paint function, you would draw a rect up to the visible bar height, but in boundingRect, return the full rect. That way, the bar could visibly be very small, but the object is of full height and would respond to mouse selection above the visible area of the bar.
As for the text, you could either add it as a child to this object and signal the parent when it gets selected, or extend the boundingRect of this Bar class and render it in the paint function.
Note that boundingRect is the area represented by the object, in local coordinates. If you have an object that isn't defined by a rectangle, you'd also want to implement the shape() function. By default, shape() calls boundingRect().
I want to draw 1 digit on the screen by the graphic framework classes. I want the fill approach of '1' to be something like
(source: qt-project.org)
but the brush of my drawn '1' is just like a yellow SolidBrush by the below code (an ugly bold yellow '1'). Can you help me what's wrong with it?
QGraphicsSimpleTextItem digit_1 = new QGraphicsSimpleTextItem;
digit_1->setText(QString::number(1));
digit_1->setPen(QPen(QColor("black")));
QLinearGradient gradient(digit_1->boundingRect().topLeft(),
digit_1->boundingRect().bottomRight());
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(1, Qt::yellow); // yellow is for example
QBrush brush(gradient);
brush.setStyle(Qt::BrushStyle::LinearGradientPattern);
digit_1->setBrush(brush);
digit_1->setFont(QFont("courier", 35, QFont::Black));
Thanks in advanced.
Your issue most likely comes from the fact that you're basing your gradient's "area" on the bounding rect of your item before you set the font size to something much larger than the default.
The bounding rect you're getting is thus much smaller than your actual bounding rect. Since the default spread method is padding, you're seeing most likely just one color (or not enough of the gradient for it to be actually visible).
So move your setFont call to the top, before you create the gradient. You can drop the setStyle on your brush, that's determined automatically from the gradient. (In fact, you can drop that brush entirely and use the gradient in setBrush.)
With the way you set up the gradient, you'll get a "diagonal" gradient. If you want it from top to bottom, use the top left and bottom left points instead.
Demo
#include <QtGui>
class W: public QGraphicsView
{
Q_OBJECT
public:
W(QWidget *parent = 0)
: QGraphicsView(parent)
{
QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem;
item->setText("Stack Overflow");
item->setPen(QPen(Qt::red));
item->setFont(QFont("courier", 60, QFont::Bold));
QLinearGradient lgrad(item->boundingRect().topLeft(),
item->boundingRect().bottomLeft());
lgrad.setColorAt(0.0, Qt::red);
lgrad.setColorAt(1.0, Qt::yellow);
item->setBrush(lgrad);
QGraphicsScene *scene = new QGraphicsScene;
scene->setBackgroundBrush(QBrush(Qt::black));
scene->addItem(item);
setScene(scene);
}
};