PyQt正确使用emit()和pyqtSignal()

问题描述 投票:0回答:3

我正在阅读 PyQt5 的一些文档,以提出一个简单的信号槽机制。由于设计考虑,我已经停止了。

考虑以下代码:

import sys
from PyQt5.QtCore import (Qt, pyqtSignal)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
    QVBoxLayout, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def printLabel(self, str):
        print(str)

    def logLabel(self, str):
        '''log to a file'''
        pass

    def initUI(self):

        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal, self)

        vbox = QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(sld)

        self.setLayout(vbox)

        #redundant connections
        sld.valueChanged.connect(lcd.display)
        sld.valueChanged.connect(self.printLabel)
        sld.valueChanged.connect(self.logLabel)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal & slot')
        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

为了跟踪对滑块所做的更改,我只需打印并记录所做的更改。我不喜欢该代码的是,我需要调用

sld.valueChanged
插槽三次才能将相同的信息发送到 3 个不同的插槽。

是否可以创建我自己的

pyqtSignal
,将整数发送到单个槽函数。反过来,槽函数是否会发出需要进行的更改?

  • 也许我不完全理解
    emit()
    的目的,因为在PyQt Signal-Slot文档中没有很好的例子来说明它的目的。我们给出的只是一个如何实现不带参数的
    emit
    的示例。

我想做的是创建一个处理emit函数的函数。考虑以下几点:

import sys
from PyQt5.QtCore import (Qt, pyqtSignal)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
    QVBoxLayout, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        #create signal
        self.val_Changed = pyqtSignal(int, name='valChanged')

        self.initUI()

    def initUI(self):

        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal, self)

        vbox = QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(sld)

        self.setLayout(vbox)

        sld.val_Changed.connect(self.handle_LCD)
        self.val_Changed.emit()

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal & slot')
        self.show()

    def handle_LCD(self, text):
        '''log'''
        print(text)
        '''connect val_Changed to lcd.display'''

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

这里显然存在一些严重的设计缺陷。我无法理解函数调用的顺序。而且我没有正确实施

pyqtSignal
。然而,我确实相信正确陈述以下三点将帮助我制作一个合适的应用程序:

  1. 对于预定义信号:将信号发送到槽函数。可以重新实现插槽以使用信号值。
  2. 生成带有一些参数的
    pyqtSignal
    对象。目前尚不清楚这些参数的目的是什么以及它们与“发出”参数有何不同。
  3. 可以重新实现
  4. emit
    以将特定信号值发送到槽函数。目前还不清楚为什么我需要发送与以前存在的信号方法不同的值。

请随意完全更改我想要做的代码,因为我还没有弄清楚它是否属于良好风格的范围。

python pyqt signals signals-slots pyqt5
3个回答
63
投票

您可以定义自己的插槽(任何可调用的 python)并将其连接到信号,然后从该插槽调用其他插槽。

class Example(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def printLabel(self, str):
        print(str)

    def logLabel(self, str):
        '''log to a file'''
        pass

    @QtCore.pyqtSlot(int)
    def on_sld_valueChanged(self, value):
        self.lcd.display(value)
        self.printLabel(value)
        self.logLabel(value)

    def initUI(self):

        self.lcd = QLCDNumber(self)
        self.sld = QSlider(Qt.Horizontal, self)

        vbox = QVBoxLayout()
        vbox.addWidget(self.lcd)
        vbox.addWidget(self.sld)

        self.setLayout(vbox)
        self.sld.valueChanged.connect(self.on_sld_valueChanged)


        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal & slot')

此外,如果您想定义自己的信号,则必须将它们定义为类变量

class Example(QWidget):
    my_signal = pyqtSignal(int)

pyqtSignal
的参数定义了将在该信号上
emit
的对象类型,所以在这种情况下,你可以这样做

self.my_signal.emit(1)

emit 可以重新实现以将特定信号值发送到槽 功能。目前还不清楚为什么我需要发送不同的 来自先前现有信号方法的值。

您通常不应该发出内置信号。您应该只需要发出您定义的信号。定义信号时,可以定义不同类型的不同签名,插槽可以选择要连接的签名。例如,你可以这样做

my_signal = pyqtSignal([int], [str])

这将定义一个具有两个不同签名的信号,并且插槽可以连接到其中一个

@pyqtSlot(int)
def on_my_signal_int(self, value):
    assert isinstance(value, int)

@pyqtSlot(str)
def on_my_signal_str(self, value):
    assert isinstance(value, str)

在实践中,我很少过载信号签名。我通常只会创建两个具有不同签名的单独信号,而不是重载相同的信号。但它存在并在 PyQt 中得到支持,因为 Qt 具有以这种方式重载的信号(例如。

QComboBox.currentIndexChanged


0
投票

接受的答案对我来说很难理解,因为使用了内置信号的 QSlider

valueChanged()
我不知道https://doc.qt.io/qtforpython/PySide6/QtWidgets/QSlider.html ,所以我添加了调用
self.sld.valueChanged.connect(self.on_sld_valueChanged)
时发出的自己的信号,以提供如何创建自己的 pyqtSignal 的示例。帖子标题:“PyQt正确使用emit()和pyqtSignal()”误导了我,我也试图理解信号和槽,所以我想在代码中添加
emit()
。我知道这不是前进的道路,而只是弄清楚它是如何运作的:

import sys

from PyQt5 import QtCore

from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
    QVBoxLayout, QApplication)

class Example(QWidget):

    #create signal
    val_Changed = pyqtSignal(int, str, name='valChanged')
    
    def __init__(self):
        super().__init__()
        self.initUI()
        
        
        self.valChanged.connect(self.mine)
        
        self.show()

    def printLabel(self, str):
        print(str)

    def logLabel(self, str):
        '''log to a file'''
        pass


    @QtCore.pyqtSlot(int, str)
    def mine(self, value, string):
        self.lcd.display(value)
        self.printLabel((str(value)+' '+ string))
        self.logLabel(value)
        
        
    def on_sld_valueChanged(self, value):
        
        self.val_Changed.emit(value, 'using-slider')


    def initUI(self):

        self.lcd = QLCDNumber(self)
        self.sld = QSlider(Qt.Horizontal, self)

        vbox = QVBoxLayout()
        vbox.addWidget(self.lcd)
        vbox.addWidget(self.sld)

        self.setLayout(vbox)
        self.sld.valueChanged.connect(self.on_sld_valueChanged)


        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal & slot')
        
if __name__ ==  '__main__' :

    app = QApplication( sys.argv ) 
    ex = Example() 
    
    sys.exit(app.exec_( ))

我会等待有人可以向我展示如何重新实现 QSlider

valueChanged()
信号


0
投票

示例应该很简单:

from PyQt6.QtCore import QObject,pyqtSignal,Qt,pyqtSlot
class Test(QObject):
    signal=pyqtSignal(int)

    @pyqtSlot(int) # no need, but will increase speed and reduce memory use
    def slot2(self,i):
        print('slot2',i)

@pyqtSlot(int)
def slot1(i):
    print('slot1',i)

test=Test()
test.signal.connect(slot1)
test.signal.emit(1)
test.signal.connect(test.slot2,type=Qt.ConnectionType.AutoConnection)
test.signal.emit(2)
test.signal.disconnect(slot1)
test.signal.disconnect(test.slot2)
test.signal.emit(3)

结果:

slot1 1
slot1 2
slot2 2

https://doc.qt.io/qtforpython-6/PySide6/QtCore/Qt.html#PySide6.QtCore.Qt.ConnectionType

© www.soinside.com 2019 - 2024. All rights reserved.