Kommunikation zwischen Objekten - Signal & Slots

Zentraler Teil eines GUI ist die Reaktion auf Ereignisse: Maus-Klick oder -Bewegung, Text-Eingabe, Menueauswahl, etc. Dies ist in der Regel der komplexeste Teil bei der Erstellung eines GUIs. Qt benutzt dafür den sog. Signal & Slots Mechanismus.

Image abstract-connections

Image concrete-connections Signal & Slots ist eine Qt spezifische C++ Erweiterung.


// Beispiel zum SIGNAL-SLOT-Mechanismus
//
#include<QObject>
#include<iostream>

using namespace std;

// Eine Klasse, die Signals und Slots besitzt.
class Foo : public QObject
{
  Q_OBJECT;
public:
  Foo(); // Konstruktor
  int  value() const { return val; }
public slots: 
 // Der Wert von "val" wird geändert.
 void setValue( int );
signals:
 // Das Signal soll ausgesandt werden, wenn "val" geändert wird.
 void valueChanged( int ); 
private:
 int  val;
};
Foo::Foo()
{
  val = 1;
}
void Foo::setValue( int v )
{
  // val wird nur neu gesetzt, wenn tatsächlich ein anderer Wert übergeben wird.
  if ( v != val ) {
    val = v;
    emit valueChanged(v);
  }
}
#include"sigslot.moc"
int main()
{
  // Zwei Foo-Objekte
  Foo *a = new Foo();
  Foo *b = new Foo();
  // Das Signal des einen wird mit dem Slot des anderen Objekts verbunden.
  QObject::connect(a, SIGNAL(valueChanged(int)), b, SLOT(setValue(int)));
  b->setValue( 11 ); // b.val hat den Wert 11
  cout << "\nb:" << b->value() << "\n" ;
  a->setValue( 79 ); // a.val hat den Wert 79 und durch die obige Verknüpfung
                     // hat auch b.val den Wert 79
  cout << "\na:" << a->value() << "\n" ; 
  cout << "b:" << b->value() << "\n\n" ; 
  return 0;
}



Einfaches Beispiel mit Knopf und vordefinierten Signal & Slots



// Knopf mit  Funktion: wenn man draufdrueckt  beendet sich das Programm.
// Signal und Slot beide vor-definiert
#include <QApplication>
#include <QPushButton>
#include <QWidget>
class MyWidget : public QWidget
{
public:
  MyWidget(void);
private:
  QPushButton *ende;
}; 
MyWidget::MyWidget(void)
{
  ende = new QPushButton("Raus hier!",this);
  ende->setGeometry(10,10,100,30);
  // qApp ist ein globaler Zeiger auf QApplication
  QObject::connect(ende,SIGNAL(clicked()),qApp,SLOT(quit()));
}
int main(int argc,char** argv)
{
  QApplication app(argc,argv);
  QWidget* mywidget = new MyWidget();
  mywidget->resize(120,50);
  mywidget->show();
  return app.exec();
}

Beispiel mit Knopf und eigenem Slot


// Knopf mit  Funktion: jetzt selbst-definierte Aktion.
//
// erst mit moc drueber: $QTDIR/bin/moc -o drueckmich.moc drueckmich.C
#include <QApplication>
#include <QPushButton>
#include <QObject>

class MyWidget : public QWidget
{
  Q_OBJECT;
public:
  MyWidget(void); 
public slots:
  void autsch(void);
private:
  QPushButton *ende;
}; 
MyWidget::MyWidget(void)
{
  ende = new QPushButton("Drueck mich!",this);
  ende->setGeometry(10,10,200,40);

  // qApp ist ein globaler Zeiger auf QApplication
  QObject::connect(ende,SIGNAL(clicked()),this,SLOT(autsch()));
}
void MyWidget::autsch() {
  ende->setText("Aua, nicht so grob !");
}
#include "drueckmich.moc"
int main(int argc,char** argv)
{
  QApplication app(argc,argv);
  QWidget* mywidget = new MyWidget();
  mywidget->resize(120,50);
  //  app.setMainWidget(mywidget);
  mywidget->show();
  return app.exec();
}


Einfacher Taschenrechner



// addInt.C
#include <QApplication>
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QLayout>
#include <QString>
class MyWidget : public QWidget
{
  Q_OBJECT;
public:
  MyWidget(void);
public slots:
  void addint(void);
private:
  QPushButton *add;
  QLineEdit* num1 ;
  QLineEdit* num2 ;
  QLineEdit* num3 ;
}; 
MyWidget::MyWidget(void)
{
  num1 = new QLineEdit(this);  // Text-Feld
  num1->setText( "0" );
  num2 = new QLineEdit(this);  // Text-Feld
  num2->setText( "0" );
  num3 = new QLineEdit(this);  // Text-Feld
  num3->setText( "0" );
  QPushButton *qpb = new QPushButton("Add",this); // Knopf
  QLabel *qlab = new QLabel("Result",this); // label
  QVBoxLayout* layout = new QVBoxLayout(this);
  layout->addWidget(num1);
  layout->addWidget(qpb);
  layout->addWidget(num2);
  layout->addWidget(qlab);
  layout->addWidget(num3);
  // 
  QObject::connect(qpb,SIGNAL(clicked()),this,SLOT(addint())); // button => addint
  QObject::connect(num1,SIGNAL(returnPressed()),this,SLOT(addint())); // textfield => addint
  QObject::connect(num2,SIGNAL(returnPressed()),this,SLOT(addint()));
}
void MyWidget::addint() { // add 2 integers
  int v1 = (num1->text()).toInt(); // QString ==> int
  int v2 = (num2->text()).toInt();
  int v3 = v1 + v2;
  QString qs;
  qs.setNum(v3);  // int ==> QString
  num3->setText(qs);
}
#include "addInt.moc"

int main(int argc,char** argv)
{
  QApplication app(argc,argv);
  QWidget* mywidget = new MyWidget();
  mywidget->resize(120,130);
  mywidget->show();
  return app.exec();
}
 


Pulldown-Menues Und hier noch ein etwas laengeres Beispiel zu Pulldown-Menues.


// moveCircle3.C  
//
#include <QApplication>
#include <QMenuBar>
#include <QMessageBox>
#include <QPainter>
#include <QPixmap>
#include <QWidget>
#include <QResizeEvent>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QScrollArea>
#include <QColorDialog>
#include <iostream>
#include <cmath>

class myDraw : public QWidget  // Zeichen--Widget
{
  Q_OBJECT;
 
public:
  myDraw();
public slots:
  void setColor( QColor );
  QColor getColor() ;
protected:
  virtual void paintEvent(QPaintEvent*);
  virtual void mousePressEvent( QMouseEvent* );
  virtual void mouseMoveEvent( QMouseEvent* );
private:
  int xpos, ypos, radius; // circle parameters
  QColor mycol;
};

/// Separate Klasse fuer  Haupt-Widget
class myWindow : public QWidget  
{
  Q_OBJECT

public:
  myWindow();

private slots:
  void slotAbout();
  void slotAboutQt();
  void slotColorMenu( );
  void slotColorRed( );
  void slotColorYellow( );
  void slotColorBlue( );

signals:
  void colorChanged( QColor );

protected:
  virtual void resizeEvent( QResizeEvent* );

private:
  QMenuBar *menubar;
  QMenu *filemenu;
  QMenu *colormenu;
  QMenu *helpmenu;
  QScrollArea *scrollarea;
  myDraw *mydr;
};

#include "moveCircle3.moc"

// Implementations

myDraw::myDraw()
{
  xpos = 50; ypos = 50; radius = 40;
  mycol = Qt::yellow;
  setBackgroundRole(QPalette::Base);
  setAutoFillBackground(true);
}

void myDraw::paintEvent(QPaintEvent* )
{
  QBrush b1( mycol );  
  QPainter *paint = new QPainter();
  paint->begin( this);  
  paint->setPen( Qt::red );
  paint->setBrush( b1 );
  paint->drawEllipse( xpos, ypos, radius, radius );
  paint->end();
}
// reagiert auf Maus-Klick
void myDraw::mousePressEvent( QMouseEvent* event )
{
  xpos = (event->pos()).x();
  ypos = (event->pos()).y();
  update();
}
// reagiert auf Mausbewegung, waehrend eine Taste gedrueckt ist.
void myDraw::mouseMoveEvent( QMouseEvent* event )
{
  xpos = (event->pos()).x();
  ypos = (event->pos()).y();
  update();
}
void myDraw::setColor( QColor new_color )
{
  mycol = new_color;
  update();
}
QColor myDraw::getColor(  )
{
  return( mycol );
}
myWindow::myWindow()
{

  // setup menues
  // first three popup-menues
  filemenu = new QMenu("File");
  filemenu->addAction( "&Quit", qApp, SLOT( quit() ) ); 
              
  colormenu = new QMenu("Color");
  colormenu->addAction( "Red",  this, SLOT( slotColorRed( )));
  colormenu->addAction( "Yellow",  this, SLOT( slotColorYellow( )));
  colormenu->addAction( "Blue",  this, SLOT( slotColorBlue( )));
  colormenu->addAction( "Set color ...",  this, SLOT( slotColorMenu(  )));

  helpmenu = new QMenu("Help"); 
  helpmenu->addAction( "&About MoveCircle", this, SLOT( slotAbout() ) );
  helpmenu->addAction( "About &Qt", this, SLOT( slotAboutQt() ) );

  // create menubar in widget and add the 3 popup-menues
  menubar = new QMenuBar( this );
  menubar->addMenu(filemenu );
  menubar->addMenu( colormenu );
  //  menubar->insertSeparator();
  menubar->addMenu(helpmenu );

  // Hier wird ein ScollArea definiert
  scrollarea = new QScrollArea( this );
  scrollarea->setGeometry( 0, menubar->height(), width(), height() - menubar->height() );
            
  // Die Malflaeche wird in ausreichender Groesse erzeugt.             
  mydr = new myDraw();
  mydr->setGeometry( 0, 0, 1000, 1000 );
  // und in die ScrollArea eingefügt.
  scrollarea->setWidget( mydr );
  // Verknuepfung des Signals colorChanged() mit dem Slot setColor()
  // der Malfläche zum Setzen der Farbe 
  QObject::connect( this, SIGNAL( colorChanged( QColor ) ),
                    mydr, SLOT( setColor( QColor ) ) );
}
// Bei einer Veränderung der Größe muss ScrollArea angepasst werden.
void myWindow::resizeEvent( QResizeEvent* )
{
  scrollarea->setGeometry( 0, menubar->height(), width(), height() - menubar->height() ); 
}
void myWindow::slotAbout()
{
  QMessageBox::information( this, "About QtTriangle 2", 
                "This is the MoveCircle application\n"
                "No Copyright by Nobody\n");
}
void myWindow::slotAboutQt()
{
  QMessageBox::aboutQt( this, "About Qt" );
}

// dieser Slot setzt die Farbe nicht direkt, da die zugehörige
// Datenstruktur nicht zur selben Klasse gehört.
// Statt dessen wird ein Signal ausgesandt.
void myWindow::slotColorMenu(  )
{
  QColor col = QColorDialog::getColor(mydr->getColor(), this);
  if (!col.isValid())
    return;
  colorChanged(col);
}
void myWindow::slotColorRed(  ) {  colorChanged(Qt::red); }
void myWindow::slotColorYellow(  ) {  colorChanged(Qt::yellow); }
void myWindow::slotColorBlue(  ) {  colorChanged(Qt::blue); }

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

    myWindow *wid = new myWindow();
    wid->resize(300,300);
    wid->show();
    return app.exec();
}



GDuckeck 2019-08-01