PyQt, JavaScript and WorkerScript



  • I'm trying to work with WorkerScript that will call a class in Python, but the way I have it setup is giving me an error when I run it.

    Here is what I have so far:

    main.py

    import sys
    
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtQml import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtQuick import *
    
    class test(QObject):
    
        @pyqtSlot()
        def printStatus(self):
            print("hello from main.py")
    
    def main():
    
        app = QGuiApplication(sys.argv)
    
        engine = QQmlApplicationEngine()
    
        tryTest = test()
        engine.rootContext().setContextProperty('tryTest', tryTest)
    
        engine.load(QUrl("main.qml"))
        app.exec()
    
    if __name__ == "__main__":
        main()
    

    workerscript.js

    WorkerScript.onMessage = function() {
        prt()
    }
    
    function prt() {
        tryTest.printStatus();
    }
    

    Part of my QML file that sets up the WorkerScript

    Timer {
            id: timer1
            interval: 1000
            repeat: true
            running: true
    
            onTriggered: {
                worker.sendMessage();
            }
        }
    
        WorkerScript {
            id: worker
            source: "workerscript.js"
        }
    

    When I execute main.py my UI loads fine, but then when the WorkerScript calls my js file I get the following error message:

    workerscript.js:6: ReferenceError: tryTest is not defined

    I'm assuming it's because workerscript.js doesn't know about my main.py. When I import workerscript.js into my QML file and call the function without using the WorkerScript it executes just fine. Any idea what I am doing wrong? I am using PyQt5 and Python 3.



  • @JasonS Hi! The JS file is executed on its own thread and in its own context. Thus it can't access anything from your QML engine's context, including the context properties you set. The only way to interact with the WorkerScript is using sendMessage and the onMessage.



  • @Wieland Hmm...I think I'm following you, but aren't I using sendMessage? I create the WorkerScript element as worker that has workerscript.js as the source file. In my onTriggered from my timer I do worker.sendMessage().

    Timer {
            id: timer1
            interval: 1000
            repeat: true
            running: true
    
            onTriggered: {
                worker.sendMessage();
            }
        }
    
        WorkerScript {
            id: worker
            source: "workerscript.js"
        }
    

    Then what I'm trying to do is access my Python class from my JS file which is where I'm getting the error. It's not seeing the Python file even though the Python script is what loads the UI. Is this what you mean or are you saying what I am trying to do wont work? If it wont work do you have any suggestions on how I can access my class in the Python file using a different thread like the WorkerScript does? I need to interact with GPIO on my Raspberry PI which will take time based on the fact that I will be turning a motor on for an extended period of time. I don't want the GUI to lock up while this happens. I thought the WorkerScript would be perfect for this.



  • You're using tryTest in your JS file, but it's not available there. I guess what you actually want looks more like:

    workerscript.js

    WorkerScript.onMessage = function(message) {
        // compute something based on message
        var z = message.x + message.y
        // send result
        WorkerScript.sendMessage({x:message.x, y:message.y, z:z})
    }
    

    main.qml

    Timer {
            id: timer
            interval: 1000
            repeat: true
            running: true
    
            property real x : 23
            property real y : 42
    
            onTriggered: worker.sendMessage({x:timer.x, y:timer.y});
        }
    
        WorkerScript {
            id: worker
            source: "workerscript.js"
            onMessage: {
                  tryTest.printStatus(); // <- that's OK
                  console.log("z=x+y <=> "+ messageObject.z +"="+ messageObject.x +"+"+ messageObject.y )
            }
        }
    


  • @Wieland Ok, I see the issue now and what the script you provided is doing. Basically it looks like you could put anything in the workerscript.js sendMessage just so it triggers the onMessage in the QML, then since the Python is hooked up to the QML it knows about tryTest. Correct? I think I proved this by passing "" to sendMessage... WorkerScript.sendMessage("").

    Seems like a hokey way of doing it. Well, at least I know this works, but I think I'll look for a cleaner solution. I think I have a couple:

    https://wiki.qt.io/Updating-QML-content-from-Python-threads
    http://stackoverflow.com/questions/10650960/concurrent-programming-in-python-and-qt

    and this one gives multiple options:
    http://stackoverflow.com/questions/6783194/background-thread-with-qthread-in-pyqt

    If you have other ideas it would be super duper if you shared, otherwise I have some playing around to do.

    Thanks for all your help.



  • Ah, sry, I was a bit out of it. Controlling your motor from a WorkerScript looks like a bad idea in general and I have doubts if it's even possible without some really dirty hacks. QtQuick is good for implementing GUIs but all business logic belongs in a C++ backend (or Python backend, if you like that better). QML is great for GUIs but its too limited to do any serious tasks beyond just that. Also JavaScript is really slow and I don't think you want to control real hardware with that.



  • @JasonS said in PyQt, JavaScript and WorkerScript:

    Correct?

    Yes, that's how it works :) Also your idea of using an extra thread in Python to control the motor seems to be the best idea.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.