Реклама

QML - Урок 006. Custom Calendar in Qt QML or Qt QML Android

Qt, QML, Android, CalendarStyle, Calendar

В данном уроке хотелось бы рассказать о том, каким образом можно кастомизировать внешний вид объекта Calendar в Qt Qml . Подправить например цвета, шрифт, а также красиво вписать его в диалоговое окно для выбора даты. Поэтому определимся с тем, как должно работать наше приложение и как оно должно выглядеть:

  1. В главном окне приложения будет находиться стандартная кнопка, на которой отображается дата (хотя если хотите, то можете и её кастомизировать );
  2. При нажатии на кнопку открывается диалоговое окно в котором располагается Calendar и две кнопки ("Ok" и "Cancel"). В Calendar устанавливается дата, которая была указана на кнопке;
  3. При нажатии на кнопку "Cancel" ничего не происходит, а диалоговое окно просто закрывается;
  4. При нажатии на кнопку "Ok" диалоговое окно закрывается, а на кнопке главного окна отобразится дата, которая была выбрана в календаре.

Полагаю, что Вы уже успели заметить, что кастомизация интерфейса приложения в Qt QML проходит одинаково, как для Desktop версий, так и для Android. Иначе бы разработка под Qt не считалась кроссплатформенной. Следовательно я не буду сильно вдаваться в разницу настройки отображения диалогового окна для Desktop версии или для Android версии. Этот момент я объяснял в уроке по созданию кастомизированного диалогового окна .

Структура проекта для Custom Calendar

Для демонстрации примера был создан новый проект со следующей структурой:

  • QmlCalendarCustom.pro - профайл проекта;
  • main.cpp - основной файл исходных кодов;
  • main.qml - основной файл интерфейса на QML;
  • left_arrow.png - не нажатая стрелка влево;
  • left_arrow_disable.png - нажатая стрелка влево;
  • right_arrow.png - не нажатая стрелка вправо;
  • right_arrow_disable.png - нажатая стрелка вправо.

Стрелки

Применяю следующие изображения для кастомизации пролистывания месяцев в Calendar.

main.cpp

Данный файл создаётся по умолчанию, но я приведу его программный код, чтобы не было путаницы.

#include <QApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

А вот всё безобразие будем творить именно в данном файле. В нём же создадим Custom Calendar, который будет устанавливать дату на кнопку.

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    /* Создадим переменную для хранения даты, чтобы не заморачиваться
     * с конвертацией типов
     * */
    property var tempDate: new Date();

    Button {
        id: button
        // Устанавливаем текущую дату при запуске приложения на кнопку
        text: Qt.formatDate(tempDate, "dd.MM.yyyy");
        anchors.centerIn: parent // Центруем кнопку в окне

        // По клику на кнопку запускаем диалоговое окно черз кастомную функцию
        onClicked: dialogCalendar.show(tempDate)

    }

    Dialog {
        id: dialogCalendar
        // Задаём размеры диалогового окна
        width: 250
        height: 300

        // Создаем контент диалогового окна
        contentItem: Rectangle {
            id: dialogRect
            color: "#f7f7f7"

            // Первым идёт кастомный календарь
            Calendar {
                id: calendar
                // Размещаем его в верхней части диалога и растягиваем по ширине
                anchors.top: parent.top
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.bottom: row.top

                // Стилизуем Календарь
                style: CalendarStyle {

                    // Стилизуем navigationBar
                    navigationBar: Rectangle {
                        /* Он будет состоять из прямоугольника,
                         * в котором будет располагаться две кнопки и label
                         * */
                        height: 48
                        color: "#f7f7f7"

                        /* Горизонтальный разделитель,
                         * который отделяет navigationBar от поля с  числами
                         * */
                        Rectangle {
                            color: "#d7d7d7"
                            height: 1
                            width: parent.width
                            anchors.bottom: parent.bottom
                        }

                        // Кнопка промотки месяцев назад
                        Button {
                            id: previousMonth
                            width: parent.height - 8
                            height: width
                            anchors.verticalCenter: parent.verticalCenter
                            anchors.left: parent.left
                            anchors.leftMargin: 8

                            /* По клику по кнопке вызываем функцию
                             * календаря, которая отматывает месяц назад
                             * */
                            onClicked: control.showPreviousMonth()

                            // Стилизуем кнопку
                            style: ButtonStyle {
                                background: Rectangle {
                                    // Окрашиваем фон кнопки
                                    color: "#f7f7f7"
                                    /* И помещаем изображение, у которго будет
                                     * два источника файлов в зависимости от того
                                     * нажата кнопка или нет
                                     */
                                    Image {
                                        source: control.pressed ? "left_arrow_disable.png" : "left_arrow.png"
                                        width: parent.height - 8
                                        height: width
                                    }
                                }
                            }
                        }

                        // Помещаем стилизованный label
                        Label {
                            id: dateText
                            /* Забираем данные из title календаря,
                             * который в данном случае не будет виден
                             * и будет заменён данным label
                             */
                            text: styleData.title
                            color:  "#34aadc"
                            elide: Text.ElideRight
                            horizontalAlignment: Text.AlignHCenter
                            font.pixelSize: 16
                            anchors.verticalCenter: parent.verticalCenter
                            anchors.left: previousMonth.right
                            anchors.leftMargin: 2
                            anchors.right: nextMonth.left
                            anchors.rightMargin: 2
                        }

                        // Кнопка промотки месяцев вперёд
                        Button {
                            id: nextMonth
                            width: parent.height - 8
                            height: width
                            anchors.verticalCenter: parent.verticalCenter
                            anchors.right: parent.right

                            /* По клику по кнопке вызываем функцию
                             * календаря, которая отматывает месяц назад
                             * */
                            onClicked: control.showNextMonth()

                             // Стилизуем кнопку
                            style: ButtonStyle {
                                // Окрашиваем фон кнопки
                                background: Rectangle {
                                    color: "#f7f7f7"
                                    /* И помещаем изображение, у которго будет
                                     * два источника файлов в зависимости от того
                                     * нажата кнопка или нет
                                     */
                                    Image {
                                        source: control.pressed ? "right_arrow_disable.png" : "right_arrow.png"
                                        width: parent.height - 8
                                        height: width
                                    }
                                }
                            }
                        }
                    }


                    // Стилизуем отображением квадратиков с числами месяца
                    dayDelegate: Rectangle {
                        anchors.fill: parent
                        anchors.margins: styleData.selected ? -1 : 0
                        // Определяем цвет в зависимости от того, выбрана дата или нет
                        color: styleData.date !== undefined && styleData.selected ? selectedDateColor : "transparent"

                        // Задаём предопределённые переменные с цветами, доступные только для чтения
                        readonly property color sameMonthDateTextColor: "#444"
                        readonly property color selectedDateColor: "#34aadc"
                        readonly property color selectedDateTextColor: "white"
                        readonly property color differentMonthDateTextColor: "#bbb"
                        readonly property color invalidDateColor: "#dddddd"

                        // Помещаем Label для отображения числа
                        Label {
                            id: dayDelegateText
                            text: styleData.date.getDate() // Устанавливаем число в текущий квадрат
                            anchors.centerIn: parent
                            horizontalAlignment: Text.AlignRight
                            font.pixelSize: 10

                            // Установка цвета
                            color: {
                                var theColor = invalidDateColor; // Устанавливаем невалидный цвет текста
                                if (styleData.valid) {
                                    /* Определяем цвет текста в зависимости от того
                                     * относится ли дата к выбранному месяцу или нет
                                     * */
                                    theColor = styleData.visibleMonth ? sameMonthDateTextColor : differentMonthDateTextColor;
                                    if (styleData.selected)
                                        // Перекрашиваем цвет текста, если выбрана данная дата в календаре
                                        theColor = selectedDateTextColor;
                                }
                                theColor;
                            }
                        }
                    }
                }
            }

            // Делаем панель с кнопками
            Row {
                id: row
                height: 48
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.bottom: parent.bottom

                // Кнопка для закрытия диалога
                Button {
                    id: dialogButtonCalCancel
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: parent.width / 2 - 1

                    style: ButtonStyle {
                        background: Rectangle {
                            color: control.pressed ? "#d7d7d7" : "#f7f7f7"
                            border.width: 0
                        }

                        label: Text {
                            text: qsTr("Cancel")
                            font.pixelSize: 14
                            color: "#34aadc"
                            verticalAlignment: Text.AlignVCenter
                            horizontalAlignment: Text.AlignHCenter
                        }
                    }
                    // По нажатию на кнопку - просто закрываем диалог
                    onClicked: dialogCalendar.close()
                }

                // Вертикальный разделитель между кнопками
                Rectangle {
                    id: dividerVertical
                    width: 2
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    color: "#d7d7d7"
                }

                // Кнопка подтверждения выбранной даты
                Button {
                    id: dialogButtonCalOk
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: parent.width / 2 - 1

                    style: ButtonStyle {
                        background: Rectangle {
                            color: control.pressed ? "#d7d7d7" : "#f7f7f7"
                            border.width: 0
                        }

                        label: Text {
                            text: qsTr("Ok")
                            font.pixelSize: 14
                            color: "#34aadc"
                            verticalAlignment: Text.AlignVCenter
                            horizontalAlignment: Text.AlignHCenter
                        }
                    }

                    /* По клику по кнопке сохраняем выбранную дату во временную переменную
                     * и помещаем эту дату на кнопку в главном окне,
                     * после чего закрываем диалог
                     */
                    onClicked: {
                        tempDate = calendar.selectedDate
                        button.text = Qt.formatDate(tempDate, "dd.MM.yyyy");
                        dialogCalendar.close();
                    }
                }
            }
        }

        /* Данная функция необходима для того, чтобы
         * установить дату с кнопки в календарь,
         * иначе календарь откроется с текущей датой
         */
        function show(x){
            calendar.selectedDate = x
            dialogCalendar.open()
        }
    }
}

Итог

В результате у Вас должно получиться приложение со следующим диалоговым окном, в котором присутствует Custom Calendar. Также в видеоуроке привеена демонстрация работы приложения с комментариями по программному коду.

Видеоурок

Реклама

Комментарии

  • #
  • 13 октября 2017 г. 1:13

Все очень круто, больше спасибо.
Подскажите пжлст что такое styleData?
Это какой то объект из dialogCalendar
Если я захочу другое отображение колендаря, мне нужно будет перегружать функции в новом классе?

Добрый день!
Ага, это внутренний объект стилей.

И да, понадобится перегружать. А вообще посмотрите ещё календарь из Quick.Controls 2.0, поскольку этот пример из Quick.Controls 1.4 - он устаревший.
А в новых контролах совсем иначе сделано, там вроде бы вместо стилей делегаты используются, всё несколько проще. Будут вопросы, на форуме спрашивайте по новым контролом, что смогу, подскажу.
  • #
  • 13 октября 2017 г. 16:20

Спасибо)

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
  • BlinCT
  • 22 октября 2017 г. 12:46

C++ - Тест 003. Условия и циклы

  • Результат 64 баллов
  • Очки рейтинга -1
  • Kiops
  • 22 октября 2017 г. 3:56

C++ - Тест 001. Первая программа и типы данных

  • Результат 86 баллов
  • Очки рейтинга 6
  • Kiops
  • 22 октября 2017 г. 2:41

Qt - Тест 001. Сигналы и слоты

  • Результат 100 баллов
  • Очки рейтинга 10
Последние комментарии
  • EVILEG
  • 21 октября 2017 г. 3:06

Qt/C++ - Урок 031. QCustomPlot - строим график по времени

Добавил архив с проектом

  • EVILEG
  • 20 октября 2017 г. 20:06

Qt/C++ - Урок 031. QCustomPlot - строим график по времени

После работы поищу, должен где-то быть на винте.

  • Миша
  • 20 октября 2017 г. 20:04

Qt/C++ - Урок 031. QCustomPlot - строим график по времени

не могли бы вы выложить архив с рабочей версией скрипта?

  • EVILEG
  • 20 октября 2017 г. 20:03

Qt/C++ - Урок 030. QCustomPlot - быстрый старт в работе с графиками

Использование дизайнера в Qt Creator и использование ui файлов является распространённой практикой в Qt фреймворке. Написать отдельную статью про то, что это такое? - может быть. Опи...

  • Миша
  • 20 октября 2017 г. 19:43

Qt/C++ - Урок 030. QCustomPlot - быстрый старт в работе с графиками

Но почему вы это не описали? Не могли бы вы описать.

Сейчас обсуждают на форуме
  • EVILEG
  • 22 октября 2017 г. 12:05

Закрепление якорей в момент создания объекта через JS

Добрый день! Якоря - это не те свойства, которые можно устанавливать сразу по инициализации, лучше их править после создания объекта, поскольку при одновременной установке они могут в...

  • EVILEG
  • 21 октября 2017 г. 23:33

Создание истории редактирования постов на сайте

Ясно. Тогда я лучше не буду тратить время на его проверку. Тем более, что я использую гугловский prettyprint для подсветки кода. Спасибо за информацию.

QFile::copy() возвращает false

Получилось! Спасибо огромное! path1 = "C:/Users/555/Pictures/00GAF13AP001-002.jpg"true

  • cordsac
  • 19 октября 2017 г. 15:49

How can I select the QGraphicView Item and change the properties

Ok I'll check it sir,If you can please do article(tutorial) about this,Its really useful.Thank you if you can give me some sample code when you free.thanks again

  • cordsac
  • 17 октября 2017 г. 19:28

How can I open SVG file through QT

Okay,Thank you sir :)