私はあなたがここでしたのと同じ質問をしていました。この問題に数時間格闘した後に思いついた解決策に進む前に、一般に、大量のウィジェットをシーンに追加したい場合、シーンにウィジェットを追加することはお勧めできません。これに伴う問題については、こちらをご覧ください。
さて、軌道に戻ります。必要なものを実装しようとすると、大きな問題として発生することがいくつかあります。
mouseMove()
ウィジェット プロキシのとを台無しにするとmouseHover()
、ウィジェットの UI コンポーネントの動作が台無しになります。
- 同時に、選択可能で移動可能にしたいので、何らかの形でそのような機能をウィジェットプロキシに追加する必要があります
どうやってそれを行うのですか?まあ、これは直接的には不可能のようです。しかし、私は仕事を成し遂げる簡単な解決策を思いついた - aQGraphicsProxyWidget
と a QGraphicsItem
!
ウィジェットで UI コンポーネントの機能を保持するには、プロキシを子としてグラフィック アイテムに追加する必要があります。一方、グラフィック アイテム (プロキシの親) は、選択部分と移動部分をカバーします。
これは小さなデモです (これは私が現在自分自身で研究しているものであり、ほとんどの場合、C++ Qt の代わりに PyQt を使用しているため、バグを除外しません):
scenewithmovableproxys.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_HPP
scenewithmovableproxys.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("---");
}
ここにいくつかのスクリーンショットがあります:
初期ビュー

3つのうち2つが選択されました

移動中

回転中

ウィジェットの操作 - QComboBox の使用

ウィジェットの操作 - QCheckBox の使用

QGraphicsItem
(および)の性質上QGraphicsProxyWidget
、最も厄介な問題がいくつかありますが、その中で最も厄介なのはオーバーラップです
重複

ただし、衝突検出を使用し、基本的にオーバーラップをまったく許可しないことで、オーバーラップを比較的簡単に回避できます。関数QGraphicsProxyWidget
も使用できるため、この状況を処理するためのメカニズムを実装できます。私はそのすべてに慣れていないので(SO :Dで質問に答えながら2日前に開始しました) 、これを処理するためにそれ自体に何らかの統合されたメカニズムがあるかもしれません。QGraphicsItem
collisionWithItem(...)
QGraphicsScene
QGraphicsScene
ゆがみ
回転のスクリーンショットで、以前は完璧に見えていた直線が視覚的におかしいことに気付いたかもしれません。これらはいわゆるジャギーです。現在、それらを取り除く方法がわかりません。高品質のアンチエイリアシングを使用してみましたが、アンチエイリアシングだけの場合よりも結果がさらに悪くなります ( QPainter::Antialiasing
)。
さらなる研究
私は現在、画像処理用の複合ノードベースの UI を作成したい小さなプロジェクトに取り組んでいます。このトピックをオンラインで調べると、人々が単純な を使用してQGraphicsItem
おり、ノード構成自体がQGraphicsViewer
ウィジェットの外部に部分的に外部委託されているという解決策が常に返されました。上で説明したデザインを使用して、後で何をしたいかによってさらに追加することができます。複数QGraphicItem
の をウィジェット プロキシにアタッチすることもできます。これに夢中になることもできますが、パフォーマンスに影響があることを覚えておいてください(回答の冒頭でリンクしたブログ投稿を何度も読んでください)。