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. position center of Text on a point
Forum Updated to NodeBB v4.3 + New Features

position center of Text on a point

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
6 Posts 3 Posters 1.2k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    petero3
    wrote on last edited by petero3
    #1

    Hi

    Example: positioning numerals around a clock face.
    Using sin and cos to get the x and y is the easy bit.
    But the text is appearing below the desired point, rather than centered over it.
    Even with
    verticalAlignment: Text.AlignVCenter

    What's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?

    import QtQuick
    
    Item {
        anchors.fill: parent
    
        Repeater {
            model: 12
    
            Text {
                property int radius: parent.height / 2
                text: index
                x: radius + radius * Math.cos(2 * Math.PI * index / 12)
                y: radius + radius * Math.sin(2 * Math.PI * index / 12)
                verticalAlignment: Text.AlignVCenter
            }
        }
    }
    

    QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2

    Thank you
    -- Peter

    fcarneyF L 3 Replies Last reply
    0
    • P petero3

      Hi

      Example: positioning numerals around a clock face.
      Using sin and cos to get the x and y is the easy bit.
      But the text is appearing below the desired point, rather than centered over it.
      Even with
      verticalAlignment: Text.AlignVCenter

      What's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?

      import QtQuick
      
      Item {
          anchors.fill: parent
      
          Repeater {
              model: 12
      
              Text {
                  property int radius: parent.height / 2
                  text: index
                  x: radius + radius * Math.cos(2 * Math.PI * index / 12)
                  y: radius + radius * Math.sin(2 * Math.PI * index / 12)
                  verticalAlignment: Text.AlignVCenter
              }
          }
      }
      

      QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2

      Thank you
      -- Peter

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

      @petero3
      I have played with this a bit. My first thought is to rotate an invisible Item and then counter rotate the text:

      Rectangle {
              anchors.right: parent.right
              anchors.bottom: parent.bottom
      
              width: 200
              height: 200
      
              Repeater {
                  id: clock_repeater
      
                  model: 12
      
                  Item {
                      anchors.horizontalCenter: parent.horizontalCenter
      
                      property real rotinc: 360/clock_repeater.model
      
                      rotation: rotinc * (index + 1)
      
                      height: parent.height
                      width: number_text.width
      
                      Text {
                          id: number_text
      
                          rotation: -parent.rotation
      
                          text: "%1".arg(index + 1)
                      }
                  }
              }
          }
      

      This might be expensive. Maybe not if just matrix calculation.

      Then I thought about taking a position of rotated object to position the text:

      Rectangle {
              anchors.right: parent.right
              anchors.bottom: parent.bottom
      
              width: 200
              height: 200
      
              Repeater {
                  id: clock_repeater
      
                  model: 12
      
                  Item {
                      id: rotate_container
      
                      anchors.horizontalCenter: parent.horizontalCenter
      
                      height: parent.height
                      width: number_text.width
      
                      Item {
                          anchors.fill: parent
      
                          Rectangle {
                              id: rotate_anchor
      
                              width: 2
                              height: 2
      
                              border.width: 1
      
                              anchors.top: parent.top
                              anchors.topMargin: 10
                              anchors.horizontalCenter: parent.horizontalCenter
                          }
      
                          property real rotinc: 360/clock_repeater.model
      
                          rotation: rotinc * (index + 1)
                      }
      
                      Item {
                          width: 16
                          height: 16
      
                          property var cpoint: rotate_anchor.mapToItem(rotate_container, rotate_anchor.x, rotate_anchor.y)
      
                          x: cpoint.x - width/2
                          y: cpoint.y - height/2
      
                          Text {
                              id: number_text
      
                              anchors.centerIn: parent
      
                              text: "%1".arg(index + 1)
                          }
                      }
                  }
              }
          }
      

      But this seems to have turned into a mess. So I think the counter rotate is probably simpler.

      C++ is a perfectly valid school of magic.

      P 1 Reply Last reply
      1
      • fcarneyF fcarney

        @petero3
        I have played with this a bit. My first thought is to rotate an invisible Item and then counter rotate the text:

        Rectangle {
                anchors.right: parent.right
                anchors.bottom: parent.bottom
        
                width: 200
                height: 200
        
                Repeater {
                    id: clock_repeater
        
                    model: 12
        
                    Item {
                        anchors.horizontalCenter: parent.horizontalCenter
        
                        property real rotinc: 360/clock_repeater.model
        
                        rotation: rotinc * (index + 1)
        
                        height: parent.height
                        width: number_text.width
        
                        Text {
                            id: number_text
        
                            rotation: -parent.rotation
        
                            text: "%1".arg(index + 1)
                        }
                    }
                }
            }
        

        This might be expensive. Maybe not if just matrix calculation.

        Then I thought about taking a position of rotated object to position the text:

        Rectangle {
                anchors.right: parent.right
                anchors.bottom: parent.bottom
        
                width: 200
                height: 200
        
                Repeater {
                    id: clock_repeater
        
                    model: 12
        
                    Item {
                        id: rotate_container
        
                        anchors.horizontalCenter: parent.horizontalCenter
        
                        height: parent.height
                        width: number_text.width
        
                        Item {
                            anchors.fill: parent
        
                            Rectangle {
                                id: rotate_anchor
        
                                width: 2
                                height: 2
        
                                border.width: 1
        
                                anchors.top: parent.top
                                anchors.topMargin: 10
                                anchors.horizontalCenter: parent.horizontalCenter
                            }
        
                            property real rotinc: 360/clock_repeater.model
        
                            rotation: rotinc * (index + 1)
                        }
        
                        Item {
                            width: 16
                            height: 16
        
                            property var cpoint: rotate_anchor.mapToItem(rotate_container, rotate_anchor.x, rotate_anchor.y)
        
                            x: cpoint.x - width/2
                            y: cpoint.y - height/2
        
                            Text {
                                id: number_text
        
                                anchors.centerIn: parent
        
                                text: "%1".arg(index + 1)
                            }
                        }
                    }
                }
            }
        

        But this seems to have turned into a mess. So I think the counter rotate is probably simpler.

        P Offline
        P Offline
        petero3
        wrote on last edited by petero3
        #3

        @fcarney

        Thank you very much!

        Ok I can visualise how that works, after temporarily changing Item to Rectangle and adding semi-transparent color to it.
        So the anchors.horizontalCenter and width: number_text.width (of the wrapper Item) gets the center of the text in the right place.
        And then counter-rotate is exploiting that "The default transform origin is Item.Center." (of the text, so it stays in the right place).
        Effective!

        So I guess there isn't a direct way to tell QML to center an item/text over a specific point?

        So rotate/counter-rotate is one workaround. But if the points were along an ellipse or other non-circular curve, then rotate/counter-rotate would be trickier? But I could borrow your idea of a wrapper item, and remember that "The default clip value is false". Then create a wrapper item of size 0 in the desired location and center the text relative to that:

        import QtQuick
        
        Item {
            width: 400
            height: 400
        
            Repeater {
                id: clock_repeater
                model: 12
        
                Item {
                    property int radius: parent.height / 2
        
                    x: radius + radius * Math.cos(2 * Math.PI * index / clock_repeater.model)
                    y: radius + radius * Math.sin(2 * Math.PI * index / clock_repeater.model)
                    
                    width: 0
                    height: 0
                    
                    Text {
                        text: index
                        anchors.centerIn: parent
                    }                    
                }        
            }
        }
        
        1 Reply Last reply
        1
        • P petero3

          Hi

          Example: positioning numerals around a clock face.
          Using sin and cos to get the x and y is the easy bit.
          But the text is appearing below the desired point, rather than centered over it.
          Even with
          verticalAlignment: Text.AlignVCenter

          What's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?

          import QtQuick
          
          Item {
              anchors.fill: parent
          
              Repeater {
                  model: 12
          
                  Text {
                      property int radius: parent.height / 2
                      text: index
                      x: radius + radius * Math.cos(2 * Math.PI * index / 12)
                      y: radius + radius * Math.sin(2 * Math.PI * index / 12)
                      verticalAlignment: Text.AlignVCenter
                  }
              }
          }
          

          QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2

          Thank you
          -- Peter

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

          @petero3
          I got to thinking about the second attempt I made. I need to set the origin of the object to the center of the object. Then using 0 sized anchor object I can precisely place them without counter rotation. No idea which way is faster though:

          Rectangle {
                  anchors.right: parent.right
                  anchors.bottom: parent.bottom
          
                  width: 200
                  height: 200
          
                  Repeater {
                      id: clock_repeater
          
                      model: 12
          
                      Item {
                          id: rotate_container
          
                          anchors.horizontalCenter: parent.horizontalCenter
          
                          height: parent.height
                          width: number_text.width
          
                          Item {
                              anchors.fill: parent
          
                              Item {
                                  id: rotate_anchor
          
                                  anchors.top: parent.top
                                  anchors.topMargin: 10
                                  anchors.horizontalCenter: parent.horizontalCenter
                              }
          
                              property real rotinc: 360/clock_repeater.model
          
                              rotation: rotinc * (index + 1)
                          }
          
          
          
                          Text {
                              id: number_text
          
                              transformOrigin: Item.Center
          
                              property var cpoint: rotate_anchor.mapFromItem(rotate_container, rotate_anchor.x, rotate_anchor.y)
          
                              x: cpoint.x
                              y: cpoint.y
          
                              text: "%1".arg(index + 1)
                          }
          
                      }
                  }
              }
          

          I try to avoid a lot of Javascript math and get the QML C++ objects to do the math. This is written in C++ and should be faster. I have found some algorithms in Javascript to be slow.

          C++ is a perfectly valid school of magic.

          1 Reply Last reply
          0
          • P petero3

            Hi

            Example: positioning numerals around a clock face.
            Using sin and cos to get the x and y is the easy bit.
            But the text is appearing below the desired point, rather than centered over it.
            Even with
            verticalAlignment: Text.AlignVCenter

            What's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?

            import QtQuick
            
            Item {
                anchors.fill: parent
            
                Repeater {
                    model: 12
            
                    Text {
                        property int radius: parent.height / 2
                        text: index
                        x: radius + radius * Math.cos(2 * Math.PI * index / 12)
                        y: radius + radius * Math.sin(2 * Math.PI * index / 12)
                        verticalAlignment: Text.AlignVCenter
                    }
                }
            }
            

            QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2

            Thank you
            -- Peter

            L Offline
            L Offline
            lemons
            wrote on last edited by lemons
            #5

            @petero3 You can also simply translate the text item itself.
            Or add the translation to the x/y calculation.

            Item {
                id: wrapper
                anchors.fill: parent
                property int pointSize: 16
                anchors.margins: pointSize/2
                Repeater {
                    model: 12
            
                    Text {
                        id: text
                        property int radius: parent.height / 2
                        text: index
                        font.pointSize: wrapper.pointSize
                        x: radius + radius * Math.cos(2 * Math.PI * index / 12)
                        y: radius + radius * Math.sin(2 * Math.PI * index / 12)
                        verticalAlignment: Text.AlignVCenter
                        transform: Translate {
                            y: -text.height/2
                            x: -text.width/2
                        }
                    }
                }
            }
            

            Or use a PathView instead, which centers the element on the point automatically:

            PathView {
                id: pathView
                anchors.fill: parent
                property int pointSize: 16
                anchors.margins: pointSize/2
                model: 12
                delegate: Text {
                    id: text
                    font.pointSize: pathView.pointSize
                    text: index
                }
                path: Path {
                    startX: pathView.width/2; startY: 0
            
                    PathArc {
                        x: pathView.width/2; y: pathView.height
                        radiusX: pathView.width/2; radiusY: pathView.height/2
                        direction: PathArc.Clockwise
            
                    }
                    PathArc {
                        x: pathView.width/2; y: 0
                        radiusX: pathView.width/2; radiusY: pathView.height/2
                        direction: PathArc.Clockwise
            
                    }
                }
            }
            
            P 1 Reply Last reply
            1
            • L lemons

              @petero3 You can also simply translate the text item itself.
              Or add the translation to the x/y calculation.

              Item {
                  id: wrapper
                  anchors.fill: parent
                  property int pointSize: 16
                  anchors.margins: pointSize/2
                  Repeater {
                      model: 12
              
                      Text {
                          id: text
                          property int radius: parent.height / 2
                          text: index
                          font.pointSize: wrapper.pointSize
                          x: radius + radius * Math.cos(2 * Math.PI * index / 12)
                          y: radius + radius * Math.sin(2 * Math.PI * index / 12)
                          verticalAlignment: Text.AlignVCenter
                          transform: Translate {
                              y: -text.height/2
                              x: -text.width/2
                          }
                      }
                  }
              }
              

              Or use a PathView instead, which centers the element on the point automatically:

              PathView {
                  id: pathView
                  anchors.fill: parent
                  property int pointSize: 16
                  anchors.margins: pointSize/2
                  model: 12
                  delegate: Text {
                      id: text
                      font.pointSize: pathView.pointSize
                      text: index
                  }
                  path: Path {
                      startX: pathView.width/2; startY: 0
              
                      PathArc {
                          x: pathView.width/2; y: pathView.height
                          radiusX: pathView.width/2; radiusY: pathView.height/2
                          direction: PathArc.Clockwise
              
                      }
                      PathArc {
                          x: pathView.width/2; y: 0
                          radiusX: pathView.width/2; radiusY: pathView.height/2
                          direction: PathArc.Clockwise
              
                      }
                  }
              }
              
              P Offline
              P Offline
              petero3
              wrote on last edited by petero3
              #6

              Hi @lemons
              Thank you very much.
              Ok, so PathView automatically spaces the delegate out evenly along the path. Thanks!

              PS. re "-text.width/2" - yes that is how we normally do it. But just wondered if there was a more QML-ish way. Telling Qt to position using the center, rather than a corner. There is anchors.centerIn and there is "The default transform origin is Item.Center"...

              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