【问题标题】:Copy/paste text from QClipboard freezes program从 QClipboard 复制/粘贴文本冻结程序
【发布时间】:2019-07-29 19:07:40
【问题描述】:

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

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

之前关于 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_cliboard 时,它认为它更改了数据,因此它从 .dataChanged.connect() 运行函数。在read_cliboard中,您可以在应对剪贴板之前断开它,并在应对后再次连接。

标签: python pyqt qtablewidget pyside2 qclipboard


【解决方案1】:

您不应该以这种方式使用dataChanged 信号,原因有两个:

  • 每次剪贴板在整个系统中发生变化时都会调用它;
  • 清除剪贴板显然会改变它的内容,导致递归调用你的read_clipboard方法;您显然可以按照@furas 的建议暂时断开信号,但第一个问题仍然存在。

此外,您不能将 QItemSelectionModel 用于 setText,因为它需要一个字符串。

更好的解决方案是覆盖自定义 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)

【讨论】:

  • 您的代码有错误: 1) 在第一个代码事件中是一个 QKeyEvent,我认为不能与 QtGui.QKeySequence.Copy 进行比较。 2)在第二个中,您有同样的错误,但如果在发送 QKeyEvent 之前发送 Repaint 事件会发生什么?
  • @eyllanesc 我曾经检查 QEvent.KeyPress 然后做一个event.matches(QtGui.QKeySequence.StandardKey),然后我发现了类似的例子并且对此也很怀疑,但我试了一下,它实际上似乎工作正常。我使用的是旧版本的 PyQt5 (5.7.1),我现在无法升级,同时实现可能已经改变。你得到什么错误?
  • 我认为你很幸运,因为正如你指出的那样没有意义,我没有测试过它,但正如它所说:比隐式更好
  • 我并没有说这没有意义,但我持怀疑态度 :-) 我想得越多,我就越意识到它确实有意义。同样根据他们implement it的消息来源。你对另一个电话是正确的:我在这里得到了 3 次:第一次是按下“c”后的 QEvent.ShortcutOverride,第二次是 KeyPress,最后是释放;奇怪的是,如果我在“c”之前释放 Ctrl,则根本没有释放事件!无论如何,我将对其进行编辑以确保仅为 keyPressEvent 调用它。
  • Qt 为QKeyEvent/QKeySequence 定义了operator==。这是documented in Qt4,但在 Qt5 中没有。我已将其报告为错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
  • 2017-02-08
  • 2021-07-09
  • 1970-01-01
  • 2018-10-26
  • 1970-01-01
相关资源
最近更新 更多