5

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);

ウィジェットは正しく表示されますが、移動も選択もできません!

どんな助けでも大歓迎です;)

4

2 に答える 2

14

私はあなたがここでしたのと同じ質問をしていました。この問題に数時間格闘した後に思いついた解決策に進む前に、一般に、大量のウィジェットをシーンに追加したい場合、シーンにウィジェットを追加することはお勧めできません。これに伴う問題については、こちらをご覧ください。

さて、軌道に戻ります。必要なものを実装しようとすると、大きな問題として発生することがいくつかあります。

  • 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日前に開始しました) 、これを処理するためにそれ自体に何らかの統合されたメカニズムがあるかもしれません。QGraphicsItemcollisionWithItem(...)QGraphicsSceneQGraphicsScene

ゆがみ 回転のスクリーンショットで、以前は完璧に見えていた直線が視覚的におかしいことに気付いたかもしれません。これらはいわゆるジャギーです。現在、それらを取り除く方法がわかりません。高品質のアンチエイリアシングを使用してみましたが、アンチエイリアシングだけの場合よりも結果がさらに悪くなります ( QPainter::Antialiasing)。

さらなる研究 私は現在、画像処理用の複合ノードベースの UI を作成したい小さなプロジェクトに取り組んでいます。このトピックをオンラインで調べると、人々が単純な を使用してQGraphicsItemおり、ノード構成自体がQGraphicsViewerウィジェットの外部に部分的に外部委託されているという解決策が常に返されました。上で説明したデザインを使用して、後で何をしたいかによってさらに追加することができます。複数QGraphicItemの をウィジェット プロキシにアタッチすることもできます。これに夢中になることもできますが、パフォーマンスに影響があることを覚えておいてください(回答の冒頭でリンクしたブログ投稿を何度も読んでください)。

于 2016-02-28T16:00:06.667 に答える
-2

この動作は QGraphicsProxyWidget が意図したものではありません。たとえば、QTextEdit ウィジェットがあり、このウィジェットのテキストをクリックして何かを選択したいとします。この時点で、このマウス イベントを選択プロセスのために QTextEdit で処理するか、QTextEdit を移動するために GraphicsWidget で処理するかという問題があります。

オプション 1: QGraphicsProxyWidget から派生し、マウス関数 (mouseMove など) で QGraphicsItem::mouseMove() を再度呼び出すことができます。これにより、ウィジェットを移動する機能が再び元に戻ります。

オプション 2: これは回避策に似ていますが、ほとんどの場合、オプション 1 で必要になるイベントを管理できなくなるため、より良い解決策です。Widget W よりも大きい GraphicsRectItem R を作成するだけです。 R 可動など シーンに W を追加すると、QGraphicsProxyWidget P を受け取ります。P->setParentItem(R) を呼び出し、最後に scene->AddItem(R);

親の四角形を移動することで、ウィジェットを移動できるようになりました。R を親とする GraphicsRectIrems r を追加して、サイズ変更関数を実装することもできます。r の EventFilter をインストールし、フィルター関数で mouseMove イベントに反応するだけです。

于 2014-09-29T16:01:31.547 に答える