I'm using a QTableView
along with a superclassed QSqlTableModel
to display a sqlite
table into Qt and inserting new records dropping a csv file.
I have followed the doc and came up with the example reported below. I have recreated a very light reproducible example to show what is happening to my code, no sanity check or quality code has been intentionally used. It's tested against PySide6
import sys
from qtpy.QtWidgets import QApplication, QTableView, QWidget
from qtpy.QtCore import QModelIndex, QMimeData, Qt
from qtpy.QtSql import QSqlDatabase, QSqlTableModel, QSqlQuery
from pandas import read_csv
def create_table():
# Dummy very simple table
_query_str = """CREATE TABLE MyTable (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Field1 INTEGER,
Field2 TEXT);"""
query = QSqlQuery(db=db, query=_query_str)
query.exec_()
class MyTableModel(QSqlTableModel):
def __init__(self, table_name, db):
QSqlTableModel.__init__(self, db=db)
self.setTable(table_name)
def canDropMimeData(self, data: QMimeData, action: Qt.DropAction, row: int, column: int, parent: QModelIndex) -> bool:
return True # <-- Just for the example
def supportedDropActions(self) -> Qt.DropAction:
return Qt.DropAction.CopyAction | Qt.DropAction.MoveAction | Qt.DropAction.LinkAction
def dropMimeData(self, data: QMimeData, action: Qt.DropAction, row: int, column: int, parent: QModelIndex) -> bool:
csv_filename = data.urls()[0].toLocalFile()
df = read_csv(csv_filename, delimiter=',', header=0)
for _, row in df.iterrows():
record = self.record()
record.remove(0) # <-- Remove the ID field
record.setValue('Field1', row['Field1'].values[0])
record.setValue('Field2', row['Field2'].values[0])
self.insertRecord(-1, record)
if __name__ == '__main__':
# In memory database just for the purpose of the example
db = QSqlDatabase.addDatabase("QSQLITE", ":memory:")
db.open()
if not db.open():
raise "Database not opened"
create_table()
app = QApplication([])
table = QTableView()
model = MyTableModel('MyTable', db)
table.setModel(model)
table.setAcceptDrops(True)
table.show()
sys.exit(app.exec_())
What I get is that canDropMimeData
and supportedDropActions
are correctly called, but (using debug) dropMimeData
is never called
And the below image shows that, even if canDropMimeData
returns True
, the file seems not to be accepted.
I found out that the problem is with QSqlTableModel
. If I use a bare QStandardItemModel
, everything works fine. Any work-around?
By default, item models don't provide drag and drop support.
In order to properly allow that, many aspects have to be checked, including that the flags()
returned by any index that would accept drop must also have the Qt.ItemIsDropEnabled
.
If you want to allow that only for the model (drop on an empty area, not on items), that index would be the root index, aka, an invalid index:
def flags(self, index):
flags = super().flags(index)
if not index.isValid():
flags |= Qt.ItemIsDropEnabled
return flags