英文:
Returning a custom Python model from a slot?
问题
我正在尝试为一个 List
设置详情窗口。
我已经设置了一个点击项目会创建一个新 Window
并将点击项目的信息传递到这个新 Window
的功能,我然后尝试将嵌套的 List
的模型分配给一个 Python 函数的返回值(使用 @Slot
装饰器)。这个函数基于传入的信息实例化一个新的自定义模型(从 QAbstractListModel
继承),然后返回它。
我的问题是,当我点击一个项目以打开窗口时,会抛出以下错误:
file:///home/cassidy/Projects/portmod/portmod/_gui/Manage/PackageDetailsWindow.qml:50: TypeError: Property 'get_local_flags_model' of object Config(0x55907ace9e70) is not a function
这是我的Python函数,它存储在传递给 QML 上下文作为 config
的 Config
类中:
@QtCore.Slot(str, result=QtCore.QObject) # type: ignore
def get_local_flags_model(self, atom: str) -> FlagListModel:
return FlagListModel(get_local_flags(Atom(atom)))
这是我的自定义模型:
from typing import Any, Dict, Tuple, Union
from PySide6.QtCore import QAbstractListModel, QModelIndex, QPersistentModelIndex, Qt
class FlagListModel(QAbstractListModel):
def __init__(self, use_flags: Dict[str, Tuple[bool, str]]):
super().__init__()
self._data = use_flags
def rowCount(self) -> int:
return len(self._data)
def data(
self,
index: Union[QModelIndex, QPersistentModelIndex],
role: int,
) -> Any:
if role == Qt.ItemDataRole.DisplayRole:
return list(self._data.keys())[index.row()]
elif role == Qt.ItemDataRole.CheckStateRole:
state = list(self._data.values())[index.row()][0]
if state:
return Qt.CheckState.Checked
else:
return Qt.CheckState.Unchecked
我的详情窗口:
import QtQuick 6.0
import QtQuick.Controls.Basic 6.0
import QtQuick.Layouts 6.0
Window {
id: detailsWindow
property string atom
title: detailsWindow.atom
height: root.height / 1.5
width: root.width / 1.5
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
// TODO: 如果没有USE flags,则不显示
GroupBox {
title: "USE Flags"
Layout.fillWidth: true
Layout.fillHeight: true
ListView {
id: useListView
clip: true
model: config.get_local_flags_model(detailsWindow.atom);
width: parent.width
height: parent.height
delegate: CheckDelegate {
text: modelData
checkState: model.check
width: ListView.view.width - 20
}
ScrollBar.vertical: ScrollBar {}
}
}
}
}
英文:
I'm trying to setup up a details window for a List
.
I've already set something up where clicking on an item creates a new Window
with Qt.createComponent
, and information from the clicked item is passed to this new Window
. I then try to assign the model of a nested List
to the return value of a Python function (decorated with @Slot
). This function instantiates a new custom model (subclassed from QAbstractListModel
) based on the information passed in, and returns it.
My problem is, when I click on an item to launch the window, it throws this error:
file:///home/cassidy/Projects/portmod/portmod/_gui/Manage/PackageDetailsWindow.qml:50: TypeError: Property 'get_local_flags_model' of object Config(0x55907ace9e70) is not a function
Here's my Python function. This is stored in a Config
class that's passed to the QML context as config
:
@QtCore.Slot(str, result=QtCore.QObject) # type: ignore
def get_local_flags_model(self, atom: str) -> FlagListModel:
return FlagListModel(get_local_flags(Atom(atom)))
Here's my custom model:
from typing import Any, Dict, Tuple, Union
from PySide6.QtCore import QAbstractListModel, QModelIndex, QPersistentModelIndex, Qt
class FlagListModel(QAbstractListModel):
def __init__(self, use_flags: Dict[str, Tuple[bool, str]]):
super().__init__()
self._data = use_flags
def rowCount(self) -> int:
return len(self._data)
def data(
self,
index: Union[QModelIndex, QPersistentModelIndex],
role: int,
) -> Any:
if role == Qt.ItemDataRole.DisplayRole:
return list(self._data.keys())[index.row()]
elif role == Qt.ItemDataRole.CheckStateRole:
state = list(self._data.values())[index.row()][0]
if state:
return Qt.CheckState.Checked
else:
return Qt.CheckState.Unchecked
My details window:
import QtQuick 6.0
import QtQuick.Controls.Basic 6.0
import QtQuick.Layouts 6.0
Window {
id: detailsWindow
property string atom
title: detailsWindow.atom
height: root.height / 1.5
width: root.width / 1.5
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
// TODO: Don't show if there are no USE flags
GroupBox {
title: "USE Flags"
Layout.fillWidth: true
Layout.fillHeight: true
ListView {
id: useListView
clip: true
model: config.get_local_flags_model(detailsWindow.atom);
width: parent.width
height: parent.height
delegate: CheckDelegate {
text: modelData
checkState: model.check
width: ListView.view.width - 20
}
ScrollBar.vertical: ScrollBar {}
}
}
}
}
答案1
得分: 1
-
似乎你将
config
实例注册为上下文属性。这不是最佳实践,而是使用qmlRegisterSingletonInstance
。 -
你尝试“property bind”一个
Slot
,据我所知,这是不可行的,你可以在组件完成时设置模型属性,即
ListView {
id: useListView
Component.onCompleted: {useListView.model = config.get_local_flags_model(detailsWindow.atom);}
}
请注意,通常情况下,你会在模型中进行预初始化,但我不知道你的用例是什么,所以很难说。
附注:我看到你使用了注解,如果你感兴趣,我有一个库,基于注解创建插槽(以及许多其他技巧),兼容mypy。
英文:
-
Seems like you registered
config
instance as a context property.
This is not best-practice instead useqmlRegisterSingletonInstance
-
You tried to "property bind" a
Slot
, AFAIK this is not feasible,you can set the model property when to component is completed. i.e
ListView{
id: useListView
Component.onCompleted: {useListView.model = config.get_local_flags_model(detailsWindow.atom);}
}
Note that generaly you would have pre-initialized it in the model, but I don't know what is your use-case so it is hard to tell.
sidenote: I see that you use annotations, if you are interested I have a library that creates slots based on annotations (and many other hacks), that is mypy compatible.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论