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
Forum Updated to NodeBB v4.3 + New Features

QSettings: inferring type of stored value

Scheduled Pinned Locked Moved Solved General and Desktop
17 Posts 4 Posters 1.6k 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.
  • mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on last edited by mzimmers
    #1

    Hi all - I recently began experimenting with QSettings. I'd like to write a QML Component that lists the settings and allows the user to change their values. Ideally this would be done with a ListView using the keys as the model.

    I have this working with a single data type (QString) for the value. I'd like to add bools and a few enums. Is there some way to determine the type of a stored QSettings value, or do I need to maintain an external list for this? I can't find anything in the QSettings documentation about this.

    Thanks...

    Christian EhrlicherC Axel SpoerlA 2 Replies Last reply
    0
    • Axel SpoerlA Offline
      Axel SpoerlA Offline
      Axel Spoerl
      Moderators
      wrote on last edited by Axel Spoerl
      #5

      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.

      Software Engineer
      The Qt Company, Oslo

      mzimmersM 2 Replies Last reply
      2
      • mzimmersM mzimmers

        Hi all - I recently began experimenting with QSettings. I'd like to write a QML Component that lists the settings and allows the user to change their values. Ideally this would be done with a ListView using the keys as the model.

        I have this working with a single data type (QString) for the value. I'd like to add bools and a few enums. Is there some way to determine the type of a stored QSettings value, or do I need to maintain an external list for this? I can't find anything in the QSettings documentation about this.

        Thanks...

        Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #2

        @mzimmers said in QSettings: inferring type of stored value:

        can't find anything in the QSettings documentation about this.

        Why should you - QSettings works with QVariant so you should look there.

        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
        1
        • mzimmersM mzimmers

          Hi all - I recently began experimenting with QSettings. I'd like to write a QML Component that lists the settings and allows the user to change their values. Ideally this would be done with a ListView using the keys as the model.

          I have this working with a single data type (QString) for the value. I'd like to add bools and a few enums. Is there some way to determine the type of a stored QSettings value, or do I need to maintain an external list for this? I can't find anything in the QSettings documentation about this.

          Thanks...

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

          @mzimmers said in QSettings: inferring type of stored value:

          and a few enums

          The issue with enumerations is, that they are stored as int values in the settings file.
          When they are read back from the settings file, they are interpreted as integers.
          You can cast them to the enumeration, if their type is known, e.g. by the key.

          Software Engineer
          The Qt Company, Oslo

          mzimmersM 1 Reply Last reply
          0
          • Axel SpoerlA Axel Spoerl

            @mzimmers said in QSettings: inferring type of stored value:

            and a few enums

            The issue with enumerations is, that they are stored as int values in the settings file.
            When they are read back from the settings file, they are interpreted as integers.
            You can cast them to the enumeration, if their type is known, e.g. by the key.

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

            @Axel-Spoerl understood. So, I take it that there really is no way to infer a setting's type from the QSettings object?

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

              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.

              Software Engineer
              The Qt Company, Oslo

              mzimmersM 2 Replies Last reply
              2
              • 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
                #6

                @Axel-Spoerl thanks for the detailed reply. I'd like to back up for a moment, as I seem to be missing something here.

                I've implemented a wrapper class around QSettings. (Normally I'd post code in code blocks, but in this case I think an image will be more informative.) Notice that despite my attempting to store a bool setting, when I read it back, it comes back as a QString.
                Screenshot 2024-08-25 095020.png
                Am I doing something wrong, or do I simply need to follow your second option in order to derive the type?

                Thanks...

                1 Reply Last reply
                0
                • 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 Online
                            JonBJ Online
                            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