QTableView + QSqlTableModel - Изменение значений в ячейках
Нужна помощь по настройке и редактированию таблицы для моего приложения. Есть база данных SQlite с одной простой таблицей, из которой берутся значения и вставляются в таблицу QTableView. Я создал кнопки "Add", "Change", "Delete" для редактированию строк в таблице. Но они работают неправильно.
Как сохранить добавленную строку в БД? После перезагрузки приложения новая строка не отображается и не создается в БД.
Не получается отредактировать уже имеющиеся строки. После ввода данных все значения пропадают и появляется знак !
Не получается внести изменения с помощью кнопки "Change", выходит ошибка "edit: editing failed".
Я сделал минимальный пример моего приложения. Пример создает в папке с приложением маленькую базу данных SQlite3, состоящую из 1 таблицы и 2 строк.
main.py
import sys, os, sqlite3
from datetime import datetime
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtCore import *
CONFIG_NAME = 'config.ini'
DB_NAME = 'nsi.db'
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__()
self.window_pref()
self.show_widgets()
def window_pref(self):
self.setWindowTitle('PyQt5 APP')
self.def_width = 800
self.def_height = 400
self.def_size = self.setMinimumSize(self.def_width, self.def_height)
def show_widgets(self):
self.createConnection()
self.fillDB()
self.setupMainWidgets()
def createConnection(self):
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(DB_NAME)
if not db.open():
QMessageBox.warning(self, 'PyQt5 APP',
'Error:{}'.format(db.lastError().text()))
sys.exit(1)
def fillDB(self):
query = QSqlQuery()
query.exec_("""\
CREATE TABLE sprav (
id_nsi INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
nsi_name TEXT UNIQUE NOT NULL,
file_date TEXT NOT NULL,
file_name TEXT NOT NULL)
""")
query.prepare("""\
INSERT INTO sprav (nsi_name, file_date, file_name)VALUES (?, ?, ?)
""")
sample_list = (('nsi1', 'january', 'file1'), ('nsi2', 'may', 'file2'))
for i in sample_list:
query.addBindValue(i[0])
query.addBindValue(i[1])
query.addBindValue(i[2])
query.exec_()
def setupMainWidgets(self):
mw_widget = QWidget()
main_panel = QHBoxLayout(mw_widget)
# SQL Table
self.modelSql = QSqlTableModel()
self.modelSql.setTable('sprav')
self.modelSql.setQuery(QSqlQuery(
'SELECT nsi_name, file_date, file_name FROM sprav'))
self.modelSql.setHeaderData(self.modelSql.fieldIndex('nsi_name'),
Qt.Horizontal, 'Name')
self.modelSql.setHeaderData(self.modelSql.fieldIndex('file_date'),
Qt.Horizontal, 'Date')
self.modelSql.setHeaderData(self.modelSql.fieldIndex('file_name'),
Qt.Horizontal, 'File')
self.modelSql.setEditStrategy(QSqlTableModel.OnFieldChange)
self.modelSql.select()
# QTableView()
self.table_view = QTableView()
self.table_view.setSelectionBehavior(1)
self.table_view.setAlternatingRowColors(True)
self.table_view.setModel(self.modelSql)
self.table_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
main_panel.addWidget(self.table_view)
# QVBoxLayout()
right_panel = QVBoxLayout()
line = QFrame()
line.setFrameShape(QFrame.HLine)
self.add_record = QPushButton('Add', self)
self.add_record.clicked.connect(self.addRecord)
self.change_record = QPushButton('Change', self)
self.change_record.clicked.connect(self.changeRecord)
self.delete_record = QPushButton('Delete', self)
self.delete_record.clicked.connect(self.delRecord)
right_panel.addSpacing(20)
right_panel.addWidget(line)
right_panel.addWidget(self.add_record)
right_panel.addWidget(self.change_record)
right_panel.addWidget(self.delete_record)
right_panel.addStretch()
main_panel.addLayout(right_panel)
self.setCentralWidget(mw_widget)
def addRecord(self):
row = self.modelSql.rowCount()
self.modelSql.insertRow(row)
index = self.modelSql.index(row, 0)
self.table_view.setCurrentIndex(index)
self.table_view.edit(index)
def delRecord(self):
cur_item = self.table_view.selectedIndexes()
for index in cur_item:
self.modelSql.removeRow(index.row())
self.modelSql.select()
def changeRecord(self):
self.table_view.edit(self.table_view.currentIndex())
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Ответы (1 шт):
Попробуйте так:
import sys
import os
import sqlite3
from datetime import datetime
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *
from PyQt5.QtSql import *
CONFIG_NAME = 'config.ini'
DB_NAME = 'nsi.db'
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
class Dialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('Input Dialog')
self.line_edit_name = QLineEdit()
self.line_edit_date = QLineEdit()
self.line_edit_file = QLineEdit()
form_layout = QFormLayout()
form_layout.addRow('Name:', self.line_edit_name)
form_layout.addRow('Date:', self.line_edit_date)
form_layout.addRow('File:', self.line_edit_file)
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
main_layout = QVBoxLayout()
main_layout.addLayout(form_layout)
main_layout.addWidget(button_box)
self.setLayout(main_layout)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.window_pref()
self.show_widgets()
def window_pref(self):
self.setWindowTitle('PyQt5 APP')
self.def_width = 800
self.def_height = 400
self.def_size = self.setMinimumSize(self.def_width, self.def_height)
def show_widgets(self):
self.createConnection()
self.fillDB()
self.setupMainWidgets()
def createConnection(self):
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(DB_NAME)
if not db.open():
QMessageBox.warning(self, 'PyQt5 APP',
'Error:{}'.format(db.lastError().text()))
sys.exit(1)
def fillDB(self):
query = QSqlQuery()
query.exec_("""\
CREATE TABLE sprav (
id_nsi INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
nsi_name TEXT UNIQUE NOT NULL,
file_date TEXT NOT NULL,
file_name TEXT NOT NULL)
""")
query.prepare("""\
INSERT INTO sprav (nsi_name, file_date, file_name)VALUES (?, ?, ?)
""")
sample_list = (('nsi1', 'january', 'file1'), ('nsi2', 'may', 'file2'))
for i in sample_list:
print(i)
query.addBindValue(i[0])
query.addBindValue(i[1])
query.addBindValue(i[2])
query.exec_()
def setupMainWidgets(self):
mw_widget = QWidget()
main_panel = QHBoxLayout(mw_widget)
# QSqlTableModel
self.modelSql = QSqlTableModel()
self.modelSql.setTable('sprav')
self.modelSql.setEditStrategy(QSqlTableModel.OnFieldChange)
self.modelSql.select()
# - self.modelSql.setQuery(QSqlQuery(
# - 'SELECT nsi_name, file_date, file_name FROM sprav'))
self.modelSql.setHeaderData(0, Qt.Horizontal, "Id")
self.modelSql.setHeaderData(1, Qt.Horizontal, 'Name')
self.modelSql.setHeaderData(2, Qt.Horizontal, 'Date')
self.modelSql.setHeaderData(3, Qt.Horizontal, 'File')
# QTableView()
self.table_view = QTableView()
self.table_view.setSelectionBehavior(1)
self.table_view.setAlternatingRowColors(True)
self.table_view.setModel(self.modelSql)
# https://doc.qt.io/qt-5/qtableview.html#setColumnHidden
self.table_view.setColumnHidden(0, True)
# ??? [self.table_view.resizeColumnToContents(i) for i in (1, 2, 3)]
self.table_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
# ??? - self.table_view.selectionModel().selectionChanged.connect(
# ??? - self.on_selection_changed)
main_panel.addWidget(self.table_view)
# QVBoxLayout()
right_panel = QVBoxLayout()
line = QFrame()
line.setFrameShape(QFrame.HLine)
self.add_record = QPushButton('Add', self)
self.add_record.clicked.connect(self.addRecord)
self.change_record = QPushButton('Change', self)
self.change_record.clicked.connect(self.changeRecord)
# self.change_record.setEnabled(False)
self.delete_record = QPushButton('Delete', self)
self.delete_record.clicked.connect(self.delRecord)
# self.delete_record.setEnabled(False)
right_panel.addSpacing(20)
right_panel.addWidget(line)
right_panel.addWidget(self.add_record)
right_panel.addWidget(self.change_record)
right_panel.addWidget(self.delete_record)
right_panel.addStretch()
main_panel.addLayout(right_panel)
self.setCentralWidget(mw_widget)
self.add_record.setFocus() # +++
def addRecord(self):
'''
row = self.modelSql.rowCount()
self.modelSql.insertRow(row)
index = self.modelSql.index(row, 0)
self.table_view.setCurrentIndex(index)
self.table_view.edit(index)
'''
inputDialog = Dialog()
rez = inputDialog.exec()
if not rez:
msg = QMessageBox.information(self, 'Внимание', 'Диалог сброшен.')
return
name = inputDialog.line_edit_name.text()
date = inputDialog.line_edit_date.text()
file = inputDialog.line_edit_file.text()
if not name or not date or not file:
msg = QMessageBox.information(self, 'Внимание', 'Заполните пожалуйста все поля.')
return
r = self.modelSql.record()
r.setValue("nsi_name", name)
r.setValue("file_date", date)
r.setValue("file_name", file)
self.modelSql.insertRecord(-1, r)
self.modelSql.select()
def delRecord(self):
'''
cur_item = self.table_view.selectedIndexes()
for index in cur_item:
self.modelSql.removeRow(index.row())
self.modelSql.select()
'''
row = self.table_view.currentIndex().row()
if row == -1:
msg = QMessageBox.information(self, 'Внимание', 'Выберите запись для удаления.')
return
name = self.modelSql.record(row).value(1)
date = self.modelSql.record(row).value(2)
file = self.modelSql.record(row).value(3)
inputDialog = Dialog()
inputDialog.setWindowTitle('Удалить запись ???')
inputDialog.line_edit_name.setText(name)
inputDialog.line_edit_date.setText(date)
inputDialog.line_edit_file.setText(file)
rez = inputDialog.exec()
if not rez:
msg = QMessageBox.information(self, 'Внимание', 'Диалог сброшен.')
return
self.modelSql.removeRow(self.table_view.currentIndex().row())
self.modelSql.select()
msg = QMessageBox.information(self, 'Успех', 'Запись удалена.')
def changeRecord(self):
self.table_view.edit(self.table_view.currentIndex())
''' ???
def getDirectory(self):
dirlist = QFileDialog.getExistingDirectory(self, "Choose folder")
return dirlist
def on_selection_changed(self):
self.change_record.setEnabled(
bool(self.table_view.selectionModel().selectedRows()))
self.delete_record.setEnabled(
bool(self.table_view.selectionModel().selectedRows()))
'''
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Update:
Я попробовал ваши вариант, проблема осталась - если удалить строку, а затем открыть приложение заново, то строка появляется снова. Надо чтобы изменения сохранялись в БД. Причем, если добавить запись, то при повторном открытии новая запись отобразится. Не сохраняется только удаленная запись.
Вы про удаление в своем вопросе ничего не спрашивали и это от меня шло вам бонусом.
Все работает как надо.
Вы видимо удаляете одну из записей, которую в методе
fillDB()постоянно создаете:sample_list = (('nsi1', 'january', 'file1'), ('nsi2', 'may', 'file2')) for i in sample_list: print(i) query.addBindValue(i[0]) query.addBindValue(i[1]) query.addBindValue(i[2]) query.exec_()
Удалите вызов метода:
# self.fillDB()
после того как вы первый раз создали таблицу.



