Ich bin wahrscheinlich zu spät mit der Antwort zu kommen, aber die Frage selbst war interessant für mich, so grabe ich hinein und war in der Lage, eine Lösung zu finden.
Wie sich herausstellte, gibt es mehrere Fehler, den leeren Zustand des Tabellenmodells so zu implementieren, dass es immer noch die Spaltenüberschriften anzeigt und es einem erlaubt, sich selbst zu löschen.
Die erste Sache ist, dass Qt scheint nicht die Spaltenüberschriften zu zeichnen, wenn columnCount
Methode 0 für die ungültige QModelIndex
zurückgeben. Es scheint wirklich nicht zu stören headerData
Methode in diesem Fall aufzurufen. Die Lösung ist, niemals 0 von columnCount
mit ungültigem QModelIndex
zurückzugeben, selbst wenn die Anzahl der Zeilen tatsächlich 0 ist und unsere zugrunde liegende Datenstruktur ein 2D-Array ist, wobei 0 Zeilen 0 Spalten bedeuten.
Der zweite gotcha ist, dass Sie scheinen eine benutzerdefinierte Ansicht Subklassen QTableView
benötigen, weil Sie dragEnterEvent
und dragMoveEvent
außer Kraft setzen müssen bedingungslos Drag betritt und ziehen bewegt zu akzeptieren. Andernfalls hätten die Drop-Ereignisse keine Chance, ausgelöst zu werden, selbst wenn Sie die Eigenschaft acceptDrops
der Ansicht auf True
setzen. Die Standardimplementierung von dragEnterEvent
und/oder dragMoveEvent
scheint mit dem zugrunde liegenden Modell der Ansicht zu sprechen. Wenn das Modell keine Zeilen enthält, verweigert die Ansicht das Akzeptieren von Drops.
Die dritte Sache ist nicht viel eine Frage, es ist nur, dass in der dropMimeData
Methode Ihres Modells Sie Zeilen einfügen müssen, wenn Sie keine haben, um den Raum für das gefallene Element zu machen.
Hier ist die vollständige Lösung für pyside 1.2.1/Qt 4.8.5 mit Python getestet 2.7:
import sys
from PySide import QtCore, QtGui
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, headers = [], items = [[]], parent = None):
super(TableModel,self).__init__(parent)
self.__items = items
self.__headers = headers
row_counter = 0
for row_item in self.__items:
column_counter = 0
for column_item in row_item:
idx = self.createIndex(row_counter, column_counter)
self.setData(idx, column_item, QtCore.Qt.EditRole)
self.dataChanged.emit(idx, idx)
column_counter += 1
row_counter += 1
num_headers = len(self.__headers)
for section in range(0, num_headers):
self.setHeaderData(section, QtCore.Qt.Horizontal, self.__headers[section])
self.headerDataChanged.emit(QtCore.Qt.Horizontal, 0, num_headers)
def index(self, row, column, parent):
if row < 0 or row >= len(self.__items):
return QtCore.QModelIndex()
return self.createIndex(row, column, self.__items[row])
def parent(self, index):
return QtCore.QModelIndex()
def rowCount(self, index):
if index.isValid():
return
num_rows = len(self.__items)
# checking for empty nested columns list within a single "row"
if num_rows == 1:
if len(self.__items[0]) == 0:
return 0
return num_rows
def columnCount(self, index):
if index.isValid():
return 0
# compute the max column count within all rows
max_column_count = 0
for row in self.__items:
column_count = len(row)
if column_count > max_column_count:
max_column_count = column_count
# if there are no real columns, make the column count return the number of headers instead
if max_column_count < len(self.__headers):
max_column_count = len(self.__headers)
return max_column_count
def flags(self, index):
if not index.isValid():
return QtCore.Qt.ItemIsEnabled
if index.column() == 0:
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | \
QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsEditable
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled
def supportedDropActions(self):
return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction
def insertRows(self, row, count, index):
if index.isValid():
return False
if count <= 0:
return False
num_columns = self.columnCount(index)
# inserting 'count' empty rows starting at 'row'
self.beginInsertRows(QtCore.QModelIndex(), row, row + count - 1)
for i in range(0, count):
# inserting as many columns as the table currently has
self.__items.insert(row + i, ["" for i in range(0, num_columns)])
self.endInsertRows()
return True
def removeRows(self, row, count, index):
if index.isValid():
return False
if count <= 0:
return False
num_rows = self.rowCount(QtCore.QModelIndex())
self.beginRemoveRows(QtCore.QModelIndex(), row, row + count - 1)
for i in range(count, 0, -1):
self.__items.pop(row - i + 1)
self.endRemoveRows()
return True
def data(self,index,role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
if row < 0 or row >= len(self.__items):
return ""
if column < 0 or column >= len(self.__items[row]):
return ""
else:
return self.__items[row][column]
return None
def setData(self, index, value, role=QtCore.Qt.EditRole):
if not index.isValid:
return False
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
if row < 0 or row >= self.rowCount(QtCore.QModelIndex()):
return False
if column < 0 or column >= len(self.__items[row]):
return False
self.__items[row].pop(column)
self.__items[row].insert(column, value)
self.dataChanged.emit(index, index)
return True
return False
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
if section < 0 or section >= len(self.__headers):
return ""
else:
return self.__headers[section]
return None
def mimeTypes(self):
return ['application/vnd.tableviewdragdrop.list']
def mimeData(self, indexes):
mimedata = QtCore.QMimeData()
encoded_data = QtCore.QByteArray()
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.WriteOnly)
for index in indexes:
if index.isValid():
text = self.data(index, QtCore.Qt.DisplayRole)
stream << QtCore.QByteArray(text)
mimedata.setData('application/vnd.tableviewdragdrop.list', encoded_data)
return mimedata
def dropMimeData(self, data, action, row, column, parent):
if action == QtCore.Qt.IgnoreAction:
return True
if not data.hasFormat('application/vnd.tableviewdragdrop.list'):
return False
if column > 0:
return False
num_rows = self.rowCount(QtCore.QModelIndex())
begin_row = 0
if row != -1:
begin_row = row
elif parent.isValid():
begin_row = parent.row()
else:
begin_row = num_rows
if begin_row == num_rows:
self.insertRows(begin_row, 1, QtCore.QModelIndex())
if column < 0:
if parent.isValid():
column = parent.column()
else:
column = 0
encoded_data = data.data('application/vnd.tableviewdragdrop.list')
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly)
new_items = []
rows = 0
while not stream.atEnd():
text = QtCore.QByteArray()
stream >> text
new_items.append(str(text))
rows += 1
for text in new_items:
idx = self.index(begin_row, column, QtCore.QModelIndex())
self.setData(idx, text, QtCore.Qt.EditRole)
begin_row += 1
return True
class TableView(QtGui.QTableView):
def __init__(self, parent=None):
super(TableView, self).__init__(parent)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
event.accept()
def dragMoveEvent(self, event):
event.accept()
class MainForm(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
self.left_model = TableModel(headers=["column0", "column1", "column2"])
#self.left_model = TableModel(items=[["left0", "left1", "left2"],["left3","left4","left5"]],
# headers=["column0", "column1", "column2"])
self.right_model = TableModel(items=[['right0', 'right1', 'right2'], ['right3', 'right4', 'right5']],
headers=['column0', 'column1', 'column2'])
self.left_view = TableView()
self.left_view.setModel(self.left_model)
self.left_view.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.right_view = TableView()
self.right_view.setModel(self.right_model)
self.right_view.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.layout = QtGui.QHBoxLayout()
self.layout.addWidget(self.left_view)
self.layout.addWidget(self.right_view)
self.window = QtGui.QWidget()
self.window.setLayout(self.layout)
self.setCentralWidget(self.window)
def main():
app = QtGui.QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
main()