Почему я получаю «Ошибка типа: значение не определено и не может быть преобразовано в объект» в Qt QML?

Я сделал простой список в Qt Qml. Я использую PySide2 и QStringListModel для заполнения списка. Когда список больше, как в этом простом примере, щелкнув printBtn (который должен изменить цвет текста на зеленый) в Qt Qml, получите следующую ошибку: «TypeError: значение не определено и не может быть преобразовано в объект». Если список содержит меньше элементов, вроде все нормально. Как изменить мой код, чтобы не было этой проблемы в будущем.

Возможно, это ошибка в Qt? Есть ли обходной путь?

См. мой простой фрагмент кода ниже:

Питон:

import sys
from PySide2.QtCore import Qt, QStringListModel, QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine

class Main(QObject):

    clicked = Signal()

    def __init__(self, parent=None):
        super(Main, self).__init__(parent)

    @Slot()
    def justClicking(self):
        self.clicked.emit()

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    main = Main()
    engine.rootContext().setContextProperty("main", main)

    data = ["London", "New York", "Paris", "Tokyo", "Sydney", "Madrid", "Mumbai", "Istanbul", "Bangkok", "Moscow", "Toronto", "Rome", "Shanghai", "Rio de Janeiro", "Seoul", "Berlin", "Cape Town", "Amsterdam", "Los Angeles", "San Francisco", "Cairo", "Barcelona", "Buenos Aires", "Melbourne", "Athens", "Vancouver", "Dubai", "Prague", "Edinburgh", "Singapore"]

    model = QStringListModel()
    model.setStringList(data)

    context = engine.rootContext()
    context.setContextProperty("myModel", model)

    engine.load("main.qml")
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

Qml-код:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15

Window {
    visible: true
    color: "#161515"
    width: 640
    height: 480
    title: qsTr("Listview Example")

    Rectangle {
        id: rectangle
        x: 51
        y: 63
        width: 539
        height: 143
        color: "#ffffff"

        ListView {
            id: listView
            anchors.fill: parent
            model: myModel
            currentIndex: 0
            delegate: Rectangle {
                id: dlg
                width: listView.width
                height: 25
                color: index === listView.currentIndex ? "lightgrey" : "#00000000"
                radius: 10

                property alias colorOfText: titleText.color

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        listView.currentIndex = index
                    }
                }
                Text {
                    id: titleText
                    text: model.display
                    font.pixelSize: 18
                    color: "black"
                    padding: 5
                }
                Connections {
                    target: main
                    function onClicked() {
                        var myListItemObjectC = listView.contentItem.children[listView.currentIndex]
                        myListItemObjectC.colorOfText = "green"
                    }
                }
            }
        }
    }
    Button {
        id: printBtn
        x: 286
        y: 241
        width: 69
        height: 35
        text: qsTr("Print")
        onClicked: {
            main.justClicking()
        }
    }
}

Я пытался не использовать QStringListModel, безуспешно пытался добавить их в ListModel. Я ценю любую помощь!

Я перенес ваше приложение на pyside6, и оно работает.

Stephen Quan 12.04.2023 09:09

Можете ли вы поделиться своим кодом? @СтивенКуан

rolando_cser 12.04.2023 09:13

Я также перенес на PySide6, и он не работает, все еще сталкиваясь с той же проблемой.

rolando_cser 12.04.2023 09:47

Я запускал его на Ubuntu с pyside6/Qt6.4.0 и на macOS с pyside6/Qt6.5.0. Я разместил свой код в качестве ответа, но мне неловко, поскольку код на самом деле является вашим кодом с очень незначительными изменениями.

Stephen Quan 12.04.2023 10:08
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
112
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я перенес ваше приложение на PySide6. Похоже, он работает с минимальными изменениями кода.

Вот main.py со ссылками PySide2, измененными на PySide6:

import sys
from PySide6.QtCore import Qt, QStringListModel, QObject, Signal, Slot
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

class Main(QObject):

    clicked = Signal()

    def __init__(self, parent=None):
        super(Main, self).__init__(parent)

    @Slot()
    def justClicking(self):
        self.clicked.emit()

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    main = Main()
    engine.rootContext().setContextProperty("main", main)

    data = ["London", "New York", "Paris", "Tokyo", "Sydney", "Madrid", "Mumbai", "Istanbul", "Bangkok", "Moscow", "Toronto", "Rome", "Shanghai", "Rio de Janeiro", "Seoul", "Berlin", "Cape Town", "Amsterdam", "Los Angeles", "San Francisco", "Cairo", "Barcelona", "Buenos Aires", "Melbourne", "Athens", "Vancouver", "Dubai", "Prague", "Edinburgh", "Singapore"]

    model = QStringListModel()
    model.setStringList(data)

    context = engine.rootContext()
    context.setContextProperty("myModel", model)

    engine.load("main.qml")
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

Вот main.qml с импортом Qt5, замененным импортом Qt6:

Я переработал ваш main.qml, чтобы сохранить список «зеленых» предметов. Нам нужно сделать это, потому что вы не можете получить доступ к делегатам напрямую, так как Qt будет создавать и уничтожать делегаты по требованию. Нам нужно полагаться на видимых делегатов и на то, что они привязаны к чему-то, что мы можем показать.

import QtQuick
import QtQuick.Controls
import QtQuick.Window

Window {
    visible: true
    color: "#161515"
    width: 640
    height: 480
    title: qsTr("Listview Example")

    property list<string> green

    Rectangle {
        id: rectangle
        x: 51
        y: 63
        width: 539
        height: 143
        color: "#ffffff"

        ListView {
            id: listView
            anchors.fill: parent
            model: myModel
            currentIndex: 0
            delegate: Rectangle {
                id: dlg
                width: listView.width
                height: 25
                color: index === listView.currentIndex ? "lightgrey" : "#00000000"
                radius: 10
                property var modelText: model.display

                property alias colorOfText: titleText.color

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        listView.currentIndex = index
                    }
                }
                Text {
                    id: titleText
                    text: modelText
                    font.pixelSize: 18
                    color: green.indexOf(modelText) !== -1 ? "green" : "black"
                    padding: 5
                }
            }
        }
    }
    Button {
        id: printBtn
        x: 286
        y: 241
        width: 69
        height: 35
        text: qsTr("Print")
        onClicked: {
            main.justClicking()
        }
    }
    Connections {
        target: main
        function onClicked() {
            if (green.indexOf(listView.currentItem.modelText) === -1) {
                green.push(listView.currentItem.modelText);
            }
        }
    }
}

Чтобы лучше понять жизненный цикл ваших делегатов, рассмотрите возможность добавления следующего кода к вашему делегату:

                Component.onCompleted: console.info(`Construction for ${model.display} ${index}`);
                Component.onDestruction: console.info(`Destruction for ${model.display} ${index}`);

Затем, когда вы прокручиваете свой ListView вверх и вниз, вы увидите, что делегаты создаются и уничтожаются. По этой причине вы не можете программно получить доступ к своим делегатам, поскольку они могут существовать или не существовать.

Вот скриншот вашего приложения:

Привет! Хорошо, если вы выберете последний элемент «Сингапур» и нажмете кнопку «Печать», разве вы не получите сообщение об ошибке «TypeError: значение не определено и не может быть преобразовано в объект» в консоли и текст «Сингапур» не станет зеленым? @Стивен Куан

rolando_cser 12.04.2023 10:29

Теперь, когда я понял ваш main.qml, я переписал его часть. Смотрите обновленный main.qml и соответствующее объяснение.

Stephen Quan 12.04.2023 10:46

Другие вопросы по теме