超简单、低耦合!pyqt、pyside界面关闭后自动保存已设置参数

超简单、低耦合! pyqt、pyside界面关闭后自动保存已设置参数


(温馨提示:最底部领取无套路、免费打包好项目,直接运行!)

前言

在小才编写PyQt5、Pyside6界面程序时,界面上经常需要许多的QlineEditQCheckBox…等等控件,并且在程序运行之前经常要对这些控件手动输入参数,但是一旦我们程序运行完了并将界面关闭后,再重新启动,这些参数就需要重新设置,小才觉得很是麻烦。因此小才就写了一个超简单、低耦合,对已经写好的界面不需要做修改就能实现自动保存的程序!
演示:
演示

正文

初始思路

一开始的思路是在界面关闭事件closeEvent中获取界面上所有QLineEdit、QCheckBox等控件当前的值,然后保存至一个json文件中,程序启动的时候再一个一个的对应读取和设置。但是这样做就会存在几个问题:

  1. 麻烦。复杂。繁琐。有时候需求界面上有几十个输入框,输入框还有各自的命名,想想都难受啊。
  2. 项目与项目之间不通用。小才我呀不可能每写一个项目就写一次这种方式的自动保存,重复性工作太多,效率底下。
  3. 代码臃肿。

实现思路

小才想到实现依赖python中getattr()findChildren()函数。

1. getattr():用于返回一个对象的属性值。
def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value
    
    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.
    """
    pass
getattr(object, name[, default])
  • object – 对象。
  • name – 字符串,对象属性。
  • default – 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。
  • 返回值:返回对象属性值。

例:QlineEdit

  • 正常设置和取得值:
# 在主界面对象中有一个QLineEdit对象
self.lineEdit = QLineEdit(self)
# 正常设置值
self.lineEdit.setText("hello world")
# 正常获取值
a = self.lineEdit.text()
  • 使用getattr
# 在主界面对象中有一个QLineEdit对象
self.lineEdit = QLineEdit(self)
getattr(getattr(self, "lineEdit"), "setText")("hello world")
a = getattr(getattr(self, "lineEdit"), "text")()

这样我就可以使用lineEdit、text、setText属性字符串来动态的获取和设置某个属性。

2. findChildren():获得满足条件的所有子对象

PyQt5中的findChildren()是一个用于遍历QObject(如QWidget、QMainWindow等)及其所有子对象的方法。这个函数通过指定一个或多个QObjects作为查找路径,搜索并返回匹配特定类型或名称的所有子对象。

例:
如果你有一个窗口,并想找到所有的按钮,你可以这样做:

parent_widget = YourMainWindowInstance  # 替换成实际的窗口实例
buttons = parent_widget.findChildren(QPushButton)
for button in buttons:
    print(button.objectName())  # 打印每个按钮的名字

这里,QPushButton是你想要查找的类型。如果找到了匹配的对象,它们将作为一个列表返回;如果没有找到,则返回空列表。

代码实现

了解上面两个主要函数后

废话不多说直接上代码: 使用的虽然是pyside6,但是pyqt5的控件类名一致的,很容易平替。

 # 支持页面上以下控件的自动保存和回复 并可指定某些控件对象名排除自动保存
 QLineEdit: "QLineEdit",
 QComboBox: "QComboBox",
 QCheckBox: "QCheckBox",
 QRadioButton: "QRadioButton",
 QSpinBox: "QSpinBox",
 QDoubleSpinBox: "QDoubleSpinBox",
 QPlainTextEdit: "QPlainTextEdit",
 QTextEdit: "QTextEdit"

restore_screen.py

import json
import os.path

from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QLineEdit, QCheckBox, QRadioButton, QSpinBox, QComboBox, QDoubleSpinBox, QTextEdit, \
    QPlainTextEdit

from check_except import check_except

'''
此模块用于恢复应用程序在上一次退出时的界面状态。
'''
class RestoreScreenModule:
    def __init__(self, main_ui):
        # 绑定主界面实例
        self.main_ui = main_ui

        # 配置文件存储路径初始化
        self.config_file_dir = "./config"
        self.config_file_name = "restore_screen.json"
        os.makedirs(self.config_file_dir, exist_ok=True)  # 创建目录(如果不存在)
        self.config_file_path = os.path.join(self.config_file_dir, self.config_file_name)

        # 定义需要保存信息的控件类型及对应获取控件值的方法
        self.save_widget_info = {
            QLineEdit: "text",
            QComboBox: "currentIndex",
            QCheckBox: "isChecked",
            QRadioButton: "isChecked",
            QSpinBox: "value",
            QDoubleSpinBox: "value",
            QTextEdit: "toPlainText",
            QPlainTextEdit: "toPlainText"
        }

        # 设置控件信息的方法字典  第一个元素:对设置的值需转换的类型,第二个元素:设置值的方法
        self.set_widget_info = {
            QLineEdit: (None, "setText"),
            QComboBox: (int, "setCurrentIndex"),
            QCheckBox: (None, "setChecked"),
            QRadioButton: (None, "setChecked"),
            QSpinBox: (int, "setValue"),
            QDoubleSpinBox: (float, "setValue"),
            QTextEdit: (None, "setPlainText"),
            QPlainTextEdit: (None, "setPlainText")
        }

        # 控件类名到字符串的映射
        self.widget_to_str_info = {
            QLineEdit: "QLineEdit",
            QComboBox: "QComboBox",
            QCheckBox: "QCheckBox",
            QRadioButton: "QRadioButton",
            QSpinBox: "QSpinBox",
            QDoubleSpinBox: "QDoubleSpinBox",
            QPlainTextEdit: "QPlainTextEdit",
            QTextEdit: "QTextEdit"
        }

        # 字符串到控件类的映射
        self.str_to_widget_info = {
            "QLineEdit": QLineEdit,
            "QComboBox": QComboBox,
            "QCheckBox": QCheckBox,
            "QRadioButton": QRadioButton,
            "QSpinBox": QSpinBox,
            "QDoubleSpinBox": QDoubleSpinBox,
            "QPlainTextEdit": QPlainTextEdit,
            "QTextEdit": QTextEdit
        }

        # 排除不需要保存状态的控件列表
        self.exclude_widget_list = [
            "lineEdit",
        ]

        # 初始化配置字典并尝试从文件中恢复上次的状态
        self.restore_last_config()

        # 设置定时器,每隔5秒保存当前界面状态
        self.timer = QTimer()
        self.timer.timeout.connect(self.save_current_screen)
        self.timer.start(5000)

    # 重置配置字典
    def reset_config_dict(self):
        self.config_dict = {}
        for key in self.str_to_widget_info.keys():
            self.config_dict[key] = {}

    # 使用装饰器处理异常
    @check_except()
    def restore_last_config(self):
        # 尝试从配置文件中读取并恢复状态
        if os.path.exists(self.config_file_path):
            with open(self.config_file_path, "r", encoding="utf-8") as f:
                self.config_dict = json.load(f)

            for key1, value1 in self.config_dict.items():
                process = self.set_widget_info.get(self.str_to_widget_info.get(key1))

                for widget_name, widget_value in value1.items():
                    if process[0] is not None:
                        widget_value = process[0](widget_value)

                    if hasattr(self.main_ui, widget_name):
                        widget = getattr(self.main_ui, widget_name)
                        getattr(widget, process[1])(widget_value)

    # 使用装饰器处理异常
    @check_except()
    def save_current_screen(self):
        # 重置配置字典
        self.reset_config_dict()

        # 遍历所有需要保存状态的控件,并记录它们的状态
        for key, value in self.save_widget_info.items():
            widget_str = self.widget_to_str_info.get(key)
            if widget_str is None:
                continue

            config_key = self.config_dict.get(widget_str)
            if config_key is None:
                continue

            for widget in self.main_ui.findChildren(key):
                widget_name = widget.objectName()
                if widget_name in self.exclude_widget_list:
                    continue

                self.config_dict[widget_str][widget_name] = getattr(widget, value)()

        # 将当前状态保存到文件中
        with open(self.config_file_path, "w", encoding="utf-8") as f:
            json.dump(self.config_dict, f, ensure_ascii=False, indent=4)

方法上的装饰器check_except是为了方便异常捕捉,小才不想因为显示报错而导致页面卡住然后退出。实现请看小才得另一篇文章python
程序运行异常与计算耗时@check_excpt装饰器
,不想看的话注释掉@check_except()即可。

使用案例

使用Qt designer简单制作一个界面包含一些常用的控件:
简单界面

界面testUI.py

# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'test.ui'
##
## Created by: Qt User Interface Compiler version 6.7.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
    QFont, QFontDatabase, QGradient, QIcon,
    QImage, QKeySequence, QLinearGradient, QPainter,
    QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QDoubleSpinBox,
    QHBoxLayout, QLineEdit, QMainWindow, QPlainTextEdit,
    QRadioButton, QSizePolicy, QSpinBox, QTextEdit,
    QWidget)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(816, 322)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.horizontalLayout = QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.spinBox = QSpinBox(self.centralwidget)
        self.spinBox.setObjectName(u"spinBox")
        self.spinBox.setMinimumSize(QSize(100, 0))
        self.spinBox.setMaximumSize(QSize(100, 16777215))

        self.horizontalLayout.addWidget(self.spinBox)

        self.doubleSpinBox = QDoubleSpinBox(self.centralwidget)
        self.doubleSpinBox.setObjectName(u"doubleSpinBox")
        self.doubleSpinBox.setMinimumSize(QSize(100, 0))
        self.doubleSpinBox.setMaximumSize(QSize(100, 16777215))

        self.horizontalLayout.addWidget(self.doubleSpinBox)

        self.comboBox = QComboBox(self.centralwidget)
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.setObjectName(u"comboBox")
        self.comboBox.setMinimumSize(QSize(100, 0))
        self.comboBox.setMaximumSize(QSize(100, 16777215))

        self.horizontalLayout.addWidget(self.comboBox)

        self.plainTextEdit = QPlainTextEdit(self.centralwidget)
        self.plainTextEdit.setObjectName(u"plainTextEdit")
        self.plainTextEdit.setMinimumSize(QSize(0, 100))
        self.plainTextEdit.setMaximumSize(QSize(16777215, 100))

        self.horizontalLayout.addWidget(self.plainTextEdit)

        self.textEdit = QTextEdit(self.centralwidget)
        self.textEdit.setObjectName(u"textEdit")
        self.textEdit.setMinimumSize(QSize(0, 100))
        self.textEdit.setMaximumSize(QSize(16777215, 100))

        self.horizontalLayout.addWidget(self.textEdit)

        self.checkBox = QCheckBox(self.centralwidget)
        self.checkBox.setObjectName(u"checkBox")

        self.horizontalLayout.addWidget(self.checkBox)

        self.radioButton = QRadioButton(self.centralwidget)
        self.radioButton.setObjectName(u"radioButton")

        self.horizontalLayout.addWidget(self.radioButton)

        self.lineEdit = QLineEdit(self.centralwidget)
        self.lineEdit.setObjectName(u"lineEdit")
        self.lineEdit.setMinimumSize(QSize(100, 0))
        self.lineEdit.setMaximumSize(QSize(100, 16777215))

        self.horizontalLayout.addWidget(self.lineEdit)

        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
        self.comboBox.setItemText(0, QCoreApplication.translate("MainWindow", u"1", None))
        self.comboBox.setItemText(1, QCoreApplication.translate("MainWindow", u"2", None))
        self.comboBox.setItemText(2, QCoreApplication.translate("MainWindow", u"3", None))

        self.checkBox.setText(QCoreApplication.translate("MainWindow", u"CheckBox", None))
        self.radioButton.setText(QCoreApplication.translate("MainWindow", u"RadioButton", None))
    # retranslateUi


设置主启动界面main.py

# -*- coding: utf-8 -*-
"""
@Time : 2024/9/12 21:40
@Auth : 公众号-人才兄呐
@File :main.py
@IDE :PyCharm
"""

import sys

from PySide6.QtWidgets import QMainWindow, QApplication

from restore_screen import RestoreScreenModule
from testUI import Ui_MainWindow

class MainUI(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainUI, self).__init__(parent)
        self.setupUi(self)

        # 设置自动恢复上次退出状态模块
        self.restore_screen_module = RestoreScreenModule(self)

    def closeEvent(self, event):
        # 关闭事件调用restore_screen_module模块保存当前状态
        self.restore_screen_module.save_current_screen()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainUI()
    win.show()

    sys.exit(app.exec())

感谢

“点赞+评论,让我知道你在关注我,感谢每一个支持我的人!”

“不要忘记关注我,点赞收藏,我会为你带来更多优质内容!”

“你的关注是我前进的动力,你的点赞是我创作的源泉。”

“点个赞吧!让我知道你在支持我,我会继续努力的!”

“关注我,点赞收藏,让我有更多的动力去创作更好的内容!”

“你的每一个点赞,都是我创作的动力,感谢你的关注和支持!”

“希望你喜欢我的内容,记得关注我哦!我会继续为大家带来更好的作品!”
感谢

资源获取

感谢您的支持和鼓励! 😊🙏
如果大家对相关文章感兴趣,可以搜索并关注公众号"人才兄呐",查看和领取打包资源,完全免费哒!无套路直接领取。如果资源不存在可以直接联系小才我给你私发!更多有用资源持续手动更新中~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是个人才呐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值