使QGraphicsProxyWidget可移动&可选择的 [英] Make QGraphicsProxyWidget movable & selectable

查看:140
本文介绍了使QGraphicsProxyWidget可移动&可选择的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将一个 QWidget 放入一个 QGraphicsView 中,并使用 QGraphicsProxyWidget 使该窗口小部件可以选择和移动.(这非常适合 QGraphicsRectItem QGraphicItem 等.)

这是我当前正在使用的代码:

 //创建新的QGraphicsScene并分配给graphicsView场景=新的QGraphicsScene(this);ui-> graphicsView-> setScene(scene);//创建小部件并添加到场景MyWidget *小部件=新的MyWidget;QGraphicsProxyWidget * proxy = scene-> addWidget(widget);//使其成为可选proxy-> setFlag(QGraphicsItem :: ItemIsSelectable,true);//使可移动proxy-> setFlag(QGraphicsItem :: ItemIsMovable,true); 

窗口小部件可以正确显示,但是既不能移动,也不能选择!

任何帮助将不胜感激;)

解决方案

我在问自己一个与您在此处相同的问题.经过数小时的努力之后,在提出解决方案之前,我想出了一个问题,通常来说,如果要在其中添加大量小部件,在场景中添加小部件并不是一个好主意.它.您可以阅读

三分之二的人选择

移动

旋转

与小部件交互-使用QComboBox

与小部件进行交互-使用QCheckBox

现在,由于 QGraphicsItem (以及 QGraphicsProxyWidget )的性质,存在一些最令人讨厌的问题是重叠

重叠

但是,使用碰撞检测可以相对容易地避免重叠,并且基本上完全不允许重叠.由于 QGraphicsProxyWidget 也可以使用 QGraphicsItem 函数 collisionWithItem(...),因此您可以实现一种处理这种情况的机制.由于我是 QGraphicsScene 的新手(两天前开始,在此处回答SO:D的问题时),所以 QGraphicsScene 本身可能存在某种集成机制处理这个.

失真在旋转屏幕截图中,您可能已经注意到,以前看起来很完美的直线有些视觉上的怪异.这些就是所谓的锯齿.目前,我不知道如何摆脱这些.我尝试使用高质量抗锯齿,但结果甚至比仅抗锯齿( QPainter :: Antialiasing )还要糟糕.

深入研究实际上,我目前正在从事我的一个小项目,我想在该项目中创建一个基于复合节点的UI进行图像处理.在线查找此主题总是返回解决方案,其中人们使用简单的 QGraphicsItem s,而节点配置本身部分外包给了 QGraphicsViewer 小部件的外部.您可以使用我上面描述的设计,并根据以后要添加的内容对其进行添加.多个 QGraphicItem 也可以附加到小部件代理.您可以为此感到疯狂,但请记住这会对性能产生影响(请一遍又一遍阅读我在回答开始时链接的博客文章).

I want to put a QWidget into a QGraphicsView and make the widget selectable and movable by using QGraphicsProxyWidget. (This works perfectly for QGraphicsRectItem, QGraphicItem etc.)

This is the code I'm using currently:

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

The widget is displayed correctly, but it is neither movable nor selectable!

Any help would be greatly appreciated ;)

解决方案

I was asking myself the same question as you did here. Before I go on with the solution I came up with after several hours of struggling with this issue I have to mention that in general adding widgets to the scene is not a good idea if you want to have a large amount of those in it. You can read about the issue that come with this here.

Now back on track. Here are a couple of the things that arise as great issues when trying to implement the thing you want to:

  • If you mess with the mouseMove() and mouseHover() of your widget proxy you will screw up the way the widget's UI component behave.
  • At the same time you want it to be selectable and movable so you have to add somehow such features to the widget proxy

How do we do that? Well, directly this seems to be impossible. However I came up with a simple solution that gets the work done - combine a QGraphicsProxyWidget with a QGraphicsItem!

In order to preserve the UI components' functionality in your widget you need to add the proxy as a child to the graphics item. The graphics item (the parent of the proxy) on the other hand will cover the selection and moving part.

Here is a small demo (I do not exclude bugs since this is something that I'm currently researching myself and most of the time I use PyQt instead of 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("---");
}

Here are some screenshots:

Initial view

Two out of three selected

Moving

Rotating

Interacting with widget - using the QComboBox

Interacting with widget - using the QCheckBox

Now due to the nature of QGraphicsItem (as well as QGraphicsProxyWidget) there are some issue the most annoying of which is the overlapping

Overlapping

However overlapping can be relatively easily avoided by using collision detection and basically not allowing overlapping at all. Since QGraphicsProxyWidget can also use the QGraphicsItem function collisionWithItem(...) you can implement a mechanism for handling this situation. Since I'm new to QGraphicsScene and all that (started 2 days ago while answering a question here on SO :D) there might be some sort of integrated machanism in QGraphicsScene itself to handle this.

Distortion In the rotation screenshot you might have noticed that there is some visual weirdness going on with what were previously perfect looking straight lines. These are the so called jaggies. Currently I don't know how to get rid of those. I have tried using high quality antialiasing but the results is even worse than with just antialiasing (QPainter::Antialiasing).

Futher research I am actually currently working on a small project of mine where I want to create a composite-node-based UI for image processing. Looking this topic up online always returned solutions where people were using simple QGraphicsItems and the node configuration itself was partially outsourced to external to the QGraphicsViewer widgets. You can use the design I have described above and add more to it depending on what you want to do later on. Multiple QGraphicItems can also be attached to the widget proxy. You can go crazy with this but remember that there is a performance impact (read the blog post I've linked at the beginning of my answer again and again).

这篇关于使QGraphicsProxyWidget可移动&amp;可选择的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆