PyQt:如何在 QTableView 中的光标处插入文本

Posted

技术标签:

【中文标题】PyQt:如何在 QTableView 中的光标处插入文本【英文标题】:PyQt: How to insert text at the cursor in QTableView 【发布时间】:2016-12-03 16:47:17 【问题描述】:

我有一个如下所示的 QTableView。我想按下测试按钮并在光标处插入一个“a” - 例如在(行,列)=(2,2)处的“11”中间。即用户双击单元格(2,2),将光标置于“11”的中间,按下Test。期望的结果:“1a1”。

这可行吗?如果是,如何?非常感谢。

# coding: utf-8

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *

MY_ARRAY = [['00', '01', '02'],
            ['10', '11', '12'],
            ['20', '21', '22']]


class MyWindow(QTableView):
    def __init__(self, *args):
        super(MyWindow, self).__init__()

        self.tablemodel = MyTableModel(MY_ARRAY)

        self.tableview = QTableView()

        self.tableview.setModel(self.tablemodel)

        self.tableview.setItemDelegate(MyDelegate(self))

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.tableview)

        self.button1 = QPushButton("Test")

        self.button1.released.connect(self.test)

        self.layout.addWidget(self.button1)
        self.setLayout(self.layout)

    def test(self):

        # MY_ARRAY.append([30,31,32])

        index = self.tableview.currentIndex()
        item = self.tablemodel.data(index, Qt.DisplayRole)

        print("item %s " % item)

        item_edit = self.tableview.edit(index)

        qDebug("qDebug: item_edit %s " % item_edit)

        MY_ARRAY.insert(index.row(), ['30', '31', '32'])

        self.tablemodel.layoutChanged.emit()

        qDebug("  " .format(MY_ARRAY))

        qcursor = QCursor.pos()
        qDebug("  ".format(qcursor))

        qcursor1 = self.mapFromGlobal(qcursor)
        qDebug("  ".format(qcursor1))

        # qDebug(" self.tableview.indexAt(qcursor)  ".format(self.tableview(qcursor)))
        # qDebug(" self.tableview.indexAt(qcursor1)  ".format(self.tableview(qcursor1)))

        # print(' index.row(): ', index.row())

        qDebug(
            " tableview.rowViewportPosition %s " %
            self.tableview.rowViewportPosition(index.row()))
        qDebug(
            " tableview.columnViewportPosition %s " %
            self.tableview.columnViewportPosition(index.column()))

        # qDebug(" tableview.viewport() %s " % self.tableview.viewport(qcursor))

        item = self.tableview.setCurrentIndex(index)
        qDebug(" tableview.item() %s " % self.tableview)


class MyTableModel(QAbstractTableModel):
    def __init__(self, datain, parent=None, *args):
        super(MyTableModel, self).__init__(parent, *args)

        self.arraydata = datain

    def rowCount(self, parent):
        return len(self.arraydata)

    def columnCount(self, parent):
        return len(self.arraydata[0])

    def data(self, index, role):
        if not index.isValid():
            return None

        elif not (role == Qt.DisplayRole or role == Qt.EditRole):
            return None
        return (self.arraydata[index.row()][index.column()])

    def setData(self, index, value, role=Qt.EditRole):
        self.arraydata[index.row()][index.column()] = value
        return True

    def flags(self, index):
        return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable


class MyDelegate(QStyledItemDelegate):

    def __init__(self, parent=None):
        super(MyDelegate, self).__init__(parent)

    def createEditor(self, parent, option, index):
        editor = QLineEdit(parent)
        self.connect(editor, SIGNAL("returnPressed()"),
                     self.commitAndCloseEditor)
        return editor

    def commitAndCloseEditor(self):
        editor = self.sender()
        if isinstance(editor, (QTextEdit, QLineEdit)):
            self.emit(SIGNAL("commitData(QWidget*)"), editor)
            self.emit(SIGNAL("closeEditor(QWidget*)"), editor)

    def setEditorData(self, editor, index):
        text = index.model().data(index, Qt.DisplayRole)

        editor.setText(text)

    def setModelData(self, editor, model, index):
        model.setData(index, editor.text())

def main():
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

【问题讨论】:

欢迎来到 Stack Overflow!看起来你正在寻求家庭作业帮助。虽然我们对此本身没有任何问题,但请注意这些dos and don'ts,并相应地编辑您的问题。 @JoeC。这看起来真的不像一个家庭作业问题。 嗯,这不是家庭作业。这是我业余项目中关于双语言对齐的问题。一个算法会自动对齐两个文本。很多时候会出现错位,需要手动对齐。我对 PyQt 比较陌生。我花了几天时间阅读书籍和谷歌搜索,但找不到任何解决方案。所以如果有人能指点一两个,我真的很感激。 我真正需要的是一种方法来抓取放置光标的表格元素(单元格),例如 cell = mytableview.cellAt(i,j) (无论如何去做这个?)。据我了解,该单元格是一个 QLineEdit 实例。然后,我可以执行 qtextcursor = cell.textCursor() 之类的操作,并像文本编辑器一样处理 qtextcursor(网上有很多关于编辑器的教程)。 【参考方案1】:

表格单元格没有光标,也不能直接编辑。编辑功能由 item-delegate 提供。默认情况下,文本数据的编辑器小部件是QLineEdit,但其他数据类型可能使用不同的编辑器小部件,例如用于数字数据的QSpinBox,或用于布尔数据的QComboBox。使用的具体小部件可以通过设置custom item-delegate来控制。

使用按钮之类的东西在编辑小部件中插入文本的大问题是,一旦单击按钮,编辑器就会自动关闭(并销毁)。因此,使用上下文菜单添加自定义操作会简单得多:

class MyWindow(QTableView):
    def __init__(self, *args):
        ...
        self.delegate = MyDelegate(self)
        self.delegate.contextMenuRequested.connect(self.showContextMenu)
        self.tableview.setItemDelegate(self.delegate)

    def showContextMenu(self, editor, pos):
        pos = editor.mapToGlobal(pos)
        menu = editor.createStandardContextMenu()
        menu.addSeparator()
        action = menu.addAction('Insert Text')
        if menu.exec_(pos) is action:
            editor.insert(' foo ')

class MyDelegate(QStyledItemDelegate):
    contextMenuRequested = pyqtSignal(object, QPoint)

    def createEditor(self, parent, option, index):
        editor = QLineEdit(parent)
        editor.setContextMenuPolicy(Qt.CustomContextMenu)
        editor.customContextMenuRequested.connect(
            lambda pos: self.contextMenuRequested.emit(editor, pos))
        return editor

【讨论】:

非常感谢。 “1a1”只是一个例子。 MyDelegate(QStyledItemDelegate) 的 createEditor() 创建一个 QLineEdit 编辑器,我只是不知道如何使用它。我的用例如下所示:每个单元格都包含一个字符串(一段英文文本或其他语言的文本)。用户直观地检查单元格的内容并决定插入一些额外文本的位置,然后将光标放在该位置并按下按钮。在此期间我找到了QTableView的indexWidget,但不确定是否可以使用。 @mike。使用单独的按钮插入文本将很难实现。上下文菜单会简单得多。请参阅我的更新答案。 再次非常感谢。我会尝试使用您的建议。很好的帮助,我很感激。【参考方案2】:

经过大量的努力和使用 qDebug,我终于找到了解决方案。我相信它可以进一步改进。但我对 PyQt 了解不多。这个想法是在编辑器关闭之前将光标位置缓存在 MyDelegate(QStyledItemDelegate) 中。我希望它对遇到同样问题的人有用。

class MyDelegate(QStyledItemDelegate):
    ...
    def createEditor(self, parent, option, index):
        self.cursorpos = -1  # unset flag
        editor = QLineEdit(parent)
        self.connect(editor, SIGNAL("editingFinished()"),
                         self.commitAndCloseEditor)
        return editor

    def commitAndCloseEditor(self):
        editor = self.sender()
        self.cursorpos = editor.cursorPosition()
        if isinstance(editor, (QTextEdit, QLineEdit)):
            self.emit(SIGNAL("commitData(QWidget*)"), editor)
            self.emit(SIGNAL("closeEditor(QWidget*)"), editor)

下面给出了全部内容。

# coding: utf-8

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *

MY_ARRAY = [['00', '01', '02'],
            ['10', '11', '12'],
            ['20', '21', '22']]


class MyWindow(QTableView):
    def __init__(self, *args):
        super(MyWindow, self).__init__()

        self.tablemodel = MyTableModel(MY_ARRAY)

        self.tableview = QTableView()

        self.tableview.setModel(self.tablemodel)

        # self.tableview.setItemDelegate(MyDelegate(self))

        self.delegate = MyDelegate(self)
        self.tableview.setItemDelegate(self.delegate)

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.tableview)

        self.button1 = QPushButton("Test")

        self.button1.released.connect(self.test)

        self.layout.addWidget(self.button1)
        self.setLayout(self.layout)

    def test(self):

        index = self.tableview.currentIndex()
        item = self.tablemodel.data(index, Qt.DisplayRole)

        qDebug("item %s " % item)

        qDebug(" <test><MyDelegateMyDelegate> self.delegate.cursorpos: %s " % self.delegate.cursorpos)

        cursorpos = self.delegate.cursorpos
        qDebug(" <test> cursor pos %s " % cursorpos)
        if cursorpos > -1:
            index.model().setData(index, item[:cursorpos] + ' foo ' + item[cursorpos:])
            self.tablemodel.layoutChanged.emit()
            self.delegate.cursorpos = -1


class MyTableModel(QAbstractTableModel):
    def __init__(self, datain, parent=None, *args):
        super(MyTableModel, self).__init__(parent, *args)

        self.arraydata = datain

    def rowCount(self, parent):
        return len(self.arraydata)

    def columnCount(self, parent):
        return len(self.arraydata[0])

    def data(self, index, role):
        if not index.isValid():
            return None

        elif not (role == Qt.DisplayRole or role == Qt.EditRole):
            return None
        return (self.arraydata[index.row()][index.column()])

    def setData(self, index, value, role=Qt.EditRole):
        self.arraydata[index.row()][index.column()] = value
        return True

    def flags(self, index):
        return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable


class MyDelegate(QStyledItemDelegate):

    def __init__(self, parent=None):
        super(MyDelegate, self).__init__(parent)
        self.cursorpos = -1  # unset flag

    def createEditor(self, parent, option, index):
        self.cursorpos = -1  # unset flag
        editor = QLineEdit(parent)
        self.connect(editor, SIGNAL("editingFinished()"),
                         self.commitAndCloseEditor)
        return editor

    def commitAndCloseEditor(self):
        editor = self.sender()
        self.cursorpos = editor.cursorPosition()
        if isinstance(editor, (QTextEdit, QLineEdit)):
            self.emit(SIGNAL("commitData(QWidget*)"), editor)
            self.emit(SIGNAL("closeEditor(QWidget*)"), editor)

    def setEditorData(self, editor, index):
        text = index.model().data(index, Qt.DisplayRole)

        editor.setText(text)


    def setModelData(self, editor, model, index):
        model.setData(index, editor.text())

def main():
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

【讨论】:

以上是关于PyQt:如何在 QTableView 中的光标处插入文本的主要内容,如果未能解决你的问题,请参考以下文章

PyQt:如何调整 QTableView 标题大小/列宽

PyQt4 - QTableView - 如何循环 QTableView

如何通过右键单击QTableView获取行号?

如何在 Pyqt4 中设置 QTableView 标头名称

如何将数据发送到 QTableView/QTableWidget (PyQt)

PyQt4中的QTableView选定元素