Je souhaite créer un widget de montage. J'ai une classe que j'insère dans un widget Timeline
, en utilisant QGraphicsScene
et QGraphicsView
.L'échange de widgets dans QGraphicsView échoue
Si j'insère trois lignes dans l'ordre, je peux les montrer dans le widget. Puis je veux les faire glisser afin de réorganiser les rangées. Par exemple, si je
+----------------------+
| Row 1 |
+----------------------+
| Row 2 |
+----------------------+
| Row 3 |
+----------------------+
Si je fais glisser la ligne 1 entre Row2 et Row3 j'obtenir
+----------------------+
| Row 2 |
+----------------------+
| Row 1 |
+----------------------+
| Row 3 |
+----------------------+
Et ainsi de suite. Lorsque je commence à faire glisser les travaux de réorganisation, mais après quelques glisser (je fais toujours glisser la première rangée entre les deux autres) glisser s'arrête. Je ne peux plus faire glisser la première rangée. Ensuite, je commence à faire glisser une autre ligne, puis cela fonctionne à nouveau.
Ce sont les classes que j'ai utilisé (je ne peux pas utiliser uniquement les fichiers HPP en raison de fichiers moc):
classe 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);
}
classe 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();
}
classe MainWindow:
#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();
}
Quand je commence le programme que j'obtenir:
Puis je fais glisser la ligne rouge entre le vert et le bleu:
Maintenant, je ne peux pas faire glisser la ligne verte, mais si j'en traîne une autre dans une autre position, je peux encore faire glisser la ligne verte.
Qu'est-ce que je fais mal?
Merci de ne pas avoir bien compris la méthode 'prepareGeometryChange()'. Je suis nouveau à cette partie de Qt ... Et aussi merci pour d'autres suggestions précieuses. – Jepessen