Dealing with QVariants in a settings-module with python 2.7.6 / PyQt4
<p>I have previously read several topics on this site and other platforms, but my code does still not work as desired. I seem not to be able to put all the puzzle pieces together and need someone to look over my code. </p>

<p>I'm writing a plugin for the program QtiPlot.  Im using Python 2.7.6 and PyQt4. I created the Plugin GUI with QT Designer. I'm also new to Python. Im using these "old" resources because my predecessor has used them.</p>

<p>My current task is to develop the settings, i.e. being able to save and restore parameters.
I found a template on this site for this purpose: <a href="http://stackoverflow.com/questions/23279125/python-pyqt4-functions-to-save-and-restore-ui-widget-values">Python PyQt4 functions to save and restore UI widget values?</a></p>

<p>I want to save parameters to an Ini-File.
However I have problems with the QVariants. Instead of the strings I inserted in the plugin, the expression "PyQt4.QtCore.QVariant object at 0x08667FB0" is being saved. I already know that this is an issue due to the fact that the QVariants are not correcly converted back to Python objects. </p>

<p>So, in order to convert the QVariants back manually, I added the phrase "toString()" to the value assignment of QLineEdit-Objects in the restore function(the line commented out is the previous version). But then my QLineEdit-Blocks in the plugin are empty, which confuses me. I read in the documentation that an empty string is returned if the QVariant does not consist of one of the preset types, including string. But this happens although I entered a string before.</p>

<p>Does this mean that the strings are not correctly saved in the first place? Or else what does it mean, what do I miss or what am I doing wrong?</p>

<p>I also noticed that no values are stored in the Ini-File, so that part of the code is also still buggy. But I'm not sure if this due to the fact that the save-function doesn't work or because the Ini-construction is wrong itself.</p>

<p>Further, I tried to use the SIP-Module at the head of the configuration file to fix my problem(it is commented out, too, because it didn't work for me so far). But although I placed this at the head of the code, I then get the error "API 'QVariant' has already been set to version 1". I don't understand why or from what the SIP-instruction is overridden. Is there any way to fix this?</p>

<p>My Configuration program looks like this:</p>

<pre><code>#import sip    
#sip.setapi('QVariant', 2)    
#sip.setapi('QString', 2)

import inspect    
import sys

from PyQt4.QtCore import *    
from PyQt4.QtGui import *

sys.path.append("C:\\Program Files (x86)\\QtiPlot\\app\\02 Python Packages")

import PyQt4_Save_Restore_UI_Widget_Values

class app_conf(QtGui.QWidget):

    def __init__(self):

        super(self.__class__, self).__init__()

        self.version = 1.0

        QtCore.QCoreApplication.setOrganizationName("Organization")
        QtCore.QCoreApplication.setApplicationName("Application")
        QtCore.QSettings.setPath(QSettings.IniFormat, QSettings.UserScope, "C:\\Program Files (x86)\\QtiPlot\\app\\02 Python Packages\\saved.ini")
        self.settings = QtCore.QSettings("C:\\Program Files (x86)\\QtiPlot\\app\\02 Python Packages\\saved.ini", QSettings.IniFormat)

        from PyQt4 import uic

        self.ui = uic.loadUi(r"C:\Program Files (x86)/QtiPlot/app/03 UI Files/Config.ui")

        PyQt4_Save_Restore_UI_Widget_Values.gui_restore_settings(self.ui, self.settings)

        self.ui.closeEvent = self.closeEvent
        self.ui.show()


    def closeEvent(self, event):            
        PyQt4_Save_Restore_UI_Widget_Values.gui_save_settings(self.ui, self.settings)


window = app_conf()
</code></pre>

<p>And my settings module(PyQt4_Save_Restore_UI_Widget_Values.py) looks like this:</p>

<pre><code>#===================================================================
# Module with functions to save &amp; restore qt widget values
# Written by: Alan Lilly 
# Website: http://panofish.net
#===================================================================

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import inspect


def gui_save_settings(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)


def gui_restore_settings(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
            value = settings.value(name).toString()
            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.toBool())   # restore checkbox


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

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() 
</code></pre>

<p>The best solution is to use <code>sip.setapi</code>. But to get it to work properly, it <strong>must</strong> be invoked before the <em>first</em> import of PyQt in your application. So it needs to go in the main script, not the config module:</p>

<pre><code>#===================================================================
# Module with functions to save &amp; restore qt widget values
# Written by: Alan Lilly 
# Website: http://panofish.net
#===================================================================
import sys
import sip
sip.setapi('QVariant', 2)    
sip.setapi('QString', 2)
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import inspect
</code></pre>

<p>This will ensure <code>QVariant</code> and <code>QString</code> are always automatically converted to ordinary python types - so no need to use <code>unicode()</code>, <code>toString()</code>, <code>toBool()</code>, etc.</p>

python pyqt4