В прошлой записи рассмотрели использование своих C++ моделей в QML.
Сейчас же раcсмотрим использование Qt моделей на примере QFileSystemModel.
QFileSystemModel содержит следующие роли:
| C++ (Qt) | 1
2
3
4
| QFileSystemModel::FileIconRole Qt::DecorationRole
QFileSystemModel::FilePathRole Qt::UserRole + 1
QFileSystemModel::FileNameRole Qt::UserRole + 2
QFileSystemModel::FilePermissions Qt::UserRole + 3 |
|
В принципе этого достаточно, для отображения имен файлов, но не достаточно, для обхода файловой системы.
QFileSystemModel содержит много полезных методов, но не содержит Q_INVOKABLE методов, позволяющих использовать их из QML, поэтому придется переопределить следующим способом:
| 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
| class FileSystemModel: public QFileSystemModel
{
Q_OBJECT
Q_PROPERTY(QString rootPath READ rootPath WRITE setRootPath NOTIFY rootPathChanged)
public:
FileSystemModel(QObject* parent = 0);
/*!
* @brief получить корень
*/
QString rootPath()const;
/*!
* @brief установить корень
*/
void setRootPath(const QString& path);
/*!
* @brief обертка над QFileSystemModel::index
*/
Q_INVOKABLE QModelIndex indexFromPath(const QString & path, int column = 0) const;
/*!
* @brief получение имени файла
*/
Q_INVOKABLE QString fileName(const QModelIndex & index) const;
/*!
* @brief получиение пути
*/
Q_INVOKABLE QString filePath(const QModelIndex & index) const;
/*!
* @brief проверка на каталог
*/
Q_INVOKABLE bool isDir(const QString& path) const;
/*!
* @brief получение родительского индекса
*/
Q_INVOKABLE QModelIndex parentIndex(const QModelIndex& child) const;
signals:
void rootPathChanged();
}; |
|
Реализация крайне проста:
| 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
| FileSystemModel::FileSystemModel(QObject* parent):
QFileSystemModel(parent)
{
setFilter(QDir::Files | QDir::AllDirs);
}
QString FileSystemModel::rootPath()const
{
return QFileSystemModel::rootPath();
}
void FileSystemModel::setRootPath(const QString& path)
{
QFileSystemModel::setRootPath(path);
emit rootPathChanged();
}
QModelIndex FileSystemModel::indexFromPath(const QString & path, int column ) const
{
return QFileSystemModel::index(path, column);
}
QString FileSystemModel::fileName(const QModelIndex & index) const
{
return QFileSystemModel::fileName(index);
}
QString FileSystemModel::filePath(const QModelIndex & index) const
{
return QFileSystemModel::filePath(index);
}
bool FileSystemModel::isDir(const QString& path) const
{
return QFileSystemModel::isDir(index(path));
}
QModelIndex FileSystemModel::parentIndex(const QModelIndex& child) const
{
return QFileSystemModel::parent(child);
} |
|
Стоит отметить установку фильтра в конструкторе.
Далее следует обычная регистрация типа в QML:
| C++ (Qt) | 1
| qmlRegisterType<FileSystemModel>(uri, 1, 0, "FileSystemModelBinding"); |
|
Большинство методов принимают QModelIndex в качестве параметра, а в том же ListModel используются целые индексы.
Тут и приходит на помощь VisualDataModel имеющий в себе свойство | JavaScript | 1
| rootIndex : QModelIndex |
|
.
Инкапсулируем свою модель в VisualDataModel:
| 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
| import QtQuick 2.0
import QmlExtension 1.0
VisualDataModel{
id: fsModel
property alias rootPath:fsBinding.rootPath
function cdUp(){
rootIndex = fsBinding.parentIndex(fsModel.rootIndex);
}
function isDir(path){
return fsBinding.isDir(path);
}
function cd(path){
rootIndex=fsBinding.indexFromPath(path)
}
model:FileSystemModelBinding{
id: fsBinding
}
} |
|
Сохраним это в FileSystemModel.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
| Rectangle {
width: 500
height: 500
//color: "#4e4d4e"
FileSystemModel{
id:fsModel
rootPath:"/"
delegate:Item{
height: view.cellHeight
width: view.cellWidth
Text{
anchors.horizontalCenter: icon.horizontalCenter
text:fileName
horizontalAlignment: Text.AlignHCenter
wrapMode:Text.WordWrap
width: parent.width
font.family: "Verdana"
//color: "white"
}
}
}
GridView{
id:view
width: parent.width
height: parent.height
anchors.fill: parent
model: fsModel
cellHeight : 128
cellWidth : 128
}
} |
|
В Windows отобразится сетка с названием диска, в Unix сетка с содержимым корня.
Теперь хочется отображать иконки. И тут встает вопрос, как использовать QIcon или QImage/QPixmap в QML?
Для этих целей есть поставщик изображений QQuickImageProvider
Когда мы пишем такую схему:
| JavaScript | 1
| Image { source: "image://MyImageProvider/image.png" } |
|
То ищется поставщик зарегистрированный под именем MyImageProvider, в который передастся идентификатор
объекта, который требуется отобразит, в данном случае это image.png.
Описываем поставщика:
| 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
| /*!
* @brief поставщик иконок
*/
class FileSystemIconProvider: public QQuickImageProvider
{
public:
/*!
\brief дефолтная длина
*/
static const size_t DEFAULT_WIDTH = 64;
/*!
\brief дефолтная высота
*/
static const size_t DEFAULT_HEIGTH = 64;
FileSystemIconProvider();
virtual QPixmap requestPixmap(const QString & id, QSize * size, const QSize & requestedSize);
private:
QFileIconProvider iconProvider_; //!< поставщик иконок fs
}; |
|
И реализация:
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
13
| FileSystemIconProvider::FileSystemIconProvider():
QQuickImageProvider(QQmlImageProviderBase::Pixmap)
{
}
QPixmap FileSystemIconProvider::requestPixmap(const QString & id, QSize * size, const QSize & requestedSize)
{
*size = QSize(DEFAULT_WIDTH, DEFAULT_HEIGTH);
QIcon icon = iconProvider_.icon(id);
return icon.pixmap( requestedSize.isValid() ? requestedSize : *size);
} |
|
Здесь id идентификатор ресурса, size истинный размер ресурса, requestedSize запрашиваемый размер из QML Image.
Добавляем поставщика в движек:
| C++ (Qt) | 1
2
3
4
| void QMLExtensionPlugin::initializeEngine(QQmlEngine * engine, const char * uri)
{
engine->addImageProvider("filesystem", new FileSystemIconProvider());
} |
|
Теперь собираем все вместе:
| 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
| import QtQuick 2.0
Rectangle {
width: 500
height: 500
//color: "#4e4d4e"
FileSystemModel{
id:fsModel
rootPath:"/"
delegate:Item{
height: view.cellHeight
width: view.cellWidth
Column{
anchors.centerIn: parent
width: parent.width
Image {
id: icon
source: "image://filesystem/"+filePath
smooth: true
}
Text{
anchors.horizontalCenter: icon.horizontalCenter
text:fileName
horizontalAlignment: Text.AlignHCenter
wrapMode:Text.WordWrap
width: parent.width
font.family: "Verdana"
//color: "white"
}
}
MouseArea{
anchors.fill: parent
onDoubleClicked: {
fsModel.cd(filePath)
}
}
}
}
GridView{
id:view
width: parent.width
height: parent.height
anchors.fill: parent
model: fsModel
cellHeight : 128
cellWidth : 128
}
} |
|
Одна из особенности QFileIconProvider в Windows, он не может отдавать большие иконки Windows Vista.
|