There is qml file can't call function to pyd file problem.
There are several files:
i. main.py -> python.exe run entry
ii. mainWidget.py -> show MyItem.qml ui and setContextProperty to MyItem.qml
iii. MyItem.qml -> when you click mouse area will call MyObect function "cppMethod" and "cppSlot" in mainWidget.py
iv. setup.py -> build extension file to generate pyd used
v. mainWidget.pyd -> generate by Cython
In mainWidget.py, there is MyObject class, in this class, function "cppMethod" and "cppSlot" will print log on python.exe
And then I call "setContextProperty" function to register "myobject" to MyItem.qml so that MyItem.qml will recognize "myobject" object.
When I run main.py on python.exe, it will show a widget.
Try to click in this widget, it will call MyObect function "cppMethod" and "cppSlot". It worked!!!
It will show:
qml: start
call the c++ method with Hello from QMLTest
call the c++ slot with 12345
qml: end
After that, I try to protect the code, so I use Cython(with setup.py) to generate file "mainWidget.pyd".
So I replace mainWidget.pyd for mainWidget.py.
When I run main.py with python.exe with "mainWidget.pyd", I click mouse area in the widget, it will show
"TypeError: Property 'cppMethod' of object MyObject(0x21822d07430) is not a function".
It means MyItem.qml doesn't recognize "myobject".
Is there any way to fix this problem?? How to fix this problem??
ps. I use Python version 3.6.2, Qt version 5.6.2
main.py
import mainWidget
widget = mainWidget.main()
mainWidget.py
import os
import sys
import PySide2
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtQuickWidgets import *
class MyObject(PySide2.QtCore.QObject):
def __init__(self, parent=None):
super(MyObject, self).__init__(parent)
#PySide2.QtCore.Slot(str)
def cppMethod(self, msg):
print ("call the c++ method with ",msg)
#PySide2.QtCore.Slot(int)
def cppSlot(self, number):
print ("call the c++ slot with", number)
def main():
app = QApplication(sys.argv)
view = QQuickWidget()
myclass = MyObject()
view.rootContext().setContextProperty("myObject",myclass)
relativeUrl = QUrl(".//MyItem.qml")
view.setSource(relativeUrl)
view.show()
sys.exit(app.exec_())
MyItem.qml
import QtQuick 2.5
import QtQuick.Controls 1.5
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
import QtQml 2.2
Item {
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: {
console.log("start")
myObject.cppMethod("Hello from QML")
myObject.cppSlot(12345)
console.log("end")
}
}
}
setup.py
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("mainWidget.py")
)
Cython does not implement the MOC, so QML can not invoke the methods (if the generated .c is reviewed, it is observed that everything is implemented except the #Slot that the MOC creates).
A workaround is to separate the code that uses the slot and use cython in that part after you import and use the compiled code in the slot.
Related
I have a fresh QtCreator installation, and I set it up to run using a fresh install of Python3.8 on which I pip-installed both pyside2 and pyside6.
When I create a new Qt for Python - Window (UI file) application, whatever I do to the UI file the window always shows up empty and with the default size when I run the app.
I've tried with a QDialog, QMainApplication, using Pyside2 or Pyside6, I've checked that it was correctly loading the UI (and the right one) - no dice. It just won't update, and appears not to have any reason not to.
Default code for completeness:
# This Python file uses the following encoding: utf-8
import os
from pathlib import Path
import sys
from PySide2.QtWidgets import QApplication, QDialog
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
class Dialog(QDialog):
def __init__(self):
super(Dialog, self).__init__()
self.load_ui()
def load_ui(self):
loader = QUiLoader()
path = os.fspath(Path(__file__).resolve().parent / "form.ui")
ui_file = QFile(path)
ui_file.open(QFile.ReadOnly)
loader.load(ui_file, self)
ui_file.close()
if __name__ == "__main__":
app = QApplication([])
widget = Dialog()
widget.show()
sys.exit(app.exec_())
(In the UI I just drag-dropped a button right in the middle and saved the file)
Am I forgetting something fundamental? I'm only used to programming in C++ using QtCreator.
I was expecting this to work right out of the box, but it's not.
This only dynamically load the UI as a new widget, with this custom class as a parent.
If I want signals and slots to work, the only thing I've found was to add a custom build step:
Command: <path to pyside6 install, use pip show to know where>\uic.exe
Arguments: <filename of the .ui file> -o <the python translation .py
which will serve as baseclass> -g python
Working directory: [your project dir]
Then signals and slots need to be connected manually, so QtCreator really only enables you to draw the user interface but all the logic still needs to be done by hand. Component variables are normally named after their UI name, but you can see for yourself in the baseclass file. This is a big step back from how QtCreator is used in C++ ("go to slot" will not work).
Code to use:
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QFile
from PySide6.QtUiTools import QUiLoader
from YourGenPyFileName import Ui_YourUIName
class MainWindow(QMainWindow, Ui_YourUIName):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
if __name__ == "__main__":
app = QApplication([])
widget = MainWindow()
widget.show()
sys.exit(app.exec_())
I am getting these errors, when i try to launch the program:
plugin cannot be loaded for module, cannot install type into protected module
Platform:
Python 3.8
PyQt5 5.15.0
Visual Studio Community 2019
Windows 10 Pro 1909
main python file (the whole thing is pretty much the example from here: https://codeloop.org/pyqt5-creating-first-window/ )
import numpy
import os
import sys
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtWidgets import QApplication
def main():
app =QApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load(os.path.join(os.path.dirname(__file__), "MainApp.qml"))
if not engine.rootObjects():
return -1
return app.exec_()
if __name__ == '__main__':
main();
The corresponding QML File "MainApp.qml":
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick 2.15
Window {
visible:true
width:600
height:400
color:"yellow"
title: "PyQt5 QML Window"
Button {
text: "Something"
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
}
Without using anything from QtQuick.Controls, it works, but as soon as i add the button, it generates these errors:
QQmlApplicationEngine failed to load component
file:///C:/Users/elano/Source/Repos/Projekt-1-SS2020/Projekt-1-SS2020/MainApp.qml:15:5: Type Button unavailable
file:///C:/Users/elano/vpqt/lib/site-packages/PyQt5/Qt/qml/QtQuick/Controls.2/qmldir: plugin cannot be loaded for module "QtQuick.Controls": Cannot install type 'VerticalHeaderView' into protected module 'QtQuick.Controls' version '2'
<Unknown File>: Cannot install type 'HorizontalHeaderView' into protected module 'QtQuick.Controls' version '2'
<Unknown File>: Cannot install element 'SplitHandle' into protected module 'QtQuick.Controls' version '2'
...
The file it tries to find (qmldir) does exist, and contains this:
module QtQuick.Controls
plugin qtquickcontrols2plugin
classname QtQuickControls2Plugin
depends QtQuick.Templates 2.5
designersupported
Does anyone know what is going on here? Is more information needed?
Okay, now i feel stupid. I already kinda had my answer in my code, but since it didn't get Pyside2 to work, i commented it out...
The problem was, that an environment variable was not set properly. Adding this after my import statements in my main file fixed it:
dirname = os.path.dirname(PyQt5.__file__)
plugin_path = os.path.join(dirname, 'plugins', 'platforms')
os.environ['QML2_IMPORT_PATH'] = os.path.join(dirname, 'qml')
No tutorial i found has ever mentioned this. Great.
I have a main.qml file that look like below
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TextField {
id:textarea
anchors.centerIn: parent
Button {
text: "Click Me"
anchors.leftMargin: 34
id:textareabutton
y: 0
anchors.left:textarea.right
onClicked: {
someclass.say(textarea.text)
}
}
}
TextField {
id:textarea2
anchors.horizontalCenterOffset: 0
anchors.topMargin: 37
anchors.top: textarea.bottom
anchors.horizontalCenter: textarea.horizontalCenter
}
Connections {
target: someclass
onToPython : {
textarea2.text = say
}
}
}
i have a python class file which i have added using add file option in qtcreator, when i run main.qml i am getting errors related to the classes being undefined as below
qrc:/main.qml:33:5: QML Connections: Cannot assign to non-existent property "onToPython"
qrc:/main.qml:34: ReferenceError: someclass is not defined
qrc:/main.qml:22: ReferenceError: someclass is not defined
I have the external tools configured in the Qt creator for python and when i run through it,it works. However it is not working when i run main.qml. What am i missing, how can i make use of the python class file.
below is the python file that invokes the QML , if i run from python it works, i want to run qml file and invoke this class
import sys
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtCore import QObject,pyqtSignal,pyqtSlot
class someclassr(QObject):
def __init__(self):
QObject.__init__(self)
toPython=pyqtSignal(str, arguments=["say"])
#pyqtSlot(str)
def say (self,name):
word= "hi " + name
self.toPython.emit(word)
app = QGuiApplication(sys.argv)
engine=QQmlApplicationEngine()
classloader=someclassr()
engine.rootContext().setContextProperty('someclass',classloader)
engine.load('main.qml')
engine.quit.connect(app.quit)
sys.exit(app.exec_())
Short answer: There's no built-in integration between QML and Python. I'm not sure why one'd assume that there was, but there really isn't. Qt Creator is a multi-language IDE, and its support for Python doesn't imply that QML and Python are integrated.
Having said that, Python classes can be easily integrated with Qt and QML using PyQt. If you don't want to depend on PyQt, you can integrate the two manually by writing adapter classes that call into the Python runtime that your application would link with.
I hava a Python file ang a QML file. i get value in Python file from QML, and the type of value is QJSValue , i want to convert it to Python list. i don't know how to do it.
following is my code.
test.python
#!/usr/bin/env python
# encoding: utf-8
from PyQt5.QtCore import QUrl, QObject, pyqtSlot,QVariant
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
from PyQt5.QtQml import QJSValue
class MyMain(QObject):
#pyqtSlot(QJSValue)
def get_value(self,value):
print(value,type(value))
if __name__ == '__main__':
path = 'test.qml'
app = QApplication([])
view = QQuickView()
con = MyMain()
context = view.rootContext()
context.setContextProperty("con",con)
view.engine().quit.connect(app.quit)
view.setSource(QUrl(path))
view.show()
app.exec()
test.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
Button {
text: "click"
onClicked: {
con.get_value([{"name":"a","text":"1"},{"name":"b","text":"2"}])
}
}
I realize this question is extremely old, but I thought I'd post how I came to the solution in case someone else stumbles upon it via Google.
While it isn't technically a direct conversion to a python list, what I found useful was to do myQJSValueInstance.toVariant() which converts the instance to a QVariant. I could then use the object as needed (e.g., iterating through it as if it were a normal python list).
I have just designed my application inside pyQt Designer 5, generated my main.ui to main.py and my assets.qrc to assets_rc.py. No errors, when I now run main.py from my terminal nothing happends. Have I missed a step? Am I supposed to edit my main.py file now?
Cheers!
Python 3.3.0
pyQT 5
This is for PyQt4, but should be the same for PyQt5.
Let's say your ui is called "mainwindow.ui". Compile this with pyuic4 into "mainWindowUi.py" (or whatever, just stick to the name).
Now create a file "mainWindow.py" with more or less this content:
from PyQt4 import QtGui
from mainWindowUi import Ui_MainWindow #same name as appears in mainWindowUi.py
class MainWindow (QtGui.QMainWindow): #Or wherever you are inheriting from
def __init__ (self, parent = None):
super (MainWindow, self).__init__ ()
self.ui = Ui_MainWindow () #same name as appears in mainWindowUi.py
self.ui.setupUi (self)
#implement slots and signals and other funny things
Now create a file "program.py" with more or less this content:
#! /usr/bin/python3.3
import sys
from PyQt4 import QtGui
from mainWindow import MainWindow
def main():
app = QtGui.QApplication (sys.argv)
m = MainWindow ()
m.show ()
sys.exit (app.exec_ () )
if __name__ == '__main__':
main ()
Run the program.py file. This is more or less the skeleton of a Qt application.