Files:
This plugin shows how to make a C++ model available to QML. It presents a simple file list for a single folder (directory) and allows the presented folder to be changed.
We do not explain the model implementation in detail, but rather focus on the mechanics of making the model available to QML.
The type we are creating can be used from QML like this:
import Qt 4.7 import Qt.labs.folderlistmodel 1.0 ListView { FolderListModel { id: foldermodel nameFilters: ["*.qml"] } Component { id: filedelegate Text { text: fileName } } model: foldermodel delegate: filedelegate }
We are subclassing QAbstractListModel which will allow us to give data to QML and send notifications when the data changes:
class QDeclarativeFolderListModel : public QAbstractListModel, public QDeclarativeParserStatus { Q_OBJECT Q_INTERFACES(QDeclarativeParserStatus)
As you see, we also inherit the QDeclarativeParserStatus interface, so that we can delay initial processing until we have all properties set (via componentComplete() below).
The first thing to do when devising a new type for QML is to define the properties you want the type to have:
Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged) Q_PROPERTY(QUrl parentFolder READ parentFolder NOTIFY folderChanged) Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters) Q_PROPERTY(SortField sortField READ sortField WRITE setSortField) Q_PROPERTY(bool sortReversed READ sortReversed WRITE setSortReversed) Q_PROPERTY(bool showDirs READ showDirs WRITE setShowDirs) Q_PROPERTY(bool showDotAndDotDot READ showDotAndDotDot WRITE setShowDotAndDotDot) Q_PROPERTY(bool showOnlyReadable READ showOnlyReadable WRITE setShowOnlyReadable) Q_PROPERTY(int count READ count)
The purposes of each of these should be pretty obvious - in QML we will set the folder to display (a file: URL), and the kinds of files we want to show in the view of the model.
Next are the constructor, destructor, and standard QAbstractListModel subclassing requirements:
public: QDeclarativeFolderListModel(QObject *parent = 0); ~QDeclarativeFolderListModel(); enum Roles { FileNameRole = Qt::UserRole+1, FilePathRole = Qt::UserRole+2 }; int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const;
The data() function is where we provide model values. The rowCount() function is also a standard part of the QAbstractListModel interface, but we also want to provide a simpler count property:
int count() const { return rowCount(QModelIndex()); }
Then we have the functions for the remaining properties which we defined above:
QUrl folder() const; void setFolder(const QUrl &folder); QUrl parentFolder() const; QStringList nameFilters() const; void setNameFilters(const QStringList &filters); enum SortField { Unsorted, Name, Time, Size, Type }; SortField sortField() const; void setSortField(SortField field); Q_ENUMS(SortField) bool sortReversed() const; void setSortReversed(bool rev); bool showDirs() const; void setShowDirs(bool); bool showDotAndDotDot() const; void setShowDotAndDotDot(bool); bool showOnlyReadable() const; void setShowOnlyReadable(bool);
Imperative actions upon the model are made available to QML via a Q_INVOKABLE tag on a normal member function. The isFolder(index) function says whether the value at index is a folder:
Q_INVOKABLE bool isFolder(int index) const;
Then we have the QDeclarativeParserStatus interface:
virtual void classBegin(); virtual void componentComplete();
Then the NOTIFY function for the folders property. The implementation will emit this when the folder property is changed.
Q_SIGNALS: void folderChanged();
The class ends with some implementation details:
private Q_SLOTS: void refresh(); void inserted(const QModelIndex &index, int start, int end); void removed(const QModelIndex &index, int start, int end); void handleDataChanged(const QModelIndex &start, const QModelIndex &end); private: Q_DISABLE_COPY(QDeclarativeFolderListModel) QDeclarativeFolderListModelPrivate *d; };
Lastly, the boilerplare to declare the type for QML use:
QML_DECLARE_TYPE(QDeclarativeFolderListModel)
To make this class available to QML, we only need to make a simple subclass of QDeclarativeExtensionPlugin:
class QmlFolderListModelPlugin : public QDeclarativeExtensionPlugin { Q_OBJECT public: virtual void registerTypes(const char *uri) { Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.folderlistmodel")); #ifndef QT_NO_DIRMODEL qmlRegisterType<QDeclarativeFolderListModel>(uri,1,0,"FolderListModel"); #endif } };
and then use the standard Qt plugin export macro:
Q_EXPORT_PLUGIN2(qmlfolderlistmodelplugin, QT_PREPEND_NAMESPACE(QmlFolderListModelPlugin));
Finally, in order for QML to connect the "import" statement to our plugin, we list it in the qmldir file:
src/imports/folderlistmodel/qmldir
This qmldir file and the compiled plugin will be installed in $QTDIR/imports/Qt/labs/folderlistmodel/ where the QML engine will find it (since $QTDIR/imports is the value of QLibraryInf::libraryPath()).
We'll not discuss the model implementation in detail, as it is not specific to QML - any Qt C++ model can be interfaced to QML. This implementation is basically just takes the krufty old QDirModel(obsolete), which is a tree with lots of detailed roles and re-presents it as a simpler list model where each item is just a fileName and a filePath (as a file: URL rather than a plain file, since QML works with URLs for all content).