Issue
I am trying to figure out how to pass data from a TextField input to my Python class. I think the way to go about this is through using signals and slots; however, I am using custom QML components in my widget.qml (main qml class) that are passing the data. I need to do this so I can perform more complex operations on the data passed in through the TextField.
I am confused about how I can pass data from my BasicContainer.qml to my python class, and not sure if the logic in my widget.qml class would even support that since it uses nested models. I've attached an example that hopefully gets to the point with the control flow. I can use a basic signal in widget.qml that I added, but if I do the same thing in BasicContainer.qml I get a 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()
}
}
}
}
}
IMPORTANT EDIT:
I modified the code to use signals to pass values from the BasicContainer class all the way to Python, so when you make a text input and press enter, the new text you enter will be logged. This should have solved everything; however, the moment I try to make any styling changes like the text color change line in BasicContainer.qml: textColor: "#FF3333"
will cause my application to break, resulting in this error:
root.displayValueChanged.connect(display) AttributeError 'Nonetype' object has no attribute 'displayValueChanged'
Solution
Nevermind, it seems that the correct solution is just what I had above; expect for the part where I used textColor because apparently that is just supposed to be color
and not textColor
.However, the signals work as expected which was my goal to this question.
Answered By - SCP3008
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.