在 PyQt5 中刷新 window 中的小部件时是否需要重新加载整个 window?

Is it necessary to reload the whole window when refreshing a widget in a window in PyQt5?

我创建了一个 PyQt5 window,其中 SQL table 的数据显示在一个框架(QtSql.QSqlTableModel,QTableView)和一个用于插入新数据的按钮中(InsertButton)。为了刷新显示的 table,我使用了 self.MonthTab.InsertButton.clicked.connect(self.startMonth),并根据在组合框中选择的年份使用 QSortFilterProxyModelsetFilterFixedString 过滤值,到目前为止效果很好。 但是当我过滤条目(仅显示例如 2020 条目)并插入一个新条目时,table 通过重建 window 进行更新,并且组合框被设置回默认值。因此,我不再看到过滤后的 2020 条目。 我想在更新 table 时保留组合框设置,所以我尝试仅更新 table 框架(update()hide()/show(),调用月份 class(createView()handle_db)而不是月份 window),但更改始终不可见,直到(手动)重新加载月份 window。据我了解,startMonth 将月 class 元素放在月 window 中,组合框必须在月 class 中才能在月 window 中看到,这将尝试更新框架而不是组合框时,这是一个两难选择,这是没有意义的。所以可能我对 PyQt windows 和 QWidgets 有什么误解。 是否可以在运行时只更新一个小部件而不重新加载整个 window?是否可以在月 window 中显示未放置在月 class 中的小部件?我对 PyQt 和 Qt 很陌生,我用谷歌搜索并尝试了很多,但直到现在都没有成功,所以我希望你能给我一个提示,告诉我哪里出了问题或如何解决它。

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_())


我正在使用 Python 3.8、PyQt5 5.15.0、PyCharm 2020.2 和 Linux Mint 19.3。

我担心“最小可重现示例”非常大:

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_())

您可以通过专门调用 widget.update() 告诉 Qt 随时刷新任何小部件。例如,self.MonthTab.update().

但是,如果您以正确的方式使用模型和过滤器模型,显示它们的小部件将在模型更改时自动刷新。

如果要向 table 中插入数据,则必须使用与模型关联的 QSqlRecord:

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)

注:删除self.MonatTab.InsertButton.clicked.connect(self.startMonat)