Почему QGraphicsProxyWidget не меняет размер должным образом после перехода в QGraphicsView?

У меня есть QListWidget и QGraphicsViewоба являются подклассами, чтобы перезаписать некоторые из своих членов. Я подготовил минимальный проверяемый пример, показывающий мою проблему.

Из QListWidget Я могу перетащить определенное поле, представленное значком QTableWidget и бросьте их в QGraphicsView и для этого я использую QGraphicsProxyWidget подход, как показано ниже.

Важно отметить, что зеленый QGraphicsRectItem он используется для перемещения по QTableWidget а также регулировка его размеров.

Проблема: перетаскивание из QListWidgetне проблема. Но заглянув в QGraphicsView проблема, потому что, как вы видите, QGraphicsRectItem прямо на вершине QTableWidget.

Минимальный проверяемый пример можно найти здесь и ниже для полноты:

прокси1

Однако как только я коснусь QSizeGrip нижний правый угол QGraphicsProxyWidget то QGraphicsRectItem приспособиться к размеру QTableWidgetItem:

прокси2

Ниже минимальный проверяемый пример:

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

И наконец, в чем проблема:

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

Я провел обширное исследование по этой теме и наткнулся на этот другой пост, который был очень полезен, потому что заставил меня решить проблему изменения размера внутри QGraphicsView но это не заставило меня решить проблему падения виджета внутри QGraphicsView.

В сообщении, о котором я упоминал, перейти к подклассу желаемого виджета и добавить дополнительные сигналы, называемые sizeChanged(); и это то, что я сделал.

Я дополнительно переместил следующий оператор после AND перед QSizeGrip декларация:

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

Но это не повлекло за собой никаких изменений, и у меня все еще есть ошибка.

Спасибо, что указали в правильном направлении для решения этой проблемы.

1 ответ

Решение

Причина

Вы создаете 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, 0, 0, 0, QPen(Qt::black),
                                         QBrush(Qt::darkGreen));
            auto *sizeGrip = new QSizeGrip(wgt);
            auto *layout = new QHBoxLayout(wgt);

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

            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, 10, 10));
        }
    }
}

Примечание: используйте QGraphicsSceneDragDropEvent::scenePos() чтобы правильно расположить упавший предмет.

Результат

Предлагаемое изменение сразу дает желаемый результат:

Другие вопросы по тегам