Заметки о QML, QT, C++

QML. Как сделать кнопку?

Объект типа "Кнопка" должна обладать следующей функциональностью:

  1. Изменять свой вид при наведении курсора мыши.
  2. Восстанавливать исходное состояние при сведении курсора мыши.
  3. Изменять вид при нажатии на кнопку.
  4. Восстанавливать вид (2), когда кнопка отпущена.
  5. Вызывать какие-либо изменения.

Напишем на QML такой интерфейс: две кнопки и белый прямоугольник, при щелчке на одной кнопке прямоугольник становится зеленым, по другой - голубым, при щелчке на самом прямоугольнике он снова становится белым.
Кнопки на QML

Обычно кнопку делают из прямоугольника. Однако можно подозревать, что ее поведение можно реализовать и взяв за основу другой объект QML.

Чтобы кнопка менялась при наведении и сведении мыши, в MouseArea следует установить значение свойства hoverEnabled в true, а также установить значения onEntered, onExited, например, так:

hoverEnabled: true
onEntered: parent.border.color = "gray"
onExited: parent.border.color = "green"

(Для onExited указан цвет, который был задан для основного (1) состояния кнопки).

Изменение кнопки при щелчке на ней задается следующим образом в свойстве color прямоугольника:

color: button_green_mouse_area.pressed ? "green" : "lightgreen"

С помощью onClicked в MouseArea можно задать изменение свойства другого объекта:

onClicked: rect_color_change.color = "lightgreen"

Возникла сложность, не получается одновременно менять одно и тоже свойство прямоугольника. Если цвет заполнения меняется при нажатии, то при наведении получается менять только цвет рамки. Иначе изменение цвета при нажатии не срабатывает.

Весь код интерфейса:

// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
import QtQuick 1.1
 
Rectangle {
    width: 390
    height: 210
 
    Rectangle {
        id: button_green
        x: 35
        y: 37
        width: 119
        height: 51
        color: button_green_mouse_area.pressed ? "green" : "lightgreen"
        radius: 7
        border.width: 3
        border.color: "green"
        Text {
            id: button_green_label
            x: 38
            y: 18
            text: qsTr("Зеленый")
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            font.pixelSize: 16
        }
        MouseArea {
            id: button_green_mouse_area
            anchors.fill: parent
            hoverEnabled: true
            onEntered: parent.border.color = "gray"
            onExited: parent.border.color = "green"
            onClicked: rect_color_change.color = "lightgreen"
        }
    }
 
    Rectangle {
        id: button_blue
        x: 35
        y: 118
        width: 119
        height: 51
        color: button_blue_mouse_area.pressed ? "darkblue" : "lightblue"
        radius: 7
        border.width: 3
        border.color: "darkblue"
        Text {
            id: button_blue_label
            x: 38
            y: 18
            text: qsTr("Голубой")
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            font.pixelSize: 16
        }
        MouseArea {
            id: button_blue_mouse_area
            anchors.fill: parent
            hoverEnabled: true
            onEntered: parent.border.color = "gray"
            onExited: parent.border.color = "darkblue"
            onClicked: rect_color_change.color = "lightblue"
        }
    }
 
    Rectangle {
        id: rect_color_change
        x: 186
        y: 30
        width: 167
        height: 148
        color: "#ffffff"
        border.width: 3
        border.color: "#000000"
        MouseArea {
            id: rect_color_change_mouse_area
            anchors.fill: parent
            onEntered: rect_color_change.color = "white"
        }
    }
 
}

QML. Анимация градиета

Пример взят отсюда. Добавлен mouseExitAnim. Одним файлом *.qml код выглядит так:
import QtQuick 1.1
 
Rectangle {
    property string rsGradientStop0: "#FF7C7C7C" // свойство для хранения цвета
    property string rsGradientStop1: "#FF4E4E4E"
    id: greyButton
    width: 85
    height: 23
    border.color: "Wheat"
    gradient: Gradient { // добавление градиента
        GradientStop {
            id: gradientStop0
            position: 0
            color: rsGradientStop0
        }
        GradientStop {
            id: gradientStop1
            position: 1
            color: rsGradientStop1
        }
    }
    Text {
        id: text
        color: "Wheat"
        text: qsTr("Ok")
        font.underline: false
        font.bold: false
        font.pixelSize: 12
        anchors.centerIn: parent
    }
    ParallelAnimation {
        id: mouseEnterAnim
        PropertyAnimation {
            target: gradientStop0 // id градиента
            properties: "color"
            to: rsGradientStop1
            duration: 300
        }
        PropertyAnimation {
            target: gradientStop1
            properties: "color"
            to: rsGradientStop0
            duration: 300
        }
    }
    ParallelAnimation {
        id: mouseExitAnim
        PropertyAnimation {
            target: gradientStop0
            properties: "color"
            to: rsGradientStop0
            duration: 300
        }
        PropertyAnimation {
            target: gradientStop1
            properties: "color"
            to: rsGradientStop1
            duration: 300
        }
    }
    MouseArea {
        id: mouse
        anchors.fill: greyButton
        hoverEnabled: true
        onEntered: mouseEnterAnim.start()
        onExited: mouseExitAnim.start()
    }
}
Другими словами, у объекта GradientStop при событии анимируется изменение свойства color к определенному значению.

QML. Поворот градиента

По умолчанию делается только горизонтальный градиент. Чтобы получить под другим углом, надо повернуть всю фигуру (прямоугольник), т.к. свойство rotation отсутствует у Gradient.

Круг можно получить установив у квадрата radius в 50.

Если требуется вертикальный градиент для прямоугольника, то тогда при его создании следует поменять значения местами для требуемых ширины и высоты. После поворота на 900 высота станет шириной.

Выходит, что получить градиетную заливку по диагонали и сохранить при этом прямоугольник не наклоненным, невозможно. Для круга такое возможно.

Поворот градиента в QML

// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
import QtQuick 1.1
 
Rectangle {
    width:500
    height:250
    gradient: Gradient {
        GradientStop {
            position: 0
            color: "#584d43"
        }
 
        GradientStop {
            position: 0.250
            color: "#ecd2b9"
        }
 
        GradientStop {
            position: 0.500
            color: "#584d43"
        }
 
        GradientStop {
            position: 0.750
            color: "#ecd2b9"
        }
 
        GradientStop {
            position: 1
            color: "#584d43"
        }
    }
 
    Rectangle {
        x: 61
        y: 75
        width: 150
        height: 100
        radius: 0
        rotation: 45
        gradient: Gradient {
            GradientStop {
                position: 0
                color: "#251a38"
            }
            GradientStop {
                position: 0.500
                color: "#ffffff"
            }
            GradientStop {
                position: 1
                color: "#251a38"
            }
        }
    }
 
    Rectangle {
        x: 344
        y: 75
        width: 100
        height: 100
        radius: 50
        rotation: 45
        gradient: Gradient {
            GradientStop {
                position: 0
                color: "#251a38"
            }
            GradientStop {
                position: 0.500
                color: "#ffffff"
            }
            GradientStop {
                position: 1
                color: "#251a38"
            }
        }
    }
 
}

С++. Перегруженные функции и аргументы по умолчанию

В программе может потребоваться выполнить одни и теже действия (функцию), однако входные данные при этом могут быть разными. На помощь приходят два механизма:

В каких случаях следует использовать первый, а в каких второй?

Перегрузка функций заключается в том, что в программе существуют несколько одноименных функций, которые различаются между собой количеством параметров и|или их типом. То, какая функция вызывается, определяется по переданным аргументам. Перегрузка функций - более гибкая и универсальная идея.

При задании аргументов по умолчанию не приходится дублировать код (есть всего одна функция). Однако при этом нельзя передавать аргументы разного типа, использовать значение по умолчанию первого аргумента (стоящих впереди) и передать для второго (стоящих после).

Понятно, что перегрузку функций и установку аргументов по умолчанию можно использовать совместно.

Ниже приведены примеры, взятые их книги Р.Лафоре "Объектно-ориентированное программирование в C++".

Перегруженная функция (переменное число аргументов)

#include <iostream>
using namespace std;
 
void repchar();
void repchar(char);
void repchar(char, int);
 
int main() {
    repchar();
    repchar('=');
    repchar('+',30);
    return 0;
}
 
void repchar() {
    for (int j=0; j<45; j++)
        cout << '*';
    cout << endl;
}
 
void repchar(char ch) {
    for (int j=0; j<45; j++)
        cout << ch;
    cout << endl;
}
 
void repchar(char ch, int n) {
    for (int j=0; j<n; j++)
        cout << ch;
    cout << endl;
}

Вывод:

*********************************************
=============================================
++++++++++++++++++++++++++++++

Перегруженная функция (различные типы аргументов)

#include <iostream>
using namespace std;
 
struct Distance {
    int feet;
    float inches;
};
 
void engldisp(Distance);
void engldisp(float);
 
int main() {
    Distance d1;
    float d2;
    cout << "Введите число футов: ";
    cin >> d1.feet;
    cout << "Введите число дюймов: ";
    cin >> d1.inches;
    cout << "Введите длину в дюймах: ";
    cin >> d2;
    cout << "d1 = ";
    engldisp(d1);
    cout << "\nd2 = ";
    engldisp(d2);
    cout << endl;
    return 0;
}
 
void engldisp(Distance dd) {
    cout << dd.feet << "\'-" << dd.inches << "\"";
}
 
void engldisp(float dd) {
    int feet = static_cast<int>(dd/12);
    float inches = dd - feet * 12;
    cout << feet << "\'-" << inches << "\"";
}

Введите число футов: 45
Введите число дюймов: 32
Введите длину в дюймах: 123
d1 = 45'-32"
d2 = 10'-3"

Установка аргументов по умолчанию

#include <iostream>
using namespace std;
 
void repchar(char='*', int=45);
 
int main() {
    repchar();
    repchar('=');
    repchar('+',30);
    return 0;
}
 
void repchar(char ch, int n) {
    for (int j=0; j<n; j++)
        cout << ch;
    cout << endl;
}

Вывод:

*********************************************
=============================================
++++++++++++++++++++++++++++++