Python 从QClipboard冻结程序复制/粘贴文本

Python 从QClipboard冻结程序复制/粘贴文本,python,pyqt,qtablewidget,pyside2,qclipboard,Python,Pyqt,Qtablewidget,Pyside2,Qclipboard,我有一个QTableWidget,当单击一行时,它会选择该行中的所有单元格。我正在尝试添加“复制”功能,以便在选中行并粘贴到文本编辑器中时可以^ctrl-c。但是,在我当前的代码中,一旦我^ctrl-c一行,我复制的行就会不断被复制 我在“read_clipboard”方法中实现了一个print语句,以查看复制的行是否已被读取,这就是我发现该行一直被复制的原因,就像在无限循环中一样 之前关于PyQt/Qt和QClipboard的堆栈溢出问题对我来说都不是有效的 def __init__(self

我有一个QTableWidget,当单击一行时,它会选择该行中的所有单元格。我正在尝试添加“复制”功能,以便在选中行并粘贴到文本编辑器中时可以^ctrl-c。但是,在我当前的代码中,一旦我^ctrl-c一行,我复制的行就会不断被复制

我在“read_clipboard”方法中实现了一个print语句,以查看复制的行是否已被读取,这就是我发现该行一直被复制的原因,就像在无限循环中一样

之前关于PyQt/Qt和QClipboard的堆栈溢出问题对我来说都不是有效的

def __init__(self):
   super(MainWindow, self).__init__()
   self.setupUi(self)
   self.my_selector = self.my_tableWidget.selectionModel()

   # Where I detect the signal to call my "read_clipboard" method
   QtGui.QGuiApplication.clipboard().dataChanged.connect(self.read_clipboard)

   self.show()

def read_clipboard(self):
    selection = self.my_selector.selectedIndexes()
    if selection:
        print(selection)
        QtGui.QGuiApplication.clipboard().clear()
        QtGui.QGuiApplication.clipboard().setText(selection)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainWin = MainWindow()  # Creates MainWindow object
    ret = app.exec_()
    sys.exit(ret)

当我^ctrl-c一行时,程序会像在无限循环中一样连续打印“选择”,我不知道在它运行一次后如何停止它,以便我可以只复制那一行。

出于两个原因,您不应该以这种方式使用
数据更改
信号:

  • 每当剪贴板在整个系统中发生更改时,将调用它
  • 清除剪贴板将明显改变其内容,从而递归调用
    read\u剪贴板
    方法;显然,您可以按照@furas的建议暂时断开信号,但第一个问题仍然存在
此外,不能对
setText
使用QItemSelectionModel,因为它需要字符串

更好的解决方案是覆盖自定义QTableWidget类的keyPressEvent,以便在默认实现对其进行操作之前捕获其“复制”操作:

class MyTableWidget(QtWidgets.QTableWidget):
    def keyPressEvent(self, event):
        if event == QtGui.QKeySequence.Copy:
            # set clipboard only if it's not a key repetition
            if not event.isAutoRepeat():
                QtWidgets.QApplication.clipboard().setText(', '.join(i.data() for i in self.selectedIndexes() if i.data()))
        else:
            super(MyTableWidget, self).keyPressEvent(event)
另一种类似的可能性是在表中安装事件筛选器并检查其关键事件:

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.my_tableWidget.installEventFilter(self)

    def eventFilter(self, source, event):
        if source == self.table and event.type() == QtCore.QEvent.KeyPress and event == QtGui.QKeySequence.Copy:
            if not event.isAutoRepeat():
                QtWidgets.QApplication.clipboard().setText(', '.join(i.data() for i in self.table.selectedIndexes() if i.data()))
            # return True to ensure that the event is not actually propagated
            # to the table widget, nor its parent(s)
            return True
        return super(MainWindow, self).eventFilter(source, event)

由于某种原因,当它运行
read\u cliboard
时,它认为它更改了数据,所以它运行
.dataChanged.connect()
中的函数。在
read_cliboard
中,您可以在处理到cliboard之前断开它,然后在处理后重新连接。您的代码有错误:1)在第一个代码事件中是一个QKeyEvent,我认为它不能与QtGui.QKeySequence.Copy进行比较。2) 在第二个示例中,您会遇到相同的错误,但如果在QKeyEvent发送之前发送重新绘制事件,会发生什么情况?@eyllanesc我曾经检查QEvent.KeyPress,然后执行
事件.matches(QtGui.QKeySequence.StandardKey)
,然后我发现了类似的示例,对此也非常怀疑,但我试了一下,它实际上似乎工作得很好。我使用的是PyQt5(5.7.1)的旧版本,目前无法升级,同时可能会更改实现。你会犯什么错误?我认为你很幸运,因为你指出它毫无意义,我没有测试它,但正如它所说:显式比隐式好我没有说它毫无意义,但我对此表示怀疑:-)我越想它,我越意识到它有意义。根据消息来源,他们也是这样做的。关于另一个电话,你是对的:我在这里听到了三次:第一次是QEvent.ShortcutOverride,在按下“c”之后,第二次是a键,最后是释放;奇怪的是,如果我在“c”之前释放Ctrl,根本就没有释放事件!无论如何,我将对其进行编辑,以确保仅为keyPressEvent.Qt定义了
QKeyEvent/QKeySequence
调用它。这是,但在Qt5中丢失了。我把它当作一个bug报告了。