2017-02-21 46 views
0

我想创建时间轴小部件。 我有一个类,我使用QGraphicsSceneQGraphicsView插入Timeline小部件。QGraphicsView中的交换小部件失败

如果我按顺序插入三行,我可以在小部件中显示它们。

然后我想拖动它们以重新排列行。举例来说,如果我有

+----------------------+ 
| Row 1    | 
+----------------------+ 
| Row 2    | 
+----------------------+ 
| Row 3    | 
+----------------------+ 

如果我拖行2和ROW3之间的第1行我获得

+----------------------+ 
| Row 2    | 
+----------------------+ 
| Row 1    | 
+----------------------+ 
| Row 3    | 
+----------------------+ 

等。

当我开始拖动重新排序的作品,但拖动一些后(我拖动其他两个之间的第一行)拖动停止。我不能拖动第一行。然后,我开始拖动另一行,然后再次运行。

这些都是我所使用的类(我不能使用,只是因为MOC文件的HPP文件):

Row类:

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

Timeline类:

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

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

当我开始计划我获得:

enter image description here

然后我拖动绿色和蓝色之间的红色行:

enter image description here

现在我不能拖动绿色的行,但是如果我将另一个拖动到另一个位置,我可以再次拖动绿色的行。

我在做什么错了?

回答

1

当您更改Row::m_origin时,您更改Row::boundingRect()返回的值,而无需致电QGraphicsItem::prepareGeometryChange()

但Qt文档指出:

如果你想改变项目的边框,您必须首先 调用prepareGeometryChange()。这通知即将发生的 更改的场景,以便它可以更新其项目几何索引;否则, 场景将不知道该项目的新几何体,并且结果为 未定义(通常,渲染工件留在视图中)。


此外,你可以用一个简单的代码做到这一点。

  • 删除m_origin并使用QGraphicsItem::pos()QGraphicsItem::setPos()
  • Row::boundingRect()现在可以简单地返回QRectF(0.0, 0.0, m_width, m_height),并且您只有在更改m_widthm_height时才致电QGraphicsItem::prepareGeometryChange()
  • 也是一样Row::paint()

这样,您就可以利用QGraphicsScene定位系统,而无需在处理几何形状的变化。


在一个侧面的而不是替代class Row : public QObject, public QGraphicsItem你可以写class Row : public QGraphicsObject

+0

谢谢我没有很好地理解'prepareGeometryChange()'方法。我是Qt的这个新手......并且还要感谢其他宝贵的建议。 – Jepessen