Qt/Sinais e slots

< Qt

A comunicação entre os widgets, no Qt, é feita através de sinais (signals) e slots.

O mecanismo para ligar um sinal a um slot é através da função connect:

  QObject::connect( p_widget1, signal1, p_widget2, slot2);

em que p_widget1 é um pointer para o widget sobre o qual a ação ocorre, signal1 é o sinal indicativo desta ação, p_widget2 é o widget que vai responder ao sinal e slot2 é a resposta.

IntroduçãoEditar

O objetivo é colocar um botão e uma caixa de texto. Ao clicar o botão (que possui como texto a string "OK"), a caixa de texto deve responder assumindo o valor "OK".

O esboço de código abaixo não faz nada, apenas mostra os widgets:

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

    QPushButton button1("OK", &vbox);
    QLineEdit lineedit1("", &vbox);

    app.setMainWidget(&vbox);
    vbox.show();
    return app.exec();
}

Precisamos que lineedit1 execute o seu membro setText("OK"); assim que o botão for clicado, ou seja, precisamos fazer a conexão:

    QObject::connect( &button1, SIGNAL(clicked()), &lineedit1, SLOT(setText("OK")) );

O problema é que, pela documentação da função connect[1], o número de argumentos do signal deve ser o mesmo do número de argumentos do slot - e, neste caso, o signal não tem argumentos porém estamos querendo que o slot tenha um argumento.

Ou seja, o código abaixo está errado:

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

    QPushButton button1("OK", &vbox);
    QLineEdit lineedit1("", &vbox);

    QObject::connect( &button1, SIGNAL(clicked()), &lineedit1, SLOT(setText("OK")) );
   
    app.setMainWidget(&vbox);
    vbox.show();
    return app.exec();
}

Solução orientada a objetosEditar

A solução é transformar setText("OK") em um membro do QLineEdit. Isto é feito através do mecanismo conhecido por herança, ou seja, será criada uma nova classe, NovoLineEdit, que herdará tudo da classe QLineEdit, e terá, além disso, um novo membro, escreve_ok() que fará apenas o setText("OK").

O header .hEditar

Uma das formas de fazer isso é criar um outro arquivo, de extensão .h (no nosso exemplo, chamado de qt_button_1.h) que contém a definição da nova classe:

#include <qlineedit.h>

class NovoLineEdit : public QLineEdit
{
  Q_OBJECT

  public:
    NovoLineEdit(const QString & contents, QWidget * parent, const char * name = 0);

  private slots:
    void escreve_ok();
};

Detalhando este arquivo:

Como a nova classe herda da QLineEdit, temos que incluir qlineedit.h

class NovoLineEdit : public QLineEdit

é a declaração da nova classe (NovoLineEdit), como uma classe filha da classe QLineEdit.

A linha Q_OBJECT não é uma instrução em C++. É necessária para informar ao pré-compilador moc de que é necessário habilitar os sinais e slots, ou seja, o Q_OBJECT habilata os sinais emitidos na GUI e os slots contidos na GUI.

É necessário incluir o constructor

  public:
    NovoLineEdit(const QString & contents, QWidget * parent, const char * name = 0);

já que este será chamado pelo programa.

Finalmente,

  private slots:
    void escreve_ok();

é a função (slot) que será ligada ao signal. Note-se, de novo, que a palavra slots não faz parte da linguagem C++, sendo, assim como Q_OBJECT, coisas que o moc usa para gerar informações exotéricas.

O fonte .cppEditar

O fonte em C++ é:

#include <qapplication.h>
#include <qvbox.h>
#include <qpushbutton.h>
#include <qlineedit.h>
#include "qt_button_1.h"

void NovoLineEdit::escreve_ok()
{
  setText("OK");
}

NovoLineEdit::NovoLineEdit(const QString & contents, QWidget * parent, const char * name) :
    QLineEdit(contents, parent, name)
{
}

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

    QPushButton button1("OK", &vbox);
    NovoLineEdit lineedit1("", &vbox);

    QObject::connect( &button1, SIGNAL(clicked()), &lineedit1, SLOT(escreve_ok()) );
   
    app.setMainWidget(&vbox);
    vbox.show();
    return app.exec();
}

Detalhando:

void NovoLineEdit::escreve_ok()
{
  setText("OK");
}

é o slot. Como pode ser visto, este é um membro normal da classe certa.

NovoLineEdit::NovoLineEdit(const QString & contents, QWidget * parent, const char * name) :
    QLineEdit(contents, parent, name)
{
}

é o constructor da nova classe. O objetivo é que NovoLineEdit seja construído exatamente da mesma forma que o QLineEdit - lembrando que a única diferença é o acréscimo do membro escreve_ok.

    QObject::connect( &button1, SIGNAL(clicked()), &lineedit1, SLOT(escreve_ok()) );

finalmente, a conexão entre o ato de clicar no botão e o QLineEdit responder é feita.

Referências

  1. Signals and Slots - documentação oficial em inglês