如何将 属性 从 python 公开到 QMLin 树视图
how to expose the property from python to QMLin tree view
我有一个要求,我必须将在 python 中编写的某些属性公开到树视图的 QML 端我无法这样做,因为我不太确定如何去做。
这是我为我的 treeview 后端编写的代码,以便我解析我的 treeviewModel class
中的 JSON 模型
tree_Model.py
import PySide2
from PySide2 import QtCore
from PySide2.QtCore import QAbstractItemModel, QByteArray
from PySide2.QtGui import Qt
from TreeItem import TreeItem
class tree_Model(QAbstractItemModel):
SchoolNameRole = Qt.UserRole + 1
FirstNameRole = Qt.UserRole + 2
LastNameRole = Qt.UserRole + 3
GenderRole = Qt.UserRole + 4
AgeRole = Qt.UserRole + 5
PhoneNumberRole = Qt.UserRole + 6
def __init__(self , data , parent = None):
super(tree_Model, self).__init__(parent)
print("wohoo came off pa")
self.rootItem = TreeItem({"SchoolName","FirstName","LastName","Gender","Age","PhoneNumber"})
self.setupModelData(data,self.rootItem)
def roleNames(self):
roles = {
tree_Model.SchoolNameRole: QByteArray(b'SchoolName'),
tree_Model.FirstNameRole: QByteArray(b'FirstName'),
tree_Model.LastNameRole: QByteArray(b'LastName'),
tree_Model.GenderRole: QByteArray(b'Gender'),
tree_Model.AgeRole: QByteArray(b'Age'),
tree_Model.PhoneNumberRole: QByteArray(b'PhoneNumber')
}
return roles
def columnCount(self, parent: PySide2.QtCore.QModelIndex) -> int:
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index: PySide2.QtCore.QModelIndex, role: int):
if not index.isValid():
return None
item: TreeItem = index.internalPointer()
if role == tree_Model.SchoolNameRole:
if item.isSchool_QProperty:
return item
if role == tree_Model.FirstNameRole:
if not item.isSchool_QProperty:
return item.StudentData['Firstname']
if role == tree_Model.LastNameRole:
if not item.isSchool_QProperty:
return item.StudentData['LastName']
if role == tree_Model.GenderRole:
if not item.isSchool_QProperty:
return item.StudentData['Gender']
if role == tree_Model.AgeRole:
if not item.isSchool_QProperty:
return item.StudentData['Age']
if role == tree_Model.PhoneNumberRole:
if not item.isSchool_QProperty:
return item.StudentData['PhoneNumber']
if role == QtCore.Qt.EditRole:
return item.value
if role != QtCore.Qt.DisplayRole:
return None
return item.StudentData
def flags(self, index: PySide2.QtCore.QModelIndex) -> PySide2.QtCore.Qt.ItemFlags:
if not index.isValid():
return QtCore.Qt.NoItemFlags
if index.internalPointer().isStudent:
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable
else:
return QtCore.Qt.NoItemFlags
def headerData(self, section: int, orientation: PySide2.QtCore.Qt.Orientation, role: int):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.rootItem.data(section)
return None
def index(self, row: int, column: int,
parent: PySide2.QtCore.QModelIndex) -> PySide2.QtCore.QModelIndex:
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer() # type: ignore[assignment] #[BLRFPGT-869]
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index: PySide2.QtCore.QModelIndex) -> PySide2.QtCore.QModelIndex:
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
parentItem: TreeItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent: PySide2.QtCore.QModelIndex) -> int:
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
def setupModelData(self,jsondata, parent: TreeItem) -> None:
parents = [parent]
number = 0
while number < len(jsondata):
position = 0
while position < len(jsondata[number]):
if jsondata[number][position] != ' ':
break
position += 1
json_value = jsondata[number][position:]
if json_value:
schoolItem = TreeItem(json_value[0], parents[-1])
schoolItem.isSchool_QProperty = True
schoolItem.isStudent_QProperty = False
schoolItem.schoolName_QProperty = json_value[0]
for students in json_value[1]:
studentItem = TreeItem(students, schoolItem)
schoolItem.appendChild(studentItem)
studentItem.isStudent_QProperty = True
studentItem.isSchool_QProperty = False
parents[-1].appendChild(schoolItem)
number += 1
TreeItem.py
class TreeItem(QObject):
def __init__(self, data, parent = None):
super().__init__(parent)
self.parentItem = parent
self.StudentData = data
self.childItems = []
self._schoolName = None
self._isSchool = False
self._isStudent = False
def appendChild(self, item) ->None:
self.childItems.append(item)
def child(self, row:int):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return len(self.StudentData)
def data(self, column: int):
try:
data = self.StudentData[column]
return data
except IndexError:
return None
def parent(self):
return self.parentItem
def row(self) -> int:
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
def schoolName(self) -> str:
return self._schoolName
def schoolName_set(self, schoolName: str):
self._schoolName = schoolName
schoolName_QProperty = Property(str, schoolName, schoolName_set)
def isSchool(self) -> bool:
return self._isSchool
def isSchool_set(self, isSchool: bool) -> None:
self._isSchool = isSchool
isSchool_QProperty = Property(bool, isSchool, isSchool_set)
def isStudent(self) -> bool:
return self._isStudent
def isStudent_set(self, isStudent: bool) -> None:
self._isStudent = isStudent
isStudent_QProperty = Property(bool, isStudent, isStudent_set)
我已经通过将它们设为 QProperty 将每个项目暴露给 qml。
main.py
import json
import os
from pathlib import Path
import sys
from PySide2.QtGui import QGuiApplication,Qt
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtQuick import QQuickView
from tree_Model import tree_Model
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
school_Student_Model = []
with open("sample4.json") as f:
data = json.load(f)
for i in data['SchoolList']:
print("the school names are", i['SchoolName'])
school_name = i['SchoolName']
students_info = i["Students"]
school_Student_Model.append((school_name, students_info))
model = tree_Model(school_Student_Model)
model.setHeaderData(0, Qt.Horizontal, "ID")
engine.rootContext().setContextProperty("schoolModel", model)
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
在 QML 中我如何访问这些 QProperty 这是我尝试过的但它似乎不起作用
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 1.4
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TreeView{
id: schoolTreeView
model: schoolModel
height: parent.height
width: parent.width
alternatingRowColors: true
TableViewColumn{
id:schoolName_id
title: "SchoolName"
role: "SchoolName"
width: 100
delegate: someDelegate
}
Component{
id:someDelegate
Item {
id: schoolname_id
Label{
text: styleData.value.schoolName_QProperty
}
}
}
TableViewColumn{
id:student_first_Name_id
title: "FirstName"
role: "FirstName"
width: 100
}
TableViewColumn{
id:student_last_Name_id
title: "LastName"
role: "LastName"
width: 100
}
TableViewColumn{
id:student_gender_id
title: "Gender"
role: "Gender"
width: 300
delegate: gender_delegate
}
Component{
id:gender_delegate
Item {
visible: model.Gender.isStudent_QProperty? false : true
// visible: styleData.value.isStudent_QProperty? true : false
Row{
spacing: 10
RadioButton{
id:male_button
text: "Male"
checked: styleData.value === "male"? true : false
}
RadioButton{
text: "Female"
checked: male_button.checked? false : true
}
}
}
}
TableViewColumn{
id:student_age_id
title: "Age"
role: "Age"
width: 100
}
TableViewColumn{
id:student_ph_num_id
title: "PhoneNumber"
role: "PhoneNumber"
width: 100
}
}
}
JSON文件是,放在项目的工作目录下
sample4.json
{
"SchoolList": [
{
"SchoolName": "Faps",
"Students": [
{
"Firstname": "abc",
"LastName": "ask",
"Age": 25,
"Gender": "male",
"PhoneNumber": "32423423"
},
{
"Firstname": "def",
"LastName": "ask",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1278371267"
},
{
"Firstname": "ijk",
"LastName": "lmn",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "8372873187"
},
{
"Firstname": "opq",
"LastName": "rst",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "2434234234"
},
{
"Firstname": "uvw",
"LastName": "xyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"Firstname": "abcd",
"LastName": "efgh",
"Age": 12,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
},
{
"SchoolName": "Germains",
"Students": [
{
"Firstname": "lmno",
"LastName": "pqr",
"Age": 26,
"Gender": "male",
"PhoneNumber": "234234"
},
{
"Firstname": "stuv",
"LastName": "wxyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "123213"
},
{
"Firstname": "sfadf",
"LastName": "dfsdf",
"Age": 26,
"Gender": "Female",
"PhoneNumber": "12323423"
},
{
"Firstname": "dsfsd",
"LastName": "sdfsd",
"Age": 30,
"Gender": "Female",
"PhoneNumber": "1231323"
},
{
"Firstname": "sdfsd",
"LastName": "sdfsd",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"Firstname": "awdas",
"LastName": "cvfs",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
}
]
}
当代码为 运行 时 pycharm 我们将看到这样的输出
有应用于父元素的男性女性单选按钮,我怎样才能确保删除父元素的那些单选按钮并仅将它们应用于子元素。
该结构令人困惑,因此我将从头开始实施该模型以使其可读。另一方面,与其改变项目的可见性,不如使用加载器,这样我们只在需要时构建项目。
注意:我稍微更改了 json 以便键与角色匹配:
from dataclasses import dataclass, field
from enum import IntEnum, auto
import json
import os
from pathlib import Path
from dataclasses_json import dataclass_json
from PySide2.QtCore import (
QAbstractItemModel,
QByteArray,
QCoreApplication,
QModelIndex,
Qt,
QUrl,
)
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
CURRENT_DIRECTORY = Path(__file__).resolve().parent
@dataclass_json
@dataclass
class Student:
FirstName: str
LastName: str
Age: int
Gender: str
PhoneNumber: str
@dataclass_json
@dataclass
class School:
SchoolName: str
Students: list[Student] = field(default_factory=list)
class SchoolRoles(IntEnum):
SchoolNameRole = Qt.UserRole
class StudentRoles(IntEnum):
FirstnameRole = SchoolRoles.SchoolNameRole + 1
LastNameRole = auto()
AgeRole = auto()
GenderRole = auto()
PhoneNumberRole = auto()
ROLES_MAPPPING = {
SchoolRoles.SchoolNameRole: "SchoolName",
StudentRoles.FirstnameRole: "FirstName",
StudentRoles.LastNameRole: "LastName",
StudentRoles.AgeRole: "Age",
StudentRoles.AgeRole: "Age",
StudentRoles.GenderRole: "Gender",
StudentRoles.PhoneNumberRole: "PhoneNumber",
}
@dataclass
class Model(QAbstractItemModel):
schools: list[School] = field(default_factory=list)
def __post_init__(self):
super().__init__()
self._rolenames = dict()
self._rolenames[Qt.DisplayRole] = QByteArray(b"display")
for role, name in ROLES_MAPPPING.items():
self._rolenames[role] = QByteArray(name.encode())
def add_school(self, school):
self.schools.append(school)
def columnCount(self, index=QModelIndex()):
return 1
def roleNames(self):
return self._rolenames
def rowCount(self, parent):
if parent.isValid():
item = parent.internalPointer()
if isinstance(item, School):
return len(item.Students)
return 0
else:
return len(self.schools)
def index(self, row, column, parent=QModelIndex()):
if column != 0:
return QModelIndex()
if not self.hasIndex(row, column, parent):
return QModelIndex()
if parent.isValid():
item = parent.internalPointer()
if isinstance(item, School):
return self.createIndex(row, column, item.Students[row])
return QModelIndex()
else:
return self.createIndex(row, column, self.schools[row])
def parent(self, index):
if not index.isValid():
return QModelIndex()
item = index.internalPointer()
if isinstance(item, School):
return QModelIndex()
elif isinstance(item, Student):
row = -1
parent_item = None
for i, school in enumerate(self.schools):
if item in school.Students:
row = i
parent_item = school
break
if row >= 0:
return self.createIndex(row, 0, parent_item)
return QModelIndex()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return
item = index.internalPointer()
if role == Qt.DisplayRole:
return str(index.internalPointer())
elif role == SchoolRoles.SchoolNameRole:
if isinstance(item, School):
return item.SchoolName
elif isinstance(item, Student):
return index.parent().internalPointer().SchoolName
return
elif isinstance(item, Student):
prop = ROLES_MAPPPING.get(role)
if prop is not None:
return getattr(item, prop)
def main():
app = QGuiApplication()
model = Model()
with open(CURRENT_DIRECTORY / "sample.json") as f:
data = json.load(f)
for dict_school in data.get("SchoolList", []):
school = School.from_dict(dict_school)
model.add_school(school)
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("schoolModel", model)
filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
url = QUrl.fromLocalFile(filename)
def handle_object_created(obj, obj_url):
if obj is None and url == obj_url:
QCoreApplication.exit(-1)
engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
engine.load(url)
app.exec_()
if __name__ == "__main__":
main()
import QtQuick 2.14
import QtQuick.Controls 1.4
import QtQuick.Window 2.14
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TreeView {
id: schoolTreeView
model: schoolModel
anchors.fill: parent
alternatingRowColors: true
TableViewColumn {
id: schoolName_id
title: "SchoolName"
role: "SchoolName"
width: 100
}
TableViewColumn {
id: student_first_Name_id
title: "FirstName"
role: "FirstName"
width: 100
}
TableViewColumn {
id: student_last_Name_id
title: "LastName"
role: "LastName"
width: 100
}
TableViewColumn {
title: "Gender"
role: "Gender"
width: 150
delegate: Loader {
id: loader
property string gender: styleData.value ? styleData.value : ""
sourceComponent: styleData.value ? customDelegate : null
}
}
TableViewColumn {
id: student_age_id
title: "Age"
role: "Age"
width: 100
}
TableViewColumn {
id: student_ph_num_id
title: "PhoneNumber"
role: "PhoneNumber"
width: 100
}
}
property Component customDelegate: Row {
spacing: 10
RadioButton {
id: male_button
text: "Male"
checked: gender === "male"
}
RadioButton {
text: "Female"
checked: gender === "Female"
}
}
}
{
"SchoolList": [
{
"SchoolName": "Faps",
"Students": [
{
"FirstName": "abc",
"LastName": "ask",
"Age": 25,
"Gender": "male",
"PhoneNumber": "32423423"
},
{
"FirstName": "def",
"LastName": "ask",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1278371267"
},
{
"FirstName": "ijk",
"LastName": "lmn",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "8372873187"
},
{
"FirstName": "opq",
"LastName": "rst",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "2434234234"
},
{
"FirstName": "uvw",
"LastName": "xyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"FirstName": "abcd",
"LastName": "efgh",
"Age": 12,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
},
{
"SchoolName": "Germains",
"Students": [
{
"FirstName": "lmno",
"LastName": "pqr",
"Age": 26,
"Gender": "male",
"PhoneNumber": "234234"
},
{
"FirstName": "stuv",
"LastName": "wxyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "123213"
},
{
"FirstName": "sfadf",
"LastName": "dfsdf",
"Age": 26,
"Gender": "Female",
"PhoneNumber": "12323423"
},
{
"FirstName": "dsfsd",
"LastName": "sdfsd",
"Age": 30,
"Gender": "Female",
"PhoneNumber": "1231323"
},
{
"FirstName": "sdfsd",
"LastName": "sdfsd",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"FirstName": "awdas",
"LastName": "cvfs",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
}
]
}
我有一个要求,我必须将在 python 中编写的某些属性公开到树视图的 QML 端我无法这样做,因为我不太确定如何去做。
这是我为我的 treeview 后端编写的代码,以便我解析我的 treeviewModel class
中的 JSON 模型tree_Model.py
import PySide2
from PySide2 import QtCore
from PySide2.QtCore import QAbstractItemModel, QByteArray
from PySide2.QtGui import Qt
from TreeItem import TreeItem
class tree_Model(QAbstractItemModel):
SchoolNameRole = Qt.UserRole + 1
FirstNameRole = Qt.UserRole + 2
LastNameRole = Qt.UserRole + 3
GenderRole = Qt.UserRole + 4
AgeRole = Qt.UserRole + 5
PhoneNumberRole = Qt.UserRole + 6
def __init__(self , data , parent = None):
super(tree_Model, self).__init__(parent)
print("wohoo came off pa")
self.rootItem = TreeItem({"SchoolName","FirstName","LastName","Gender","Age","PhoneNumber"})
self.setupModelData(data,self.rootItem)
def roleNames(self):
roles = {
tree_Model.SchoolNameRole: QByteArray(b'SchoolName'),
tree_Model.FirstNameRole: QByteArray(b'FirstName'),
tree_Model.LastNameRole: QByteArray(b'LastName'),
tree_Model.GenderRole: QByteArray(b'Gender'),
tree_Model.AgeRole: QByteArray(b'Age'),
tree_Model.PhoneNumberRole: QByteArray(b'PhoneNumber')
}
return roles
def columnCount(self, parent: PySide2.QtCore.QModelIndex) -> int:
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index: PySide2.QtCore.QModelIndex, role: int):
if not index.isValid():
return None
item: TreeItem = index.internalPointer()
if role == tree_Model.SchoolNameRole:
if item.isSchool_QProperty:
return item
if role == tree_Model.FirstNameRole:
if not item.isSchool_QProperty:
return item.StudentData['Firstname']
if role == tree_Model.LastNameRole:
if not item.isSchool_QProperty:
return item.StudentData['LastName']
if role == tree_Model.GenderRole:
if not item.isSchool_QProperty:
return item.StudentData['Gender']
if role == tree_Model.AgeRole:
if not item.isSchool_QProperty:
return item.StudentData['Age']
if role == tree_Model.PhoneNumberRole:
if not item.isSchool_QProperty:
return item.StudentData['PhoneNumber']
if role == QtCore.Qt.EditRole:
return item.value
if role != QtCore.Qt.DisplayRole:
return None
return item.StudentData
def flags(self, index: PySide2.QtCore.QModelIndex) -> PySide2.QtCore.Qt.ItemFlags:
if not index.isValid():
return QtCore.Qt.NoItemFlags
if index.internalPointer().isStudent:
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable
else:
return QtCore.Qt.NoItemFlags
def headerData(self, section: int, orientation: PySide2.QtCore.Qt.Orientation, role: int):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.rootItem.data(section)
return None
def index(self, row: int, column: int,
parent: PySide2.QtCore.QModelIndex) -> PySide2.QtCore.QModelIndex:
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer() # type: ignore[assignment] #[BLRFPGT-869]
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index: PySide2.QtCore.QModelIndex) -> PySide2.QtCore.QModelIndex:
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
parentItem: TreeItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent: PySide2.QtCore.QModelIndex) -> int:
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
def setupModelData(self,jsondata, parent: TreeItem) -> None:
parents = [parent]
number = 0
while number < len(jsondata):
position = 0
while position < len(jsondata[number]):
if jsondata[number][position] != ' ':
break
position += 1
json_value = jsondata[number][position:]
if json_value:
schoolItem = TreeItem(json_value[0], parents[-1])
schoolItem.isSchool_QProperty = True
schoolItem.isStudent_QProperty = False
schoolItem.schoolName_QProperty = json_value[0]
for students in json_value[1]:
studentItem = TreeItem(students, schoolItem)
schoolItem.appendChild(studentItem)
studentItem.isStudent_QProperty = True
studentItem.isSchool_QProperty = False
parents[-1].appendChild(schoolItem)
number += 1
TreeItem.py
class TreeItem(QObject):
def __init__(self, data, parent = None):
super().__init__(parent)
self.parentItem = parent
self.StudentData = data
self.childItems = []
self._schoolName = None
self._isSchool = False
self._isStudent = False
def appendChild(self, item) ->None:
self.childItems.append(item)
def child(self, row:int):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return len(self.StudentData)
def data(self, column: int):
try:
data = self.StudentData[column]
return data
except IndexError:
return None
def parent(self):
return self.parentItem
def row(self) -> int:
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
def schoolName(self) -> str:
return self._schoolName
def schoolName_set(self, schoolName: str):
self._schoolName = schoolName
schoolName_QProperty = Property(str, schoolName, schoolName_set)
def isSchool(self) -> bool:
return self._isSchool
def isSchool_set(self, isSchool: bool) -> None:
self._isSchool = isSchool
isSchool_QProperty = Property(bool, isSchool, isSchool_set)
def isStudent(self) -> bool:
return self._isStudent
def isStudent_set(self, isStudent: bool) -> None:
self._isStudent = isStudent
isStudent_QProperty = Property(bool, isStudent, isStudent_set)
我已经通过将它们设为 QProperty 将每个项目暴露给 qml。
main.py
import json
import os
from pathlib import Path
import sys
from PySide2.QtGui import QGuiApplication,Qt
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtQuick import QQuickView
from tree_Model import tree_Model
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
school_Student_Model = []
with open("sample4.json") as f:
data = json.load(f)
for i in data['SchoolList']:
print("the school names are", i['SchoolName'])
school_name = i['SchoolName']
students_info = i["Students"]
school_Student_Model.append((school_name, students_info))
model = tree_Model(school_Student_Model)
model.setHeaderData(0, Qt.Horizontal, "ID")
engine.rootContext().setContextProperty("schoolModel", model)
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
在 QML 中我如何访问这些 QProperty 这是我尝试过的但它似乎不起作用
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 1.4
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TreeView{
id: schoolTreeView
model: schoolModel
height: parent.height
width: parent.width
alternatingRowColors: true
TableViewColumn{
id:schoolName_id
title: "SchoolName"
role: "SchoolName"
width: 100
delegate: someDelegate
}
Component{
id:someDelegate
Item {
id: schoolname_id
Label{
text: styleData.value.schoolName_QProperty
}
}
}
TableViewColumn{
id:student_first_Name_id
title: "FirstName"
role: "FirstName"
width: 100
}
TableViewColumn{
id:student_last_Name_id
title: "LastName"
role: "LastName"
width: 100
}
TableViewColumn{
id:student_gender_id
title: "Gender"
role: "Gender"
width: 300
delegate: gender_delegate
}
Component{
id:gender_delegate
Item {
visible: model.Gender.isStudent_QProperty? false : true
// visible: styleData.value.isStudent_QProperty? true : false
Row{
spacing: 10
RadioButton{
id:male_button
text: "Male"
checked: styleData.value === "male"? true : false
}
RadioButton{
text: "Female"
checked: male_button.checked? false : true
}
}
}
}
TableViewColumn{
id:student_age_id
title: "Age"
role: "Age"
width: 100
}
TableViewColumn{
id:student_ph_num_id
title: "PhoneNumber"
role: "PhoneNumber"
width: 100
}
}
}
JSON文件是,放在项目的工作目录下 sample4.json
{
"SchoolList": [
{
"SchoolName": "Faps",
"Students": [
{
"Firstname": "abc",
"LastName": "ask",
"Age": 25,
"Gender": "male",
"PhoneNumber": "32423423"
},
{
"Firstname": "def",
"LastName": "ask",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1278371267"
},
{
"Firstname": "ijk",
"LastName": "lmn",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "8372873187"
},
{
"Firstname": "opq",
"LastName": "rst",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "2434234234"
},
{
"Firstname": "uvw",
"LastName": "xyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"Firstname": "abcd",
"LastName": "efgh",
"Age": 12,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
},
{
"SchoolName": "Germains",
"Students": [
{
"Firstname": "lmno",
"LastName": "pqr",
"Age": 26,
"Gender": "male",
"PhoneNumber": "234234"
},
{
"Firstname": "stuv",
"LastName": "wxyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "123213"
},
{
"Firstname": "sfadf",
"LastName": "dfsdf",
"Age": 26,
"Gender": "Female",
"PhoneNumber": "12323423"
},
{
"Firstname": "dsfsd",
"LastName": "sdfsd",
"Age": 30,
"Gender": "Female",
"PhoneNumber": "1231323"
},
{
"Firstname": "sdfsd",
"LastName": "sdfsd",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"Firstname": "awdas",
"LastName": "cvfs",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
}
]
}
当代码为 运行 时 pycharm 我们将看到这样的输出
有应用于父元素的男性女性单选按钮,我怎样才能确保删除父元素的那些单选按钮并仅将它们应用于子元素。
该结构令人困惑,因此我将从头开始实施该模型以使其可读。另一方面,与其改变项目的可见性,不如使用加载器,这样我们只在需要时构建项目。
注意:我稍微更改了 json 以便键与角色匹配:
from dataclasses import dataclass, field
from enum import IntEnum, auto
import json
import os
from pathlib import Path
from dataclasses_json import dataclass_json
from PySide2.QtCore import (
QAbstractItemModel,
QByteArray,
QCoreApplication,
QModelIndex,
Qt,
QUrl,
)
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
CURRENT_DIRECTORY = Path(__file__).resolve().parent
@dataclass_json
@dataclass
class Student:
FirstName: str
LastName: str
Age: int
Gender: str
PhoneNumber: str
@dataclass_json
@dataclass
class School:
SchoolName: str
Students: list[Student] = field(default_factory=list)
class SchoolRoles(IntEnum):
SchoolNameRole = Qt.UserRole
class StudentRoles(IntEnum):
FirstnameRole = SchoolRoles.SchoolNameRole + 1
LastNameRole = auto()
AgeRole = auto()
GenderRole = auto()
PhoneNumberRole = auto()
ROLES_MAPPPING = {
SchoolRoles.SchoolNameRole: "SchoolName",
StudentRoles.FirstnameRole: "FirstName",
StudentRoles.LastNameRole: "LastName",
StudentRoles.AgeRole: "Age",
StudentRoles.AgeRole: "Age",
StudentRoles.GenderRole: "Gender",
StudentRoles.PhoneNumberRole: "PhoneNumber",
}
@dataclass
class Model(QAbstractItemModel):
schools: list[School] = field(default_factory=list)
def __post_init__(self):
super().__init__()
self._rolenames = dict()
self._rolenames[Qt.DisplayRole] = QByteArray(b"display")
for role, name in ROLES_MAPPPING.items():
self._rolenames[role] = QByteArray(name.encode())
def add_school(self, school):
self.schools.append(school)
def columnCount(self, index=QModelIndex()):
return 1
def roleNames(self):
return self._rolenames
def rowCount(self, parent):
if parent.isValid():
item = parent.internalPointer()
if isinstance(item, School):
return len(item.Students)
return 0
else:
return len(self.schools)
def index(self, row, column, parent=QModelIndex()):
if column != 0:
return QModelIndex()
if not self.hasIndex(row, column, parent):
return QModelIndex()
if parent.isValid():
item = parent.internalPointer()
if isinstance(item, School):
return self.createIndex(row, column, item.Students[row])
return QModelIndex()
else:
return self.createIndex(row, column, self.schools[row])
def parent(self, index):
if not index.isValid():
return QModelIndex()
item = index.internalPointer()
if isinstance(item, School):
return QModelIndex()
elif isinstance(item, Student):
row = -1
parent_item = None
for i, school in enumerate(self.schools):
if item in school.Students:
row = i
parent_item = school
break
if row >= 0:
return self.createIndex(row, 0, parent_item)
return QModelIndex()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return
item = index.internalPointer()
if role == Qt.DisplayRole:
return str(index.internalPointer())
elif role == SchoolRoles.SchoolNameRole:
if isinstance(item, School):
return item.SchoolName
elif isinstance(item, Student):
return index.parent().internalPointer().SchoolName
return
elif isinstance(item, Student):
prop = ROLES_MAPPPING.get(role)
if prop is not None:
return getattr(item, prop)
def main():
app = QGuiApplication()
model = Model()
with open(CURRENT_DIRECTORY / "sample.json") as f:
data = json.load(f)
for dict_school in data.get("SchoolList", []):
school = School.from_dict(dict_school)
model.add_school(school)
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("schoolModel", model)
filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
url = QUrl.fromLocalFile(filename)
def handle_object_created(obj, obj_url):
if obj is None and url == obj_url:
QCoreApplication.exit(-1)
engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
engine.load(url)
app.exec_()
if __name__ == "__main__":
main()
import QtQuick 2.14
import QtQuick.Controls 1.4
import QtQuick.Window 2.14
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TreeView {
id: schoolTreeView
model: schoolModel
anchors.fill: parent
alternatingRowColors: true
TableViewColumn {
id: schoolName_id
title: "SchoolName"
role: "SchoolName"
width: 100
}
TableViewColumn {
id: student_first_Name_id
title: "FirstName"
role: "FirstName"
width: 100
}
TableViewColumn {
id: student_last_Name_id
title: "LastName"
role: "LastName"
width: 100
}
TableViewColumn {
title: "Gender"
role: "Gender"
width: 150
delegate: Loader {
id: loader
property string gender: styleData.value ? styleData.value : ""
sourceComponent: styleData.value ? customDelegate : null
}
}
TableViewColumn {
id: student_age_id
title: "Age"
role: "Age"
width: 100
}
TableViewColumn {
id: student_ph_num_id
title: "PhoneNumber"
role: "PhoneNumber"
width: 100
}
}
property Component customDelegate: Row {
spacing: 10
RadioButton {
id: male_button
text: "Male"
checked: gender === "male"
}
RadioButton {
text: "Female"
checked: gender === "Female"
}
}
}
{
"SchoolList": [
{
"SchoolName": "Faps",
"Students": [
{
"FirstName": "abc",
"LastName": "ask",
"Age": 25,
"Gender": "male",
"PhoneNumber": "32423423"
},
{
"FirstName": "def",
"LastName": "ask",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1278371267"
},
{
"FirstName": "ijk",
"LastName": "lmn",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "8372873187"
},
{
"FirstName": "opq",
"LastName": "rst",
"Age": 25,
"Gender": "Female",
"PhoneNumber": "2434234234"
},
{
"FirstName": "uvw",
"LastName": "xyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"FirstName": "abcd",
"LastName": "efgh",
"Age": 12,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
},
{
"SchoolName": "Germains",
"Students": [
{
"FirstName": "lmno",
"LastName": "pqr",
"Age": 26,
"Gender": "male",
"PhoneNumber": "234234"
},
{
"FirstName": "stuv",
"LastName": "wxyz",
"Age": 26,
"Gender": "male",
"PhoneNumber": "123213"
},
{
"FirstName": "sfadf",
"LastName": "dfsdf",
"Age": 26,
"Gender": "Female",
"PhoneNumber": "12323423"
},
{
"FirstName": "dsfsd",
"LastName": "sdfsd",
"Age": 30,
"Gender": "Female",
"PhoneNumber": "1231323"
},
{
"FirstName": "sdfsd",
"LastName": "sdfsd",
"Age": 26,
"Gender": "male",
"PhoneNumber": "124331423"
},
{
"FirstName": "awdas",
"LastName": "cvfs",
"Age": 26,
"Gender": "male",
"PhoneNumber": "1231323424"
}
]
}
]
}