PyQt’s Model/View Framework -- A Quick...

Preview:

Citation preview

Chen Chun-Chia

PYQT’S MODEL/VIEW FRAMEWORK -- A QUICK OVERVIEW

Qt / PyQt / PySide

Qt

PyQt / PyKDE

PySide

1995 2000 2005 2010

1.0

2.0

2.0 3.0 4.0

3.0 4.0

1.0 1.0.8

4.8.5

4.7.4

A Simple PyQt App

import sys from PyQt4 import QtGui, QtCore app = QtGui.QApplication(sys.argv) sys.exit(app.exec_())

Your GUI Widgets

A Simple PyQt App

import sys from PyQt4.QtGui import * from PyQt4.QtCore import * app = QApplication(sys.argv) sys.exit(app.exec_())

Your GUI Widgets

OR Not Recommend

A Simple PyQt App – Example

import sys from PyQt4.QtGui import * from PyQt4.QtCore import * app = QApplication(sys.argv) sys.exit(app.exec_())

win = QWidget() win.show()

Customize Widget

class AWidget(QWidget): def __init__(self, parent=None):

or any other widget you want ↙

Customize Widget ~ super.__init__

class AWidget(QWidget): def __init__(self, parent=None): super(AWidget, self).__init__(parent)

↖ remember to call superclass.__init__()

Customize Widget ~ more Fields / Methods

class AWidget(QWidget): def __init__(self, parent=None): super(AWidget, self).__init__(parent) self.a_field = ... def a_method(self, ...): ...

Customize Widget ~ Show

class AWidget(QWidget): def __init__(self, parent=None): super(AWidget, self).__init__(parent) self.a_field = ... def a_method(self, ...): ...

app = QApplication(sys.argv) win = AWidget() win.show() sys.exit(app.exec_())

Model / View Architecture

Data

Model

View

Rendering

Model / View Architecture with Delegate

Data

Model

View

Rendering

Rendering

Editing

Delegate

Display Data

Data

Model

View

Rendering

Rendering

Editing

Delegate

Example ~ A List

Data

Model

View

data = [70, 90, 20, 50]

Delegate

Build List Model class MyListModel(QAbstractListModel): def __init__(self, parent=None): def rowCount(self, parent=QModelIndex()): def data(self, index, role=Qt.DisplayRole): Data

View

Delegate

Model

Build List Model class MyListModel(QAbstractListModel): def __init__(self, parent=None): super(MyListModel, self).__init__(parent) self._data = [70, 90, 20, 50] def rowCount(self, parent=QModelIndex()): def data(self, index, role=Qt.DisplayRole): Data

View

Delegate

Model

Build List Model ~ rowCount class MyListModel(QAbstractListModel): def __init__(self, parent=None): super(MyListModel, self).__init__(parent) self._data = [70, 90, 20, 50] def rowCount(self, parent=QModelIndex()): return len(self._data) def data(self, index, role=Qt.DisplayRole):

[int]

Data

View

Delegate

Model

Build List Model ~ data class MyListModel(QAbstractListModel): def __init__(self, parent=None): super(MyListModel, self).__init__(parent) self._data = [70, 90, 20, 50] def rowCount(self, parent=QModelIndex()): return len(self._data) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not 0 <= index.row() < self.rowCount(): return QVariant() row = index.row() if role == Qt.DisplayRole: return str(self._data[row]) return QVariant()

[str / QString]

Data

View

Delegate

Model

Show List View app = QApplication(sys.argv) model = MyListModel() view = QListView() view.setModel(model) view.show() sys.exit(app.exec_())

Data

Delegate

Model

View

Display Data with Roles class MyListModel(QAbstractListModel): def __init__(self, parent=None): super(MyListModel, self).__init__(parent) self._data = [70, 90, 20, 50] def rowCount(self, parent=QModelIndex()): return len(self._data) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not 0 <= index.row() < self.rowCount(): return QVariant() row = index.row() if role == Qt.DisplayRole: return str(self._data[row]) return QVariant()

Qt.ItemDataRole • General purpose roles

• Qt.DisplayRole Text of the item (QString) • Qt.DecorationRole Icon of the item (QColor, QIcon or QPixmap) • Qt.EditRole Editing data for editor (QString) • Qt.ToolTipRole Tooltip of the item (QString) • Qt.StatusTipRole Text in the status bar (QString) • Qt.WhatsThisRole Text in "What's This?" mode (QString) • Qt.SizeHintRole Size hint in view (QSize)

Qt.ItemDataRole (More...) • Roles describing appearance and meta data:

• Qt.FontRole Font of the item (QFont) • Qt.TextAlignmentRole Text alignment of the item (Qt.AlignmentFlag) • Qt.BackgroundRole Background of the item (QBrush) • Qt.ForegroundRole Foreground of the item (QBrush) • Qt.CheckStateRole Checked state of the item (Qt.CheckState)

• Accessibility roles: • Qt.AccessibleTextRole Text for accessibility (QString) • Qt.AccessibleDescriptionRole Description for accessibility (QString)

• User roles: • Qt.UserRole 1st role for specific purposes

Qt.DecorationRole • General purpose roles

• Qt.DisplayRole Text of the item (QString) • Qt.DecorationRole Icon of the item (QColor, QIcon or QPixmap) • Qt.EditRole Editing data for editor (QString) • Qt.ToolTipRole Tooltip of the item (QString) • Qt.StatusTipRole Text in the status bar (QString) • Qt.WhatsThisRole Text in "What's This?" mode (QString) • Qt.SizeHintRole Size hint in view (QSize)

Qt.ToolTipRole • General purpose roles

• Qt.DisplayRole Text of the item (QString) • Qt.DecorationRole Icon of the item (QColor, QIcon or QPixmap) • Qt.EditRole Editing data for editor (QString) • Qt.ToolTipRole Tooltip of the item (QString) • Qt.StatusTipRole Text in the status bar (QString) • Qt.WhatsThisRole Text in "What's This?" mode (QString) • Qt.SizeHintRole Size hint in view (QSize)

Qt.TextAlignmentRole • Roles describing appearance and meta data:

• Qt.FontRole Font of the item (QFont) • Qt.TextAlignmentRole Text alignment of the item (Qt.AlignmentFlag) • Qt.BackgroundRole Background of the item (QBrush) • Qt.ForegroundRole Foreground of the item (QBrush) • Qt.CheckStateRole Checked state of the item (Qt.CheckState)

• Accessibility roles: • Qt.AccessibleTextRole Text for accessibility (QString) • Qt.AccessibleDescriptionRole Description for accessibility (QString)

• User roles: • Qt.UserRole 1st role for specific purposes

Qt.BackgroundRole • Roles describing appearance and meta data:

• Qt.FontRole Font of the item (QFont) • Qt.TextAlignmentRole Text alignment of the item (Qt.AlignmentFlag) • Qt.BackgroundRole Background of the item (QBrush) • Qt.ForegroundRole Foreground of the item (QBrush) • Qt.CheckStateRole Checked state of the item (Qt.CheckState)

• Accessibility roles: • Qt.AccessibleTextRole Text for accessibility (QString) • Qt.AccessibleDescriptionRole Description for accessibility (QString)

• User roles: • Qt.UserRole 1st role for specific purposes

Qt.ForegroundRole • Roles describing appearance and meta data:

• Qt.FontRole Font of the item (QFont) • Qt.TextAlignmentRole Text alignment of the item (Qt.AlignmentFlag) • Qt.BackgroundRole Background of the item (QBrush) • Qt.ForegroundRole Foreground of the item (QBrush) • Qt.CheckStateRole Checked state of the item (Qt.CheckState)

• Accessibility roles: • Qt.AccessibleTextRole Text for accessibility (QString) • Qt.AccessibleDescriptionRole Description for accessibility (QString)

• User roles: • Qt.UserRole 1st role for specific purposes

Qt.CheckStateRole • Roles describing appearance and meta data:

• Qt.FontRole Font of the item (QFont) • Qt.TextAlignmentRole Text alignment of the item (Qt.AlignmentFlag) • Qt.BackgroundRole Background of the item (QBrush) • Qt.ForegroundRole Foreground of the item (QBrush) • Qt.CheckStateRole Checked state of the item (Qt.CheckState)

• Accessibility roles: • Qt.AccessibleTextRole Text for accessibility (QString) • Qt.AccessibleDescriptionRole Description for accessibility (QString)

• User roles: • Qt.UserRole 1st role for specific purposes

Qt.UserRole • Roles describing appearance and meta data:

• Qt.FontRole Font of the item (QFont) • Qt.TextAlignmentRole Text alignment of the item (Qt.AlignmentFlag) • Qt.BackgroundRole Background of the item (QBrush) • Qt.ForegroundRole Foreground of the item (QBrush) • Qt.CheckStateRole Checked state of the item (Qt.CheckState)

• Accessibility roles: • Qt.AccessibleTextRole Text for accessibility (QString) • Qt.AccessibleDescriptionRole Description for accessibility (QString)

• User roles: • Qt.UserRole 1st role for specific purposes

↖ UserRole, UserRole+1, UserRole+2, ...

For user roles, it is up to the developer to decide which types to use and ensure that components use the correct types when accessing and setting data.

Display Data with Delegate

View Rendering

Editing

Delegate

Data

Model

Rendering

Build Item Delegate ~ paint class MyDelegate(QStyledItemDelegate): def paint(self, painter, option, index):

Data

Model

View

Delegate

Build Item Delegate ~ Get Model Data class MyDelegate(QStyledItemDelegate): def paint(self, painter, option, index): item_var = index.data(Qt.DisplayRole) item_str = item_var.toPyObject() opts = QStyleOptionProgressBarV2() opts.rect = option.rect opts.minimum = 0 opts.maximum = 100 opts.text = item_str opts.textAlignment = Qt.AlignCenter opts.textVisible = True opts.progress = int(item_str) QApplication.style().drawControl( QStyle.CE_ProgressBar, opts, painter)

Data

Model

View

Delegate

[QVariant]

Build Item Delegate ~ Trans to Python Type class MyDelegate(QStyledItemDelegate): def paint(self, painter, option, index): item_var = index.data(Qt.DisplayRole) item_str = item_var.toPyObject() opts = QStyleOptionProgressBarV2() opts.rect = option.rect opts.minimum = 0 opts.maximum = 100 opts.text = item_str opts.textAlignment = Qt.AlignCenter opts.textVisible = True opts.progress = int(item_str) QApplication.style().drawControl( QStyle.CE_ProgressBar, opts, painter)

Data

Model

View

Delegate

[Python Type of Item]

Build Item Delegate ~ Draw Control class MyDelegate(QStyledItemDelegate): def paint(self, painter, option, index): item_var = index.data(Qt.DisplayRole) item_str = item_var.toPyObject() opts = QStyleOptionProgressBarV2() opts.rect = option.rect opts.minimum = 0 opts.maximum = 100 opts.text = item_str opts.textAlignment = Qt.AlignCenter opts.textVisible = True opts.progress = int(item_str) QApplication.style().drawControl( QStyle.CE_ProgressBar, opts, painter)

Data

Model

View

Delegate

Draw Progress Bar

Set Delegate into View app = QApplication(sys.argv) model = MyListModel() delegate = MyDelegate() view = QListView() view.setModel(model) view.setItemDelegate(delegate) view.show() sys.exit(app.exec_())

Data

Model

View

Delegate

Edit Data Use Default Editor = QLineEdit

Data

Model

View

Rendering

Rendering

Editing

Delegate

Activate Default Editor class MyListModel(QAbstractListModel): def flags(self, index): flag = super(MyListModel, self).flags(index) return flag | Qt.ItemIsEditable

Data

View

Model

Delegate

Activate Default Editor class MyListModel(QAbstractListModel): def flags(self, index): flag = super(MyListModel, self).flags(index) return flag | Qt.ItemIsEditable

Data

View

Model

Delegate

Activate Default Editor class MyListModel(QAbstractListModel): def flags(self, index): flag = super(MyListModel, self).flags(index) return flag | Qt.ItemIsEditable

Data

View

Model

? ?

empty

not change

Delegate

Load Model Data to Editor class MyListModel(QAbstractListModel): def data(self, index, role=Qt.DisplayRole): ... elif role == Qt.EditRole: return str(self._data[row]) ...

View

Model

Data

Delegate

Load Model Data to Editor class MyListModel(QAbstractListModel): def data(self, index, role=Qt.DisplayRole): ... elif role == Qt.EditRole: return str(self._data[row]) ...

View

Model

Data

✔ ?

good

still not change ...

Delegate

After Editing ~ Set Data to Model class MyListModel(QAbstractListModel): def setData(self, index, value, role=Qt.EditRole): ... if role == Qt.EditRole: value_int, ok = value.toInt() if ok: self._data[row] = value_int self.dataChanged.emit(index, index) return True return False ...

Model

Data

Delegate

View

After Editing ~ Set Data to Model class MyListModel(QAbstractListModel): def setData(self, index, value, role=Qt.EditRole): ... if role == Qt.EditRole: value_int, ok = value.toInt() if ok: self._data[row] = value_int self.dataChanged.emit(index, index) return True return False ...

[QVariant → int]

Model

Data

Delegate

View

After Editing ~ Set Data to Model class MyListModel(QAbstractListModel): def setData(self, index, value, role=Qt.EditRole): ... if role == Qt.EditRole: value_int, ok = value.toInt() if ok: self._data[row] = value_int self.dataChanged.emit(index, index) return True return False ...

Update internal data ↙

Model

Data

Delegate

View

After Editing ~ Set Data to Model class MyListModel(QAbstractListModel): def setData(self, index, value, role=Qt.EditRole): ... if role == Qt.EditRole: value_int, ok = value.toInt() if ok: self._data[row] = value_int self.dataChanged.emit(index, index) return True return False ...

↖ Notify view that data have changed

Model

Data

Delegate

View

After Editing ~ Set Data to Model class MyListModel(QAbstractListModel): def setData(self, index, value, role=Qt.EditRole): ... if role == Qt.EditRole: value_int, ok = value.toInt() if ok: self._data[row] = value_int self.dataChanged.emit(index, index) return True return False ...

↖ Return whether data have successfully set or not

Model

Data

Delegate

View

Edit Data with Default Editor

Model

View

Data

✔ ✔

good

it changed

Delegate

Edit Data with Default Editor

Model

View

Data

Model.data

Model.flags

Delegate

Edit Data with Default Editor

Model

View

Data

Model.setData

Model.flags

Model.data

Delegate

Edit Data with Delegate

Data

Model

View

Rendering

Editing

Delegate

Rendering

Recall : Default Editor

Model.flags

Model.data Model.setData

Recall : Default Editor Create editor ↙

Model.flags

Model.data Model.setData

Set editor data ↙ Set model data

Build Edit Delegate class MyEditDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): def setEditorData(self, editor, index): def setModelData(self, editor, model, index):

Data

Model

View

Delegate

Build Edit Delegate ~ createEditor class MyEditDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): sbox = QSpinBox(parent) sbox.setRange(0, 100) return sbox def setEditorData(self, editor, index): def setModelData(self, editor, model, index):

↖ ← Create editor ... and return itself

Data

Model

View

Delegate

Build Edit Delegate ~ setEditorData class MyEditDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): sbox = QSpinBox(parent) sbox.setRange(0, 100) return sbox def setEditorData(self, editor, index): item_var = index.data(Qt.DisplayRole) item_str = item_var.toPyObject() item_int = int(item_str) editor.setValue(item_int) def setModelData(self, editor, model, index):

← Set editor data

View

Delegate

Model

Data

Build Edit Delegate ~ setModelData class MyEditDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): sbox = QSpinBox(parent) sbox.setRange(0, 100) return sbox def setEditorData(self, editor, index): item_var = index.data(Qt.DisplayRole) item_str = item_var.toPyObject() item_int = int(item_str) editor.setValue(item_int) def setModelData(self, editor, model, index): data_int = editor.value() data_var = QVariant(data_int) model.setData(index, data_var)

↖ Set model data

View

Delegate

Model

Data

Set Delegate into View app = QApplication(sys.argv) model = MyListModel() delegate = MyEditDelegate() view = QListView() view.setModel(model) view.setItemDelegate(delegate) view.show() sys.exit(app.exec_())

Data

Model

View

Delegate

Edit Data with Delegate

Model

Delegate

View

Data

← QSpinBox ↙

Sort ? Filter ? Use QSortFilterProxyModel

Data

Model

View

Proxy Model

Rendering

Editing

Delegate

Sort Data ~ QSortFilterProxyModel

Data

Model

View

Proxy Model

class SortProxyModel(QSortFilterProxyModel): def lessThan(self, left_index, right_index):

Sort Data ~ QSortFilterProxyModel

Data

Model

View

Proxy Model

class SortProxyModel(QSortFilterProxyModel): def lessThan(self, left_index, right_index): left_var = left_index.data(Qt.DisplayRole) right_var = right_index.data(Qt.DisplayRole) left_str = left_var.toPyObject() right_str = right_var.toPyObject() left_int = int(left_str) right_int = int(right_str) return (left_int < right_int)

↖ return True or False

Apply SortProxyModel to Model and View

Proxy Model

app = QApplication(sys.argv) model = MyListModel() proxy = SortProxyModel() proxy.setSourceModel(model) proxy.sort(0) view = QListView() view.setModel(proxy) view.show() sys.exit(app.exec_())

View

Model

Data

← Sort data by column 0

Sort Data

Proxy Model

View

Model

Data

↖ Data sorted

Filter Data ~ QSortFilterProxyModel

Data

Model

View

Proxy Model

class FilterProxyModel(QSortFilterProxyModel): def filterAcceptsRow(self, src_row, src_parent):

Filter Data ~ QSortFilterProxyModel

Data

Model

View

Proxy Model

class FilterProxyModel(QSortFilterProxyModel): def filterAcceptsRow(self, src_row, src_parent): src_model = self.sourceModel() src_index = src_model.index(src_row, 0) item_var = src_index.data(Qt.DisplayRole) item_int = int(item_var.toPyObject()) return (item_int >= 60)

↖ return True or False

Apply FilterProxyModel

Proxy Model

app = QApplication(sys.argv) model = MyListModel() proxy = FilterProxyModel() proxy.setSourceModel(model) proxy.setDynamicSortFilter(True) view = QListView() view.setModel(proxy) view.show() sys.exit(app.exec_())

View

Model

Data

↖ If True, data will re-filter when original model is changed

Filter Data

Proxy Model

View

Model

Data

↖ Data filtered

Sort & Filter ~ Recap class ProxyModel(QSortFilterProxyModel): def lessThan(self, left_index, right_index) def filterAcceptsRow(self, src_row, src_parent)

Proxy Model

View

Model

Data

Live DEMO Server Loading Monitor

Server A Server B Server C

Loading % Loading % Loading %

A %

B %

C %

References • PyQt (Riverbank) http://www.riverbankcomputing.co.uk/

• Qt http://qt.nokia.com/

• PySide http://www.pyside.org/

• Rapid GUI Programming with Python and Qt

• by Mark Summerfield • ISBN: 978-0132354189

• Advanced Qt Programming • by Mark Summerfield • ISBN: 978-0321635907

References • C++ GUI Programming with Qt 4

• by Jasmin Blanchette and Mark Summerfield • ISBN: 978-0132354165

Q & A

Contacts • PTT cccx • Plurk ccc_ • Facebook ccc.larc • Google ccc.larc • Email ccc.larc@gmail.com

陳俊嘉 a.k.a CCC

Recommended