如何保存和恢复小部件的每个实例唯一的小部件属性? [英] How to save and restore widget properties that is unique for each instance of the widget?

查看:60
本文介绍了如何保存和恢复小部件的每个实例唯一的小部件属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够在我的 PyQt5 应用程序中保存和恢复小部件状态(属性和值).小部件状态应保存到 .ini 文件中.这已经在以下 Stackoverflow 答案中得到了证明:

  • ma​​in_app.py

    导入系统从 PyQt5 导入 QtWidgets、uic、QtCore、QtGui从 PyQt5.QtCore 导入 QFileInfo、QSettings、QObject从 PyQt5.QtWidgets 导入 qApp从 ui_mainwindow 导入 Ui_MainWindowdef settings_value_is_valid(val):# https://stackoverflow.com/a/60028282/4988010if isinstance(val, QtGui.QPixmap):返回不是 val.isNull()返回真def settings_restore(settings):# https://stackoverflow.com/a/60028282/4988010finfo = QtCore.QFileInfo(settings.fileName())如果 finfo.exists() 和 finfo.isFile():对于 QtWidgets.qApp.allWidgets() 中的 w:如果 w.objectName() 而不是 w.objectName().startswith(qt_"):# 如果 w.objectName():mo = w.metaObject()对于范围内的 i(mo.propertyCount()):prop = mo.property(i)名称 = prop.name()last_value = w.property(name)key = "{}/{}".format(w.objectName(), name)# 打印(道具,名称,last_value,键)如果不是 settings.contains(key):继续val = settings.value(key, type=type(last_value),)如果 (val != last_value和 settings_value_is_valid(val)和 prop.isValid()和 prop.isWritable()):w.setProperty(name, val)def settings_save(设置):# https://stackoverflow.com/a/60028282/4988010对于 QtWidgets.qApp.allWidgets() 中的 w:如果 w.objectName() 而不是 w.objectName().startswith(qt_"):# 如果 w.objectName():mo = w.metaObject()对于范围内的 i(mo.propertyCount()):prop = mo.property(i)名称 = prop.name()key = "{}/{}".format(w.objectName(), name)val = w.property(name)如果 settings_value_is_valid(val) 和 prop.isValid() 和 prop.isWritable():settings.setValue(key, w.property(name))类 MyApp(QtWidgets.QMainWindow):def __init__(self, app_name='DefaultAppName'):super(MyApp, self).__init__()self.settings = QSettings("./temp/gui_settings-{}.ini".format(app_name), QSettings.IniFormat)self.ui = Ui_MainWindow()self.ui.setupUi(self)# 设置窗口标题和qlineedit来表示应用实例的名称self.ui.app_instance_name.setText(app_name)self.setWindowTitle(app_name)# 加载从以前的应用程序使用中保存的已保存配置文件self.config_widgets_load_settings()self.ui.action_save_current_config.triggered.connect(self.config_widgets_save_settings)self.ui.action_load_config.triggered.connect(self.config_widgets_load_settings)self.ui.action_clear_config_file.triggered.connect(self.config_clear_settings)def config_widgets_save_settings(self):# 将当前状态写入设置配置文件settings_save(self.settings)def config_widgets_load_settings(self):# 加载设置配置文件settings_restore(self.settings)def config_clear_settings(self):# 清除设置配置文件self.settings.clear()如果 __name__ == '__main__':app = QtWidgets.QApplication(sys.argv)# MyApp 的第一个实例app_one = MyApp(app_name='App#1')app_one.show()# MyApp 的第二个实例app_two = MyApp(app_name='App#2')app_two.show()app.exec_()

    ui_mainwindow.py

    from PyQt5 import QtCore, QtGui, QtWidgets类 Ui_MainWindow(对象):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(330, 202)self.centralwidget = QtWidgets.QWidget(MainWindow)self.centralwidget.setObjectName("centralwidget")self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 80, 291, 51))self.gridLayoutWidget.setObjectName("gridLayoutWidget")self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)self.gridLayout.setContentsMargins(0, 0, 0, 0)self.gridLayout.setObjectName("gridLayout")self.lineEdit1 = QtWidgets.QLineEdit(self.gridLayoutWidget)self.lineEdit1.setObjectName("lineEdit1")self.gridLayout.addWidget(self.lineEdit1, 2, 0, 1, 1)self.pushButton1 = QtWidgets.QPushButton(self.gridLayoutWidget)self.pushButton1.setCheckable(True)self.pushButton1.setObjectName("pushButton1")self.gridLayout.addWidget(self.pushButton1, 2, 1, 1, 1)self.spinBox1 = QtWidgets.QSpinBox(self.gridLayoutWidget)self.spinBox1.setMaximum(10000)self.spinBox1.setProperty("value", 37)self.spinBox1.setObjectName("spinBox1")self.gridLayout.addWidget(self.spinBox1, 2, 2, 1, 1)self.label = QtWidgets.QLabel(self.gridLayoutWidget)self.label.setObjectName("标签")self.gridLayout.addWidget(self.label, 1, 0, 1, 3)self.horizo​​ntalLayoutWidget = QtWidgets.QWidget(self.centralwidget)self.horizo​​ntalLayoutWidget.setGeometry(QtCore.QRect(50, 20, 241, 31))self.horizo​​ntalLayoutWidget.setObjectName("horizo​​ntalLayoutWidget")self.horizo​​ntalLayout = QtWidgets.QHBoxLayout(self.horizo​​ntalLayoutWidget)self.horizo​​ntalLayout.setContentsMargins(0, 0, 0, 0)self.horizo​​ntalLayout.setObjectName("horizo​​ntalLayout")self.label_2 = QtWidgets.QLabel(self.horizo​​ntalLayoutWidget)self.label_2.setObjectName("label_2")self.horizo​​ntalLayout.addWidget(self.label_2)self.app_instance_name = QtWidgets.QLineEdit(self.horizo​​ntalLayoutWidget)self.app_instance_name.setObjectName("app_instance_name")self.horizo​​ntalLayout.addWidget(self.app_instance_name)MainWindow.setCentralWidget(self.centralwidget)self.menubar = QtWidgets.QMenuBar(MainWindow)self.menubar.setGeometry(QtCore.QRect(0, 0, 330, 21))self.menubar.setObjectName("menubar")self.menuFile = QtWidgets.QMenu(self.menubar)self.menuFile.setObjectName("menuFile")MainWindow.setMenuBar(self.menubar)self.statusbar = QtWidgets.QStatusBar(MainWindow)self.statusbar.setObjectName("statusbar")MainWindow.setStatusBar(self.statusbar)self.action_save_current_config = QtWidgets.QAction(MainWindow)self.action_save_current_config.setObjectName("action_save_current_config")self.action_load_config = QtWidgets.QAction(MainWindow)self.action_load_config.setObjectName("action_load_config")self.action_clear_config_file = QtWidgets.QAction(MainWindow)self.action_clear_config_file.setObjectName("action_clear_config_file")self.menuFile.addAction(self.action_save_current_config)self.menuFile.addAction(self.action_load_config)self.menuFile.addAction(self.action_clear_config_file)self.menubar.addAction(self.menuFile.menuAction())self.retranslateUi(MainWindow)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate(MainWindow", MainWindow"))self.lineEdit1.setText(_translate(MainWindow", 这是默认文本"))self.pushButton1.setText(_translate("MainWindow", "Push me"))self.label.setText(_translate(MainWindow", 带有各种小部件的简单应用程序"))self.label_2.setText(_translate(MainWindow", App 实例名称"))self.menuFile.setTitle(_translate("MainWindow", "File"))self.action_save_current_config.setText(_translate("MainWindow", "Save current config"))self.action_load_config.setText(_translate("MainWindow", "Load config"))self.action_clear_config_file.setText(_translate(MainWindow", 清除配置文件"))如果 __name__ == __main__":导入系统app = QtWidgets.QApplication(sys.argv)MainWindow = QtWidgets.QMainWindow()ui = Ui_MainWindow()ui.setupUi(主窗口)MainWindow.show()sys.exit(app.exec_())

    解决方案

    我在深入研究 Qt 文档后找到了解决方案.每个 QWidget 都是 QObject,它有一个方法叫做 findChildren().此方法可用于指定要为其保存/恢复设置的父小部件.

    以下创建的方法将有助于保存/恢复设置.我的贡献是创建函数 settings_get_all_widgets().保存/恢复功能最初由 @eyllanesc 创建,我对其进行了修改以解决此问题.

    def settings_get_all_widgets(parent):# 可能的解决方法:# https://stackoverflow.com/questions/64202927/how-to-save-and-restore-widget-properties-that-is-unique-for-each-instance-of-th如果家长:# 在给定的父级中查找 QWidget 类型的所有子级all_widgets = parent.findChildren(QtWidgets.QWidget)如果 parent.isWidgetType():# 如果 parent 是 QWidget 类型,则将 parent 本身添加到列表中all_widgets.append(parent)别的:# 如果没有给出父级,则从所有 PyQt 应用程序中获取所有小部件all_widgets = QtWidgets.qApp.allWidgets()返回 all_widgetsdef settings_value_is_valid(val):# 原文改编自:# https://stackoverflow.com/a/60028282/4988010# https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281if isinstance(val, QtGui.QPixmap):返回不是 val.isNull()返回真def settings_restore(settings, parent=None):# 原文改编自:# https://stackoverflow.com/a/60028282/4988010# https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281如果不是设置:返回all_widgets = settings_get_all_widgets(parent)finfo = QtCore.QFileInfo(settings.fileName())如果 finfo.exists() 和 finfo.isFile():对于 all_widgets 中的 w:如果 w.objectName() 而不是 w.objectName().startswith(qt_"):# 如果 w.objectName():mo = w.metaObject()对于范围内的 i(mo.propertyCount()):prop = mo.property(i)名称 = prop.name()last_value = w.property(name)key = "{}/{}".format(w.objectName(), name)如果不是 settings.contains(key):继续val = settings.value(key, type=type(last_value),)如果 (val != last_value和 settings_value_is_valid(val)和 prop.isValid()和 prop.isWritable()):w.setProperty(name, val)def settings_save(settings, parent=None):# 原文改编自:# https://stackoverflow.com/a/60028282/4988010# https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281如果没有设置:返回all_widgets = settings_get_all_widgets(parent)对于 all_widgets 中的 w:如果 w.objectName() 而不是 w.objectName().startswith(qt_"):mo = w.metaObject()对于范围内的 i(mo.propertyCount()):prop = mo.property(i)名称 = prop.name()key = "{}/{}".format(w.objectName(), name)val = w.property(name)如果 settings_value_is_valid(val) 和 prop.isValid() 和 prop.isWritable():settings.setValue(key, w.property(name))

    以下是自包含的工作代码,其中包含原始问题的解决方案.

    导入系统从 PyQt5 导入 QtWidgets、uic、QtCore、QtGui从 PyQt5.QtCore 导入 QFileInfo、QSettings、QObject从 PyQt5.QtWidgets 导入 qApp# 从 ui_mainwindow 导入 Ui_MainWindowdef settings_get_all_widgets(parent):# 可能的解决方法:# https://stackoverflow.com/questions/64202927/how-to-save-and-restore-widget-properties-that-is-unique-for-each-instance-of-th如果家长:# 在给定的父级中查找 QWidget 类型的所有子级all_widgets = parent.findChildren(QtWidgets.QWidget)如果 parent.isWidgetType():# 如果 parent 是 QWidget 类型,则将 parent 本身添加到列表中all_widgets.append(parent)别的:# 如果没有给出父级,则从所有 PyQt 应用程序中获取所有小部件all_widgets = QtWidgets.qApp.allWidgets()返回 all_widgetsdef settings_value_is_valid(val):# 原文改编自:# https://stackoverflow.com/a/60028282/4988010# https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281if isinstance(val, QtGui.QPixmap):返回不是 val.isNull()返回真def settings_restore(settings, parent=None):# 原文改编自:# https://stackoverflow.com/a/60028282/4988010# https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281如果不是设置:返回all_widgets = settings_get_all_widgets(parent)finfo = QtCore.QFileInfo(settings.fileName())如果 finfo.exists() 和 finfo.isFile():对于 all_widgets 中的 w:如果 w.objectName() 而不是 w.objectName().startswith(qt_"):# 如果 w.objectName():mo = w.metaObject()对于范围内的 i(mo.propertyCount()):prop = mo.property(i)名称 = prop.name()last_value = w.property(name)key = "{}/{}".format(w.objectName(), name)如果不是 settings.contains(key):继续val = settings.value(key, type=type(last_value),)如果 (val != last_value和 settings_value_is_valid(val)和 prop.isValid()和 prop.isWritable()):w.setProperty(name, val)def settings_save(settings, parent=None):# 原文改编自:# https://stackoverflow.com/a/60028282/4988010# https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281如果不是设置:返回all_widgets = settings_get_all_widgets(parent)对于 all_widgets 中的 w:如果 w.objectName() 而不是 w.objectName().startswith(qt_"):mo = w.metaObject()对于范围内的 i(mo.propertyCount()):prop = mo.property(i)名称 = prop.name()key = "{}/{}".format(w.objectName(), name)val = w.property(name)如果 settings_value_is_valid(val) 和 prop.isValid() 和 prop.isWritable():settings.setValue(key, w.property(name))类 MyApp(QtWidgets.QMainWindow):def __init__(self, app_name='DefaultAppName'):super(MyApp, self).__init__()self.settings = QSettings("./temp/gui_settings-{}.ini".format(app_name), QSettings.IniFormat)self.ui = Ui_MainWindow()self.ui.setupUi(self)# 设置窗口标题和qlineedit来表示应用实例的名称self.ui.app_instance_name.setText(app_name)self.setWindowTitle(app_name)# 加载从以前的应用程序使用中保存的已保存配置文件self.config_widgets_load_settings()self.ui.action_save_current_config.triggered.connect(self.config_widgets_save_settings)self.ui.action_load_config.triggered.connect(self.config_widgets_load_settings)self.ui.action_clear_config_file.triggered.connect(self.config_clear_settings)def config_widgets_save_settings(self):# 将当前状态写入设置配置文件settings_save(self.settings, self)def config_widgets_load_settings(self):# 加载设置配置文件settings_restore(self.settings, self)def config_clear_settings(self):# 清除设置配置文件self.settings.clear()类 Ui_MainWindow(对象):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(330, 202)self.centralwidget = QtWidgets.QWidget(MainWindow)self.centralwidget.setObjectName("centralwidget")self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 80, 291, 51))self.gridLayoutWidget.setObjectName("gridLayoutWidget")self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)self.gridLayout.setContentsMargins(0, 0, 0, 0)self.gridLayout.setObjectName("gridLayout")self.lineEdit1 = QtWidgets.QLineEdit(self.gridLayoutWidget)self.lineEdit1.setObjectName("lineEdit1")self.gridLayout.addWidget(self.lineEdit1, 2, 0, 1, 1)self.pushButton1 = QtWidgets.QPushButton(self.gridLayoutWidget)self.pushButton1.setCheckable(True)self.pushButton1.setObjectName("pushButton1")self.gridLayout.addWidget(self.pushButton1, 2, 1, 1, 1)self.spinBox1 = QtWidgets.QSpinBox(self.gridLayoutWidget)self.spinBox1.setMaximum(10000)self.spinBox1.setProperty("value", 37)self.spinBox1.setObjectName("spinBox1")self.gridLayout.addWidget(self.spinBox1, 2, 2, 1, 1)self.label = QtWidgets.QLabel(self.gridLayoutWidget)self.label.setObjectName("标签")self.gridLayout.addWidget(self.label, 1, 0, 1, 3)self.horizo​​ntalLayoutWidget = QtWidgets.QWidget(self.centralwidget)self.horizo​​ntalLayoutWidget.setGeometry(QtCore.QRect(50, 20, 241, 31))self.horizo​​ntalLayoutWidget.setObjectName("horizo​​ntalLayoutWidget")self.horizo​​ntalLayout = QtWidgets.QHBoxLayout(self.horizo​​ntalLayoutWidget)self.horizo​​ntalLayout.setContentsMargins(0, 0, 0, 0)self.horizo​​ntalLayout.setObjectName("horizo​​ntalLayout")self.label_2 = QtWidgets.QLabel(self.horizo​​ntalLayoutWidget)self.label_2.setObjectName("label_2")self.horizo​​ntalLayout.addWidget(self.label_2)self.app_instance_name = QtWidgets.QLineEdit(self.horizo​​ntalLayoutWidget)self.app_instance_name.setObjectName("app_instance_name")self.horizo​​ntalLayout.addWidget(self.app_instance_name)MainWindow.setCentralWidget(self.centralwidget)self.menubar = QtWidgets.QMenuBar(MainWindow)self.menubar.setGeometry(QtCore.QRect(0, 0, 330, 21))self.menubar.setObjectName("menubar")self.menuFile = QtWidgets.QMenu(self.menubar)self.menuFile.setObjectName("menuFile")MainWindow.setMenuBar(self.menubar)self.statusbar = QtWidgets.QStatusBar(MainWindow)self.statusbar.setObjectName("statusbar")MainWindow.setStatusBar(self.statusbar)self.action_save_current_config = QtWidgets.QAction(MainWindow)self.action_save_current_config.setObjectName("action_save_current_config")self.action_load_config = QtWidgets.QAction(MainWindow)self.action_load_config.setObjectName("action_load_config")self.action_clear_config_file = QtWidgets.QAction(MainWindow)self.action_clear_config_file.setObjectName("action_clear_config_file")self.menuFile.addAction(self.action_save_current_config)self.menuFile.addAction(self.action_load_config)self.menuFile.addAction(self.action_clear_config_file)self.menubar.addAction(self.menuFile.menuAction())self.retranslateUi(MainWindow)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate(MainWindow", MainWindow"))self.lineEdit1.setText(_translate(MainWindow", 这是默认文本"))self.pushButton1.setText(_translate("MainWindow", "Push me"))self.label.setText(_translate(MainWindow", 带有各种小部件的简单应用程序"))self.label_2.setText(_translate(MainWindow", App 实例名称"))self.menuFile.setTitle(_translate("MainWindow", "File"))self.action_save_current_config.setText(_translate("MainWindow", "Save current config"))self.action_load_config.setText(_translate("MainWindow", "Load config"))self.action_clear_config_file.setText(_translate(MainWindow", 清除配置文件"))如果 __name__ == '__main__':app = QtWidgets.QApplication(sys.argv)# MyApp 的第一个实例app_one = MyApp(app_name='App#1')app_one.show()# MyApp 的第二个实例app_two = MyApp(app_name='App#2')app_two.show()app.exec_()

    I would like the ability to save and restore the widget state (properties and values) in my PyQt5 app. The widget state should be saved to an .ini file. This has already been demonstrated in the following Stackoverflow answers:

    However, the answers to above questions do not address a specific issue I am facing in my example code below. I want the saving and restoring from the settings .ini file to be unique for every instance of the parent widget (main window app) that is running. Therefore, when saving the widget properties, the iteration over all the widgets should only occur on the children of the parent widget (main window) and not through all the widgets that are currently running in the global application.

    I think the issue in my example code below is related to the line for w in QtWidgets.qApp.allWidgets():. I think this line iterates over ALL the widgets that are currently open in PyQt5 global application. However, when there are multiple instances of the same parent widget, there will be duplication in the objectName. Although a unique name can be given to each instance (ex: app_name) during init() and be accounted for in the QSettings key, this may not be the best universally adaptable solution. Therefore how can I fix the issue I am facing? How can I get settings_save() function to iterate through all the children widget of the instance of the parent widget (main window app) and NOT over ALL the parent widgets currently running in the global application? In Qt docs, I can't find a function similar to allWidgets() that will allow me to specify the parent widget (ex: QMainWindow) and give me all the widgets and objects under it. If getting all the widgets inside a parent widget was possible, then I can easily modify the function settings_save() include not only the QSetting variable as the argument, but also the instance of the widget for which I want to save the settings for.

    main_app.py

    import sys
    from PyQt5 import QtWidgets, uic, QtCore, QtGui
    from PyQt5.QtCore import QFileInfo, QSettings, QObject
    from PyQt5.QtWidgets import qApp
    
    from ui_mainwindow import Ui_MainWindow
    
    def settings_value_is_valid(val):
        # https://stackoverflow.com/a/60028282/4988010
        if isinstance(val, QtGui.QPixmap):
            return not val.isNull()
        return True
    
    def settings_restore(settings):
        # https://stackoverflow.com/a/60028282/4988010
        finfo = QtCore.QFileInfo(settings.fileName())
    
        if finfo.exists() and finfo.isFile():
            for w in QtWidgets.qApp.allWidgets():
                if w.objectName() and not w.objectName().startswith("qt_"):
                # if w.objectName():
                    mo = w.metaObject()
                    for i in range(mo.propertyCount()):
                        prop = mo.property(i)
                        name = prop.name()
                        last_value = w.property(name)
                        key = "{}/{}".format(w.objectName(), name)
                        # print(prop, name, last_value, key)
                        if not settings.contains(key):
                            continue
                        val = settings.value(key, type=type(last_value),)
                        if (
                            val != last_value
                            and settings_value_is_valid(val)
                            and prop.isValid()
                            and prop.isWritable()
                        ):
                            w.setProperty(name, val)
    
    def settings_save(settings):
        # https://stackoverflow.com/a/60028282/4988010
        for w in QtWidgets.qApp.allWidgets():
            if w.objectName() and not w.objectName().startswith("qt_"):
            # if w.objectName():
                mo = w.metaObject()
                for i in range(mo.propertyCount()):
                    prop = mo.property(i)
                    name = prop.name()
                    key = "{}/{}".format(w.objectName(), name)
                    val = w.property(name)
                    if settings_value_is_valid(val) and prop.isValid() and prop.isWritable():
                        settings.setValue(key, w.property(name))
    
    class MyApp(QtWidgets.QMainWindow):
        
        def __init__(self, app_name='DefaultAppName'):
            super(MyApp, self).__init__()
            
            self.settings = QSettings("./temp/gui_settings-{}.ini".format(app_name), QSettings.IniFormat)
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
            
            # Set window titlte and qlineedit to indicate the name of app instance
            self.ui.app_instance_name.setText(app_name)
            self.setWindowTitle(app_name)
    
            # Load the saved config file saved from previous app usage 
            self.config_widgets_load_settings()
    
            self.ui.action_save_current_config.triggered.connect(self.config_widgets_save_settings)
            self.ui.action_load_config.triggered.connect(self.config_widgets_load_settings)
            self.ui.action_clear_config_file.triggered.connect(self.config_clear_settings)
    
        def config_widgets_save_settings(self):
            # Write current state to the settings config file
            settings_save(self.settings)
    
        def config_widgets_load_settings(self):
            # Load settings config file 
            settings_restore(self.settings)
    
        def config_clear_settings(self):
            # Clear the settings config file 
            self.settings.clear()
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
    
        # First instance of the MyApp 
        app_one = MyApp(app_name='App#1')
        app_one.show()
        
        # Second instance of the MyApp
        app_two = MyApp(app_name='App#2')
        app_two.show()
    
        app.exec_()
    
    

    ui_mainwindow.py

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(330, 202)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)
            self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 80, 291, 51))
            self.gridLayoutWidget.setObjectName("gridLayoutWidget")
            self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
            self.gridLayout.setContentsMargins(0, 0, 0, 0)
            self.gridLayout.setObjectName("gridLayout")
            self.lineEdit1 = QtWidgets.QLineEdit(self.gridLayoutWidget)
            self.lineEdit1.setObjectName("lineEdit1")
            self.gridLayout.addWidget(self.lineEdit1, 2, 0, 1, 1)
            self.pushButton1 = QtWidgets.QPushButton(self.gridLayoutWidget)
            self.pushButton1.setCheckable(True)
            self.pushButton1.setObjectName("pushButton1")
            self.gridLayout.addWidget(self.pushButton1, 2, 1, 1, 1)
            self.spinBox1 = QtWidgets.QSpinBox(self.gridLayoutWidget)
            self.spinBox1.setMaximum(10000)
            self.spinBox1.setProperty("value", 37)
            self.spinBox1.setObjectName("spinBox1")
            self.gridLayout.addWidget(self.spinBox1, 2, 2, 1, 1)
            self.label = QtWidgets.QLabel(self.gridLayoutWidget)
            self.label.setObjectName("label")
            self.gridLayout.addWidget(self.label, 1, 0, 1, 3)
            self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
            self.horizontalLayoutWidget.setGeometry(QtCore.QRect(50, 20, 241, 31))
            self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
            self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
            self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
            self.horizontalLayout.setObjectName("horizontalLayout")
            self.label_2 = QtWidgets.QLabel(self.horizontalLayoutWidget)
            self.label_2.setObjectName("label_2")
            self.horizontalLayout.addWidget(self.label_2)
            self.app_instance_name = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
            self.app_instance_name.setObjectName("app_instance_name")
            self.horizontalLayout.addWidget(self.app_instance_name)
            MainWindow.setCentralWidget(self.centralwidget)
            self.menubar = QtWidgets.QMenuBar(MainWindow)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 330, 21))
            self.menubar.setObjectName("menubar")
            self.menuFile = QtWidgets.QMenu(self.menubar)
            self.menuFile.setObjectName("menuFile")
            MainWindow.setMenuBar(self.menubar)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
            self.action_save_current_config = QtWidgets.QAction(MainWindow)
            self.action_save_current_config.setObjectName("action_save_current_config")
            self.action_load_config = QtWidgets.QAction(MainWindow)
            self.action_load_config.setObjectName("action_load_config")
            self.action_clear_config_file = QtWidgets.QAction(MainWindow)
            self.action_clear_config_file.setObjectName("action_clear_config_file")
            self.menuFile.addAction(self.action_save_current_config)
            self.menuFile.addAction(self.action_load_config)
            self.menuFile.addAction(self.action_clear_config_file)
            self.menubar.addAction(self.menuFile.menuAction())
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
            self.lineEdit1.setText(_translate("MainWindow", "This is default text"))
            self.pushButton1.setText(_translate("MainWindow", "Push me"))
            self.label.setText(_translate("MainWindow", "My simple app with various widgets"))
            self.label_2.setText(_translate("MainWindow", "App instance name"))
            self.menuFile.setTitle(_translate("MainWindow", "File"))
            self.action_save_current_config.setText(_translate("MainWindow", "Save current config"))
            self.action_load_config.setText(_translate("MainWindow", "Load config"))
            self.action_clear_config_file.setText(_translate("MainWindow", "Clear config file"))
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        MainWindow = QtWidgets.QMainWindow()
        ui = Ui_MainWindow()
        ui.setupUi(MainWindow)
        MainWindow.show()
        sys.exit(app.exec_())
    

    解决方案

    I figured out the solution after digging through Qt documentations further. Every QWidget is of type QObject, which has a method called findChildren(). This method can be used to specify the parent widget for which you want to save/restore the settings for.

    Below are the methods created that will help assist in saving/restoring the settings. My contribution is the creation of the function settings_get_all_widgets(). The saving/restoring functions were originally created by @eyllanesc which were modified by me as a solution to this question.

    def settings_get_all_widgets(parent):
        # Possible fix to the issue: 
        # https://stackoverflow.com/questions/64202927/how-to-save-and-restore-widget-properties-that-is-unique-for-each-instance-of-th 
        
        if parent:
            # Find all children inside the given parent that is of type QWidget 
            all_widgets = parent.findChildren(QtWidgets.QWidget)
            if parent.isWidgetType():
                # If parent is of type QWidget, add the parent itself to the list 
                all_widgets.append(parent)
        else:
            # If no parent is given then get all the widgets from all the PyQt applications 
            all_widgets = QtWidgets.qApp.allWidgets()
        
        return all_widgets
    
    def settings_value_is_valid(val):
        # Originally adapted from:
        # https://stackoverflow.com/a/60028282/4988010
        # https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281
        if isinstance(val, QtGui.QPixmap):
            return not val.isNull()
        return True
    
    def settings_restore(settings, parent=None):
        # Originally adapted from:
        # https://stackoverflow.com/a/60028282/4988010
        # https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281
    
        if not settings:
            return
    
        all_widgets = settings_get_all_widgets(parent)
    
        finfo = QtCore.QFileInfo(settings.fileName())
        if finfo.exists() and finfo.isFile():
            for w in all_widgets:
                if w.objectName() and not w.objectName().startswith("qt_"):
                # if w.objectName():
                    mo = w.metaObject()
                    for i in range(mo.propertyCount()):
                        prop = mo.property(i)
                        name = prop.name()
                        last_value = w.property(name)
                        key = "{}/{}".format(w.objectName(), name)
                        if not settings.contains(key):
                            continue
                        val = settings.value(key, type=type(last_value),)
                        if (
                            val != last_value
                            and settings_value_is_valid(val)
                            and prop.isValid()
                            and prop.isWritable()
                        ):
                            w.setProperty(name, val)
    
    def settings_save(settings, parent=None):
        # Originally adapted from:
        # https://stackoverflow.com/a/60028282/4988010
        # https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281
    
        if not settings:
            return
        all_widgets = settings_get_all_widgets(parent)
        
        for w in all_widgets:
            if w.objectName() and not w.objectName().startswith("qt_"):
                mo = w.metaObject()
                for i in range(mo.propertyCount()):
                    prop = mo.property(i)
                    name = prop.name()
                    key = "{}/{}".format(w.objectName(), name)
                    val = w.property(name)
                    if settings_value_is_valid(val) and prop.isValid() and prop.isWritable():
                        settings.setValue(key, w.property(name))
    

    Below is the self contained working code which contains the solution to the original question.

    import sys
    from PyQt5 import QtWidgets, uic, QtCore, QtGui
    from PyQt5.QtCore import QFileInfo, QSettings, QObject
    from PyQt5.QtWidgets import qApp
    # from ui_mainwindow import Ui_MainWindow
    
    
    def settings_get_all_widgets(parent):
        # Possible fix to the issue: 
        # https://stackoverflow.com/questions/64202927/how-to-save-and-restore-widget-properties-that-is-unique-for-each-instance-of-th 
        
        if parent:
            # Find all children inside the given parent that is of type QWidget 
            all_widgets = parent.findChildren(QtWidgets.QWidget)
            if parent.isWidgetType():
                # If parent is of type QWidget, add the parent itself to the list 
                all_widgets.append(parent)
        else:
            # If no parent is given then get all the widgets from all the PyQt applications 
            all_widgets = QtWidgets.qApp.allWidgets()
        
        return all_widgets
    
    def settings_value_is_valid(val):
        # Originally adapted from:
        # https://stackoverflow.com/a/60028282/4988010
        # https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281
        if isinstance(val, QtGui.QPixmap):
            return not val.isNull()
        return True
    
    def settings_restore(settings, parent=None):
        # Originally adapted from:
        # https://stackoverflow.com/a/60028282/4988010
        # https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281
    
        if not settings:
            return
    
        all_widgets = settings_get_all_widgets(parent)
    
        finfo = QtCore.QFileInfo(settings.fileName())
        if finfo.exists() and finfo.isFile():
            for w in all_widgets:
                if w.objectName() and not w.objectName().startswith("qt_"):
                # if w.objectName():
                    mo = w.metaObject()
                    for i in range(mo.propertyCount()):
                        prop = mo.property(i)
                        name = prop.name()
                        last_value = w.property(name)
                        key = "{}/{}".format(w.objectName(), name)
                        if not settings.contains(key):
                            continue
                        val = settings.value(key, type=type(last_value),)
                        if (
                            val != last_value
                            and settings_value_is_valid(val)
                            and prop.isValid()
                            and prop.isWritable()
                        ):
                            w.setProperty(name, val)
    
    def settings_save(settings, parent=None):
        # Originally adapted from:
        # https://stackoverflow.com/a/60028282/4988010
        # https://github.com/eyllanesc/stackoverflow/issues/26#issuecomment-703184281
    
        if not settings:
            return
        all_widgets = settings_get_all_widgets(parent)
        
        for w in all_widgets:
            if w.objectName() and not w.objectName().startswith("qt_"):
                mo = w.metaObject()
                for i in range(mo.propertyCount()):
                    prop = mo.property(i)
                    name = prop.name()
                    key = "{}/{}".format(w.objectName(), name)
                    val = w.property(name)
                    if settings_value_is_valid(val) and prop.isValid() and prop.isWritable():
                        settings.setValue(key, w.property(name))
    
    class MyApp(QtWidgets.QMainWindow):
        
        def __init__(self, app_name='DefaultAppName'):
            super(MyApp, self).__init__()
            
            self.settings = QSettings("./temp/gui_settings-{}.ini".format(app_name), QSettings.IniFormat)
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
            
            # Set window titlte and qlineedit to indicate the name of app instance
            self.ui.app_instance_name.setText(app_name)
            self.setWindowTitle(app_name)
    
            # Load the saved config file saved from previous app usage 
            self.config_widgets_load_settings()
    
            self.ui.action_save_current_config.triggered.connect(self.config_widgets_save_settings)
            self.ui.action_load_config.triggered.connect(self.config_widgets_load_settings)
            self.ui.action_clear_config_file.triggered.connect(self.config_clear_settings)
    
        def config_widgets_save_settings(self):
            # Write current state to the settings config file
            settings_save(self.settings, self)
    
        def config_widgets_load_settings(self):
            # Load settings config file 
            settings_restore(self.settings, self)
    
        def config_clear_settings(self):
            # Clear the settings config file 
            self.settings.clear()
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(330, 202)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)
            self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 80, 291, 51))
            self.gridLayoutWidget.setObjectName("gridLayoutWidget")
            self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
            self.gridLayout.setContentsMargins(0, 0, 0, 0)
            self.gridLayout.setObjectName("gridLayout")
            self.lineEdit1 = QtWidgets.QLineEdit(self.gridLayoutWidget)
            self.lineEdit1.setObjectName("lineEdit1")
            self.gridLayout.addWidget(self.lineEdit1, 2, 0, 1, 1)
            self.pushButton1 = QtWidgets.QPushButton(self.gridLayoutWidget)
            self.pushButton1.setCheckable(True)
            self.pushButton1.setObjectName("pushButton1")
            self.gridLayout.addWidget(self.pushButton1, 2, 1, 1, 1)
            self.spinBox1 = QtWidgets.QSpinBox(self.gridLayoutWidget)
            self.spinBox1.setMaximum(10000)
            self.spinBox1.setProperty("value", 37)
            self.spinBox1.setObjectName("spinBox1")
            self.gridLayout.addWidget(self.spinBox1, 2, 2, 1, 1)
            self.label = QtWidgets.QLabel(self.gridLayoutWidget)
            self.label.setObjectName("label")
            self.gridLayout.addWidget(self.label, 1, 0, 1, 3)
            self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
            self.horizontalLayoutWidget.setGeometry(QtCore.QRect(50, 20, 241, 31))
            self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
            self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
            self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
            self.horizontalLayout.setObjectName("horizontalLayout")
            self.label_2 = QtWidgets.QLabel(self.horizontalLayoutWidget)
            self.label_2.setObjectName("label_2")
            self.horizontalLayout.addWidget(self.label_2)
            self.app_instance_name = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
            self.app_instance_name.setObjectName("app_instance_name")
            self.horizontalLayout.addWidget(self.app_instance_name)
            MainWindow.setCentralWidget(self.centralwidget)
            self.menubar = QtWidgets.QMenuBar(MainWindow)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 330, 21))
            self.menubar.setObjectName("menubar")
            self.menuFile = QtWidgets.QMenu(self.menubar)
            self.menuFile.setObjectName("menuFile")
            MainWindow.setMenuBar(self.menubar)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
            self.action_save_current_config = QtWidgets.QAction(MainWindow)
            self.action_save_current_config.setObjectName("action_save_current_config")
            self.action_load_config = QtWidgets.QAction(MainWindow)
            self.action_load_config.setObjectName("action_load_config")
            self.action_clear_config_file = QtWidgets.QAction(MainWindow)
            self.action_clear_config_file.setObjectName("action_clear_config_file")
            self.menuFile.addAction(self.action_save_current_config)
            self.menuFile.addAction(self.action_load_config)
            self.menuFile.addAction(self.action_clear_config_file)
            self.menubar.addAction(self.menuFile.menuAction())
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
            self.lineEdit1.setText(_translate("MainWindow", "This is default text"))
            self.pushButton1.setText(_translate("MainWindow", "Push me"))
            self.label.setText(_translate("MainWindow", "My simple app with various widgets"))
            self.label_2.setText(_translate("MainWindow", "App instance name"))
            self.menuFile.setTitle(_translate("MainWindow", "File"))
            self.action_save_current_config.setText(_translate("MainWindow", "Save current config"))
            self.action_load_config.setText(_translate("MainWindow", "Load config"))
            self.action_clear_config_file.setText(_translate("MainWindow", "Clear config file"))
    
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
    
        # First instance of the MyApp 
        app_one = MyApp(app_name='App#1')
        app_one.show()
        
        # Second instance of the MyApp
        app_two = MyApp(app_name='App#2')
        app_two.show()
    
        app.exec_()
    

    这篇关于如何保存和恢复小部件的每个实例唯一的小部件属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆