Название: Обьект для отображения циферблата
Отправлено: SektorCT от Июнь 20, 2023, 15:35
Добрый день всем. Нужно нарисовтаь циферблат, разные значения, 2 стрелки. Нашел что в qml есть Canvas. Вопрос к тем кто с подобным сталкивался, какова производительность если через него делать? Есть ли другие способы создание такого обьекта? Если будет штук 10-20 таких обьектов как сильно может пострадать производительность? Само собою все значения, изменения и вся логика будет только на строне С++.
Спасибо.
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июнь 21, 2023, 12:58
Может кто использовал QSGGeometryNode, с его помощью можно такое нарисовтаь?
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июнь 26, 2023, 12:16
Нашел один пример в котормо изпользуется обьект Shape И получилось такое сделать Shape { id: trackShape
height: appWin.height layer.enabled: true layer.samples: 8 visible: !appWin.hideTrack width: appWin.width
ShapePath { id: trackShapePath
capStyle: appWin.capStyle fillColor: internal.transparentColor strokeColor: appWin.trackColor strokeWidth: appWin.trackWidth
PathAngleArc { centerX: appWin.width / 2 centerY: appWin.height / 2 radiusX: internal.baseRadius radiusY: internal.baseRadius startAngle: appWin.startAngle - 90 sweepAngle: internal.actualSpanAngle } } }
https://ibb.co/Q6nW1kv (https://ibb.co/Q6nW1kv) А мне надо бы вот до такого примерно довести. То есть шкалы и стрелка, какой то центральынй круглый обьект в котором будут значения более точные. Может кто подскажет как стрелки и шкалы создать? https://ibb.co/9yzZgZR (https://ibb.co/9yzZgZR)
Название: Re: Обьект для отображения циферблата
Отправлено: kambala от Июнь 26, 2023, 15:34
шкалы (и стрелки) — это, очевидно, просто линии PathLine. или надо еще написать код как ходить по кругу чтоб их проставить? :)
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июль 04, 2023, 16:22
В общем поулчилось сделать и Shape сам внешнйи круг и для каждого tickmark высчитан угол наклона. Но вот он ивсе у меня распологаются в центре. https://ibb.co/mDL2X00 (https://ibb.co/mDL2X00) Может кто скажет как якарями привязать к внутренней строне дуги чтоыб тики рапологались правильно? Или можно по другому? Ниже код: Shape { id: trackShape height: appWin.size width: appWin.size layer.enabled: true layer.samples: 8 rotation: 180
ShapePath { id: trackShapePath capStyle: Qt.FlatCap fillColor: "transparent" strokeColor: "#333333" strokeWidth: pelData.mainCircleBorderSize
PathAngleArc { centerX: appWin.size / 2 centerY: appWin.size / 2 radiusX: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, appWin.progressWidth) / 2 radiusY: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, appWin.progressWidth) / 2 startAngle: appWin.startAngle - 90 sweepAngle: 320 - 40 } } }
Rectangle { id: mainForeground z: 1 anchors.centerIn: mainItem height: pelData.centralCircleRadius width: pelData.centralCircleRadius color: "gray" border.width: pelData.secondCircleBorderSize border.color: "#505050" radius: height / 2 visible: false
Text { id: textValue z: 1 anchors.centerIn: parent text: pelData.currentValue font.pixelSize: pelData.textValueSize font.bold: true } }
Repeater { id: repeaterTickMark
model: pelData.gaugeDataModel anchors.fill: parent
delegate: Rectangle { width: model.eWidth height: model.eHeight color: model.eColorTickMark antialiasing: true
function major() { if(model.eWithText) return true return false }
Rectangle { anchors.fill: parent anchors.topMargin: major() ? 160 : 165 anchors.bottomMargin: major() ? 3 : 23 color: "blue"
Component.onCompleted: { console.log(model.eAngle, model.index, model.eWidth, model.eWithText) } }
transform: [ Rotation { angle: model.eAngle }, Translate { x: mainItem.width / 2 y: mainItem.height / 2 } ] } }
Название: Re: Обьект для отображения циферблата
Отправлено: kambala от Июль 04, 2023, 21:44
можно ж просто сместить координату на толщину дуги
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июль 05, 2023, 11:08
можно ж просто сместить координату на толщину дуги
А вы не могли бы примером написать как это можно сделать? не представлю пока. И такой вопрос, мне надо будет к мажорным тикмаркам еще приделать надпись, значение его. Как понимаю это можно так же будет сделать как вы описали?
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июль 05, 2023, 14:08
Смысл такой что мне надо со внутренней стороны дуги разместить мажорные и минорные тикмарки. они будут иметь разную длину. И вот задумка была что они все рипетером раскиданы, а внутри уже видимая цветная часть с разными размерами. Потом уже и надписи со значениями у мажорных будут, но это второстепенное.
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июль 07, 2023, 15:55
Ниже код в котормо я пытаюсь с помощью PathMultiline размещать нужные мне тикмарки. То есть я на месте второго Shape хочу и разместить тики. И поулчается тчо надо и постояныне расчеты делать если обьект еняет размер. В приведенном коде в PathMultiline я просто для реста пробую на основе 2 хотябы тиков создать в нужном месте. Мне уже посоветовали что надо по теории Пифагора искать. Могли бы на этмо примере показать если вы знаете как это можно сделать? import QtQuick import QtQuick.Controls import QtQuick.Shapes
Item { id: appWin
property int progressWidth: 20 property int trackWidth: 3 property int handleHeight: 22 property int handleWidth: 22 property real outerRadius: 15.0 property real minorInsetRadius: outerRadius - 20 property real majorInsetRadius: outerRadius - 20 property real labelInsetRadius: outerRadius - 30
// control param property double minimumValue: 0.0 property double maximumValue: 160.0 property real startAngle: 40 property real endAngle: 320 property double value
property int size: appWin.height
property real needleRotation: { var percentage = (appWin.value - appWin.minimumValue) / (appWin.maximumValue - appWin.minimumValue); appWin.startAngle + percentage * Math.abs(appWin.endAngle - appWin.startAngle); }
Item { id: mainItem anchors.horizontalCenter: appWin.horizontalCenter anchors.verticalCenter: appWin.verticalCenter height: appWin.size width: appWin.size
Rectangle { id: mianBackgraund height: appWin.size width: appWin.size color: "gray" radius: height / 2 }
Shape { id: trackShape height: appWin.size width: appWin.size layer.enabled: true layer.samples: 8 rotation: 180 // перевернуть обьект по вертикали
ShapePath { id: trackShapePath capStyle: Qt.FlatCap // закругленные концы дуги fillColor: "transparent" // цвет заполнения внутри круга strokeColor: "#505050" // цвет внешнего кольца(границы) strokeWidth: 15 // толщина внешней границы
PathAngleArc { centerX: appWin.size / 2 // центровка для обоих точек centerY: appWin.size / 2 radiusX: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, appWin.progressWidth) / 2 radiusY: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, appWin.progressWidth) / 2 startAngle: appWin.startAngle - 90 // на сколько будет повернут обьект(все зависит от начальной точки) sweepAngle: 320 - 40 // угол актуального пропуска в дуге(будет виден пролет от 40 до 320 градусов) } } }
// Текст со значением куда повернута стрелка Text { id: textValue z: 1 anchors.centerIn: mainItem text: value.toFixed(1) font.pixelSize: 35 font.bold: true }
// В нем мы будем создавать шкалы минутной величины(если мажорные тики это 0, 50, 100 и так далее, то для минорных это 10,20,30) Shape { id: minorTickMarketShape height: appWin.size width: appWin.size layer.enabled: true layer.samples: 8 // rotation: 180 // перевернуть обьект по вертикали
ShapePath { id: minorTickMarketShapePath capStyle: Qt.FlatCap // закругленные концы дуги fillColor: "transparent" // цвет заполнения внутри круга strokeColor: "blue" // цвет внешнего кольца(границы) strokeWidth: 10 // толщина внешней границы
PathAngleArc { id: pathAngleArc centerX: appWin.size / 2 // центровка для обоих точек centerY: appWin.size / 2 radiusX: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, 50) / 2 radiusY: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, 50) / 2 startAngle: appWin.startAngle - 90 // на сколько будет повернут обьект(все зависит от начальной точки) sweepAngle: 320 - 40 // угол актуального пропуска в дуге(будет виден пролет от 40 до 320 градусов) }
PathMultiline { paths: [ [Qt.point(minorTickMarketShape.height / 2, minorTickMarketShape.width / 2), Qt.point(50, 220)],
[Qt.point(50.5, 80.12472), Qt.point(50.95, 80.90414)] ] } } }
Repeater { id: tickmarkRepeater model: 7 anchors.fill: parent
property real p: Math.abs(135 - (-135)) / (tickmarkRepeater.model - 1)
delegate: Loader { id: tickmarkLoader x: appWin.width / 2 y: appWin.height / 2 sourceComponent: tickmark
transform: [ Rotation { angle: ((-135) + 360 + (index * tickmarkRepeater.p) ) }, Translate { x: Math.sin(((-135) + 180 + index * tickmarkRepeater.p) * (Math.PI / 180)) * appWin.majorInsetRadius * -1 y: Math.cos(((-135) + 180 + index * tickmarkRepeater.p) * (Math.PI / 180)) * appWin.majorInsetRadius } ] } }
// Стрелка Rectangle { id: needle width: 10 height: trackShape.height / 2 color: "#FFac89" radius: width / 2 antialiasing: true
transform: [ Rotation { angle: needleRotation }, Translate { x: mainItem.width / 2 y: mainItem.height / 2 } ] } }
Behavior on value { NumberAnimation { easing.overshoot: 1.2 duration: 5000 easing.type: Easing.OutBack } } }
Название: Re: Обьект для отображения циферблата
Отправлено: kambala от Июль 08, 2023, 15:49
запускаешь код, а там qrc:/main.qml:0: ReferenceError: tickmark is not defined qrc:/main.qml:0: ReferenceError: tickmark is not defined qrc:/main.qml:0: ReferenceError: tickmark is not defined qrc:/main.qml:0: ReferenceError: tickmark is not defined qrc:/main.qml:163: ReferenceError: tickmark is not defined qrc:/main.qml:163: ReferenceError: tickmark is not defined qrc:/main.qml:163: ReferenceError: tickmark is not defined qrc:/main.qml:105: ReferenceError: value is not defined qrc:/main.qml:190: ReferenceError: needleRotation is not defined как ты думаешь, сколько человек захочет читать твою простыню после этого? :) да, тут надо использовать теорему Пифагора, точнее полярные координаты (они как раз из нее и выводятся). import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import QtQuick.Layouts 1.3 import QtQuick.Shapes 1.15 Window { readonly property real quarterLength: 50 readonly property real minuteLength: 20 width: 640 height: 480 visible: true title: qsTr("Hello World") /* Shape { id: circle anchors.centerIn: parent ShapePath { strokeColor: "red" PathAngleArc { radiusX: Math.min(window.width, window.height) / 2 radiusY: radiusX startAngle: 0 sweepAngle: 360 } } } */ Rectangle { id: circle anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: width radius: width / 2 border.color: "red" } Repeater { id: tickmarks model: 12 Shape { required property int index readonly property real phi: index * 2 * Math.PI / tickmarks.count readonly property real polarX: Math.cos(phi) readonly property real polarY: Math.sin(phi) x: circle.radius * polarX y: circle.radius * polarY transform: Translate { x: circle.x + circle.radius y: circle.y + circle.radius } ShapePath { strokeColor: "blue" strokeWidth: 1 PathLine { readonly property real tickmarkLength: index % 3 == 0 ? quarterLength : minuteLength relativeX: -polarX * tickmarkLength relativeY: -polarY * tickmarkLength } } } } }
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июль 10, 2023, 10:52
Извиняюсь, я не все увидел что надо было убрать из рабочего кода. Но надеюсь не создало проблем. Спасибо за пример, сейчас буду изучать. И такой вопрос, я же правильно понимаю что для правильного позиционирования тикмарков нужно размер внешней дуги знать?
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июль 10, 2023, 10:59
И почему вы закоментили обьект внешней дуги Shape и сделали на основа rectangle? Я в начале тоже думал использовать rectangle но понял что дугу сделать ен вариант. ну то есть вот такое https://ibb.co/4ZWgwkD (https://ibb.co/4ZWgwkD) с Rectangle уже не сделать И я еще заметил, я в коде думал что стоит использовать PathMultiline но вы по другому продемонстрировали. Могли бы обьяснить почему? Есть какие то выгоды?
Название: Re: Обьект для отображения циферблата
Отправлено: kambala от Июль 11, 2023, 09:45
Но надеюсь не создало проблем.
конечно не создало, ведь я в твой код даже смотреть не стал :) я же правильно понимаю что для правильного позиционирования тикмарков нужно размер внешней дуги знать?
надо знать радиус окружности, почитай все-таки про полярные координаты И почему вы закоментили обьект внешней дуги Shape и сделали на основа rectangle? Я в начале тоже думал использовать rectangle но понял что дугу сделать ен вариант. ну то есть вот такое https://ibb.co/4ZWgwkD (https://ibb.co/4ZWgwkD) с Rectangle уже не сделать
я думал, тебе надо обычные часы с полной окружностью, через Rectangle ее просто чуть меньше писать. А если тебе надо получить как на картинке (типа спидометр), то да, нужен вариант с Shape. И я еще заметил, я в коде думал что стоит использовать PathMultiline но вы по другому продемонстрировали. Могли бы обьяснить почему? Есть какие то выгоды?
PathMultiline оперирует полигонами (многоугольниками), насколько я вижу, а нам достаточно обычных прямых для отметок. Плюс раз PathMultiline ожидает массив полигонов, то заполнять его придется в рантайме через жс (ну чтоб не хардкодить каждую отметку). кстати, если в моем примере репитер поместить внутрь окружности, то можно убрать добавление координат окружности в Translate (на радиус все равно надо выполнять трансляцию, чтоб переместить начало координат из верхнего левого угла в центр окружности)
Название: Re: Обьект для отображения циферблата
Отправлено: SektorCT от Июль 12, 2023, 14:37
PathMultiline оперирует полигонами (многоугольниками), насколько я вижу, а нам достаточно обычных прямых для отметок. Плюс раз PathMultiline ожидает массив полигонов, то заполнять его придется в рантайме через жс (ну чтоб не хардкодить каждую отметку).
кстати, если в моем примере репитер поместить внутрь окружности, то можно убрать добавление координат окружности в Translate (на радиус все равно надо выполнять трансляцию, чтоб переместить начало координат из верхнего левого угла в центр окружности)
Могли бы пожалуйста пояснить ан счет последнего пункта избавления от Translate? И такой вопрос, если в С++ мы знаем размер самого обьекта Shape что представлен дугой(он динамически менятся будет), мы знаем толщику дуги, то есть ShapePath-strokeWidth:15(так же динамически меняется, 15 это пример), мы можем для каждого тикмарка вычеслить в С++ угол, то как надо сделать расчет этих координат чтобы их контейнером можно было послать в qml? я именно это сейчас пытаюсь выяснить. Ниже код правлен. Item { id: appWin
property int progressWidth: 20 property int trackWidth: 7
property int size: 200
Item { id: mainItem anchors.horizontalCenter: appWin.horizontalCenter anchors.verticalCenter: appWin.verticalCenter height: appWin.size width: appWin.size rotation: 180
Rectangle { id: mianBackgraund height: appWin.size width: appWin.size color: "transparent" radius: height / 2 }
Shape { id: trackShape height: appWin.size width: appWin.size layer.enabled: true layer.samples: 8
ShapePath { id: trackShapePath capStyle: Qt.FlatCap fillColor: "transparent" strokeColor: "#333333" strokeWidth: 7
PathAngleArc { centerX: appWin.size / 2 centerY: appWin.size / 2 radiusX: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, appWin.progressWidth) / 2 radiusY: Math.min(appWin.size, appWin.size) / 2 - Math.max(appWin.trackWidth, appWin.progressWidth) / 2 startAngle: 290 sweepAngle: 320 } } }
Rectangle { id: mainForeground z: 1 anchors.centerIn: mainItem height: 78 width: 78 color: "transparent" border.width: 3 border.color: "#333333" radius: height / 2 }
Repeater { id: repeaterTickMark
model: 9 anchors.fill: parent
delegate: Rectangle { width: model.eWidth height: trackShape.height / 2 - 7 color: "#00000000"
Rectangle { anchors.fill: parent // anchors.topMargin: model.eMajorTickMark ? 160 : 165 // TODO: rewrite // anchors.bottomMargin: model.eMajorTickMark ? 0 : 23 // color: model.eColorTickMark }
transform: [ Rotation { // angle: model.eAngle }, Translate { x: mainItem.width / 2 y: mainItem.height / 2 } ] } }
Rectangle { id: needle width: 5 height: trackShape.height / 2 // TODO: rewrite color: "transparent" antialiasing: true
Rectangle { anchors.fill: parent anchors.topMargin: mainForeground.height / 2 + 1 color: "#ffffff" }
transform: [ Rotation { // angle: pelData.angleNeedle }, Translate { x: mainItem.width / 2 y: mainItem.height / 2 } ] } } }
Название: Re: Обьект для отображения циферблата
Отправлено: kambala от Июль 12, 2023, 21:06
Могли бы пожалуйста пояснить ан счет последнего пункта избавления от Translate? просто засунуть Repeater внутрь элемента circle: Rectangle { id: circle anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: width radius: width / 2 border.color: "red" Repeater { id: tickmarks model: 12 Shape { required property int index readonly property real phi: index * 2 * Math.PI / tickmarks.count readonly property real polarX: Math.cos(phi) readonly property real polarY: Math.sin(phi) x: circle.radius * polarX y: circle.radius * polarY transform: Translate { x: /*circle.x +*/ circle.radius y: /*circle.y +*/ circle.radius } ShapePath { strokeColor: "blue" strokeWidth: 1 PathLine { readonly property real tickmarkLength: index % 3 == 0 ? quarterLength : minuteLength relativeX: -polarX * tickmarkLength relativeY: -polarY * tickmarkLength } } } } }
И такой вопрос, если в С++ мы знаем размер самого обьекта Shape что представлен дугой(он динамически менятся будет), мы знаем толщику дуги, то есть ShapePath-strokeWidth:15(так же динамически меняется, 15 это пример), мы можем для каждого тикмарка вычеслить в С++ угол, то как надо сделать расчет этих координат чтобы их контейнером можно было послать в qml? я именно это сейчас пытаюсь выяснить. угол (phi в моем примере) от толщины дуги не зависит. если ты ее упомянул для вычисления координаты внутреннего края дуги, то надо в вычислениях просто вычитать толщину дуги из радиуса окружности. массив точек можно передать например как QVariantList, https://doc.qt.io/qt-6/qtqml-cppintegration-data.html По коду не очень понимаю куда ты хочешь высунуть свой массив, вместо Repeater?
|