Return an object in a role in Python and get a ref

2019-02-20 17:47发布

I'm writing a Twitter client. I implemented TweetItem and TweetModel. The issue is that there is a role in TweetItem called original. I want it to point to the original tweet.

Update: There were some typo in my code. Now I fixed them.

import sys
from PyQt4 import QtCore, QtGui, QtDeclarative


class TweetModel(QtCore.QAbstractListModel):
    def __init__(self, prototype, parent=None):
        QtCore.QAbstractListModel.__init__(self, parent)
        self.setRoleNames(prototype.roleNames())
        self.tweets = []

    def appendRow(self, item):
        self.tweets.append(item)

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

    def data(self, index, role):
        return self.tweets[index.row()].data(role)


class TweetItem(QtCore.QAbstractItemModel):
    def __init__(self, id=None, original=None, parent=None):
        QtCore.QAbstractItemModel.__init__(self, parent)
        self.idRole = QtCore.Qt.UserRole + 1
        # More Roles
        self.originalRole = QtCore.Qt.UserRole + 6

        self.id = id
        self.original = original

    def roleNames(self):
        names = {}
        names[self.idRole] = "id"
        names[self.originalRole] = "original"
        return names

    def data(self, role):
        if role == self.idRole:
            return self.id
        elif role == self.originalRole:
            # self.original == <__main__.TweetItem object at 0x7fb703d95d40>
            return self.original
        else:
            return None


if __name__ == "__main__":
    model = TweetModel(TweetItem())
    item = TweetItem("0001", None, model)
    model.appendRow(TweetItem("0002", item, model))

    App = QtGui.QApplication(sys.argv)
    view = QtDeclarative.QDeclarativeView()
    view.rootContext().setContextProperty("mymodel", model)
    view.setSource(QtCore.QUrl.fromLocalFile("main.qml"))
    view.show()
    App.exec_()

But I can not use it in QML. I get an undefined value.

import QtQuick 1.0

Rectangle {
  width: 360
  height: 360

  ListView {
         anchors.fill: parent
         model: mymodel
         // original.id == undefined
         delegate: Component { Text { text: id + " " + original.id } }
  } 
}

So, is it possible to return an object in a role and use it?

标签: python qt pyqt qml
3条回答
ら.Afraid
2楼-- · 2019-02-20 18:30
  1. Straight answer: you got undefined because, because your TweetItem.original is in fact None.
  2. I'm not sure what you are trying to accomplish, but I want to clear some possible misunderstandings. QAbstractItemModel is a model class too, it's somewhat strange to see

    def data(self, index, role):
        return self.tweets[index.row()].data(role)
    

there.

查看更多
冷血范
3楼-- · 2019-02-20 18:39

As was suggested by dant3 use QObject with properties.

Here is an example how to do it:

import sys
from PyQt4 import QtCore, QtGui, QtDeclarative
from PyQt4.QtCore import pyqtProperty, pyqtSignal, QObject


class TweetModel(QtCore.QAbstractListModel):
    def __init__(self, prototype, parent=None):
        QtCore.QAbstractListModel.__init__(self, parent)
        self.setRoleNames(prototype.roles)
        self.tweets = []

    def appendRow(self, item):
        self.tweets.append(item)

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

    def data(self, index, role):
        return self.tweets[index.row()].data(role)


class TweetItem(QObject):
    roles = {
        QtCore.Qt.UserRole + 1: 'id',
        QtCore.Qt.UserRole + 6: 'original',
    }

    id_changed = pyqtSignal()

    def __init__(self, id=None, original=None, parent=None):
        QObject.__init__(self, parent=parent)

        self._data = {'original': original}
        self.id = id

    def data(self, key):
        return self._data[self.roles[key]]

    @pyqtProperty(str, notify=id_changed)
    def id(self):
        return self._data['id']

    @id.setter
    def id(self, value):
        if self._data.get('id') != value:
            self._data['id'] = value
            self.id_changed.emit()

if __name__ == "__main__":
    model = TweetModel(TweetItem)
    item = TweetItem("0001", None, model)
    model.appendRow(TweetItem("0002", item, model))

    App = QtGui.QApplication(sys.argv)
    view = QtDeclarative.QDeclarativeView()
    view.rootContext().setContextProperty("mymodel", model)
    view.setSource(QtCore.QUrl.fromLocalFile("main.qml"))
    view.show()
    App.exec_()

The QML file remains the same.

I didn't make original a property, since you get it as model data, but you can make it in the same fashion as id.

查看更多
我想做一个坏孩纸
4楼-- · 2019-02-20 18:42

My guess is that you got undefined not on accessing your TweetItem object, but on trying to get id out of it.

As it looks, your implementation of QAbstractItemModel in TweetItem is simply incorrect. Not does it's data method accepts any index, nor it implements rowCount. Consult Qt documentation on how to implement QAbstractItemModel interface correctly if you still wan't to use it.

My advice, to achieve what you want, is to subclass QObject instead and provide your additional data as it's named properties to use them in QML. See this page of Qt guidelines on how to bind your QObject with QML.

查看更多
登录 后发表回答