Search code examples
python-3.xpyqt5qtableviewqsqltablemodel

How to copy selected row data from QSqlTableModel tableview into reqired Qlineedit input widgets?


Example images: enter image description here

Below is my example code:

from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5 import uic
import sys
import sqlite3


class UI(QMainWindow):
    def __init__(self):
        super(UI, self).__init__()
        uic.loadUi("tableview.ui", self)
        self.show()

        db = QSqlDatabase.addDatabase('QSQLITE')
        db.setDatabaseName('book.db')
        db.open()

        self.model = QtSql.QSqlTableModel(self)
        self.model.setTable("card")
        self.model.select()
        self.tableView.setModel(self.model)

        #self.view_msa()

        self.pushButton.clicked.connect(self.edit_items)

    def edit_view(self):

        self.editWindow = QtWidgets.QWidget()
        self.editWindow.resize(300, 300)
        self.editWindow.setWindowTitle("Edit Window")
        self.editWindow.setWindowModality(Qt.ApplicationModal)
        
        self.update = QtWidgets.QPushButton(self.editWindow)
        self.update.setGeometry(QtCore.QRect(60, 150, 75, 23))
        self.update.setObjectName("update")
        self.widget = QtWidgets.QWidget(self.editWindow)
        self.widget.setGeometry(QtCore.QRect(60, 60, 201, 74))
        self.widget.setObjectName("widget")
        self.formLayout = QtWidgets.QFormLayout(self.widget)
        self.formLayout.setContentsMargins(0, 0, 0, 0)
        self.formLayout.setObjectName("formLayout")
        self.label = QtWidgets.QLabel(self.widget)
        self.label.setObjectName("label")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
        self.name_line = QtWidgets.QLineEdit(self.widget)
        self.name_line.setObjectName("name_line")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.name_line)
        self.label_2 = QtWidgets.QLabel(self.widget)
        self.label_2.setObjectName("label_2")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2)
        self.age_line = QtWidgets.QLineEdit(self.widget)
        self.age_line.setObjectName("age_line")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.age_line)
        self.label_3 = QtWidgets.QLabel(self.widget)
        self.label_3.setObjectName("label_3")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3)
        self.address_line = QtWidgets.QLineEdit(self.widget)
        self.address_line.setObjectName("address_line")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.address_line)

        self.update.setText("Update")
        self.label.setText("Name")
        self.label_2.setText("Age")
        self.label_3.setText("Address")

        self.name_line.setReadOnly(True)
        
        self.editWindow.show()

    def edit_items(self):
        index = (self.tableView.selectionModel().currentIndex())
        id_us = (self.tableView.model().data(index))
        print(str(id_us))
        self.edit_view()
        
app = QApplication(sys.argv)
window = UI()
app.exec_()

I am using: self.model = QtSql.QSqlTableModel, Qtableview, 3 Lineedit input widgets and 2 QPushbuttons. My SQLite3 database have 3 columns that are " name, age(int values) and gender " My Question is: How is it possible to do that if i select a row from tableview that 3 items should be copy into their respective lineedit input widgets as name, age and address. The name Editline is read-only, Then again if the update button is pressed, the QSqlTableModel should be updated with the reference of name lineedit(the name lineedit is read-only).


Solution

  • You need to access the individual indexes for each column you're interested into. This can be done by calling index.sibling or via a simple model.index(row, column) as soon as you know the current row.

    Then, you can use a QDialog, which is better than a QWidget with the window modality set, as you can use its exec_() method to "block" the function until the dialog is closed.

    In the following example I've connected the update button to the dialog.accept() slot, in order to update the data only when the button is clicked, otherwise if the dialog is cancelled (by pressing Esc or closing it) no change is applied.

    As you can see, I didn't set any instance attribute (as you did with self.editWindow, etc), as their reference is only important within the function scope, and since you're going to recreate the dialog each time there's no use in setting them as attributes.

    class UI(QMainWindow):
        def __init__(self):
            # ...
            self.pushButton.clicked.connect(self.edit_items)
    
        def edit_items(self):
            if not self.model.rowCount():
                return
            index = self.tableView.currentIndex()
            if index.isValid():
                row = index.row()
            else:
                row = 0
    
            dialog = QtWidgets.QDialog()
            dialog.setWindowTitle("Edit Window")
            layout = QtWidgets.QVBoxLayout(dialog)
    
            formLayout = QtWidgets.QFormLayout()
            layout.addLayout(formLayout)
    
            name_line = QtWidgets.QLineEdit(self.model.index(row, 0).data())
            formLayout.addRow('Name', name_line)
            name_line.setReadOnly(True)
    
            age_edit = QtWidgets.QSpinBox()
            formLayout.addRow('Age', age_edit)
            age_edit.setFocus()
            age_edit.setValue(self.model.index(row, 1).data())
    
            genders = 'M', 'F'
            gender_combo = QtWidgets.QComboBox()
            formLayout.addRow('Gender', gender_combo)
            gender_combo.addItems(genders)
            gender = self.model.index(row, 2).data()
            if gender and gender.upper() in genders:
                gender_combo.setCurrentIndex(genders.index(gender.upper()))
            else:
                gender_combo.setCurrentIndex(-1)
    
            updateButton = QtWidgets.QPushButton('Update')
            layout.addWidget(updateButton)
            updateButton.clicked.connect(dialog.accept)
    
            if not dialog.exec_():
                return
    
            self.model.setData(self.model.index(row, 1), age_edit.value(), 
                QtCore.Qt.EditRole)
            if gender_combo.currentIndex() >= 0:
                self.model.setData(self.model.index(row, 2), 
                    gender_combo.currentText(), QtCore.Qt.EditRole)
            # submit all changes to the database
            self.model.submitAll()
    

    Note: avoid overwriting existing class attributes, as you did with self.update.