Python Forum
[PyQt] PyQt5 QTableView SQLite : edit cell
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] PyQt5 QTableView SQLite : edit cell
#1
Hi!
I'm newbie with PyQt5 and now I have an app for Windows with PyQt5 and a SQLite database. Showing the data in QTableView works fine, even with cell format like numbers right aligned with fixed decimals, etc. What I can't achieve is to edit data in a QTableView cell and write the changed data back to the SQLite database. Does anyone know an example app (github, etc.) or a tutorial or something similar? I could show and edit the data in extra textfields but I want to edit the data directly in the QTableView.

Thanks
Hein
Reply
#2
You can use

model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)

This is an old project where I used it. Double click and edit cell.

from PyQt5 import QtSql, QtPrintSupport
from PyQt5.QtGui import QTextDocument, QIcon, QTextCursor, QTextTableFormat
from PyQt5.QtCore import QFileInfo, Qt, QSettings, QSize, QFile, QTextStream
from PyQt5.QtWidgets import (QMainWindow, QTableView, QDialog, QGridLayout, QPushButton, 
                                                            QLineEdit, QWidget, QFileDialog, QComboBox, QMessageBox, QApplication)
import sys
###################################
class MyWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MyWindow, self).__init__()
        self.setObjectName("SqliteViewer")
        root = QFileInfo(__file__).absolutePath()
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.settings = QSettings('Axel Schneider', self.objectName())
        self.viewer = QTableView()
        self.db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
        self.model = QtSql.QSqlTableModel()
        self.delrow = -1
        self.dbfile = ""
        self.tablename = ""
        self.headers = []
        self.results = ""
        self.mycolumn = 0
        self.viewer.verticalHeader().setVisible(False)
        self.setStyleSheet(stylesheet(self))        
        self.viewer.setModel(self.model)
        self.viewer.clicked.connect(self.findrow)
        self.viewer.selectionModel().selectionChanged.connect(self.getCellText)
        
        self.dlg = QDialog()
        self.layout = QGridLayout()
        self.layout.addWidget(self.viewer,0, 0, 1, 4)
        
        addBtn = QPushButton("insert row")
        addBtn.setIcon(QIcon.fromTheme("add"))
        addBtn.setFixedWidth(110)
        addBtn.clicked.connect(self.addrow)
        self.layout.addWidget(addBtn, 1, 0)
        
        delBtn = QPushButton("delete row")
        delBtn.setIcon(QIcon.fromTheme("remove"))
        delBtn.setFixedWidth(110)
        delBtn.clicked.connect(self.deleteRow)
        self.layout.addWidget(delBtn,1, 1)

        self.editor = QLineEdit()
        self.editor.returnPressed.connect(self.editCell)
        self.editor.setStatusTip("ENTER new value")
        self.editor.setToolTip("ENTER new value")
        self.layout.addWidget(self.editor,1, 2)

        self.findfield = QLineEdit()
        self.findfield.addAction(QIcon.fromTheme("edit-find"), 0)
        self.findfield.returnPressed.connect(self.findCell)
        self.findfield.setFixedWidth(200)
        self.findfield.setPlaceholderText("find")
        self.findfield.setStatusTip("ENTER to find")
        self.findfield.setToolTip("ENTER to find")
        self.layout.addWidget(self.findfield,1, 3)

        self.myWidget = QWidget()
        self.myWidget.setLayout(self.layout)

        self.createToolbar()
        self.statusBar().showMessage("Ready")
        self.setCentralWidget(self.myWidget)
        self.setWindowIcon(QIcon.fromTheme("office-database"))
        self.setGeometry(20,20,600,450)
        self.setWindowTitle("SqliteViewer")
        self.readSettings()
        self.msg("Ready")
        self.viewer.setFocus()

    def createToolbar(self):
        self.actionOpen = QPushButton("Open DB")
        self.actionOpen.clicked.connect(self.fileOpen)
        icon = QIcon.fromTheme("document-open")
        self.actionOpen.setShortcut("Ctrl+O")
        self.actionOpen.setShortcutEnabled(True)
        self.actionOpen.setIcon(icon)
        self.actionOpen.setObjectName("actionOpen")
        self.actionOpen.setStatusTip("Open Database")
        self.actionOpen.setToolTip("Open Database")

        self.actionHide = QPushButton()
        self.actionHide.clicked.connect(self.toggleVerticalHeaders)
        icon = QIcon.fromTheme("pane-hide-symbolic")
        self.actionHide.setIcon(icon)
        self.actionHide.setToolTip("toggle vertical Headers")
        self.actionHide.setShortcut("F3")
        self.actionHide.setShortcutEnabled(True)
        self.actionHide.setStatusTip("toggle vertical Headers")

        ### first row as headers
        self.actionHeaders = QPushButton()
        self.actionHeaders.clicked.connect(self.selectedRowToHeaders)
        icon = QIcon.fromTheme("ok")
        self.actionHeaders.setIcon(icon)
        self.actionHeaders.setToolTip("selected row to headers")
        self.actionHeaders.setShortcut("F5")
        self.actionHeaders.setShortcutEnabled(True)
        self.actionHeaders.setStatusTip("selected row to headers")

        self.actionPreview = QPushButton()
        self.actionPreview.clicked.connect(self.handlePreview)
        icon = QIcon.fromTheme("document-print-preview")
        self.actionPreview.setShortcut("Shift+Ctrl+P")
        self.actionPreview.setShortcutEnabled(True)
        self.actionPreview.setIcon(icon)
        self.actionPreview.setObjectName("actionPreview")
        self.actionPreview.setStatusTip("Print Preview")
        self.actionPreview.setToolTip("Print Preview")

        self.actionPrint = QPushButton()
        self.actionPrint.clicked.connect(self.handlePrint)
        icon = QIcon.fromTheme("document-print")
        self.actionPrint.setShortcut("Shift+Ctrl+P")
        self.actionPrint.setShortcutEnabled(True)
        self.actionPrint.setIcon(icon)
        self.actionPrint.setObjectName("actionPrint")
        self.actionPrint.setStatusTip("Print")
        self.actionPrint.setToolTip("Print")

        ###############################
        self.tb = self.addToolBar("ToolBar")
        self.tb.setIconSize(QSize(16, 16))
        self.tb.setMovable(False)
        self.tb.addWidget(self.actionOpen)
        self.tb.addSeparator()
        self.tb.addWidget(self.actionPreview)
        self.tb.addWidget(self.actionPrint)
        ### sep
        self.tb.addSeparator()
        self.tb.addSeparator()
        ### popupMenu
        self.pop = QComboBox()
        self.pop.setFixedWidth(200)
        self.pop.currentIndexChanged.connect(self.setTableName)
        self.tb.addWidget(self.pop)
        self.tb.addSeparator()
        self.tb.addWidget(self.actionHide)
        self.addToolBar(self.tb)

    def deleteRow(self):
        row = self.viewer.currentIndex().row()
        self.model.removeRow(row)
        self.initializeModel()
        self.viewer.selectRow(row)

    def selectedRowToHeaders(self):
        if self.model.rowCount() > 0:
            headers = []
            row = self.selectedRow()
            for column in range(self.model.columnCount()):
                headers.append(self.model.data(self.model.index(row, column)))
                self.model.setHeaderData(column, Qt.Horizontal, headers[column], Qt.EditRole)
            print(headers)

    def findCell(self):
        column = 0
        ftext = self.findfield.text()
        model = self.viewer.model()
        if self.viewer.selectionModel().hasSelection():
            row =  self.viewer.selectionModel().selectedIndexes()[0].row() 
            row = row + 1
        else:
            row = 0
        start = model.index(row, column)
        matches = model.match(start, Qt.DisplayRole,ftext, 1, Qt.MatchContains)
        if matches:
            print("found", ftext, matches)
            index = matches[0]
            self.viewer.selectionModel().select(index, QItemSelectionModel.Select)
        else:
            column = column + 1
            self.findNextCell(column)

    def findNextCell(self, column):
        self.viewer.clearSelection()
        ftext = self.findfield.text()
        model = self.viewer.model()
        if self.viewer.selectionModel().hasSelection():
            row =  self.viewer.selectionModel().selectedIndexes()[0].row()
            row = row + 1
        else:
            row = 0
        start = model.index(row, column)
        matches = model.match(start, Qt.DisplayRole,ftext, 1, Qt.MatchContains)
        if matches:
            print("found", ftext)
            index = matches[0]
            self.viewer.selectionModel().select(index, QItemSelectionModel.Select)
        else:
            column = column + 1
            self.findNextCell(column)

    def toggleVerticalHeaders(self):
        if self.viewer.verticalHeader().isVisible() == False:
            self.viewer.verticalHeader().setVisible(True)
        else:
            self.viewer.verticalHeader().setVisible(False)

    def fileOpen(self):
        tablelist = []
        fileName, _ = QFileDialog.getOpenFileName(None, "Open Database File", "/home/brian/Dokumente/DB", "DB (*.sqlite *.db *.sql3);; All Files (*.*)")
        if fileName:
            self.fileOpenStartup(fileName)

    def fileOpenStartup(self, fileName):
        tablelist = []
        if fileName:
            self.db.close()
            self.dbfile = fileName
            self.db.setDatabaseName(self.dbfile)
            self.db.open()
            print("Tables:", self.db.tables())
            tablelist = self.db.tables()
            self.fillComboBox(tablelist)
            self.msg("please choose Table from the ComboBox")

    def setAutoWidth(self):
        self.viewer.resizeColumnsToContents()

    def fillComboBox(self, tablelist):
        self.pop.clear()
        self.pop.insertItem(0, "choose Table ...")
        self.pop.setCurrentIndex(0)
        for row in tablelist:
            self.pop.insertItem(self.pop.count(), row)
        if self.pop.count() > 1:
            self.pop.setCurrentIndex(1)
            self.setTableName()

    def getCellText(self):
        if self.viewer.selectionModel().hasSelection():
            item = self.viewer.selectedIndexes()[0]
            if not item == None:
                name = item.data()
            else:
                name = ""
            self.editor.setText(str(name))
        else:
            self.editor.clear()

    def editCell(self):
        item = self.viewer.selectedIndexes()[0]
        row = self.selectedRow()
        column = self.selectedColumn()
        self.model.setData(item, self.editor.text())

    def setTableName(self):
        if not self.pop.currentText() == "choose Table ...":
            self.tablename = self.pop.currentText()
            print("DB is:", self.dbfile)
            self.msg("initialize")
            self.initializeModel()

    def initializeModel(self):
        print("Table selected:", self.tablename)
        self.model.setTable(self.tablename)
        self.model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
        self.model.select()
        self.setAutoWidth()
        self.msg(self.tablename + " loaded *** " + str(self.model.rowCount()) + " records")

    def addrow(self):
        row =  self.viewer.selectionModel().selectedIndexes()[0].row()
        ret = self.model.insertRow(row)
        if ret:
            self.viewer.selectRow(row)
            item = self.viewer.selectedIndexes()[0]
            self.model.setData(item, str(row))
        
    def findrow(self, i):
        self.delrow = i.row()

    def selectedRow(self):
        if self.viewer.selectionModel().hasSelection():
            row =  self.viewer.selectionModel().selectedIndexes()[0].row()
            return int(row)

    def selectedColumn(self):
        column =  self.viewer.selectionModel().selectedIndexes()[0].column()
        return int(column)

    def closeEvent(self, e):
        self.writeSettings()
        e.accept()

    def readSettings(self):
        print("reading settings")
        if self.settings.contains('geometry'):
            self.setGeometry(self.settings.value('geometry'))

    def writeSettings(self):
        print("writing settings")
        self.settings.setValue('geometry', self.geometry())

    def msg(self, message):
        self.statusBar().showMessage(message)

    def handlePrint(self):
        if self.model.rowCount() == 0:
            self.msg("no rows")
        else:
            dialog = QtPrintSupport.QPrintDialog()
            if dialog.exec_() == QDialog.Accepted:
                self.handlePaintRequest(dialog.printer())
                self.msg("Document printed")

    def handlePreview(self):
        if self.model.rowCount() == 0:
            self.msg("no rows")
        else:
            dialog = QtPrintSupport.QPrintPreviewDialog()
            dialog.setFixedSize(1000,700)
            dialog.paintRequested.connect(self.handlePaintRequest)
            dialog.exec_()
            self.msg("Print Preview closed")

    def handlePaintRequest(self, printer):
        printer.setDocName(self.tablename)
        document = QTextDocument()
        cursor = QTextCursor(document)
        model = self.viewer.model()
        tableFormat = QTextTableFormat()
        tableFormat.setBorder(0.2)
        tableFormat.setBorderStyle(3)
        tableFormat.setCellSpacing(0);
        tableFormat.setTopMargin(0);
        tableFormat.setCellPadding(4)
        table = cursor.insertTable(model.rowCount() + 1, model.columnCount(), tableFormat)
        model = self.viewer.model()
        ### get headers
        myheaders = []
        for i in range(0, model.columnCount()):
            myheader = model.headerData(i, Qt.Horizontal)
            cursor.insertText(myheader)
            cursor.movePosition(QTextCursor.NextCell)
        ### get cells
        for row in range(0, model.rowCount()):
           for col in range(0, model.columnCount()):
               index = model.index( row, col )
               cursor.insertText(str(index.data()))
               cursor.movePosition(QTextCursor.NextCell)
        document.print_(printer)

def stylesheet(self):
        return """
        QTableView
        {
            border: 1px solid grey;
            border-radius: 0px;
            font-size: 8pt;
            background-color: #e8eaf3;
            selection-color: #ffffff;
        }
        QTableView::item:hover
        {   
            color: black;
            background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #729fcf, stop:1 #d3d7cf);           
        }
        
        QTableView::item:selected 
        {
            color: #F4F4F4;
            background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #6169e1, stop:1 #3465a4);
        } 

        QStatusBar
        {
            font-size: 7pt;
            color: #57579e;
        }

        QPushButton
        {
            font-size: 8pt;
            icon-size: 16px;
        }

        QPushButton:hover
        {   
            color: black;
            background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #729fcf, stop:1 #d3d7cf);           
            border: 1px solid #b7b7b7 inset;
            border-radius: 3px;
        }
        QComboBox
        {
            font-size: 8pt;
        }
    """
###################################     
if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setApplicationName('MyWindow')
    main = MyWindow("")
    main.show()
    if len(sys.argv) > 1:
        print(sys.argv[1])
        main.fileOpenStartup(sys.argv[1])
    sys.exit(app.exec_())
Reply
#3
Thanks Axel, that's great.
Clap
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] QStyledItemDelegate and QTableView malonn 1 1,632 Feb-07-2023, 07:15 PM
Last Post: malonn
  [PyQt] QTableView set header labels HeinKurz 2 6,713 Jan-23-2023, 08:46 AM
Last Post: HeinKurz
  [PyQt] Determine whether text in QTableView cell is fully visible or not random_nick 0 980 Oct-27-2022, 09:29 PM
Last Post: random_nick
  [PyQt] QTableView: scroll to top cell of the screen random_nick 2 2,831 Oct-08-2022, 12:29 AM
Last Post: random_nick
  [PyQt] [Solved]Add a Blank Row To QTableView Extra 3 5,498 Oct-02-2022, 04:53 PM
Last Post: Extra
  How to update the list of a combo box in a QTableView panoss 10 6,234 Feb-05-2022, 03:24 PM
Last Post: panoss
  [PyQt] How to Copy-Paste a table from Office apps to QTableView? Vittorio 5 7,229 Aug-05-2021, 11:14 AM
Last Post: Axel_Erfurt
  [PyQt] Qtableview adapte size to WBPYTHON 3 11,257 Mar-23-2020, 01:51 AM
Last Post: deanhystad
  PyQt5: How do you set the user input of a line edit to a specific variable? YoshikageKira 17 11,586 Dec-26-2019, 03:18 PM
Last Post: Denni
  Huge code problems (buttons(PyQt5),PyQt5 Threads, Windows etc) ZenWoR 0 2,821 Apr-06-2019, 11:15 PM
Last Post: ZenWoR

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020