Появился Qt 5.0.1 с компилятором MinGW и начал потихонечку пробовать.
В этой записи будет рассматриваться Qt5, но так же упомяну как некоторые вещи делались в Qt4.
Как то мне потребовался компонент, для выбора/установке даты в приложение с тач-интерфейсом на Qt4, а готового календаря не оказалось.
Поэтому будем делать свой календарь 
Как хотелось, что бы выглядело в коде
| JavaScript | 1
2
3
4
| Calendar{
month: 5
year: 2014
} |
|
Здесь вывод календаря на май месяц 2014 года.
Я не пошел по пути генерации компонентов и JavaScript, а использовал модели и view, для создания динамических свойств.
Описываем модель календаря.
Как оказалась, View элементы QML, будут с любой моделью работаться как со списком, поэтому наследуемся от QAbstractListModel.
Введем перечисление ролей, через которые будет идти обращение из QML к данным нашей модели:
| C++ (Qt) | 1
2
3
4
5
| enum Roles
{
Date = Qt::UserRole + 1,
Month
}; |
|
Модель будет хранить следующие данные:
| C++ (Qt) | 1
2
3
4
5
| struct Record
{
int day;
int month;
}; |
|
month предназначен, для выделения ячейки другим цветом.
Дальше добавим свойства месяц и год, что бы календарь был настраиваемым из QML:
| C++ (Qt) | 1
2
| Q_PROPERTY(int month READ month WRITE setMonth NOTIFY monthChanged)
Q_PROPERTY(int year READ year WRITE setYear NOTIFY yearChanged) |
|
Добавим два метода, которые мы сможем вызывать из QML, что бы получать имя дня недели и имя месяца:
| C++ (Qt) | 1
2
| Q_INVOKABLE QString shortDayName(int day)const;
Q_INVOKABLE QString longMonthName(int month)const; |
|
Методы которые нужно переопределить:
| C++ (Qt) | 1
2
3
| virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
virtual QHash<int, QByteArray> roleNames() const; |
|
В Qt4 был метод:
| C++ (Qt) | 1
| void QAbstractItemModel::setRoleNames(const QHash<int, QByteArray> & roleNames) [protected] |
|
Что позволял не переопределять roleNames
Полный вид класса такой:
Кликните здесь для просмотра всего текста
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
| class CalendarModel : public QAbstractListModel
{
Q_OBJECT
/*!
* \brief Роли модели
*/
enum Roles
{
Date = Qt::UserRole + 1, //!< день
Month //!< месяц
};
/*! свойство месяц */
Q_PROPERTY(int month READ month WRITE setMonth NOTIFY monthChanged)
/*! свойство гов */
Q_PROPERTY(int year READ year WRITE setYear NOTIFY yearChanged)
/*!
* \brief запись хранимая в модели
*/
struct Record
{
int day; //!< день
int month; //!< месяц
};
public:
explicit CalendarModel(QObject *parent = 0);
/*!
* \brief month получить месяц
*/
int month()const;
/*!
* \brief setMonth установить месяц
* \param month от 1 до 12
*/
void setMonth(int month);
/*!
* \brief year получить год
*/
int year()const;
/*!
* \brief setYear установить год
* \param year
*/
void setYear(int year);
/*!
* \brief получить короткое имя дня недели
* \param day день недели от 1 до 7
* Получение короткого названия дня недели.
* 1 = "Mon"
*/
Q_INVOKABLE QString shortDayName(int day)const;
/*!
* \brief получить длинное имя месяца
* \param month месяц от 1 до 12
*/
Q_INVOKABLE QString longMonthName(int month)const;
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
virtual QHash<int, QByteArray> roleNames() const;
signals:
/*!
* \brief monthChanged
* Оповещение изменения месяца
*/
void monthChanged();
/*!
* \brief yearChanged
* Оповещение изменения года
*/
void yearChanged();
public slots:
/*!
* \brief updateModel обновление модели
*/
void updateModel();
private:
int month_; //!< месяц
int year_; //!< год
QVector<Record> data_;//!< хранимые данные
QHash<int, QByteArray> roles_;
}; |
|
Из реализации приведем пару интересных методов:
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| CalendarModel::CalendarModel(QObject *parent) :
QAbstractListModel(parent)
{
roles_[Date] = "date";
roles_[Month] = "month";
QDate curentDate = QDate::currentDate();
month_ = curentDate.month();
year_ = curentDate.year();
updateModel();
}
QHash<int, QByteArray> CalendarModel::roleNames() const
{
return roles_;
}
QString CalendarModel::shortDayName(int day)const
{
return QDate::shortDayName(day);
}
QString CalendarModel::longMonthName(int month)const
{
return QDate::longMonthName(month);
}
QVariant CalendarModel::data(const QModelIndex & index, int role ) const
{
if(!index.isValid())
return QVariant();
switch (role)
{
case Date: return data_[index.row()].day;
case Month: return data_[index.row()].month;
default: return QVariant();
}
} |
|
Модель есть, теперь надо зарегистрировать среди типов QML
| C++ (Qt) | 1
| qmlRegisterType<CalendarModel>("QmlExtension", 1, 0, "CalendarModel"); |
|
Здесь QmlExtension это где находится компонент, этот путь мы будем писать при импорте в QML. 1, 0 это версия, дальше имя, которое будет доступно в QML.
Теперь самое время написать внешний вид.
Тут все довольно просто:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
| import QtQuick 2.0
import QmlExtension 1.0
Rectangle {
width: 300
height: 200
border.width: 1
border.color: "gray"
property int headerHeight: 40
property alias year: calModel.year
property alias month: calModel.month
CalendarModel{
id: calModel
}
GridView{
id:view
anchors.left: parent.left
anchors.right: parent.right
height: parent.height
width: parent.width
model:calModel
cellWidth: width / 7
cellHeight: (height-headerHeight)/(count/7)
interactive: false
header:Item {
height: headerHeight
width: parent.width
Text {
width: parent.width
horizontalAlignment: Text.AlignHCenter
text: calModel.longMonthName(calModel.month);
font.family: "Verdana"
font.bold: true
font.pointSize: 13
}
Row{
height: parent.height/2
anchors.left: parent.left;
anchors.right: parent.right
anchors.bottom: parent.bottom
Repeater{
model:7
Text {
width: view.cellWidth
height: parent.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: calModel.shortDayName(index+1)
font.family: "Verdana"
font.bold: true
}
}
}
}
delegate: Rectangle{
height:view.cellHeight
width: view.cellWidth
border.width: 1
border.color: "gray"
Rectangle{
height: parent.height
width: parent.width
color: calModel.month != month ? "lightgray":"white"
opacity: 0.5
}
Text{
anchors.centerIn: parent
text:date
font.family: "Verdana"
font.bold: true
}
}
}
} |
|
Обращаю внимание на вторую строчку:
import QmlExtension 1.0
Здесь происходит загрузка нашего компонента.
Календарь на месяц это здорово, но давайте сделаем на целый год:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| import QtQuick 2.0
import QmlExtension 1.0
Flickable{
contentWidth: calendarGrid.width;
contentHeight: calendarGrid.height
width: 900
height: 500
Grid{
id: calendarGrid
columns : 3
rows: 4
spacing:20
Repeater{
model:12
Calendar{
month: index+1
}
}
}
} |
|
Все здорово, но неудобно, хочется все это было доступно в qmlscene.
Поэтому напишем плагин.
Выглядит плагин так:
| C++ (Qt) | 1
2
3
4
5
6
7
8
| class QMLExtensionPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
public:
void registerTypes(const char *uri);
}; |
|
В Qt4 для этого предназначен QDeclarativeExtensionPlugin.
Реализация простая:
| C++ (Qt) | 1
2
3
4
| void QMLExtensionPlugin::registerTypes(const char *uri)
{
qmlRegisterType<CalendarModel>(uri, 1, 0, "CalendarModel");
} |
|
Дальше пишем qmldir файл описывающий из чего состоит этот компонент
| C | 1
2
3
4
| module QmlExtension
Calendar 1.0 Calendar.qml
YearCalendar 1.0 YearCalendar.qml
plugin qmlextension |
|
В Qt4 плагины располагаются в подкаталоге imports, в Qt5 подкаталоге qml.
При распространения приложения, кладем плагин рядом и устанавливаем пути поиска
В Qt5
| C++ (Qt) | 1
| void QQmlEngine::addPluginPath(const QString & path) |
|
В Qt4
| C++ (Qt) | 1
| void QDeclarativeEngine::addPluginPath ( const QString & path ) |
|
Исходные тексты лежат на Google Code
|