PyQt5 与 QML

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PyQt5 与 QML相关的知识,希望对你有一定的参考价值。

参考技术A QML 是一种描述性语言(declarative language),它可以让您使用传统的言语快速开发应用。下面介绍利用 PyQt5 与 QML 快速开发 GUI。

首先,创建一个名为 view.qml 的 QML 文件:

该文件描述的是“绿色的矩形框”,“文本为:Hello World”。接下来要使用 PyQt5 获取并创建 GUI:

效果:

将 QML 中的 QSortFilterProxyModel 与 PyQt5 一起使用

【中文标题】将 QML 中的 QSortFilterProxyModel 与 PyQt5 一起使用【英文标题】:Use a QSortFilterProxyModel from QML with PyQt5 【发布时间】:2016-04-24 13:04:07 【问题描述】:

我尝试在 PyQt5 中将 QML 视图与 QSortFilterProxyModel 结合起来。不幸的是,我无法让它以任何方式工作。 我现在的主要问题似乎是将项目从 QML 传回。但即使这是可能的,它似乎也不起作用,如果我直接在 python 中设置模型,我会得到TypeError: QSortFilterProxyModel.setSourceModel(QAbstractItemModel): argument 1 has unexpected type 'PyCapsule'

目前我有:

class SortFilterProxyModel(QSortFilterProxyModel):

    @pyqtProperty(QQmlListReference)
    def source (self):
        return self._source

    @source.setter
    def source (self, source):
        setSourceModel(source)
        self._source = source


class MyItem(QObject):

    nameChanged = pyqtSignal()

    def __init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name

    @pyqtProperty('QString', notify=nameChanged)
    def name(self):
        return self._name


class MyModel(QObject):

    itemsChanged = pyqtSignal()

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self._items = [MyItem('one'), MyItem('two'), MyItem('three')]

    @pyqtProperty(QQmlListProperty, notify=itemsChanged)
    def items(self):
        print('Query for items')
        return QQmlListProperty(MyItem, self, self._items)

    @pyqtSlot()
    def new_item(self):
        print('Append new item')
        self._items.append(MyItem('new'))
        self.itemsChanged.emit()

import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import MyModel 1.0
import MyItem 1.0
import SortFilterProxyModel 1.0

ApplicationWindow 

    function getCurrentIndex(list, element) 
        console.log('getCurrentIndex')
        if (list && element) 
            for (var i = 0; i < list.length; i++) 
                if (list[i].name === element.name) 
                    console.log('Found item at pos: ' + i)
                    return i
                
            
        
        return -1
    

    id: mainWindow
    width: 800; height: 600
    color: "gray"

    MyModel 
        id: mymodel
    

    SortFilterProxyModel 
        id: proxyModel
        source: mymodel.items
    

    TableView 
        anchors.fill: parent
        //model: mymodel.items
        model: proxyModel
        TableViewColumn 
            role: "name"
            title: "Name"
        
    


完整来源:

https://github.com/sturmf/python_samples/tree/master/pyqt_qsortfilterproxymodel

【问题讨论】:

QSortFilterProxyModel的源模型必须是QAbstractItemModel,其中QQmlListProperty和QQmlListReference不是派生自 【参考方案1】:

按照 Frank 的建议,让它工作的唯一方法是切换到 QAbstractListModel。这是代码的重要部分:

class MyItem(QObject):

    nameChanged = pyqtSignal()

    def __init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name

    @pyqtProperty('QString', notify=nameChanged)
    def name(self):
        return self._name

class MyModel(QAbstractListModel):
    NameRole = Qt.UserRole + 1
    _roles = NameRole: "name"

    def __init__(self, parent=None):
        print("constructing")
        super().__init__(parent)
        self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
        self._column_count = 1

    def roleNames(self):
        print("roleNames")
        return self._roles

    def rowCount(self, parent=QModelIndex()):
        print("rowCount", len(self._items))
        return len(self._items)

    def data(self, index, role=Qt.DisplayRole):
        print("in data")
        try:
            item = self._items[index.row()]
        except IndexError:
            return QVariant()

        if role == self.NameRole:
            return item.name

        return QVariant()

还有一个我可以在 QML 中使用的 SortFilterProxyModel

class SortFilterProxyModel(QSortFilterProxyModel):

    def __init__(self, parent):
        super().__init__(parent)

    @pyqtProperty(QAbstractItemModel)
    def source (self):
        return self._source

    @source.setter
    def source (self, source):
        self.setSourceModel(source)
        self._source = source

    def roleKey(self, role):
        roles = self.roleNames()
        for key, value in roles.items():
            if value == role:
                return key
        return -1

    @pyqtSlot(str, int)
    def sort(self, role, order):
        self.setSortRole(self.roleKey(role));
        super().sort(0, order);

现在我可以在 QML 中执行以下操作

MyModel 
    id: mymodel


SortFilterProxyModel 
    id: proxyModel
    source: mymodel


TableView 
    id: tableView
    anchors.fill: parent
    model: proxyModel
    sortIndicatorVisible: true
    onSortIndicatorOrderChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
    onSortIndicatorColumnChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
    TableViewColumn 
        title: "Name"
        role: "name"
    

【讨论】:

【参考方案2】:

我现在还实现了此处描述的另一种方式:http://blog.qt.io/blog/2014/04/16/qt-weekly-6-sorting-and-filtering-a-tableview/

SortFilterProxyModel 中的实现有点长,但 QML 源代码变得更好。此版本还包括一个过滤器实现 这也使它更长。

class MyItem(QObject):

    nameChanged = pyqtSignal()

    def __init__(self, name, parent=None):
        QObject.__init__(self, parent)
        self._name = name

    @pyqtProperty('QString', notify=nameChanged)
    def name(self):
        return self._name


class MyModel(QAbstractListModel):
    NameRole = Qt.UserRole + 1
    _roles = NameRole: "name"

    def __init__(self, parent=None):
        super().__init__(parent)
        self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
        self._column_count = 1

    def roleNames(self):
        return self._roles

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

    def data(self, index, role=Qt.DisplayRole):
        try:
            item = self._items[index.row()]
        except IndexError:
            return QVariant()

        if role == self.NameRole:
            return item.name

        return QVariant()

还有一个我可以在 QML 中使用的 SortFilterProxyModel

class SortFilterProxyModel(QSortFilterProxyModel):

    class FilterSyntax:
        RegExp, Wildcard, FixedString = range(3)

    Q_ENUMS(FilterSyntax)

    def __init__(self, parent):
        super().__init__(parent)

    @pyqtProperty(QAbstractItemModel)
    def source(self):
        return super().sourceModel()

    @source.setter
    def source(self, source):
        self.setSourceModel(source)

    @pyqtProperty(int)
    def sortOrder(self):
        return self._order

    @sortOrder.setter
    def sortOrder(self, order):
        self._order = order
        super().sort(0, order)

    @pyqtProperty(QByteArray)
    def sortRole(self):
        return self._roleNames().get(super().sortRole())

    @sortRole.setter
    def sortRole(self, role):
        super().setSortRole(self._roleKey(role))

    @pyqtProperty(QByteArray)
    def filterRole(self):
        return self._roleNames().get(super().filterRole())

    @filterRole.setter
    def filterRole(self, role):
        super().setFilterRole(self._roleKey(role))

    @pyqtProperty(str)
    def filterString(self):
        return super().filterRegExp().pattern()

    @filterString.setter
    def filterString(self, filter):
        super().setFilterRegExp(QRegExp(filter, super().filterCaseSensitivity(), self.filterSyntax))

    @pyqtProperty(int)
    def filterSyntax(self):
        return super().filterRegExp().patternSyntax()

    @filterSyntax.setter
    def filterSyntax(self, syntax):
        super().setFilterRegExp(QRegExp(self.filterString, super().filterCaseSensitivity(), syntax))

    def filterAcceptsRow(self, sourceRow, sourceParent):
        rx = super().filterRegExp()
        if not rx or rx.isEmpty():
            return True
        model = super().sourceModel()
        sourceIndex = model.index(sourceRow, 0, sourceParent)
        # skip invalid indexes
        if not sourceIndex.isValid():
            return True
        # If no filterRole is set, iterate through all keys
        if not self.filterRole or self.filterRole == "":
            roles = self._roleNames()
            for key, value in roles.items():
                data = model.data(sourceIndex, key)
                if rx.indexIn(data) != -1:
                    return True
            return False
        # Here we have a filterRole set so only search in that
        data = model.data(sourceIndex, self._roleKey(self.filterRole))
        return rx.indexIn(data) != -1

    def _roleKey(self, role):
        roles = self.roleNames()
        for key, value in roles.items():
            if value == role:
                return key
        return -1

    def _roleNames(self):
        source = super().sourceModel()
        if source:
            return source.roleNames()
        return 

现在我可以在 QML 中执行以下操作

MyModel 
    id: mymodel


SortFilterProxyModel 
    id: proxyModel
    source: mymodel

    sortOrder: tableView.sortIndicatorOrder
    sortCaseSensitivity: Qt.CaseInsensitive
    sortRole: tableView.getColumn(tableView.sortIndicatorColumn).role

    filterString: "*" + searchBox.text + "*"
    filterSyntax: SortFilterProxyModel.Wildcard
    filterCaseSensitivity: Qt.CaseInsensitive
    filterRole: tableView.getColumn(tableView.sortIndicatorColumn).role


TableView 
    id: tableView
    anchors.fill: parent
    model: proxyModel
    sortIndicatorVisible: true
    TableViewColumn 
        role: "name"
        title: "Name"
    

【讨论】:

以上是关于PyQt5 与 QML的主要内容,如果未能解决你的问题,请参考以下文章

PyQt5番外篇:PyQt5与Opencv的小小融合

ImportError 与“导入 PyQt5.QtSql”

pyqt5学习——pyqt5中.qrc资源文件的创建与编写

PyQt5 与 QThread 崩溃

PyQt5 - 将逻辑与 ui 分离导致功能无法正常工作

PyCharm+PyQt5(5.15.2)+mysql, PyQt5连接mysql,踩坑与解决办法