QGraphicsView 中的交换小部件失败
Swap widgets in QGraphicsView fails
我想创建一个时间轴小部件。
我有一个 Row
class 插入 Timeline
小部件,使用 QGraphicsScene
和 QGraphicsView
.
如果我按顺序插入三行,我可以在小部件中显示它们。
然后我想拖动它们以重新排序行。例如,如果我有
+----------------------+
| Row 1 |
+----------------------+
| Row 2 |
+----------------------+
| Row 3 |
+----------------------+
如果我将第 1 行拖到第 2 行和第 3 行之间,我会得到
+----------------------+
| Row 2 |
+----------------------+
| Row 1 |
+----------------------+
| Row 3 |
+----------------------+
以此类推
当我开始拖动时重新排序有效,但经过一些拖动(我总是拖动其他两行之间的第一行)拖动停止。我不能再拖第一行了。然后我开始拖动另一行然后它又起作用了。
这些是我用过的 classes(由于 moc 文件,我不能只使用 hpp 文件):
行class:
#ifndef ROW_HPP_
#define ROW_HPP_
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QBrush>
#include <QObject>
const qreal TopZValue{ std::numeric_limits<qreal>::max() };
class Row : public QObject, public QGraphicsItem {
Q_OBJECT
public:
Row();
virtual ~Row() = default;
void setBrush(const QBrush& b);
void setOrigin(int x, int y);
void setHeight(int height);
int getHeight() const;
const QPoint& getOrigin() const;
public:
virtual QRectF boundingRect() const override;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
signals:
void originUpdated(const QPoint& origin);
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
private:
void drawBackground(QPainter* painter);
private:
QBrush m_background;
int m_height = 0;
int m_width = 0;
QPoint m_origin;
qreal m_zValueWhenDragged = 0.0;
};
#endif // !ROW_HPP_
// CPP file
#include "Row.hpp"
Row::Row() :
QGraphicsItem(nullptr) {
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}
void Row::setBrush(const QBrush& b) {
m_background = b;
}
void Row::setOrigin(int x, int y) {
m_origin.rx() = x;
m_origin.ry() = y;
setPos(0, 0);
}
void Row::setHeight(int height) {
m_height = height;
}
int Row::getHeight() const {
return m_height;
}
const QPoint& Row::getOrigin() const {
return m_origin;
}
QRectF Row::boundingRect() const {
return QRectF(m_origin.x(), m_origin.y(), m_width, m_height);
}
void Row::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
Q_UNUSED(option)
Q_UNUSED(widget)
drawBackground(painter);
QGraphicsView *view = scene()->views().first();
m_width = view->width();
painter->drawRect(m_origin.x(), m_origin.y(), m_width, m_height);
}
void Row::mousePressEvent(QGraphicsSceneMouseEvent *event) {
m_zValueWhenDragged = zValue();
QGraphicsItem::mousePressEvent(event);
}
void Row::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
setZValue(TopZValue);
QGraphicsItem::mouseMoveEvent(event);
}
void Row::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
setZValue(m_zValueWhenDragged);
QPoint newOrigin(0, m_origin.y() + scenePos().toPoint().y());
m_origin = newOrigin;
emit originUpdated(newOrigin);
QGraphicsItem::mouseReleaseEvent(event);
}
void Row::drawBackground(QPainter* painter) {
auto brush = painter->brush();
auto width = painter->viewport().width();
painter->setBrush(m_background);
painter->drawRect(m_origin.x(), m_origin.y(), width, m_height);
painter->setBrush(brush);
}
时间轴class:
#ifndef TIMELINE_HPP_
#define TIMELINE_HPP_
#include "Row.hpp"
#include <QHBoxLayout>
#include <QMainWindow>
#include <QWidget>
class Timeline : public QWidget {
Q_OBJECT
public:
Timeline(QWidget* parent = nullptr);
virtual ~Timeline() = default;
size_t addRow(Row* row);
size_t getNumberOfRows() const;
private slots:
void setRowOrigin(const QPoint& origin);
private:
void orderRowsOriginsByTheirPosition();
private:
QGraphicsView* m_view;
QGraphicsScene* m_scene;
QHBoxLayout* m_layout;
std::vector<Row*> m_rows;
};
#endif //!TIMELINE_HPP_
// CPP file
#include "Timeline.hpp"
Timeline::Timeline(QWidget* parent) :
QWidget(parent) {
m_view = new QGraphicsView(this);
m_scene = new QGraphicsScene(this);
m_layout = new QHBoxLayout(this);
m_layout->addWidget(m_view);
m_view->setScene(m_scene);
m_view->setAlignment(Qt::AlignTop | Qt::AlignLeft);
}
size_t Timeline::addRow(Row* row) {
m_rows.push_back(row);
m_scene->addItem(row);
orderRowsOriginsByTheirPosition();
connect(row, &Row::originUpdated, this, &Timeline::setRowOrigin);
return getNumberOfRows();
}
size_t Timeline::getNumberOfRows() const {
return m_rows.size();
}
void Timeline::setRowOrigin(const QPoint& origin) {
Q_UNUSED(origin)
orderRowsOriginsByTheirPosition();
}
void Timeline::orderRowsOriginsByTheirPosition() {
int offsetY = 0;
std::sort(m_rows.begin(), m_rows.end(), [] (Row* left, Row* right) { return left->getOrigin().y() < right->getOrigin().y();});
for (auto& it : m_rows) {
it->setOrigin(0, offsetY);
offsetY += it->getHeight();
}
m_scene->update();
m_view->update();
}
主窗口class:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "Timeline.hpp"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow() = default;
private:
Timeline* m_timeline;
};
#endif // MAINWINDOW_H
// CPP file
#include "MainWindow.hpp"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_timeline(new Timeline(this)) {
setCentralWidget(m_timeline);
setMinimumSize(300, 200);
auto row1 = new Row();
row1->setHeight(40);
m_timeline->addRow(row1);
row1->setBrush(Qt::red);
auto row2 = new Row();
row2->setHeight(30);
m_timeline->addRow(row2);
row2->setBrush(Qt::blue);
auto row3 = new Row();
row3->setHeight(50);
m_timeline->addRow(row3);
row3->setBrush(Qt::green);
}
main.cpp
#include "Row.hpp"
#include "MainWindow.hpp"
#include <QCoreApplication>
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
当我启动程序时,我得到:
然后我将红色行拖到绿色和蓝色之间:
现在不能拖绿行,但是如果我在另一个位置拖另一个,我可以再拖绿行。
我做错了什么?
当您更改 Row::m_origin
时,您更改了由 Row::boundingRect()
编辑的值 return 而无需调用 QGraphicsItem::prepareGeometryChange()
.
但 Qt 文档指出:
If you want to change the item's bounding rectangle, you must first
call prepareGeometryChange(). This notifies the scene of the imminent
change, so that it can update its item geometry index; otherwise, the
scene will be unaware of the item's new geometry, and the results are
undefined (typically, rendering artifacts are left within the view).
你也可以用更简单的代码来实现。
- 删除
m_origin
并使用 QGraphicsItem::pos()
和 QGraphicsItem::setPos()
。
Row::boundingRect()
现在可以简单地 return QRectF(0.0, 0.0, m_width, m_height)
并且您仅在更改 m_width
或 m_height
.[=41= 时才调用 QGraphicsItem::prepareGeometryChange()
]
Row::paint()
也是如此
这样您就可以利用 QGraphicsScene 定位系统,而无需处理几何变化。
在一边而不是 class Row : public QObject, public QGraphicsItem
你可以写 class Row : public QGraphicsObject
.
我想创建一个时间轴小部件。
我有一个 Row
class 插入 Timeline
小部件,使用 QGraphicsScene
和 QGraphicsView
.
如果我按顺序插入三行,我可以在小部件中显示它们。
然后我想拖动它们以重新排序行。例如,如果我有
+----------------------+
| Row 1 |
+----------------------+
| Row 2 |
+----------------------+
| Row 3 |
+----------------------+
如果我将第 1 行拖到第 2 行和第 3 行之间,我会得到
+----------------------+
| Row 2 |
+----------------------+
| Row 1 |
+----------------------+
| Row 3 |
+----------------------+
以此类推
当我开始拖动时重新排序有效,但经过一些拖动(我总是拖动其他两行之间的第一行)拖动停止。我不能再拖第一行了。然后我开始拖动另一行然后它又起作用了。
这些是我用过的 classes(由于 moc 文件,我不能只使用 hpp 文件):
行class:
#ifndef ROW_HPP_
#define ROW_HPP_
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QBrush>
#include <QObject>
const qreal TopZValue{ std::numeric_limits<qreal>::max() };
class Row : public QObject, public QGraphicsItem {
Q_OBJECT
public:
Row();
virtual ~Row() = default;
void setBrush(const QBrush& b);
void setOrigin(int x, int y);
void setHeight(int height);
int getHeight() const;
const QPoint& getOrigin() const;
public:
virtual QRectF boundingRect() const override;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
signals:
void originUpdated(const QPoint& origin);
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
private:
void drawBackground(QPainter* painter);
private:
QBrush m_background;
int m_height = 0;
int m_width = 0;
QPoint m_origin;
qreal m_zValueWhenDragged = 0.0;
};
#endif // !ROW_HPP_
// CPP file
#include "Row.hpp"
Row::Row() :
QGraphicsItem(nullptr) {
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}
void Row::setBrush(const QBrush& b) {
m_background = b;
}
void Row::setOrigin(int x, int y) {
m_origin.rx() = x;
m_origin.ry() = y;
setPos(0, 0);
}
void Row::setHeight(int height) {
m_height = height;
}
int Row::getHeight() const {
return m_height;
}
const QPoint& Row::getOrigin() const {
return m_origin;
}
QRectF Row::boundingRect() const {
return QRectF(m_origin.x(), m_origin.y(), m_width, m_height);
}
void Row::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
Q_UNUSED(option)
Q_UNUSED(widget)
drawBackground(painter);
QGraphicsView *view = scene()->views().first();
m_width = view->width();
painter->drawRect(m_origin.x(), m_origin.y(), m_width, m_height);
}
void Row::mousePressEvent(QGraphicsSceneMouseEvent *event) {
m_zValueWhenDragged = zValue();
QGraphicsItem::mousePressEvent(event);
}
void Row::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
setZValue(TopZValue);
QGraphicsItem::mouseMoveEvent(event);
}
void Row::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
setZValue(m_zValueWhenDragged);
QPoint newOrigin(0, m_origin.y() + scenePos().toPoint().y());
m_origin = newOrigin;
emit originUpdated(newOrigin);
QGraphicsItem::mouseReleaseEvent(event);
}
void Row::drawBackground(QPainter* painter) {
auto brush = painter->brush();
auto width = painter->viewport().width();
painter->setBrush(m_background);
painter->drawRect(m_origin.x(), m_origin.y(), width, m_height);
painter->setBrush(brush);
}
时间轴class:
#ifndef TIMELINE_HPP_
#define TIMELINE_HPP_
#include "Row.hpp"
#include <QHBoxLayout>
#include <QMainWindow>
#include <QWidget>
class Timeline : public QWidget {
Q_OBJECT
public:
Timeline(QWidget* parent = nullptr);
virtual ~Timeline() = default;
size_t addRow(Row* row);
size_t getNumberOfRows() const;
private slots:
void setRowOrigin(const QPoint& origin);
private:
void orderRowsOriginsByTheirPosition();
private:
QGraphicsView* m_view;
QGraphicsScene* m_scene;
QHBoxLayout* m_layout;
std::vector<Row*> m_rows;
};
#endif //!TIMELINE_HPP_
// CPP file
#include "Timeline.hpp"
Timeline::Timeline(QWidget* parent) :
QWidget(parent) {
m_view = new QGraphicsView(this);
m_scene = new QGraphicsScene(this);
m_layout = new QHBoxLayout(this);
m_layout->addWidget(m_view);
m_view->setScene(m_scene);
m_view->setAlignment(Qt::AlignTop | Qt::AlignLeft);
}
size_t Timeline::addRow(Row* row) {
m_rows.push_back(row);
m_scene->addItem(row);
orderRowsOriginsByTheirPosition();
connect(row, &Row::originUpdated, this, &Timeline::setRowOrigin);
return getNumberOfRows();
}
size_t Timeline::getNumberOfRows() const {
return m_rows.size();
}
void Timeline::setRowOrigin(const QPoint& origin) {
Q_UNUSED(origin)
orderRowsOriginsByTheirPosition();
}
void Timeline::orderRowsOriginsByTheirPosition() {
int offsetY = 0;
std::sort(m_rows.begin(), m_rows.end(), [] (Row* left, Row* right) { return left->getOrigin().y() < right->getOrigin().y();});
for (auto& it : m_rows) {
it->setOrigin(0, offsetY);
offsetY += it->getHeight();
}
m_scene->update();
m_view->update();
}
主窗口class:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "Timeline.hpp"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow() = default;
private:
Timeline* m_timeline;
};
#endif // MAINWINDOW_H
// CPP file
#include "MainWindow.hpp"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_timeline(new Timeline(this)) {
setCentralWidget(m_timeline);
setMinimumSize(300, 200);
auto row1 = new Row();
row1->setHeight(40);
m_timeline->addRow(row1);
row1->setBrush(Qt::red);
auto row2 = new Row();
row2->setHeight(30);
m_timeline->addRow(row2);
row2->setBrush(Qt::blue);
auto row3 = new Row();
row3->setHeight(50);
m_timeline->addRow(row3);
row3->setBrush(Qt::green);
}
main.cpp
#include "Row.hpp"
#include "MainWindow.hpp"
#include <QCoreApplication>
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
当我启动程序时,我得到:
然后我将红色行拖到绿色和蓝色之间:
现在不能拖绿行,但是如果我在另一个位置拖另一个,我可以再拖绿行。
我做错了什么?
当您更改 Row::m_origin
时,您更改了由 Row::boundingRect()
编辑的值 return 而无需调用 QGraphicsItem::prepareGeometryChange()
.
但 Qt 文档指出:
If you want to change the item's bounding rectangle, you must first call prepareGeometryChange(). This notifies the scene of the imminent change, so that it can update its item geometry index; otherwise, the scene will be unaware of the item's new geometry, and the results are undefined (typically, rendering artifacts are left within the view).
你也可以用更简单的代码来实现。
- 删除
m_origin
并使用QGraphicsItem::pos()
和QGraphicsItem::setPos()
。 Row::boundingRect()
现在可以简单地 returnQRectF(0.0, 0.0, m_width, m_height)
并且您仅在更改m_width
或m_height
.[=41= 时才调用QGraphicsItem::prepareGeometryChange()
]Row::paint()
也是如此
这样您就可以利用 QGraphicsScene 定位系统,而无需处理几何变化。
在一边而不是 class Row : public QObject, public QGraphicsItem
你可以写 class Row : public QGraphicsObject
.