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
= QtGui.QApplication(sys.argv)
app = QtGui.QDirModel()
model = QtDeclarative.QDeclarativeView()
view "dirModel", model)
view.rootContext().setContextProperty("list.qml"))
view.setSource(QtCore.QUrl.fromLocalFile(
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;
.fill: parent
anchors
VisualDataModel {id: listModel
model: dirModel
Item {
id: itemDelegate
width: listView.width; height: 25
Rectangle {
id: content
.fill: parent
anchorscolor: "transparent"
Text { text: fileName }
}states: State {
name: "active"; when: itemDelegate.activeFocus
PropertyChanges { target: content; color: "#FFDDDD" }
}MouseArea {
.fill: parent
anchorsonClicked: {
.currentIndex = index
listView.forceActiveFocus()
itemDelegateif (model.hasModelChildren) {
.rootIndex = listModel.modelIndex(index)
animModel.running = true
animation
}
}
}Keys.onRightPressed: {
if (model.hasModelChildren) {
.rootIndex = listModel.modelIndex(index)
animModel.running = true
animation
}
}Keys.onLeftPressed: {
// if statement does not work as intended
if (listModel.parentModelIndex() != listModel.rootIndex) {
.x = -listView.width
listView.rootIndex = listModel.parentModelIndex()
listModel.running = true
leftAnimation
}
}Keys.onUpPressed: {
if (index > 0) {
.currentIndex = index - 1
listViewelse if (listView.keyNavigationWraps) {
} .currentIndex = listView.count - 1
listView
}.rootIndex = listModel.modelIndex(listView.currentIndex)
animModel
}Keys.onDownPressed: {
if (listModel.count > index + 1) {
.currentIndex = index + 1
listViewelse if (listView.keyNavigationWraps) {
} .currentIndex = 0
listView
}.rootIndex = listModel.modelIndex(listView.currentIndex)
animModel
}
}
}
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: {
.x = 0
listView.rootIndex = animModel.rootIndex
listModel.rootIndex = listModel.modelIndex(0)
animModel
}
}
}SequentialAnimation {
id: leftAnimation
NumberAnimation {
target: listView
property: "x"
to: 0
duration: 100
}ScriptAction {
script: {
.rootIndex = listModel.modelIndex(0)
animModel
}
}
}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
} }
Comments
new version
Here is a new version. It works much nicer already. It does expose some flaws in the QML implementation 4.7.2: it may crash and valgrind lists a plethora of problems.
C++ to call the QML:
and a new version that remembers the positions of the directories you have been:
By Jos van den Oever at Mon, 05/30/2011 - 06:18