我想将QWidget
放入QGraphicsView
并使用QGraphicsProxyWidget
使小部件可选择并移动。
(这适用于QGraphicsRectItem
,QGraphicItem
等。)
这是我目前正在使用的代码:
// Create new QGraphicsScene and assign to graphicsView
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
// Create widget and add to scene
MyWidget *widget = new MyWidget;
QGraphicsProxyWidget *proxy = scene->addWidget(widget);
// Make selectable
proxy->setFlag(QGraphicsItem::ItemIsSelectable, true);
// Make movable
proxy->setFlag(QGraphicsItem::ItemIsMovable, true);
小部件显示正确,但既不可移动也不可选择!
非常感谢任何帮助;)
答案 0 :(得分:10)
我问自己和你在这里做的问题一样。在我继续解决这个问题之前,我想出了几个小时的努力解决这个问题后,我不得不提一下,一般来说添加小部件到现场并不是一个好主意,如果你想要有大量的它即可。您可以阅读此here附带的问题。
现在回到正轨。以下是在尝试实现您想要的内容时出现的一些重大问题:
mouseMove()
和mouseHover()
,那么您将搞乱窗口小部件的UI组件的行为方式。 我们如何做到这一点?好吧,直接这似乎是不可能的。但是我提出了一个简单的解决方案来完成工作 - 将QGraphicsProxyWidget
和QGraphicsItem
结合起来!
为了保留UI组件'您需要将代理作为子项添加到图形项中的功能。另一方面,图形项(代理的父项)将覆盖选择和移动部分。
这是一个小型演示(我不排除错误,因为这是我目前正在研究的东西,而且大多数时候我使用的是PyQt而不是C ++ Qt):
<强>的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_HPP
<强> 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));
// Add widget proxies + their parenting graphics items to scene
addWidgetToScene(QPoint(10, 10));
addWidgetToScene(QPoint(300, 100));
addWidgetToScene(QPoint(200, 200));
}
SceneWithMovableProxies::~SceneWithMovableProxies()
{
delete ui;
}
void SceneWithMovableProxies::addWidgetToScene(QPoint initPos)
{
// Create a widget
SceneWidgetItem *widget = new SceneWidgetItem();
// Create the graphics item that will be used to move the widget around the screen as well as be selectable (for example in case we want to delete a widget that is in the scene)
// Depending on the position of the graphics item relative to its widget proxy you can adjust the size and location of both
QGraphicsRectItem *proxyControl = ui->graphicsView->scene()->addRect(initPos.x(), initPos.y(), widget->width(), 20, QPen(Qt::black), QBrush(Qt::darkGreen)); // widget->width() works properly here because of the resize(layout->sizeHint()) that we have used inside it
proxyControl->setFlag(QGraphicsItem::ItemIsMovable, true);
proxyControl->setFlag(QGraphicsItem::ItemIsSelectable, true);
// Create the proxy by adding the widget to the scene
QGraphicsProxyWidget * const proxy = ui->graphicsView->scene()->addWidget(widget);
// In my case the rectangular graphics item is supposed to be above my widget so the position of the widget is shifted along the Y axis based on the height of the rectangle of that graphics item
proxy->setPos(initPos.x(), initPos.y()+proxyControl->rect().height());
proxy->setParentItem(proxyControl);
// proxyControl->setRotation(45); // Because the widget is a child of the graphics item if we do some sort of transformation to it, the change will be propagated to the widget too!
}
<强> 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_HPP
<强> scenewidgetitem.cpp 强>
#include "scenewidgetitem.hpp"
// Create a widget with whichever UI components you like
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);
// Maybe add some signals :P
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);
// Resizing the widget to its layout's content is very important. If
// you don't do that the parenting graphics item will not visually fit
// to its child widget and you will get a mess
resize(layout->sizeHint());
setLayout(layout);
}
SceneWidgetItem::~SceneWidgetItem()
{
}
void SceneWidgetItem::reset()
{
combobox->setCurrentIndex(0);
label->setText("---");
}
以下是一些截图:
<强>旋转强>
现在由于QGraphicsItem
(以及QGraphicsProxyWidget
)的性质,有一些问题最令人讨厌的是重叠
然而,通过使用碰撞检测可以相对容易地避免重叠,并且基本上根本不允许重叠。由于QGraphicsProxyWidget
也可以使用QGraphicsItem
函数collisionWithItem(...)
,因此您可以实现处理此情况的机制。由于我是QGraphicsScene
的新手以及所有这些(2天前在SO:D处回答问题时开始),QGraphicsScene
本身可能会有某种综合机制来处理这个问题。
<强>失真强>
在旋转屏幕截图中,您可能已经注意到有一些视觉上的奇怪现象,以前看起来很完美的直线。这些就是所谓的锯齿。目前我不知道如何摆脱这些。我尝试使用高质量抗锯齿但结果甚至比只使用抗锯齿(QPainter::Antialiasing
)更糟糕。
进一步的研究
我实际上正在开发一个我的小项目,我想为图像处理创建一个基于复合节点的UI。在线查看此主题始终返回人们使用简单QGraphicsItem
的解决方案,并且节点配置本身部分外包到QGraphicsViewer
小部件的外部。您可以使用我上面描述的设计,并根据您以后要执行的操作添加更多内容。多个QGraphicItem
也可以附加到窗口小部件代理。你可以对此发疯,但请记住,这会对性能产生影响(请阅读我在我的答案开头一遍又一遍地链接的博文)。
答案 1 :(得分:-2)
QGraphicsProxyWidget不打算此行为,例如:考虑你有一个QTextEdit小部件,你想点击这个小部件的文本来选择一些东西。此时您遇到的问题是,QTextEdit是应该为选择过程处理此鼠标事件还是要通过GraphicsWidget来移动QTextEdit。
选项1: 从QGraphicsProxyWidget和鼠标函数(mouseMove,...)派生,你可以再次调用QGraphicsItem :: mouseMove(),这应该再次带回移动Widget的功能。
选项2: 这更像是一种解决方法,但在大多数情况下是更好的解决方案,因为它会阻止您按照选项1的需要管理事件。 只需创建一个GraphicsRectItem R,它比你的小部件W大。设置R可移动等。将W添加到你的场景,你将收到一个QGraphicsProxyWidget P.调用P-> setParentItem(R),最后是scene-&gt; AddItem(R );
现在可以通过移动父矩形来移动窗口小部件。您还可以添加其他具有R作为父级的GraphicsRectIrems r,例如实现调整大小功能。您只需要为r安装EventFilter并对过滤器函数中的mouseMove事件做出反应。