一、简述

图形视图对将任何 widget 嵌入到场景中提供了无缝的支持。可以嵌入简单的 widget例如QLineEdit、QPushButton也可以是复杂的 widget例如QTabWidget甚至是完整的主窗口。

要将 widget 嵌入场景中只需要简单地调用 QGraphicsScene::addWidget()或者创建一个 QGraphicsProxyWidget 对象并将 widget 手工地嵌入其中。

二、QGraphicsProxyWidget

QGraphicsProxyWidget 类提供了一个代理层用于在 QGraphicsScene 中嵌入一个 QWidget。

QGraphicsProxyWidget 嵌入基于 QWidget 的部件例如QPushButton、QFontComboBox甚至 QFileDialog、QGraphicsScene。它在两个对象之间转发事件并在 QWidget 的基于整数的 geometry 和 QGraphicsWidget 的基于 qreal 的 geometry 之间进行转换。

QGraphicsProxyWidget 支持 QWidget 的所有核心功能包括Tab 焦点、键盘输入、拖放和弹出窗口。还可以嵌入复杂的 widget例如带有子 widget 的 widget。

QGraphicsProxyWidget 通过为每个 popup 创建一个子代理来负责嵌入 widget 的 popup 孩子的自动嵌入。这意味着当嵌入的 QComboBox 显示其弹出列表时会自动创建一个新的 QGraphicsProxyWidget嵌入 popup 并正确定位。这只有当 popup 是嵌入的 widget 的孩子时才有效(例如 QToolButton::setMenu() 要求 QMenu 实例是 QToolButton 的孩子。)

三、使用 QGraphicsProxyWidget 嵌入 Widget

使用 QGraphicsProxyWidget 嵌入一个 widget 有两种方法

  • 将一个 widget 指针和任何相关的 Qt::WindowFlags 传递给 QGraphicsScene::addWidget() - 最常用的方式。
  • 创建一个新的 QGraphicsProxyWidget然后调用 setWidget() 嵌入一个 QWidget。

例如在下面的代码段中我们在代理中嵌入了一个 QGroupBox
image.png

  1. #include <QFormLayout>
  2. #include <QGraphicsPolygonItem>
  3. #include <QGraphicsScene>
  4. #include <QGraphicsView>
  5. #include <QGroupBox>
  6. #include <QLabel>
  7. #include <QLineEdit>
  8. #include <QString>
  9. Widget::Widget(QWidget* parent)
  10. : QWidget(parent)
  11. , ui(new Ui::Widget)
  12. {
  13. ui->setupUi(this);
  14. QGroupBox* pGroupBox = new QGroupBox();
  15. QLabel* pTellLabel = new QLabel();
  16. QLabel* pAddressLabel = new QLabel();
  17. QLineEdit* pTellLineEdit = new QLineEdit();
  18. QLineEdit* pAddressLineEdit = new QLineEdit();
  19. pGroupBox->setTitle(tr("联系方式"));
  20. pTellLabel->setText(tr("电话号码"));
  21. pAddressLabel->setText(tr("居住地址"));
  22. pTellLineEdit->setPlaceholderText(tr("手机/固话"));
  23. pAddressLineEdit->setPlaceholderText(tr("具体到门牌号"));
  24. QFormLayout* pLayout = new QFormLayout;
  25. pLayout->addRow(pTellLabel, pTellLineEdit);
  26. pLayout->addRow(pAddressLabel, pAddressLineEdit);
  27. pLayout->setSpacing(10);
  28. pLayout->setMargin(20);
  29. pGroupBox->setLayout(pLayout);
  30. QGraphicsScene* scene = new QGraphicsScene();
  31. scene->addWidget(pGroupBox);
  32. QGraphicsView* view = new QGraphicsView();
  33. view->setScene(scene);
  34. QVBoxLayout* layout = new QVBoxLayout(this);
  35. layout->addWidget(view);
  36. }

QGraphicsProxyWidget 与 QWidget 共享所有权因此如果这两个 widget 中的任一个被销毁另一个也将被自动销毁。

四、同步 Widget 状态

QGraphicsProxyWidget 保持其与嵌入的 widget 状态同步。例如如果代理被隐藏或禁用嵌入的 widget 也将被隐藏或禁用反之亦然。

当 widget 通过调用 addWidget() 被嵌入时QGraphicsProxyWidget 将 widget 的状态例如visible、enabled、geometry、size hints复制到代理中然后两者将尽可能地保持同步。 默认情况下当一个 widget 被嵌入到代理中后widget 和代理都是可见的因为 QGraphicsWidget 在创建时是可见的不必调用 show()。如果显式隐藏嵌入的 widget 代理也将变为不可见。

  1. QGraphicsScene scene;
  2. QLineEdit *edit = new QLineEdit;
  3. QGraphicsProxyWidget *proxy = scene.addWidget(edit);
  4. edit->isVisible(); // 返回 true
  5. proxy->isVisible(); // 也返回 true
  6. edit->hide();
  7. edit->isVisible(); // 返回 false
  8. proxy->isVisible(); // 也返回 false

QGraphicsProxyWidget 保持以下状态的对称性

QWidget 状态 QGraphicsProxyWidget 状态 说明
QWidget::enabled QGraphicsProxyWidget::enabled
QWidget::visible QGraphicsProxyWidget::visible 显式状态也是对称的
QWidget::geometry QGraphicsProxyWidget::geometry 当嵌入的 widget 可见时Geometry 仅保证是对称的
QWidget::layoutDirection QGraphicsProxyWidget::layoutDirection
QWidget::style QGraphicsProxyWidget::style
QWidget::palette QGraphicsProxyWidget::palette
QWidget::font QGraphicsProxyWidget::font
QWidget::cursor QGraphicsProxyWidget::cursor 嵌入的 widget 覆盖代理光标代理光标的改变依赖嵌入的子 widget 当前在鼠标下。
QWidget::sizeHint() QGraphicsProxyWidget::sizeHint() 嵌入的 widget 的所有大小提示功能均由代理转发
QWidget::getContentsMargins() QGraphicsProxyWidget::getContentsMargins() 通过 setWidget() 更新一次
QWidget::windowTitle QGraphicsProxyWidget::windowTitle 通过 setWidget() 更新一次

注意:当 widget 被嵌入时QGraphicsScene 保持嵌入的 widget 在一个特殊的状态防止它影响其他 widget嵌入和非嵌入。在此状态下widget 在行为上可能与未嵌入时略有不同。

警告:QGraphicsProxyWidget 类是为了便于桥接 QWidgets 和 QGraphicsItems 而提供不应该用于高性能场景。