【问题标题】:Selection exclusion in nested QGraphicsScenes嵌套 QGraphicsScenes 中的选择排除
【发布时间】:2020-11-04 02:05:39
【问题描述】:

我有一个包含项目的 QGraphicsScene(我们称之为母场景),其中一些还包含 QGraphicsScene(我们称它们为子场景)。

观察:女儿场景和母亲场景中的选择互不打扰。这意味着,如果我在母场景中选择一个项目,我可以选择子场景中的项目,而母场景中的项目将保持选中状态。

我的预期结果:我希望我在一个场景中的选择清除任何其他场景中的选择。

[为清晰而编辑]当我在一个场景中选择一个项目时,我想取消选择其他场景中的选定项目,而不是在不同的场景中有多个焦点。

我的动机:我想这样做是因为当我使用键盘快捷键时,我不知道 Qt 会选择哪个场景。当我只有一个女儿时,选择母亲(我更喜欢女儿)。

到目前为止我的解决方案: 在子场景容器项中,mousePressEvent 清除了母场景的选择。我发现这个解决方案非常丑陋,我想知道是否有人知道一个更好的解决方案,它会使用一些内部 Qt 功能。现在看来,这是一个糟糕的 DIY 解决方案,会带来很多问题。

提前致谢!

[编辑:最小示例] 在这个例子中,我们可以同时选择两个嵌套元素。我宁愿在整个场景中一次只选择一个。

#include <QApplication>
#include <QGraphicsView>

#include <QGraphicsScene>
#include <QMainWindow>
#include <QGraphicsItem>
#include <QGraphicsLinearLayout>
#include <QGraphicsWidget>
#include <QGraphicsProxyWidget>

// Item that gets red contour when selected
class SimpleItem : public QGraphicsItem
{
public :
    SimpleItem():QGraphicsItem()
    {
        setFlag(QGraphicsItem::ItemIsSelectable, true);
    }

    QRectF boundingRect() const override { return QRectF(-20, -20, 40, 40);}

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
    {
        painter->setPen(Qt::black);
        if(isSelected())
            painter->setPen(Qt::red);
        painter->setBrush(Qt::gray);
        painter->drawRect(boundingRect());
    }
};

// Item that contains a QGraphicsScene in a layout
// This item gets also a red contour when selected
class SceneItem : public QGraphicsWidget
{
public :
    SceneItem():QGraphicsWidget()
    {
        setFlag(QGraphicsItem::ItemIsSelectable, true);
        setFocusPolicy(Qt::ClickFocus);

        // Create the inner scene
        QGraphicsLinearLayout * layout = new QGraphicsLinearLayout;
        setLayout(layout);
        QGraphicsScene * scene = new QGraphicsScene;
        QGraphicsView * view = new QGraphicsView(scene);
        QGraphicsProxyWidget * proxy = new QGraphicsProxyWidget;
        layout->addItem(proxy);
        proxy->setWidget(view);

        // Add a simple item
        SimpleItem * simpleItem = new SimpleItem;
        scene->addItem(simpleItem);
    }

    QRectF boundingRect() const override { return QRectF(0, 0, 100, 100);}

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
    {
        painter->setPen(Qt::black);
        if(isSelected())
            painter->setPen(Qt::red);
        painter->setBrush(Qt::lightGray);
        painter->drawRect(boundingRect());
    }
};

// Main
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QMainWindow w;

    QGraphicsScene * scene = new QGraphicsScene;
    QGraphicsView * view = new QGraphicsView(scene);
    view->setDragMode(QGraphicsView::RubberBandDrag);
    w.setCentralWidget(view);

    SceneItem * item = new SceneItem;
    scene->addItem(item);

    w.show();

    return a.exec();
}

【问题讨论】:

  • 这似乎是一个 XY 问题。为什么要嵌套场景?
  • 我很确定这不是 XY 问题。我嵌套场景是因为我希望一些项目包含图形场景,这就是我的用例。
  • 我嵌套场景是因为我希望一些项目包含图形场景这从您的帖子中显而易见。如果QGraphicsScene 的预期用途是,那就是另一回事了。无论如何,如果没有Minimal, Reproducible Example,就很难理解您的问题。寻找解决方案。
  • 感谢您查看我的问题。这是一个代码示例。
  • 请看this,从Graphics View 支持嵌入QWidgetsGraphics View 高性能的关键在于减少绘制量每一帧。 QGraphicsWidget 和 QGraphicsProxyWidget 一起是巨大的性能杀手,因为它们不能以有效的方式呈现。

标签: c++ qt nested selection qgraphicsscene


【解决方案1】:

免责声明

我个人不建议在 QGraphicsScene 中嵌入小部件:

Graphics View 高性能的关键在于减少每帧的绘制量。 QGraphicsWidget 和 QGraphicsProxyWidget 一起是巨大的性能杀手,因为它们不能以有效的方式呈现。

此引文的来源以及有关该主题的更多信息可在此博客文章中找到:

Should you still be using QGraphicsView?

解决方案

如果我必须不惜一切代价使用 OP 使用的确切方法,我的解决方案是:

  1. 使用QGraphicsScene::selectionChanged 对选择更改做出反应并
  2. 使用QGraphicsScene::selectedItems 检查是否选择了某个项目。

示例

这是 OP 提供的 MVCE,由我修改以演示如何实施建议的解决方案:

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsLinearLayout>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
#include <QPainter>

class SimpleItem : public QGraphicsItem
{
public :
    explicit SimpleItem(QGraphicsItem *parent = nullptr) :
        QGraphicsItem(parent) { setFlag(ItemIsSelectable, true); }

    QRectF boundingRect() const override { return QRectF(-20, -20, 40, 40); }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *,
               QWidget *) override {
        painter->setPen(isSelected() ? Qt::red : Qt::black);
        painter->setBrush(Qt::gray);
        painter->drawRect(boundingRect());
    }
};

class SceneItem : public QGraphicsWidget
{
    QGraphicsScene *m_scene;
public:
    explicit SceneItem(QGraphicsItem *parent = nullptr) :
        QGraphicsWidget(parent),
        m_scene(new QGraphicsScene(this)) {
        auto *layout = new QGraphicsLinearLayout;
        auto *view = new QGraphicsView(m_scene);
        auto *proxy = new QGraphicsProxyWidget(this);
        auto *simpleItem = new SimpleItem(this);

        m_scene->addItem(simpleItem);
        proxy->setWidget(view);
        layout->addItem(proxy);

        setLayout(layout);
        setFocusPolicy(Qt::ClickFocus);
        setFlag(QGraphicsItem::ItemIsSelectable, true);
    }

    QGraphicsScene *scene() const { return m_scene; }
    QRectF boundingRect() const override { return QRectF(0, 0, 100, 100);}
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *,
               QWidget *) override {
        painter->setPen(isSelected() ? Qt::red : Qt::black);
        painter->setBrush(Qt::lightGray);
        painter->drawRect(boundingRect());
    }
    int type() const override { return UserType; }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QMainWindow w;
    auto *scene = new QGraphicsScene;
    auto *view = new QGraphicsView(scene);
    auto *item = new SceneItem;

    scene->addItem(item);
    view->setDragMode(QGraphicsView::RubberBandDrag);

    QObject::connect(item->scene(), &QGraphicsScene::selectionChanged, [item, scene](){
        if (!item->scene()->selectedItems().isEmpty())
            scene->clearSelection();
    });

    QObject::connect(scene, &QGraphicsScene::selectionChanged, [scene](){
        if (scene->selectedItems().isEmpty())
            return;

        for (auto *item : scene->items())
            if (item->type() == QGraphicsItem::UserType)
                static_cast<SceneItem *>(item)->scene()->clearSelection();
    });

    w.setCentralWidget(view);
    w.show();

    return a.exec();
}

【讨论】:

  • 非常感谢您的回答。这两个信息都非常有用。现在我将使用场景信号。我根本没有考虑它们,对于我的情况,这是一个可以接受的解决方案。我一定会期待 QML 的场景。我坚持图形视图“就像蚌壳一样”,因为我知道这一点,而且我没有努力真正进入 QML。但我需要几天时间来评估我是否可以足够快地调整我的应用程序。
  • @Gustavitch,这是一个艰难的决定。我面临同样的问题,我期待 Qt 6,它承诺提供更好的 QML。还有一件事。如果你喜欢这个答案,请考虑在你有足够的代表时投票。点。
  • 是的,在技术选择方面做出艰难的决定。再次感谢,我当然会赞成你的回答!
  • 哦,好吧,我很抱歉我不知道“XY 问题”的含义……我以为你告诉我我的坐标是错误的,因此我的选择可能也是如此…… . 我以为你把我的问题弄错了。感谢您提供此补充材料!
  • 谢谢!祝你好运!
猜你喜欢
  • 1970-01-01
  • 2014-08-21
  • 2017-04-22
  • 2016-01-07
  • 2013-03-27
  • 2022-12-01
  • 1970-01-01
  • 2011-02-20
  • 1970-01-01
相关资源
最近更新 更多