I created a PyQt5 window where the data of an SQL table is shown in a frame (QtSql.QSqlTableModel, QTableView) and a Button to insert new data (InsertButton
). In Order to refresh the shown table, I used self.MonthTab.InsertButton.clicked.connect(self.startMonth)
, and filtered the values with QSortFilterProxyModel
and setFilterFixedString
by the year chosen in the combobox, which works fine so far.
But when I filter the entries (showing e. g. 2020 entries only) and insert a new entry, the table is updated by rebuilding the window and the combobox is set back to default values. Accordingly, I don't see the filtered 2020 entries any more.
I want to keep the combobox settings when the table is updated, so I tried to update the table frame only (update()
, hide()
/show()
, call Month class(createView()
and handle_db
) instead of the Month window), but the changes are always invisible until (manually) reloading the Month window. To my understanding, startMonth puts the Month class elements in the Month window, and the comboboxes have to be in the Month class to be seen on Month window, which would be a dilemma when trying to update the frame but not the comboboxes, which makes no sense. So probably I got something wrong about PyQt windows and QWidgets.
Is it possible to update just a widget during runtime without reloading the whole window? Is it possible to show widgets in the Month window which are not placed in the Month class? I'm quite new to PyQt and Qt and I googled and tried a lot, but until now without success, so I hope you may give me a hint what's wrong or how to solve it.
class Month(Menu):
def __init__(self, parent=Menu):
super(Month, self).__init__(parent)
self.x = datetime.datetime.now()
self.year = QtWidgets.QComboBox(self)
self.year.setGeometry(100, 80, 100, 24)
self.year.setMouseTracking(False)
self.year.addItem(self.x.strftime("%G"))
self.year.addItem("2019")
self.year.addItem("2020")
self.year.setEditable(False)
self.year.currentTextChanged.connect(self.date_filter)
self.InsertButton = QtWidgets.QPushButton(self)
self.InsertButton.setText("Insert")
self.InsertButton.clicked.connect(self.clicked) # inserts values into sql database table
self.handle_db()
self.createView("Table Model (View 1)", self.model)
def createDB(self): ... # provides sql-connection, creates database
def initializeModel(self, model): ...
def createView(self, title, model): ...
def handle_db(self): ... # creates model, proxymodel, view, layout and frame for showing table
def date_filter(self):
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setGeometry(200, 50, 900, 650)
print("MainWindow")
self.startMonth()
def startMonth(self):
self.MonthTab = Month(self)
self.setWindowTitle("Month")
self.setCentralWidget(self.MonthTab)
self.MonthTab.InsertButton.clicked.connect(self.startMonth) # rebuilds Month window -> table is updated
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
Month().createDB()
w = MainWindow()
sys.exit(app.exec_())
I'm using Python 3.8, PyQt5 5.15.0, PyCharm 2020.2 and Linux Mint 19.3.
I fear the "minimal reproducible example" is quite large:
import sys
from PyQt5 import QtWidgets, QtSql, QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, \
QVBoxLayout, QTableView, QPushButton, \
QMessageBox, qApp, QFrame
from PyQt5.QtCore import QSortFilterProxyModel
import datetime
class Menu(QWidget): # die allgemeine Fenster-Parent-Class
def __init__(self, parent=None):
super(Menu, self).__init__()
self.MonatBTN = QPushButton("Monat", self)
self.MonatBTN.move(20, 30)
class Monat(Menu):
def __init__(self, parent=Menu):
super(Monat, self).__init__(parent)
self.x = datetime.datetime.now()
self.jahr = QtWidgets.QComboBox(self)
self.jahr.setGeometry(100, 80, 100, 24)
self.jahr.setMouseTracking(False)
self.jahr.addItem(self.x.strftime("%G"))
self.jahr.addItem("2019")
self.jahr.addItem("2020")
self.jahr.setEditable(False)
self.jahr.currentTextChanged.connect(self.date_filter)
self.date_frame = QFrame(self)
self.date_frame.setFrameShape(QFrame.StyledPanel)
self.date_frame.setLineWidth(1)
self.date_frame.setGeometry(100, 500, 161, 50)
self.date_picker = QtWidgets.QDateEdit(calendarPopup=True)
self.date_picker.setDateTime(QtCore.QDateTime.currentDateTime())
self.date_layout = QVBoxLayout()
self.date_layout.addWidget(self.date_picker)
self.date_frame.setLayout(self.date_layout)
self.date_frame.show()
self.posten = QtWidgets.QComboBox(self)
self.posten.setGeometry(300, 513, 161, 24)
self.posten.setMouseTracking(False)
self.posten.addItem("")
self.posten.addItem("Apple")
self.posten.addItem("Peach")
self.posten.addItem("Orange")
self.posten.setEditable(True)
self.wert = QtWidgets.QComboBox(self)
self.wert.setGeometry(500, 513, 161, 24)
self.wert.setMouseTracking(False)
self.wert.addItem("")
self.wert.addItem("20")
self.wert.addItem("300")
self.wert.addItem("-15")
self.wert.setEditable(True)
self.InsertButton = QtWidgets.QPushButton(self)
self.InsertButton.setText("Einfügen")
self.InsertButton.move(700, 513)
self.InsertButton.clicked.connect(self.clicked)
self.handle_db()
self.createView("Table Model (View 1)", self.model)
def createDB(self):
print("creating")
self.cash_db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
self.cash_db.setDatabaseName('cash.db')
if not self.cash_db.open():
QMessageBox.critical(None, qApp.tr("Cannot open database"),
qApp.tr("Unable to establish a database connection.\n"
"Click Cancel to exit."),
QMessageBox.Cancel)
return False
query = QtSql.QSqlQuery()
# query.exec_("create table cash(id int primary key, "
query.exec_("create table cash( "
"date varchar(20), datum varchar(20), posten varchar(40), wert varchar(20))")
return True
def initializeModel(self, model):
self.model.setTable('cash')
self.model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
self.model.select()
self.model.setHeaderData(0, QtCore.Qt.Horizontal, "Date")
self.model.setHeaderData(1, QtCore.Qt.Horizontal, "Datum")
self.model.setHeaderData(2, QtCore.Qt.Horizontal, "Posten")
self.model.setHeaderData(3, QtCore.Qt.Horizontal, "Wert")
def createView(self, title, model):
self.view = QTableView()
self.view.setModel(model)
self.view.setWindowTitle(title)
return self.view
def handle_db(self):
print("handling")
self.model = QtSql.QSqlTableModel()
self.delrow = -1
self.initializeModel(self.model)
self.proxyModel = QSortFilterProxyModel()
self.proxyModel.setSourceModel(self.model)
self.view = self.createView("Table Model (View 1)", self.model)
self.view.setModel(self.proxyModel)
self.view.setSortingEnabled(True)
self.view.hideColumn(0)
self.view.header = self.view.horizontalHeader()
self.view.header.resizeSection(1, 100)
self.view.header.resizeSection(2, 466)
self.view.header.resizeSection(3, 100)
self.view.resizeRowsToContents()
self.proxyModel.setSourceModel(self.model)
self.view.setModel(self.proxyModel)
self.frame = QFrame(self)
self.frame.setFrameShape(QFrame.StyledPanel)
self.frame.setLineWidth(1)
self.frame.setGeometry(100, 120, 700, 300)
self.layout = QVBoxLayout()
self.layout.addWidget(self.view)
self.frame.setLayout(self.layout)
self.frame.show()
def date_filter(self):
jahr = self.jahr.currentText()
filter = jahr
self.proxyModel.setFilterFixedString(filter)
self.view.resizeRowsToContents()
def clicked(self):
d = self.date_picker.date()
date = d.toPyDate()
datum = (date.strftime("%d.%m.%Y"))
posten = self.posten.currentText()
v = self.wert.currentText()
val = ('%10.2f') % float(v)
wert = str(val)
query = QtSql.QSqlQuery()
query.exec_(
"insert into cash (date, datum, posten, wert) "
"values('%s', '%s', '%s', '%s')" % (date, datum, posten, wert))
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setGeometry(200, 50, 900, 650)
print("MainWindow")
self.startMonat()
def startMonat(self):
self.MonatTab = Monat(self)
self.setWindowTitle("Cash - Monat")
self.setCentralWidget(self.MonatTab)
self.MonatTab.InsertButton.clicked.connect(self.startMonat)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
Monat().createDB()
w = MainWindow()
sys.exit(app.exec_())
If you want to insert data into the table then you must use the QSqlRecord associated with the model:
def clicked(self):
d = self.date_picker.date()
date = d.toPyDate()
datum = date.strftime("%d.%m.%Y")
posten = self.posten.currentText()
v = self.wert.currentText()
val = ("%10.2f") % float(v)
wert = str(val)
record = self.model.record()
record.setValue("date", date)
record.setValue("datum", datum)
record.setValue("posten", posten)
record.setValue("wert", wert)
self.model.insertRecord(-1, record)
Note: Remove self.MonatTab.InsertButton.clicked.connect(self.startMonat)