用于保存和恢复 UI 小部件值的 Python PyQt4 函数?
Posted
技术标签:
【中文标题】用于保存和恢复 UI 小部件值的 Python PyQt4 函数?【英文标题】:Python PyQt4 functions to save and restore UI widget values? 【发布时间】:2014-04-24 20:28:51 【问题描述】:在我尝试编写自己的 Python PyQt4 模块函数之前......我想问一下是否有人有这样的函数可以分享。
在我使用 PyQt4 和 qtDesigner 构建的 GUI 的许多 python 程序中,我使用 QSettings 方法在关闭和启动期间保存和恢复所有小部件的 UI 状态和值。
这个例子展示了我如何保存和恢复一些 lineEdit、checkBox 和 radioButton 字段。
有没有人可以遍历 UI 并找到所有小部件/控件及其状态并保存它们(例如 guisave())和另一个可以恢复它们的函数(例如 guirestore())?
我的 closeEvent 看起来像这样:
#---------------------------------------------
# close by x OR call to self.close
#---------------------------------------------
def closeEvent(self, event): # user clicked the x or pressed alt-F4...
UI_VERSION = 1 # increment this whenever the UI changes significantly
programname = os.path.basename(__file__)
programbase, ext = os.path.splitext(programname) # extract basename and ext from filename
settings = QtCore.QSettings("company", programbase)
settings.setValue("geometry", self.saveGeometry()) # save window geometry
settings.setValue("state", self.saveState(UI_VERSION)) # save settings (UI_VERSION is a constant you should increment when your UI changes significantly to prevent attempts to restore an invalid state.)
# save ui values, so they can be restored next time
settings.setValue("lineEditUser", self.lineEditUser.text());
settings.setValue("lineEditPass", self.lineEditPass.text());
settings.setValue("checkBoxReplace", self.checkBoxReplace.checkState());
settings.setValue("checkBoxFirst", self.checkBoxFirst.checkState());
settings.setValue("radioButton1", self.radioButton1.isChecked());
sys.exit() # prevents second call
我的 MainWindow 初始化看起来像这样:
def __init__(self, parent = None):
# initialization of the superclass
super(QtDesignerMainWindow, self).__init__(parent)
# setup the GUI --> function generated by pyuic4
self.setupUi(self)
#---------------------------------------------
# restore gui position and restore fields
#---------------------------------------------
UI_VERSION = 1
settings = QtCore.QSettings("company", programbase) # http://pyqt.sourceforge.net/Docs/PyQt4/pyqt_qsettings.html
self.restoreGeometry(settings.value("geometry"))
self.restoreState(settings.value("state"),UI_VERSION)
self.lineEditUser.setText(str(settings.value("lineEditUser"))) # restore lineEditFile
self.lineEditPass.setText(str(settings.value("lineEditPass"))) # restore lineEditFile
if settings.value("checkBoxReplace") != None:
self.checkBoxReplace.setCheckState(settings.value("checkBoxReplace")) # restore checkbox
if settings.value("checkBoxFirst") != None:
self.checkBoxFirst.setCheckState(settings.value("checkBoxFirst")) # restore checkbox
value = settings.value("radioButton1").toBool()
self.ui.radioButton1.setChecked(value)
【问题讨论】:
【参考方案1】:好的,我编写了一个具有 2 个功能的模块来完成我的要求。 并不是那么复杂,一旦我弄清楚了,但是当你创建新的 pyqt gui 程序时,它确实可以节省很多时间,你想在会话之间保存小部件字段值。 我目前只有 lineEdit、checkBox 和 combobox 字段编码。如果其他人想要添加或改进(例如单选按钮......等)......我相信其他人,包括我自己,会很感激。
#===================================================================
# Module with functions to save & restore qt widget values
# Written by: Alan Lilly
# Website: http://panofish.net
#===================================================================
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import inspect
#===================================================================
# save "ui" controls and values to registry "setting"
# currently only handles comboboxes editlines & checkboxes
# ui = qmainwindow object
# settings = qsettings object
#===================================================================
def guisave(ui, settings):
#for child in ui.children(): # works like getmembers, but because it traverses the hierarachy, you would have to call guisave recursively to traverse down the tree
for name, obj in inspect.getmembers(ui):
#if type(obj) is QComboBox: # this works similar to isinstance, but missed some field... not sure why?
if isinstance(obj, QComboBox):
name = obj.objectName() # get combobox name
index = obj.currentIndex() # get current index from combobox
text = obj.itemText(index) # get the text for current index
settings.setValue(name, text) # save combobox selection to registry
if isinstance(obj, QLineEdit):
name = obj.objectName()
value = obj.text()
settings.setValue(name, value) # save ui values, so they can be restored next time
if isinstance(obj, QCheckBox):
name = obj.objectName()
state = obj.checkState()
settings.setValue(name, state)
#===================================================================
# restore "ui" controls with values stored in registry "settings"
# currently only handles comboboxes, editlines &checkboxes
# ui = QMainWindow object
# settings = QSettings object
#===================================================================
def guirestore(ui, settings):
for name, obj in inspect.getmembers(ui):
if isinstance(obj, QComboBox):
index = obj.currentIndex() # get current region from combobox
#text = obj.itemText(index) # get the text for new selected index
name = obj.objectName()
value = unicode(settings.value(name))
if value == "":
continue
index = obj.findText(value) # get the corresponding index for specified string in combobox
if index == -1: # add to list if not found
obj.insertItems(0,[value])
index = obj.findText(value)
obj.setCurrentIndex(index)
else:
obj.setCurrentIndex(index) # preselect a combobox value by index
if isinstance(obj, QLineEdit):
name = obj.objectName()
value = unicode(settings.value(name)) # get stored value from registry
obj.setText(value) # restore lineEditFile
if isinstance(obj, QCheckBox):
name = obj.objectName()
value = settings.value(name) # get stored value from registry
if value != None:
obj.setCheckState(value) # restore checkbox
#if isinstance(obj, QRadioButton):
################################################################
if __name__ == "__main__":
# execute when run directly, but not when called as a module.
# therefore this section allows for testing this module!
#print "running directly, not as a module!"
sys.exit()
【讨论】:
非常有用的小模块。谢谢!将 QSettings 保存到 ini 文件时效果很好,例如guisave(self.ui, QtCore.QSettings('saved.ini', QtCore.QSettings.IniFormat))
创建一个可以在用户之间共享的文件。
我不得不将几行更改为 value = unicode(settings.value(name).toString())
,因为 settings.value 正在返回一个 QVariant。
酷改进 Snorfalorpagus!我喜欢:)
刚刚注意到对 QCheckBox 对象使用obj.setCheckState(value)
会启用三态,这可能不是我们所希望的。就我而言,我知道我从不想要三态,而是使用了obj.setChecked(value)
。我不确定如何在加载时检测到这一点,并为这两种情况正确恢复。
节省了我的一天:)【参考方案2】:
这是一个更新的sn-p,最初由先生分享。鲶鱼。那些伟大的功能是相同的,但现在可以在 PyQt 和 Python 的任何版本上使用,如果需要,只需稍作改动。谢谢先生。 Panofish,开源万岁! :)
变化:
为 Python3 和 PyQt5 更新 添加几何保存\恢复 添加了 QRadioButton 保存\恢复用 SetChecked() 替换 SetCheckState() 以避免三态
def guisave(self):
# Save geometry
self.settings.setValue('size', self.size())
self.settings.setValue('pos', self.pos())
for name, obj in inspect.getmembers(ui):
# if type(obj) is QComboBox: # this works similar to isinstance, but missed some field... not sure why?
if isinstance(obj, QComboBox):
name = obj.objectName() # get combobox name
index = obj.currentIndex() # get current index from combobox
text = obj.itemText(index) # get the text for current index
settings.setValue(name, text) # save combobox selection to registry
if isinstance(obj, QLineEdit):
name = obj.objectName()
value = obj.text()
settings.setValue(name, value) # save ui values, so they can be restored next time
if isinstance(obj, QCheckBox):
name = obj.objectName()
state = obj.isChecked()
settings.setValue(name, state)
if isinstance(obj, QRadioButton):
name = obj.objectName()
value = obj.isChecked() # get stored value from registry
settings.setValue(name, value)
def guirestore(self):
# Restore geometry
self.resize(self.settings.value('size', QtCore.QSize(500, 500)))
self.move(self.settings.value('pos', QtCore.QPoint(60, 60)))
for name, obj in inspect.getmembers(ui):
if isinstance(obj, QComboBox):
index = obj.currentIndex() # get current region from combobox
# text = obj.itemText(index) # get the text for new selected index
name = obj.objectName()
value = (settings.value(name))
if value == "":
continue
index = obj.findText(value) # get the corresponding index for specified string in combobox
if index == -1: # add to list if not found
obj.insertItems(0, [value])
index = obj.findText(value)
obj.setCurrentIndex(index)
else:
obj.setCurrentIndex(index) # preselect a combobox value by index
if isinstance(obj, QLineEdit):
name = obj.objectName()
value = (settings.value(name).decode('utf-8')) # get stored value from registry
obj.setText(value) # restore lineEditFile
if isinstance(obj, QCheckBox):
name = obj.objectName()
value = settings.value(name) # get stored value from registry
if value != None:
obj.setChecked(strtobool(value)) # restore checkbox
if isinstance(obj, QRadioButton):
name = obj.objectName()
value = settings.value(name) # get stored value from registry
if value != None:
obj.setChecked(strtobool(value))
【讨论】:
【参考方案3】:谢谢 Panofish 和大家 我正在为 QSlider/QSpinBox 添加一些更新。 它小而简单。
在 guisave 你可以添加:
if isinstance(obj, QSpinBox):
name = obj.objectName()
value = obj.value() # get stored value from registry
settings.setValue(name, value)
if isinstance(obj, QSlider):
name = obj.objectName()
value = obj.value() # get stored value from registry
settings.setValue(name, value)
您可以在 guirestore 添加:
if isinstance(obj, QSlider):
name = obj.objectName()
value = settings.value(name) # get stored value from registry
if value != None:
obj. setValue(int(value)) # restore value from registry
if isinstance(obj, QSpinBox):
name = obj.objectName()
value = settings.value(name) # get stored value from registry
if value != None:
obj. setValue(int(value)) # restore value from registry
【讨论】:
【参考方案4】:我正在为 QListWidget 添加更新。
伪装:
if isinstance(obj, QListWidget):
name = obj.objectName()
settings.beginWriteArray(name)
for i in range(obj.count()):
settings.setArrayIndex(i)
settings.setValue(name, obj.item(i).text())
settings.endArray()
在guirestore中:
if isinstance(obj, QListWidget):
name = obj.objectName()
size = settings.beginReadArray(name)
for i in range(size):
settings.setArrayIndex(i)
value = settings.value(name) # get stored value from registry
if value != None:
obj.addItem(value)
settings.endArray()
【讨论】:
【参考方案5】:这是这个出色代码的另一个版本,其中包括 QTabWidget,并将所有内容包装到一个类中以便于使用:
qt_utils.py:
from PyQt5 import QtGui
from PyQt5.QtWidgets import QComboBox, QCheckBox, QLineEdit,\
QRadioButton, QSpinBox, QSlider, QListWidget, QTabWidget
from PyQt5.QtCore import QSettings
from distutils.util import strtobool
import inspect
class QMainWindow(QtGui.QMainWindow):
companie_name = 'CompanieName'
software_name = 'SoftwareName'
settings_ui_name = 'defaultUiwidget'
settings_ui_user_name = 'user'
_names_to_avoid =
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent)
self.settings = QSettings(self.companie_name, self.software_name)
def closeEvent(self, e):
self._gui_save()
@classmethod
def _get_handled_types(cls):
return QComboBox, QLineEdit, QCheckBox, QRadioButton, QSpinBox, QSlider, QListWidget, QTabWidget
@classmethod
def _is_handled_type(cls, widget):
return any(isinstance(widget, t) for t in cls._get_handled_types())
def _gui_save(self):
"""
save "ui" controls and values to registry "setting"
:return:
"""
name_prefix = f"self.settings_ui_name/"
self.settings.setValue(name_prefix + "geometry", self.saveGeometry())
for name, obj in inspect.getmembers(self):
if not self._is_handled_type(obj):
continue
name = obj.objectName()
value = None
if isinstance(obj, QComboBox):
index = obj.currentIndex() # get current index from combobox
value = obj.itemText(index) # get the text for current index
elif isinstance(obj, QTabWidget):
value = obj.currentIndex()
elif isinstance(obj, QLineEdit):
value = obj.text()
elif isinstance(obj, QCheckBox):
value = obj.isChecked()
elif isinstance(obj, QRadioButton):
value = obj.isChecked()
elif isinstance(obj, QSpinBox):
value = obj.value()
elif isinstance(obj, QSlider):
value = obj.value()
elif isinstance(obj, QListWidget):
self.settings.beginWriteArray(name)
for i in range(obj.count()):
self.settings.setArrayIndex(i)
self.settings.setValue(name_prefix + name, obj.item(i).text())
self.settings.endArray()
if value is not None:
self.settings.setValue(name_prefix + name, value)
def _gui_restore(self):
"""
restore "ui" controls with values stored in registry "settings"
:return:
"""
name_prefix = f"self.settings_ui_name/"
geometry_value = self.settings.value(name_prefix + "geometry")
if geometry_value:
self.restoreGeometry(geometry_value)
for name, obj in inspect.getmembers(self):
if not self._is_handled_type(obj):
continue
if name in self._names_to_avoid:
continue
name = obj.objectName()
value = None
if not isinstance(obj, QListWidget):
value = self.settings.value(name_prefix + name)
if value is None:
continue
if isinstance(obj, QComboBox):
index = obj.findText(value) # get the corresponding index for specified string in combobox
if index == -1: # add to list if not found
obj.insertItems(0, [value])
index = obj.findText(value)
obj.setCurrentIndex(index)
else:
obj.setCurrentIndex(index) # preselect a combobox value by index
elif isinstance(obj, QTabWidget):
try:
value = int(value)
except ValueError:
value = 0
obj.setCurrentIndex(value)
elif isinstance(obj, QLineEdit):
obj.setText(value)
elif isinstance(obj, QCheckBox):
obj.setChecked(strtobool(value))
elif isinstance(obj, QRadioButton):
obj.setChecked(strtobool(value))
elif isinstance(obj, QSlider):
obj.setValue(int(value))
elif isinstance(obj, QSpinBox):
obj.setValue(int(value))
elif isinstance(obj, QListWidget):
size = self.settings.beginReadArray(name_prefix + name)
for i in range(size):
self.settings.setArrayIndex(i)
value = self.settings.value(name_prefix + name)
if value is not None:
obj.addItem(value)
self.settings.endArray()
def _add_setting(self, name, value):
name_prefix = f"self.settings_ui_user_name/"
self.settings.setValue(name_prefix + name, value)
def _get_setting(self, name):
name_prefix = f"self.settings_ui_user_name/"
return self.settings.value(name_prefix + name)
这是一个使用示例:
import qt_utils
class MyMaine(qt_utils.QMainWindow, Ui_MainWindow):
companie_name = 'Name'
software_name = 'softName'
_names_to_avoid = 'my_widget_name_not_to_save'
def __init__(self, parent=None):
super(MyMaine, self).__init__(parent)
self.setupUi(self)
self._gui_restore()
【讨论】:
【参考方案6】:我发现这些答案很有用,所以我想我会将它们放在一起并发布一个版本(用于 PySide2/Qt5),其中删除了一些重复项并提供了一个名称以将设置分组。
from PySide2.QtWidgets import *
import inspect
def GetHandledTypes():
return (QComboBox, QLineEdit, QCheckBox, QRadioButton, QSpinBox, QSlider, QListWidget)
def IsHandledType(widget):
return any(isinstance(widget, t) for t in GetHandledTypes())
#===================================================================
# save "ui" controls and values to registry "setting"
#===================================================================
def GuiSave(ui : QWidget, settings : QSettings, uiName="uiwidget"):
namePrefix = f"uiName/"
settings.setValue(namePrefix + "geometry", ui.saveGeometry())
for name, obj in inspect.getmembers(ui):
if not IsHandledType(obj):
continue
name = obj.objectName()
value = None
if isinstance(obj, QComboBox):
index = obj.currentIndex() # get current index from combobox
value = obj.itemText(index) # get the text for current index
if isinstance(obj, QLineEdit):
value = obj.text()
if isinstance(obj, QCheckBox):
value = obj.isChecked()
if isinstance(obj, QRadioButton):
value = obj.isChecked()
if isinstance(obj, QSpinBox):
value = obj.value()
if isinstance(obj, QSlider):
value = obj.value()
if isinstance(obj, QListWidget):
settings.beginWriteArray(name)
for i in range(obj.count()):
settings.setArrayIndex(i)
settings.setValue(namePrefix + name, obj.item(i).text())
settings.endArray()
elif value is not None:
settings.setValue(namePrefix + name, value)
#===================================================================
# restore "ui" controls with values stored in registry "settings"
#===================================================================
def GuiRestore(ui : QWidget, settings : QSettings, uiName="uiwidget"):
from distutils.util import strtobool
namePrefix = f"uiName/"
geometryValue = settings.value(namePrefix + "geometry")
if geometryValue:
ui.restoreGeometry(geometryValue)
for name, obj in inspect.getmembers(ui):
if not IsHandledType(obj):
continue
name = obj.objectName()
value = None
if not isinstance(obj, QListWidget):
value = settings.value(namePrefix + name)
if value is None:
continue
if isinstance(obj, QComboBox):
index = obj.findText(value) # get the corresponding index for specified string in combobox
if index == -1: # add to list if not found
obj.insertItems(0, [value])
index = obj.findText(value)
obj.setCurrentIndex(index)
else:
obj.setCurrentIndex(index) # preselect a combobox value by index
if isinstance(obj, QLineEdit):
obj.setText(value)
if isinstance(obj, QCheckBox):
obj.setChecked(strtobool(value))
if isinstance(obj, QRadioButton):
obj.setChecked(strtobool(value))
if isinstance(obj, QSlider):
obj.setValue(int(value))
if isinstance(obj, QSpinBox):
obj.setValue(int(value))
if isinstance(obj, QListWidget):
size = settings.beginReadArray(namePrefix + name)
for i in range(size):
settings.setArrayIndex(i)
value = settings.value(namePrefix + name)
if value is not None:
obj.addItem(value)
settings.endArray()```
【讨论】:
【参考方案7】:我是一名新手程序员,所以我不确定我哪里出错了,请原谅我缺乏正确的术语/技术理解,但我的应用程序有多个 QWidget 类,用于加载/卸载不同 UI 的不同应用程序“状态” (和相关功能)每个状态 - 全部进出单个主“QMainWindow”
这是使用@beesleep 类实现,但看起来有问题的代码对于上述所有示例都是相同的
我遇到了问题 -
def _is_handled_type(cls, widget):
return any(isinstance(widget, t) for t in cls._get_handled_types())
for name, obj in inspect.getmembers(self)
# print(name) <------------------------------
if not self._is_handled_type...
返回 self "QMainWindow" 的方法而不是所有活动的子小部件。
.
我的解决方法是这样的。我删了——***这部分不是必须的,只是代码不再使用了
@classmethod
def _is_handled_type(cls, widget):
return any(isinstance(widget, t) fort in cls._get_handled_types())
然后在 _gui_save() 和 _gui_restore() 中更改 -
for name, obj in inspect.getmembers(self):
if not self._is_handled_type(obj):
continue
if name in self._names_to_avoid: # _gui_restore()
continue # _gui_restore()
name = obj.objectName()
value = None
到-
for child in self._get_handled_types():
for obj in self.findChildren(child):
if obj:
name = obj.objectName()
if name in self._names_to_avoid: # _gui_restore()
continue # _gui_restore()
value = None
最后一步是您必须为要保存的每个对象分配一个名称
self.q_list = QListWidget()
self.q_list.setObjectName("List")
...
self.q_text = QLineEdit('enter text')
self.q_text.setObjectName("Text")
.
for child in self._get_handled_types():
for obj in self.findChildren(child):
if obj:
name = obj.objectName()
print(name, obj) <-------------
返回
Text <PyQt5.QtWidgets.QLineEdit object at 0x0000023DD43E2708>
List <PyQt5.QtWidgets.QListWidget object at 0x0000023DD42530D8>
最后的说明...
如果您想查看设置文件的保存位置,请添加 - print(self.settings.fileName())
在 init
干杯!
【讨论】:
以上是关于用于保存和恢复 UI 小部件值的 Python PyQt4 函数?的主要内容,如果未能解决你的问题,请参考以下文章
用于颜色选择的jQuery UI小部件(类似于Microsoft Office 2010中的小部件)。
用于 jQuery UI 的 Star Rating 小部件
通过 Python 从 .ui 文件处理 Pyside Qt 小部件的正确方法