问题
I have a QTableView as below. I'd like to press the Test button and insert an "a" at the cursor - for example in the middle of "11" at (row,column) = (2,2). That is, the user double-clicks cell (2,2) and places the cursor in the middle of "11", and presses Test. Desired result: "1a1".
Is this doable? If yes, how? Thanks very much.
# 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()
回答1:
A table cells do not have a cursor and are not directly editable. The editing capabilties are provided by the item-delegate. By default, the editor widget for text data is a QLineEdit
, but other data types may use different editor widgets, such as a QSpinBox
for numerical data, or a QComboBox
for boolean data. The specific widget used can be controlled by setting a custom item-delegate.
The big problem with using something like a button to insert text in the editing widget, is that the editor will be automatically closed (and destroyed) as soon as the button is clicked. It will therefore be much simpler to use a context-menu to add custom actions:
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
回答2:
After a lot of struggle and uses of qDebug I finally managed to find a solution. I am sure it can be further improved. But I don't know much about PyQt. The idea is to cache the cursor position in MyDelegate(QStyledItemDelegate) before the editor is closed. I hope it can be useful to someone who encounters the same problem.
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)
The whole thing is given below.
# 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()
来源:https://stackoverflow.com/questions/40950105/pyqt-how-to-insert-text-at-the-cursor-in-qtableview