I am trying to extend QML with qmlRegisterType. I have a python class - PyQml.py, main.qml files and boilerplate code for it.
Problem is I cant reference(import) PyQml object in main.qml file, I get an error --> QML module not found (PyQml) .
So far I have determined QML_IMPORT_PATH variable paths. Since I was hopelles, I created folder named PyQml with PyQml.py in it in one of the paths but still no success. Furthermore, I cant find *.pro file in Qt Creator project. I suppose I should add another path to my custom object to it.
PyQml.py
class PyQml(QObject):
def __init__(self, parent=None):
super().__init__(parent)
# Initialise the value of the properties.
self._name = ''
self._shoeSize = 0
# Define the getter of the 'name' property. The C++ type of the
# property is QString which Python will convert to and from a string.
#Property('str')
def name(self):
return self._name
# Define the setter of the 'name' property.
#name.setter
def name(self, name):
self._name = name
# Define the getter of the 'shoeSize' property. The C++ type and
# Python type of the property is int.
#Property(int)
def shoeSize(self):
return self._shoeSize
# Define the setter of the 'shoeSize' property.
#shoeSize.setter
def shoeSize(self, shoeSize):
self._shoeSize = shoeSize
qmlengine.py
import sys
import sqlite3
from PySide2 import QtCore, QtGui, QtWidgets, QtQuick
from PySide2.QtCore import Qt,QUrl
from PySide2.QtQml import QQmlApplicationEngine,qmlRegisterType
from PySide2.QtGui import QGuiApplication
from ViewModel import PyQml
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
print(QQmlApplicationEngine.importPathList(engine))
ctx = engine.rootContext()
ctx.setContextProperty("qmlapp", engine) #the string can be anything
qmlRegisterType(PyQml.PyQml, 'People', 1, 0, 'Person');
engine.load('Documents/ctmd/Qml/main.qml')
win = engine.rootObjects()[0]
win.show()
sys.exit(app.exec_())
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.4
import PyQml 1.0 ---- Error QML module not found ( PyQml)
ApplicationWindow {
menuBar: MenuBar {
Menu {
title: "File"
MenuItem { text: "Open..." }
MenuItem { text: "Close" }
}
Menu {
title: "Edit"
MenuItem { text: "Cut" }
MenuItem { text: "Copy" }
MenuItem { text: "Paste" }
}
}
Grid {
columns: 3
spacing: 2
Rectangle { color: "red"; width: 50; height: 50 }
Rectangle { color: "green"; width: 20; height: 50 }
Rectangle { color: "blue"; width: 50; height: 20 }
Rectangle { color: "cyan"; width: 50; height: 50 }
Rectangle { color: "magenta"; width: 10; height: 10 }
}
}
Project folder tree
Qml
-main.qml
PyQml.py
qmlengine.py
PyQml is just a sample class, at the end of day I want to pass custom data I calculated in python ( x,y coordinates ) and plot that data with qml
TL;DR; There is no solution to eliminate the error message since it is a limitation of QtCreator with Qt for Python. But QtCreator is only an IDE so it does not need to work there, instead you just have to run the code from the console/CMD:
python /path/of/script.py
You have the following errors:
When you register a QObject with qmlRegisterType "People" is the name of the package in QML and "Person" is the name of the component, so you should not use PyQml in the import unless you change the registry parameters.
QtCreator/QtQuickDesigner still has limitations with Python support, so the message: "Qml module not found (FooPackage)" is a sample of this. As the developers of Qt for Python/PySide2 point out in future versions they will add new features but it is currently not possible.
I see that the structure that you indicate in your publication does not match your project since for example you indicate that the main.qml is in the QML folder that is at the same level as qmlengine.py but in the load you use "Documents/ctmd/Qml/main.qml".
PySide2 has limitations with the Property decorator and its setter since it is not recognized by QML, instead it uses the extensive declaration: name_of_property = Property(type_of_property, fget = getter_of_property, ...)
If a Qt Property with a setter then it must have an associated signal.
Considering the above, the solution is:
├── PyQml.py
├── Qml
│ └── main.qml
└── qmlengine.py
qmlengine.py
import os
import sys
from PySide2.QtCore import QUrl
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType
import PyQml
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
qmlRegisterType(PyQml.PyQml, "People", 1, 0, "Person")
engine = QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("qmlapp", engine) # the string can be anything
current_dir = os.path.dirname(os.path.realpath(__file__))
filename = os.path.join(current_dir, "Qml/main.qml")
engine.load(QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
PyQml.py
from PySide2.QtCore import Property, Signal, QObject
class PyQml(QObject):
nameChanged = Signal(str)
shoeSizeChanged = Signal(int)
def __init__(self, parent=None):
super().__init__(parent)
# Initialise the value of the properties.
self._name = ""
self._shoeSize = 0
# Define the getter of the 'name' property. The C++ type of the
# property is QString which Python will convert to and from a string.
def get_name(self):
return self._name
# Define the setter of the 'name' property.
def set_name(self, name):
if self._name != name:
self._name = name
self.nameChanged.emit(name)
name = Property(str, fget=get_name, fset=set_name, notify=nameChanged)
# Define the getter of the 'shoeSize' property. The C++ type and
# Python type of the property is int.
def get_shoeSize(self):
return self._shoeSize
# Define the setter of the 'shoeSize' property.
def set_shoeSize(self, shoeSize):
if self._shoeSize != shoeSize:
self._shoeSize = shoeSize
self.shoeSizeChanged.emit(shoeSize)
shoeSize = Property(
int, fget=get_shoeSize, fset=set_shoeSize, notify=shoeSizeChanged
)
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.4
import People 1.0
ApplicationWindow {
visible: true
Person{
name: "foo"
shoeSize: 10
}
menuBar: MenuBar {
Menu {
title: "File"
MenuItem { text: "Open..." }
MenuItem { text: "Close" }
}
Menu {
title: "Edit"
MenuItem { text: "Cut" }
MenuItem { text: "Copy" }
MenuItem { text: "Paste" }
}
}
Grid {
columns: 3
spacing: 2
Rectangle { color: "red"; width: 50; height: 50 }
Rectangle { color: "green"; width: 20; height: 50 }
Rectangle { color: "blue"; width: 50; height: 20 }
Rectangle { color: "cyan"; width: 50; height: 50 }
Rectangle { color: "magenta"; width: 10; height: 10 }
}
}
I found out if I simply ignore this error
in qml window everything works fine. I even tried reinstalling IDE, error stays. Thank you for clarification.
Related
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide6.QtCore import QObject, Signal, Slot, Property, Qt,QUrl, QTimer, QAbstractListModel
class CpuLoadModel(QAbstractListModel):
def __init__(self):
QAbstractListModel.__init__(self)
self.__update_timer = QTimer(self)
self.__update_timer.setInterval(1000)
self.__update_timer.timeout.connect(self.__update)
self.__update_timer.start()
self.todos = []
def __update(self):
self.todos.append(random.randint(0, 99))
self.layoutChanged.emit()
print(self.todos)
self.dataChanged.emit(self.index,self.index,[])
def data(self, index, role):
if role == Qt.DisplayRole:
text = self.todos[index.row()]
print(f"Text: {index.row()} role : {role}")
print("--------")
return text
def rowCount(self, index):
return len(self.todos)
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
qmlRegisterType(CpuLoadModel, 'PsUtils', 1, 0, 'CpuLoadModel')
engine.load(QUrl("main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
self.dataChange.emit method is not working at all, however self.LayoutChange.emit works but with those mistakes from qml
TypeError: Cannot read property 'width' of null
The layoutChanged signal must be emitted when something in the model has changed (for example it has been reordered), and dataChanged when any of the items change data but none of them is used to indicate that a row was added, in that case it must use the beginInsertRows and endInsertRows methods.
def __update(self):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.todos.append(random.randint(0, 99))
self.endInsertRows()
Also change to:
def rowCount(self, index=QModelIndex()):
if index.isValid():
return 0
return len(self.todos)
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import PsUtils 1.0
Window {
id: root
width: 640
height: 480
visible: true
title: "CPU Load"
ListView {
id:listviewId
anchors.fill: parent
model: CpuLoadModel { }
delegate: Rectangle {
width: listviewId.width
height: 30;
color: "white"
Rectangle {
id: bar
width: listviewId.width * display / 100.0
height: 30
color: "green"
}
Text {
anchors.verticalCenter: parent.verticalCenter
x: Math.min(bar.x + bar.width + 5, parent.width-width)
text: display + "%"
}
}
}
}
Basically the mistake was the parent.width in QML we better off to use id to refer to element instead of using parent.width ( use elementId.width)
I made a QtQuick Window Gui Application for Python 3.8 on Windows. The last thing I cant figure out is how to display Python print() in the Gui Text Area. What i want is, that wherever in my Python code a print statement is and gets executed during runtime, i want to output it into the TextArea in my Gui app
I read the following post, but failed to implemet it, different errors occured and am more confused then before:
the closest and most usefull was this one:
How to capture output of Python's interpreter and show in a Text widget?
and some others:
Python/PyQt/Qt Threading: How do I print stdout/stderr right away?
How to Redirect a Python Console output to a QTextBox
How can I flush the output of the print function?
How do I direct console output to a pyqt5 plainTextEdit widget with Python?
Python Printing StdOut As It Received
working Sample Code to send a string from Python into QML TextArea
main.py
import os
from pathlib import Path
import sys
from vantage import daily
# load GUI libs
from PySide2.QtGui import QGuiApplication
from PySide2.QtCore import QSettings, QObject, Signal, Slot
from PySide2.QtQml import QQmlApplicationEngine
# load app
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
class Backend(QObject):
textwritten = Signal(str, arguments=['writen'])
def __init__(self):
super().__init__()
self.timer = QTimer()
self.timer.setInterval(100)
self.timer.timeout.connect(self.writer)
self.timer.start()
# console output write function
def writer(self):
towrite = 'i am writing'
self.textwritten.emit(str(towrite))
# create an instance of the Python object (Backend class)
back_end = Backend()
# give data back to QML
engine.rootObjects()[0].setProperty('writen', back_end)
# close app
sys.exit(app.exec_())
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
color: "#2f2f2f"
title: qsTr("alpha")
/*print out console text*/
property string texted: "Console Text"
property QtObject writen
ScrollView {
id: scrollViewCon
x: 58
y: 306
width: 507
height: 100
ScrollBar.vertical.verticalPadding: 4
ScrollBar.vertical.minimumSize: 0.4
ScrollBar.vertical.contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 100
radius: width / 2
color: control.pressed ? "#81e889" : "#f9930b"
}
TextArea {
font.family: control.font
font.pointSize: 8
color:"#f9930b"
wrapMode: TextEdit.Wrap
KeyNavigation.priority: KeyNavigation.BeforeItem
KeyNavigation.tab: textField
placeholderTextColor : "#f9930b"
opacity: 1
text: texted
placeholderText: texted //qsTr("Console")
readOnly: true
background: Rectangle {
radius: 12
border.width: 2
border.color: "#f9930b"
}
}
}
Connections {
target: writen
function onTextwritten(msg) {
texted = msg;
}
}
}
i think what needs to happen is that everytime sys.stdout is called by print() it emits a signal with itself?
leaving main.qml as is and only changing main.py
main.py
...
class Backend(QObject):
textwritten = Signal(str, arguments=['writen'])
def __init__(self):
super().__init__()
sys.stdout = self.writer(str(sys.stdout))
def writer(self, message):
#towrite = 'i am writing'
self.textwritten.emit(message)
...
The print function writes over sys.stdout so the solution is to assign some QObject that has a write method that emits a signal. For this you can use contextlib.redirect_stdout:
import os
import sys
from contextlib import redirect_stdout
from functools import cached_property
from pathlib import Path
from PySide2.QtCore import (
QCoreApplication,
QDateTime,
QObject,
Qt,
QTimer,
QUrl,
Signal,
)
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
CURRENT_DIRECTORY = Path(__file__).resolve().parent
class RedirectHelper(QObject):
stream_changed = Signal(str, name="streamChanged", arguments=["stream"])
def write(self, message):
self.stream_changed.emit(message)
class TimerTest(QObject):
#cached_property
def timer(self):
return QTimer(interval=1000, timeout=self.handle_timeout)
def handle_timeout(self):
print(QDateTime.currentDateTime().toString())
def start(self):
self.timer.start()
def main():
ret = 0
redirect_helper = RedirectHelper()
with redirect_stdout(redirect_helper):
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("redirectHelper", redirect_helper)
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)
timer_test = TimerTest()
timer_test.start()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
Flickable {
id: flickable
flickableDirection: Flickable.VerticalFlick
anchors.fill: parent
TextArea.flickable: TextArea {
id: textArea
anchors.fill: parent
readOnly: true
font.pointSize: 8
color: "#f9930b"
wrapMode: TextEdit.Wrap
placeholderTextColor: "#f9930b"
opacity: 1
placeholderText: qsTr("Console")
background: Rectangle {
radius: 12
border.width: 2
border.color: "#f9930b"
}
}
ScrollBar.vertical: ScrollBar {
}
}
Connections {
function onStreamChanged(stream) {
textArea.insert(textArea.length, stream);
}
target: redirectHelper
}
}
Is there a way for me to treat QML components as objects, and initialize them in Python? For instance, here's a simplified QML for a box:
I want to be able to replicate what a constructor method can do in Java. I want to be able to customize the text on each box through the Python script and also, at the same time, create multiple box instances that are separate from each other.
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: boxItem
width: 800
height: 118
Rectangle {
id: boxRect
height: 118
color: "#55f555"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
Text {
id: textItem
width: 463
height: 43
color: "#000000"
text: qsTr("Header Text")
anchors.left: parent.left
anchors.top: parent.top
font.pixelSize: 38
verticalAlignment: Text.AlignVCenter
font.family: "Roboto"
textFormat: Text.AutoText
anchors.leftMargin: 20
anchors.topMargin: 20
}
}
}
This is my current Python script modified from Qt's template version:
import os
import sys
from pathlib import Path
import PySide6.QtQml
from PySide6.QtQuick import QQuickView
from PySide6.QtCore import Qt, QUrl
from PySide6.QtGui import QGuiApplication
if __name__ == '__main__':
#Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
#Load the QML file
qml_file = Path(__file__).parent / "Main.qml"
view.setSource(QUrl.fromLocalFile(os.fspath(qml_file.resolve())))
#Show the window
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
#execute and cleanup
app.exec()
del view
Quick clarification: I am working with custom built QML components, not trying to edit pre-existing ones made by QT.
Applying the concepts of another technology (programming language, library, framework) are often a bad approach to use some other technology. Each technology has its own methodology and good practices to implement any requirement.
In the case of QML the other languages such as C++, python, etc. are generally considered to implement business logic (something like a backend) and QML takes care of the view and the operation. In this case, it is recommended to create a QObject that provides other QObjects that create, modify, etc. the data and this can be reflected in the view. In this particular case, for the requirement, it is enough to use a model and a custom qml Item.
Box.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: boxItem
width: 800
height: 118
property alias text: textItem.text
Rectangle {
id: boxRect
height: 118
color: "#55f555"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
Text {
id: textItem
width: 463
height: 43
color: "#000000"
anchors.left: parent.left
anchors.top: parent.top
font.pixelSize: 38
verticalAlignment: Text.AlignVCenter
font.family: "Roboto"
textFormat: Text.AutoText
anchors.leftMargin: 20
anchors.topMargin: 20
}
}
}
main.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: root
ScrollView
{
anchors.fill: parent
Column{
Repeater{
model: manager.model
Box{
text: model.display
}
}
}
}
}
main.py
import os
import sys
from pathlib import Path
from PySide6.QtCore import Property, QObject, Qt, QUrl
from PySide6.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
from PySide6.QtQuick import QQuickView
class Manager(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = QStandardItemModel()
#Property(QObject, constant=True)
def model(self):
return self._model
if __name__ == "__main__":
# Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
manager = Manager()
view.rootContext().setContextProperty("manager", manager)
qml_file = Path(__file__).parent / "main.qml"
view.setSource(QUrl.fromLocalFile(os.fspath(qml_file.resolve())))
if view.status() == QQuickView.Error:
sys.exit(-1)
view.resize(640, 480)
view.show()
for i in range(20):
item = QStandardItem(f"item-{i}")
manager.model.appendRow(item)
app.exec()
I have two qml components sitting in one module.
components
|- Edge.qml
|- Class.qml
|- qmdir
main
|- main.qml
main.py
with main.qml
import urmelgraph.components 1.0
import CustomGeometry 1.0
ApplicationWindow {
visible: true
width: 640
height: 240
title: qsTr("Test")
color: "#2C3E50"
}
and qmdir
module urmelgraph.components
Class 1.0 Class.qml
Edge 1.0 Edge.qml
I am loading both inside my python main.py file.
from PyQt5.QtGui import QGuiApplication, QColor, QSurfaceFormat
from PyQt5.QtQml import QQmlApplicationEngine, QQmlComponent, QQmlContext, qmlRegisterType, QQmlProperty
from PyQt5.QtQuick import QQuickItem, QQuickView, QSGGeometryNode, QSGGeometry, QSGNode, QSGFlatColorMaterial
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty, QUrl, QPointF, QSizeF
from pathlib import Path
class StraightLine(QQuickItem):
p1_Changed = pyqtSignal()
p2_Changed = pyqtSignal()
segment_count_Changed = pyqtSignal()
def __init__(self, parent: QQuickItem, p1: QPointF = QPointF(0,0), p2: QPointF = QPointF(1,1), segment_count: int = 2):
super().__init__(parent)
self._p1 = p1
self._p2 = p2
self._segment_count = segment_count
self.setFlag(QQuickItem.ItemHasContents, True)
#pyqtProperty("QPointF", notify = p1_Changed)
def p1(self):
return self._p1
#p1.setter
def p1(self, p1: QPointF):
if p1 == self._p1:
return
self._p1 = p1
self.p1_Changed.emit()
self.update()
#pyqtProperty("QPointF", notify = p2_Changed)
def p2(self):
return self._p2
#p2.setter
def p2(self, p2: QPointF):
if p2 == self._p2:
return
self._p2 = p2
self.p2_Changed.emit()
self.update()
#pyqtProperty(int, notify = segment_count_Changed)
def segment_count(self):
return self._segment_count
#segment_count.setter
def segment_count(self, count: int):
if count == self._segment_count:
return
self._segment_count = count
self.segment_count_Changed.emit()
self.update()
def updatePaintNode(self, oldNode: QSGGeometryNode, _):
if oldNode == None:
node = QSGGeometryNode()
geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), self._segment_count)
geometry.setLineWidth(3)
geometry.setDrawingMode(QSGGeometry.DrawLineStrip)
node.setGeometry(geometry)
node.setFlag(QSGNode.OwnsGeometry)
material = QSGFlatColorMaterial()
material.setColor(QColor(45, 100, 120))
node.setMaterial(material)
node.setFlag(QSGNode.OwnsMaterial)
else:
node = oldNode
geometry = node.geometry()
geometry.allocate(self._segment_count)
itemSize = self.size()
vertices = geometry.vertexDataAsPoint2D()
x1 = self._p1.x()
y1 = self._p1.y()
vertices[0].set(x1, y1)
x2 = self._p2.x()
y2 = self._p2.y()
vertices[1].set(x2, y2)
print(vertices[1].x)
node.markDirty(QSGNode.DirtyGeometry)
return node
if __name__ == "__main__":
import sys
path = Path("..")
resolved_path = path.resolve()
# Create an instance of the application
app = QGuiApplication(sys.argv)
# Set antialising 4 samples
format = QSurfaceFormat()
format.setSamples(4)
QSurfaceFormat.setDefaultFormat(format)
# register custom types
qmlRegisterType(StraightLine, "CustomGeometry", 1, 0, "StraightLine")
# Create QML engine
engine = QQmlApplicationEngine()
# Load the qml file into the engine
engine.addImportPath(str(resolved_path))
engine.load("main/main.qml")
# load the components
component = QQmlComponent(engine)
component.loadUrl(QUrl("components/Class.qml"))
line_component = QQmlComponent(engine)
line_component.loadUrl(QUrl("components/Edge.qml"))
# check for component creation errors
for error in component.errors():
print(error.toString())
# check for component creation errors
for error in line_component.errors():
print(error.toString())
classes = []
for index, class_name in enumerate(["Person", "Home"]):
# create a new instance of the component
cclass = component.create()
# set the class name property of the component
cclass.setProperty("className", class_name)
cclass.setX((cclass.width() + 50) * index)
cclass.setParentItem(engine.rootObjects()[0].findChild(QQuickItem))
classes.append(cclass)
line = line_component.beginCreate(engine.rootContext())
line.setProperty("anchor1", classes[0])
line.setProperty("anchor2", classes[1])
line_component.completeCreate()
# check for object creation errors
for error in line_component.errors():
print(error.toString())
for error in component.errors():
print(error.toString())
engine.quit.connect(app.quit)
sys.exit(app.exec_())
But now I want to connect the first point of the Edge E to a Class component A and the second point of the Edge E to a Class component B.
For that I have created properties in the Edge.qml.
import QtQuick 2.11
import CustomGeometry 1.0
import urmelgraph.components 1.0
StraightLine {
property Class anchor1
property Class anchor2
anchors.fill: parent
Component.onCompleted: {
console.log(anchor1)
console.log(anchor2)
}
p2: Qt.point(anchor2.x + (anchor2.width/2), anchor2.y + (anchor2.height/2))
p1: Qt.point(anchor1.x + (anchor1.width/2), anchor1.y + (anchor1.height/2))
}
This is my Class.qml
import QtQuick 2.11
import QtQuick.Layouts 1.11
Rectangle {
width: 50
height: 20
color: "#2980B9"
border.color: "#ECF0F1"
property string className
Drag.active: dragArea.drag.active
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
// disable delay when moved
drag.threshold: 0
}
Text {
text: className
}
}
In my main.py I have a classes list with all generated Class components but trying to connect the first class with the second class via Edge (line), for example, doesn't work:
line = line_component.beginCreate(engine.rootContext())
line.setProperty("anchor1", classes[0])
line.setProperty("anchor2", classes[1])
line_component.completeCreate()
However, if I create two Rectangles with id rect1 and rect2 within the main.qml file. using the QQuickItem StraightLine this code is working:
StraightLine {
anchors.fill: parent
p1: Qt.point(rect2.x + (rect2.width/2), rect2.y + (rect2.height/2))
p2: Qt.point(rect1.x + (rect1.width/2), rect1.y + (rect1.height/2))
}
Rectangle {
id: rect1
width: 10
height: 10
color: "red"
radius: width*0.5
Drag.active: dragArea.drag.active
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
// disable delay when moved
drag.threshold: 0
}
}
Rectangle {
id: rect2
width: 10
height: 10
color: "blue"
radius: width*0.5
Drag.active: dragArea2.drag.active
MouseArea {
id: dragArea2
anchors.fill: parent
drag.target: parent
// disable delay when moved
drag.threshold: 0
}
}
How can I pass the reference from those class components to my edge components to properly setup a binding for x,y,width,height?
The solution was to establish the data type of the property anchor1 and anchor2 to var.
Edge.qml
import QtQuick 2.11
import CustomGeometry 1.0
StraightLine {
property var anchor1
property var anchor2
anchors.fill: parent
Component.onCompleted: {
console.log(anchor1)
console.log(anchor2)
}
p2: Qt.point(anchor2.x + (anchor2.width/2), anchor2.y + (anchor2.height/2))
p1: Qt.point(anchor1.x + (anchor1.width/2), anchor1.y + (anchor1.height/2))
}
On the other hand, I have not included the QtQuick.Controls 1.4 import to recognize the ApplicationWindow in the main.qml:
import QtQuick.Controls 1.4
import urmelgraph.components 1.0
import CustomGeometry 1.0
ApplicationWindow {
visible: true
width: 640
height: 240
title: qsTr("Test")
color: "#2C3E50"
}
In the following link you will find the complete code
I'm trying to call a Python method from QML and use the return value.
QML receives undefined, from the Python method.
When passed back to Python it is just an empty string.
import sys
from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
class InPython(QObject):
#pyqtSlot(str, )
def login(self, Login):
print(Login)
return "a"
if __name__ == "__main__":
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
context = engine.rootContext()
context.setContextProperty("main", engine)
engine.load('Main.qml')
win = engine.rootObjects()[0]
inPython = InPython()
context.setContextProperty("loginManger", inPython)
win.show()
sys.exit(app.exec_())
Main.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.0
ApplicationWindow {
width: 800;
height: 600;
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
anchors.margins: 3
spacing: 3
Column {
spacing: 20
anchors.horizontalCenter: parent.horizontalCenter
TextField {
id: login
objectName: "login"
placeholderText: qsTr("Login")
focus: true
Layout.fillWidth: true
onAccepted: {
btnSubmit.clicked()
}
}
Button {
id: btnSubmit
objectName: "btnSubmit"
text: qsTr("Login")
Layout.fillWidth: true
onClicked: {
var a = loginManger.login(login.text);
console.log(a);
loginManger.login(a); // Python recieves ''
if (a === "a"){
login.text = "SUCCESS"
}
}
}
}
}
}
You also need to tell what you are returning from your method (pay attention to the result in the pyqtslot decorator:
class InPython(QObject):
#pyqtSlot(str, result=str) # also works: #pyqtSlot(QVariant, result=QVariant)
def login(self, Login):
print(Login)
return "a"
result – the type of the result and may be a Python type object or a
string that specifies a C++ type. This may only be given as a keyword
argument.
Documentation about #pyqtslot (and the result parameter)