Skip to content

Instantly share code, notes, and snippets.

@mottosso
Created March 21, 2015 15:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mottosso/e9efac04ba80682e92b8 to your computer and use it in GitHub Desktop.
Save mottosso/e9efac04ba80682e92b8 to your computer and use it in GitHub Desktop.
Filtering and logging in QML ListView

Filtering and logging in QML ListView

image

This example illustrates how to intercept log records and visualise them using Python and QML with QAbstractListModel, QSortFilterProxyModel and a custom logging handler.

Usage

Run filtering_log.py; it will load filtering_log.qml which will need to be located in the same directory.

import sys
import logging
from PyQt5 import QtCore, QtGui, QtQuick
class Model(QtCore.QAbstractListModel):
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self.items = []
# Keys available to us via LogRecord objects,
# added via `addItem` below.
self.roles = {
257: "threadName",
258: "name",
259: "thread",
260: "created",
261: "process",
262: "processName",
263: "args",
264: "module",
265: "filename",
266: "levelno",
267: "exc_text",
268: "pathname",
269: "lineno",
270: "msg",
271: "exc_info",
272: "funcName",
273: "relativeCreated",
274: "levelname",
275: "msecs",
}
def addItem(self, item):
self.beginInsertRows(QtCore.QModelIndex(),
self.rowCount(),
self.rowCount())
self.items.append(item.__dict__)
self.endInsertRows()
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.items)
def data(self, index, role=QtCore.Qt.DisplayRole):
try:
item = self.items[index.row()]
except IndexError:
return QtCore.QVariant()
if role in self.roles:
return item.get(self.roles[role], QtCore.QVariant())
return QtCore.QVariant()
def roleNames(self):
return self.roles
class MessageHandler(logging.Handler):
"""Custom handler to intercept log records that feed our `model`"""
def __init__(self, model, *args, **kwargs):
super(MessageHandler, self).__init__(*args, **kwargs)
self.model = model
def emit(self, record):
self.model.addItem(record)
app = QtGui.QGuiApplication(sys.argv)
view = QtQuick.QQuickView()
model = Model()
proxy = QtCore.QSortFilterProxyModel()
proxy.setSourceModel(model)
proxy.setFilterRole(model.roles.keys()[0])
proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
engine = view.engine()
context = engine.rootContext()
context.setContextProperty("qmodel", proxy)
view.setSource(QtCore.QUrl("filtering_log.qml"))
view.setResizeMode(view.SizeRootObjectToView)
view.show()
handler = MessageHandler(model)
log = logging.getLogger()
log.addHandler(handler)
log.setLevel(logging.DEBUG)
log.info("Some information here")
log.info("Some more information")
log.warning("Don't go into the light!")
log.critical("You, sir, are done for.")
log.error("Does not compute")
app.exec_()
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Rectangle {
width: 300
height: 300
color: "brown"
Row {
id: edit
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 10
TextField {
width: parent.width - combo.width
placeholderText: "Filter.."
onTextChanged: qmodel.setFilterFixedString(text)
}
ComboBox {
id: combo
width: 80
model: ["msg", "levelname"]
onCurrentIndexChanged: {
var index = [270, 274]
qmodel.filterRole = index[currentIndex]
}
}
}
Rectangle {
color: "white"
radius: 2
anchors.top: edit.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 10
anchors.topMargin: 20
ListView {
id: listView
clip: true
model: qmodel
anchors.fill: parent
anchors.margins: 5
delegate: itemDelegate
}
}
Component {
id: itemDelegate
MouseArea {
id: delegate
width: ListView.view.width
height: content.height + 10
clip: true
hoverEnabled: true
property color levelColor: levelno < 30 ? "steelblue" : "red"
Rectangle {
anchors.fill: parent
color: delegate.levelColor
opacity: delegate.containsMouse ? 0.1 : 0
}
Row {
id: content
width: parent.width
spacing: 5
anchors.verticalCenter: parent.verticalCenter
clip: true
Rectangle {
id: level
height: levelText.paintedHeight + 6
width: levelText.paintedWidth + 6
color: Qt.lighter(delegate.levelColor, 1.8)
Text {
id: levelText
text: levelname
color: delegate.levelColor
anchors.fill: parent
anchors.margins: 2
}
}
Text {
text: msg
elide: Text.ElideRight
width: delegate.width - level.width
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment