PyQt 和 QML:如何在一个插槽或函数中处理多个信号

Posted

技术标签:

【中文标题】PyQt 和 QML:如何在一个插槽或函数中处理多个信号【英文标题】:PyQt and QML: How can multiple signals be processed in one slot or function 【发布时间】:2017-01-07 22:08:37 【问题描述】:

我现在花了很长时间来弄清楚如何将 QML 信号连接到 python 中的插槽,以及如何再次与 QML GUI 通信。但我不知道如何将多个 GUI 输入发送到 python 中的同一个插槽/函数。

以下是应该发生的事情的基本示例: 用户在 GUI 中操作 SpinBox,它们的总和再次显示在 GUI 中。 我假设这是通过将多个信号路由到一个插槽或通过将每个信号路由到一个单独的插槽并让 python 以某种方式进行组合来完成的。但显然 pyqt 插槽也不允许“返回”。

非常感谢您的帮助!

QML: (注释的行是我认为可能有效的,但它没有。当信号线未注释时,它会导致“预期的令牌‘标识符’”。)

import QtQuick 2.6
import QtQuick.Controls 2.0

ApplicationWindow
    visible:true
    width:200
    height:300
    id:window
    title: "Signal/Slot Test"

    Rectangle

        //signal somethingChanged(int, int)

        //function exportSignals() 
        //    somethingChanged(spin1.value, spin2.value)
        //

        Column 
            spacing: 20
            width: parent.width


            SpinBox 
                id: spin1
                signal sig_spin1(int spin1_int)
                objectName: "spin1"
                value: 50
                width: 120
                editable: true
                Component.onCompleted: 
                    console.log("Spin1: " + spin1.value)
                    sig_spin1(spin1.value)
                    //exportSignals()
                
                onValueChanged: 
                    console.log("Spin1: " + spin1.value)
                    sig_spin1(spin1.value)
                    //exportSignals()
                
            

            SpinBox 
                id: spin2
                signal sig_spin2(int spin2_int)
                objectName: "spin2"
                value: 50
                width: 120
                editable: true
                Component.onCompleted: 
                    console.log("Spin2: " + spin2.value)
                    sig_spin2(spin2.value)
                    //exportSignals()
                
                onValueChanged: 
                    console.log("Spin2: " + spin2.value)
                    sig_spin2(spin2.value)
                    //exportSignals()
                
            

            Label 
                id: label_spin1
                objectName: "label_spin1"
                text: "Start Value"
            

            Label 
                id: label_spin2
                objectName: "label_spin2"
                text: "Start Value"
            

            Label 
                id: label_result
                objectName: "label_result"
                text: "Sum"
            

        
    

和python代码:

import sys
from PyQt5.QtCore import QObject, QUrl, Qt, pyqtSlot
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine


def spin1_update(spin1_int):
    print("Spin 1 updated")
    label_spin1 = win.findChild(QObject, "label_spin1")
    label_spin1.setProperty("text", spin1_int)

def spin2_update(spin2_int):
    print("Spin 2 updated")
    label_spin2 = win.findChild(QObject, "label_spin2")
    label_spin2.setProperty("text", spin2_int)


def spin_sum(spin1_int, spin2_int):
    my_sum = spin1_int + spin2_int
    label_result = win.findChild(QObject, "label_result")
    label_result.setProperty("text", my_sum)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    engine = QQmlApplicationEngine()
    engine.load('main.qml')
    win = engine.rootObjects()[0]

    spin1 = win.findChild(QObject, "spin1")
    spin1.sig_spin1.connect(spin1_update)

    spin2 = win.findChild(QObject, "spin2")
    spin2.sig_spin2.connect(spin2_update)

    #win.exportSignals.connect()


    win.show()
    sys.exit(app.exec_())

更新。为了完整起见,一个工作示例: main.py:

import sys

from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty,     QCoreApplication, QObject, QUrl
from PyQt5.QtQml import QQmlEngine, QQmlApplicationEngine
from PyQt5.QtWidgets import QApplication



class GuiInteraction(QObject):


    def __init__(self, parent=None):
        super().__init__(parent)


    @pyqtSlot(int, int, result=int)
    def get_sum(self, spin1, spin2):
        print("Spin1 from python: ", spin1)
        print("Spin2 from the same python slot: ", spin2)
        my_sum = spin1 + spin2
        print(my_sum)
        print()
        return my_sum




if __name__ == "__main__":
    app = QApplication(sys.argv)
    guiInteraction = GuiInteraction()

    engine = QQmlApplicationEngine()
    ctx = engine.rootContext()
    ctx.setContextProperty("guiInteraction", guiInteraction)
    engine.load('main.qml')
    win = engine.rootObjects()[0]


    win.show()
    sys.exit(app.exec_())

还有main.qml:

import QtQuick 2.6
import QtQuick.Controls 2.0

ApplicationWindow
    visible:true
    width:200
    height:300
    id:window
    title: "Signal/Slot Test"



    Rectangle

        Column 
            spacing: 20
            width: parent.width


            SpinBox 
                id: spin1
                value: 50
                width: 120
                editable: true
                Component.onCompleted: 
                    console.log("Spin1: " + spin1.value)

                
                onValueChanged: 
                    console.log("Spin1: " + spin1.value)

                    label_result.text = guiInteraction.get_sum(spin1.value, spin2.value)
                
            

            SpinBox 
                id: spin2
                value: 50
                width: 120
                editable: true
                Component.onCompleted: 
                    console.log("Spin2: " + spin2.value)

                
                onValueChanged: 
                    console.log("Spin2: " + spin2.value)
                    label_result.text = guiInteraction.get_sum(spin1.value, spin2.value)
                
            


            Label 
                id: label_result
                objectName: "label_result"
                text: "Nothing yet."
            

        
    

【问题讨论】:

为什么要用Python来设置标签的值?与 C++ 一样,您可以从 C++ 设置值,但不建议您这样做,我怀疑在 PyQt 中这样做是一种好风格。 也许我误解了这应该如何工作。我不知道 C++,但知道一些 Python。所以很自然地,我想从用户可以在 qml gui 中设置的值在 Python 中执行一些计算,然后将它们发送回 gui。这完全超出了 PyQt 的范围吗?我认为这是一个非常自然的用例。 我的意思是 Kevin Krammer 在他的回答中所描述的。您应该使用属性绑定来获取 QML 端的值。因此,不要使用 Python 写入 QML 端,而是使用 QML 从 Python 端读取。 【参考方案1】:

首先,您不会在 Python 或 C++ 端执行任何操作,尤其是值标签的更新。

对于值标签,您只需使用属性绑定,例如:

Label 
    text: "Start value: " + spin1.value

对于像求和这样的微不足道的计算,您也可以这样做。

现在,假设计算更复杂,并且您希望在 Python 端执行此操作,那么您将不需要 QML 端的任何信号。

相反,一种选择是在 Python 中使用 QObject 派生类,该类提供一个接受两个参数的槽。

该对象的一个​​实例将通过 QML 引擎的rootContext()setContextProperty() 机制暴露给 QML。 然后,每个 spinbox 的 onValueChanged 处理程序都可以使用这两个值调用此插槽。

对于结果,槽可以返回值,或者对象可以提供由槽更新并在 QML 中用作结果标签上的绑定的结果属性。

【讨论】:

【参考方案2】:

您不能按照您建议的方式将两个信号连接到同一个插槽。信号所做的只是触发槽中的代码运行一次;它不像电子设备中的物理线那样真正的连续连接(我想你可能会想像)。

当一个旋转框值发生变化时,您需要检索另一个旋转框值(从旋转框本身、标签或存储它的另一个变量)来进行添加。例如:

def spin1_update(spin1_int):
    print("Spin 1 updated")
    label_spin1 = win.findChild(QObject, "label_spin1")
    label_spin1.setProperty("text", spin1_int)
    spin_sum()

def spin2_update(spin2_int):
    print("Spin 2 updated")
    label_spin2 = win.findChild(QObject, "label_spin2")
    label_spin2.setProperty("text", spin2_int)
    spin_sum()

def spin_sum():
    spin1 = win.findChild(QObject, "spin1")
    spin2 = win.findChild(QObject, "spin2")
    my_sum = spin1.value() + spin2.value()

    label_result = win.findChild(QObject, "label_result")
    label_result.setProperty("text", my_sum)

在这种情况下,不需要将 spin_sum() 连接到任何东西。它已经被前面的两个槽函数直接调用了。

【讨论】:

全局变量在 spin_sum() 函数中仍然是“0”!并且返回不能使它们在更新槽之外可用。所以这仍然行不通。或者我需要一杯咖啡…… 好的,全局变量实际上必须在更新函数中设置为全局,即:global spin1_global。但出于好奇,如果直接使用旋转框中的值,另一种方式将如何工作。我想这是最初的问题。 我修改了我的答案,但问题是我目前无法实际测试它,所以可能会有一些小错误。

以上是关于PyQt 和 QML:如何在一个插槽或函数中处理多个信号的主要内容,如果未能解决你的问题,请参考以下文章

将 QML 信号连接到 PySide2 插槽

如何正确地将 ComboBox 的模型从 python (pyQt5) 传递给 QML?

Pyqt将插槽连接到具有默认参数的函数而没有lambda [重复]

PyQt:计算太长时发出两次信号

如何使用 QML 和 PyQt5 创建一个没有标题栏的小通知窗口

PYQT5 - 插槽函数不采用默认参数 [重复]