한국어 English

PySide에서의 시그널과 슬롯

이 페이지에서는 PySide상에서 시그널과 슬롯을 사용하는 방법에 대해 설명합니다. 우리는 최근에 도입한 새로운 방식의 시그널 슬롯을 사용하기를 적극 추천합니다. 기존 방식 역시 사용 가능합니다만 단지 참고용으로만 남겨두고 있습니다.

PyQt는 4.5 버전에서 새로운 방식의 시그널 슬롯을 도입하였습니다. 이 방식은 시그널 슬롯을 보다 파이썬 문법답게 사용할 수 있기 위해 만들어졌습니다. PySide은 PSEP 100 [pyside.org] 이라는 가이드라인에 기반하여 개발되었습니다.

기존의 방식: SIGNALSLOT

QtCore.SIGNALQtCore.SLOT 매크로를 이용하면 파이썬에서도 기존 방식의 Qt 시그널 슬롯 매커니즘을 이용할 수 있습니다.

아래의 예제에서는 QPushButton 의 clicked 시그널을 사용하고 있습니다. connect 메서드는 개체, 개체의 시그널, 연결할 슬롯을 인자로 하여 호출됩니다. 이러한 방식은 전혀 파이썬 언어와는 상당한 이질감이 있습니다.

  1. ...
  2.  
  3. def someFunc():
  4.     print "someFunc has been called!"
  5.  
  6. ...
  7.  
  8. button = QtGui.QPushButton("Call someFunc")
  9. QtCore.QObject.connect(button, QtCore.SIGNAL('clicked()'), someFunc)
  10.  
  11. ...

새로운 방식의 문법: Signal() 과 Slot()

새로운 방식의 시그널 슬롯 연결방식은 기존과는 다른 문법을 사용합니다. 위의 예제와 동일한 기능을 다음과 같이 구현할 수 있습니다. :

  1. ...
  2.  
  3. def someFunc():
  4.     print "someFunc has been called!"
  5.  
  6. button = QtGui.QPushButton("Call someFunc")
  7. button.clicked.connect(someFunc)
  8.  
  9. ...

QtCore.Signal() 클래스 사용법

시그널은 QtCore.Signal() 클래스를 이용하여 정의할 수 있습니다. 인자의 형식은 파이썬 형식과 C 형식 둘 다 지원합니다. 다만, 시그널을 오버로드할 경우에는 튜플이나 리스트 형식으로 인자를 전달해야 합니다.

또한, 이 클래스는 name 이라는 인자명을 사용하여 시그널의 이름을 전달할 수도 있습니다. 만약 이름이 전달되지 않을 경우에는 시그널에 지정된 변수의 이름이 바로 시그널의 이름이 됩니다.

아래의 예제는 QtCore.Signal() 의 사용방법을 여러 가지로 소개하고 있습니다.

주의할 점: 시그널은 반드시 QObject 를 상속한 클래스 안에서 정의해야 합니다. QMetaObject 클래스에서 시그널 정보를 관리하기 때문입니다.

QtCore.Slot() 사용법

슬롯은 QtCore.Slot() 라는 장식자(decorator)를 사용하여 지정할 수 있으며, 슬롯의 오버로드도 가능합니다. 즉, 시그널 클래스와 마찬가지로 타입 정보를 전달함으로써 슬롯을 선언할 수 있습니다. 하지만 Signal() 클래스와는 달리 튜플이나 리스트를 이용하여 다양한 인자 형식을 한 번에 선언할 수는 없으며, 대신 인자형식 하나 당 장식자를 하나씩 정의하면서 시그널을 선언해 주어야 합니다. 아래의 예제 모음에서 보다 명확히 설명해 줄 것입니다.

키워드에도 차이점이 있습니다. Slot() 은 name과 result를 인자로 받습니다. result는 리턴값의 타입을 정의하는 키워드이며 파이썬, C 타입 양쪽 다 사용할 수 잇습니다. name 의 역할은 Signal() 에서의 역할과 동일합니다. name 이 전달되지 않을 경우, 장식자에서 선언된 함수의 이름이 바로 슬롯의 이름이 됩니다.

예제 모음

아래의 예제들은 PySide 상에서 시그널, 슬롯을 정의하는 방법과 연결하는 방법을 설명해 주고 있습니다. 기본적인 연결방법은 물론 보다 복잡한 예제도 다루고 있습니다.

  • Hello World 예제: 가장 기본적인 예제입니다. 파라미터 없이 시그널, 스롯을 연결하는 법이 나와 있습니다.

  1. #!/usr/bin/env python
  2.  
  3. import sys
  4. from PySide import QtCore, QtGui
  5.  
  6. # define a function that will be used as a slot
  7. def sayHello():
  8.     print 'Hello world!'
  9.  
  10. app = QtGui.QApplication(sys.argv)
  11.  
  12. button = QtGui.QPushButton('Say hello!')
  13.  
  14. # connect the clicked signal to the sayHello slot
  15. button.clicked.connect(sayHello)
  16. button.show()
  17.  
  18. sys.exit(app.exec_())

  • 아래의 예제는 위의 Hello World 예제를 수정하여 시그널 슬롯으로 파라미터가 전달하도록 하였습니다.

  1. #!/usr/bin/env python
  2.  
  3. import sys
  4. from PySide import QtCore
  5.  
  6. # define a new slot that receives a string and has
  7. # 'saySomeWords' as its name
  8. @QtCore.Slot(str)
  9. def saySomeWords(words):
  10.     print words
  11.  
  12. class Communicate(QtCore.QObject):
  13.     # create a new signal on the fly and name it 'speak'
  14.     speak = QtCore.Signal(str)
  15.  
  16. someone = Communicate()
  17. # connect signal and slot
  18. someone.speak.connect(saySomeWords)
  19. # emit 'speak' signal
  20. someone.speak.emit("Hello everybody!")

  • 위 예제를 바탕으로 시그널 슬롯을 오버로드 합니다.

  1. #!/usr/bin/env python
  2.  
  3. import sys
  4. from PySide import QtCore
  5.  
  6. # define a new slot that receives a C 'int' or a 'str'
  7. # and has 'saySomething' as its name
  8. @QtCore.Slot(int)
  9. @QtCore.Slot(str)
  10. def saySomething(stuff):
  11.     print stuff
  12.  
  13. class Communicate(QtCore.QObject):
  14.     # create two new signals on the fly: one will handle
  15.     # int type, the other will handle strings
  16.     speakNumber = QtCore.Signal(int)
  17.     speakWord = QtCore.Signal(str)
  18.  
  19. someone = Communicate()
  20. # connect signal and slot properly
  21. someone.speakNumber.connect(saySomething)
  22. someone.speakWord.connect(saySomething)
  23. # emit each 'speak' signal
  24. someone.speakNumber.emit(10)
  25. someone.speakWord.emit("Hello everybody!")

  • 아래의 예제는 시그널의 연결과 호출을 위와는 다른 방식으로 행합니다.

  1. #!/usr/bin/env python
  2.  
  3. import sys
  4. from PySide import QtCore
  5.  
  6. # define a new slot that receives an C 'int' or a 'str'
  7. # and has 'saySomething' as its name
  8. @QtCore.Slot(int)
  9. @QtCore.Slot(str)
  10. def saySomething(stuff):
  11.     print stuff
  12.  
  13. class Communicate(QtCore.QObject):
  14.     # create two new signals on the fly: one will handle
  15.     # int type, the other will handle strings
  16.     speak = QtCore.Signal((int,), (str,))
  17.  
  18. someone = Communicate()
  19. # connect signal and slot. As 'int' is the default
  20. # we have to specify the str when connecting the
  21. # second signal
  22. someone.speak.connect(saySomething)
  23. someone.speak[str].connect(saySomething)
  24.  
  25. # emit 'speak' signal with different arguments.
  26. # we have to specify the str as int is the default
  27. someone.speak.emit(10)
  28. someone.speak[str].emit("Hello everybody!")

PyQt 와의 호환성

PyQt 는 시그널 슬롯의 이름을 정의하는 방식이 PySide와는 다릅니다. PyQt로부터 소스를 컨버팅 하려면 아래와 같은 방법으로 사용하기를 추천합니다.

  1. from PySide.QtCore import Signal as pyqtSignal
  2. from PySide.QtCore import Slot as pyqtSlot

또는

  1. QtCore.pyqtSignal = QtCore.Signal
  2. QtCore.pyqtSlot = QtCore.Slot

위 코드를 통해 PyQt용 pyqtSignalpyqtSlot 이 PySide에서는 SignalSlot 로 변환됩니다.

Categories: