Как ссылаться на пользовательские индексы ролей между Python и QML - PullRequest
2 голосов
/ 05 апреля 2019

Я реализую QAbstractListModel в классе в Python для использования в QML.Я определил две пользовательские роли в модели.В этой модели я также реализовал функцию Slot 'get' для возврата данных из указанного индекса и роли.Когда я передаю роль обратно в функцию 'get', я получаю целое число, отличное от того, которое я определил для роли в моей модели.

Я пытался передать роли и индекс обратно из QML в функцию 'get', определенную в моей модели.Индекс работает должным образом, но роли возвращают значения, отличные от того, что я определил в моей модели.

gui.py

main ()

def main():
    # create the application instance
    app = QApplication(sys.argv)
    # create a QML engine
    engine = PoaGUI()

    # instantiate Route class
    # route = Route()

    # add example routes for routemodel
    routelist = [
        {'stringinput' : 'Route 1', 'selected' : True},
        {'stringinput' : 'Route 2', 'selected' : False},
        {'stringinput' : 'Route 3', 'selected' : True},
        {'stringinput' : 'Route 4', 'selected' : False}
    ]

    # instantiate ListModel class
    routemodel = ListModel(routelist)

    # register the python type bindings to QML engine
    engine.rootContext().setContextProperty('RouteModel', routemodel)

    # load main QML file and start app engine
    engine.load('view.qml')

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

QAbstractListModel

class ListModel(QAbstractListModel):

    # create new user roles as class variables
    StringInput = Qt.UserRole + 0
    Selected = Qt.UserRole + 1
    # ADD USERROLE FOR SELECTED TO COMPLETE

    def __init__(self, datain=[], parent=None):
        super().__init__(parent)

        self._list = datain

    def rowCount(self, parent=QModelIndex()):

        return len(self._list)

    def data(self, index=QModelIndex, role=Qt.DisplayRole):

        #store QModelIndex passed to data
        row = index.row()

        if index.isValid() and role == self.StringInput:
            return self._list[row]['stringinput']
        if index.isValid() and role == self.Selected:
            return self._list[row]['selected']
        else:
            return None

    def roleNames(self):

        return {
            self.StringInput: b'stringinput',
            self.Selected: b'selected'
        }

    @Slot(int, int, result='QVariant')
    def get(self, row, role):
        # show difference between role supplied 
        # from QML vs definition in model
        print(role)
        print('Selected: ' + str(self.Selected))
        print('StringInput: ' + str(self.StringInput))
        if role == self.StringInput:
            print('stringinput')
            return self._list[row]['stringinput']
        elif role == self.Selected:
            print('selected')
            return self._list[row]['selected']
        else:
            return None


    @Slot(int, bool)
    def modSelected(self, row, selval):
        # set index of the row changes
        ix = self.index(row, 0)
        self._list[row]['selected'] = selval
        # communicate that changes were made
        self.dataChanged.emit(ix, ix, self.roleNames())

view.qml

Реализация ListView

CustomComp.CustomList {
       id: routelist
       model: RouteModel
       keyNavigationWraps: true

       listwidth: 300
       listheight: 600
       delegate: CustomComp.SingleListDelegate {}

       Layout.alignment: Qt.AlignCenter
    }

Делегат для ListView

import QtQuick 2.12
import QtQuick.Controls 2.12

Rectangle {
    id: singleItemList

    property int delegateIndex: index
    property bool altcolor: false
    property string altcolorcode: "#242526"
    property string highlightcolorcode: "#242526"

    width: parent.width; height: 20
    color: (singleItemList.altcolor) ? ((index % 2 == 0) ? altcolorcode:"#000000") : "#000000"
    border.color: "#ffcc00"

    Text {
        id: listText
        anchors.verticalCenter: parent.verticalCenter
        anchors.horizontalCenter: parent.horizontalCenter
        font.pixelSize: 14
        color: "#ffcc00"
        // delegate directly uses provided string
        text: stringinput
    }

    MouseArea {
        id: mousearea

        anchors.fill: parent
        onClicked: {
            singleItemList.ListView.view.currentIndex = index
            console.log(singleItemList.ListView.view.currentIndex)
            singleItemList.delegateSelect()
        }
    }

    function delegateSelect() {
        if (singleItemList.ListView.view.model.get(index, selected)) {
            singleItemList.color = "#000000"
            singleItemList.ListView.view.model.modSelected(index, false)
            // singleItemList.ListView.view.model.set(index, {"selected": false})
        }
        else {
            singleItemList.color = highlightcolorcode
            singleItemList.ListView.view.model.modSelected(index, true)
            // singleItemList.ListView.view.model.set(index, {"selected": true})
        }
        // console.log(singleItemList.ListView.view.model.get(index, selected))
    }
}

При наличии двух пользовательских ролей, когда я передаю роль обратно в свою модель из QML, я получаю целые числа в порядке0 и 1. Я использовал Qt.UserRole, чтобы определить свои номера ролей для модели, которую я сгенерировал в Python.Поэтому целые числа для этих ролей, определенных в моей модели, равны 256 и 257. Кажется, что QML отлично справляется с моделью, потому что когда я предоставляю свою модель для ListView и ссылаюсь на одну из моих пользовательских ролей в делегате для отображения в виде текста, список заполняетсякак и ожидалось.Почему целые числа для двух моих пользовательских ролей в Python отличаются от целых в QML?В таком случае, как я могу успешно вернуть эти роли для использования другой функцией в моей модели?

Моя конечная цель - создать функции, аналогичные функциям «get» и «set» в QML ListView, нотак для модели, которую я построил в Python.

1 Ответ

1 голос
/ 05 апреля 2019

У вас проблема XY , ваша реальная цель - отредактировать значения модели из QML. Поэтому решение состоит в том, чтобы реализовать метод setData() и отредактировать данные с помощью selected = foo_value, в вашем конкретном случае: selected != selected

TL; DR;

Вы неправильно понимаете понятие «выбранный», «выбранный» в QML - это значение модели, связанной с выбранной ролью, то есть то, что оно означает:

def roleNames(self):
    return {
        ListModel.StringInput: b"stringinput",
        ListModel.Selected: b"selected",
    }

То есть в QML выбрана не роль, а значение, связанное с ролью и индексом, то есть эквивалентно:

selected = model.data(index, ListModel.Selected)

Следовательно, вы получаете 1 и 0, которые являются преобразованием логических значений true и false соответственно.

Это сделано для абстракции ролей.


Итак, вы понимаете, когда используете следующее:

QML code               Python Code
foo_value = selected ~ foo_value = model.data(index, ListModel.Selected)

Но когда вы используете:

QML code               Python Code
selected = foo_value ~ model.setData(index, foo_value, ListModel.Selected)

Решение

Итак, в вашем случае решение:

class ListModel(QAbstractListModel):

    StringInput = Qt.UserRole + 0
    Selected = Qt.UserRole + 1

    def __init__(self, datain=[], parent=None):
        super().__init__(parent)
        self._list = datain

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

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return QVariant()
        row = index.row()
        if 0 <= row < self.rowCount():
            if role == ListModel.StringInput:
                return self._list[row]["stringinput"]
            elif role == ListModel.Selected:
                return self._list[row]["selected"]
        return QVariant()

    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid():
            return False
        row = index.row()
        result = False
        if 0 <= row < self.rowCount():
            if role == ListModel.StringInput:
                self._list[row]["stringinput"] = value
                result = True
            elif role == ListModel.Selected:
                self._list[row]["selected"] = value
                result = True
        if result:
            self.dataChanged.emit(index, index, (role,))
        return result

    def roleNames(self):
        return {
            ListModel.StringInput: b"stringinput",
            ListModel.Selected: b"selected",
        }

SingleListDelegate.qml

import QtQuick 2.12
import QtQuick.Controls 2.12

Rectangle {
    id: singleItemList

    property int delegateIndex: index
    property color highlightcolor: "#242526"
    property color defaultcolor : "#000000"
    width: parent.width; height: 20
    color: selected ? highlightcolor : defaultcolor
    border.color: "#ffcc00"

    Text {
        id: listText
        anchors.verticalCenter: parent.verticalCenter
        anchors.horizontalCenter: parent.horizontalCenter
        font.pixelSize: 14
        color: "#ffcc00"
        text: stringinput
    }

    MouseArea {
        id: mousearea
        anchors.fill: parent
        onClicked: {
            singleItemList.ListView.view.currentIndex = index
            console.log(singleItemList.ListView.view.currentIndex)
            // invert the boolean
            selected = !selected
        }
    }
}
...