本文记录如何使用 PyQt5 在 QTableWidgets 之间拖放行数据。
演示示例
直接上代码,代码中有注释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | import sys from PyQt5.QtWidgets import (QApplication, QWidget, QHBoxLayout, QAbstractItemView, QTableWidget, QHeaderView, QTableWidgetItem) class TableWidgetDragDropRows(QTableWidget): '''TableWidgetDragDropRows 支持在各个QTableWidget之间拖放行的QTableWidget自定义扩展类 Extends: QTableWidget ''' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setDragEnabled(True) self.setAcceptDrops(True) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setDragDropOverwriteMode(False) self.last_drop_row = None def dropMimeData(self, row, col, mimeData, action): '''dropMimeData 复写此方法以获取要插入的行的索引 Arguments: row {int} -- 所在行的索引 col {int} -- 所在列的索引 mimeData {str} -- mimeData action {str} -- action Returns: bool -- 操作成功与否 ''' self.last_drop_row = row return True def dropEvent(self, event): '''dropEvent 拖放的放事件 Arguments: event {object} -- 事件对象 ''' # 将从中移动所选行的QTableWidget sender = event.source() # 默认dropEvent方法触发带有参数的dropMimeData(我们感兴趣的是行索引)。 super().dropEvent(event) # 现在我们直到要在哪里插入所选的行了 dropRow = self.last_drop_row selectedRows = sender.getselectedRowsFast() # 分配传送所需的空间 for _ in selectedRows: self.insertRow(dropRow) # 如果发送者和接收者是同一个,那么,在创建新的空行之后,所选的行可能会更改它们的位置 sel_rows_offsets = [0 if self != sender or srow < dropRow else len( selectedRows) for srow in selectedRows] selectedRows = [row + offset for row, offset in zip(selectedRows, sel_rows_offsets)] # 复制所选的行的内容到空行中 for i, srow in enumerate(selectedRows): for j in range(self.columnCount()): item = sender.item(srow, j) if item: source = QTableWidgetItem(item) self.setItem(dropRow + i, j, source) # 删除所选的行 for srow in reversed(selectedRows): sender.removeRow(srow) event.accept() def getselectedRowsFast(self): selectedRows = [] for item in self.selectedItems(): if item.row() not in selectedRows: selectedRows.append(item.row()) selectedRows.sort() return selectedRows class MainWindow(QWidget): def __init__(self): super().__init__() # 设置窗体的标题 self.setWindowTitle("QTableWidget 拖放") # 设置窗体布局 self.layout = QHBoxLayout() self.setLayout(self.layout) # 初始化 self.initUI() def initUI(self): ### 开始: 定义demo所需的内容 ### colNames = ['类型', '名称'] rowsData = [ ('玄幻', '权力的游戏'), ('战争', '兄弟连'), ('律政', '傲骨贤妻'), ('爱情', '我爱上了你然后你出车祸了?白血病了?我们是兄妹?') ] ### 结束: 定义demo所需的内容 ### # 各个 QTableWidget 实例所需的列和行的数量 lenOfCols = len(colNames) lenOfRowsData = len(rowsData) ### 开始: 生成QTableWidget实例 ### self.tableWidgets = [] # 生成索引为从 0 到 (lenOfRowsData-1) 的 (lenOfRowsData-1) 个 QTableWidget for _ in range(lenOfRowsData): tableWidgetInstance = TableWidgetDragDropRows() # 表头和各个行的填充样式 header = tableWidgetInstance.horizontalHeader() header.setSectionResizeMode(QHeaderView.Stretch) ### 开始: 填充各个 QTableWidget 实例的表头 ### tableWidgetInstance.setColumnCount(lenOfCols) tableWidgetInstance.setHorizontalHeaderLabels(colNames) ### 结束: 填充各个 QTableWidget 实例的表头 ### self.tableWidgets.append(tableWidgetInstance) self.layout.addWidget(tableWidgetInstance) ### 结束: 生成QTableWidget实例 ### ### 开始: 使用demo数据填充第一个 QTableWidget实例 ### theFirstTableWidget = self.tableWidgets[0] for i, (theType, theName) in enumerate(rowsData): t = QTableWidgetItem(theType) n = QTableWidgetItem(theName) theFirstTableWidget.insertRow(theFirstTableWidget.rowCount()) theFirstTableWidget.setItem(i, 0, t) theFirstTableWidget.setItem(i, 1, n) ### 结束: 使用demo数据填充第一个 QTableWidget实例 ### if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) |