c++ - 在相对于 QGraphicsView 的静态位置绘制项目

标签 c++ qt qgraphicsview qgraphicsitem qgraphicsscene

我想绘制一条始终出现在我的QGraphicsView 右上角的通知。但是,QGraphicsItems 位置是在场景坐标中指定的,因此如果用户平移/缩放以查看场景的不同部分,此通知将移出屏幕。

我发现我可以通过在当前 View 发生变化时移动和缩放通知来模拟此行为。但这似乎非常低效而且一点也不优雅。

QGraphicsView 似乎应该支持这种行为。文档提到了一个标志 ItemIsPanel,这听起来很有希望,但没有提到 View 中的静态放置。 ItemIgnoresTransformations 也有助于缩放,但不支持平移。

是否有支持此行为的任何内置 Qt 功能?

最佳答案

让通知成为原始场景的一部分的天真的解决方案是不好的——它打破了模型- View 分离。您可以有多个 View ,所有 View 都显示一个场景,但通常只有其中一个 View 可以根据需要显示通知。

另一种简单的方法是在您的 View 上叠加一个 QWidget 通知。问题在于,在某些架构上,将常规 QWidgets 叠加在加速 QGLWidgets 之上会使前者消失。请注意,QGraphicsView 的视口(viewport)可能是 QGLWidget!

因此,唯一可移植的解决方案是明确地在 QGraphicsSceneView 的 viewport() 中的所有其他内容之上进行绘制。

下面是一个完整的例子。

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"

关于c++ - 在相对于 QGraphicsView 的静态位置绘制项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18411250/

相关文章:

c++ - 拦截对 std::streambuf 的所有写入

c++ - 静态变量 - undefined reference

c++ - 防止自己忘记在 tr() 中包含字符串文字

python - 如何使用 PyQt 更新 QGraphicsView 中的 QPixmap

python - 获取沿 QPainterPath 的单击点

c++ - 传递给 lambda 函数的空指针不再为空

c++ - 如何与 Boost.Process 0.5 中的进程同步交互

c++ - 在 Qt 中发出 Clicked() 后获取单击的按钮 (C++)

python - 使用 PyQt 自定义 QDial notch ticks

c++ - 使用预定义大小在 C++ 中实现 Map