Qt 신호 슬롯 목록

Qt 신호 슬롯 목록

신호와 슬롯은 객체 간의 통신에 사용됩니다. 신호 및 슬롯 메커니즘은 Qt의 핵심 기능이며 아마도 다른 프레임워크에서 제공하는 기능과 가장 다를 것입니다. 신호와 슬롯은 Qt의 메타 객체 시스템으로 가능합니다.

GUI 프로그래밍에서 우리는 하나의 위젯을 변경할 때 일반적으로 다른 위젯에 알리기를 원합니다. 보다 일반적으로 우리는 모든 유형의 개체가 서로 통신할 수 있기를 원합니다. 예를 들어 사용자가 닫기 버튼을 클릭하면 창의 Close() 함수를 호출할 수 있습니다.

다른 툴킷은 콜백을 사용하여 이 통신을 수행합니다. 콜백 함수는 함수에 대한 포인터이므로 핸들러에서 이벤트를 알리려면 다른 함수(콜백 함수)에 대한 포인터를 핸들러에 전달해야 합니다. 그런 다음 핸들러 함수는 적절한 경우 콜백을 호출합니다. 이 접근 방식을 사용하는 성공적인 프레임워크가 존재하지만 콜백은 직관적이지 않을 수 있으며 콜백 매개변수의 유형 정확성을 보장하는 데 문제가 있을 수 있습니다.

Qt에는 콜백 기술에 대한 대안이 있습니다. 신호와 슬롯을 사용합니다. 특정 이벤트가 발생하면 신호를 보냅니다. Qt의 위젯에는 미리 정의된 많은 신호가 있지만 우리는 항상 위젯을 하위 클래스로 분류하여 자체 신호를 추가할 수 있습니다. 슬롯은 특정 신호에 대한 응답으로 호출되는 함수입니다. Qt의 위젯에는 많은 사전 정의된 슬롯이 있지만 관심 있는 신호를 처리하기 위해 위젯을 하위 클래스로 분류하고 고유한 슬롯을 추가하는 것이 일반적입니다.

여기에 이미지 설명 삽입

신호 및 슬롯 메커니즘은 유형이 안전합니다. 신호의 서명은 수신 슬롯의 서명과 일치해야 합니다. (실제로 슬롯의 서명은 추가 인수를 무시할 수 있기 때문에 수신하는 신호보다 짧을 수 있습니다.) 서명이 호환되므로 컴파일러는 함수 포인터 기반 구문을 사용할 때 유형 불일치를 감지하는 데 도움을 줄 수 있습니다. 문자열 기반 SIGNAL 및 SLOT 구문은 런타임 시 유형 불일치를 감지합니다. 신호와 슬롯은 느슨하게 연결되어 있습니다. 신호를 내보내는 클래스는 어느 슬롯이 신호를 받는지 알지도 못하고 신경도 쓰지 않습니다. **Qt의 신호 및 슬롯 메커니즘은 신호를 슬롯에 연결하면 신호의 인수와 함께 올바른 시간에 슬롯이 호출되도록 합니다. ** 신호 및 슬롯은 모든 유형의 인수를 얼마든지 허용할 ​​수 있습니다. 완전히 형식이 안전합니다.

QObject 또는 그 하위 클래스(예: QWidget)에서 상속하는 모든 클래스는 신호 및 슬롯을 포함할 수 있습니다. 신호는 객체가 다른 객체가 관심을 가질 만한 방식으로 상태를 변경할 때 방출됩니다. 이것이 모든 개체가 통신하기 위해 수행하는 모든 작업입니다. 자신이 보내는 신호를 수신하는 것이 있는지 여부를 모르고 상관하지 않습니다. 이것은 진정한 정보 캡슐화이며 개체가 구성 요소 기반 소프트웨어 엔지니어링에 사용될 수 있도록 합니다.

슬롯은 신호를 받는 데 사용할 수 있지만 일반 멤버 함수이기도 합니다. 물체가 자신의 신호를 수신하는지 여부를 알 수 없는 것처럼 슬롯은 신호가 연결되어 있는지 여부를 알 수 없습니다. 이렇게 하면 Qt를 사용하여 진정으로 독립적인 구성 요소를 만들 수 있습니다.

하나의 슬롯에 원하는 만큼의 신호를 연결할 수 있고, 슬롯에 원하는 만큼의 신호를 연결할 수 있습니다. 하나의 신호를 다른 신호에 직접 연결하는 것도 가능합니다. (첫 번째 신호가 방출되는 즉시 두 번째 신호가 방출됩니다.)

신호와 슬롯은 강력한 컴포넌트 프로그래밍 메커니즘을 형성합니다.

신호

개체의 클라이언트나 소유자가 관심을 가질 수 있는 방식으로 개체의 내부 상태가 변경되면 신호가 방출됩니다. 신호는 공개 액세스 기능이며 어디에서나 내보낼 수 있지만 신호를 정의하는 클래스와 해당 하위 클래스에서만 신호를 내보낼 것을 권장합니다.

신호가 방출되면 신호에 연결된 슬롯은 일반적으로 일반 함수 호출과 마찬가지로 즉시 실행됩니다. 이 경우 신호 및 슬롯 메커니즘은 GUI 이벤트 루프와 완전히 독립적입니다. 모든 슬롯이 반환되면 emit 문 다음의 코드가 실행됩니다. 대기 중인 연결을 사용할 때는 상황이 약간 다릅니다. 이 경우 emit 키워드 다음의 코드는 즉시 계속되고 슬롯은 나중에 실행됩니다.

여러 슬롯이 신호에 연결된 경우 해당 슬롯은 신호가 방출될 때 연결된 순서대로 차례로 실행됩니다.

신호는 moc에 의해 자동으로 생성되며 에 있을 수 없습니다. CPP 파일. 반환 유형을 가질 수 없습니다(즉, void 사용).

매개변수에 대한 참고 사항: 우리의 경험에 따르면 신호와 슬롯은 특수 유형을 사용하지 않는 경우 더 재사용할 수 있습니다. QScrollBar::valueChanged()가 가상의 QScrollBar::Range와 같은 특수 유형을 사용하는 경우 QScrollBar용으로 특별히 설계된 슬롯에만 연결할 수 있습니다. 서로 다른 입력 위젯을 함께 연결할 수 없습니다.

슬롯

슬롯에 연결된 시그널이 방출되면 슬롯이 호출됩니다. 슬롯은 일반적인 C++ 함수이며 일반적으로 호출할 수 있으며 유일한 속성은 신호를 슬롯에 연결할 수 있다는 것입니다.

슬롯은 일반 멤버 함수이기 때문에 직접 호출될 때 일반 C++ 규칙을 따릅니다. 그러나 슬롯으로서 신호-슬롯 연결을 통해 액세스 수준에 관계없이 모든 구성 요소에서 호출할 수 있습니다. 이는 임의 클래스의 인스턴스에서 방출된 신호로 인해 관련 없는 클래스의 인스턴스에서 개인 슬롯이 호출될 수 있음을 의미합니다.

또한 슬롯을 가상으로 정의할 수 있으며 실제로 매우 유용한 것으로 나타났습니다.

시그널과 슬롯은 더 많은 유연성을 제공하기 때문에 콜백보다 약간 느리지만 실제 애플리케이션의 경우에는 이 점에서 차이가 그리 크지 않습니다. 일반적으로 어떤 슬롯에 연결된 신호를 방출하는 것은 가상이 아닌 함수 호출을 수신기에 직접 호출하는 것보다 약 10배 느립니다. 이것은 연결 개체를 찾고, 모든 연결을 안전하게 반복하고(즉, 전송 중에 후속 수신자가 파괴되는지 확인) 일반적인 방식으로 모든 매개 변수를 마샬링하는 데 필요한 오버헤드입니다. 10개의 비가상 함수 호출이 많은 것처럼 들릴 수 있지만 새 작업 또는 삭제 작업보다 오버헤드가 훨씬 적습니다. 문자열, 벡터 또는 목록 작업을 수행한 후에는 배경을 새로 만들거나 삭제해야 하며 신호 및 슬롯 오버헤드는 전체 함수 호출 비용의 작은 부분에 불과합니다. 슬롯에서 syscall을 수행하든 간접적으로 10개 이상의 함수를 호출하든 마찬가지입니다. 신호 및 슬롯 메커니즘의 단순성과 유연성은 오버헤드를 충분히 감수할 가치가 있으며 사용자는 이를 알아채지도 못할 것입니다.

신호 또는 슬롯이라는 변수를 정의하는 다른 라이브러리는 Qt 기반 응용 프로그램으로 컴파일할 때 컴파일러 경고 및 오류를 유발할 수 있습니다. 이 문제를 해결하려면 # undef 전처리기 기호를 사용하십시오.

Qt에서 연결 사용

이전 버전의 쓰기 방법은 더 복잡하며 형식 매개변수를 포함하여 신호 및 슬롯을 명확하게 지정해야 합니다.

class MyButton : public QWidget
{
    Q_OBJECT
public:
    explicit MyButton(QWidget *parent = nullptr);

signals:
    void sigClicked();
    void sigClicked(bool check);
};

구버전이 한눈에 쏙쏙 들어오는데 더 번거롭다.

connect(m_pBtn,SIGNAL(sigClicked()),this,SLOT(onClicked()));
connect(m_pBtn,SIGNAL(sigClicked(bool)),this,SLOT(onClicked(bool)));

Qt5.0 이후에 작성하는 방법

connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked);

신호가 과부하되면 오류가 보고됩니다.

오류: 'connect' connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked) 호출에 일치하는 멤버 함수가 없습니다.

시그널 함수의 특정 버전을 지정하려면 다음 작성 방법을 사용하십시오. 슬롯 함수가 과부하인 경우 이전 방법을 사용할 수도 있습니다.

connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, &Widget::onClicked);

신호 함수 과부하 최적화

그러나 여전히 오버로드된 슬롯 기능을 연결할 수 없습니다.

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),this,&Widget::onClicked);

Lambda 함수 작성 방법

슬롯 함수의 내용이 단순하다면 슬롯 링크를 따로 정의할 필요 없이 람다 함수를 직접 사용하는 것이 편리하다.

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),

               [=](bool check){

                /* do something.. */

                });

connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, [=](bool check){

                 //do something

                 });

람다 함수

[capture](parameters) mutable ->return-type{statement}

1.[캡처]: 목록을 캡처합니다. 캡처 목록은 항상 Lambda 함수의 시작 부분에 나타납니다. 실제로 []는 람다 인용문입니다. 컴파일러는 elicitor를 기반으로 다음 코드가 Lambda 함수인지 판단합니다. 캡처 목록은 Lambda 함수에서 사용할 컨텍스트의 변수를 캡처할 수 있습니다.

2. (매개변수): 매개변수 목록. 일반 함수의 매개변수 목록과 일치합니다. 매개변수 전달이 필요하지 않은 경우 괄호 "()"와 함께 생략할 수 있습니다.

3. 변경 가능: 변경 가능 수정자. 기본적으로 Lambda 함수는 항상 const 함수이며 mutable은 해당 constness를 취소할 수 있습니다. 이 수정자를 사용할 때 매개변수 목록을 생략할 수 없습니다(매개변수가 비어 있더라도).

4.->반환 유형: 반환 유형. 추적 반환 형식 형식으로 함수의 반환 형식을 선언합니다. 반환 값이 필요하지 않은 경우 "->" 기호와 함께 생략할 수도 있습니다. 또한 반환 유형이 명확한 경우 컴파일러가 반환 유형을 추론할 수 있도록 이 부분도 생략할 수 있습니다.

5. {문장}: 함수 본문. 내용은 일반 함수와 동일하지만 파라미터 외에 캡처된 모든 변수도 사용할 수 있습니다.

일반 함수와 가장 큰 차이점은 매개 변수 외에도 Lambda 함수는 캡처 목록을 통해 일부 컨텍스트에서 데이터에 액세스할 수 있다는 것입니다. 특히 캡처 목록은 Lambda에서 사용할 수 있는 컨텍스트의 데이터와 이를 사용하는 방법(값 기준 또는 참조 기준)을 설명합니다. 구문적으로 "[]"에 포함되는 것은 캡처 목록이며 캡처 목록은 쉼표로 구분된 여러 캡처 항목으로 구성됩니다. 캡처 목록은 다음 형식으로 제공됩니다.

1. [var]는 값 전송 방법이 변수 var를 캡처함을 나타냅니다.
2. [=]는 값 전송 방법이 상위 범위(이를 포함하여)의 모든 변수를 캡처함을 나타냅니다.
3. [&var]는 참조 전송 방법이 변수 var를 캡처함을 나타냅니다. 변수 var;
4. [& ]는 참조 전송 방법이 상위 범위(this 포함)의 모든 변수를 캡처함을 의미합니다.
5. [this]는 값 전송 방법이 현재 this 포인터를 캡처함을 의미합니다.

위에서 언급한 상위 범위, 즉 Lambda 함수를 포함하는 문 블록, 쉽게 말해서 Lambda를 포함하는 "{}" 코드 블록입니다. 위의 캡처 목록은 다음과 같이 결합할 수도 있습니다.


1.[=,&a,&b]는 변수 a와 b를 참조로 캡처하고 다른 모든 변수는 값으로 캡처하는 것을 의미합니다. -reference 메서드는 다른 모든 변수를 캡처합니다.

그러나 캡처 목록은 변수가 반복적으로 전달되는 것을 허용하지 않는다는 점은 주목할 가치가 있습니다. 다음 예제는 컴파일 시간 오류로 이어지는 일반적인 중복입니다. 예를 들어:

3. [=, a] 여기서는 모든 변수를 값 전달로 캡쳐했지만 a가 반복적으로 캡쳐되면 오류가 발생함
4. [&,&this] 여기 &에서 모든 변수를 참조 전달로 캡쳐한 후 이것을 캡쳐 역시 반복입니다.

람다 함수 예시

[캡처] 설명

이전 섹션의 프로젝트를 기반으로 mybutton_1 버튼을 추가하고 버튼을 누른 다음 버튼 텍스트를 변경합니다. 위 람다 식의 기본 구성 코드에 따르면 다음과 같이 쓸 수 있습니다(잘못된 표기법).

QPushButton *mybutton_1=new QPushButton(this);
mybutton_1->setText("关注<程序媛讲QT>");
mybutton_1->move(150,100);   //移动按键
connect(mybutton_1,&QPushButton::pressed,
        []()
        {
             mybutton_1->setText("Lambda表达式");
         }
);

이때 컴파일 시 "error: 'mybutton_1' is not capture"라는 오류 메시지가 표시되는데, 이는 mybutton_1이 내 범위에 없다는 의미입니다. 이때 [capture]를 사용하여 외부 변수를 전달해야 합니다. 이 시점에서 mybutton_1만 전달하면 코드가 정상적으로 컴파일되고 실행될 수 있습니다. connect() 함수 코드는 다음과 같이 변경됩니다.

connect(mybutton_1,&QPushButton::pressed,
        [mybutton_1]()
        {
              mybutton_1->setText("Lambda表达式");
        }
);

이 시점에서 코드를 컴파일하고 정상적으로 실행할 수 있습니다. 그러면 외부에 변수가 여러 개 있을 때 일일이 []에 쓰기가 번거로우니 이때 "="를 사용하면 됩니다. 예를 들어:

QPushButton *mybutton_1=new QPushButton(this);
mybutton_1->setText("关注<程序媛讲QT>");
mybutton_1->move(150,100);   //移动按键
int a=2,b=7;
connect(mybutton_1,&QPushButton::pressed,
        [=]()
         {
               mybutton_1->setText("Lambda表达式");
               qDebug()<<a+b;
          }
);

qDebug()는 C 언어의 printf와 비슷한 출력물입니다. 사용할 때 #include "QDebug"가 필요합니다.
  컴파일하고 실행하면 버튼을 누른 후 버튼 텍스트가 변경되고 텍스트 "9"가 출력됩니다. 외부 변수 a, b, mybutton_1의 값에 "="가 전달되었음을 알 수 있다.
  "="는 모든 외부 지역 변수와 클래스의 모든 구성원을 값으로 전달할 수 있음을 알 수 있습니다. 마찬가지로 "this"는 클래스의 모든 멤버가 값으로 전달됨을 의미하고 "&"는 모든 외부 지역 변수가 참조됨을 의미합니다.

요약하다:

  • [capture]: 람다 식에서 이 부분은 반드시 존재해야 하며 생략할 수 없습니다.
  • =: 모든 외부 지역 변수와 클래스의 모든 구성원을 값으로 람다 식에 전달합니다.기본값은 읽기 전용이며 변수 값은 변경할 수 없습니다.
  • &: 모든 지역 변수(람다식이 위치한 클래스에서 이것을 포함), 참조의 전달 방식.
  • this: Lambda가 위치한 클래스의 멤버 변수를 함수 본문에 사용할 수 있습니다.
  • "="를 권장합니다.

변경 가능 설명

위에서 언급했듯이 "="는 값으로 전달됩니다. 기본적으로 읽기 전용입니다. 외부 변수를 변경하려면 이때 변경 가능을 사용할 수 있습니다. 코드는 다음과 같습니다.

connect(mybutton_1,&QPushButton::pressed,
        [=]() mutable
        {
              b=3;
              mybutton_1->setText("Lambda表达式");
              qDebug()<<a+b;
         }
);
  • 요약하다:
  • 람다 식에서 mutable은 생략할 수 있습니다. 변수를 값으로 전달할 때 가변 수정자를 추가하면 Lambda 표현식에서 변수 값을 수정할 수 있지만 값 자체는 수정되지 않습니다.

(매개변수) 설명

Lambda 표현식에 매개변수가 없으면 이 부분을 무시할 수 있으며, 신호에 매개변수가 있으면 ()를 사용하여 함수 매개변수를 오버로드할 수 있습니다. 예를 들어:

connect(mybutton_1,&QPushButton::clicked,
             [=](bool Check)
             {
                  qDebug()<<Check;
             }
         );

QPushButton의 신호 void QAbstractButton::clicked(bool checked = false)에는 매개변수가 있으며 매개변수는 ()를 통해 Lambda 표현식으로 전달될 수 있습니다.

요약하다:

utton::clicked,
[=](bool 확인)
{ qDebug()<<확인; } );



 QPushButton的信号void QAbstractButton::clicked(bool checked = false)是有参数的,通过()可将参数传入Lambda表达式中。

**总结:**

- **在Lambda表达式中无参数时,(parameters)部分是可以省略的。**

추천

출처blog.csdn.net/weixin_43925768/article/details/131124411