수색…


소개

신호와 슬롯은 객체 간의 통신에 사용됩니다. 신호와 슬롯 메커니즘은 Qt의 핵심 기능입니다. GUI 프로그래밍에서 하나의 위젯을 변경하면 종종 다른 위젯에 통지되기를 원합니다. 더 일반적으로, 우리는 어떤 종류의 객체도 서로 통신 할 수 있기를 원합니다. 신호는 다른 객체에 대해 흥미로울 수있는 방식으로 상태를 변경할 때 객체에 의해 방출됩니다. 슬롯은 신호를 수신하는 데 사용할 수 있지만 일반 멤버 기능이기도합니다.

비고

이 주제에 대한 공식 문서는 여기 에서 찾을 수 있습니다 .

작은 예

신호와 슬롯은 객체 간의 통신에 사용됩니다. 신호와 슬롯 메커니즘은 Qt의 핵심 기능이며 아마도 다른 프레임 워크에서 제공하는 기능과 가장 다른 부분 일 것입니다.

최소 예제는 하나의 신호, 하나의 슬롯 및 하나의 연결을 가진 클래스를 필요로합니다.

counter.h

#ifndef COUNTER_H
#define COUNTER_H

#include <QWidget>
#include <QDebug>

class Counter : public QWidget
{
    /*
     * All classes that contain signals or slots must mention Q_OBJECT
     * at the top of their declaration.
     * They must also derive (directly or indirectly) from QObject.
     */
    Q_OBJECT

public:
    Counter (QWidget *parent = 0): QWidget(parent)
    {
            m_value = 0;

            /*
             * The most important line: connect the signal to the slot.
             */
            connect(this, &Counter::valueChanged, this, &Counter::printvalue);
    }

    void setValue(int value)
    {
        if (value != m_value) {
            m_value = value;
            /*
             * The emit line emits the signal valueChanged() from
             * the object, with the new value as argument.
             */
            emit valueChanged(m_value);
        }
    }

public slots:
    void printValue(int value)
    {
        qDebug() << "new value: " << value;
    }

signals:
    void valueChanged(int newValue);

private:
    int m_value;

};

#endif

main 은 새로운 가치를 설정합니다. 슬롯이 어떻게 호출되는지 확인하고 값을 출력 할 수 있습니다.

#include <QtGui>
#include "counter.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Counter counter;
    counter.setValue(10);
    counter.show();

    return app.exec();
}

마지막으로, 우리 프로젝트 파일 :

SOURCES   = \
            main.cpp
HEADERS   = \
            counter.h

새로운 Qt5 연결 구문

SIGNALSLOT 매크로를 사용하는 기존의 connect 구문은 런타임에 완전히 작동하지만 두 가지 단점이 있습니다. 즉, 런타임 오버 헤드 (바이너리 크기 오버 헤드가 발생 함)가 있으며 컴파일 타임 정확성 검사가 없습니다. 새로운 구문은 두 가지 문제를 해결합니다. 예제에서 구문을 확인하기 전에 특히 어떤 일이 발생하는지 더 잘 알아야합니다.

우리가 집을 짓고 있고 케이블을 연결하고 싶다고합시다. 이것은 연결 함수가하는 것과 정확히 같습니다. 신호와 슬롯은이 연결을 필요로합니다. 요점은 당신이 하나의 연결을 할 경우, 당신은 더 중첩 연결에주의해야합니다. 신호를 슬롯에 연결할 때마다 신호가 방출 될 때마다 슬롯 함수를 호출하기 만하면 컴파일러에 알려줍니다. 이것은 정확히 일어난 일입니다.

다음은 main.cpp 샘플입니다.

#include <QApplication>
#include <QDebug>
#include <QTimer>

inline void onTick()
{
   qDebug() << "onTick()";
}

struct OnTimerTickListener {
   void onTimerTick()
   {
       qDebug() << "OnTimerTickListener::onTimerTick()";
   }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    OnTimerTickListener listenerObject;

    QTimer timer;
    // Connecting to a non-member function
    QObject::connect(&timer, &QTimer::timeout, onTick);
    // Connecting to an object member method
    QObject::connect(&timer, &QTimer::timeout, &listenerObject, &OnTimerTickListener::onTimerTick);
    // Connecting to a lambda
    QObject::connect(&timer, &QTimer::timeout, [](){
        qDebug() << "lambda-onTick";
    });    

    return app.exec();
}

힌트 : 이전 구문 ( SIGNAL / SLOT 매크로)에서는 슬롯 또는 신호가있는 모든 클래스에 대해 Qt 메타 컴파일러 (MCC)가 실행되어야합니다. 코딩 관점에서 볼 때 이러한 클래스에는 Q_OBJECT 매크로가 있어야 함을 의미합니다 (이 클래스에서 MOC를 실행할 필요성을 나타냄).

반면에 새로운 신택스는 시그널이 작동하려면 MOC가 필요하지만 슬롯은 작동 하지 않아야 합니다. 클래스에만 슬롯과 신호가없는 경우, Q_OBJECT 매크로를 가질 필요가 없으므로 MOC를 호출 할 수 없으므로 최종 바이너리 크기가 줄어들뿐만 아니라 컴파일 시간이 단축됩니다 (MOC 호출이없고 생성 된 컴파일러 호출이 필요 없음). *_moc.cpp 파일).

과부하 된 신호 / 슬롯 연결

Qt5의 새로운 연결 구문은 과부하 된 신호와 슬롯 연결이라는 큰 약점이 있습니다. 컴파일러가 오버로드를 해결할 수 있도록 멤버 함수 포인터에 static_cast 를 사용하거나 (Qt 5.7에서 시작) qOverload 및 friends :

#include <QObject>

class MyObject : public QObject
{
    Q_OBJECT
public:
    explicit MyObject(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    void slot(const QString &string) {}
    void slot(const int integer) {}

signals:
    void signal(const QString &string) {}
    void signal(const int integer) {}
};

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);

    // using pointers to make connect calls just a little simpler
    MyObject *a = new MyObject;
    MyObject *b = new MyObject;

    // COMPILE ERROR! the compiler does not know which overloads to pick :(
    QObject::connect(a, &MyObject::signal, b, &MyObject::slot);

    // this works, now the compiler knows which overload to pick, it is very ugly and hard to remember though...
    QObject::connect(
        a,
        static_cast<void(MyObject::*)(int)>(&MyObject::signal),
        b,
        static_cast<void(MyObject::*)(int)>(&MyObject::slot));

    // ...so starting in Qt 5.7 we can use qOverload and friends:
    // this requires C++14 enabled:
    QObject::connect(
        a,
        qOverload<int>(&MyObject::signal),
        b,
        qOverload<int>(&MyObject::slot));

    // this is slightly longer, but works in C++11:
    QObject::connect(
        a,
        QOverload<int>::of(&MyObject::signal),
        b,
        QOverload<int>::of(&MyObject::slot));

    // there are also qConstOverload/qNonConstOverload and QConstOverload/QNonConstOverload, the names should be self-explanatory
}

다중 창 신호 슬롯 연결

신호와 슬롯을 사용하는 간단한 멀티 윈도우 예제.

Main Window 뷰를 제어하는 ​​MainWindow 클래스가있다. 웹 사이트 클래스에 의해 제어되는 두 번째 창.

두 클래스는 웹 사이트 창에서 버튼을 클릭하면 MainWindow에서 어떤 일이 발생하도록 연결됩니다 (텍스트 레이블이 변경됨).

나는 GitHub 에도있는 간단한 예제를 만들었다.

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "website.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void changeText();

private slots:
    void on_openButton_clicked();

private:
    Ui::MainWindow *ui;

    //You want to keep a pointer to a new Website window
    Website* webWindow;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::changeText()
{
    ui->text->setText("New Text");
    delete webWindow;
}

void MainWindow::on_openButton_clicked()
{
    webWindow = new Website();
    QObject::connect(webWindow, SIGNAL(buttonPressed()), this, SLOT(changeText()));
    webWindow->show();
}

website.h

#ifndef WEBSITE_H
#define WEBSITE_H

#include <QDialog>

namespace Ui {
class Website;
}

class Website : public QDialog
{
    Q_OBJECT

public:
    explicit Website(QWidget *parent = 0);
    ~Website();

signals:
    void buttonPressed();

private slots:
    void on_changeButton_clicked();

private:
    Ui::Website *ui;
};

#endif // WEBSITE_H

website.cpp

#include "website.h"
#include "ui_website.h"

Website::Website(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Website)
{
    ui->setupUi(this);
}

Website::~Website()
{
    delete ui;
}

void Website::on_changeButton_clicked()
{
    emit buttonPressed();
}

프로젝트 구성 :

SOURCES += main.cpp\
        mainwindow.cpp \
    website.cpp

HEADERS  += mainwindow.h \
    website.h

FORMS    += mainwindow.ui \
    website.ui

위스 (Uis)가 구성 될 것이라고 생각하십시오.

  • 메인 창 : "텍스트"라는 레이블과 "openButton"이라는 버튼
  • 웹 사이트 창 : "changeButton"버튼

따라서 키포인트는 신호와 슬롯 사이의 연결과 윈도우 포인터 또는 참조의 관리입니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow