为什么 QGraphicsProxyWidget 在放入 QGraphicsView 后不能正确调整大小?
Why QGraphicsProxyWidget does not resize properly after dropping into QGraphicsView?
我有一个 QListWidget
和一个 QGraphicsView
都被子类化以覆盖它们的一些成员。
我准备了一个最小的可验证示例来展示我遇到的问题。
从 QListWidget
我可以拖放由 QTableWidget
表示的特定字段并将它们放到 QGraphicsView
中,为了做到这一点,我使用 QGraphicsProxyWidget
方法如下所示。
重要的是要提到绿色 QGraphicsRectItem
它用于在 QTableWidget
周围移动以及调整其尺寸。
问题:从QListWidget
拖拽没有问题。但是放入 QGraphicsView
是一个问题,因为如您所见,QGraphicsRectItem
就在 QTableWidget
.
之上
为了完整性,可以在 here 和下面找到最小的可验证示例:
但是只要我触摸 QGraphicsProxyWidget
的 QSizeGrip
右下角,QGraphicsRectItem
就会根据 QTableWidgetItem
:
下面是最小的可验证示例:
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);
}
}
}
我对 topic and came across 进行了广泛的研究,这非常有用,因为它让我解决了 QGraphicsView
内的调整大小问题,但它并没有让我解决 QGraphicsView
内的小部件掉落问题=21=].
我提到的 post 继续对所需的小部件进行子类化并添加一个名为 sizeChanged();
的附加信号,这就是我所做的。
我还在 QSizeGrip
声明之前的 AND 之后移动了以下语句:
connect(wgt, &CustomTableWidget::sizeChanged, [wgt, proxyControl](){
proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 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, 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()
正确放置掉落的物品。
结果
建议的更改会立即为您提供所需的结果:
我有一个 QListWidget
和一个 QGraphicsView
都被子类化以覆盖它们的一些成员。
我准备了一个最小的可验证示例来展示我遇到的问题。
从 QListWidget
我可以拖放由 QTableWidget
表示的特定字段并将它们放到 QGraphicsView
中,为了做到这一点,我使用 QGraphicsProxyWidget
方法如下所示。
重要的是要提到绿色 QGraphicsRectItem
它用于在 QTableWidget
周围移动以及调整其尺寸。
问题:从QListWidget
拖拽没有问题。但是放入 QGraphicsView
是一个问题,因为如您所见,QGraphicsRectItem
就在 QTableWidget
.
为了完整性,可以在 here 和下面找到最小的可验证示例:
但是只要我触摸 QGraphicsProxyWidget
的 QSizeGrip
右下角,QGraphicsRectItem
就会根据 QTableWidgetItem
:
下面是最小的可验证示例:
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
内的小部件掉落问题=21=].
我提到的 post 继续对所需的小部件进行子类化并添加一个名为 sizeChanged();
的附加信号,这就是我所做的。
我还在 QSizeGrip
声明之前的 AND 之后移动了以下语句:
connect(wgt, &CustomTableWidget::sizeChanged, [wgt, proxyControl](){
proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 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, 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()
正确放置掉落的物品。
结果
建议的更改会立即为您提供所需的结果: