如何在PyQt5中保存动态应用程序数据(在应用程序本身内添加的小部件)?

问题描述 投票:0回答:1

**在应用程序中,我将新的 LineEdit 和 Pushbutton 添加到创建的 Gridlayout 中,并且需要在关闭时保存数据并为下一个会话加载数据。告诉我如何实现这个
**
这是一个示例界面

enter image description here

import sys

from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QLabel, QGridLayout, QLineEdit, QPushButton, \
    QMessageBox

from Designer.designerTest import Ui_MainWindow  # Импорт класса Ui_MainWindow из другого модуля


#################################################################################################

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()  # Создание экземпляра класса Ui_MainWindow
        self.ui.setupUi(self)  # Инициализация UI элементов

        self.layout = QVBoxLayout()
        self.ui.widget_2.setLayout(self.layout)  # Установка QVBoxLayout как layout для виджета widget_2

        self.ui.btnSaveTopic.clicked.connect(
        self.create_new_label)  # Подключение функции create_new_label при нажатии на кнопку btnSaveTopic

###########################

    def create_new_label(self):  # Создание новой метки и вызов функции new_grid
        if self.ui.lineTopic.text() != "":
            text = self.ui.lineTopic.text()
            label = QLabel(text)
            label.setStyleSheet('color: white; font: 14pt;')
            self.layout.addWidget(label)

            self.new_grid()  # Создание нового grid

            self.ui.lineTopic.clear()
        else:
            QMessageBox.warning(None, "Ошибка", "Введите тему")  # Показ предупреждающего сообщения

###########################

    def new_grid(self):  # Создание нового grid layout с элементами
        grid = QGridLayout()
        self.layout.addLayout(grid)

        line_edit = self.create_new_line_edit()
        grid.addWidget(line_edit, 0, 0)

        plus_right = QPushButton()
        plus_right.setFixedSize(20, 20)
        plus_right.setIcon(QIcon("icons/iconPlus.svg"))
        plus_right.setIconSize(QSize(14, 14))

        plus_down = QPushButton()
        plus_down.setFixedSize(20, 20)
        plus_down.setIcon(QIcon("icons/iconPlus.svg"))
        plus_down.setIconSize(QSize(14, 14))

        grid.addWidget(plus_right, 0, 1)
        grid.addWidget(plus_down, 1, 0)

        plus_right.clicked.connect(lambda: self.create_new_btn_right(grid))
        plus_down.clicked.connect(lambda: self.create_new_btn_down(grid))

        self.column_stretch(grid)  # Установка растягиваемости колонок и строк

###########################

    def create_new_btn_down(self, grid):  # Создание новой строки при нажатии на кнопку вниз
        clicked_widget = self.sender()

        # Получение позиции нажатого виджета в сетке
        for i in range(grid.count()):
            item = grid.itemAt(i)
            if item.widget() == clicked_widget:
                row, column = grid.getItemPosition(i)[:2]
                break
        else:
            return

        line_edit = self.create_new_line_edit()
        plus_right = self.create_new_button(lambda: self.create_new_btn_right(grid))
        plus_down = self.create_new_button(lambda: self.create_new_btn_down(grid))

        grid.addWidget(line_edit, row, column)
        grid.addWidget(plus_down, row + 1, column)
        grid.addWidget(plus_right, row, column + 1)

###########################

    def create_new_btn_right(self, grid):  # Создание новой колонки при нажатии на кнопку вправо
        clicked_widget = self.sender()

        # Получение позиции нажатого виджета в сетке
        for i in range(grid.count()):
            item = grid.itemAt(i)
            if item.widget() == clicked_widget:
                row, column = grid.getItemPosition(i)[:2]
                break
        else:
            return

        line_edit = self.create_new_line_edit()
        plus_right = self.create_new_button(lambda: self.create_new_btn_right(grid))

        grid.addWidget(line_edit, row, column)
        grid.addWidget(plus_right, row, column + 1)

        self.column_stretch(grid)

###########################

    @staticmethod
    def column_stretch(grid):  # Установка растягиваемой колонок и строк сетки
        grid.setColumnStretch(grid.columnCount(), 1)
        grid.setRowStretch(grid.rowCount(), 1)

    @staticmethod
   def create_new_line_edit():  # Создание нового QLineEdit
        line_edit = QLineEdit()
        line_edit.setFixedSize(30, 30)
        line_edit.setStyleSheet("background-color: white; color: black; font: 10pt;")
        line_edit.setAlignment(Qt.AlignCenter)
        return line_edit

    @staticmethod
    def create_new_button(callback):  # Создание новой кнопки
        button = QPushButton()
        button.setFixedSize(20, 20)
        button.setIcon(QIcon("icons/iconPlus.svg"))
        button.setIconSize(QSize(14, 14))
        button.clicked.connect(callback)
        return button

#################################################################################################

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

我尝试了使用 JSON、PICKLE、SQLITE3 的不同保存方法,但它对我不起作用

python pyqt5
1个回答
0
投票

这是我通常做的事情:我保存每个小部件数据并以某种方式声明,以便稍后在下次打开应用程序时检索它。

对于下面的这个示例(这不是你的,我只是创建了一个较小的新示例,只是为了展示我是如何做到的),我将每个小部件状态(例如文本和用户角色数据)保存到 python 字典中对象,并使用json将其保存到文件中。

稍后,当应用程序再次加载时,将检索该文件数据,并将其从 Json 字符串转换回 python 字典对象。字典的每个键对应于代码内定义的小部件状态,并用于将小部件状态重置回文件中提示的状态。

现在,请注意,这个文件确实只是一个提示,其内容可能是错误的(因为它的 Json,任何用户都可以像 CSS 一样编辑它)。如果文件不存在,或者文件中存储的数据错误,因为我们的加载函数根本不应该失败,所以每个键总是准备好默认值。

from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QComboBox, QMenuBar, QButtonGroup, QRadioButton
import json

CONTEXT_FILENAME = 'settings.json'

class Scene(QWidget):
    def __init__(self):
        super().__init__()

        self.ledit = QLineEdit()
        self.rdbA = QRadioButton('Option A')
        self.rdbB = QRadioButton('Option B')
        self.rdbC = QRadioButton('Option C')
        self.combo = QComboBox()
        self.group = QButtonGroup()

        self.group.addButton(self.rdbA, 100)
        self.group.addButton(self.rdbB, 200)
        self.group.addButton(self.rdbC, 300)

        self.combo.addItem('Item 1', 111)
        self.combo.addItem('Item 2', 222)
        self.combo.addItem('Item 3', 333)

        layout = QVBoxLayout()
        layout.addWidget(self.ledit)
        layout.addWidget(self.rdbA)
        layout.addWidget(self.rdbB)
        layout.addWidget(self.rdbC)
        layout.addWidget(self.combo)
        layout.addStretch()
        self.setLayout(layout)

    # Saves current widget states and values into a context object, and convert it
    # into a JSON String, which will be saved into a corresponding file that can be
    # loaded later on.
    #
    # In Short:
    #            WIDGETS -> CONTEXT OBJ -> JSON STRING -> FILE
    #
    def save(self):
        context = {}

        context['ledit-text'] = self.ledit.text()
        context['radio-selected'] = self.group.checkedId()
        context['combo-current'] = self.combo.currentData()

        try:
            fp = open(CONTEXT_FILENAME, 'wb')
        except Exception as error:
            print('Failed to create the save: ', error)
            return False
        else:
            data = b''
            try:
                data = json.dumps(context).encode('utf-8')
            except Exception as error:
                print('Failed to convert to JSON String: ', error)
                fp.close()
                return False
            else:
                fp.write(data)
                fp.close()
                return True

    # Loads context saved as a JSON String in an known file, and reset each widget
    # based on the loaded context. If no such file exists, use default values.
    #
    # In Short:
    #            FILE -> JSON STRING -> CONTEXT OBJ -> WIDGETS
    #
    def load(self):
        context = {}

        try:
            fp = open(CONTEXT_FILENAME, 'rb')
        except FileNotFoundError:
            context = {}
        except Exception as error:
            print('another exception just occurred: ', error)
            context = {}
        else:
            data = fp.read()
            fp.close()

            try:
                context = json.loads(data.decode('utf-8'))
            except Exception as error:
                print('failed to load JSON String: ', error)
                context = {}

        self.ledit.setText(context.get('ledit-text', ''))

        radio = context.get('radio-selected', -1)
        button = self.group.button(radio)
        if (button is not None):
            button.setChecked(True)
        else:
            self.rdbA.setChecked(True)

        index = self.combo.findData(context.get('combo-current', -1))
        if (index == -1):
            self.combo.setCurrentIndex(0)
        else:
            self.combo.setCurrentIndex(index)

    # Catch the close event, and automatically save before the app closes.
    def closeEvent(self, ev):
        self.save()
        ev.accept()

###

if __name__ == '__main__':
    app = QApplication()
    win = Scene()
    win.load()
    win.show()
    app.exec_()
© www.soinside.com 2019 - 2024. All rights reserved.