Multi-selection-lists-in-Python-with-QML: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
[[Category:LanguageBindings::PySide]]<br />[[Category:snippets]]<br />[[Category:Developing_with_Qt::Qt Quick]]<br />[[Category:Developing_with_Qt::Qt Quick::QML]]<br />[[Category:Developing_with_Qt::Qt Quick::Tutorial]]<br />[toc align_right=&quot;yes&amp;quot; depth=&quot;2&amp;quot;]
[[Category:LanguageBindings::PySide]]
[[Category:snippets]]
[[Category:Developing_with_Qt::Qt Quick]]
[[Category:Developing_with_Qt::Qt Quick::QML]]
[[Category:Developing_with_Qt::Qt Quick::Tutorial]]
[toc align_right="yes" depth="2"]


= Multi-selection lists in Python with QML =
= Multi-selection lists in Python with QML =
Line 13: Line 18:
We can use the docstring as window title later on (using '''''doc'''''):
We can use the docstring as window title later on (using '''''doc'''''):


<code><br /># <s>*</s> coding: utf-8 <s>*</s>
<code>
# -*- coding: utf-8 -*-


&quot;&quot;&quot;Click-your-Zen of PySide&amp;quot;&quot;&quot;<br /></code>
"""Click-your-Zen of PySide"""
</code>


=== Imports ===
=== Imports ===
Line 21: Line 28:
We need '''sys''' for the command line arguments and we need '''this''' for some free sample data:
We need '''sys''' for the command line arguments and we need '''this''' for some free sample data:


<code><br />import sys<br />import this
<code>
import sys
import this


from PySide import QtCore<br />from PySide import QtGui<br />from PySide import QtDeclarative<br /></code>
from PySide import QtCore
from PySide import QtGui
from PySide import QtDeclarative
</code>


=== QObject wrapper with &quot;checked&amp;quot; property ===
=== QObject wrapper with "checked" property ===


Just as with the previous example, we now define a wrapper object. The difference here is that it has a &quot;checked&amp;quot; property and a convenience method for toggling the property (don't forget that you need to '''emit''' the &quot;changed&amp;quot; signal, otherwise QML would not know that something has changed!).
Just as with the previous example, we now define a wrapper object. The difference here is that it has a "checked" property and a convenience method for toggling the property (don't forget that you need to '''emit''' the "changed" signal, otherwise QML would not know that something has changed!).


<code><br />class ZenWrapper(QtCore.QObject):<br /> def ''init''(self, zenItem):<br /> QtCore.QObject.''init''(self)<br /> self._zenItem = zenItem<br /> self._checked = False
<code>
class ZenWrapper(QtCore.QObject):
def ''init''(self, zenItem):
QtCore.QObject.''init''(self)
self._zenItem = zenItem
self._checked = False


def _name(self):<br /> return self._zenItem
def _name(self):
return self._zenItem


def is_checked(self):<br /> return self._checked
def is_checked(self):
return self._checked


def toggle_checked(self):<br /> self._checked = not self._checked<br /> self.changed.emit()
def toggle_checked(self):
self._checked = not self._checked
self.changed.emit()


changed = QtCore.Signal()
changed = QtCore.Signal()


name = QtCore.Property(unicode, ''name, notify=changed)<br /> checked = QtCore.Property(bool, is_checked, notify=changed)<br /></code>
name = QtCore.Property(unicode, ''name, notify=changed)
<br />h3. List model, with helper to get checked items
checked = QtCore.Property(bool, is_checked, notify=changed)
<br />This is again mostly the same as in the previous example, but we add a new helper method '''checked''' that takes care of retrieving all the checked items from the zenItems list:
</code>
<br /><code><br />class ZenListModel(QtCore.QAbstractListModel):<br /> definit<span class="zenItems self,">:<br /> QtCore.QAbstractListModel.</span>init''_(self)<br /> self._zenItems = zenItems<br /> self.setRoleNames({0: 'zenItem'})


def rowCount(self, parent=QtCore.QModelIndex()):<br /> return len(self._zenItems)
h3. List model, with helper to get checked items


def checked(self):<br /> return [x for x in self._zenItems if x.checked]
This is again mostly the same as in the previous example, but we add a new helper method '''checked''' that takes care of retrieving all the checked items from the zenItems list:


def data(self, index, role):<br /> if index.isValid() and role == 0:<br /> return self.''zenItems[index.row()]<br /></code>
<code>
<br />h3. Controller for toggling and retrieving changes
class ZenListModel(QtCore.QAbstractListModel):
<br />This controller provides a '''toggled''' slot that will be called by QML when we click on an item. We toggle the checked state of the item and (for the sake of demonstration) get the list of currently-checked items and print it on the console. We also set the window title to show the amount of currently selected items:
definit<span class="zenItems self,">:
<br /><code><br />class Controller(QtCore.QObject):<br /> &amp;#64;QtCore.Slot(QtCore.QObject, QtCore.QObject)<br /> def toggled(self, model, wrapper):<br /> global view,doc''_<br /> wrapper.toggle_checked()<br /> new_list = model.checked()<br /> print '='*20, 'New List', '='*20<br /> print ''.join(x.name for x in new_list)<br /> view.setWindowTitle('%s (d)' (''doc'', len(new_list)))<br /></code>
QtCore.QAbstractListModel.</span>init''_(self)
self._zenItems = zenItems
self.setRoleNames({0: 'zenItem'})
 
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._zenItems)
 
def checked(self):
return [x for x in self._zenItems if x.checked]
 
def data(self, index, role):
if index.isValid() and role == 0:
return self.''zenItems[index.row()]
</code>
 
h3. Controller for toggling and retrieving changes
 
This controller provides a '''toggled''' slot that will be called by QML when we click on an item. We toggle the checked state of the item and (for the sake of demonstration) get the list of currently-checked items and print it on the console. We also set the window title to show the amount of currently selected items:
 
<code>
class Controller(QtCore.QObject):
&amp;#64;QtCore.Slot(QtCore.QObject, QtCore.QObject)
def toggled(self, model, wrapper):
global view,doc''_
wrapper.toggle_checked()
new_list = model.checked()
print '='*20, 'New List', '='*20
print ''.join(x.name for x in new_list)
view.setWindowTitle('%s (d)' (''doc'', len(new_list)))
</code>


=== Model and controller setup ===
=== Model and controller setup ===
Line 57: Line 106:
Here we generate some example data and instantiate the controller and the list model:
Here we generate some example data and instantiate the controller and the list model:


<code><br />zenItems = [ZenWrapper(zenItem) for zenItem in  ''.join([this.d.get(c, c) for c in  this.s]).splitlines()[2:]]
<code>
zenItems = [ZenWrapper(zenItem) for zenItem in  ''.join([this.d.get(c, c) for c in  this.s]).splitlines()[2:]]


controller = Controller()<br />zenItemList = ZenListModel(zenItems)<br /></code>
controller = Controller()
zenItemList = ZenListModel(zenItems)
</code>


=== QApplication / QDeclarativeView + Glue code ===
=== QApplication / QDeclarativeView + Glue code ===
Line 65: Line 117:
This piece of code creates a new QApplication instance, a new QDeclarativeView and also exposes the controller and the list model to the QML environment, so it can be accessed from there:
This piece of code creates a new QApplication instance, a new QDeclarativeView and also exposes the controller and the list model to the QML environment, so it can be accessed from there:


<code><br />app = QtGui.QApplication(sys.argv)
<code>
app = QtGui.QApplication(sys.argv)


view = QtDeclarative.QDeclarativeView()<br />view.setWindowTitle(''doc'')<br />view.setResizeMode(QtDeclarative.QDeclarativeView.SizeRootObjectToView)
view = QtDeclarative.QDeclarativeView()
view.setWindowTitle(''doc'')
view.setResizeMode(QtDeclarative.QDeclarativeView.SizeRootObjectToView)


rc = view.rootContext()
rc = view.rootContext()


rc.setContextProperty('controller', controller)<br />rc.setContextProperty('pythonListModel', zenItemList)<br />view.setSource(''file''.replace('.py', '.qml'))
rc.setContextProperty('controller', controller)
rc.setContextProperty('pythonListModel', zenItemList)
view.setSource(''file''.replace('.py', '.qml'))


view.show()<br />app.exec_()<br /></code>
view.show()
app.exec_()
</code>


== MultiList.qml ==
== MultiList.qml ==


<code><br />import Qt 4.7
<code>
import Qt 4.7


ListView {<br /> id: pythonList<br /> width: 300<br /> height: 500<br /> model: pythonListModel
ListView {
id: pythonList
width: 300
height: 500
model: pythonListModel


delegate: Component {<br /> Rectangle {<br /> width: pythonList.width<br /> height: 30<br /> color: model.zenItem.checked?&quot;#00B8F5&amp;quot;:(index%2?&quot;#eee&amp;quot;:&quot;#ddd&amp;quot;)<br /> Text {<br /> elide: Text.ElideRight<br /> text: model.zenItem.name<br /> color: (model.zenItem.checked?&quot;white&amp;quot;:&quot;black&amp;quot;)<br /> anchors {<br /> verticalCenter: parent.verticalCenter<br /> left: parent.left<br /> right: (model.zenItem.checked?checkbox.left:parent.right)<br /> leftMargin: 5<br /> }<br /> }<br /> Text {<br /> id: checkbox<br /> text: &quot;&amp;quot;<br /> font.pixelSize: parent.height<br /> font.bold: true<br /> visible: model.zenItem.checked<br /> color: &quot;white&amp;quot;<br /> anchors {<br /> verticalCenter: parent.verticalCenter<br /> right: parent.right<br /> rightMargin: 5<br /> }<br /> }<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: { controller.toggled(pythonListModel, model.zenItem) }<br /> }<br /> }<br /> }<br />}<br /></code>
delegate: Component {
Rectangle {
width: pythonList.width
height: 30
color: model.zenItem.checked?"#00B8F5":(index%2?"#eee":"#ddd")
Text {
elide: Text.ElideRight
text: model.zenItem.name
color: (model.zenItem.checked?"white":"black")
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
right: (model.zenItem.checked?checkbox.left:parent.right)
leftMargin: 5
}
}
Text {
id: checkbox
text: ""
font.pixelSize: parent.height
font.bold: true
visible: model.zenItem.checked
color: "white"
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 5
}
}
MouseArea {
anchors.fill: parent
onClicked: { controller.toggled(pythonListModel, model.zenItem) }
}
}
}
}
</code>


== How the example app looks ==
== How the example app looks ==


Starting the app using '''python MultiList.py''' should give something like this:
Starting the app using '''python MultiList.py''' should give something like this:

Revision as of 09:43, 25 February 2015

[toc align_right="yes" depth="2"]

Multi-selection lists in Python with QML

This is an extension of the Selectable list of Python objects in QML and focuses more on modifying the list and getting selected items out of the QML view.

MultiList.py

This is the Python code that takes care of setting up the model and controlling it.

Encoding declaration and module docstring

We can use the docstring as window title later on (using doc):

# -*- coding: utf-8 -*-

"""Click-your-Zen of PySide"""

Imports

We need sys for the command line arguments and we need this for some free sample data:

import sys
import this

from PySide import QtCore
from PySide import QtGui
from PySide import QtDeclarative

QObject wrapper with "checked" property

Just as with the previous example, we now define a wrapper object. The difference here is that it has a "checked" property and a convenience method for toggling the property (don't forget that you need to emit the "changed" signal, otherwise QML would not know that something has changed!).

class ZenWrapper(QtCore.QObject):
 def ''init''(self, zenItem):
 QtCore.QObject.''init''(self)
 self._zenItem = zenItem
 self._checked = False

def _name(self):
 return self._zenItem

def is_checked(self):
 return self._checked

def toggle_checked(self):
 self._checked = not self._checked
 self.changed.emit()

changed = QtCore.Signal()

name = QtCore.Property(unicode, ''name, notify=changed)
 checked = QtCore.Property(bool, is_checked, notify=changed)

h3. List model, with helper to get checked items

This is again mostly the same as in the previous example, but we add a new helper method checked that takes care of retrieving all the checked items from the zenItems list:

class ZenListModel(QtCore.QAbstractListModel):
 definit<span class="zenItems self,">:
 QtCore.QAbstractListModel.</span>init''_(self)
 self._zenItems = zenItems
 self.setRoleNames({0: 'zenItem'})

def rowCount(self, parent=QtCore.QModelIndex()):
 return len(self._zenItems)

def checked(self):
 return [x for x in self._zenItems if x.checked]

def data(self, index, role):
 if index.isValid() and role == 0:
 return self.''zenItems[index.row()]

h3. Controller for toggling and retrieving changes

This controller provides a toggled slot that will be called by QML when we click on an item. We toggle the checked state of the item and (for the sake of demonstration) get the list of currently-checked items and print it on the console. We also set the window title to show the amount of currently selected items:

class Controller(QtCore.QObject):
 &amp;#64;QtCore.Slot(QtCore.QObject, QtCore.QObject)
 def toggled(self, model, wrapper):
 global view,doc''_
 wrapper.toggle_checked()
 new_list = model.checked()
 print '='*20, 'New List', '='*20
 print ''.join(x.name for x in new_list)
 view.setWindowTitle('%s (d)' (''doc'', len(new_list)))

Model and controller setup

Here we generate some example data and instantiate the controller and the list model:

zenItems = [ZenWrapper(zenItem) for zenItem in  ''.join([this.d.get(c, c) for c in  this.s]).splitlines()[2:]]

controller = Controller()
zenItemList = ZenListModel(zenItems)

QApplication / QDeclarativeView + Glue code

This piece of code creates a new QApplication instance, a new QDeclarativeView and also exposes the controller and the list model to the QML environment, so it can be accessed from there:

app = QtGui.QApplication(sys.argv)

view = QtDeclarative.QDeclarativeView()
view.setWindowTitle(''doc'')
view.setResizeMode(QtDeclarative.QDeclarativeView.SizeRootObjectToView)

rc = view.rootContext()

rc.setContextProperty('controller', controller)
rc.setContextProperty('pythonListModel', zenItemList)
view.setSource(''file''.replace('.py', '.qml'))

view.show()
app.exec_()

MultiList.qml

import Qt 4.7

ListView {
 id: pythonList
 width: 300
 height: 500
 model: pythonListModel

delegate: Component {
 Rectangle {
 width: pythonList.width
 height: 30
 color: model.zenItem.checked?"#00B8F5":(index%2?"#eee":"#ddd")
 Text {
 elide: Text.ElideRight
 text: model.zenItem.name
 color: (model.zenItem.checked?"white":"black")
 anchors {
 verticalCenter: parent.verticalCenter
 left: parent.left
 right: (model.zenItem.checked?checkbox.left:parent.right)
 leftMargin: 5
 }
 }
 Text {
 id: checkbox
 text: "✔"
 font.pixelSize: parent.height
 font.bold: true
 visible: model.zenItem.checked
 color: "white"
 anchors {
 verticalCenter: parent.verticalCenter
 right: parent.right
 rightMargin: 5
 }
 }
 MouseArea {
 anchors.fill: parent
 onClicked: { controller.toggled(pythonListModel, model.zenItem) }
 }
 }
 }
}

How the example app looks

Starting the app using python MultiList.py should give something like this: