放入QGraphicsView后,为什么QGraphicsProxyWidget不能正确调整大小?

问题描述

我有一个QListWidgetQGraphicsView都被子类化以覆盖其某些成员。 我准备了一个最小的可验证示例来显示我的问题。

我可以从QListWidget拖放由QTableWidget表示的特定字段,并将它们拖放到QGraphicsView中,为此,我正在使用QGraphicsProxyWidget如下所示。

重要的是要提到绿色的QGraphicsRectItem用于在QTableWidget周围移动并调整其尺寸。

问题:从QListWidget拖动不是问题。但是进入QGraphicsView一个问题,因为您看到QGraphicsRectItem就在QTableWidget的上方。

为了完整起见,可以在here及以下找到最小的可验证示例:

proxy1

但是,当我触摸QSizegrip的右下角QGraphicsProxyWidget时,QGraphicsRectItem会根据QTableWidgetItem的尺寸进行自我调整:

proxy2

下面是最小的可验证示例:

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc,char *argv[])
{
    QApplication a(argc,argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>

#include "scene.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    Scene *mScene;

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mScene = new Scene;
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);
    ui->graphicsView->setScene(mScene);
    ui->graphicsView->show();
}

MainWindow::~MainWindow()
{
    delete ui;
}

选项列表。h

#ifndef OPTIONLIST_H
#define OPTIONLIST_H


#include <QListWidget>

class OptionList : public QListWidget {
public:
  OptionList(QWidget *parent = nullptr);

protected:
  void startDrag(Qt::DropActions supportedActions);
};

#endif // OPTIONLIST_H

optionlist.cpp

#include "optionlist.h"

#include <QDrag>

OptionList::OptionList(QWidget *parent) : QListWidget(parent) {

  setDragEnabled(true);
  setDropIndicatorShown(true);
  setSelectionMode(QAbstractItemView::SingleSelection);
  setDefaultDropAction(Qt::copyAction);
  setviewmode(QListView::ListMode);

  for (const QString &workspaceTree : {"Images","Path","Connection"}) {
    QListWidgetItem *img = new QListWidgetItem;
    img->setText(workspaceTree);
    img->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable |
                   Qt::ItemIsDragEnabled);
    addItem(img);
  }
}

void OptionList::startDrag(Qt::DropActions supportedActions) {
  if (supportedActions & Qt::copyAction) {
    QList<QListWidgetItem *> m_items = selectedItems();
    if (m_items.isEmpty())
      return;
    QMimeData *data = mimeData(m_items);
    QDrag *drag = new QDrag(this);
    Qpixmap pixmap("/home/Icon_icon.png");
    drag->setpixmap(pixmap);
    drag->setMimeData(data);
    drag->setHotSpot(pixmap.rect().center());
    drag->exec(Qt::copyAction);
  } else
    QListWidget::startDrag(supportedActions);
}

customtablewidget.h

#ifndef CUSTOMTABLEWIDGET_H
#define CUSTOMTABLEWIDGET_H

#include <QTableWidget>
#include <QResizeEvent>

class CustomTableWidget : public QTableWidget
{
    Q_OBJECT

public:
    CustomTableWidget(QWidget *parent = Q_NULLPTR) : QTableWidget(parent){}
    void resizeEvent(QResizeEvent *event);

signals:
    void sizeChanged();

};

#endif // CUSTOMTABLEWIDGET_H

customtablewidget.cpp

#include "customtablewidget.h"

void CustomTableWidget::resizeEvent(QResizeEvent *event)
{
    QTableWidget::resizeEvent(event);

    emit sizeChanged();
}

最后是问题所在:

scene.h

#ifndef SCENE_H
#define SCENE_H

#include <QGraphicsScene>

class Scene : public QGraphicsScene
{
public:
    Scene(QObject *parent = nullptr);

protected:
  void dragenterEvent(QGraphicsSceneDragDropEvent *event);
  void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
  void dropEvent(QGraphicsSceneDragDropEvent *event);
};

#endif // SCENE_H

scene.cpp

#include "scene.h"
#include "customtablewidget.h"

#include <QGraphicsSceneDragDropEvent>
#include <QMimeData>
#include <QTableWidget>
#include <QGraphicsProxyWidget>
#include <QVBoxLayout>
#include <QMetaEnum>
#include <QEvent>
#include <QSizegrip>

Scene::Scene(QObject *parent)
{
    setBackgroundBrush(Qt::lightGray);

}

void Scene::dragenterEvent(QGraphicsSceneDragDropEvent *event) {
  if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
    event->setAccepted(true);
}

void Scene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) {
  if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
    event->setAccepted(true);
}

void Scene::dropEvent(QGraphicsSceneDragDropEvent *event) {
    QByteArray encoded =
      event->mimeData()->data("application/x-qabstractitemmodeldatalist");
    QDataStream stream(&encoded,qiodevice::ReadOnly);

    QStringList rosTables;
    QString newString;

    while (!stream.atEnd()) {
    int row,col;
    QMap<int,QVariant> roleDataMap;
    stream >> row >> col >> roleDataMap;
    rosTables << roleDataMap[Qt::displayRole].toString();
    }
    for (const QString &tableType : rosTables) {
        if(tableType == "Images")
        {
            QPoint initPos(0,0);
            CustomTableWidget *wgt = new CustomTableWidget;
            QGraphicsRectItem *proxyControl = addRect(initPos.x(),initPos.y(),wgt->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
            QSizegrip * sizegrip = new QSizegrip(wgt);
            QHBoxLayout *layout = new QHBoxLayout(wgt);
            //QGraphicsRectItem *proxyControl = addRect(initPos.x(),QBrush(Qt::darkGreen));

            layout->setContentsMargins(0,0);
            layout->addWidget(sizegrip,Qt::AlignRight | Qt::AlignBottom);

            connect(wgt,&CustomTableWidget::sizeChanged,[wgt,proxyControl](){
                proxyControl->setRect(wgt->geometry().adjusted(-10,-10,10,10));
            });

            proxyControl->setPos(initPos.x(),initPos.y());
            proxyControl->setFlag(QGraphicsItem::ItemIsMovable,true);
            proxyControl->setFlag(QGraphicsItem::ItemIsSelectable,true);

            wgt->setColumnCount(2);
            wgt->setRowCount(2);
            for (int ridx = 0 ; ridx < wgt->rowCount() ; ridx++ )
            {
                for (int cidx = 0 ; cidx < wgt->columnCount() ; cidx++)
                {
                    QTableWidgetItem* item = new QTableWidgetItem();
                    item->setText(QString("%1").arg(ridx));
                    wgt->setItem(ridx,cidx,item);
                }
            }
            QGraphicsProxyWidget * const proxy = addWidget(wgt);
            // 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);
        }
    }
}

我对this主题进行了广泛的研究,发现this other post很有用,因为它使我解决QGraphicsView内部的大小调整问题,但并没有解决我的问题QGraphicsView内的小部件的问题。

我提到的帖子继续对所需的小部件进行子类化,并添加一个名为sizeChanged();的附加信号,这就是我所做的。

我还在QSizegrip声明的AND后面移动了以下语句:

        connect(wgt,proxyControl](){
            proxyControl->setRect(wgt->geometry().adjusted(-10,10));
        });

但是它没有引起任何变化,我仍然有错误

感谢您指出正确的方向来解决此问题。

解决方法

原因

您创建proxyControl并以wgt->width()作为宽度:

QGraphicsRectItem *proxyControl = addRect(initPos.x(),initPos.y(),wgt->width(),20,QPen(Qt::black),QBrush(Qt::darkGreen));

但是,那时wgt没有最终的几何形状。

解决方案

设置proxyControl 之后,您完成了wgt

示例

这是我为您写的一个示例,以演示如何实现建议的解决方案。像这样更改您的dropEvent

void Scene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    QByteArray encoded =
            event->mimeData()->data("application/x-qabstractitemmodeldatalist");
    QDataStream stream(&encoded,QIODevice::ReadOnly);

    QStringList rosTables;
    QString newString;

    while (!stream.atEnd()) {
        int row,col;
        QMap<int,QVariant> roleDataMap;
        stream >> row >> col >> roleDataMap;
        rosTables << roleDataMap[Qt::DisplayRole].toString();
    }

    for (const QString &tableType : rosTables) {
        if (tableType == "Images") {
            QPoint initPos(0,0);
            auto *wgt = new CustomTableWidget;
            auto *proxyControl = addRect(0,QBrush(Qt::darkGreen));
            auto *sizeGrip = new QSizeGrip(wgt);
            auto *layout = new QHBoxLayout(wgt);

            layout->setContentsMargins(0,0);
            layout->addWidget(sizeGrip,Qt::AlignRight | Qt::AlignBottom);

            connect(wgt,&CustomTableWidget::sizeChanged,[wgt,proxyControl](){
                proxyControl->setRect(wgt->geometry().adjusted(-10,-10,10,10));
            });

            wgt->setColumnCount(2);
            wgt->setRowCount(2);

            for (int ridx = 0; ridx < wgt->rowCount(); ridx++) {
                for (int cidx = 0; cidx < wgt->columnCount(); cidx++) {
                    auto *item = new QTableWidgetItem();

                    item->setText(QString("%1").arg(ridx));
                    wgt->setItem(ridx,cidx,item);
                }
            }

            auto *const proxy = addWidget(wgt);


            proxy->setPos(initPos.x(),initPos.y()
                          + proxyControl->rect().height());
            proxy->setParentItem(proxyControl);

            proxyControl->setPos(initPos.x(),initPos.y());
            proxyControl->setFlag(QGraphicsItem::ItemIsMovable,true);
            proxyControl->setFlag(QGraphicsItem::ItemIsSelectable,true);
            proxyControl->setRect(wgt->geometry().adjusted(-10,10));
        }
    }
}

注意:使用QGraphicsSceneDragDropEvent::scenePos()正确放置放置的物品。

结果

建议的更改会立即为您提供所需的结果:

Properly positioned handle