【问题标题】:Python PyQt4 functions to save and restore UI widget values?用于保存和恢复 UI 小部件值的 Python PyQt4 函数?
【发布时间】: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)

【问题讨论】:

    标签: python pyqt4


    【解决方案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

              干杯!

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-01-05
                • 1970-01-01
                • 2019-03-13
                • 2011-10-15
                相关资源
                最近更新 更多