Drag and drop to a Pushbutton
-
@dencla The item view documentation I linked shows how to configure views for both operations. Granted it's for a list view however the technique is the same for a table view.
@SGaist I have discovered an issue With my drop implementation. When I use my CustomSqlTableModel with my CustomSqlTableView everything works correctly. When I use my CustomSqlQueryModel the drop in the CustomSqlTable Model says it dropped the item, but in reality it fails on model->insertrows() call and the model->setData() call, co the data never gets dropped. As far as I can tell the model calls are valid. What am I missing?
I have the text below:
#include <QApplication> #include <QSqlTableModel> #include <QTableView> #include <QMimeData> #include <QMouseEvent> #include <QDrag> #include <QDebug> class CustomSqlTableModel : public QSqlTableModel { public: CustomSqlTableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase()) : QSqlTableModel(parent, db) {} Qt::DropActions supportedDropActions() const override { return Qt::CopyAction | Qt::MoveAction; } Qt::ItemFlags flags(const QModelIndex &index) const override { Qt::ItemFlags defaultFlags = QSqlTableModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; return defaultFlags; } QStringList mimeTypes() const override { return QStringList("application/vnd.text.list"); } QMimeData *mimeData(const QModelIndexList &indexes) const override { QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid()) { QString text = data(index, Qt::DisplayRole).toString(); stream << text; } } mimeData->setData("application/vnd.text.list", encodedData); return mimeData; } }; #include <QMouseEvent> class CustomSqlTableView : public QTableView { public: CustomSqlTableView(QWidget *parent = nullptr) : QTableView(parent) { setAcceptDrops(true); } protected: void mousePressEvent(QMouseEvent *event) override { if (event->button() == Qt::LeftButton) { m_startPos = event->pos(); QModelIndex index = indexAt(event->pos()); if (index.isValid()) { m_draggedItem = model()->data(index, Qt::DisplayRole).toString(); } } QTableView::mousePressEvent(event); } void mouseMoveEvent(QMouseEvent *event) override { if (event->buttons() & Qt::LeftButton) { int distance = (event->pos() - m_startPos).manhattanLength(); if (distance >= QApplication::startDragDistance()) { performDrag(); } } QTableView::mouseMoveEvent(event); } void performDrag() { if (!m_draggedItem.isEmpty()) { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); stream << m_draggedItem; mimeData->setData("application/vnd.text.list", encodedData); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->exec(Qt::CopyAction); } } void dragEnterEvent(QDragEnterEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) event->accept(); else event->ignore(); } void dragMoveEvent(QDragMoveEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) { event->setDropAction(Qt::CopyAction); event->accept(); } else { event->ignore(); } } void dropEvent(QDropEvent *event) override { const QMimeData *mimeData = event->mimeData(); if (!mimeData->hasFormat("application/vnd.text.list")) { event->ignore(); return; } QByteArray encodedData = mimeData->data("application/vnd.text.list"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList items; while (!stream.atEnd()) { QString text; stream >> text; items.append(text); } qDebug() << "Dropped Items:" << items; // Get the model associated with the table view QSqlTableModel *model = Qobject_cast<QSqlTableModel*>(this->model()); if (!model) { QSqlQueryModel *model = Qobject_cast<QSqlQueryModel*>(this->model()); if (!model) { event->ignore(); return; } } // Get the row and column where the items are dropped QModelIndex index = indexAt(event->pos()); int row = index.isValid() ? index.row() : 5; // *int row = index.isValid() ? index.row() : model->rowCount();* int column = index.isValid() ? index.column() : 0; // Insert the dropped items into the model of the target view for (int i = 0; i < items.size(); ++i) { // Ensure we have enough rows and columns for the dropped items if (row + i >= model->rowCount()) { if(model->insertRows(row + i, 1)) qDebug() << "inserted row"; } if (column + i >= model->columnCount()) { if(model->insertColumns(column + i, 1)) qDebug() << "inserted column"; } // Set the data at the appropriate row and column *if(model->setData(model->index(row + i, column + i), items.at(i))) qDebug() << "inserted data";* // Adjust the column index according to your needs } // Emit signal to notify the view of the changes emit model->layoutChanged(); event->accept(); } private: QPoint m_startPos; QString m_draggedItem; };
I have marked the problematic code. Below is how they are called:
CustomSqlTableModel model1; model1.setTable("person"); model1.setEditStrategy(QSqlTableModel::OnManualSubmit); model1.select(); model1.setHeaderData(0, Qt::Horizontal, QObject::tr("ID")); model1.setHeaderData(1, Qt::Horizontal, QObject::tr("First name")); model1.setHeaderData(2, Qt::Horizontal, QObject::tr("Last name")); CustomSqlQueryModel *model2 = new CustomSqlQueryModel; model2->setQuery("SELECT * FROM people "); CustomSqlTableView view1; view1.setModel(&model1); CustomSqlTableView view2; view2.setModel(model2); view1.setGeometry(100, 100, 300, 200); view2.setGeometry(500, 100, 300, 200); view1.show(); view2.show();
I'm sure that it has something to do with the model assignment, but I'm not sure what?
Thanks any help will be appreciated.
-
@SGaist Here is the CustomSqlQueryModel code:
#include <QSqlQueryModel> #include <QMimeData> #include <QDataStream> class CustomSqlQueryModel : public QSqlQueryModel { public: CustomSqlQueryModel(QObject *parent = nullptr) : QSqlQueryModel(parent) {} Qt::DropActions supportedDropActions() const override { return Qt::CopyAction | Qt::MoveAction; } Qt::ItemFlags flags(const QModelIndex &index) const override { Qt::ItemFlags flags = QSqlQueryModel::flags(index); if (index.isValid()) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | flags; } return flags; } QStringList mimeTypes() const override { return QStringList("application/vnd.text.list"); } QMimeData *mimeData(const QModelIndexList &indexes) const override { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid()) { QString text = data(index, Qt::DisplayRole).toString(); stream << text; } } mimeData->setData("application/vnd.text.list", encodedData); return mimeData; } bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override { if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat("application/vnd.text.list")) { return false; } if (column > 0) { return false; } int beginRow; if (row != -1) { beginRow = row; } else if (parent.isValid()) { beginRow = parent.row(); } else { beginRow = rowCount(QModelIndex()); } QByteArray encodedData = data->data("application/vnd.text.list"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList newItems; while (!stream.atEnd()) { QString text; stream >> text; newItems << text; } insertRows(beginRow, newItems.count(), QModelIndex()); foreach (const QString &text, newItems) { QModelIndex idx = index(beginRow, 0, QModelIndex()); setData(idx, text); beginRow++; } return true; } };
-
@SGaist I have discovered an issue With my drop implementation. When I use my CustomSqlTableModel with my CustomSqlTableView everything works correctly. When I use my CustomSqlQueryModel the drop in the CustomSqlTable Model says it dropped the item, but in reality it fails on model->insertrows() call and the model->setData() call, co the data never gets dropped. As far as I can tell the model calls are valid. What am I missing?
I have the text below:
#include <QApplication> #include <QSqlTableModel> #include <QTableView> #include <QMimeData> #include <QMouseEvent> #include <QDrag> #include <QDebug> class CustomSqlTableModel : public QSqlTableModel { public: CustomSqlTableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase()) : QSqlTableModel(parent, db) {} Qt::DropActions supportedDropActions() const override { return Qt::CopyAction | Qt::MoveAction; } Qt::ItemFlags flags(const QModelIndex &index) const override { Qt::ItemFlags defaultFlags = QSqlTableModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; return defaultFlags; } QStringList mimeTypes() const override { return QStringList("application/vnd.text.list"); } QMimeData *mimeData(const QModelIndexList &indexes) const override { QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid()) { QString text = data(index, Qt::DisplayRole).toString(); stream << text; } } mimeData->setData("application/vnd.text.list", encodedData); return mimeData; } }; #include <QMouseEvent> class CustomSqlTableView : public QTableView { public: CustomSqlTableView(QWidget *parent = nullptr) : QTableView(parent) { setAcceptDrops(true); } protected: void mousePressEvent(QMouseEvent *event) override { if (event->button() == Qt::LeftButton) { m_startPos = event->pos(); QModelIndex index = indexAt(event->pos()); if (index.isValid()) { m_draggedItem = model()->data(index, Qt::DisplayRole).toString(); } } QTableView::mousePressEvent(event); } void mouseMoveEvent(QMouseEvent *event) override { if (event->buttons() & Qt::LeftButton) { int distance = (event->pos() - m_startPos).manhattanLength(); if (distance >= QApplication::startDragDistance()) { performDrag(); } } QTableView::mouseMoveEvent(event); } void performDrag() { if (!m_draggedItem.isEmpty()) { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); stream << m_draggedItem; mimeData->setData("application/vnd.text.list", encodedData); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->exec(Qt::CopyAction); } } void dragEnterEvent(QDragEnterEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) event->accept(); else event->ignore(); } void dragMoveEvent(QDragMoveEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) { event->setDropAction(Qt::CopyAction); event->accept(); } else { event->ignore(); } } void dropEvent(QDropEvent *event) override { const QMimeData *mimeData = event->mimeData(); if (!mimeData->hasFormat("application/vnd.text.list")) { event->ignore(); return; } QByteArray encodedData = mimeData->data("application/vnd.text.list"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList items; while (!stream.atEnd()) { QString text; stream >> text; items.append(text); } qDebug() << "Dropped Items:" << items; // Get the model associated with the table view QSqlTableModel *model = Qobject_cast<QSqlTableModel*>(this->model()); if (!model) { QSqlQueryModel *model = Qobject_cast<QSqlQueryModel*>(this->model()); if (!model) { event->ignore(); return; } } // Get the row and column where the items are dropped QModelIndex index = indexAt(event->pos()); int row = index.isValid() ? index.row() : 5; // *int row = index.isValid() ? index.row() : model->rowCount();* int column = index.isValid() ? index.column() : 0; // Insert the dropped items into the model of the target view for (int i = 0; i < items.size(); ++i) { // Ensure we have enough rows and columns for the dropped items if (row + i >= model->rowCount()) { if(model->insertRows(row + i, 1)) qDebug() << "inserted row"; } if (column + i >= model->columnCount()) { if(model->insertColumns(column + i, 1)) qDebug() << "inserted column"; } // Set the data at the appropriate row and column *if(model->setData(model->index(row + i, column + i), items.at(i))) qDebug() << "inserted data";* // Adjust the column index according to your needs } // Emit signal to notify the view of the changes emit model->layoutChanged(); event->accept(); } private: QPoint m_startPos; QString m_draggedItem; };
I have marked the problematic code. Below is how they are called:
CustomSqlTableModel model1; model1.setTable("person"); model1.setEditStrategy(QSqlTableModel::OnManualSubmit); model1.select(); model1.setHeaderData(0, Qt::Horizontal, QObject::tr("ID")); model1.setHeaderData(1, Qt::Horizontal, QObject::tr("First name")); model1.setHeaderData(2, Qt::Horizontal, QObject::tr("Last name")); CustomSqlQueryModel *model2 = new CustomSqlQueryModel; model2->setQuery("SELECT * FROM people "); CustomSqlTableView view1; view1.setModel(&model1); CustomSqlTableView view2; view2.setModel(model2); view1.setGeometry(100, 100, 300, 200); view2.setGeometry(500, 100, 300, 200); view1.show(); view2.show();
I'm sure that it has something to do with the model assignment, but I'm not sure what?
Thanks any help will be appreciated.
@dencla said in Drag and drop to a Pushbutton:
but in reality it fails on model->insertrows() call and the model->setData() call, co the data never gets dropped.
Are you saying if you get rid of all drop stuff etc. you cannot call
insertrows()
with successful return result on yourCustomSqlTableModel
?Back a while
class CustomSqlTableModel : public QSqlTableModel {
now
class CustomSqlQueryModel : public QSqlQueryModel {
Have you changed this without saying anything?? A
QSqlTableModel
is editable, aQSqlQueryModel
is not, now I don't know which you have. -
@dencla said in Drag and drop to a Pushbutton:
but in reality it fails on model->insertrows() call and the model->setData() call, co the data never gets dropped.
Are you saying if you get rid of all drop stuff etc. you cannot call
insertrows()
with successful return result on yourCustomSqlTableModel
?Back a while
class CustomSqlTableModel : public QSqlTableModel {
now
class CustomSqlQueryModel : public QSqlQueryModel {
Have you changed this without saying anything?? A
QSqlTableModel
is editable, aQSqlQueryModel
is not, now I don't know which you have.@JonB I have both, it is when I have created the view with a queryModel that it does not work. Are you saying that it is because it is a query model that it is not working? It allows me to create a call to a query model to insert a row.
QSqlTableModel tmodel; tmodel.insertRows(0,1,QModelIndex()); QSqlQueryModel qmodel; qmodel.insertRows(0,1,QModelIndex());
It would make sense if that is the case, so why can I create the call.
-
@JonB I have both, it is when I have created the view with a queryModel that it does not work. Are you saying that it is because it is a query model that it is not working? It allows me to create a call to a query model to insert a row.
QSqlTableModel tmodel; tmodel.insertRows(0,1,QModelIndex()); QSqlQueryModel qmodel; qmodel.insertRows(0,1,QModelIndex());
It would make sense if that is the case, so why can I create the call.
@dencla
insertRows()
returns abool
result, why don't you print that out? Being able to call something from C++ versus what result it might return if it does not work are two different things.https://doc.qt.io/qt-6/qabstractitemmodel.html#insertRows
On models that support this, inserts count rows into the model before the given row.
Returns true if the rows were successfully inserted; otherwise returns false.
https://doc.qt.io/qt-6/qsqlquerymodel.html
The QSqlQueryModel class provides a read-only data model for SQL result sets.
https://doc.qt.io/qt-6/qsqltablemodel.html
The QSqlTableModel class provides an editable data model for a single database table.
What would be the point of alteration or drag and drop into for a
QSqlQueryModel
? -
@dencla
insertRows()
returns abool
result, why don't you print that out? Being able to call something from C++ versus what result it might return if it does not work are two different things.https://doc.qt.io/qt-6/qabstractitemmodel.html#insertRows
On models that support this, inserts count rows into the model before the given row.
Returns true if the rows were successfully inserted; otherwise returns false.
https://doc.qt.io/qt-6/qsqlquerymodel.html
The QSqlQueryModel class provides a read-only data model for SQL result sets.
https://doc.qt.io/qt-6/qsqltablemodel.html
The QSqlTableModel class provides an editable data model for a single database table.
What would be the point of alteration or drag and drop into for a
QSqlQueryModel
? -
@dencla
insertRows()
returns abool
result, why don't you print that out? Being able to call something from C++ versus what result it might return if it does not work are two different things.https://doc.qt.io/qt-6/qabstractitemmodel.html#insertRows
On models that support this, inserts count rows into the model before the given row.
Returns true if the rows were successfully inserted; otherwise returns false.
https://doc.qt.io/qt-6/qsqlquerymodel.html
The QSqlQueryModel class provides a read-only data model for SQL result sets.
https://doc.qt.io/qt-6/qsqltablemodel.html
The QSqlTableModel class provides an editable data model for a single database table.
What would be the point of alteration or drag and drop into for a
QSqlQueryModel
? -
@JonB I think that you are correct. It does return false for the qmodel. so why does the query model allow calls to edit it?
@dencla said in Drag and drop to a Pushbutton:
so why does the query model allow calls to edit it?
Because QSqlQueryModel is derived from QAbstractTableModel if you are asking about compilation. At runtime we have already said it returns an unsuccessful result if you call it. Some model types allow editing, some do not.
When I create a ui->CustomTableView nothing is displayed, but when I create a normal tableView it does display the database.
I don't know what this means. If a
QTableView
works but aCustomTableView
derived from that does not look at your code differences in the custom view. I don't know what this has to do withQSqlQueryModel
vsQSqlTableModel
, nor with drag and drop. -
@dencla said in Drag and drop to a Pushbutton:
so why does the query model allow calls to edit it?
Because QSqlQueryModel is derived from QAbstractTableModel if you are asking about compilation. At runtime we have already said it returns an unsuccessful result if you call it. Some model types allow editing, some do not.
When I create a ui->CustomTableView nothing is displayed, but when I create a normal tableView it does display the database.
I don't know what this means. If a
QTableView
works but aCustomTableView
derived from that does not look at your code differences in the custom view. I don't know what this has to do withQSqlQueryModel
vsQSqlTableModel
, nor with drag and drop.@JonB It does not have a direct reason for drag and drop. When I was working on my CustomSqlTableView drag and drop works when I called it directly in the example but when I tried to promote it to a CustomSqlTableView it does not display in a ui->table view, but a CustomQueryModel does, but does not work.
So I'm back to figuring out why, so I can drag to a pushbutton. Which is why i need the display.
Thanks for you help. It is taking me farther down the road.
-
@dencla said in Drag and drop to a Pushbutton:
so why does the query model allow calls to edit it?
Because QSqlQueryModel is derived from QAbstractTableModel if you are asking about compilation. At runtime we have already said it returns an unsuccessful result if you call it. Some model types allow editing, some do not.
When I create a ui->CustomTableView nothing is displayed, but when I create a normal tableView it does display the database.
I don't know what this means. If a
QTableView
works but aCustomTableView
derived from that does not look at your code differences in the custom view. I don't know what this has to do withQSqlQueryModel
vsQSqlTableModel
, nor with drag and drop. -
@dencla said in Drag and drop to a Pushbutton:
so why does the query model allow calls to edit it?
Because QSqlQueryModel is derived from QAbstractTableModel if you are asking about compilation. At runtime we have already said it returns an unsuccessful result if you call it. Some model types allow editing, some do not.
When I create a ui->CustomTableView nothing is displayed, but when I create a normal tableView it does display the database.
I don't know what this means. If a
QTableView
works but aCustomTableView
derived from that does not look at your code differences in the custom view. I don't know what this has to do withQSqlQueryModel
vsQSqlTableModel
, nor with drag and drop.@JonB I can Drag and drop from a ui->tableView, now I need to drop to a CustomPushButton. I have promoted a ui->QPushButton to a ui->CustomPushButton. When I drag the drop indicator goes away and the drop fails. I am not sure what triggers the event.
Below is the code for the CustomPushButton::
#include <QPushButton> #include <QMimeData> #include <QDataStream> #include <QDropEvent> class CustomPushButton : public QPushButton { public: CustomPushButton(QWidget *parent = nullptr) : QPushButton(parent) { setAcceptDrops(true); } // Override the virtual method to handle drop events void dropEvent(QDropEvent *event) override { const QMimeData *mimeData = event->mimeData(); if (mimeData && mimeData->hasText()) { setText(mimeData->text()); event->acceptProposedAction(); } } }; Any Ideas
-
@JonB I can Drag and drop from a ui->tableView, now I need to drop to a CustomPushButton. I have promoted a ui->QPushButton to a ui->CustomPushButton. When I drag the drop indicator goes away and the drop fails. I am not sure what triggers the event.
Below is the code for the CustomPushButton::
#include <QPushButton> #include <QMimeData> #include <QDataStream> #include <QDropEvent> class CustomPushButton : public QPushButton { public: CustomPushButton(QWidget *parent = nullptr) : QPushButton(parent) { setAcceptDrops(true); } // Override the virtual method to handle drop events void dropEvent(QDropEvent *event) override { const QMimeData *mimeData = event->mimeData(); if (mimeData && mimeData->hasText()) { setText(mimeData->text()); event->acceptProposedAction(); } } }; Any Ideas
-
@dencla you also need to implement the dragEnterEvent and dragMoveEvent methods as they handle the dragging until you drop.
-
@dencla you also need to implement the dragEnterEvent and dragMoveEvent methods as they handle the dragging until you drop.
@SGaist This is what I have now. It still does not register the drag on the CustomPushButton.
#include <QPushButton> #include <QMimeData> #include <QDataStream> #include <QDropEvent> #include <QDebug> class CustomPushButton : public QPushButton { public: CustomPushButton(QWidget *parent = nullptr) : QPushButton(parent) { setAcceptDrops(true); // Enable drop events for the push button } protected: void dragEnterEvent(QDragEnterEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) event->accept(); else event->ignore(); } void dragMoveEvent(QDragMoveEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) { event->setDropAction(Qt::CopyAction); event->accept(); } else { event->ignore(); } } void dropEvent(QDropEvent *event) override { const QMimeData *mimeData = event->mimeData(); if (!mimeData->hasFormat("application/vnd.text.list")) { event->ignore(); return; } QByteArray encodedData = mimeData->data("application/vnd.text.list"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList items; while (!stream.atEnd()) { QString text; stream >> text; items.append(text); } qDebug() << "Dropped Items:" << items; // Perform actions based on the dropped items if(items.size() > 0) { setText(items.at(0)); qDebug() << "inserted data"; } // Call the base class implementation to handle other drop events QPushButton::dropEvent(event); } };
-
@SGaist This is what I have now. It still does not register the drag on the CustomPushButton.
#include <QPushButton> #include <QMimeData> #include <QDataStream> #include <QDropEvent> #include <QDebug> class CustomPushButton : public QPushButton { public: CustomPushButton(QWidget *parent = nullptr) : QPushButton(parent) { setAcceptDrops(true); // Enable drop events for the push button } protected: void dragEnterEvent(QDragEnterEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) event->accept(); else event->ignore(); } void dragMoveEvent(QDragMoveEvent *event) override { if (event->mimeData()->hasFormat("application/vnd.text.list")) { event->setDropAction(Qt::CopyAction); event->accept(); } else { event->ignore(); } } void dropEvent(QDropEvent *event) override { const QMimeData *mimeData = event->mimeData(); if (!mimeData->hasFormat("application/vnd.text.list")) { event->ignore(); return; } QByteArray encodedData = mimeData->data("application/vnd.text.list"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList items; while (!stream.atEnd()) { QString text; stream >> text; items.append(text); } qDebug() << "Dropped Items:" << items; // Perform actions based on the dropped items if(items.size() > 0) { setText(items.at(0)); qDebug() << "inserted data"; } // Call the base class implementation to handle other drop events QPushButton::dropEvent(event); } };
-
-
@SGaist I recompiled the project in Linux and it works.
I don't know why it doesn't work on my apple computer.Thanks