2017-02-21 21 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