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. use of model/view programming
Forum Updated to NodeBB v4.3 + New Features

use of model/view programming

Scheduled Pinned Locked Moved Unsolved General and Desktop
27 Posts 6 Posters 4.9k 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.
  • mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on last edited by
    #1

    Hi all -

    I started to discuss this in another thread, but it got hijacked (by me), so I thought I'd start fresh.

    I'm writing an app using model/view for the first time. I think I get the concepts (at least most of them), but there are a couple of implementation details I'm not sure about.

    My program has:

    • a worker object, which contains a Devices object that is derived from QAbstractTableModel.
    • a widget object, which contains a QTableView.

    I understand that when I construct the widget, I need to do this:

        ui->tableView->setModel(???);
        tableView.show()
    

    But how do I point to the table model that is part of the worker object?

    I could have the worker object return the address of the table model to main(), who could then pass it to the widget c'tor. But this seems cumbersome and prone to failure. Can someone advise on a preferred method for accomplishing this?

    Thanks.

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

      Hi,

      As suggested in your other thread, your Worker class should rather be a member of your custom model. That way there's no need for your GUI to know anything about your worker.

      Unless you keep your worker and model separated and then you add specific APIs to your model to communicate with your Worker object.

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

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

        Hi SGaist -

        Yes, I do remember you saying this, but...I don't see how this helps. As long as the view (ie, the widget) is logically separate from the model (the worker and its table), I still need some way of conveying the model information to the widget, don't I?

        Or, are you suggesting that my custom model contain my worker and my widget?

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

          That's the idea: you instantiate the model like any other Qt model and then set it on the widget(s) that will use it.

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

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

            What does it mean to "set it on" the widget(s) that will use it?

            JonBJ 1 Reply Last reply
            0
            • mzimmersM mzimmers

              What does it mean to "set it on" the widget(s) that will use it?

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

              @mzimmers
              Use the view-widget's method to "attach" it to the model. For example, a QTableView has setModel().

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

                How do you make the view aware of the model, though? Is it something straightforward such as an additional argument on the c'tor?

                I get that you use setModel(), but where do you get the argument to put into the setModel() call?

                JonBJ 1 Reply Last reply
                0
                • mzimmersM mzimmers

                  How do you make the view aware of the model, though? Is it something straightforward such as an additional argument on the c'tor?

                  I get that you use setModel(), but where do you get the argument to put into the setModel() call?

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

                  @mzimmers
                  It's your model. That's all. Like you wrote:

                  a Devices object that is derived from QAbstractTableModel.

                  http://doc.qt.io/qt-5/qabstractitemview.html#setModel

                  You instantiate some QAbstractItemModel. That's all there is to it.

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

                    I can tell that this is another one of those times where I'm being extra-dense. Let me try to pose my question like this -- in the tutorial, there's this example:

                    int main(int argc, char *argv[])
                    {
                        QApplication a(argc, argv);
                        QTableView tableView;
                        MyModel myModel(0);
                        tableView.setModel( &myModel );
                        tableView.show();
                        return a.exec();
                    }
                    

                    This is straightforward enough, because tableView is a top-level object. In my case, however, it's a part of ui, a (private) member of my widget object. I can't access it from main().

                    class Devices : public QAbstractTableModel
                    {
                    private:
                        ModelData devices;
                    ...
                    }
                    class Worker : public QObject
                    {
                        Q_OBJECT
                    private:
                        Devices devices;
                    ...
                    }
                    int main(int argc, char *argv[])
                    {
                        int rc;
                        QApplication a(argc, argv);
                        Widget widget;
                    
                        QThread* thread = new QThread;
                        Worker* worker = new Worker();
                    ...
                    }
                    

                    So...am I doing it wrong? Or, should I just obtain the address to devices from the worker object, and pass it to the widget object at construction? Or, something else?

                    JonBJ 1 Reply Last reply
                    0
                    • mzimmersM mzimmers

                      I can tell that this is another one of those times where I'm being extra-dense. Let me try to pose my question like this -- in the tutorial, there's this example:

                      int main(int argc, char *argv[])
                      {
                          QApplication a(argc, argv);
                          QTableView tableView;
                          MyModel myModel(0);
                          tableView.setModel( &myModel );
                          tableView.show();
                          return a.exec();
                      }
                      

                      This is straightforward enough, because tableView is a top-level object. In my case, however, it's a part of ui, a (private) member of my widget object. I can't access it from main().

                      class Devices : public QAbstractTableModel
                      {
                      private:
                          ModelData devices;
                      ...
                      }
                      class Worker : public QObject
                      {
                          Q_OBJECT
                      private:
                          Devices devices;
                      ...
                      }
                      int main(int argc, char *argv[])
                      {
                          int rc;
                          QApplication a(argc, argv);
                          Widget widget;
                      
                          QThread* thread = new QThread;
                          Worker* worker = new Worker();
                      ...
                      }
                      

                      So...am I doing it wrong? Or, should I just obtain the address to devices from the worker object, and pass it to the widget object at construction? Or, something else?

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

                      @mzimmers
                      Now I'm really jumping in here, because I don't know what's going on. At the risk of sticking my neck out....

                      Since @SGaist wrote:

                      As suggested in your other thread, your Worker class should rather be a member of your custom model. That way there's no need for your GUI to know anything about your worker.

                      So your Worker would be a member of your Devices. In your main, you don't directly create a Worker, you create a Devices (which creates its won Worker), and you create your view-widget. Then you set the view's model to your Devices instance.

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

                        Hi Jon -

                        No worries about jumping in; I'm quite new to many of these concepts and appreciate all feedback.

                        I'm still confused about the design that SGaist has proposed. In my (admittedly limited) Qt experience, I'm accustomed to the main() routine creating the worker and the widget objects, and using the signal/slot mechanism to connect them.

                        I understand that SGaist's proposal entails main creating objects from Devices and Widget, and Devices would contain Worker. (I don't yet understand the rationale for this, but this hopefully will become clear as I go along.) What I don't understand, is how this solves the issue of conveying the model from Devices to Widget. Specifically, in Widget::Widget(), I know I should have a line like:

                            ui->tableView->setModel(???);
                        

                        My question is WHAT should replace the "???" above? How do I tell my widget what/where the model is?

                        This seems like a really simple question, so I'm probably not asking it well. Hopefully someone will get what I'm trying to say here.

                        Gojir4G 1 Reply Last reply
                        0
                        • mzimmersM mzimmers

                          Hi Jon -

                          No worries about jumping in; I'm quite new to many of these concepts and appreciate all feedback.

                          I'm still confused about the design that SGaist has proposed. In my (admittedly limited) Qt experience, I'm accustomed to the main() routine creating the worker and the widget objects, and using the signal/slot mechanism to connect them.

                          I understand that SGaist's proposal entails main creating objects from Devices and Widget, and Devices would contain Worker. (I don't yet understand the rationale for this, but this hopefully will become clear as I go along.) What I don't understand, is how this solves the issue of conveying the model from Devices to Widget. Specifically, in Widget::Widget(), I know I should have a line like:

                              ui->tableView->setModel(???);
                          

                          My question is WHAT should replace the "???" above? How do I tell my widget what/where the model is?

                          This seems like a really simple question, so I'm probably not asking it well. Hopefully someone will get what I'm trying to say here.

                          Gojir4G Offline
                          Gojir4G Offline
                          Gojir4
                          wrote on last edited by
                          #12

                          @mzimmers said in use of model/view programming:

                          ui->tableView->setModel(???);
                          

                          My question is WHAT should replace the "???" above? How do I tell my widget what/where the model is?

                          Hello mzimmers ,

                          Actually your first question almost answers the second one. I mean, you literally tell to your "Item view", or in other words, any widget inheriting from QAbstractItemView, like QTableView, that the model is the one given with setModel().

                          ui->tableView.setModel( &myModel );
                          

                          From this point, the table view will access data from the model, by calling QAbstractItemModel::rowCount(), QAbstractItemModel::columnCount() and QAbstractItemModel::data(), which you must override when inheriting from QAbstractTableModel. These methods are called in standard C++ way, not using signal/slot mechanism, because the view needs the value at the instant is asks for it.

                          You can also "connect" (using setModel(), not in a signal/slot way) multiple views to a single model. So the view(s) and the model must be in the same thread, otherwise, you may have, and you will certainly have, memory access errors.

                          This doesn't prevent usage of a worker, inside the model, to handle heavy tasks, like populating or sorting your model and sending results using signals, but to provide the data to the view, you must be in the same thread.

                          The only way I can see to achieve this design, is to make your own table view and you own model, by reimplementing QAbstractItemView and QAbstractItemModel, with your own mechanism supporting a model in a different thread.

                          mzimmersM 1 Reply Last reply
                          2
                          • Gojir4G Gojir4

                            @mzimmers said in use of model/view programming:

                            ui->tableView->setModel(???);
                            

                            My question is WHAT should replace the "???" above? How do I tell my widget what/where the model is?

                            Hello mzimmers ,

                            Actually your first question almost answers the second one. I mean, you literally tell to your "Item view", or in other words, any widget inheriting from QAbstractItemView, like QTableView, that the model is the one given with setModel().

                            ui->tableView.setModel( &myModel );
                            

                            From this point, the table view will access data from the model, by calling QAbstractItemModel::rowCount(), QAbstractItemModel::columnCount() and QAbstractItemModel::data(), which you must override when inheriting from QAbstractTableModel. These methods are called in standard C++ way, not using signal/slot mechanism, because the view needs the value at the instant is asks for it.

                            You can also "connect" (using setModel(), not in a signal/slot way) multiple views to a single model. So the view(s) and the model must be in the same thread, otherwise, you may have, and you will certainly have, memory access errors.

                            This doesn't prevent usage of a worker, inside the model, to handle heavy tasks, like populating or sorting your model and sending results using signals, but to provide the data to the view, you must be in the same thread.

                            The only way I can see to achieve this design, is to make your own table view and you own model, by reimplementing QAbstractItemView and QAbstractItemModel, with your own mechanism supporting a model in a different thread.

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

                            @Gojir4 said in use of model/view programming:

                            Actually your first question almost answers the second one. I mean, you literally tell to your "Item view", or in other words, any widget inheriting from QAbstractItemView, like QTableView, that the model is the one given with setModel().
                            ui->tableView.setModel( &myModel );

                            OK, let me ask the question this way: this would be my Widget c'tor:

                            Widget::Widget(QWidget *parent) :
                                QWidget(parent),
                                ui(new Ui::Widget)
                            {
                                ui->tableView->setModel(&myModel)
                                ui->setupUi(this);
                            }
                            

                            This of course won't compile, because myModel is undefined within the Widget class. Where do I get it from?

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

                              Make it a member of your class.

                              Or add a method to Widget that takes a model in parameter and sets it on the view.

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

                              1 Reply Last reply
                              4
                              • mzimmersM Offline
                                mzimmersM Offline
                                mzimmers
                                wrote on last edited by
                                #15

                                OK, now we're getting somewhere. Can I simply pass in a pointer to it in the c'tor, then?

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

                                  That's another possibility yes.

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

                                  1 Reply Last reply
                                  2
                                  • mzimmersM mzimmers

                                    OK, now we're getting somewhere. Can I simply pass in a pointer to it in the c'tor, then?

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

                                    @mzimmers
                                    Yes, if you'd like to do it there, as per @SGaist 's example.

                                    Is your confusion that you don't know that you can, for example, make your example Widget constructor take extra parameters beyond what the base QWidget() takes? You can pass whatever additional stuff you like to your constructor if you wish to write it like that.

                                    1 Reply Last reply
                                    2
                                    • mzimmersM Offline
                                      mzimmersM Offline
                                      mzimmers
                                      wrote on last edited by mzimmers
                                      #18

                                      Thanks, guys. No, my confusion wasn't using the c'tor, it was whether manually passing a pointer to the model, into the display widget, was the right way to do this. I'm going to go implement this, and will return with my next confusion in a bit. Thanks again...

                                      UPDATE:

                                      Believe it or not, I have it working. (Still haven't implemented SGaist's design suggestion but I will.) So, I think the next thing to do is to implement my override of insertRows()/beginInsertRows().

                                      So, is the correct sequence in insertRows() to:

                                      1. call beginInsertRows() and add a row
                                      2. add the data to be inserted to my private copy of the data
                                      3. call endInsertRows()

                                      In other words, will the insertRows() function I write will modify my copy of the data (the model)?

                                      Also, beginInsertRows() has an argument const QModelIndex &parent. What is this, and where do I get it from?

                                      Thanks...

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

                                        I've used the address book example as a guide, and it's sort of working. I can successfully add a row, and populate its contents. But when I try to update a row (actually just one column in the row), the update doesn't show in the widget.

                                        Here's my update code...am I forgetting a window refresh or something? I don't see anything like that in the address book example.

                                        Thanks...

                                            // update the row. Easiest to just do all fields.
                                            deviceTable[row.Srow] = device;
                                            QModelIndex id = index(row.Srow, 0, QModelIndex());
                                            setData(id, device.macAddr, Qt::EditRole);
                                            id = index(row.Srow, 1, QModelIndex());
                                            setData(id, device.devName, Qt::EditRole);
                                            id = index(row.Srow, 2, QModelIndex());
                                            setData(id, device.latestHB, Qt::EditRole);
                                        
                                            //emit a signal to make the view re-read identified data.
                                            QModelIndex topLeft = createIndex(0, 0);
                                            row.Urow = deviceTable.size();
                                            QModelIndex bottomRight = createIndex(row.Srow, NBR_COLS_IN_TABLE);
                                            emit dataChanged(topLeft, bottomRight);
                                        

                                        Edit: it was pointed out that my row and column values were off by 1 each; I've corrected that (not reflected in the code above), and the behavior is unchanged.

                                        The field should update about once a second, but it updates whenever it loses or gains focus. Any suggestions are appreciated.

                                        kshegunovK 1 Reply Last reply
                                        0
                                        • mzimmersM mzimmers

                                          I've used the address book example as a guide, and it's sort of working. I can successfully add a row, and populate its contents. But when I try to update a row (actually just one column in the row), the update doesn't show in the widget.

                                          Here's my update code...am I forgetting a window refresh or something? I don't see anything like that in the address book example.

                                          Thanks...

                                              // update the row. Easiest to just do all fields.
                                              deviceTable[row.Srow] = device;
                                              QModelIndex id = index(row.Srow, 0, QModelIndex());
                                              setData(id, device.macAddr, Qt::EditRole);
                                              id = index(row.Srow, 1, QModelIndex());
                                              setData(id, device.devName, Qt::EditRole);
                                              id = index(row.Srow, 2, QModelIndex());
                                              setData(id, device.latestHB, Qt::EditRole);
                                          
                                              //emit a signal to make the view re-read identified data.
                                              QModelIndex topLeft = createIndex(0, 0);
                                              row.Urow = deviceTable.size();
                                              QModelIndex bottomRight = createIndex(row.Srow, NBR_COLS_IN_TABLE);
                                              emit dataChanged(topLeft, bottomRight);
                                          

                                          Edit: it was pointed out that my row and column values were off by 1 each; I've corrected that (not reflected in the code above), and the behavior is unchanged.

                                          The field should update about once a second, but it updates whenever it loses or gains focus. Any suggestions are appreciated.

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

                                          If I recall correctly, dataChanged should be enough to trigger the view to update. @VRonin can you work your magic here, what are we missing?

                                          Read and abide by the Qt Code of Conduct

                                          1 Reply Last reply
                                          1

                                          • Login

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