Search code examples
c++qtqt5qgraphicsviewqgraphicsscene

How to change Qt's default selection behavior?


I am new to QGraphicsView. So for the practice purpose I was drawing some shapes like Rectangle, Ellipse, Polygon, Polyline, Text etc.
I observed here that, whenever we click on these drawn objects ( select them using mouse click )
dotted line rectangle appears around the object which indicates that, object is selected.
If I want to change that Qt's default behavior of selection, how to do that ?

I do not want dotted rectangle around circle, when it will be clicked by mouse, but that dots should be around its circumference.

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QStyleOptionGraphicsItem>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

public:
    virtual void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};
#endif // WIDGET_H   

widget.cpp

    #include "widget.h"
    #include "ui_widget.h"
    #include<QGraphicsScene>
    #include<QGraphicsView>
    #include<QGraphicsEllipseItem>
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        QGraphicsScene *scene = new QGraphicsScene(this);
    
        QGraphicsView *view = new QGraphicsView(this);
    
        QGraphicsEllipseItem *ellipse = new QGraphicsEllipseItem();
        ellipse = scene->addEllipse(2,2,80,80);
        //ellipse->setFlag(QGraphicsItem::ItemIsSelectable);
        scene->addItem(ellipse);
    
        view->setScene(scene);
        ui->verticalLayout->addWidget(view);
    
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    
    void Widget :: paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
            auto copied_option = *option;
            copied_option.state &= ~QStyle::State_Selected;
            auto selected = option->state & QStyle::State_Selected;
            paint(painter, &copied_option, widget);
            if (selected) {
                QPainterPath path;
                path.addEllipse(2,2,80,80);
                painter->save();
                painter->setBrush(Qt::NoBrush);
                painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
                painter->drawPath(path);
                painter->restore();
      }
}

Solution

  • As far as I'm aware there isn't any 'built in' facility to fo what you want. In the current code base you'll see...

    void QGraphicsEllipseItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                                     QWidget *widget)
    {
        Q_D(QGraphicsEllipseItem);
        Q_UNUSED(widget);
        painter->setPen(d->pen);
        painter->setBrush(d->brush);
        if ((d->spanAngle != 0) && (qAbs(d->spanAngle) % (360 * 16) == 0))
            painter->drawEllipse(d->rect);
        else
            painter->drawPie(d->rect, d->startAngle, d->spanAngle);
        if (option->state & QStyle::State_Selected)
            qt_graphicsItem_highlightSelected(this, painter, option);
    }
    

    So the selection status indicator is drawn by qt_graphicsItem_highlightSelected which is called directly from the paint method. qt_graphicsItem_highlightSelected itself simply sets up a few basic parameters and ends by drawing the dashed rectangular border.

    The obvious way to replace the default behaviour would be to subclass the QGraphicsItem class of interest and override the paint member (untested)...

    #include <QApplication>
    #include <QGraphicsEllipseItem>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QPainter>
    #include <QStyleOptionGraphicsItem>
    
    class ellipse: public QGraphicsEllipseItem {
    public:
        explicit ellipse (qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = nullptr)
            : QGraphicsEllipseItem(x, y, width, height, parent)
            {}
        virtual void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
        {
            auto copied_option = *option;
            copied_option.state &= ~QStyle::State_Selected;
            auto selected = option->state & QStyle::State_Selected;
            QGraphicsEllipseItem::paint(painter, &copied_option, widget);
            if (selected) {
                painter->save();
                painter->setBrush(Qt::NoBrush);
                painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
                painter->drawPath(shape());
                painter->restore();
            }
        }
    };
    
    int main (int argc, char **argv)
    {
      QApplication app(argc, argv);
      QGraphicsView view;
      QGraphicsScene scene;
      view.setScene(&scene);
      ellipse e(2, 2, 80, 80);
      e.setFlag(QGraphicsItem::ItemIsSelectable);
      scene.addItem(&e);
      view.show();
      return app.exec();
    }