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. Saving the whole application state( ```QTableWidget```, ```QPushButton```, ```QProcess```, objects created with ```new```)
Forum Updated to NodeBB v4.3 + New Features

Saving the whole application state( ```QTableWidget```, ```QPushButton```, ```QProcess```, objects created with ```new```)

Scheduled Pinned Locked Moved Solved General and Desktop
18 Posts 4 Posters 1.1k Views 3 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.
  • H Offline
    H Offline
    hbatalha
    wrote on last edited by hbatalha
    #1

    Hello everyone!!

    I am new to Qt and I just learned how to use QSettings to save basic things like 'confirm_on_exit' from a checkbox.

    And now I want something much more complex.

    In the application I have a QTableWidget which stores a lot rows and in each row I have 2 widgets (QPushButton) and some other texts in the columns. Each of these cellwidgets are created dynamically, they are related to the row they are in and there are a bunch of connect's to perform a lot of things in that row.
    Also, when each row is created there is an instance of a class that is created for it that contains some stated and in that class there is a function that is immediately called that creates a new QProcess.

    It would be unacceptable for the user to close the application and lose all the stuff on the QTableWidget.

    I want to save all of that so when the user restarts the application nothing would be lost.

    I have no idea on how to this as I have read the entire QSettings documention and found nothing that allows me to do that( I am hoping I missed something).

    My actual code is much bigger than this, this is some basic example thta hopefully will illustrate what I want.

    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        ui->tableWidget->setColumnCount(4);
    
        QStringList headers;
        headers << "Header1" << "Header2";
    
        ui->tableWidget->setHorizontalHeaderLabels(headers);
        ui->tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(""));
        ui->tableWidget->setHorizontalHeaderItem(3, new QTableWidgetItem(""));
        ui->tableWidget->setColumnWidth(0, 400);
        ui->tableWidget->setColumnWidth(1, 50);
    
        ui->tableWidget->setShowGrid(false);
    }
    
    void MainWindow::on_pushButton_4_clicked()
    {
        QStringList args;
    
        for(qsizetype i = 0, len = some_number; i < len; ++i)
        {
            args << "some_args"; 
    
            const int dest_row = ui->tableWidget->rowCount();
    
            ui->tableWidget->setRowCount(dest_row + 1);
    
            QPushButton* button1 = new QPushButton();
            pause_button1->setIcon(QIcon(QPixmap(":/Icons/Icons/icon1.png")));
    
            QPushButton* button2= new QPushButton();
            cancel_button->setIcon(QIcon(QPixmap(":/Icons/Icons/icon2.png")));
    
    
            ui->tableWidget->setItem(dest_row, 0, new QTableWidgetItem("Text1");
            ui->tableWidget->setItem(dest_row, 1, new QTableWidgetItem("Text2"));
            ui->tableWidget->setCellWidget(dest_row, 2, button1 );
            ui->tableWidget->setCellWidget(dest_row, 3, button2);
    
           FooClass *foo_class = new FooClass(args);
     
            connect(button1, SIGNAL(clicked(bool)), this, SLOT(someTask(bool))); 
           
            connect(button2, SIGNAL(clicked()), foo_class , SLOT(sometask()));
    
             connect(cancel_button, &QPushButton::clicked, [this, button1, button2, args]()
            
                        // some code
            
                        });
    
            vector_to_hold_FooClass_instances.push_back(foo_class );
    
            QSring arg = args.join(" ");
    
            QMap_args_rows[dest_row] = arg;
    
            foo_class->start();
    
            args.clear();
        }
    }
    

    FooClass

    class FooClass: public QObject
    {
        Q_OBJECT
    
    public:
        FooClass(QStringList argmnts);
    
        void start();
    
        // some states
        bool isPaused();
        bool shouldResume();
        bool isCanceled();
    
        QString filename;
        QString args;
    
    signals:
         // some signals
    
    public slots:
         // some slots
    };
    
    
    FooClass::FooClass(QStringList argmnts) : args(argmnts)
    {
        paused = false;
        resume = false;
        canceled = false;
    }
    
    void FooClass::start()
    {
        paused = false;
        resume = false;
        canceled = false;
    
        process = new QProcess;
    
        connect(process, SIGNAL(readyRead()), SLOT(someTak()));
    
        process->setProcessChannelMode(QProcess::MergedChannels);
    
        connect(process, &QProcess::stateChanged, [this](QProcess::ProcessState newState)
        {
            if(newState == QProcess::NotRunning)
            {
                process->deleteLater();
                process = nullptr;
    
                emit processFinished();
            }
        });
    
        process->start("program.exe", args);
    }
    
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by SGaist
      #2

      Hi,

      Yes there is, check beginGroup and beginWriteArray.

      You can then neatly store each rows data/state.

      You might also want to consider having a dedicated function which creates a "state" of your widget such that it can be reloaded a bit like QMainWindow.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      H 1 Reply Last reply
      1
      • SGaistS SGaist

        Hi,

        Yes there is, check beginGroup and beginWriteArray.

        You can then neatly store each rows data/state.

        You might also want to consider having a dedicated function which creates a "state" of your widget such that it can be reloaded a bit like QMainWindow.

        H Offline
        H Offline
        hbatalha
        wrote on last edited by hbatalha
        #3

        @SGaist said

        Yes there is, check beginGroup and beingArray.
        …
        You can then neatly store each rows data/state.

        I have checked them out but didn't understand how I would do that.
        Can you provide me with some basic usage code just to give a head start and an idea?

        JonBJ 1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          My bad it was beginWriteArray.

          You have examples in the documentation of both methods.

          Did you check them ?

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          H 1 Reply Last reply
          0
          • H hbatalha

            @SGaist said

            Yes there is, check beginGroup and beingArray.
            …
            You can then neatly store each rows data/state.

            I have checked them out but didn't understand how I would do that.
            Can you provide me with some basic usage code just to give a head start and an idea?

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

            @hbatalha
            While you can try to write code to "creates a "state" of your widget such that it can be reloaded ", from experience you will probably be better if you can express and keep a model of your current state and save/restore that.

            If using QTableWidget you should be saving its model(), not the widget itself. And be able to recreate its items from that.

            QWidgets are not intended to be saved. In any case, when you say

            there are a bunch of connect's to perform a lot of things in that row

            If you are doing connect()s dynamically you will not want to try to serialize connections. It is preferable to rebuild these at load time from a model.

            H 1 Reply Last reply
            0
            • SGaistS SGaist

              My bad it was beginWriteArray.

              You have examples in the documentation of both methods.

              Did you check them ?

              H Offline
              H Offline
              hbatalha
              wrote on last edited by
              #6

              @SGaist said

              Did you check them ?

              Yes, but couldn't see how I would use that on a QTableWidget in my case.

              kshegunovK 1 Reply Last reply
              0
              • H hbatalha

                @SGaist said

                Did you check them ?

                Yes, but couldn't see how I would use that on a QTableWidget in my case.

                kshegunovK Offline
                kshegunovK Offline
                kshegunov
                Moderators
                wrote on last edited by kshegunov
                #7

                You can't, not out of the box. You can store the window's geometry and such, but storing a (child) widget state makes no sense. Firstly the geometry of the children is controlled by the layouts, so it's not something one usually concerns oneself with. And secondly the data backing the widgets/views may not be available, loaded or even accessible on a second run, so you need to guarantee a consistent state yourself.

                Read and abide by the Qt Code of Conduct

                H 1 Reply Last reply
                2
                • JonBJ JonB

                  @hbatalha
                  While you can try to write code to "creates a "state" of your widget such that it can be reloaded ", from experience you will probably be better if you can express and keep a model of your current state and save/restore that.

                  If using QTableWidget you should be saving its model(), not the widget itself. And be able to recreate its items from that.

                  QWidgets are not intended to be saved. In any case, when you say

                  there are a bunch of connect's to perform a lot of things in that row

                  If you are doing connect()s dynamically you will not want to try to serialize connections. It is preferable to rebuild these at load time from a model.

                  H Offline
                  H Offline
                  hbatalha
                  wrote on last edited by
                  #8

                  @JonB said

                  If using QTableWidget you should be saving its model(), not the widget itself. And be able to recreate its items from that.
                  Something like this?

                   QSettings qsettings(QSettings::IniFormat, QSettings::UserScope, "Company", "App");
                  
                   qsettings.setValue("TableModel", QVariant::fromValue(ui->tableWidget->model()));
                  
                  
                  kshegunovK JonBJ 2 Replies Last reply
                  0
                  • H hbatalha

                    @JonB said

                    If using QTableWidget you should be saving its model(), not the widget itself. And be able to recreate its items from that.
                    Something like this?

                     QSettings qsettings(QSettings::IniFormat, QSettings::UserScope, "Company", "App");
                    
                     qsettings.setValue("TableModel", QVariant::fromValue(ui->tableWidget->model()));
                    
                    
                    kshegunovK Offline
                    kshegunovK Offline
                    kshegunov
                    Moderators
                    wrote on last edited by
                    #9

                    No, no. QObjects are not serializable by themselves. You are not going to find a straightforward and simple solution to what you're looking for. You must write your own code, handling your own use case.

                    Read and abide by the Qt Code of Conduct

                    1 Reply Last reply
                    1
                    • H hbatalha

                      @JonB said

                      If using QTableWidget you should be saving its model(), not the widget itself. And be able to recreate its items from that.
                      Something like this?

                       QSettings qsettings(QSettings::IniFormat, QSettings::UserScope, "Company", "App");
                      
                       qsettings.setValue("TableModel", QVariant::fromValue(ui->tableWidget->model()));
                      
                      
                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by JonB
                      #10

                      @hbatalha
                      Unfortunately I think not :( But note that I have not tried this --- have you? I cannot imagine that any Qt model is serializable, so what does QVariant::fromValue(ui->tableWidget->model()) actually produce?

                      So, unless that works (please tell me if it does!), I meant it is your job to iterate all the rows/columns saving whatever data role values you have stored in it, such that you can reconstruct the model when you deserialize that data. The issue of storing the serialization on a QSettings is a detail. One possibility would be to save the data as JSON in a single setting's key's value.

                      H 1 Reply Last reply
                      0
                      • JonBJ JonB

                        @hbatalha
                        Unfortunately I think not :( But note that I have not tried this --- have you? I cannot imagine that any Qt model is serializable, so what does QVariant::fromValue(ui->tableWidget->model()) actually produce?

                        So, unless that works (please tell me if it does!), I meant it is your job to iterate all the rows/columns saving whatever data role values you have stored in it, such that you can reconstruct the model when you deserialize that data. The issue of storing the serialization on a QSettings is a detail. One possibility would be to save the data as JSON in a single setting's key's value.

                        H Offline
                        H Offline
                        hbatalha
                        wrote on last edited by
                        #11

                        @JonB said

                        So, unless that works (please tell me if it does!)

                        Yes it "works", it does save some values in the ini file, but don't know what they mean nor how I can load from that as I can't convert QVariant to QAbstractItemModel*.

                        JonBJ 1 Reply Last reply
                        0
                        • H hbatalha

                          @JonB said

                          So, unless that works (please tell me if it does!)

                          Yes it "works", it does save some values in the ini file, but don't know what they mean nor how I can load from that as I can't convert QVariant to QAbstractItemModel*.

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

                          @hbatalha

                          Yes it "works", it does save some values in the ini file

                          I really don't think it does (i.e. save the table contents/info)! And nor does @kshegunov. What exactly does that line produce? If it's a few bytes long, it can't possibly be the necessary information to reconstruct :)

                          H 1 Reply Last reply
                          0
                          • kshegunovK kshegunov

                            You can't, not out of the box. You can store the window's geometry and such, but storing a (child) widget state makes no sense. Firstly the geometry of the children is controlled by the layouts, so it's not something one usually concerns oneself with. And secondly the data backing the widgets/views may not be available, loaded or even accessible on a second run, so you need to guarantee a consistent state yourself.

                            H Offline
                            H Offline
                            hbatalha
                            wrote on last edited by hbatalha
                            #13

                            @kshegunov said

                            You can't, not out of the box

                            So any idea on how I can achieve that?

                            JonBJ 1 Reply Last reply
                            0
                            • H hbatalha

                              @kshegunov said

                              You can't, not out of the box

                              So any idea on how I can achieve that?

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

                              @hbatalha
                              The way I said! You have to save what you want from the model with your own code. There isn't a magic wand. Both of us are telling you that! But doing it this way is likely to be a lot easier than actually trying to serialize a model or a widget with all its properties/attributes etc. as an object.

                              H 1 Reply Last reply
                              2
                              • JonBJ JonB

                                @hbatalha

                                Yes it "works", it does save some values in the ini file

                                I really don't think it does (i.e. save the table contents/info)! And nor does @kshegunov. What exactly does that line produce? If it's a few bytes long, it can't possibly be the necessary information to reconstruct :)

                                H Offline
                                H Offline
                                hbatalha
                                wrote on last edited by hbatalha
                                #15

                                @JonB
                                I don't think it does neither. That's why I had 'works' in quotes. What I meant is that it compiles and save some stuff in the file. And then I saw that the application informs that it can't save QAbstractItemModel*

                                This is what it saves to the file App.ini:

                                [General]
                                TableModel=@Variant(\0\0\0\x7f\0\0\0\x14QAbstractItemModel*\0)
                                
                                
                                JonBJ 1 Reply Last reply
                                0
                                • H hbatalha

                                  @JonB
                                  I don't think it does neither. That's why I had 'works' in quotes. What I meant is that it compiles and save some stuff in the file. And then I saw that the application informs that it can't save QAbstractItemModel*

                                  This is what it saves to the file App.ini:

                                  [General]
                                  TableModel=@Variant(\0\0\0\x7f\0\0\0\x14QAbstractItemModel*\0)
                                  
                                  
                                  JonBJ Offline
                                  JonBJ Offline
                                  JonB
                                  wrote on last edited by JonB
                                  #16

                                  @hbatalha
                                  Yes, that is what I/ @kshegunov expected. All it's serializing is the pointer value with its type name! Because that's all it knows how to do. And will never get further, you have to write the code to serialize/save the data in the model, and to restore it. Which I think is easier than trying to serialize/save the widgets/the visible table for restoration.

                                  1 Reply Last reply
                                  0
                                  • JonBJ JonB

                                    @hbatalha
                                    The way I said! You have to save what you want from the model with your own code. There isn't a magic wand. Both of us are telling you that! But doing it this way is likely to be a lot easier than actually trying to serialize a model or a widget with all its properties/attributes etc. as an object.

                                    H Offline
                                    H Offline
                                    hbatalha
                                    wrote on last edited by
                                    #17

                                    @JonB said

                                    The way I said! You have to save what you want from the model with your own code. There isn't a magic wand. Both of us are telling you that! But doing it this way is likely to be a lot easier than actually trying to serialize a model or a widget with all its properties/attributes etc. as an object.

                                    yeah, I already thought about a way to do that, it requires me to change a lot of working code but that seems to be the way to do it. I will get back to you once I finish.

                                    1 Reply Last reply
                                    1
                                    • H Offline
                                      H Offline
                                      hbatalha
                                      wrote on last edited by hbatalha
                                      #18

                                      @JonB, @SGaist , @kshegunov

                                      I am happy to inform that I have achieved the goal stated in the OP.

                                      What I did was create a function that that stores all the instances info in the QSettings and another function that will will create all the rows in the QTableWIdget from the info saved.

                                      Something like this:

                                      
                                      void saveSetting()
                                      {
                                          QSettings qsettings(QSettings::IniFormat, QSettings::UserScope, "Company", "APP");
                                      
                                          qsettings.beginWriteArray("Table");
                                          qsettings.remove("");
                                          qsettings.endArray();
                                      
                                          qsettings.beginWriteArray("Table");
                                          for(qsizetype i = 0, len = vector_to_hold_FooClass_instances.size(); i < len; ++i)
                                          {
                                              qsettings.setArrayIndex(i);
                                              qsettings.setValue("args", vector_to_hold_FooClass_instances.at(i)->getArgs());
                                      
                                               // I replaced the statuses with enum class instead of using ``bool``
                                               // I couldn't find a better t save enum class to QSettings so I used this workaround
                                              qsettings.setValue("status", static_cast<int>(vector_to_hold_FooClass_instances.at(i)->status));
                                          }
                                          qsettings.endArray();
                                      
                                      }
                                      

                                      and on loading:

                                      void MainWindow::loadSettings()
                                      {
                                          QSettings qsettings(QSettings::IniFormat, QSettings::UserScope, "HBatalha", "Table");
                                      
                                         int size = qsettings.beginReadArray("Table");
                                         
                                         for(int i = 0; i < size; ++i)    
                                         {        
                                              QStringList args = qsettings.value("args").toString().split(" ");
                                              
                                              FooClass::Status status = static_cast<FooClass::Status>(qsettings.value("status").toInt());        
                                             const int dest_row = ui->tableWidget->rowCount();
                                      
                                              ui->tableWidget->setRowCount(dest_row + 1);
                                      
                                              QPushButton* button1 = new QPushButton();
                                              pause_button1->setIcon(QIcon(QPixmap(":/Icons/Icons/icon1.png")));
                                      
                                              QPushButton* button2= new QPushButton();
                                              cancel_button->setIcon(QIcon(QPixmap(":/Icons/Icons/icon2.png")));
                                      
                                      
                                              ui->tableWidget->setItem(dest_row, 0, new QTableWidgetItem("Text1");
                                              ui->tableWidget->setItem(dest_row, 1, new QTableWidgetItem("Text2"));
                                              ui->tableWidget->setCellWidget(dest_row, 2, button1 );
                                              ui->tableWidget->setCellWidget(dest_row, 3, button2);
                                      
                                             FooClass *foo_class = new FooClass(args);
                                              foo_class->status = status;
                                       
                                              connect(button1, SIGNAL(clicked(bool)), this, SLOT(someTask(bool))); 
                                             
                                              connect(button2, SIGNAL(clicked()), foo_class , SLOT(sometask()));
                                      
                                               connect(cancel_button, &QPushButton::clicked, [this, button1, button2, args]()
                                              
                                                          // some code
                                              
                                                          });
                                      
                                              vector_to_hold_FooClass_instances.push_back(foo_class );
                                      
                                              QSring arg = args.join(" ");
                                      
                                              QMap_args_rows[dest_row] = arg;
                                      
                                              foo_class->start();
                                      
                                              args.clear();
                                          }
                                          qsettings.endArray();
                                      }
                                      
                                      

                                      What do you guys think?

                                      1 Reply Last reply
                                      0

                                      • Login

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