Diferència entre revisions de la pàgina «Ús d'arxius vectorials SVG al QML (Qt6)»
Línia 844: | Línia 844: | ||
* La <code>Botonera </code> continua controlant <code>ampMeter1 </code> i <code>ampMeter3 </code>. | * La <code>Botonera </code> continua controlant <code>ampMeter1 </code> i <code>ampMeter3 </code>. | ||
− | === | + | === Modularització completa amb <code>Vista.qml </code> i <code>Controlador.qml </code> - '''ampMeter00 _09''' === |
Aquesta és la versió més modularitzada, amb el títol "ampMeter00 _09 - Modular". <code>main.qml </code> se simplifica enormement delegant les responsabilitats a dos nous components de nivell superior: ''' <code>Vista </code>''' i ''' <code>Controlador </code>'''. | Aquesta és la versió més modularitzada, amb el títol "ampMeter00 _09 - Modular". <code>main.qml </code> se simplifica enormement delegant les responsabilitats a dos nous components de nivell superior: ''' <code>Vista </code>''' i ''' <code>Controlador </code>'''. | ||
Revisió del 14:16, 10 set 2025
Codis s'aquesta pàgina al GitHub
Contingut
- 1 Explicació de main.qml d'ampMeter00_00
- 2 Canvis Progressius en el Codi
- 2.1 Interactivitat i animació (d'ampMeter00_00 a ampMeter00_01)
- 2.2 Animació automàtica i contínua (d'ampMeter00_01 a ampMeter00_02)
- 2.3 Model de dades ListModel i repetició Repeater (d'ampMeter00_02 a ampMeter00_03)
- 2.4 Component Botonera. QML reutilitzable (d'ampMeter00_03 a ampMeter00_04)
- 2.5 Component AmpMeter (d' ampMeter00_04 a ampMeter00_05)
- 3 Modularitat i separació de responsabilitats
- 3.1 ampMeter00_05 (Punt de referència inicial per a la modularitat i separació de responsabilitats)
- 3.2 ampMeter00_06 (Introducció de Múltiples AmpMeters)
- 3.3 ampMeter00_07 (Incorporació d'un lliscador o slider)
- 3.4 ampMeter00_08 (Modularització del lliscador amb `BarraLliscant.qml`)
- 3.5 ampMeter00_09 (Modularització completa amb `Vista.qml` i `Controlador.qml`)
- 4 Modularitat i separació de responsabilitats
- 4.1 ampMeter00 _05 (Punt de referència inicial per a la modularitat i separació de responsabilitats)
- 4.2 ampMeter00 _06 (Introducció de Múltiples AmpMeters)
- 4.3 ampMeter00 _07 (Incorporació d'un lliscador o slider)
- 4.4 ampMeter00 _08 (Modularització del lliscador amb BarraLliscant.qml )
- 4.5 Modularització completa amb Vista.qml i Controlador.qml - ampMeter00 _09
Explicació de main.qml
d'ampMeter00_00
El fitxer main.qml
de ampMeter00_00
estableix la base d'una aplicació Qt Quick que simula un amperímetre simple [1].
Estructura bàsica de la finestra
import QtQuick
: Importa el mòdul Qt Quick necessari per als elements de la interfície d'usuari [1].Window
: Defineix la finestra principal de l'aplicació [1].width: 640
,height: 480
: Estableix les dimensions de la finestra [1].visible: true
: Fa que la finestra sigui visible quan s'inicia l'aplicació [1].title: qsTr("ampMeter00")
: Defineix el títol de la finestra com "ampMeter00" [1].
Element arrel (Rectangle)
Rectangle { id: root ... }
: Dins la finestra, hi ha unRectangle
anomenatroot
que serveix com a contenidor principal [1].anchors.fill: parent
: Fa que aquest rectangle ompli completament la finestra pare [1].color: "white"
: Estableix el color de fons del rectangle com a blanc [1].
Imatge de fons de l'amperímetre
Image { id: fonsSvg ... }
: S'afegeix un elementImage
per al fons de l'amperímetre [1].source: "qrc:/material/AmpMeterFons_c.svg"
: Carrega la imatge de fons des dels recursos de l'aplicació [1].fillMode: Image.PreserveAspectFit
: Aquesta propietat assegura que la imatge mantingui les seves proporcions originals mentre s'ajusta dins de l'espai disponible [1].anchors.centerIn: parent
: Centra la imatge de fons dins del seu element pare (root
rectangle) [1].
Imatge de l'agulla de l'amperímetre
Image { id: agullaSvg ... }
: S'afegeix un altre elementImage
per representar l'agulla de l'amperímetre [2].source: "qrc:/material/AmpMeterAgulla_c.svg"
: Carrega la imatge de l'agulla [2].fillMode: Image.PreserveAspectFit
: Igual que amb el fons, manté les proporcions de l'agulla [2].anchors.centerIn: parent
: Centra l'agulla dins del seu pare (root
rectangle) [2].rotation: 45
: Estableix una rotació inicial de 45 graus per a l'agulla [2]. Aquest és el punt clau de control en aquesta primera versió, ja que la rotació es defineix de manera estàtica [2].
En resum, ampMeter00_00
presenta una interfície d'usuari bàsica amb un fons i una agulla d'amperímetre, on l'agulla es mostra en una posició fixa de 45 graus [1, 2].
main.qml:
// main.qml import QtQuick // [1] Window { width: 640 height: 480 visible: true title: qsTr("ampMeter00_00") Rectangle { id: root anchors.fill: parent color: "white" Image { id: fonsSvg source: "qrc:/material/AmpMeterFons_c.svg" fillMode: Image.PreserveAspectFit anchors.centerIn: parent } Image { // [2] id: agullaSvg source: "qrc:/material/AmpMeterAgulla_c.svg" fillMode: Image.PreserveAspectFit anchors.centerIn: parent rotation: 45 } } }
Canvis Progressius en el Codi
A continuació, s'expliquen els canvis introduïts en cada iteració del projecte:
Interactivitat i animació (d'ampMeter00_00
a ampMeter00_01
)
La versió ampMeter00_01
introdueix la interactivitat i l'animació per a l'agulla de l'amperímetre.
- Rotació Inicial i Opacitat de l'Agulla:
- La
rotation
inicial de l'agulla (agullaSvg
) es canvia a0
. - S'afegeix una propietat
opacity: 0.8
a l'agulla.
- La
- Animació de Rotació (Behavior):
- S'afegeix un
Behavior on rotation
a l'agulla. Això significa que cada vegada que la propietatrotation
de l'agulla canvia, s'executarà una animació. NumberAnimation { duration: 300; easing.type: Easing.InOutBack }
: L'animació durarà 300 mil·lisegons i utilitzarà una funció d'acceleracióEasing.InOutBack
per a un efecte més dinàmic.
- S'afegeix un
- Botons de Control:
- S'afegeix una fila de botons (
Row
) a la part inferior de la pantalla, anomenadacontrolButtons
. - Aquests botons es centren horitzontalment (
anchors.horizontalCenter: parent.horizontalCenter
) i es col·loquen a 20 píxels de la part inferior (anchors.bottomMargin: 20
). - Hi ha un
spacing: 20
entre els botons. - Cada botó és un
Rectangle
gris amb unwidth
iheight
de 60 píxels i unradius
de 30 (creant cercles). - Cada
Rectangle
conté unText
blanc amb la seva etiqueta (per exemple, "-60", "0", "60"). - Cada botó té una
MouseArea
que detecta clics. Quan es clica, larotation
de l'agulla (agullaSvg.rotation
) es defineix a un valor específic (-45, -20, 0, 20, 45). - El
cursorShape
de laMouseArea
es defineix comQt.PointingHandCursor
per indicar que és un element clicable.
- S'afegeix una fila de botons (
Aquesta versió permet a l'usuari controlar la rotació de l'agulla mitjançant botons, amb una animació suau cada vegada que l'agulla canvia de posició.
main.qml:
// main.qml import QtQuick Window { width: 640 height: 480 visible: true title: qsTr("ampMeter00_01") Rectangle { id: root anchors.fill: parent color: "white" Image { id: fonsSvg source: "qrc:/material/AmpMeterFons_c.svg" fillMode: Image.PreserveAspectFit anchors.centerIn: parent } Image { id: agullaSvg source: "qrc:/material/AmpMeterAgulla_c.svg" fillMode: Image.PreserveAspectFit opacity: 0.8 anchors.centerIn: parent rotation: 0 Behavior on rotation { NumberAnimation { duration: 300 easing.type: Easing.InOutBack } } } Row { id: controlButtons anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: 20 spacing: 20 Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "-60" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = -45 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "-30" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = -20 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "0" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = 0 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "30" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = 20 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "60" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = 45 cursorShape: Qt.PointingHandCursor } } } } }
Animació automàtica i contínua (d'ampMeter00_01
a ampMeter00_02
)
La versió ampMeter00_02
afegeix una animació automàtica i contínua per a l'agulla.
- Canvi de Funció d'Acceleració:
- La funció d'acceleració (
easing.type
) delNumberAnimation
de l'agulla es canvia deEasing.InOutBack
aEasing.InOutQuad
.
- La funció d'acceleració (
- Animació Seqüencial:
- S'afegeix una
SequentialAnimation on rotation
a l'agulla (agullaSvg
). loops: Animation.Infinite
: Aquesta animació es repetirà indefinidament.- Consisteix en dues
RotationAnimation
s:- Una que fa girar l'agulla fins a
45
graus durant1000
mil·lisegons. - Una segona que la fa girar fins a
-45
graus durant500
mil·lisegons.
- Una que fa girar l'agulla fins a
- Aquesta animació fa que l'agulla es mogui contínuament entre
45
i-45
graus quan no es controla manualment.
- S'afegeix una
- Els botons de control manual romanen igual, permetent a l'usuari sobrescriure l'animació automàtica.
main.qml:
// main.qml import QtQuick Window { width: 640 height: 480 visible: true title: qsTr("ampMeter00_02") Rectangle { id: root anchors.fill: parent color: "white" Image { id: fonsSvg source: "qrc:/material/AmpMeterFons_c.svg" fillMode: Image.PreserveAspectFit anchors.centerIn: parent } Image { id: agullaSvg source: "qrc:/material/AmpMeterAgulla_c.svg" fillMode: Image.PreserveAspectFit opacity: 0.8 anchors.centerIn: parent rotation: 0 Behavior on rotation { NumberAnimation { duration: 300 easing.type: Easing.InOutQuad } } SequentialAnimation on rotation { loops: Animation.Infinite RotationAnimation { to: 45 duration: 1000 } RotationAnimation { to: -45 duration: 500 } } } Row { id: controlButtons anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: 20 spacing: 20 Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "-60" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = -45 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "-30" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = -20 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "0" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = 0 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "30" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = 20 cursorShape: Qt.PointingHandCursor } } Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: "60" color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = 45 cursorShape: Qt.PointingHandCursor } } } } }
Model de dades ListModel i repetició Repeater (d'ampMeter00_02
a ampMeter00_03
)
La versió ampMeter00_03
refactoritza el codi dels botons utilitzant un model de dades (ListModel
) i un Repeater
, millorant la modularitat i la facilitat de manteniment.
- Eliminació de l'Animació Seqüencial:
- La
SequentialAnimation on rotation
s'elimina.
- La
- Model de Dades per a Botons (
ListModel
):- Es defineix un
ListModel
ambid: buttonModel
. - Conté
ListElement
s, cadascun amb unalabel
(el text del botó) i unvalue
(el valor de rotació corresponent). Per exemple,{ label: "-60"; value: -45 }
.
- Es defineix un
- Generació de Botons amb
Repeater
:- En lloc de declarar cada botó individualment, s'utilitza un
Repeater
ambmodel: buttonModel
dins delRow
decontrolButtons
. - El
Repeater
crea unRectangle
per a cada element del model. - El
Text
de cada botó utilitzamodel.label
i l'esdevenimentonClicked
de laMouseArea
utilitzaagullaSvg.rotation = model.value
.
- En lloc de declarar cada botó individualment, s'utilitza un
Aquest canvi redueix la duplicitat de codi i facilita l'addició o modificació de botons en el futur.
main.qml:
// main.qml import QtQuick Window { width: 640 height: 480 visible: true title: qsTr("ampMeter00_03") Rectangle { id: root anchors.fill: parent color: "white" Image { id: fonsSvg source: "qrc:/material/AmpMeterFons_c.svg" fillMode: Image.PreserveAspectFit anchors.centerIn: parent } Image { id: agullaSvg source: "qrc:/material/AmpMeterAgulla_c.svg" fillMode: Image.PreserveAspectFit opacity: 0.8 anchors.centerIn: parent rotation: 0 Behavior on rotation { NumberAnimation { duration: 300 easing.type: Easing.InOutBack } } } ListModel { id: buttonModel ListElement { label: "-60"; value: -45 } ListElement { label: "-30"; value: -20 } ListElement { label: "0"; value: 0 } ListElement { label: "30"; value: 20 } ListElement { label: "60"; value: 45 } } Row { id: controlButtons anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: 20 spacing: 20 Repeater { model: buttonModel Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: model.label color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: agullaSvg.rotation = model.value cursorShape: Qt.PointingHandCursor } } } } } }
Component Botonera. QML reutilitzable (d'ampMeter00_03
a ampMeter00_04
)
La versió ampMeter00_04
millora la modularitat extraient la lògica dels botons en un component QML reutilitzable anomenat Botonera.qml
.
- Creació del Component
Botonera.qml
:- Es crea un nou fitxer
Botonera.qml
que conté la implementació delRow
i elRepeater
per als botons. - Aquest component té una propietat
property alias model: repeater.model
que permet al component pare passar-li elListModel
. - Defineix un
signal buttonClicked(real value)
que s'emet quan es prem un botó, enviant elvalue
de rotació. - Dins del
MouseArea
de cada botó,onClicked
ara cridabotonera.buttonClicked(model.value)
.
- Es crea un nou fitxer
- Ús del Component
Botonera
amain.qml
:- A
main.qml
, elRow
original de botons es reemplaça per una instància del nou componentBotonera
. controlButtons
(la instància deBotonera
) rep elbuttonModel
mitjançantmodel: buttonModel
.- Captura el senyal
onButtonClicked
del componentBotonera
i actualitza la rotació de l'agulla ambagullaSvg.rotation = value
.
- A
Aquesta refactorització permet reutilitzar el bloc de botons en altres parts de l'aplicació o en altres projectes, fent el codi de main.qml
més net i centrat en la composició.
main.qml:
// main.qml import QtQuick Window { width: 640 height: 480 visible: true title: qsTr("ampMeter00_04") Rectangle { id: root anchors.fill: parent color: "white" Image { id: fonsSvg source: "qrc:/material/AmpMeterFons_c.svg" fillMode: Image.PreserveAspectFit anchors.centerIn: parent } Image { id: agullaSvg source: "qrc:/material/AmpMeterAgulla_c.svg" fillMode: Image.PreserveAspectFit opacity: 0.8 anchors.centerIn: parent rotation: 0 Behavior on rotation { NumberAnimation { duration: 300 easing.type: Easing.InOutBack } } } ListModel { id: buttonModel ListElement { label: "-60"; value: -45 } ListElement { label: "-30"; value: -20 } ListElement { label: "0"; value: 0 } ListElement { label: "30"; value: 20 } ListElement { label: "60"; value: 45 } } Botonera { id: controlButtons anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: 20 model: buttonModel onButtonClicked: function(value) { agullaSvg.rotation = value } } } }
Botonera.qml:
// Botonera.qml import QtQuick Row { id: botonera spacing: 20 property alias model: repeater.model signal buttonClicked(real value) Repeater { id: repeater Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: model.label color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: botonera.buttonClicked(model.value) cursorShape: Qt.PointingHandCursor } } } }
Component AmpMeter (d' ampMeter00_04
a ampMeter00_05
)
La versió ampMeter00_05
continua amb la refactorització, encapsulant la lògica de l'amperímetre (fons i agulla) en un component QML propi anomenat AmpMeter.qml
.
- Creació del Component
AmpMeter.qml
:- Es crea un nou fitxer
AmpMeter.qml
que conté el fons (fonsSvg
) i l'agulla (agullaSvg
) de l'amperímetre. - Exposa una propietat
property alias rotation: agullaSvg.rotation
per permetre al component pare controlar directament la rotació de l'agulla interna. - També defineix propietats per personalitzar les imatges de fons i agulla (
fonsSource
,agullaSource
) i l'opacitat de l'agulla (agullaOpacity
). - El
Behavior on rotation
per a l'agulla es troba ara dins d'aquest component.
- Es crea un nou fitxer
- Ús del Component
AmpMeter
amain.qml
:- A
main.qml
, les dues imatges originals (fonsSvg
iagullaSvg
) es reemplacen per una instància del nou componentAmpMeter
. - L'esdeveniment
onButtonClicked
delBotonera
ara actualitza la rotació de l'amperímetre ambampMeter.rotation = value
, utilitzant la propietat exposada pel componentAmpMeter
.
- A
Aquesta última refactorització crea un component autònom (AmpMeter
) que conté tota la lògica i la presentació de l'amperímetre, fent que main.qml
sigui extremadament simple i es basi en la composició de components.
main.qml:
// main.qml import QtQuick Window { width: 640 height: 480 visible: true title: qsTr("ampMeter00_05") Rectangle { id: root anchors.fill: parent color: "white" AmpMeter { id: ampMeter anchors.centerIn: parent width: parent.width height: parent.height } ListModel { id: buttonModel ListElement { label: "-60"; value: -45 } ListElement { label: "-30"; value: -20 } ListElement { label: "0"; value: 0 } ListElement { label: "30"; value: 20 } ListElement { label: "60"; value: 45 } } Botonera { id: controlButtons anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: 20 model: buttonModel onButtonClicked: function(value) { ampMeter.rotation = value } } } }
Botonera.qml:
// Botonera.qml import QtQuick Row { id: botonera spacing: 20 property alias model: repeater.model signal buttonClicked(real value) Repeater { id: repeater Rectangle { width: 60 height: 60 radius: 30 color: "gray" Text { anchors.centerIn: parent text: model.label color: "white" font.pixelSize: 14 font.bold: true } MouseArea { anchors.fill: parent onClicked: botonera.buttonClicked(model.value) cursorShape: Qt.PointingHandCursor } } } }
AmpMeter.qml:
// AmpMeter.qml import QtQuick Item { id: ampMeter property alias rotation: agullaSvg.rotation property string fonsSource: "qrc:/material/AmpMeterFons_c.svg" property string agullaSource: "qrc:/material/AmpMeterAgulla_c.svg" property real agullaOpacity: 0.8 Image { id: fonsSvg source: ampMeter.fonsSource fillMode: Image.PreserveAspectFit anchors.centerIn: parent } Image { id: agullaSvg source: ampMeter.agullaSource fillMode: Image.PreserveAspectFit opacity: ampMeter.agullaOpacity anchors.centerIn: parent rotation: 0 Behavior on rotation { NumberAnimation { duration: 300 easing.type: Easing.InOutBack } } } }
Modularitat i separació de responsabilitats
L'evolució dels codis, prenent com a referència inicial ampMeter00_05, mostra una progressió cap a una major modularitat i control de la interfície d'usuari a través de la introducció de nous components QML i la separació de responsabilitats.
ampMeter00_05 (Punt de referència inicial per a la modularitat i separació de responsabilitats)
En aquesta versió, el fitxer `main.qml` configura una finestra amb un `Rectangle` blanc. Inclou dos components QML personalitzats: `AmpMeter` i `Botonera`.
- El component `AmpMeter` es centra a la pantalla i gestiona la visualització del fons i l'agulla d'un amperímetre. La rotació de l'agulla es controla mitjançant una propietat `rotation` exposada i animada amb una `NumberAnimation` que utilitza `Easing.InOutBack`.
- El component `Botonera` es col·loca a la part inferior i es centra horitzontalment. S'alimenta d'un `ListModel` anomenat `buttonModel` que conté les etiquetes i els valors de rotació per als botons. Quan un botó és clicat, emet un senyal `buttonClicked` amb el seu valor, i aquest valor s'assigna a la propietat `rotation` del component `ampMeter`.
ampMeter00_06 (Introducció de Múltiples AmpMeters)
Aquesta versió es titula "ampMeter00_06 - Triple" i incrementa l'amplada de la finestra a 960 píxels. La millora principal és la capacitat de mostrar i controlar tres components `AmpMeter` simultàniament.
- Els tres `AmpMeter` (`ampMeter1`, `ampMeter2`, `ampMeter3`) estan organitzats en una `Row` (fila) centrada, amb un desplaçament vertical per deixar espai als controls. Cadascun té dimensions i escala definides de manera individual (per exemple, `ampMeter1` i `ampMeter3` amb `scale: 0.5`, i `ampMeter2` amb `scale: 0.8`).
- El `ListModel` dels botons es manté, però el gestor `onButtonClicked` a `main.qml` ara actualitza la propietat `rotation` dels tres components `AmpMeter` de forma idèntica (`ampMeter1.rotation = value`, `ampMeter2.rotation = value`, `ampMeter3.rotation = value`).
- Els components `Botonera.qml` i `AmpMeter.qml` conserven la seva estructura anterior.
ampMeter00_07 (Incorporació d'un lliscador o slider)
Continuant amb la configuració "Triple", aquesta versió afegeix un nou element de control: un slider (lliscador).
- Es mantenen els tres components `AmpMeter` en fila.
- S'introdueix un `Column` (`sliderContainer`) que conté la lògica del slider, situat just a sobre dels botons de control.
- Aquest slider consisteix en un `Rectangle` que serveix de pista i un altre `Rectangle` (`sliderHandle`) que fa de mànec lliscant.
- Un `MouseArea` permet arrossegar el `sliderHandle` només en l'eix X (`drag.axis: Drag.XAxis`).
- El gestor `onPositionChanged` calcula un valor (`value`) entre -60 i +60, el converteix en un angle de rotació entre -45 i +45, i l'aplica exclusivament a `ampMeter2` (`ampMeter2.rotation = rotation`).
- Es mostren etiquetes per als valors extrems (-60 i +60) i el valor actual del slider.
- La `Botonera` ara només controla `ampMeter1` i `ampMeter3`, ja que `ampMeter2` és controlat pel nou slider.
ampMeter00_08 (Modularització del lliscador amb `BarraLliscant.qml`)
En aquesta etapa, la lògica del lliscador es refactoritza en un component QML independent anomenat `BarraLliscant.qml`.
- A `main.qml`, el `sliderContainer` es reemplaça per una instància del component `BarraLliscant`.
- `BarraLliscant.qml` encapsula tota la interfície i la lògica del lliscador, incloent les etiquetes, la pista, el mànec, el càlcul del valor i la visualització del valor actual.
- Defineix propietats configurables com `minValue`, `maxValue`, `sliderWidth` i `sliderHeight`.
- Emet un senyal `valueChanged(real value)` quan el valor del lliscador canvia.
- A `main.qml`, el senyal `onValueChanged` de la `BarraLliscant` s'utilitza per convertir el valor del lliscador en rotació i aplicar-lo a `ampMeter2`.
- El component `AmpMeter.qml` introdueix propietats `fondoSource` i `agullaSource` per personalitzar les imatges, tot i que inicialment mantenen els mateixos valors per defecte.
- La `Botonera` continua controlant `ampMeter1` i `ampMeter3`.
ampMeter00_09 (Modularització completa amb `Vista.qml` i `Controlador.qml`)
Aquesta és la versió més modularitzada, amb el títol "ampMeter00_09 - Modular". `main.qml` se simplifica enormement delegant les responsabilitats a dos nous components de nivell superior: `Vista` i `Controlador`.
- `Vista.qml`: Aquest component agrupa els tres components `AmpMeter` en una `Row`.
- Exposa les propietats `ampMeter1Rotation`, `ampMeter2Rotation`, `ampMeter3Rotation` com àlies per controlar la rotació de cada `AmpMeter` des del seu pare.
- Permet personalitzar l'escala de cada `AmpMeter` (`ampMeter1Scale`, `ampMeter2Scale`, `ampMeter3Scale`).
- Una novetat important és que cada `AmpMeter` dins de la `Vista` pot tenir un tipus d'animació d'easing diferent (`Easing.InOutQuad`, `Easing.InOutBack`, `Easing.InOutCubic`), cosa que requereix una nova propietat `easingType` a `AmpMeter.qml`.
- `Controlador.qml`: Aquest component agrupa la `BarraLliscant` i la `Botonera` en una `Column`.
- Rep el `buttonModel` per a la `Botonera`.
- Emet senyals `sliderValueChanged(real value)` i `buttonClicked(real value)` per comunicar els esdeveniments dels seus components interns al seu pare.
- Connecta el senyal `onValueChanged` de la seva `BarraLliscant` interna al seu propi `sliderValueChanged`.
- Connecta el senyal `onButtonClicked` de la seva `Botonera` interna al seu propi `buttonClicked`.
- A `main.qml`, el gestor `onSliderValueChanged` del `Controlador` ara actualitza `vista.ampMeter2Rotation`.
- El gestor `onButtonClicked` del `Controlador` actualitza `vista.ampMeter1Rotation` i `vista.ampMeter3Rotation`.
En resum, l'evolució del codi il·lustra una transició des d'una aplicació monolítica amb la lògica de control i vista al fitxer `main.qml`, cap a una estructura modular on components reutilitzables (`AmpMeter`, `Botonera`, `BarraLliscant`) s'organitzen en components de més alt nivell (`Vista`, `Controlador`), millorant la mantenibilitat i la claredat del codi.
Modularitat i separació de responsabilitats
L'evolució dels codis, prenent com a referència inicial ampMeter00 _05, mostra una progressió cap a una major modularitat i control de la interfície d'usuari a través de la introducció de nous components QML i la separació de responsabilitats.
ampMeter00 _05 (Punt de referència inicial per a la modularitat i separació de responsabilitats)
En aquesta versió, el fitxer main.qml
configura una finestra amb un Rectangle
blanc. Inclou dos components QML personalitzats: AmpMeter
i Botonera
.
- El component
AmpMeter
es centra a la pantalla i gestiona la visualització del fons i l'agulla d'un amperímetre. La rotació de l'agulla es controla mitjançant una propietatrotation
exposada i animada amb unaNumberAnimation
que utilitzaEasing.InOutBack
. - El component
Botonera
es col·loca a la part inferior i es centra horitzontalment. S'alimenta d'unListModel
anomenatbuttonModel
que conté les etiquetes i els valors de rotació per als botons. Quan un botó és clicat, emet un senyalbuttonClicked
amb el seu valor, i aquest valor s'assigna a la propietatrotation
del componentampMeter
.
ampMeter00 _06 (Introducció de Múltiples AmpMeters)
Aquesta versió es titula "ampMeter00 _06 - Triple" i incrementa l'amplada de la finestra a 960 píxels. La millora principal és la capacitat de mostrar i controlar tres components AmpMeter
simultàniament.
- Els tres
AmpMeter
(ampMeter1
,ampMeter2
,ampMeter3
) estan organitzats en unaRow
(fila) centrada, amb un desplaçament vertical per deixar espai als controls. Cadascun té dimensions i escala definides de manera individual (per exemple,ampMeter1
iampMeter3
ambscale: 0.5
, iampMeter2
ambscale: 0.8
). - El
ListModel
dels botons es manté, però el gestoronButtonClicked
amain.qml
ara actualitza la propietatrotation
dels tres componentsAmpMeter
de forma idèntica (ampMeter1.rotation = value
,ampMeter2.rotation = value
,ampMeter3.rotation = value
). - Els components
Botonera.qml
iAmpMeter.qml
conserven la seva estructura anterior.
ampMeter00 _07 (Incorporació d'un lliscador o slider)
Continuant amb la configuració "Triple", aquesta versió afegeix un nou element de control: un slider (lliscador).
- Es mantenen els tres components
AmpMeter
en fila. - S'introdueix un
Column
(sliderContainer
) que conté la lògica del slider, situat just a sobre dels botons de control. - Aquest slider consisteix en un
Rectangle
que serveix de pista i un altreRectangle
(sliderHandle
) que fa de mànec lliscant. - Un
MouseArea
permet arrossegar elsliderHandle
només en l'eix X (drag.axis: Drag.XAxis
). - El gestor
onPositionChanged
calcula un valor (value
) entre -60 i +60, el converteix en un angle de rotació entre -45 i +45, i l'aplica exclusivament aampMeter2
(ampMeter2.rotation = rotation
). - Es mostren etiquetes per als valors extrems (-60 i +60) i el valor actual del slider.
- La
Botonera
ara només controlaampMeter1
iampMeter3
, ja queampMeter2
és controlat pel nou slider.
ampMeter00 _08 (Modularització del lliscador amb BarraLliscant.qml
)
En aquesta etapa, la lògica del lliscador es refactoritza en un component QML independent anomenat BarraLliscant.qml
.
- A
main.qml
, elsliderContainer
es reemplaça per una instància del componentBarraLliscant
. BarraLliscant.qml
encapsula tota la interfície i la lògica del lliscador, incloent les etiquetes, la pista, el mànec, el càlcul del valor i la visualització del valor actual.- Defineix propietats configurables com
minValue
,maxValue
,sliderWidth
isliderHeight
. - Emet un senyal
valueChanged(real value)
quan el valor del lliscador canvia. - A
main.qml
, el senyalonValueChanged
de laBarraLliscant
s'utilitza per convertir el valor del lliscador en rotació i aplicar-lo aampMeter2
. - El component
AmpMeter.qml
introdueix propietatsfondoSource
iagullaSource
per personalitzar les imatges, tot i que inicialment mantenen els mateixos valors per defecte. - La
Botonera
continua controlantampMeter1
iampMeter3
.
Modularització completa amb Vista.qml
i Controlador.qml
- ampMeter00 _09
Aquesta és la versió més modularitzada, amb el títol "ampMeter00 _09 - Modular". main.qml
se simplifica enormement delegant les responsabilitats a dos nous components de nivell superior: Vista
i Controlador
.
-
Vista.qml
: Aquest component agrupa els tres componentsAmpMeter
en unaRow
.- Exposa les propietats
ampMeter1Rotation
,ampMeter2Rotation
,ampMeter3Rotation
com àlies per controlar la rotació de cadaAmpMeter
des del seu pare. - Permet personalitzar l'escala de cada
AmpMeter
(ampMeter1Scale
,ampMeter2Scale
,ampMeter3Scale
). - Una novetat important és que cada
AmpMeter
dins de laVista
pot tenir un tipus d'animació deasing diferent (Easing.InOutQuad
,Easing.InOutBack
,Easing.InOutCubic
), cosa que requereix una nova propietateasingType
aAmpMeter.qml
.
- Exposa les propietats
-
Controlador.qml
: Aquest component agrupa laBarraLliscant
i laBotonera
en unaColumn
.- Rep el
buttonModel
per a laBotonera
. - Emet senyals
sliderValueChanged(real value)
ibuttonClicked(real value)
per comunicar els esdeveniments dels seus components interns al seu pare. - Connecta el senyal
onValueChanged
de la sevaBarraLliscant
interna al seu propisliderValueChanged
. - Connecta el senyal
onButtonClicked
de la sevaBotonera
interna al seu propibuttonClicked
.
- Rep el
- A
main.qml
, el gestoronSliderValueChanged
delControlador
ara actualitzavista.ampMeter2Rotation
. - El gestor
onButtonClicked
delControlador
actualitzavista.ampMeter1Rotation
ivista.ampMeter3Rotation
.
En resum, l'evolució del codi il·lustra una transició des d'una aplicació monolítica amb la lògica de control i vista al fitxer main.qml
, cap a una estructura modular on components reutilitzables ( AmpMeter
, Botonera
, BarraLliscant
) s'organitzen en components de més alt nivell ( Vista
, Controlador
), millorant la mantenibilitat i la claredat del codi.