我和你一样也在思考这个问题。在我解决这个问题的几个小时里,我必须提到一个普遍的问题:如果您想在场景中放置大量小部件,则
将小部件添加到场景中并不是一个好主意。您可以在
此处阅读有关此问题的信息。
现在回到正题。在尝试实现您想要的内容时,会出现以下一些重要问题:
- 如果更改小部件代理的
mouseMove()
和mouseHover()
,将会破坏小部件UI组件的行为。
- 同时,您希望它可选择和可移动,因此必须以某种方式向小部件代理添加此类功能。
我们该怎么做呢?嗯,直接来看似乎是不可能的。但是,我想出了一个简单的解决方案 - 将
QGraphicsProxyWidget
与
QGraphicsItem
结合起来!
为了保留小部件的UI组件功能,您需要将代理作为子项添加到图形项中。另一方面,图形项(代理的父项)将覆盖选择和移动部分。
这是一个小演示(我不排除错误,因为这是我目前正在研究的内容,大多数时间我使用PyQt而不是C++ Qt):
scenewithmovableproxies.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SceneWithMovableProxies</class>
<widget class="QWidget" name="SceneWithMovableProxies">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>SceneWithMovableProxies</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGraphicsView" name="graphicsView"/>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
main.cpp
#include "scenewithmovableproxies.hpp"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SceneWithMovableProxies w;
w.show();
return a.exec();
}
scenewithmovableproxies.hpp
#ifndef SCENEWITHMOVABLEPROXIES_HPP
#define SCENEWITHMOVABLEPROXIES_HPP
#include <QWidget>
#include <QPoint>
namespace Ui {
class SceneWithMovableProxies;
}
class SceneWithMovableProxies : public QWidget
{
Q_OBJECT
public:
explicit SceneWithMovableProxies(QWidget *parent = 0);
~SceneWithMovableProxies();
private:
Ui::SceneWithMovableProxies *ui;
void addWidgetToScene(QPoint initPos);
};
#endif
scenewithmovableproxies.cpp
#include "scenewithmovableproxies.hpp"
#include "ui_scenewithmovableproxies.h"
#include "scenewidgetitem.hpp"
#include <QGraphicsProxyWidget>
SceneWithMovableProxies::SceneWithMovableProxies(QWidget *parent) :
QWidget(parent),
ui(new Ui::SceneWithMovableProxies)
{
ui->setupUi(this);
ui->graphicsView->setRenderHint(QPainter::Antialiasing);
ui->graphicsView->setScene(new QGraphicsScene(this));
addWidgetToScene(QPoint(10, 10));
addWidgetToScene(QPoint(300, 100));
addWidgetToScene(QPoint(200, 200));
}
SceneWithMovableProxies::~SceneWithMovableProxies()
{
delete ui;
}
void SceneWithMovableProxies::addWidgetToScene(QPoint initPos)
{
SceneWidgetItem *widget = new SceneWidgetItem();
QGraphicsRectItem *proxyControl = ui->graphicsView->scene()->addRect(initPos.x(), initPos.y(), widget->width(), 20, QPen(Qt::black), QBrush(Qt::darkGreen));
proxyControl->setFlag(QGraphicsItem::ItemIsMovable, true);
proxyControl->setFlag(QGraphicsItem::ItemIsSelectable, true);
QGraphicsProxyWidget * const proxy = ui->graphicsView->scene()->addWidget(widget);
proxy->setPos(initPos.x(), initPos.y()+proxyControl->rect().height());
proxy->setParentItem(proxyControl);
}
scenewidgetitem.hpp
#ifndef SCENEWIDGETITEM_HPP
#define SCENEWIDGETITEM_HPP
#include <QWidget>
#include <QVBoxLayout>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
class SceneWidgetItem : public QWidget
{
Q_OBJECT
QVBoxLayout *layout;
QLabel *label;
QCheckBox *checkbox;
QComboBox *combobox;
QPushButton *resetButton;
public:
explicit SceneWidgetItem(QWidget *parent = 0);
~SceneWidgetItem();
signals:
public slots:
void reset();
};
#endif
scenewidgetitem.cpp
#include "scenewidgetitem.hpp"
SceneWidgetItem::SceneWidgetItem(QWidget *parent) : QWidget(parent)
{
layout = new QVBoxLayout(this);
checkbox = new QCheckBox("Enable proxy", this);
checkbox->setChecked(true);
combobox = new QComboBox(this);
combobox->addItem("---");
combobox->addItem("Item 1");
combobox->addItem("Item 2");
combobox->addItem("Item 3");
label = new QLabel(this);
label->setText(combobox->itemText(0));
resetButton = new QPushButton("Reset", this);
connect(checkbox, SIGNAL(toggled(bool)), combobox, SLOT(setEnabled(bool)));
connect(checkbox, SIGNAL(toggled(bool)), resetButton, SLOT(setEnabled(bool)));
connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(reset()));
connect(combobox, SIGNAL(currentIndexChanged(QString)), label, SLOT(setText(QString)));
layout->addWidget(checkbox);
layout->addWidget(label);
layout->addWidget(resetButton);
layout->addWidget(combobox);
resize(layout->sizeHint());
setLayout(layout);
}
SceneWidgetItem::~SceneWidgetItem()
{
}
void SceneWidgetItem::reset()
{
combobox->setCurrentIndex(0);
label->setText("---");
}
以下是一些屏幕截图:
初始视图
![enter image description here](https://istack.dev59.com/Ee8q4.webp)
三个中选择两个
![enter image description here](https://istack.dev59.com/TWID1.webp)
移动
![enter image description here](https://istack.dev59.com/Pa8YT.webp)
旋转
![enter image description here](https://istack.dev59.com/AGKhp.webp)
使用QComboBox与小部件交互
使用QCheckBox与小部件交互
![在此输入图片描述](https://istack.dev59.com/cb0vL.webp)
由于
QGraphicsItem
(以及
QGraphicsProxyWidget
)的特性,存在一些问题,其中最烦人的是重叠。
重叠问题
![在此输入图片描述](https://istack.dev59.com/hdVZE.webp)
然而,通过使用碰撞检测并基本上
不允许重叠,可以相对容易地避免重叠。由于
QGraphicsProxyWidget
也可以使用
QGraphicsItem
函数
collisionWithItem(...)
,因此您可以实现处理此情况的机制。由于我刚开始接触
QGraphicsScene
和所有这些(在SO上回答问题时2天前开始:D),所以
QGraphicsScene
本身可能有某种集成机制来处理这个问题。
扭曲
在旋转截图中,您可能已经注意到先前完美的直线出现了一些视觉异常。这些是所谓的jaggies。目前我不知道如何摆脱它们。我尝试使用高质量抗锯齿,但结果甚至比仅使用抗锯齿(QPainter::Antialiasing
)更糟。
进一步研究
我目前正在开展一个小项目,想要创建一个基于复合节点的图像处理用户界面。在网上查找此主题时,总是返回人们使用简单的QGraphicsItem
并且节点配置部分外包给QGraphicsViewer
小部件的解决方案。您可以使用我上面描述的设计,并根据以后想要做什么添加更多内容。多个QGraphicItem
也可以附加到小部件代理上。您可以尽情发挥,但请记住这会产生性能影响(再次阅读我在答案开头提供的博客帖子)。