首页 > 解决方案 > 如何保存 qtablewidget 的当前值?

问题描述

需要保存 QTableWidget 的当前值。QTableWidget 将在 QTabWidget 内部,并且将有几个相邻的选项卡,其中包含多个表。在表格里面会有QCheckBox,QComoBox作为cellwidgetitem,这些值也需要保存。除此之外,我还需要保存网格布局内的 QSpinBox 和 QDoubleSpinBox 的值。我正在从https://gist.github.com/eyllanesc/be8a476bb7038c7579c58609d7d0f031获取代码的帮助,它将 QLineEdit 的值保存在 QFormLayout 中,其中 QFormLayout 在 QTabWidget 中。如果实例化了多个选项卡,则无法保存选项卡内的 QLineEdit 值。此外,如果在 QTabWidget 下方添加 QTableWidget,则 QTableWidget 的值也无法保存。

# -*- coding: utf-8 -*-
import sys

from PyQt5.QtCore import QFileInfo, QSettings
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import qApp, QApplication, QMainWindow, QFormLayout, QLineEdit, QTabWidget, QWidget, QAction, QVBoxLayout, QTableWidget, QTableWidgetItem


def restore(settings):
    finfo = QFileInfo(settings.fileName())
    print(settings.fileName())
    if finfo.exists() and finfo.isFile():
        for w in qApp.allWidgets():
            mo = w.metaObject()
            if w.objectName() != "":
                for i in range(mo.propertyCount()):
                    name = mo.property(i).name()
                    val = settings.value("{}/{}".format(w.objectName(), name), w.property(name))
                    w.setProperty(name, val)


def save(settings):
    for w in qApp.allWidgets():
        mo = w.metaObject()
        if w.objectName() != "":
            for i in range(mo.propertyCount()):
                name = mo.property(i).name()
                settings.setValue("{}/{}".format(w.objectName(), name), w.property(name))


class mainwindow(QMainWindow):
    settings = QSettings("gui.ng", QSettings.IniFormat)

    def __init__(self, parent=None):
        super(mainwindow, self).__init__(parent)
        self.setObjectName("MainWindow")
        self.initUI()

        restore(self.settings)

    def initUI(self):
        exitAction = QAction(QIcon('icon\\exit.png'), 'Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.triggered.connect(self.close)

        self.toolbar = self.addToolBar('Exit')
        self.toolbar.setMovable(False)
        self.toolbar.addAction(exitAction)

        self.tab_widget = QTabWidget(self)  # add tab
        self.tab_widget.setObjectName("tabWidget")

        self.tab2 = QWidget()
        self.tab2.setObjectName("tab2")

        self.tab3 = QWidget()
        self.tab3.setObjectName("tab3")

        self.tab_widget.addTab(self.tab2, "Tab_2")
        self.tab_widget.addTab(self.tab3, "Tab_3")
        self.tab2UI()
        self.tab3UI()

        self.vlay = QVBoxLayout()
        self.vlay.addWidget(self.tab_widget)

        self.qtable = QTableWidget()
        self.qtable.setRowCount(3)
        self.qtable.setColumnCount(3)
        self.qtable.setItem(0, 0, QTableWidgetItem("text1"))
        self.qtable.setItem(0, 1, QTableWidgetItem("text1"))
        self.qtable.setItem(0, 2, QTableWidgetItem("text1"))
        self.qtable.setItem(1, 0, QTableWidgetItem("text2"))
        self.qtable.setItem(1, 1, QTableWidgetItem("text2"))
        self.qtable.setItem(1, 2, QTableWidgetItem("text2"))
        self.qtable.setItem(2, 0, QTableWidgetItem("text3"))
        self.qtable.setItem(2, 1, QTableWidgetItem("text3"))
        self.qtable.setItem(2, 2, QTableWidgetItem("text3"))

        self.vlay.addWidget(self.qtable)

        self.qVlayWidget = QWidget()
        self.qVlayWidget.setLayout(self.vlay)

        self.setCentralWidget(self.qVlayWidget)

    def tab2UI(self):
        self.layout_2 = QFormLayout()
        nameLe = QLineEdit(self)
        nameLe.setObjectName("nameLe_2")
        self.layout_2.addRow("Name_2", nameLe)

        addressLe = QLineEdit()
        addressLe.setObjectName("addressLe_2")
        self.layout_2.addRow("Address_2", addressLe)

        self.tab2.setLayout(self.layout_2)

    def tab3UI(self):
        self.layout_3 = QFormLayout()
        nameLe = QLineEdit(self)
        nameLe.setObjectName("nameLe_3")
        self.layout_3.addRow("Name_3", nameLe)

        addressLe = QLineEdit()
        addressLe.setObjectName("addressLe_3")
        self.layout_3.addRow("Address_3", addressLe)

        self.tab3.setLayout(self.layout_3)    

    def closeEvent(self, event):
        save(self.settings)
        QMainWindow.closeEvent(self, event)


def main():
    app = QApplication(sys.argv)
    ex = mainwindow()
    ex.setGeometry(100, 100, 1000, 600)
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

标签: python-3.xpyqt5qtablewidgetqtabwidget

解决方案


QLineEdit 数据被存储,问题是它没有显示。小部件的visible属性取决于它的祖先[s];由于 QTabWidget 的每个选项卡都有一个 QWidget,因此只有当前一个可见,而另一个(包括它们的所有子级)不可见。QLineEdit 是一个复杂的小部件,如果您手动取消设置其所有内部组件的可见属性,它们将无法再次正确恢复。
如果您在将第二个选项卡设置为当前选项卡的情况下关闭窗口,这一点就会变得清晰:一旦您再次打开它,将显示第二个选项卡并正确显示其内容,而第一个 QLineEdit 不会。

一种解决方法是检查小部件是否“存储”为不可见并且具有父级。如果是这种情况,请不要应用 visible 属性。

def restore(settings):
    # ...
    for w in qApp.allWidgets():
        mo = w.metaObject()
        parent = w.parent()
        if w.objectName() != "":
            for i in range(mo.propertyCount()):
                name = mo.property(i).name()
                val = settings.value("{}/{}".format(w.objectName(), name), w.property(name))
                if name == 'visible' and val == 'false' and parent:
                    continue
                w.setProperty(name, val)

也就是说,这显然不是保存任何小部件数据的好方法,因为它保存了它拥有的每个属性:示例的标题是“保存和恢复小部件的功能”,而不是您需要的可编辑数据.
此外,QTableWidget 内容不能使用此方法存储,因为它的数据不是小部件属性的一部分(无论如何都没有保存任何其他内容,因为您没有设置它的对象名称)。

您必须找到自己的实现来保存这些内容,这可能类似于以下代码。
请注意,我使用 QByteArray 来存储表数据。您创建一个 QByteArray,然后创建一个 QDataStream,并将其作为参数,用于读取或写入。请注意,读取和写入都是相应的,因为数据总是在写入时附加或在每次读取时“弹出”。

class mainwindow(QMainWindow):
    # ...
    def save(self):
        # using findChildren is for simplicity, it's probably better to create
        # your own list of widgets to cycle through
        for w in self.findChildren((QLineEdit, QTableWidget)):
            name = w.objectName()
            if isinstance(w, QLineEdit):
                self.settings.setValue("{}/text".format(name), w.text())
            elif isinstance(w, QTableWidget):
                # while we could add sub-setting keys for each combination of
                # row/column items, it's better to store the data in a single
                # "container"
                data = QByteArray()
                stream = QDataStream(data, QIODevice.WriteOnly)

                rowCount = w.rowCount()
                columnCount = w.columnCount()
                # write the row and column count first
                stream.writeInt(rowCount)
                stream.writeInt(columnCount)

                # then write the data
                for row in range(rowCount):
                    for col in range(columnCount):
                        stream.writeQString(w.item(row, col).text())
                self.settings.setValue("{}/data".format(name), data)

    def restore(self):
        for w in self.findChildren((QLineEdit, QTableWidget)):
            name = w.objectName()
            if isinstance(w, QLineEdit):
                w.setText(self.settings.value("{}/text".format(name), w.text()))
            elif isinstance(w, QTableWidget):
                data = self.settings.value("{}/data".format(name))
                if not data:
                    continue
                stream = QDataStream(data, QIODevice.ReadOnly)

                # read the row and column count first
                rowCount = stream.readInt()
                columnCount = stream.readInt()
                w.setRowCount(rowCount)
                w.setColumnCount(columnCount)

                # then read the data
                for row in range(rowCount):
                    for col in range(columnCount):
                        cellText = stream.readQString()
                        if cellText:
                            w.item(row, col).setText(cellText)

推荐阅读