Я, наверное, слишком поздно, чтобы придумать ответ, но сам вопрос был мне интересен, так что я копаться в нем и был в состоянии найти решение.
Как оказалось, для реализации пустого состояния табличной модели есть несколько ошибок, чтобы они по-прежнему отображали заголовки столбцов и, кроме того, позволяли удалять вещи сами по себе.
Первый способ заключается в том, что Qt, похоже, не рисует заголовки столбцов, если columnCount
метод возвращает 0 для недопустимого QModelIndex
. В этом случае, по-видимому, даже не стоит называть метод headerData
. Решение состоит в том, чтобы никогда не возвращать 0 из columnCount
с недопустимым QModelIndex
, даже если количество строк на самом деле равно 0, а наша базовая структура данных - это 2D-массив, где 0 строк - 0 столбцов.
Второй Гоча является то, что вам кажется, нужно настраиваемое представление подклассов QTableView
потому, что вам нужно переопределить dragEnterEvent
и dragMoveEvent
принять сопротивление входит и сопротивление движется безоговорочно. В противном случае события сбрасывания не будут иметь возможности запускать, даже если вы установили свойство acceptDrops
вида на True
. По умолчанию реализация dragEnterEvent
и/или dragMoveEvent
, похоже, относится к базовой модели представления, и если у модели нет строк, представление отказывается принимать капли.
Третий gotcha - это не очень-то похожее, это именно то, что в методе dropMimeData
вашей модели вам нужно вставлять строки, если у вас их нет, чтобы сделать комнату для сброшенного предмета.
Вот полное решение для PySide 1.2.1/Qt 4.8.5 протестирована с Python 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()