Categories
Mastering Development

Why QGraphicsProxyWidget does not resize properly after dropping into QGraphicsView?

I have a QListWidget and a QGraphicsView both subclassed to overwrite some of their members.
I prepared a minimal verifiable example showing the problem I have.

From the QListWidget I can drag and drop specific field represented by a QTableWidget and drop them into a QGraphicsView and in order to do that I am using a QGraphicsProxyWidget approach as shown below.

It is important to mention that the green QGraphicsRectItem it is used to move around the QTableWidget as well as adjusting its dimension.

The problem: dragging from the QListWidget is not a problem. But dropping into QGraphicsView is a problem because as you see the QGraphicsRectItem is right on top of the QTableWidget.

The minimal verifiable example can be found here and below for completeness:

proxy1

However as soon as I touch the QSizeGrip bottom right of the QGraphicsProxyWidget the QGraphicsRectItem adjust itself with the dimension of the QTableWidgetItem:

proxy2

Below the minimal verifiable example:

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

optionlist.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();
}

And finally where the problem is:

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(), initPos.y(), wgt->width(), 20, QPen(Qt::black), QBrush(Qt::darkGreen));

            layout->setContentsMargins(0, 0, 0, 0);
            layout->addWidget(sizeGrip, 0, 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);
        }
    }
}

I did extensive research about this topic and came across this other post which was very useful because made me solve the resizing issue inside the QGraphicsView but it didn’t make me solve the drop problem of the widget inside the QGraphicsView.

The post I mentioned proceed to subclass the widget desired and add an additional signals called sizeChanged(); and that is what I did.

I additionally moved the following statement after AND before the QSizeGrip declaration:

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

But it didn’t cause any change and I still have the bug.

Thanks for pointing in the right direction for solving this problem.

Leave a Reply

Your email address will not be published. Required fields are marked *