PyQt5 QTableWidget实现复制粘贴报AttributeError如何修复
错误原因
- 事件分发优先级冲突:你为
QTableWidget安装的事件过滤器优先级高于主窗口的keyPressEvent,当按下Ctrl+C时,事件会被事件过滤器优先捕获,执行完copySelection后返回True终止了事件传递,导致keyPressEvent中给self.copied_cells赋值的代码永远不会执行,粘贴时自然找不到该属性。 - 调用对象错误:粘贴逻辑中
self.setItem是错误写法,setItem是QTableWidget类的方法,你需要调用self.tableWidget.setItem才能正常写入单元格。 - 缺少容错判断:没有判断
copied_cells是否存在就直接访问,未复制就按Ctrl+V也会触发报错。
修复方案
直接合并复制粘贴逻辑到事件过滤器中,删掉冗余的keyPressEvent重复判断即可,完整可运行代码如下:
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import QTableWidget, QApplication, QMainWindow, QTableWidgetItem,QFileSystemModel, QFileDialog,QVBoxLayout,QWidget from PyQt5.QtCore import Qt from PyQt5.uic import loadUi import sys, csv, io class Main_P(QMainWindow): def __init__(self): QMainWindow.__init__(self) loadUi("test_copy&paste.ui",self) self.tableWidget.installEventFilter(self) # 初始化存储复制单元格的属性 self.copied_cells = [] def eventFilter(self, source, event): if event.type() == QtCore.QEvent.KeyPress: # 处理复制 if event.matches(QtGui.QKeySequence.Copy): self.copySelection() return True # 处理粘贴 elif event.matches(QtGui.QKeySequence.Paste): self.pasteSelection() return True return super(Main_P, self).eventFilter(source, event) def copySelection(self): selection = self.tableWidget.selectedIndexes() if not selection: return # 记录复制的单元格 self.copied_cells = sorted(selection) # 同时写入剪贴板,支持粘贴到外部程序 rows = sorted(index.row() for index in selection) columns = sorted(index.column() for index in selection) rowcount = rows[-1] - rows[0] + 1 colcount = columns[-1] - columns[0] + 1 table = [[''] * colcount for _ in range(rowcount)] for index in selection: row = index.row() - rows[0] column = index.column() - columns[0] table[row][column] = index.data() if index.data() else '' stream = io.StringIO() csv.writer(stream, delimiter='\t').writerows(table) QtWidgets.qApp.clipboard().setText(stream.getvalue()) def pasteSelection(self): # 先判断有没有复制内容 if not self.copied_cells: return # 计算粘贴的偏移量 current_row = self.tableWidget.currentRow() current_col = self.tableWidget.currentColumn() if current_row < 0 or current_col < 0: return row_offset = current_row - self.copied_cells[0].row() col_offset = current_col - self.copied_cells[0].column() # 写入单元格,超出表格范围的自动忽略 max_row = self.tableWidget.rowCount() - 1 max_col = self.tableWidget.columnCount() - 1 for cell in self.copied_cells: target_row = cell.row() + row_offset target_col = cell.column() + col_offset if 0 <= target_row <= max_row and 0 <= target_col <= max_col: self.tableWidget.setItem(target_row, target_col, QTableWidgetItem(cell.data())) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) win = Main_P() win.show() sys.exit(app.exec_())
优化说明
- 增加了边界判断,粘贴内容超出表格范围时不会触发索引报错
- 复制时同时支持内部粘贴和粘贴到Excel、记事本等外部程序
- 增加了未选中单元格、未复制就粘贴的容错处理,避免异常崩溃
内容的提问来源于stack exchange,提问作者daniel ajj
相关产品推荐
相关产品推荐

