Teoria A08 - Joc Pedra, Paper, Tisora amb Qt Quick

Índex

  1. Introducció a Qt Quick Controls
  2. Navegació amb SwipeView
  3. Persistència de dades amb Settings
  4. Sistema de recursos Qt (qrc)
  5. Generació aleatòria en QML
  6. Propietats custom i signals
  7. States i Transitions
  8. Mode dia/nit - Gestió de temes
  9. Referències i recursos addicionals

1. Introducció a Qt Quick Controls

Objectiu

Presenta els controls bàsics de Qt Quick necessaris per crear interfícies d'usuari funcionals i interactives.

Conceptes clau

Qt Quick Controls proporciona un conjunt de components d'interfície d'usuari llests per emprar. Els més rellevants per al nostre projecte són:

Sistema d'anchors

Qt Quick empra un sistema d'anchors per posicionar elements de manera relativa:

Rectangle {
    anchors.fill: parent          // Omple tot l'espai del pare
    anchors.centerIn: parent      // Centra dins del pare
    anchors.left: parent.left     // Alinea a l'esquerra
    anchors.margins: 10           // Marges a tots els costats
}

Exemple complet: Botó simple

import QtQuick 2.15
import QtQuick.Controls 6.5

Window {
    width: 400
    height: 300
    visible: true
    title: "Exemple Botó"
    
    Button {
        text: "Prem-me"
        anchors.centerIn: parent
        
        onClicked: {
            console.log("Botó clicat!")
        }
    }
}

Exemple: Imatge amb àrea clicable

Rectangle {
    width: 100
    height: 100
    color: "lightblue"
    
    Image {
        source: "imatge.png"
        anchors.fill: parent
        anchors.margins: 10
        fillMode: Image.PreserveAspectFit
        
        MouseArea {
            anchors.fill: parent
            onClicked: {
                console.log("Imatge clicada")
            }
        }
    }
}

Propietats essencials

Cada component té propietats que en defineixen l'aspecte i comportament:

Text {
    text: "Hola món"
    color: "blue"
    font.pixelSize: 24
    font.bold: true
    horizontalAlignment: Text.AlignHCenter
}

Rectangle {
    width: 200
    height: 100
    color: "green"
    radius: 10              // Arrodoniment cantonades
    border.color: "black"
    border.width: 2
}

Punts importants


2. Navegació amb SwipeView

Objectiu

Implementa navegació entre múltiples pantalles dins una aplicació mitjançant lliscament (swipe) o canvi programàtic.

Conceptes clau

Exemple bàsic de SwipeView

import QtQuick 2.15
import QtQuick.Controls 6.5

Window {
    width: 640
    height: 480
    visible: true
    
    SwipeView {
        id: vista
        anchors.fill: parent
        currentIndex: 0
        
        Page {
            id: pagina1
            background: Rectangle { color: "lightblue" }
            
            Label {
                text: "Pàgina 1"
                anchors.centerIn: parent
                font.pixelSize: 36
            }
        }
        
        Page {
            id: pagina2
            background: Rectangle { color: "lightgreen" }
            
            Label {
                text: "Pàgina 2"
                anchors.centerIn: parent
                font.pixelSize: 36
            }
        }
    }
    
    PageIndicator {
        count: vista.count
        currentIndex: vista.currentIndex
        anchors.bottom: parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter
    }
}

Navegació programàtica

Canvia de pàgina des del codi sense necessitat de lliscar:

Button {
    text: "Anar a configuració"
    onClicked: {
        vista.currentIndex = 1
    }
}

Button {
    text: "Tornar al joc"
    onClicked: {
        vista.currentIndex = 0
    }
}

PageIndicator personalitzat

Pots personalitzar l'aspecte dels indicadors de pàgina:

PageIndicator {
    id: indicator
    count: vista.count
    currentIndex: vista.currentIndex
    
    anchors.bottom: vista.bottom
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.bottomMargin: 20
    
    delegate: Rectangle {
        width: 8
        height: 8
        radius: 4
        color: index === indicator.currentIndex ? "blue" : "gray"
    }
}

Transicions entre pàgines

SwipeView permet afegir animacions en canviar de pàgina:

SwipeView {
    id: vista
    
    // Desactiva animació de swipe per fer-ne una custom
    interactive: true
    
    Behavior on currentIndex {
        NumberAnimation {
            duration: 300
            easing.type: Easing.InOutQuad
        }
    }
}

Punts importants


3. Persistència de dades amb Settings

Objectiu

Desa i recupera dades entre execucions de l'aplicació de manera senzilla i automàtica.

Conceptes clau

Qt Labs Settings proporciona un component per emmagatzemar preferències de l'aplicació:

Importació necessària

import Qt.labs.settings 1.0

Exemple bàsic

import QtQuick 2.15
import QtQuick.Controls 6.5
import Qt.labs.settings 1.0

Window {
    width: 400
    height: 300
    visible: true
    
    Settings {
        id: configuracio
        category: "estadistiques"
        
        property int partidesGuanyades: 0
        property int partidesPerdudes: 0
    }
    
    Column {
        anchors.centerIn: parent
        spacing: 10
        
        Label {
            text: "Guanyades: " + configuracio.partidesGuanyades
        }
        
        Label {
            text: "Perdudes: " + configuracio.partidesPerdudes
        }
        
        Button {
            text: "He guanyat!"
            onClicked: {
                configuracio.partidesGuanyades += 1
            }
        }
        
        Button {
            text: "He perdut"
            onClicked: {
                configuracio.partidesPerdudes += 1
            }
        }
        
        Button {
            text: "Reset"
            onClicked: {
                configuracio.partidesGuanyades = 0
                configuracio.partidesPerdudes = 0
            }
        }
    }
}

Múltiples categories

Pots organitzar configuracions en diferents grups:

Settings {
    id: configJoc
    category: "joc"
    
    property string dificultat: "mitja"
    property bool soActivat: true
}

Settings {
    id: configVisual
    category: "visual"
    
    property bool modeFosc: false
    property int midaFont: 14
}

Tipus de dades suportats

Settings pot desar diversos tipus de dades:

Settings {
    id: config
    
    // Tipus bàsics
    property int numero: 42
    property real decimal: 3.14
    property bool boolea: true
    property string text: "Hola"
    
    // Data i hora
    property date data: new Date()
    
    // Arrays (com a string JSON)
    property string llistaJSON: "[]"
    
    function desaLlista(array) {
        llistaJSON = JSON.stringify(array)
    }
    
    function carregaLlista() {
        return JSON.parse(llistaJSON)
    }
}

Alias per propietats externes

Pots enllaçar propietats de Settings amb propietats d'altres components:

Settings {
    id: config
    property alias modeFosc: temaManager.darkMode
    property alias volum: reproductor.volume
}

Item {
    id: temaManager
    property bool darkMode: false
}

Item {
    id: reproductor
    property real volume: 0.5
}

On es desen les dades?

Les dades es desen en ubicacions específiques del sistema:

Punts importants


4. Sistema de recursos Qt (qrc)

Objectiu

Empaqueta recursos (imatges, fonts, fitxers QML) dins l'executable de l'aplicació per facilitar-ne la distribució i l'accés.

Conceptes clau

El sistema de recursos de Qt permet incloure fitxers dins l'aplicació:

Creació d'un fitxer .qrc

Crea un fitxer resources.qrc:

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
        <file>GamePage.qml</file>
        <file>ConfigPage.qml</file>
    </qresource>
</RCC>

Organització amb prefix i alias

Pots organitzar recursos en grups lògics:

<RCC>
    <qresource prefix="/">
        <!-- Fitxers QML -->
        <file>main.qml</file>
        <file>GamePage.qml</file>
        <file>ConfigPage.qml</file>
        
        <!-- Imatges amb alias -->
        <file alias="img/rock.png">imatges/pedra_gran.png</file>
        <file alias="img/paper.png">imatges/paper_gran.png</file>
        <file alias="img/scissors.png">imatges/tisora_gran.png</file>
        <file alias="img/settings.png">icons/configuracio.png</file>
    </qresource>
</RCC>

Accés als recursos des de QML

Pots accedir als recursos de diverses maneres:

// Amb prefix qrc:
Image {
    source: "qrc:/img/rock.png"
}

// Sense prefix (també funciona):
Image {
    source: "/img/paper.png"
}

// Ruta relativa si està al mateix prefix:
Image {
    source: "img/scissors.png"
}

Múltiples prefixos

Pots crear diferents prefixos per organitzar millor:

<RCC>
    <qresource prefix="/qml">
        <file>main.qml</file>
        <file>GamePage.qml</file>
    </qresource>
    
    <qresource prefix="/images">
        <file>rock.png</file>
        <file>paper.png</file>
    </qresource>
    
    <qresource prefix="/fonts">
        <file>Roboto-Regular.ttf</file>
    </qresource>
</RCC>

Accés amb prefixos múltiples:

Image {
    source: "qrc:/images/rock.png"
}

FontLoader {
    source: "qrc:/fonts/Roboto-Regular.ttf"
}

Configuració al CMakeLists.txt

Afegeix el fitxer .qrc al projecte:

set(PROJECT_SOURCES
    main.cpp
    resources.qrc
)

qt_add_executable(appname
    ${PROJECT_SOURCES}
)

Avantatges dels recursos

Punts importants


5. Generació aleatòria en QML

Objectiu

Genera valors aleatoris per implementar la lògica del joc on l'aplicació ha de triar una opció de manera impredictible.

Conceptes clau

JavaScript/QML proporciona funcions matemàtiques per generar aleatorietat:

Generació bàsica

// Valor aleatori entre 0 i 1
var aleatori = Math.random()
console.log(aleatori)  // Ex: 0.7382...

// Valor aleatori entre 0 i N-1
var valor = Math.floor(Math.random() * 3)
console.log(valor)  // 0, 1 o 2

Funció per triar opció aleatòria

function opcioAleatoria() {
    var opcions = ["Pedra", "Paper", "Tisora"]
    var index = Math.floor(Math.random() * opcions.length)
    return opcions[index]
}

// Ús
var jugadaApp = opcioAleatoria()
console.log("Aplicació juga:", jugadaApp)

Exemple complet per al joc RPS

Item {
    id: joc
    
    function jugadaAleatoria() {
        var opcions = ["R", "P", "S"]
        return opcions[Math.floor(Math.random() * opcions.length)]
    }
    
    function imatgePerOpcio(opcio) {
        if (opcio === "R") return "qrc:/img/rock.png"
        if (opcio === "P") return "qrc:/img/paper.png"
        if (opcio === "S") return "qrc:/img/scissors.png"
        return ""
    }
    
    MouseArea {
        onClicked: {
            var jugadaUsuari = "R"  // Exemple
            var jugadaApp = joc.jugadaAleatoria()
            
            console.log("Usuari:", jugadaUsuari)
            console.log("App:", jugadaApp)
            
            var imatgeApp = joc.imatgePerOpcio(jugadaApp)
            console.log("Imatge:", imatgeApp)
        }
    }
}

Rang personalitzat

Per generar valors en un rang específic:

// Valor entre min i max (inclosos)
function aleatoriBetween(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min
}

// Exemples
var dau = aleatoriBetween(1, 6)        // 1-6
var percentatge = aleatoriBetween(0, 100)  // 0-100

Selecció aleatòria amb pesos

Si vols que certes opcions siguin més probables:

function opcioAmbPes() {
    var aleatori = Math.random()
    
    // Pedra: 50%, Paper: 30%, Tisora: 20%
    if (aleatori < 0.5) return "R"
    if (aleatori < 0.8) return "P"
    return "S"
}

Barrejar un array

Per barrejar elements d'un array (algorisme Fisher-Yates):

function barrejar(array) {
    var copia = array.slice()  // Copia per no modificar l'original
    
    for (var i = copia.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1))
        var temp = copia[i]
        copia[i] = copia[j]
        copia[j] = temp
    }
    
    return copia
}

// Ús
var opcions = ["R", "P", "S"]
var barrejat = barrejar(opcions)
console.log(barrejat)  // Ex: ["S", "R", "P"]

Punts importants


6. Propietats custom i signals

Objectiu

Comunica dades i esdeveniments entre components QML de manera eficient i desacoblada.

Conceptes clau

QML permet crear propietats i signals personalitzats per facilitar la comunicació:

Propietats personalitzades

Item {
    id: boto
    
    // Propietats custom
    property string texteBoto: "Clic aquí"
    property color colorFons: "blue"
    property int comptador: 0
    
    Rectangle {
        color: boto.colorFons
        
        Text {
            text: boto.texteBoto + " (" + boto.comptador + ")"
        }
    }
}

Signals personalitzats

Item {
    id: boto
    
    // Signal sense paràmetres
    signal clicat()
    
    // Signal amb paràmetres
    signal valorCanviat(int nouValor)
    signal botoActivat(string missatge, bool estat)
    
    MouseArea {
        anchors.fill: parent
        onClicked: {
            boto.clicat()  // Emet el signal
            boto.valorCanviat(42)
            boto.botoActivat("Hola", true)
        }
    }
}

Escoltar signals amb Connections

// Component fill
Item {
    id: botoPersonalitzat
    
    signal botoPremut(string missatge)
    
    MouseArea {
        anchors.fill: parent
        onClicked: {
            botoPersonalitzat.botoPremut("S'ha clicat!")
        }
    }
}

// Component pare
Item {
    BotoPersonalitzat {
        id: elMeuBoto
        
        Connections {
            function onBotoPremut(missatge) {
                console.log("Rebut:", missatge)
            }
        }
    }
}

Alias de propietats

Els alias exposen propietats internes sense duplicar dades:

Rectangle {
    id: contenidor
    
    // Alias a propietats internes
    property alias texteTitol: titol.text
    property alias colorFons: fons.color
    
    Rectangle {
        id: fons
        color: "white"
        anchors.fill: parent
    }
    
    Text {
        id: titol
        text: "Títol"
    }
}

// Ús
Contenidor {
    texteTitol: "Nou títol"  // Modifica directament titol.text
    colorFons: "lightblue"   // Modifica directament fons.color
}

Exemple complet: Component de botó RPS

// BotoRPS.qml
import QtQuick 2.15

Item {
    id: botoRPS
    
    // Propietats públiques
    property string opcio: "R"
    property string imatgeSource: ""
    
    // Signal personalitzat
    signal opcioSeleccionada(string opcio, string imatge)
    
    width: 80
    height: 80
    
    Rectangle {
        id: fons
        anchors.fill: parent
        color: "orange"
        radius: 40
        
        Image {
            source: botoRPS.imatgeSource
            anchors.fill: parent
            anchors.margins: 10
            fillMode: Image.PreserveAspectFit
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: {
                botoRPS.opcioSeleccionada(botoRPS.opcio, botoRPS.imatgeSource)
            }
        }
    }
}

Ús del component:

Row {
    spacing: 20
    
    BotoRPS {
        opcio: "R"
        imatgeSource: "qrc:/img/rock.png"
        
        Connections {
            function onOpcioSeleccionada(opcio, imatge) {
                console.log("Seleccionat:", opcio)
                // Lògica del joc...
            }
        }
    }
    
    BotoRPS {
        opcio: "P"
        imatgeSource: "qrc:/img/paper.png"
        
        Connections {
            function onOpcioSeleccionada(opcio, imatge) {
                console.log("Seleccionat:", opcio)
            }
        }
    }
}

Propietats readonly

Pots crear propietats que només es poden llegir externament:

Item {
    readonly property int resultat: calcul()
    
    function calcul() {
        return 42
    }
}

Signals amb múltiples paràmetres

Item {
    signal resultatsJoc(string guanyador, int puntsUsuari, int puntsApp, string jugadaUsuari, string jugadaApp)
    
    function finalitzarJoc() {
        resultatsJoc("usuari", 5, 3, "R", "S")
    }
}

Punts importants


7. States i Transitions

Objectiu

Gestiona diferents estats visuals d'un component i anima els canvis entre ells de manera suau i professional.

Conceptes clau

Qt Quick proporciona un sistema declaratiu per gestionar estats:

Exemple bàsic d'estats

Rectangle {
    id: semàfor
    width: 100
    height: 100
    color: "red"
    
    states: [
        State {
            name: "verd"
            PropertyChanges {
                target: semàfor
                color: "green"
            }
        },
        State {
            name: "groc"
            PropertyChanges {
                target: semàfor
                color: "yellow"
            }
        },
        State {
            name: "vermell"
            PropertyChanges {
                target: semàfor
                color: "red"
            }
        }
    ]
    
    MouseArea {
        anchors.fill: parent
        onClicked: {
            if (semàfor.state === "vermell") semàfor.state = "verd"
            else if (semàfor.state === "verd") semàfor.state = "groc"
            else semàfor.state = "vermell"
        }
    }
}

Estats per al joc RPS

Rectangle {
    id: fonsJoc
    color: "#AAAAAA"
    anchors.fill: parent
    
    states: [
        State {
            name: "usuariGuanya"
            PropertyChanges {
                target: fonsJoc
                color: "lime"
            }
        },
        State {
            name: "usuariPerd"
            PropertyChanges {
                target: fonsJoc
                color: "lightcoral"
            }
        },
        State {
            name: "empat"
            PropertyChanges {
                target: fonsJoc
                color: "cyan"
            }
        }
    ]
}

// Canvi d'estat
function processarResultat(resultat) {
    if (resultat === "guanya") fonsJoc.state = "usuariGuanya"
    else if (resultat === "perd") fonsJoc.state = "usuariPerd"
    else fonsJoc.state = "empat"
}

Transitions bàsiques

Rectangle {
    id: quadrat
    color: "blue"
    
    states: [
        State {
            name: "vermell"
            PropertyChanges { target: quadrat; color: "red" }
        }
    ]
    
    transitions: [
        Transition {
            from: ""      // Estat per defecte
            to: "vermell"
            ColorAnimation {
                duration: 500
                easing.type: Easing.InOutQuad
            }
        },
        Transition {
            from: "vermell"
            to: ""
            ColorAnimation {
                duration: 300
            }
        }
    ]
}

Transició universal

Per aplicar la mateixa animació a tots els canvis:

transitions: [
    Transition {
        from: "*"  // Des de qualsevol estat
        to: "*"    // Cap a qualsevol estat
        ColorAnimation {
            duration: 400
        }
    }
]

Animacions seqüencials

transitions: [
    Transition {
        from: "*"
        to: "usuariGuanya"
        
        SequentialAnimation {
            ColorAnimation {
                to: "#AAAAAA"
                duration: 500
            }
            ColorAnimation {
                to: "lime"
                duration: 500
            }
        }
    }
]

Animacions paral·leles

State {
    name: "expandit"
    PropertyChanges {
        target: rectangle
        width: 300
        height: 300
        color: "blue"
    }
}

Transition {
    to: "expandit"
    
    ParallelAnimation {
        NumberAnimation {
            properties: "width,height"
            duration: 500
            easing.type: Easing.OutBack
        }
        ColorAnimation {
            duration: 500
        }
    }
}

Tipus d'animacions disponibles

// Animació de color
ColorAnimation {
    duration: 300
    easing.type: Easing.InOutQuad
}

// Animació de números (width, height, opacity, rotation...)
NumberAnimation {
    properties: "width,height"
    duration: 500
    easing.type: Easing.OutBounce
}

// Animació de posició
PropertyAnimation {
    property: "x"
    duration: 400
}

// Animació de rotació
RotationAnimation {
    duration: 1000
    direction: RotationAnimation.Clockwise
}

Easing types comuns

easing.type: Easing.Linear        // Velocitat constant
easing.type: Easing.InOutQuad     // Suau inici i final
easing.type: Easing.OutBounce     // Efecte de bot
easing.type: Easing.InOutElastic  // Efecte elàstic
easing.type: Easing.OutBack       // Sobrepassa lleugerament

Estats amb múltiples canvis

State {
    name: "destacat"
    PropertyChanges {
        target: boto
        width: 200
        height: 80
        color: "gold"
        scale: 1.2
        opacity: 1.0
    }
    PropertyChanges {
        target: text
        font.pixelSize: 24
        color: "black"
    }
}

Estat per defecte

L'estat buit ("") és l'estat per defecte:

Rectangle {
    // Propietats per defecte
    color: "gray"
    width: 100
    
    states: [
        State {
            name: "actiu"
            PropertyChanges {
                target: parent
                color: "blue"
                width: 150
            }
        }
    ]
    
    // Tornar a l'estat per defecte
    MouseArea {
        onClicked: {
            parent.state = ""  // Torna a gray i width 100
        }
    }
}

Punts importants


8. Mode dia/nit - Gestió de temes

Objectiu

Implementa un sistema de temes que permeti canviar entre mode clar i mode fosc a tota l'aplicació de manera centralitzada i persistent.

Concepte de Singleton

Un singleton és un patró de disseny que garanteix que només existeixi una única instància d'un objecte i que sigui accessible globalment des de qualsevol part de l'aplicació.

Avantatges en QML:

Creació del ThemeManager (singleton)

Crea el fitxer ThemeManager.qml:

// ThemeManager.qml
pragma Singleton
import QtQuick 2.15

QtObject {
    id: themeManager
    
    // Propietat principal del tema
    property bool darkMode: false
    
    // Colors per al mode clar
    property color lightBackground: "#FFFFFF"
    property color lightText: "#000000"
    property color lightAccent: "#0066CC"
    property color lightSecondary: "#F5F5F5"
    
    // Colors per al mode fosc
    property color darkBackground: "#1E1E1E"
    property color darkText: "#FFFFFF"
    property color darkAccent: "#3DAEE9"
    property color darkSecondary: "#2D2D2D"
    
    // Colors actius (calculats segons darkMode)
    property color backgroundColor: darkMode ? darkBackground : lightBackground
    property color textColor: darkMode ? darkText : lightText
    property color accentColor: darkMode ? darkAccent : lightAccent
    property color secondaryColor: darkMode ? darkSecondary : lightSecondary
    
    // Funció per canviar el tema
    function toggleTheme() {
        darkMode = !darkMode
    }
}

Registre del singleton: fitxer qmldir

Crea el fitxer qmldir al directori arrel del projecte:

singleton ThemeManager 1.0 ThemeManager.qml

Aquest fitxer indica a Qt que ThemeManager és un singleton i com accedir-hi.

Configuració al CMakeLists.txt

Assegura't que el qmldir s'inclogui als recursos:

set(PROJECT_SOURCES
    main.cpp
    resources.qrc
)

I al resources.qrc:

<RCC>
    <qresource prefix="/">
        <file>qmldir</file>
        <file>ThemeManager.qml</file>
        <file>main.qml</file>
        <!-- altres fitxers -->
    </qresource>
</RCC>

Ús del ThemeManager als components

// main.qml
import QtQuick 2.15
import QtQuick.Controls 6.5
import "."  // Importa el directori actual (on està qmldir)

Window {
    width: 640
    height: 480
    visible: true
    
    Rectangle {
        anchors.fill: parent
        color: ThemeManager.backgroundColor
        
        Column {
            anchors.centerIn: parent
            spacing: 20
            
            Label {
                text: "Hola món"
                color: ThemeManager.textColor
                font.pixelSize: 32
            }
            
            Button {
                text: darkMode ? "Mode clar" : "Mode fosc"
                onClicked: {
                    ThemeManager.toggleTheme()
                }
            }
        }
    }
}

Aplicació a múltiples components

// GamePage.qml
import QtQuick 2.15
import "."

Rectangle {
    color: ThemeManager.backgroundColor
    
    Text {
        text: "Pàgina de joc"
        color: ThemeManager.textColor
    }
    
    Rectangle {
        color: ThemeManager.secondaryColor
        // ...
    }
}

// ConfigPage.qml
import QtQuick 2.15
import QtQuick.Controls 6.5
import "."

Rectangle {
    color: ThemeManager.backgroundColor
    
    Label {
        text: "Configuració"
        color: ThemeManager.textColor
    }
    
    Switch {
        text: "Mode fosc"
        checked: ThemeManager.darkMode
        onToggled: {
            ThemeManager.toggleTheme()
        }
    }
}

Persistència del tema amb Settings

Per recordar la preferència de l'usuari:

import Qt.labs.settings 1.0

Settings {
    id: config
    category: "visual"
    
    // Enllaça directament amb ThemeManager
    property alias darkMode: ThemeManager.darkMode
}

Amb això, quan l'usuari canviï el tema, es desarà automàticament i es recuperarà en obrir l'aplicació de nou.

Paleta de colors recomanada

Mode clar:

Mode fosc:

Assegura't que:

Exemple complet amb transicions

Rectangle {
    id: fons
    color: ThemeManager.backgroundColor
    
    // Anima el canvi de color
    Behavior on color {
        ColorAnimation {
            duration: 300
            easing.type: Easing.InOutQuad
        }
    }
    
    Text {
        id: titol
        color: ThemeManager.textColor
        
        // Anima el canvi de color del text
        Behavior on color {
            ColorAnimation {
                duration: 300
            }
        }
    }
}

Ampliació: més propietats de tema

Pots afegir més propietats al ThemeManager:

pragma Singleton
import QtQuick 2.15

QtObject {
    property bool darkMode: false
    
    // Colors
    property color backgroundColor: darkMode ? "#1E1E1E" : "#FFFFFF"
    property color textColor: darkMode ? "#FFFFFF" : "#000000"
    property color accentColor: darkMode ? "#3DAEE9" : "#0066CC"
    
    // Mides de font
    property int fontSizeSmall: 12
    property int fontSizeNormal: 16
    property int fontSizeLarge: 24
    property int fontSizeTitle: 36
    
    // Espaiat
    property int spacing: 10
    property int margins: 20
    
    // Radis
    property int radiusSmall: 5
    property int radiusNormal: 10
    property int radiusLarge: 20
    
    // Durades d'animació
    property int animationFast: 150
    property int animationNormal: 300
    property int animationSlow: 500
}

Punts importants


9. Referències i recursos addicionals

Documentació oficial Qt

Qt Quick:

Components específics:

Recursos:

Tutorials i guies

Oficials:

Comunitat:

Llibres recomanats

Eines de desenvolupament

Qt Creator:

qmlscene:

Qt Design Studio:

Exemples oficials

Qt proporciona molts exemples de codi:

Comunitat i suport

Forums:

Stack Overflow:

Blogs i articles:

Vídeos i cursos

YouTube:

Cursos online:

Llicències

Qt Open Source:

Qt Commercial:

Consells finals

  1. Llegeix la documentació oficial - És completa i amb exemples
  2. Prova els exemples de Qt - Aprèn veient codi real
  3. Participa a la comunitat - Forums i Stack Overflow són molt actius
  4. Practica amb projectes petits - Millor aprendre pas a pas
  5. Empra control de versions - Git des del primer dia
  6. Escriu codi net - Comentaris i estructura clara

Bona sort amb el desenvolupament Qt Quick! 🚀