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. What's the correct way to setup the following project with a Model/View/Delegate structure?
Forum Updated to NodeBB v4.3 + New Features

What's the correct way to setup the following project with a Model/View/Delegate structure?

Scheduled Pinned Locked Moved Unsolved General and Desktop
7 Posts 3 Posters 379 Views 1 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.
  • D Offline
    D Offline
    Deckhead
    wrote on last edited by
    #1

    I'm new to Qt. I'm using QtCreator and have managed to get quite far in my project, but it's becoming unwieldy. I suspect the answer is to use Model/View/Delegates.

    I've read through all the documentation and had a couple of tries, but I don't think I'm breaking things up in a sensible way. I'm hoping someone can point me the right direction.

    My data structure looks like this:

    struct MyData {
        int dataA;
        std::string dataB;
        struct DataCStruct {
            int data1;
            int data2;
        } dataC;
        std::map<int,DataCStruct> dataD;
    };
    

    My real data is bigger, but I'm just trying to show the complexity.

    I'd like my UI to be broken up into tab-pages. Page one will just be about editing dataA. Page two might be editing dataC. Page three would be adding rows of data, deleting, etc from dataD.

    For the data model, my thought was that a 'row' would refer to either dataA, dataB, dataC, dataD. And the 'column' would only be important for dataD. Is this correct? The way I thought to implement it was:

    enum STAT { DATAA = 0, DATAB = 1, DATAC = 2, DATAD = 3 };
    

    and then in the ::data method of the model, I would do something like

    QVariant MyModel::data(const QModelIndex& index, int role) const
    {
        if(!index.isValid()) {
            return QVariant();
        }
    
        if(index.parent.isValid()) {
            // this must be an element of dataD, because none of the others would have a valid parent
            if(index.row() == 0) {
                return m_referenceToUnderlyingStruct.dataD[parent.column()].data1;
            } else if(index.row() == 1) {
                return m_referenceToUnderlyingStruct.dataD[parent.column()].data2;
        }
        else {
            // the row must indicate which "stat" this is
            switch(static_cast<STAT>(index.row()))
            {
            case STAT::DATAA:
                return m_referenceToUnderlyingStruct.dataA;
            case STAT::DATAB:
                return QString::fromStdString(m_referenceToUnderlyingStruct.dataB);
            case STAT::DATAC:
                // in this case, the column indicates which ITEM
                if(index.column() == 0) {
                    return m_referenceToUnderlyingStruct.dataC.data1;
                } else if(index.column() == 1) {
                    return m_referenceToUnderlyingStruct.dataC.data2;
                }
            }
        }
        return QVariant();
    }
    

    but you can see this is already starting to get a little confusing and unmanageable. Whilst I think I see how row, column, and parent can reference elements of any data structure, the implementation of this looks bad to me. In my real structure, I need to use an std::unordered_map, which I'm worried might break things considering there's no guarantee on the order of elements after they've moved.

    If the above is correct, how do I even begin to construct views?

    So, like I said, I want several pages across tabs to edit this single data structure. Each of these tabs might have multiple edit boxes, spin controls, tables or lists. Some of these would be for editing the structure, others would be to apply some kind of logic to determine values to write into this structure.

    Initially, each little section of controls--for example the controls to edit dataA--are on their own form; that's how I did this without a model. So if I want to now use a model, do I create a single view for these controls? I'm confused because how can the view know which elements to request from the model, is this something I determine in my custom view?

    And if I create a new View, how do I visualise this in the QtDesigner?

    From the examples I've seen, the views create Delegates to actually edit the information. But if my view is going to show both dataA and dataB, and allow the user to edit both, how does this work?

    I could really use a better example of a project than the simple examples in the documentation, because I can't see how to achieve my use case. If anyone can point me to examples, head me in the right direction, or even just list the classes and steps I'd need to do for this small example, it would be greatly appreciated.

    JonBJ Pl45m4P 2 Replies Last reply
    0
    • D Deckhead

      I'm new to Qt. I'm using QtCreator and have managed to get quite far in my project, but it's becoming unwieldy. I suspect the answer is to use Model/View/Delegates.

      I've read through all the documentation and had a couple of tries, but I don't think I'm breaking things up in a sensible way. I'm hoping someone can point me the right direction.

      My data structure looks like this:

      struct MyData {
          int dataA;
          std::string dataB;
          struct DataCStruct {
              int data1;
              int data2;
          } dataC;
          std::map<int,DataCStruct> dataD;
      };
      

      My real data is bigger, but I'm just trying to show the complexity.

      I'd like my UI to be broken up into tab-pages. Page one will just be about editing dataA. Page two might be editing dataC. Page three would be adding rows of data, deleting, etc from dataD.

      For the data model, my thought was that a 'row' would refer to either dataA, dataB, dataC, dataD. And the 'column' would only be important for dataD. Is this correct? The way I thought to implement it was:

      enum STAT { DATAA = 0, DATAB = 1, DATAC = 2, DATAD = 3 };
      

      and then in the ::data method of the model, I would do something like

      QVariant MyModel::data(const QModelIndex& index, int role) const
      {
          if(!index.isValid()) {
              return QVariant();
          }
      
          if(index.parent.isValid()) {
              // this must be an element of dataD, because none of the others would have a valid parent
              if(index.row() == 0) {
                  return m_referenceToUnderlyingStruct.dataD[parent.column()].data1;
              } else if(index.row() == 1) {
                  return m_referenceToUnderlyingStruct.dataD[parent.column()].data2;
          }
          else {
              // the row must indicate which "stat" this is
              switch(static_cast<STAT>(index.row()))
              {
              case STAT::DATAA:
                  return m_referenceToUnderlyingStruct.dataA;
              case STAT::DATAB:
                  return QString::fromStdString(m_referenceToUnderlyingStruct.dataB);
              case STAT::DATAC:
                  // in this case, the column indicates which ITEM
                  if(index.column() == 0) {
                      return m_referenceToUnderlyingStruct.dataC.data1;
                  } else if(index.column() == 1) {
                      return m_referenceToUnderlyingStruct.dataC.data2;
                  }
              }
          }
          return QVariant();
      }
      

      but you can see this is already starting to get a little confusing and unmanageable. Whilst I think I see how row, column, and parent can reference elements of any data structure, the implementation of this looks bad to me. In my real structure, I need to use an std::unordered_map, which I'm worried might break things considering there's no guarantee on the order of elements after they've moved.

      If the above is correct, how do I even begin to construct views?

      So, like I said, I want several pages across tabs to edit this single data structure. Each of these tabs might have multiple edit boxes, spin controls, tables or lists. Some of these would be for editing the structure, others would be to apply some kind of logic to determine values to write into this structure.

      Initially, each little section of controls--for example the controls to edit dataA--are on their own form; that's how I did this without a model. So if I want to now use a model, do I create a single view for these controls? I'm confused because how can the view know which elements to request from the model, is this something I determine in my custom view?

      And if I create a new View, how do I visualise this in the QtDesigner?

      From the examples I've seen, the views create Delegates to actually edit the information. But if my view is going to show both dataA and dataB, and allow the user to edit both, how does this work?

      I could really use a better example of a project than the simple examples in the documentation, because I can't see how to achieve my use case. If anyone can point me to examples, head me in the right direction, or even just list the classes and steps I'd need to do for this small example, it would be greatly appreciated.

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

      @Deckhead

      I'd like my UI to be broken up into tab-pages. Page one will just be about editing dataA. Page two might be editing dataC. Page three would be adding rows of data, deleting, etc from dataD.

      Let's be clear: do you mean there is just a single instance of MyData to be edited in the whole program, or do you mean that there is a whole table of many rows, each of which is its own MyData instance?

      There is something odd/inconsistent about a UI where the first pages are for editing data inside a single instance and the subsequent pages are about inserting/deleting multiple instances. For example, when you add a new instance at page 3 you are not able to edit its details before/while adding as that is in pages 1/2.

      For the data model, my thought was that a 'row' would refer to either dataA, dataB, dataC, dataD. And the 'column' would only be important for dataD. Is this correct?

      While technically this can be done in Qt because its data() method works with QVariants for each row+column cell value and that would allow each row+column cell to store/return a quite different value type (the first as int, the second as std::string, etc.), that is most unusual for a model, which is expected to have the type in the same column in each row. I don't think you will get any actual advantage in bothering to treat the members of your struct as separate rows. They would normally be columns.

      Don't necessarily get too hooked on "how do I visualise this in the QtDesigner". Sometimes you are better off/have to write code for which there is no Designer UI or just a "placeholder".

      If you need to add/delete rows, each of which is MyData instance, as well as edit rows, the standard/simplest/traditional interface is "master-detail". Your model has rows for each one and columns corresponding to each member. Then you can use a QTableView (on the first page) to show existing rows. There the user can press buttons to add a new row or delete an existing one. Or they can select an existing row and press a button to edit, at which point they get the widgets to edit that row on the master-detail page or moved onto other pages if you prefer. For that you might like to take a look at Qt's QDataWidgetMapper, which produces and manages editing widgets for each column in a given row.

      You may need to go more advanced for a member like your std::map<int,DataCStruct> dataD; since this itself has many rows.

      Or you may just want your own bunch of widgets over your own multiple pages and handle your own storage to/from a MyData instance.

      D 1 Reply Last reply
      2
      • JonBJ JonB

        @Deckhead

        I'd like my UI to be broken up into tab-pages. Page one will just be about editing dataA. Page two might be editing dataC. Page three would be adding rows of data, deleting, etc from dataD.

        Let's be clear: do you mean there is just a single instance of MyData to be edited in the whole program, or do you mean that there is a whole table of many rows, each of which is its own MyData instance?

        There is something odd/inconsistent about a UI where the first pages are for editing data inside a single instance and the subsequent pages are about inserting/deleting multiple instances. For example, when you add a new instance at page 3 you are not able to edit its details before/while adding as that is in pages 1/2.

        For the data model, my thought was that a 'row' would refer to either dataA, dataB, dataC, dataD. And the 'column' would only be important for dataD. Is this correct?

        While technically this can be done in Qt because its data() method works with QVariants for each row+column cell value and that would allow each row+column cell to store/return a quite different value type (the first as int, the second as std::string, etc.), that is most unusual for a model, which is expected to have the type in the same column in each row. I don't think you will get any actual advantage in bothering to treat the members of your struct as separate rows. They would normally be columns.

        Don't necessarily get too hooked on "how do I visualise this in the QtDesigner". Sometimes you are better off/have to write code for which there is no Designer UI or just a "placeholder".

        If you need to add/delete rows, each of which is MyData instance, as well as edit rows, the standard/simplest/traditional interface is "master-detail". Your model has rows for each one and columns corresponding to each member. Then you can use a QTableView (on the first page) to show existing rows. There the user can press buttons to add a new row or delete an existing one. Or they can select an existing row and press a button to edit, at which point they get the widgets to edit that row on the master-detail page or moved onto other pages if you prefer. For that you might like to take a look at Qt's QDataWidgetMapper, which produces and manages editing widgets for each column in a given row.

        You may need to go more advanced for a member like your std::map<int,DataCStruct> dataD; since this itself has many rows.

        Or you may just want your own bunch of widgets over your own multiple pages and handle your own storage to/from a MyData instance.

        D Offline
        D Offline
        Deckhead
        wrote on last edited by
        #3

        @JonB said in What's the correct way to setup the following project with a Model/View/Delegate structure?:

        @Deckhead

        I'd like my UI to be broken up into tab-pages. Page one will just be about editing dataA. Page two might be editing dataC. Page three would be adding rows of data, deleting, etc from dataD.

        Let's be clear: do you mean there is just a single instance of MyData to be edited in the whole program, or do you mean that there is a whole table of many rows, each of which is its own MyData instance?

        What I mean is there's one instance of MyData. But it's big and makes sense to split it's view and editing across tabs. So like tab 1 might be "basic details", tab 2 might be "complex information".

        To complicate things, what you put in Tab 1 might change what is available as options or validation etc in Tab 2.

        Or you may just want your own bunch of widgets over your own multiple pages and handle your own storage to/from a MyData instance.

        This is what I did originally. The problem I had was that if one widget on Tab 1 edits a value in the structure, I needed to send a signal that this happened. Multiple other forms then need to be connected to this signal etc and fail validation, potentially changing a value they control. This in turn generates a new signal. Etc etc

        To me, that seemed like I was doing things wrong. It was getting too complicated and was going to become error prone. From what I understand, the MVC parts of Qt do all that behind the scenes, so I thought it was a no brainer to use it.

        However, I did recently read that it may be better to create a Qt Model for each single attribute of the structure. Each of these small models can be easily given to the existing views. When the underlying structure receives a call from a model that an attribute is to be updated, it can call every model to inform it of the update (this will maybe be a sort of "master model" on top of the actual underlying data structure).

        The only complexity there is where there's variable containers of sub-structures, as I'd need to spin up views dynamically. But I think this should be easy.

        But then I sort of think I could just hand-roll all this anyway and get the same result. It seems to me that Qt's MVC paradigm doesn't really work for much beyond the simple examples given, and due to the complexity in complicated data models, is the same effort to use as just doing something yourself.

        But happy to be corrected!

        JonBJ 1 Reply Last reply
        0
        • D Deckhead

          @JonB said in What's the correct way to setup the following project with a Model/View/Delegate structure?:

          @Deckhead

          I'd like my UI to be broken up into tab-pages. Page one will just be about editing dataA. Page two might be editing dataC. Page three would be adding rows of data, deleting, etc from dataD.

          Let's be clear: do you mean there is just a single instance of MyData to be edited in the whole program, or do you mean that there is a whole table of many rows, each of which is its own MyData instance?

          What I mean is there's one instance of MyData. But it's big and makes sense to split it's view and editing across tabs. So like tab 1 might be "basic details", tab 2 might be "complex information".

          To complicate things, what you put in Tab 1 might change what is available as options or validation etc in Tab 2.

          Or you may just want your own bunch of widgets over your own multiple pages and handle your own storage to/from a MyData instance.

          This is what I did originally. The problem I had was that if one widget on Tab 1 edits a value in the structure, I needed to send a signal that this happened. Multiple other forms then need to be connected to this signal etc and fail validation, potentially changing a value they control. This in turn generates a new signal. Etc etc

          To me, that seemed like I was doing things wrong. It was getting too complicated and was going to become error prone. From what I understand, the MVC parts of Qt do all that behind the scenes, so I thought it was a no brainer to use it.

          However, I did recently read that it may be better to create a Qt Model for each single attribute of the structure. Each of these small models can be easily given to the existing views. When the underlying structure receives a call from a model that an attribute is to be updated, it can call every model to inform it of the update (this will maybe be a sort of "master model" on top of the actual underlying data structure).

          The only complexity there is where there's variable containers of sub-structures, as I'd need to spin up views dynamically. But I think this should be easy.

          But then I sort of think I could just hand-roll all this anyway and get the same result. It seems to me that Qt's MVC paradigm doesn't really work for much beyond the simple examples given, and due to the complexity in complicated data models, is the same effort to use as just doing something yourself.

          But happy to be corrected!

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

          @Deckhead
          Yes, you might have to connect signals between multiple widgets. Or, you change a model and that sends signals to other widgets. For something as complex and specific as you describe, where changing one value alters what's going on for other widgets, you can't expect Qt's internal MV structure to somehow automatically produce this.

          Yes you can have multiple, separate models and connect these together as you see suitable. For example, you might decide that your maps/lists are worthy of their own model, with links from the top-level model to the sub-model where appropriate. Though the QDataWidgetMapper I mentioned does handle a list as a single element in its source model.

          1 Reply Last reply
          0
          • D Deckhead

            I'm new to Qt. I'm using QtCreator and have managed to get quite far in my project, but it's becoming unwieldy. I suspect the answer is to use Model/View/Delegates.

            I've read through all the documentation and had a couple of tries, but I don't think I'm breaking things up in a sensible way. I'm hoping someone can point me the right direction.

            My data structure looks like this:

            struct MyData {
                int dataA;
                std::string dataB;
                struct DataCStruct {
                    int data1;
                    int data2;
                } dataC;
                std::map<int,DataCStruct> dataD;
            };
            

            My real data is bigger, but I'm just trying to show the complexity.

            I'd like my UI to be broken up into tab-pages. Page one will just be about editing dataA. Page two might be editing dataC. Page three would be adding rows of data, deleting, etc from dataD.

            For the data model, my thought was that a 'row' would refer to either dataA, dataB, dataC, dataD. And the 'column' would only be important for dataD. Is this correct? The way I thought to implement it was:

            enum STAT { DATAA = 0, DATAB = 1, DATAC = 2, DATAD = 3 };
            

            and then in the ::data method of the model, I would do something like

            QVariant MyModel::data(const QModelIndex& index, int role) const
            {
                if(!index.isValid()) {
                    return QVariant();
                }
            
                if(index.parent.isValid()) {
                    // this must be an element of dataD, because none of the others would have a valid parent
                    if(index.row() == 0) {
                        return m_referenceToUnderlyingStruct.dataD[parent.column()].data1;
                    } else if(index.row() == 1) {
                        return m_referenceToUnderlyingStruct.dataD[parent.column()].data2;
                }
                else {
                    // the row must indicate which "stat" this is
                    switch(static_cast<STAT>(index.row()))
                    {
                    case STAT::DATAA:
                        return m_referenceToUnderlyingStruct.dataA;
                    case STAT::DATAB:
                        return QString::fromStdString(m_referenceToUnderlyingStruct.dataB);
                    case STAT::DATAC:
                        // in this case, the column indicates which ITEM
                        if(index.column() == 0) {
                            return m_referenceToUnderlyingStruct.dataC.data1;
                        } else if(index.column() == 1) {
                            return m_referenceToUnderlyingStruct.dataC.data2;
                        }
                    }
                }
                return QVariant();
            }
            

            but you can see this is already starting to get a little confusing and unmanageable. Whilst I think I see how row, column, and parent can reference elements of any data structure, the implementation of this looks bad to me. In my real structure, I need to use an std::unordered_map, which I'm worried might break things considering there's no guarantee on the order of elements after they've moved.

            If the above is correct, how do I even begin to construct views?

            So, like I said, I want several pages across tabs to edit this single data structure. Each of these tabs might have multiple edit boxes, spin controls, tables or lists. Some of these would be for editing the structure, others would be to apply some kind of logic to determine values to write into this structure.

            Initially, each little section of controls--for example the controls to edit dataA--are on their own form; that's how I did this without a model. So if I want to now use a model, do I create a single view for these controls? I'm confused because how can the view know which elements to request from the model, is this something I determine in my custom view?

            And if I create a new View, how do I visualise this in the QtDesigner?

            From the examples I've seen, the views create Delegates to actually edit the information. But if my view is going to show both dataA and dataB, and allow the user to edit both, how does this work?

            I could really use a better example of a project than the simple examples in the documentation, because I can't see how to achieve my use case. If anyone can point me to examples, head me in the right direction, or even just list the classes and steps I'd need to do for this small example, it would be greatly appreciated.

            Pl45m4P Offline
            Pl45m4P Offline
            Pl45m4
            wrote on last edited by Pl45m4
            #5

            @Deckhead

            Out of curiosity: Why you decided to delete the other topic without any further interaction?
            It's not very nice esp. when people have commented on it. You also could have continued the discussion there ;-)
            Isn't it about the same issue?!


            If debugging is the process of removing software bugs, then programming must be the process of putting them in.

            ~E. W. Dijkstra

            D 1 Reply Last reply
            1
            • Pl45m4P Pl45m4

              @Deckhead

              Out of curiosity: Why you decided to delete the other topic without any further interaction?
              It's not very nice esp. when people have commented on it. You also could have continued the discussion there ;-)
              Isn't it about the same issue?!

              D Offline
              D Offline
              Deckhead
              wrote on last edited by
              #6

              @Pl45m4 said in What's the correct way to setup the following project with a Model/View/Delegate structure?:

              @Deckhead

              Out of curiosity: Why you decided to delete the other topic without any further interaction?
              It's not very nice esp. when people have commented on it. You also could have continued the discussion there ;-)
              Isn't it about the same issue?!

              I deleted it because there was no value in the replies for someone else who came searching for answers.

              Pl45m4P 1 Reply Last reply
              0
              • D Deckhead

                @Pl45m4 said in What's the correct way to setup the following project with a Model/View/Delegate structure?:

                @Deckhead

                Out of curiosity: Why you decided to delete the other topic without any further interaction?
                It's not very nice esp. when people have commented on it. You also could have continued the discussion there ;-)
                Isn't it about the same issue?!

                I deleted it because there was no value in the replies for someone else who came searching for answers.

                Pl45m4P Offline
                Pl45m4P Offline
                Pl45m4
                wrote on last edited by Pl45m4
                #7

                @Deckhead said in What's the correct way to setup the following project with a Model/View/Delegate structure?:

                I deleted it because there was no value in the replies for someone else who came searching for answers.

                Strange attitude...

                Usually these threads evolve over time and by the information you provide... deleting the question right away because the first two answers or so are that exactly what you are looking for is weird but ok.
                Also why do you care about future readers?! You started the topic because you needed help?!
                Good luck with that


                If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                ~E. W. Dijkstra

                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