web-dev-qa-db-ja.com

emit()およびpyqtSignal()のPyQtの適切な使用

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_())
_

スライダーに加えられた変更を追跡するために、行われた変更を印刷して記録するだけです。私がコードについて気に入らないのは、同じ情報を3つの異なるスロットに送信するために_sld.valueChanged_スロットを3回呼び出す必要があるということです。

整数を単一のスロット関数に送信する独自​​のpyqtSignalを作成することは可能ですか?そして、スロット関数は、必要な変更を発行しますか?

  • PyQt Signal-Slot docs には目的の良い例がないため、emit()の目的を完全に理解していないのかもしれません。ここで与えられているのは、パラメータなしで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を正しく実装していません。ただし、次の3つの点を正しく説明すると、適切なアプリを作成できると思います。

  1. 定義済みの信号の場合:信号をスロット関数に送信します。スロットは、信号値を使用するように再実装できます。
  2. いくつかのパラメーターでpyqtSignalオブジェクトを生成します。これらのパラメーターの目的が何であるか、および「送信」パラメーターとどのように異なるかはまだ明確ではありません。
  3. emitを再実装して、特定の信号値をスロット関数に送信できます。また、既存の信号方式とは異なる値を送信する必要がある理由もまだ明確ではありません。

私がやろうとしていることのためにコードを完全に変更してください。良いスタイルの領域にあるかどうかはまだわかりません。

19
Max

独自のスロット(任意のpython callable)を定義し、それをシグナルに接続してから、その1つのスロットから他のスロットを呼び出すことができます。

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 'dされるオブジェクトのタイプを定義するため、この場合、次のようにできます。

self.my_signal.emit(1)

emitは、特定のシグナル値をスロット関数に送信するために再実装できます。また、既存の信号方式とは異なる値を送信する必要がある理由もまだ明確ではありません。

通常、組み込みの信号を送信するべきではありません。定義する信号のみを送信する必要があります。信号を定義するとき、異なるタイプの異なる署名を定義でき、スロットは接続する署名を選択できます。たとえば、これを行うことができます

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

これにより、2つの異なるシグネチャを持つ信号が定義され、スロットはどちらかに接続できます

@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)

実際には、信号シグネチャをオーバーロードすることはめったにありません。通常、同じ信号をオーバーロードするのではなく、異なるシグネチャを持つ2つの別々の信号を作成します。しかし、Qtにはこの方法でオーバーロードされるシグナルがあるため、PyQtでサポートされています(例: QComboBox.currentIndexChanged

26
Brendan Abel