Draw an item in a static location relative to the QGraphicsView

前端 未结 1 733
闹比i
闹比i 2020-12-14 12:02

I would like to draw a notification that always appears in the upper right corner of my QGraphicsView. However, QGraphicsItems positions are specif

相关标签:
1条回答
  • 2020-12-14 12:36

    The naive solution of having the notification be a part of the original scene is bad - it breaks the model-view separation. You can have multiple views, all showing one scene, but generally on only one of them can the notification appear as desired.

    Another simple way to do it would be to overlay a QWidget notification on top of your view. The problem is that on some architectures, overlaying regular QWidgets on top of accelerated QGLWidgets will make the former disappear. Do note that a QGraphicsView's viewport may be a QGLWidget!

    Thus, the only portable solution is to explicitly do the painting on top of everything else in QGraphicsSceneView's viewport().

    Below is a complete example.

    enter image description here

    // main.cpp
    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsItem>
    
    qreal rnd() { return qrand() / (float)RAND_MAX; }
    
    class OverlaidGraphicsView : public QGraphicsView
    {
        Q_OBJECT
        QGraphicsScene * m_overlayScene;
    public:
        explicit OverlaidGraphicsView(QWidget* parent = 0) :
            QGraphicsView(parent), m_overlayScene(NULL) {}
        explicit OverlaidGraphicsView(QGraphicsScene * scene = 0, QWidget * parent = 0) :
            QGraphicsView(scene, parent), m_overlayScene(NULL) {}
        void setOverlayScene(QGraphicsScene * scene) {
            if (scene == m_overlayScene) return;
            m_overlayScene = scene;
            connect(scene, SIGNAL(changed(QList<QRectF>)), SLOT(overlayChanged()));
            update();
        }
        QGraphicsScene * overlayScene() const { return m_overlayScene; }
        void paintEvent(QPaintEvent *ev) {
            QGraphicsView::paintEvent(ev);
            if (m_overlayScene) paintOverlay();
        }
        virtual void paintOverlay() {
            QPainter p(viewport());
            p.setRenderHints(renderHints());
            m_overlayScene->render(&p, viewport()->rect());
        }
        Q_SLOT void overlayChanged() { update(); }
    };
    
    class Window : public QWidget
    {
        QGraphicsScene scene, notification;
        OverlaidGraphicsView * view;
        QGraphicsSimpleTextItem * item;
        int timerId;
        int time;
    public:
        Window() :
            view(new OverlaidGraphicsView(&scene, this)),
            timerId(-1), time(0)
        {
            for (int i = 0; i < 20; ++ i) {
                qreal w = rnd()*0.3, h = rnd()*0.3;
                scene.addEllipse(rnd()*(1-w), rnd()*(1-h), w, h, QPen(Qt::red), QBrush(Qt::lightGray));
            }
            view->fitInView(0, 0, 1, 1);
            view->setResizeAnchor(QGraphicsView::AnchorViewCenter);
            view->setRenderHint(QPainter::Antialiasing);
            view->setOverlayScene(&notification);
            item = new QGraphicsSimpleTextItem();
            item->setPen(QPen(Qt::blue));
            item->setBrush(Qt::NoBrush);
            item->setPos(95, 0);
            notification.addItem(item);
            notification.addRect(0, 0, 100, 0, Qt::NoPen, Qt::NoBrush); // strut
            timerId = startTimer(1000);
            QTimerEvent ev(timerId);
            timerEvent(&ev);
        }
        void resizeEvent(QResizeEvent * ev) {
            view->resize(size());
            view->fitInView(0, 0, 1, 1, Qt::KeepAspectRatio);
            QWidget::resizeEvent(ev);
        }
        void timerEvent(QTimerEvent * ev) {
            if (ev->timerId() != timerId) return;
            item->setText(QString::number(time++));
        }
    };
    
    int main(int argc, char ** argv)
    {
        QApplication a(argc, argv);
    
        Window window;
        window.show();
        a.exec();
    }
    
    #include "main.moc"
    
    0 讨论(0)
提交回复
热议问题