如何在 Python 和 QML 之间引用自定义角色索引
How to reference custom role indices between Python and QML
我正在 python 中的 class 中实现 QAbstractListModel 以用于 QML。我在模型中定义了两个自定义角色。在模型中,我还实现了一个插槽函数 'get' 到来自指定索引和角色的 return 数据。当我将角色传回 'get' 函数时,我收到一个与我在模型中为角色定义的整数不同的整数。
我尝试将角色和索引从 QML 传回我的模型中定义的 'get' 函数。索引按预期工作,但角色 return 值与我在模型中定义的值不同。
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 中不同?既然如此,我如何才能成功 return 那些角色供我的模型中的另一个函数使用?
我的最终目标是在 QML ListView 中创建类似于 'get' 和 'set' 的函数,但这样做是为了我在 Python.[=15 中构建的模型=]
您有一个 XY problem,您的真正目标是从 QML 编辑模型值。因此,解决方案是实施 setData()
方法并使用 selected = foo_value
编辑数据,在您的特定情况下:selected != selected
TL;DR;
你对“selected”的概念有误解,QML中的“selected”是与Selected角色关联的模型的值,就是这个意思:
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
}
}
}
我正在 python 中的 class 中实现 QAbstractListModel 以用于 QML。我在模型中定义了两个自定义角色。在模型中,我还实现了一个插槽函数 'get' 到来自指定索引和角色的 return 数据。当我将角色传回 'get' 函数时,我收到一个与我在模型中为角色定义的整数不同的整数。
我尝试将角色和索引从 QML 传回我的模型中定义的 'get' 函数。索引按预期工作,但角色 return 值与我在模型中定义的值不同。
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 中不同?既然如此,我如何才能成功 return 那些角色供我的模型中的另一个函数使用?
我的最终目标是在 QML ListView 中创建类似于 'get' 和 'set' 的函数,但这样做是为了我在 Python.[=15 中构建的模型=]
您有一个 XY problem,您的真正目标是从 QML 编辑模型值。因此,解决方案是实施 setData()
方法并使用 selected = foo_value
编辑数据,在您的特定情况下:selected != selected
TL;DR;
你对“selected”的概念有误解,QML中的“selected”是与Selected角色关联的模型的值,就是这个意思:
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
}
}
}