Language & Toolkit/QT

C++ 과 Qt/QML을 이용한 개발 - 004: (Qt5 기본) Signal and Slot

마니토73 2021. 5. 24. 11:11

Signal 은 우리말로 신호를 의미하고 Slot은 무언가를 넣을 수 있는 자리나 틈 같은 것을 의미한다. 즉 신호가 발생하면 slot에 있는 무언가를 시작한다. 디자인 패턴을 이해한다면 "생산자 소비자 패턴"을 수행한다고 생각할수도 있겠다.

보통 하나의 클래스에서 시그널과 슬롯을 구현하면서 설명을 하겠지만 신호가 발생되는 곳과 그것에 대한 처리가 발생하는 곳이 항상 같지는 않으므로 별개의 두개의 클래스를 가지고 설명해보겠다.

Signal이 발생하는 곳을 SignalGenerator라고 하고, Slot을 처리하는 곳을 SlotProcessor라고 하자. 주의할 점은 Signal and Slot 메커니즘이 작동하기 위해서는 반드시 QObject를 상속받아야 한다. 그 외에 매크로도 추가해야 하지만 Qt Creator는 간편한 위저드 기능을 제공한다.

아마 특별한 설정을 하지 않았다면 파일명이 소문자로 표시될 것이다. Qt Creator의 기본값이 소문자여서 그렇다. 아래와 같이 옵션으로 변경할 수 있다.

 

 

프로젝트명에서 마우스 우클릭을 하여 Add New 를 실행하면 위와 같은 위저드 화면이 나온다. 여기서 Base class를  QObject를 선택하면 자동으로 필요한 내용이 추가로 체크된다. 

 

생성된 파일을 헤더를 보면 필요한 상속과 매크로까지 미리 작성되어 있음을 확인할 수 있다. 이제 Sinal을 발생시키는 부분을 작성해 보자.

 

위 소스를 보면 emit란 용어는 처음 볼겠지만 대략 GeneratorTextSignal 함수가 호출되면 내부적으로 textSignal 을 호출함을 알 수 있을 것이다. 사실 위에서 보이는 signals 와 emit은 표준 c++에는 있지 않은 용어다. 즉 qt가 정의한 키워드이며 qt는 c++ 소스 코드에서 해당 키워드를 이용하여 표준 c++에 맞도록 코드를 추가하고 컴파일을 진행한다.

 

SlotProcessor의 함수를 보면 두 클래스의 의도가 SignalGenerator에서 GeneratorTextSignal을 호출하면 Signal and slot 메커니즘에 따라 SlotProcessor의 onSlotMessage가 호출되기 위한 코드이다. 두 클래스의 인스탄스를 main에서 정의해보자.

자 위 코드를 추가한 후 성급하게 빌드하고 실행해본 사람이 없길 바란다. 변수 sg 에서 GenerateTextSignal을 통해 Signal을 발생시켰지만 sp는 그 발생 사실을 알 수 없어 slot함수를 실행할 수 없게 된다. Qt는 signal / slot 키워드를 통해 추가적인 코드를 만든는 것이지 알아서 두 클래스를 연결해 주는 것은 아니다. 마법은 없다.

 

Connect 함수를 통해 두 인스탄스를 Signal / Slot을 연결해 주고 나서 실행하면 아래와 같은 텍스트가 출력됨을 Application Out 창을 통해 확인할 수 있다. connect 함수에 F1 을 클릭해서 (더이상 F1을 이야기 하지 않겠다) 설명을 보면 여러가지 버전으로 재정의 되어 있다. 처음에 주석 처리한 방식도 정상적으로 작동된다. 나는 그중에 멤버 함수의 포인터를 이용하여 연결하는 방식을 사용하였다.

자 이제 좀더 자세히 이게 뭐하는 것인지 살펴보자.  이처럼 기능이 작동되는 것을 보면 C++ 개발자라면 함수 포인터를 이용한 Callback 함수랑 뭐가 다른거지 라는 생각이 들겠지만 마지막 파라미터인 ConnectionType을 보면 보다 진보된 형태의 함수 포인터를 관리해준다고 느낄 수 있다.

즉 콜백함수와 같이 즉시 실행되는 것만이 아니라 상황에 따라 유연하게 queue에 넣어 slot 함수의 작동을 제어할 수 있다. 또한 connect함수에 대응되는 disconnect함수도 제공하여 연결을 끊을 수도 있다. 

Signal / Slot 은 개념이해는 큰 어려움이 없을 것이다. 오히려 문법이 생소해 잘 외워지지 않는데 Signal에서 선언한 함수의 형태와 Slot 에서 구현된 함수의 형태가 같아야만 하는 것을 기억하면 된다.