piptristan
piptristan

Reputation: 3

Execute a QML function from Python script?

I'm trying to make a script that will open an input box on key command; you type something into it, it closes and returns the text to Python. So far, everything is working well, except in Python when my key combination is met, I cant figure out a way to show the QML GUI. I saw some working examples in C++, but haven't seem to find any for Python.

main.py:

import os
from pathlib import Path
import sys

from PySide6.QtCore import QCoreApplication, Qt, QUrl, QObject, Signal, Slot

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from pynput.keyboard import Key, Listener, KeyCode
#is call qml function from python to show window.


CURRENT_DIRECTORY = Path(__file__).resolve().parent
COMBINATION = {Key.shift_r, Key.enter}
current = set()
#KEYBOARD LISTENER
class KeyMonitor(QObject):
    keyPressed = Signal(KeyCode)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.listener = Listener(on_press=self.on_press, on_release=self.on_release)

    def on_press(self, key):
        if key in COMBINATION:
            current.add(key)
            if all(k in current for k in COMBINATION):
                print('All modifiers active!')
                #I WANT TO CALL QML FUNCTION OPEN() RIGHT HERE.

    def on_release(self, key):
        try:
            current.remove(key)
        except KeyError:
            pass

    def stop_monitoring(self):
        self.listener.stop()

    def start_monitoring(self):
        self.listener.start()


#SLOT STUFF
class MainBackend(QObject):
    def __init__(self):
        QObject.__init__(self)

    signalPrintTxtEmitter = Signal(bool)
    @Slot()
    def printTxt(self):
        self.signalPrintTxtEmitter.emit(True)
        print('Hello from Python!')


    input_Text = Signal(str)
    @Slot(str)
    def sendToPython(self, text):
        currentText=text
        print("Python says you typed in:" + " " + currentText)


#RUNNING QML
def main():
    app = QGuiApplication(sys.argv)

    engine = QQmlApplicationEngine()

    main = MainBackend()

    engine.rootContext().setContextProperty("backend", main)

    filename = os.fspath(CURRENT_DIRECTORY / "QML" / "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)

    sys.exit(app.exec())


if __name__ == "__main__":
    monitor = KeyMonitor()
    #monitor.keyPressed.connect(print)
    monitor.start_monitoring()
    main()

main.qml:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Window 2.15

ApplicationWindow{

    id: root
    title: qsTr("Hello, I'm main.qml!")
    visible: true
    width: 400
    height: 580
    Material.theme: Material.Dark
    Material.accent: Material.LightBlue
    //Component.onCompleted: functions.close()

    TextField {
    id: input_Box
     width: 160
     placeholderText: qsTr("Enter Here")
     anchors.centerIn: parent
     focus: true
     Keys.onPressed: {
        if (event.key == Qt.Key_Return) {
            //backend.printTxt()
            backend.sendToPython(input_Box.text)
            functions.close()
            }              
        }
     
    }

    Button{
        id: button
        focus: true 
        text: qsTr("Click me!")
        onClicked: functions.openQml()
    }

    QtObject {
        id: functions
        function close() {
            root.hide()
        }
        function open() {
            root.show()
        }
    }

    Connections {
        target: backend
        function onSignalPrintTxtEmitter(boolValue){
            return
        }
        function onSignalsendToPythonEmitter(str){
            return
        }

    }
}

Upvotes: 0

Views: 1136

Answers (1)

eyllanesc
eyllanesc

Reputation: 244132

Instead of trying to call a QML function from Python, you should issue a signal from Python and call the QML function to the associated slot. Here is an example of how you can do that:

class KeyMonitor(QObject):
    keyPressed = Signal(KeyCode)
    foo = Signal()

    # ...

    def on_press(self, key):
        if key in COMBINATION:
            current.add(key)
            if all(k in current for k in COMBINATION):
                print("All modifiers active!")
                self.foo.emit()
    main = MainBackend()

    monitor = KeyMonitor()
    QTimer.singleShot(1000, monitor.foo.emit)
    monitor.start_monitoring()

    engine.rootContext().setContextProperty("backend", main)
    engine.rootContext().setContextProperty("monitor", monitor)
    Connections{
        target: monitor
        function onFoo(){
            console.log("Foo")
            functions.open()
        }
    }

Upvotes: 1

Related Questions