数据从 QML 到 Python Signal/Slots
Data from QML to Python Signal/Slots
我正在尝试弄清楚如何将数据从 TextField 输入传递到我的 Python class。我认为解决这个问题的方法是使用信号和槽;但是,我在传递数据的 widget.qml(主 qml class)中使用自定义 QML 组件。我需要这样做,以便我可以对通过 TextField 传入的数据执行更复杂的操作。
我对如何将数据从我的 BasicContainer.qml 传递到我的 python class 感到困惑,并且不确定我的 widget.qml [=44] 中的逻辑是否正确=] 甚至会支持它,因为它使用嵌套模型。我附上了一个例子,希望它能达到控制流的目的。我可以在 widget.qml 中使用我添加的基本信号,但是如果我在 BasicContainer.qml 中做同样的事情,我会得到一个 PySide6.QtQuick.QQuickItem object has no attribute 'displayValueChanged'
main.py
import os
import sys
from pathlib import Path
sys.path.append(os.path.join(os.path.dirname(sys.path[0]), ".."))
from PySide6.QtCore import Property, QUrl, QObject, Qt, QCoreApplication, Slot
from PySide6.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PySide6.QtQuick import QQuickView
CURRENT_DIRECTORY = Path(__file__).resolve().parent
# QstandardItem roles
SetText = Qt.UserRole + 1
class ControlledSignalWidget(QObject):
def __init__(self):
super().__init__()
self._model = QStandardItemModel()
# self._model.setItemRoleNames({Qt.DisplayRole: b"display"})
self._setpoints_models = []
@Property(QObject, constant=True)
def model(self):
return self._model
@Slot(int, result=QObject)
def setpoints(self, idx):
return self._setpoints_models[idx]
# create custom number of widgets
def create_widgets(self, widgets, allComponents):
counter = 1
# Iterates for the amount of widgets created
for general, widget in widgets.items():
# for key in widget:
print("Adding new widget")
item = QStandardItem(widget["Header"])
self._model.appendRow(item)
self.create_setpoints(allComponents["item" + str(counter) + "Components"])
print("Added widget")
counter += 1
def create_setpoints(self, component):
setpoints_model = QStandardItemModel()
setpoints_model.setItemRoleNames({SetText: b"textField"})
for subWidget in component:
print(subWidget)
item = QStandardItem()
item.setData(subWidget["title"], SetText)
setpoints_model.appendRow(item)
self._setpoints_models.append(setpoints_model)
def display(s):
print(s)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeViewToRootObject)
url = QUrl.fromLocalFile(os.fspath(CURRENT_DIRECTORY / "widget.qml"))
def handle_status_changed(status):
if status == QQuickView.Error:
QCoreApplication.exit(-1)
widgets = {
"widget1": {"Header": "Header Text"},
"widget2": {"Header": "Header 2 Text"},
}
item1Components = [{"title": "widget1random"}, {"title": "widget1random2"}]
item2Components = [{"title": "widget2random"}, {"title": "widget2random2"}]
allComponents = {
"item1Components": item1Components,
"item2Components": item2Components,
}
mainWidget = ControlledSignalWidget()
mainWidget.create_widgets(widgets, allComponents)
view.rootContext().setContextProperty("mainWidget", mainWidget)
view.statusChanged.connect(
handle_status_changed, Qt.ConnectionType.QueuedConnection
)
view.setSource(url)
# SIGNAL CONNECTION MADE
root = view.rootObject()
root.displayValueChanged.connect(display)
view.show()
sys.exit(app.exec())
widget.qml
import QtQuick 2.0
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.12
Item {
id: root
width: 1000
height: 800
signal displayValueChanged(string setpoint)
GridLayout{
columns: 3
Repeater{
id: repeater1
model: mainWidget.model
ColumnLayout{
property int outerIndex: index
Repeater{
id: repeater2
model: mainWidget.setpoints(outerIndex)
ColumnLayout{
BasicContainer{
Component.onCompleted: {
//Signal called
displayValueChanged(inputText)
}
}
}
}
}
}
}
}
BasicContainer.qml
import QtQuick 2.12
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.12
Item {
id: basicContainerItem
width: 300
height: 60
visible: true
signal valueChanged()
property alias inputText: containerInput.text
Rectangle {
id: rectangle
width: parent.width
ColumnLayout {
TextField {
id: containerInput
visible: true
placeholderText: qsTr("Text Field")
text: "Default value"
// Contended line
//textColor: "#FF3333"
onAccepted: {
console.log(text)
basicContainerItem.valueChanged()
}
}
}
}
}
重要编辑:
我修改了代码以使用信号将值从 BasicContainer class 一直传递到 Python,因此当您进行文本输入并按回车键时,您输入的新文本将被记录下来。这应该已经解决了一切;但是,当我尝试进行任何样式更改(例如 BasicContainer.qml 中的文本颜色更改行)时:textColor: "#FF3333"
将导致我的应用程序中断,从而导致此错误:
root.displayValueChanged.connect(display) AttributeError 'Nonetype' object has no attribute 'displayValueChanged'
没关系,看来正确的解决方案就是我上面的;期待我使用 textColor 的部分,因为显然那应该是 color
而不是 textColor
。但是,信号按预期工作,这是我对这个问题的目标。
我正在尝试弄清楚如何将数据从 TextField 输入传递到我的 Python class。我认为解决这个问题的方法是使用信号和槽;但是,我在传递数据的 widget.qml(主 qml class)中使用自定义 QML 组件。我需要这样做,以便我可以对通过 TextField 传入的数据执行更复杂的操作。
我对如何将数据从我的 BasicContainer.qml 传递到我的 python class 感到困惑,并且不确定我的 widget.qml [=44] 中的逻辑是否正确=] 甚至会支持它,因为它使用嵌套模型。我附上了一个例子,希望它能达到控制流的目的。我可以在 widget.qml 中使用我添加的基本信号,但是如果我在 BasicContainer.qml 中做同样的事情,我会得到一个 PySide6.QtQuick.QQuickItem object has no attribute 'displayValueChanged'
main.py
import os
import sys
from pathlib import Path
sys.path.append(os.path.join(os.path.dirname(sys.path[0]), ".."))
from PySide6.QtCore import Property, QUrl, QObject, Qt, QCoreApplication, Slot
from PySide6.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PySide6.QtQuick import QQuickView
CURRENT_DIRECTORY = Path(__file__).resolve().parent
# QstandardItem roles
SetText = Qt.UserRole + 1
class ControlledSignalWidget(QObject):
def __init__(self):
super().__init__()
self._model = QStandardItemModel()
# self._model.setItemRoleNames({Qt.DisplayRole: b"display"})
self._setpoints_models = []
@Property(QObject, constant=True)
def model(self):
return self._model
@Slot(int, result=QObject)
def setpoints(self, idx):
return self._setpoints_models[idx]
# create custom number of widgets
def create_widgets(self, widgets, allComponents):
counter = 1
# Iterates for the amount of widgets created
for general, widget in widgets.items():
# for key in widget:
print("Adding new widget")
item = QStandardItem(widget["Header"])
self._model.appendRow(item)
self.create_setpoints(allComponents["item" + str(counter) + "Components"])
print("Added widget")
counter += 1
def create_setpoints(self, component):
setpoints_model = QStandardItemModel()
setpoints_model.setItemRoleNames({SetText: b"textField"})
for subWidget in component:
print(subWidget)
item = QStandardItem()
item.setData(subWidget["title"], SetText)
setpoints_model.appendRow(item)
self._setpoints_models.append(setpoints_model)
def display(s):
print(s)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeViewToRootObject)
url = QUrl.fromLocalFile(os.fspath(CURRENT_DIRECTORY / "widget.qml"))
def handle_status_changed(status):
if status == QQuickView.Error:
QCoreApplication.exit(-1)
widgets = {
"widget1": {"Header": "Header Text"},
"widget2": {"Header": "Header 2 Text"},
}
item1Components = [{"title": "widget1random"}, {"title": "widget1random2"}]
item2Components = [{"title": "widget2random"}, {"title": "widget2random2"}]
allComponents = {
"item1Components": item1Components,
"item2Components": item2Components,
}
mainWidget = ControlledSignalWidget()
mainWidget.create_widgets(widgets, allComponents)
view.rootContext().setContextProperty("mainWidget", mainWidget)
view.statusChanged.connect(
handle_status_changed, Qt.ConnectionType.QueuedConnection
)
view.setSource(url)
# SIGNAL CONNECTION MADE
root = view.rootObject()
root.displayValueChanged.connect(display)
view.show()
sys.exit(app.exec())
widget.qml
import QtQuick 2.0
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.12
Item {
id: root
width: 1000
height: 800
signal displayValueChanged(string setpoint)
GridLayout{
columns: 3
Repeater{
id: repeater1
model: mainWidget.model
ColumnLayout{
property int outerIndex: index
Repeater{
id: repeater2
model: mainWidget.setpoints(outerIndex)
ColumnLayout{
BasicContainer{
Component.onCompleted: {
//Signal called
displayValueChanged(inputText)
}
}
}
}
}
}
}
}
BasicContainer.qml
import QtQuick 2.12
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.12
Item {
id: basicContainerItem
width: 300
height: 60
visible: true
signal valueChanged()
property alias inputText: containerInput.text
Rectangle {
id: rectangle
width: parent.width
ColumnLayout {
TextField {
id: containerInput
visible: true
placeholderText: qsTr("Text Field")
text: "Default value"
// Contended line
//textColor: "#FF3333"
onAccepted: {
console.log(text)
basicContainerItem.valueChanged()
}
}
}
}
}
重要编辑:
我修改了代码以使用信号将值从 BasicContainer class 一直传递到 Python,因此当您进行文本输入并按回车键时,您输入的新文本将被记录下来。这应该已经解决了一切;但是,当我尝试进行任何样式更改(例如 BasicContainer.qml 中的文本颜色更改行)时:textColor: "#FF3333"
将导致我的应用程序中断,从而导致此错误:
root.displayValueChanged.connect(display) AttributeError 'Nonetype' object has no attribute 'displayValueChanged'
没关系,看来正确的解决方案就是我上面的;期待我使用 textColor 的部分,因为显然那应该是 color
而不是 textColor
。但是,信号按预期工作,这是我对这个问题的目标。