How to paint object by overriding paintEvent in Qt? - c++

In my QGraphicsView, I have many QGraphicsItem. I want to search specific QGraphicsItem out of all the items present in view and highlight the matched item. For highlighting item, I am trying to use paintEvent.
So I am calling paintEvent, but not understanding how to heighlight border of the matched object ?
Should I need co-ordinates of that matched object ?
I tried like this:
foreach(QGraphicsItem* currentItem, _scene->items())
{
pEvent = false;
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(currentItem);
if(rItem)
{
// some logic to get i->Name()
QString name1 = i->Name();
QString name2 = "reN"; // I want to find reN named item in view
if(name1 == name2)
{
pEvent = true;
qDebug()<<"Object Found ";
this->repaint();
break;
}
}
}
void myClass::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
qDebug()<<"In paint event ";
if(pEvent)
{
QPainter qp(this);
drawBody(&qp);
}
}
void myClass::drawBody(QPainter *qp) {
Q_UNUSED(qp);
// want logic for heighlighting border of the item
}

I understand from your problem that you are overridng QGraphicsView in MyClass since paintEnent is defined QWidgets classes.
To solve your problem, if I understand correctly. It's necessary to save the QGraphicsRectItem coordinates:
QRectF rectF = item.boundingRect();
then use the following in the function drawBody:
qp.save();
const float width = 5;
QPen pen;
pen.setWidth(width );
pen.setColor("green");
painter->drawRect(rectF.x(), rectF().y(),
rectF().width(), rectF().height());
qp.restore();

Related

How to display some text besides mouse pointer when the mouse hover over QGraphicsItem's in Qt?

I have a QGraphicsView, which contains many rectangle and polylines. I wanted to print every object names ( every rectangle, polylines have names) once I clicked them on view through mouse click. I did that and worked perfectly.
But now I want to do that over mouse hovering. It means, if I hover a mouse over particular object, it should show its name besides cursor.
I tried that, but
I am not understanding, on hovering, how that particular object should get selected ?
And how to print its name besides cursor point ?
I tried this way:
bool myClass::eventFilter(QObject *watched, QEvent *event)
{
bool filterEvent = false;
switch(event->type())
{
case QEvent::MouseButtonPress:
{....}
case QEvent::MouseButtonRelease:
{...}
case QEvent::Enter:
{
QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event);
QPointF po = _view->mapToScene(mouseEvent->pos());
FindNamesOverHover(po);
}
return true;
default:
break;
}
return filterEvent;
}
void myClass::FindNamesOverHover(QPointF p)
{
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(_scene->itemAt(p,QTransform()));
if(rItem)
{
// some logic
qDebug()<< "Instance name is " << i->Name();
}
}
Constructor of class myClass
myClass::myClass(QWidget* parent) :
QDockWidget(parent)
{
scene = new QGraphicsScene(this);
view = new QGraphicsView(this);
view->setScene(_scene);
view->viewport()->installEventFilter(this);
}
Now above code works only,
When I select object through mouse click and then again hover over it.
Then it prints its name on console.
But I want only hovering ( no selection through mouse click) will show its name besides cursor point.
Can any one help me ?
You can override QGraphicsScene.mouseMoveEvent and inside it use QGraphicsScene.itemAt to query for item under cursor. To display you can use QGraphicsTextItem, just move it to correct position and apply text.

How to constrain a QRubberBand within a QLabel?

I am building a Qt5 application that allows a user to draw a rubberband with his mouse over an image, to select certain area of the image for further processing.
I got my code to only allow the user to start drawing the rubberband, by subclassing a QLabel to a custom class (frame_displayer) which mousePressEvent() is overridden and thus be only invoked when the mouse press happens within the custom classed widget.
The problem is that when the initial click is inside the frame_displayer, mouseMoveEvent(), the function that I use to change the rubberband size accordingly, keeps getting called even when the mouse cursor has been dragged outside of the frame_displayer.
I have tried using leaveEvent() and enterEvent() to control a class boolean flag that codes inside mouseMoveEvent could rely on to know whether the cursor is still within the widget. However, both leaveEvent() and enterEvent() are only called while a mouse button is not being held, thus rendering them no use for constraining the rubberband.
Also, the underMouse() always return true, for a reason unknown to me.
Segment of frame_displayer.cpp
frame_displayer::frame_displayer(QWidget * parent) : QLabel(parent)
{
_rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
}
void frame_displayer::mousePressEvent(QMouseEvent *event)
{
_lastClickedBtn = event->button();
if (_lastClickedBtn == Qt::LeftButton)
{
_mouseOriginClickPoint = event->pos();
_rubberBand->setGeometry(QRect(_mouseOriginClickPoint, _mouseClickPoint));
_rubberBand->show();
}
}
void frame_displayer::mouseMoveEvent(QMouseEvent *event)
{
if(_rubberBand != nullptr)
{
if (this->underMouse())
{
if (_lastClickedBtn == Qt::LeftButton)
{
QPoint mouseCurrentPoint = event->pos();
_rubberBand->setGeometry(QRect(_mouseOriginClickPoint, mouseCurrentPoint).normalized());
}
}
}
}
void frame_displayer::mouseReleaseEvent(QMouseEvent *event)
{
_mouseOriginClickPoint = QPoint();
_lastClickedBtn = Qt::MidButton;
if(_rubberBand != nullptr)
{
_rubberBand->hide();
_rubberBand->clearMask();
}
}
void frame_displayer::leaveEvent(QEvent *event)
{
qDebug() << "Leaving";
}
void frame_displayer::enterEvent(QEvent *event)
{
qDebug() << "Entering";
}
Thanks in advance!
I think that's the expected behaviour. If you want to limit the extents of the rubber band then simply clamp them in the mouseMoveEvent override...
void frame_displayer::mouseMoveEvent(QMouseEvent *event)
{
if(_rubberBand != nullptr)
{
if (this->underMouse())
{
if (_lastClickedBtn == Qt::LeftButton)
{
QPoint mouseCurrentPoint = event->pos();
/*
* Clamp mouseCurrentPoint to the QRect of this widget.
*/
auto clamp_rect = rect();
mouseCurrentPoint.rx() = std::min(clamp_rect.right(), std::max(clamp_rect.left(), mouseCurrentPoint.x()));
mouseCurrentPoint.ry() = std::min(clamp_rect.bottom(), std::max(clamp_rect.top(), mouseCurrentPoint.y()));
_rubberBand->setGeometry(QRect(_mouseOriginClickPoint, mouseCurrentPoint).normalized());
}
}
}
}

How can I change the QGraphicView selected Item color

How can I change QGraphicView selected item color.In this code which can select items form QGraphicView and delete and also able to disable item moving.How can I change the color of selected Item.
void Widget::on_btnDelete_clicked()
{
foreach (QGraphicsItem *item,ui->graphicsView->scene()->selectedItems()) {
delete item;
}
}
void Widget::on_btnMoveDis_clicked()
{
foreach (QGraphicsItem *item,ui->graphicsView->scene()->selectedItems()) {
item->setFlag(QGraphicsItem::ItemIsMovable,false);
}
}
At first I suggest you to read QGraphicsView examples and docs carefully , I recommend Diagram Scene Example and Elastic Nodes Example as starting point. Because QGraphicsView frame work is smart if you use it smart otherwise you may pay penalties in performance.
And now your question :
whole answer is related to qgraphicsitem_cast, how to use it?
First let me provide some initializations look code below at first I add three rectangles to scene and set their flags.
void MainWindow::on_btnInit_clicked()
{
QColor color = Qt::red;
QBrush brush = Qt::SolidPattern;
brush.setColor(color);
QRect * rectObject = new QRect(QPoint(0,0) , QSize(20,20));
scene.addRect(*rectObject , color, brush);
rectObject = new QRect(QPoint(0,30) , QSize(20,20));
color = Qt::green;
brush.setColor(color);
scene.addRect(*rectObject , color, brush);
rectObject = new QRect(QPoint(0,60) , QSize(20,20));
color = Qt::blue;
brush.setColor(color);
scene.addRect(*rectObject , color, brush);
foreach (QGraphicsItem *item,ui->graphicsView->scene()->items()) {
item->setFlag(QGraphicsItem::ItemIsMovable , true);
item->setFlag(QGraphicsItem::ItemIsSelectable , true);
}
}
I have set ItemIsMovable and ItemIsSelectable flags so for items could be selectable.
and now change color of selected item.
void MainWindow::on_btnSelectItem_clicked()
{
foreach (QGraphicsItem *item, ui->graphicsView->scene()->selectedItems())
{
QGraphicsRectItem *rect = qgraphicsitem_cast<QGraphicsRectItem *>(item);
if (!rect)
continue;
QBrush br(Qt::SolidPattern);
br.setColor(Qt::black);
rect->setBrush(br);
rect->update();
}
}
I use QGraphicsRectItem to cast rectangular objects that i added to scene before. If you want to learn more on use qgraphicsitem read Elastic Nodes Example.

Change color of placeholder text in QLineEdit

When I set the placeholder text with QLineEdit::setPlaceholderText(), it appears gray.
Is there any way to change the color to something else, for example red?
You'll have to subclass QLineEdit and paint your own placeholder in the paintEvent().
class CustomColorPlaceholderLineEdit : public QLineEdit
{
public:
CustomColorPlaceholderLineEdit(QWidget * parent = 0) : QLineEdit(parent) { color = QColor(0,0,0,128); }
void setCustomPlaceholderText(const QString &text) { this->mText = text; }
const QString &customPlaceholderText() const { return mText; }
void setCustomPlaceholderColor(const QColor &color) { this->color = color; }
const QColor &customPlaceholderColor() const { return color; }
void paintEvent(QPaintEvent *event) {
QLineEdit::paintEvent(event);
if (!hasFocus() && text().isEmpty() && !mText.isEmpty()) {
// QLineEdit's own placeholder clashes with ours.
Q_ASSERT(placeholderText().isEmpty());
QPainter p(this);
p.setPen(color);
QFontMetrics fm = fontMetrics();
int minLB = qMax(0, -fm.minLeftBearing());
QRect lineRect = this->rect();
QRect ph = lineRect.adjusted(minLB + 3, 0, 0, 0);
QString elidedText = fm.elidedText(mText, Qt::ElideRight, ph.width());
p.drawText(ph, Qt::AlignVCenter, elidedText);
}
}
private:
QString mText;
QColor color;
};
There is another a bit hacky but simple and reliable way.
connect(lineEdit, &QLineEdit::textChanged, this, &YourClass::updateLineEditStyleSheet);
void YourLineEdit::updateLineEditStyleSheet()
{
if (lineEdit->text().isEmpty()) {
lineEdit->setStyleSheet("#lineEdit { color: lightGray;"); // Set your color but remember that Qt will reduce alpha
} else {
lineEdit->setStyleSheet("#lineEdit { color: black;"); // usual color
}
}
also you can use this way to derived from QLineEdit class
If you want to use QSS instead of QPalette, try the following:
setStyleSheet("QLineEdit{"
" color: red;" //TEXT COLOR
"}"
"QLineEdit[text=\"\"]{"
" color: gray;" //TEXTHOLDER COLOR
"}");
connect(ui->lineEdit, &QLineEdit::textChanged, [=]{ style()->polish(ui->lineEdit); });
You can change the color, but bare in mind there is an alpha factor set in the placeholder from the source code (as mentioned in another comment) that cannot be removed. Therefore you will always see the placeholder darker (no white possible with this option).
You can't, at least with the current QLineEdit code.
As you can see from the source code, the placeholder text is simply taking the foreground brush of the palette and making it partially transparent, see QLineEdit::paintEvent:
if (d->shouldShowPlaceholderText()) {
if (!d->placeholderText.isEmpty()) {
QColor col = pal.text().color();
col.setAlpha(128);
QPen oldpen = p.pen();
p.setPen(col);
QRect ph = lineRect.adjusted(minLB, 0, 0, 0);
QString elidedText = fm.elidedText(d->placeholderText, Qt::ElideRight, ph.width());
p.drawText(ph, va, elidedText);
p.setPen(oldpen);
}
}
You can work with upstream into a more general solution, though. In particular I one would expect that color to be added to the palette, or in general provided by the current QStyle (for instance as a style hint).
If you want to change placeholder text color for a QLineEdit you have to customize the component's QPalette object.
QPalette p = lineEdit->palette();
p.setColor(QPalette::Mid, Qt::red); // assuming Mid is the color you want to change.
lineEdit->setPalette(p);
I don't recall exactly which QPalette::ColorRole is appropriate for changing QLineEdit's placeholder text color though.
#Meefte solution is quite good given the situation that Qt gives placeholder the same color as for the text, except it adds 50% opacity. So, there is little choice to set placeholder color to be different than the text. However, even this solution could be improved by making sure that you would not need to set some other variable than the default one Qt provides you.
The need to use default placeholderText() might arise from the situation when you have lots of QLineEdit controls which are already promoted to some control overriding QLineEdit behavior, and placeholderText() is already set through code or through Qt Creator, i.e. it would be a bit painful to introduce another dynamic property. However, if you did not promote to some child control, then it would be a necessity to do so in order to use such solution.
class CustomColorPlaceholderLineEdit : public QLineEdit
{
public:
CustomColorPlaceholderLineEdit(QWidget * parent = 0) : QLineEdit(parent) { color = QColor(0,0,0,128); }
const QString &customPlaceholderText() const { return mText; }
void setCustomPlaceholderColor(const QColor &color) { this->color = color; }
const QColor &customPlaceholderColor() const { return color; }
void paintEvent(QPaintEvent *event)
{
if(color.isValid() && text().isEmpty() && (!placeholderText().isEmpty() || !mText.isEmpty()))
{
if(!placeholderText().isEmpty())
{
// In this way, placeholderText() is taken into local variable 'mText' care. Whenever placeholderText() will change, there it will be taken care of.
mText = placeholderText();
// This will ensure Qt will not draw placeholder for us.
setPlaceholderText("");
}
// By this, we make sure Qt will paint QLineEdit default parts properly.
QLineEdit::paintEvent(e);
// And now #Meefte code is reused here.
QPainter p(this);
p.setPen(color);
QFontMetrics fm = fontMetrics();
int minLB = qMax(0, -fm.minLeftBearing());
QRect lineRect = this->rect();
QRect ph = lineRect.adjusted(minLB + 3, 0, 0, 0);
QString elidedText = fm.elidedText(mText, Qt::ElideRight, ph.width());
p.drawText(ph, Qt::AlignVCenter, elidedText);
return; // No need to paint again.
}
// Default Qt's painting behavior for QLineEdit.
QLineEdit::paintEvent(e);
}
private:
QString mText;
QColor color;
};
QT still has this problem)
I solved it like this:
bool CustomLineEdit::event(QEvent *event)
{
bool eventResult = QLineEdit::event(event);
if (event->type() == QEvent::StyleChange) {
QPalette pal = palette();
pal.setColor(QPalette::PlaceholderText, Qt::red);
setPalette(pal);
}
return eventResult;
}

Qt moving an Object in a specific Region

I'm new to Qt programming. So far I made a QGraphicsScene draw some rectangles (looks like a Chess Board) and then added an own QGraphicsItem to the scene. For this item I set the ItemIsMovable flag. Now I can move it but I would like to restrict the movement to the area where the Chess Board is.
Would I have to unset the flag and realize the movement manually or is there like an option or flag where I can specify the area it can be moved in ?
renderableObject::renderableObject(QObject */*parent*/)
{
pressed = false;
setFlag(ItemIsMovable);
}
You can reimplement the QGraphicsItem's itemChange() if you want to restrict movable area. Repositioning the item will cause itemChange to get called. You should note that ItemSendsGeometryChanges flag is needed to capture the change in position of QGraphicsItem.
class MyItem : public QGraphicsRectItem
{
public:
MyItem(const QRectF & rect, QGraphicsItem * parent = 0);
protected:
virtual QVariant itemChange ( GraphicsItemChange change, const QVariant & value );
};
MyItem::MyItem(const QRectF & rect, QGraphicsItem * parent )
:QGraphicsRectItem(rect,parent)
{
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges);
}
QVariant MyItem::itemChange ( GraphicsItemChange change, const QVariant & value )
{
if (change == ItemPositionChange && scene()) {
// value is the new position.
QPointF newPos = value.toPointF();
QRectF rect = scene()->sceneRect();
if (!rect.contains(newPos)) {
// Keep the item inside the scene rect.
newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
return newPos;
}
}
return QGraphicsItem::itemChange(change, value);
}
This will keep item moving limited to scene rect. You can define any arbitrary rect if you like.