Search code examples
pythonpython-2.7pyqtpyqt4qtablewidget

PyQt QItemDelegate setFocus to QTableWidget after "Enter" is pressed


I am using QTableWidget to creating something like excel. One of the QTableWidget column is able to let user update note with multiple line. Before use QTextEdit, user need to manually add " \n" to achieve multiple line, but this is not friendly user. I found out I can set QTextEdit into QTableWidget. By using QTextEdit, I able to type multiple line by press "Enter" or "Shift+Enter". However, I want when "Shift+Enter" is pressed, it go to next line but when "Enter" is pressed, it run self.update_MySQL function.

Below is my sample code

import sys, itertools, sip
sip.setapi('QVariant',2)
from PyQt4 import QtCore, QtGui

class CustomTextEditDelegate(QtGui.QItemDelegate):
    def createEditor(self, parent, option, index):
        editor = QtGui.QTextEdit(parent)
        return editor

    def setEditorData(self, editor, index):
        editor.setText(index.data())

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

class PIX_DATABASE_UI(QtGui.QTableWidget):
    def __init__(self, parent=None):
        super(PIX_DATABASE_UI, self).__init__(parent)

        ### signal
        self.update_tableWidget()
        self.itemEntered.connect(self.update_MySQL)

    # -----------------------------------------------------------------------------------------------------------------#
    def update_MySQL(self):
        print "MySQL Updated"

    def update_tableWidget(self):
        self.filter_columns = [u'remark']
        self.setColumnCount(len(self.filter_columns))
        self.setHorizontalHeaderLabels(self.filter_columns)
        self.setRowCount(5)

        for row, col in itertools.product(range(5), range(len(self.filter_columns))):
            if self.filter_columns[col] == "remark":
                width = self.sizeHint().width()
                self.setColumnWidth(col, width * 0.75)
                self.setItem(row, col, QtGui.QTableWidgetItem(str("ABC")))
                self.setItemDelegateForColumn(col, CustomTextEditDelegate(self))
                self.verticalHeader().setResizeMode(row, QtGui.QHeaderView.ResizeToContents)

# -----------------------------------------------------------------------------------------------------------------#
# -----------------------------------------------------------------------------------------------------------------#

if __name__ == '__main__':
    global ui
    try:
        ui.close()
    except:
        pass

    app = QtGui.QApplication(sys.argv)
    app.setStyle(QtGui.QStyleFactory.create("Plastique"))
    # print QtGui.QStyleFactory.keys()

    ui = PIX_DATABASE_UI()
    ui.show()
    sys.exit(app.exec_()) 

Conclusion:

Thanks eyllanesc, the code is help me to achieve what I want to achieve with slightly modify to the keyPressEvent. Original code is still emit even I press "Shift + Enter". Below code is what I modified.

def keyPressEvent(self, event):
    modifiers = QtGui.QApplication.keyboardModifiers()
    if modifiers != QtCore.Qt.ShiftModifier and event.key() == QtCore.Qt.Key_Return:
        self.enter.emit()
        # If you do not want a new line uncomment the following
        # return
    super(TextEdit, self).keyPressEvent(event)

So now, after edit in textEdit and press "Enter", it will run self.update_MySQL and when press "Shift + Enter", it will go to next line.


Solution

  • What you can do is that the data of the model is updated and for this the commitData signal calling the setModelData() method must be emited.

    By doing this you can use the signal itemChanged() because the data of the item is modified.

    import sys, itertools, sip
    sip.setapi('QVariant',2)
    from PyQt4 import QtCore, QtGui
    
    class TextEdit(QtGui.QTextEdit):
        pressed = QtCore.pyqtSignal()
        def keyPressEvent(self, event):
            if event.key() == QtCore.Qt.Key_Return:
                self.pressed.emit()
                # If you do not want a new line uncomment the following
                # return
            super(TextEdit, self).keyPressEvent(event)
    
    
    class CustomTextEditDelegate(QtGui.QItemDelegate):
        def createEditor(self, parent, option, index):
            editor = TextEdit(parent)
            editor.pressed.connect(self.commitAndCloseEditor)
            return editor
    
        def setEditorData(self, editor, index):
            editor.setText(index.data())
    
        def setModelData(self, editor, model, index):
            model.setData(index, editor.toPlainText())
    
        def commitAndCloseEditor(self):
            editor = self.sender()
            self.commitData.emit(editor)
            # if you want to close the editor uncomment the following
            # self.closeEditor.emit(editor, QtGui.QAbstractItemDelegate.NoHint)
    
    class PIX_DATABASE_UI(QtGui.QTableWidget):
        def __init__(self, parent=None):
            super(PIX_DATABASE_UI, self).__init__(parent)
    
            ### signal
            self.update_tableWidget()
            self.itemEntered.connect(self.update_MySQL)
            self.itemChanged.connect(self.update_MySQL)
    
        # -----------------------------------------------------------------------------------------------------------------#
        def update_MySQL(self, it):
            print("MySQL Updated", it.text())
    
        def update_tableWidget(self):
            self.filter_columns = [u'remark']
            self.setColumnCount(len(self.filter_columns))
            self.setHorizontalHeaderLabels(self.filter_columns)
            self.setRowCount(5)
    
            for row, col in itertools.product(range(5), range(len(self.filter_columns))):
                if self.filter_columns[col] == "remark":
                    width = self.sizeHint().width()
                    self.setColumnWidth(col, width * 0.75)
                    self.setItem(row, col, QtGui.QTableWidgetItem(str("ABC")))
                    self.setItemDelegateForColumn(col, CustomTextEditDelegate(self))
                    self.verticalHeader().setResizeMode(row, QtGui.QHeaderView.ResizeToContents)
    
    # -----------------------------------------------------------------------------------------------------------------#
    # -----------------------------------------------------------------------------------------------------------------#
    
    if __name__ == '__main__':
        global ui
        try:
            ui.close()
        except:
            pass
    
        app = QtGui.QApplication(sys.argv)
        app.setStyle(QtGui.QStyleFactory.create("Plastique"))
        # print QtGui.QStyleFactory.keys()
    
        ui = PIX_DATABASE_UI()
        ui.show()
        sys.exit(app.exec_())