Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. TableView - Show an optional item covering the whole row of a multi-columns table view
QtWS25 Last Chance

TableView - Show an optional item covering the whole row of a multi-columns table view

Scheduled Pinned Locked Moved Solved QML and Qt Quick
table viewitem delegateitempaint eventoverlay
10 Posts 3 Posters 3.1k Views
  • 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.
  • jeanmilostJ Offline
    jeanmilostJ Offline
    jeanmilost
    wrote on last edited by
    #1

    In a project, I added a TableView component, which is composed by a multiple columns header and several rows, as shown in the below screenshot:

    tableview with progress.png

    Depending on events happening in the application, I need to cover a part of some items with a kind of overlay, which e.g shows an error or ask a confirmation to the user, as shown in the below screenshot:

    tableview with overlay.png

    My idea was to add a special Item component in my TableWiew delegate, which would be optionally visible under certain conditions, and only drawn with the first column (i.e visible: column === 0) but covering the last part of my row by using the delegate anchoring and the parent view width.

    This solution seems possible, but has several issues:

    1. The content of the columns covered by the item are visible above the item, even if they z order is set to 0 whereas the item z order is set to 1. Probably because the TableView draws the columns randomly, depending on what it needs.
    2. I tried to draw the item on the last column instead of the first one. This resolve partially the issue mentioned in point 1 but randomly the content of several columns appear above my item, depending on if the tree decide to refresh the content of these particular columns.
    3. I tried to paint the background of each column covered by my item. This resolve the point 1 and 2, but the components contained in my item are also partially cropped by the background.

    So my questions are:

    • Is my idea correct or is there another way to achieve that?
    • If my idea isn't the good one, what is the best way to create a such item?
    • From the qml, is there a signal which notifies that a row was painted, or a way to know that a particular row was painted?
    A 1 Reply Last reply
    0
    • jeanmilostJ Offline
      jeanmilostJ Offline
      jeanmilost
      wrote on last edited by
      #10

      Thanks to all for the replies.

      I finally resolved the issue by replacing the TableView by a ListView and rewriting the columns handling manually. As the ListView draws the whole item content once, all the above mentioned issues are resolved at the same time.

      However very interesting concepts were presented here, and they may help me for other similar situations in the future. Once again, thank you very much to took the time to help me and reply to my questions.

      1 Reply Last reply
      1
      • jeanmilostJ jeanmilost

        In a project, I added a TableView component, which is composed by a multiple columns header and several rows, as shown in the below screenshot:

        tableview with progress.png

        Depending on events happening in the application, I need to cover a part of some items with a kind of overlay, which e.g shows an error or ask a confirmation to the user, as shown in the below screenshot:

        tableview with overlay.png

        My idea was to add a special Item component in my TableWiew delegate, which would be optionally visible under certain conditions, and only drawn with the first column (i.e visible: column === 0) but covering the last part of my row by using the delegate anchoring and the parent view width.

        This solution seems possible, but has several issues:

        1. The content of the columns covered by the item are visible above the item, even if they z order is set to 0 whereas the item z order is set to 1. Probably because the TableView draws the columns randomly, depending on what it needs.
        2. I tried to draw the item on the last column instead of the first one. This resolve partially the issue mentioned in point 1 but randomly the content of several columns appear above my item, depending on if the tree decide to refresh the content of these particular columns.
        3. I tried to paint the background of each column covered by my item. This resolve the point 1 and 2, but the components contained in my item are also partially cropped by the background.

        So my questions are:

        • Is my idea correct or is there another way to achieve that?
        • If my idea isn't the good one, what is the best way to create a such item?
        • From the qml, is there a signal which notifies that a row was painted, or a way to know that a particular row was painted?
        A Offline
        A Offline
        Allon
        wrote on last edited by
        #2

        @jeanmilost Hi:
        Do you want something like this?

        rowswithbutttons.gif

        Regards,
        Emmanuel

        1 Reply Last reply
        1
        • fcarneyF Offline
          fcarneyF Offline
          fcarney
          wrote on last edited by
          #3

          How did you do the status bars? Did you use a popup?
          I imagine you could use another popup and ensure it covers the status bar?
          Here is the status bar code I played with:

          import QtQuick 2.15
          import QtQuick.Window 2.15
          import QtQuick.Controls 2.12
          
          import Qt.labs.qmlmodels 1.0
          
          Window {
              id: root
          
              visible: true
              width: 800
              height: 600
              title: qsTr("TableView Testing")
          
              Row {
                  id: root_row
          
                  TableView {
                      id: tableview1
          
                      width: root.width/2
                      height: root.height - 50
          
                      clip: true
          
                      model: tablemodel
          
                      delegate: Item {
                          id: del_item
          
                          implicitWidth: tableview1.width/3
                          implicitHeight: del_height + progbar_height
          
                          property bool column0: column == 0
                          property int del_height: 25
                          property int progbar_height: statusTip < 1.0 ? 20 : 0
          
                          Rectangle {
                              anchors.fill: parent
                              color: "lightsteelblue"
                              clip: true
                              border.width: 1
                              border.color: "steelblue"
          
                              Text {
                                  id: del_text
          
                                  height: del_item.del_height
          
                                  text: display
                              }
          
                              Popup {
                                  y: del_item.del_height
                                  width: tableview1.width
                                  height: del_item.progbar_height
                                  visible: del_item.column0 && del_item.progbar_height > 0
                                  modal: false
                                  focus: false
                                  closePolicy: Popup.NoAutoClose
          
                                  background: Rectangle {
                                      color: "transparent"
                                  }
          
                                  contentItem: ProgressBar {
                                      from: 0
                                      to: 1.0
                                      value: statusTip
          
                                      anchors.fill: parent
                                      anchors.margins: 5
                                  }
                              }
                          }
                      }
                  }
              }
          
              Slider {
                  id: progress_slider
          
                  anchors.top: root_row.bottom
          
                  value: 1.0
          
                  from: 0
                  to: 1.0
              }
          
              TableModel {
                  id: tablemodel
          
                  property real progress: progress_slider.value
          
                  TableModelColumn { display: "text"; statusTip: "progress" }
                  TableModelColumn { display: "path"; statusTip: "progress" }
                  TableModelColumn { display: "image"; statusTip: "progress" }
          
                  rows: [
                      {
                          text: "one",
                          path: "/root/one.txt",
                          image: "/root/one.png",
                          progress: progress
                      },
                      {
                          text: "two",
                          path: "/root/two.txt",
                          image: "/root/two.png",
                          progress: progress
                      },
                      {
                          text: "three",
                          path: "/root/three.txt",
                          image: "/root/three.png",
                          progress: progress
                      },
                  ]
              }
          }
          

          C++ is a perfectly valid school of magic.

          1 Reply Last reply
          1
          • fcarneyF Offline
            fcarneyF Offline
            fcarney
            wrote on last edited by
            #4

            Like this:

            import QtQuick 2.15
            import QtQuick.Window 2.15
            import QtQuick.Controls 2.12
            
            import Qt.labs.qmlmodels 1.0
            
            Window {
                id: root
            
                visible: true
                width: 800
                height: 600
                title: qsTr("TableView Testing")
            
                Row {
                    id: root_row
            
                    TableView {
                        id: tableview1
            
                        width: root.width/2
                        height: root.height - 50
            
                        clip: true
            
                        model: tablemodel
            
                        delegate: Item {
                            id: del_item
            
                            implicitWidth: tableview1.width/3
                            implicitHeight: del_height + progbar_height
            
                            property bool column0: column == 0
                            property bool column1: column == 1
                            property int del_height: 25
                            property int progbar_height: statusTip < 1.0 ? 20 : 0
            
                            Rectangle {
                                anchors.fill: parent
                                color: "lightsteelblue"
                                clip: true
                                border.width: 1
                                border.color: "steelblue"
            
                                Text {
                                    id: del_text
            
                                    height: del_item.del_height
            
                                    text: display
                                }
            
                                Popup {
                                    y: del_item.del_height
                                    width: tableview1.width
                                    height: del_item.progbar_height
                                    visible: del_item.column0 && del_item.progbar_height > 0
                                    modal: false
                                    focus: false
                                    closePolicy: Popup.NoAutoClose
            
                                    background: Rectangle {
                                        color: "transparent"
                                    }
            
                                    contentItem: ProgressBar {
                                        from: 0
                                        to: 1.0
                                        value: statusTip
            
                                        anchors.fill: parent
                                        anchors.margins: 5
                                    }
                                }
                                Popup {
                                    width: del_item.implicitWidth*2
                                    height: del_item.implicitHeight // del_item.del_height // if you want to show progress bar
                                    visible: del_item.column1 && decoration
                                    modal: false
                                    focus: false
                                    closePolicy: Popup.NoAutoClose
            
                                    onHeightChanged: {
                                        //console.log(height)
                                    }
            
                                    background: Rectangle {
                                        color: "darkred"
                                    }
            
                                    Text {
                                        anchors.centerIn: parent
                                        text: "BIG BAD!"
                                    }
                                }
                            }
                        }
                    }
                }
            
                Slider {
                    id: progress_slider
            
                    anchors.top: root_row.bottom
            
                    value: 1.0
            
                    from: 0
                    to: 1.0
                }
            
                TableModel {
                    id: tablemodel
            
                    property real progress: progress_slider.value
            
                    TableModelColumn { display: "text"; statusTip: "progress"; decoration: "warning"}
                    TableModelColumn { display: "path"; statusTip: "progress"; decoration: "warning"}
                    TableModelColumn { display: "image"; statusTip: "progress"; decoration: "warning"}
            
                    rows: [
                        {
                            text: "one",
                            path: "/root/one.txt",
                            image: "/root/one.png",
                            progress: progress,
                            warning: false
                        },
                        {
                            text: "two",
                            path: "/root/two.txt",
                            image: "/root/two.png",
                            progress: progress_slider.to - progress,
                            warning: true
                        },
                        {
                            text: "three",
                            path: "/root/three.txt",
                            image: "/root/three.png",
                            progress: progress,
                            warning: false
                        },
                    ]
                }
            }
            

            C++ is a perfectly valid school of magic.

            1 Reply Last reply
            1
            • jeanmilostJ Offline
              jeanmilostJ Offline
              jeanmilost
              wrote on last edited by jeanmilost
              #5

              Thank for all the answers.

              @Allon Not exactly. In fact I need an area which covers a part of the existing row, showing extra information like a confirmation to user, or an error message. This area should appear on the top of all existing components already contained in the row, and may cover them , partially or completely. The area should also not be dependent of the columns, i.e it may cover several columns, partially or completely. I know that this behavior may be somewhat illogical, but it's a feature required by our client.

              @fcarney The status bar is just a Rectangle covering the bottom of the row. It is drawn while the first cell of the row is drawn, and as the clipping is ignored it may cover the whole row without problems. The key is that no child component is drawn on the same location covered by the progress, so there is no drawing conflicts here and I can draw it once for all (to be honest I implemented this progress based on another question answered by yourself, here: https://forum.qt.io/topic/117322/how-to-add-a-component-onto-the-whole-row-of-a-multi-column-tableview).

              But the situation isn't the same here, unfortunately. The overlay I want should cover already existing children components, and they enter in conflict with it. I tried several solutions, among others:

              • drawing my overlay while the first column is drawn. This failed because all the components contained in the other cells were overprinted above my overlay, whatever the z value.
              • drawing my overlay while the last column is drawn. This worked better, however the view seems to refresh several cells randomly, and they appear overprinted when this scenario happens.
              • force some columns to erase their background before painting the overlay (by drawing a rectangle covering the whole cell content after the normal content was painted). This worked, except that the components drawn in the overlay were also cropped when they overlapped other columns.
              • using a popup for my overlay. This was the best solution until now, however there is an issue: the popup isn't clipped into the TableView bounds.

              About the popup, this would be the solution to my issue if I can apply the above mentioned clipping. Do you know how to achieve that?

              A fcarneyF 2 Replies Last reply
              0
              • jeanmilostJ jeanmilost

                Thank for all the answers.

                @Allon Not exactly. In fact I need an area which covers a part of the existing row, showing extra information like a confirmation to user, or an error message. This area should appear on the top of all existing components already contained in the row, and may cover them , partially or completely. The area should also not be dependent of the columns, i.e it may cover several columns, partially or completely. I know that this behavior may be somewhat illogical, but it's a feature required by our client.

                @fcarney The status bar is just a Rectangle covering the bottom of the row. It is drawn while the first cell of the row is drawn, and as the clipping is ignored it may cover the whole row without problems. The key is that no child component is drawn on the same location covered by the progress, so there is no drawing conflicts here and I can draw it once for all (to be honest I implemented this progress based on another question answered by yourself, here: https://forum.qt.io/topic/117322/how-to-add-a-component-onto-the-whole-row-of-a-multi-column-tableview).

                But the situation isn't the same here, unfortunately. The overlay I want should cover already existing children components, and they enter in conflict with it. I tried several solutions, among others:

                • drawing my overlay while the first column is drawn. This failed because all the components contained in the other cells were overprinted above my overlay, whatever the z value.
                • drawing my overlay while the last column is drawn. This worked better, however the view seems to refresh several cells randomly, and they appear overprinted when this scenario happens.
                • force some columns to erase their background before painting the overlay (by drawing a rectangle covering the whole cell content after the normal content was painted). This worked, except that the components drawn in the overlay were also cropped when they overlapped other columns.
                • using a popup for my overlay. This was the best solution until now, however there is an issue: the popup isn't clipped into the TableView bounds.

                About the popup, this would be the solution to my issue if I can apply the above mentioned clipping. Do you know how to achieve that?

                A Offline
                A Offline
                Allon
                wrote on last edited by
                #6

                @jeanmilost Hi
                An area which covers a part of the existing row, is it not what happens when the cursor goes over the three points and the rectangle appears with the pen and the bin?
                Regards

                jeanmilostJ 1 Reply Last reply
                1
                • A Allon

                  @jeanmilost Hi
                  An area which covers a part of the existing row, is it not what happens when the cursor goes over the three points and the rectangle appears with the pen and the bin?
                  Regards

                  jeanmilostJ Offline
                  jeanmilostJ Offline
                  jeanmilost
                  wrote on last edited by
                  #7

                  @Allon First of all, thank you for your help. Yes, your example may match with what I need, however it's not representative of the issue I face. AFAIK, the idea is to put the three dots component and its 2 buttons overlay in a well bounded cell, or in an Item placed at the end of the row if the tree doesn't contain columns.

                  But in my case, things are different. I have a multi-columns tree, and I need an overlay which overlap several cells, completely or partially, and remain independent of these cells, breaking thus the logical provided by the tree. And a such overlay is incompatible with the standard painting process, unfortunately.

                  I'm aware that creating a multi-columns tree to break the rules and show graphical elements which overlap the cells sounds like a conception issue, and I already reported that to my client. But as he really want the tree working this way, I need to find a solution.

                  I'm also trying another approach, which use a ListView instead of a TableView, but although this may resolve the overlay issue I described above, I need to rewrite manually the whole columns management, and this brings performance issues as described in this post: https://forum.qt.io/topic/118005/listview-how-to-refresh-rows-which-content-changed-without-slowdown

                  A 1 Reply Last reply
                  0
                  • jeanmilostJ jeanmilost

                    Thank for all the answers.

                    @Allon Not exactly. In fact I need an area which covers a part of the existing row, showing extra information like a confirmation to user, or an error message. This area should appear on the top of all existing components already contained in the row, and may cover them , partially or completely. The area should also not be dependent of the columns, i.e it may cover several columns, partially or completely. I know that this behavior may be somewhat illogical, but it's a feature required by our client.

                    @fcarney The status bar is just a Rectangle covering the bottom of the row. It is drawn while the first cell of the row is drawn, and as the clipping is ignored it may cover the whole row without problems. The key is that no child component is drawn on the same location covered by the progress, so there is no drawing conflicts here and I can draw it once for all (to be honest I implemented this progress based on another question answered by yourself, here: https://forum.qt.io/topic/117322/how-to-add-a-component-onto-the-whole-row-of-a-multi-column-tableview).

                    But the situation isn't the same here, unfortunately. The overlay I want should cover already existing children components, and they enter in conflict with it. I tried several solutions, among others:

                    • drawing my overlay while the first column is drawn. This failed because all the components contained in the other cells were overprinted above my overlay, whatever the z value.
                    • drawing my overlay while the last column is drawn. This worked better, however the view seems to refresh several cells randomly, and they appear overprinted when this scenario happens.
                    • force some columns to erase their background before painting the overlay (by drawing a rectangle covering the whole cell content after the normal content was painted). This worked, except that the components drawn in the overlay were also cropped when they overlapped other columns.
                    • using a popup for my overlay. This was the best solution until now, however there is an issue: the popup isn't clipped into the TableView bounds.

                    About the popup, this would be the solution to my issue if I can apply the above mentioned clipping. Do you know how to achieve that?

                    fcarneyF Offline
                    fcarneyF Offline
                    fcarney
                    wrote on last edited by fcarney
                    #8

                    @jeanmilost said in TableView - Show an optional item covering the whole row of a multi-columns table view:

                    About the popup, this would be the solution to my issue if I can apply the above mentioned clipping. Do you know how to achieve that?

                    I didn't, but now I do:

                             delegate: Item {
                                    id: del_item
                    
                                    implicitWidth: tableview1.width/3
                                    implicitHeight: del_height + progbar_height
                    
                                    property bool column0: column == 0
                                    property int del_height: 25
                                    property int progbar_height: statusTip < 1.0 ? 20 : 0
                    
                                    property bool column1: column == 1
                                    property var coords
                                    onCoordsChanged: {
                                        badrect.x = coords.x
                                        badrect.y = coords.y
                                    }
                                    onXChanged: {
                                        coords = mapToItem(tableview1,0,0)
                                    }
                    
                                    Rectangle {
                                        id: badrect
                    
                                        color: "darkred"
                    
                                        parent: tableview1
                    
                                        visible: del_item.column1 && decoration
                    
                                        width: del_item.implicitWidth*2
                                        height: del_item.implicitHeight
                                    }
                    
                                    Rectangle {
                                        anchors.fill: parent
                                        color: "lightsteelblue"
                                        clip: true
                                        border.width: 1
                                        border.color: "steelblue"
                    
                                        Text {
                                            id: del_text
                    
                                            height: del_item.del_height
                    
                                            text: display
                                        }
                    
                                        Popup {
                                            y: del_item.del_height
                                            width: tableview1.width
                                            height: del_item.progbar_height
                                            visible: del_item.column0 && del_item.progbar_height > 0
                                            modal: false
                                            focus: false
                                            closePolicy: Popup.NoAutoClose
                    
                                            background: Rectangle {
                                                color: "transparent"
                                            }
                    
                                            contentItem: ProgressBar {
                                                from: 0
                                                to: 1.0
                                                value: statusTip
                    
                                                anchors.fill: parent
                                                anchors.margins: 5
                                            }
                                        }
                                        /*
                                        Popup {
                                            width: del_item.implicitWidth*2
                                            height: del_item.implicitHeight // del_item.del_height // if you want to show progress bar
                                            visible: del_item.column1 && decoration
                                            modal: false
                                            focus: false
                                            closePolicy: Popup.NoAutoClose
                    
                                            onHeightChanged: {
                                                //console.log(height)
                                            }
                    
                                            background: Rectangle {
                                                color: "darkred"
                                            }
                    
                                            Text {
                                                anchors.centerIn: parent
                                                text: "BIG BAD!"
                                            }
                                        }
                                        */
                                    }
                                }
                    

                    The trick is re-parenting the rectangle (badrect) to the tableview1 and capturing when the coordinates of the del_item changes. You cannot get the coordinates during the Component.onCompleted signal. It has to be done later. There might be a more elegant way to get this, but whatever. If x changes more than once you should be okay. Not sure if this will work when a cell is on the left side of a table, or at the top if tracking y. So maybe calling mapToItem with x and y in the call as dummy change triggers would be better.

                    Edit: Maybe this won't work. It still seems to have clipping issues. I don't understand why. My tableview is set to clip.
                    Edit 2: This may have more to do with my tableview not resize when the window resizes. Not sure what the issues is there.

                    C++ is a perfectly valid school of magic.

                    1 Reply Last reply
                    1
                    • jeanmilostJ jeanmilost

                      @Allon First of all, thank you for your help. Yes, your example may match with what I need, however it's not representative of the issue I face. AFAIK, the idea is to put the three dots component and its 2 buttons overlay in a well bounded cell, or in an Item placed at the end of the row if the tree doesn't contain columns.

                      But in my case, things are different. I have a multi-columns tree, and I need an overlay which overlap several cells, completely or partially, and remain independent of these cells, breaking thus the logical provided by the tree. And a such overlay is incompatible with the standard painting process, unfortunately.

                      I'm aware that creating a multi-columns tree to break the rules and show graphical elements which overlap the cells sounds like a conception issue, and I already reported that to my client. But as he really want the tree working this way, I need to find a solution.

                      I'm also trying another approach, which use a ListView instead of a TableView, but although this may resolve the overlay issue I described above, I need to rewrite manually the whole columns management, and this brings performance issues as described in this post: https://forum.qt.io/topic/118005/listview-how-to-refresh-rows-which-content-changed-without-slowdown

                      A Offline
                      A Offline
                      Allon
                      wrote on last edited by
                      #9

                      @jeanmilost
                      Hi,
                      I would still not use popup.
                      Here is how I did it.
                      https://invent.kde.org/education/gcompris/-/blob/server_allon/src/activities/server/components/ManageGroupsBar.qml

                      The line that will interest you is the following:
                      https://invent.kde.org/education/gcompris/-/blob/server_allon/src/activities/server/components/ManageGroupsBar.qml#L179

                      If I get hover the elipses mousearea I set modifyGroupCommandsRectangle to visible, which means I display the rectangle with my pen and my bin.
                      In your case this would be one rectangle with "download failed and cancel".

                      At the moment I am working with RowLayout and a repeater, but I will change it soon to a Listview + inner rectangles to be able to take advantage of the flickable property.
                      If you want to cover several cells you can anchor your modifyGroupCommandsRectangle to the anchors.right of your listview element and set the length of it to the addition of several cells length.
                      You can even using repeater index to set your "download failed and cancel" on any element of you listview.

                      Hope I got it right.

                      1 Reply Last reply
                      1
                      • jeanmilostJ Offline
                        jeanmilostJ Offline
                        jeanmilost
                        wrote on last edited by
                        #10

                        Thanks to all for the replies.

                        I finally resolved the issue by replacing the TableView by a ListView and rewriting the columns handling manually. As the ListView draws the whole item content once, all the above mentioned issues are resolved at the same time.

                        However very interesting concepts were presented here, and they may help me for other similar situations in the future. Once again, thank you very much to took the time to help me and reply to my questions.

                        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