File selector in QML and PySide
Today I wrote a file selector in QML. This was not trivial because QML has no standard element for drilling down in a tree model. So I wrote one. A bit of Python was needed to expose the file system to the QML as a data model.
I've played with Bup a bit lately and wanted to write a GUI for it. Normal Qt widgets would do, but when the bup developers asked if it would run on MeeGo, I had a look at QML.
Update: check the comments for a new version.
The Python part of the code is simple and short:
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys from PySide import QtCore, QtGui, QtDeclarative app = QtGui.QApplication(sys.argv) model = QtGui.QDirModel() view = QtDeclarative.QDeclarativeView() view.rootContext().setContextProperty("dirModel", model) view.setSource(QtCore.QUrl.fromLocalFile("list.qml")) view.show() sys.exit(app.exec_())
The QML is rather long. I post it here so other QML developers can easily find it and experiment with it. The selector can be navigated with arrow keys and mouse. The lists can be flicked. Save this QML as 'list.qml' so that the Python code can find it.
import QtQuick 1.0 Rectangle { id: page width: 400; height: 240; anchors.fill: parent VisualDataModel { id: listModel model: dirModel Item { id: itemDelegate width: listView.width; height: 25 Rectangle { id: content anchors.fill: parent color: "transparent" Text { text: fileName } } states: State { name: "active"; when: itemDelegate.activeFocus PropertyChanges { target: content; color: "#FFDDDD" } } MouseArea { anchors.fill: parent onClicked: { listView.currentIndex = index itemDelegate.forceActiveFocus() if (model.hasModelChildren) { animModel.rootIndex = listModel.modelIndex(index) animation.running = true } } } Keys.onRightPressed: { if (model.hasModelChildren) { animModel.rootIndex = listModel.modelIndex(index) animation.running = true } } Keys.onLeftPressed: { // if statement does not work as intended if (listModel.parentModelIndex() != listModel.rootIndex) { listView.x = -listView.width listModel.rootIndex = listModel.parentModelIndex() leftAnimation.running = true } } Keys.onUpPressed: { if (index > 0) { listView.currentIndex = index - 1 } else if (listView.keyNavigationWraps) { listView.currentIndex = listView.count - 1 } animModel.rootIndex = listModel.modelIndex(listView.currentIndex) } Keys.onDownPressed: { if (listModel.count > index + 1) { listView.currentIndex = index + 1 } else if (listView.keyNavigationWraps) { listView.currentIndex = 0 } animModel.rootIndex = listModel.modelIndex(listView.currentIndex) } } } VisualDataModel { id: animModel model: dirModel Rectangle { width: listView.width; height: 25 Text { text: fileName } } rootIndex: listModel.modelIndex(0) } SequentialAnimation { id: animation NumberAnimation { target: listView property: "x" to: -listView.width duration: 100 } ScriptAction { script: { listView.x = 0 listModel.rootIndex = animModel.rootIndex animModel.rootIndex = listModel.modelIndex(0) } } } SequentialAnimation { id: leftAnimation NumberAnimation { target: listView property: "x" to: 0 duration: 100 } ScriptAction { script: { animModel.rootIndex = listModel.modelIndex(0) } } } ListView { id: listView x: 0; y: 0 width: page.width * 0.8 height: page.height model: listModel focus: true keyNavigationWraps: true } ListView { id: animBox x: listView.width; y: listView.y width: listView.width height: listView.height model: animModel } }