Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QSettings: inferring type of stored value
Qt 6.11 is out! See what's new in the release blog

QSettings: inferring type of stored value

Scheduled Pinned Locked Moved Solved General and Desktop
17 Posts 4 Posters 3.5k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Christian EhrlicherC Offline
    Christian EhrlicherC Offline
    Christian Ehrlicher
    Lifetime Qt Champion
    wrote on last edited by
    #7

    It's working fine here:

    QSettings s("D:\\test.ini", QSettings::IniFormat);
    s.setValue("mybool", true);
    qDebug() << s.value("mybool");
    

    --> QVariant(bool, true)

    So I would say the debugger is simply converting the bool to a user-visible value since QVariant<bool>() can be properly converted to a QString.

    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
    Visit the Qt Academy at https://academy.qt.io/catalog

    Axel SpoerlA 1 Reply Last reply
    0
    • Christian EhrlicherC Christian Ehrlicher

      It's working fine here:

      QSettings s("D:\\test.ini", QSettings::IniFormat);
      s.setValue("mybool", true);
      qDebug() << s.value("mybool");
      

      --> QVariant(bool, true)

      So I would say the debugger is simply converting the bool to a user-visible value since QVariant<bool>() can be properly converted to a QString.

      Axel SpoerlA Offline
      Axel SpoerlA Offline
      Axel Spoerl
      Moderators
      wrote on last edited by
      #8

      @Christian-Ehrlicher
      @mzimmers

      At debugging time, the variant is still in memory. Not sure, but my guess is that the debugging output is different, when you read the settings right awawy in the class constructor.

      Software Engineer
      The Qt Company, Oslo

      1 Reply Last reply
      0
      • mzimmersM Offline
        mzimmersM Offline
        mzimmers
        wrote on last edited by
        #9

        @Axel-Spoerl @Christian-Ehrlicher I've moved some code around:

        class NgaSettings : public QObject
        {
            Q_OBJECT
            QML_ELEMENT
            QSettings *m_settings = new QSettings("company", "nga", this);
            
        public:
            explicit NgaSettings(QObject *parent = nullptr) {
                m_settings->clear();
                m_settings->setValue("name", "Mike");
                m_settings->setValue("age", "65");
                m_settings->setValue("city", "Salinas");
                m_settings->setValue("is golfing today", false);
        }
            INVOKABLE void getType(const QVariant &v) {
                if (v.canConvert<QString>()) {
                    qDebug() << "QString";
                } else if (v.canConvert<bool>()) {
                    qDebug() << "bool";
                } else {
                    qDebug() << "unknown";
                }
            }
        

        And I test it in QML:

            Component.onCompleted: {
                console.log("SettingsList.qml: type is " + ngaSettings.getType("is golfing today"))
            }
        

        getType returns true on the test for QString. I don't think re-ordering the test will help, as it appears that any non-null QString will convert to a (true) bool.

        So, I'm back to thinking that I have to maintain a map of keys to types.

        JonBJ 1 Reply Last reply
        0
        • Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #10

          What do you expect - as I said a bool can be converted to a string. You have to check the type of QVariant...

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          1 Reply Last reply
          0
          • Axel SpoerlA Offline
            Axel SpoerlA Offline
            Axel Spoerl
            Moderators
            wrote on last edited by Axel Spoerl
            #11

            QSettings out of the box expects the user to know the type of each key.
            When the settings object is constructed and key/value pairs are added, they are actually stored in a list in memory and remember their original type.
            Custom enums become integers.
            Once read back from the settings file, everything (numbers included) will be interpreted as a string by default.

            Example:

            MainWindow::MainWindow(QWidget *parent)
                : QMainWindow(parent)
                , ui(new Ui::MainWindow)
            {
                QSettings s("/home/axel/tmp/settings.conf", QSettings::Format::NativeFormat);
                qDebug() << "----------- after write -----------";
                qDebug() << "One" << s.value("One");
                qDebug() << "Two" << s.value("Two");
                qDebug() << "Three" << s.value("Three");
                qDebug() << "Boolean" << s.value("Boolean");
            }
            
            MainWindow::~MainWindow()
            {
                QSettings s("/home/axel/tmp/settings.conf", QSettings::Format::NativeFormat);
                s.setValue("One", Value1);
                s.setValue("Two", IllegalValue);
                s.setValue("Three", Dummy);
                s.setValue("Boolean", true);
                qDebug() << "----------- before write -----------";
                qDebug() << "One" << s.value("One");
                qDebug() << "Two" << s.value("Two");
                qDebug() << "Three" << s.value("Three");
                qDebug() << "Boolean" << s.value("Boolean");
                delete ui;
            }
            

            Output:

            ----------- after write -----------
            One QVariant(QString, "0")
            Two QVariant(QString, "1")
            Three QVariant(QString, "2")
            Boolean QVariant(QString, "true")
            ----------- before write -----------
            One QVariant(int, 0)
            Two QVariant(int, 1)
            Three QVariant(int, 2)
            Boolean QVariant(bool, true)
            

            Software Engineer
            The Qt Company, Oslo

            mzimmersM 1 Reply Last reply
            0
            • mzimmersM mzimmers

              @Axel-Spoerl @Christian-Ehrlicher I've moved some code around:

              class NgaSettings : public QObject
              {
                  Q_OBJECT
                  QML_ELEMENT
                  QSettings *m_settings = new QSettings("company", "nga", this);
                  
              public:
                  explicit NgaSettings(QObject *parent = nullptr) {
                      m_settings->clear();
                      m_settings->setValue("name", "Mike");
                      m_settings->setValue("age", "65");
                      m_settings->setValue("city", "Salinas");
                      m_settings->setValue("is golfing today", false);
              }
                  INVOKABLE void getType(const QVariant &v) {
                      if (v.canConvert<QString>()) {
                          qDebug() << "QString";
                      } else if (v.canConvert<bool>()) {
                          qDebug() << "bool";
                      } else {
                          qDebug() << "unknown";
                      }
                  }
              

              And I test it in QML:

                  Component.onCompleted: {
                      console.log("SettingsList.qml: type is " + ngaSettings.getType("is golfing today"))
                  }
              

              getType returns true on the test for QString. I don't think re-ordering the test will help, as it appears that any non-null QString will convert to a (true) bool.

              So, I'm back to thinking that I have to maintain a map of keys to types.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by
              #12

              @mzimmers
              I find the way you are going about things slightly odd. Usually in the case of QSettings where you are saving/restoring named "settings/variables" you know the type of the item. E.g. if you have a width variable/member/whatever it's going to be a number, if it's a last_modified it's going to be a QDateTime, etc. When you read back you know from the name which variable you're going to store the read value in so you know what type to ask for (even if you call canConvert<>() to be sure). There are not so many obvious use cases where you would need to look at the (unknown) type of a setting read in, if you don't know the type in advance what are you going to do with whatever you get anyway?

              mzimmersM 1 Reply Last reply
              0
              • Axel SpoerlA Offline
                Axel SpoerlA Offline
                Axel Spoerl
                Moderators
                wrote on last edited by
                #13

                @mzimmers
                Maybe you want to split the implementation. It looks as if a variant map is what you need for the user to view, modify, add and remove key/value pairs. Another question is how to save / restore it. Could be in a database, a proprietary file format, or streamed into a byte array and saved in settings.

                Software Engineer
                The Qt Company, Oslo

                1 Reply Last reply
                0
                • Axel SpoerlA Axel Spoerl

                  QSettings out of the box expects the user to know the type of each key.
                  When the settings object is constructed and key/value pairs are added, they are actually stored in a list in memory and remember their original type.
                  Custom enums become integers.
                  Once read back from the settings file, everything (numbers included) will be interpreted as a string by default.

                  Example:

                  MainWindow::MainWindow(QWidget *parent)
                      : QMainWindow(parent)
                      , ui(new Ui::MainWindow)
                  {
                      QSettings s("/home/axel/tmp/settings.conf", QSettings::Format::NativeFormat);
                      qDebug() << "----------- after write -----------";
                      qDebug() << "One" << s.value("One");
                      qDebug() << "Two" << s.value("Two");
                      qDebug() << "Three" << s.value("Three");
                      qDebug() << "Boolean" << s.value("Boolean");
                  }
                  
                  MainWindow::~MainWindow()
                  {
                      QSettings s("/home/axel/tmp/settings.conf", QSettings::Format::NativeFormat);
                      s.setValue("One", Value1);
                      s.setValue("Two", IllegalValue);
                      s.setValue("Three", Dummy);
                      s.setValue("Boolean", true);
                      qDebug() << "----------- before write -----------";
                      qDebug() << "One" << s.value("One");
                      qDebug() << "Two" << s.value("Two");
                      qDebug() << "Three" << s.value("Three");
                      qDebug() << "Boolean" << s.value("Boolean");
                      delete ui;
                  }
                  

                  Output:

                  ----------- after write -----------
                  One QVariant(QString, "0")
                  Two QVariant(QString, "1")
                  Three QVariant(QString, "2")
                  Boolean QVariant(QString, "true")
                  ----------- before write -----------
                  One QVariant(int, 0)
                  Two QVariant(int, 1)
                  Three QVariant(int, 2)
                  Boolean QVariant(bool, true)
                  
                  mzimmersM Offline
                  mzimmersM Offline
                  mzimmers
                  wrote on last edited by mzimmers
                  #14

                  @Axel-Spoerl said in QSettings: inferring type of stored value:

                  Once read back from the settings file, everything (numbers included) will be interpreted as a string by default.

                  Aha...I wasn't aware of this, and this explains why much of what I was trying wasn't working.

                  I notice you said "by default." Does this mean this can be changed from within the QSettings class, or do I need to handle this myself?

                  Regarding your suggestion of splitting the implementation, I think that's exactly what's necessary.

                  Thanks!

                  1 Reply Last reply
                  0
                  • JonBJ JonB

                    @mzimmers
                    I find the way you are going about things slightly odd. Usually in the case of QSettings where you are saving/restoring named "settings/variables" you know the type of the item. E.g. if you have a width variable/member/whatever it's going to be a number, if it's a last_modified it's going to be a QDateTime, etc. When you read back you know from the name which variable you're going to store the read value in so you know what type to ask for (even if you call canConvert<>() to be sure). There are not so many obvious use cases where you would need to look at the (unknown) type of a setting read in, if you don't know the type in advance what are you going to do with whatever you get anyway?

                    mzimmersM Offline
                    mzimmersM Offline
                    mzimmers
                    wrote on last edited by
                    #15

                    @JonB the intention was to use a ListView, using the QSettings keys as a model, to construct a list of settings for the user to view and alter. If I'm understanding @Axel-Spoerl correctly, though, this would be unwieldy at best. Looks like I need to make that mapping, and use those keys as my model. Not sure how I'll handle this in the QML delegate, but that's tomorrow's problem.

                    Axel SpoerlA 1 Reply Last reply
                    0
                    • mzimmersM mzimmers

                      @JonB the intention was to use a ListView, using the QSettings keys as a model, to construct a list of settings for the user to view and alter. If I'm understanding @Axel-Spoerl correctly, though, this would be unwieldy at best. Looks like I need to make that mapping, and use those keys as my model. Not sure how I'll handle this in the QML delegate, but that's tomorrow's problem.

                      Axel SpoerlA Offline
                      Axel SpoerlA Offline
                      Axel Spoerl
                      Moderators
                      wrote on last edited by
                      #16

                      @mzimmers
                      Looks like your users will ultimately modify the preferences of the application they use.
                      I really like the idea of doing that in a list view with a model. Totally cool. The model could actually serve the application to query its preferences.
                      I'd simply add data stream operators or toByteArray() / fromByteArray() helpers to store everything in QSettings ultimately.
                      But i'd not wrap the model around a QSettings object, as you already said.
                      The model could even be more powerful and allow/disallow preference modifications in certain states of the application, or fire signals if a specific preference has been changed - so the application can react.
                      Exposing that stuff to QML shouldn't be difficult. Let us know if help is needed there.
                      And thanks for bringing the topic up - I find it inspiring.

                      Software Engineer
                      The Qt Company, Oslo

                      1 Reply Last reply
                      0
                      • Axel SpoerlA Axel Spoerl

                        As @Christian-Ehrlicher said, QSettings uses QVariant with all its power and limitations under the hood.
                        When you experiment, it's important to know that QSettings buffers its QVariant value sin memory, before actually writing stuff on disk.
                        If you register custom enum types, it will actually remember the exact type until it's written and read back from disk. That might be confusing when you debug.

                        There are different ways to safely infer types to settings, but they require individual implementation.

                        • One option is to encode enum types to a string value and parse them back. I have seen implementations along the lines of @@@ENUM::EnumName::EnumValue.
                        • Another option, as said before, is to map keys to types - at the cost of hard coding.
                        • The option I'd probably choose is to implement union class around your enums. The class is actually a small QVariant for custom enums. It can be constructed with a value of any of the enums needed. A type() getter tells which enum it actually holds. QDataStream operators, or a toByteArray() / fromByteArray() implementation will eventually stream the content into a byte array that can be saved in the settings. You can implement a cast and / or a toEnum<Type>() template function. You probably want to start the data stream or the byte array with some magic numbers, so your union class can tell if it likes a byte array or not. Sounds complicated, but it's actually just a thin wrapper.
                        mzimmersM Offline
                        mzimmersM Offline
                        mzimmers
                        wrote on last edited by
                        #17

                        @Axel-Spoerl said in QSettings: inferring type of stored value:

                        One option is to encode enum types to a string value and parse them back. I have seen implementations along the lines of @@@ENUM::EnumName::EnumValue.

                        This is how I ended up doing it. Rather primitive, but effective.

                        Thanks to all who contributed to this topic.

                        1 Reply Last reply
                        1
                        • mzimmersM mzimmers has marked this topic as solved on

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved