Heesung Yang

[문제 해결] Qt Quick 2 / QML - TypeError: Cannot read property

현상

아래 코드 실행 후 동적으로 List Item을 추가한 후, 삭제 시 다음과 같은 에러 메시지가 출력됨

main.qml:45: TypeError: Cannot read property 'width' of null
  • main.py

    import os
    from pathlib import Path
    import sys
    
    from PySide2.QtGui import QGuiApplication
    from PySide2.QtQml import QQmlApplicationEngine
    
    
    if __name__ == "__main__":
        app = QGuiApplication(sys.argv)
        engine = QQmlApplicationEngine()
        engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
        if not engine.rootObjects():
            sys.exit(-1)
        sys.exit(app.exec_())
    
  • main.qml

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQuick.Controls 2.15
    
    Window {
        id: root
    
        width: 640
        height: 480
        title: "Hello, world"
        visible: true
    
        ListModel {
            id: myModel
            property int autoIndex: 1
        }
    
        Column {
            anchors.fill: parent
    
            spacing: 10
    
            Button {
                anchors.right: parent.right
                text: "Add Item"
                onClicked: {
                    myModel.append({'number': myModel.autoIndex})
                    myModel.autoIndex += 1
                }
            }
    
            ListView {
                id: listView
    
                width: 350
                height: 400
                anchors.horizontalCenter: parent.horizontalCenter
                spacing: 10
                clip: true
    
                model: myModel
    
                delegate: Rectangle {
                    width: parent.width
                    height: 50
                    color: "white"
    
                    Button {
                        anchors.right: parent.right
                        anchors.rightMargin: 15
                        anchors.horizontalCenter: parent.horizontalCenter
    
                        text: qsTr("remove: ") + number
                        onClicked: {
                            myModel.remove(index)
                        }
                    }
                }
            }
        }
    }
    

원인

https://doc.qt.io/qt-5/qml-qtquick-listview.html#example-usage

Delegates are instantiated as needed and may be destroyed at any time. As such, state should never be stored in a delegate. Delegates are usually parented to ListView’s contentItem, but typically depending on whether it’s visible in the view or not, the parent can change, and sometimes be null. Because of that, binding to the parent’s properties from within the delegate is not recommended. If you want the delegate to fill out the width of the ListView, consider using one of the following approaches instead:

ListView {
   id: listView
    // ...

    delegate: Item {
        // Incorrect.
        width: parent.width

        // Correct.
        width: listView.width
        width: ListView.view.width
        // ...
    }
}

해결 방안

delegate item(Rectangle)의 width 속성을 상위 컴포넌트와 동일하게 설정하고 싶은 경우, parent 키워드 대신 상위 컴포넌트의 id를 사용한다.

  • main.qml

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQuick.Controls 2.15
    
    Window {
        // ...생략
        Column {
            // ...생략
            Button {
                // ...생략
            }
            ListView {
                id: listView
                // ...생략
                model: myModel
    
                delegate: Rectangle {
                    width: listView.width       // parent.width -> listView.width 변경
                    // ...생략
    
                    Button {
                        // ...생략
                    }
                }
            }
        }
    }
    

참고