Signals and slots question



  • Ok, I have been reading about signals and slots and can't quite figure it out between three classes. This is what I seem to understand and it doesn't work. I am sure I am missing something fundamental. This is how I understand how it should work. I want to sychronize the combo boxes in Object1 and Object2 so that if the combobox in Object1 is changed, the combobox in the Object2 will be updated to match. Is this how it is properly set up? I have a mainwindow class that loads the settings and then send a signal to class1 to set the combobox to the last used item. Then there is a onComboBoxchanged on Object1 that will send a signal to Class2 so that it will have the combobox updated there as well. I can get Object1 to work, but when I try to send the information to Object2, it isn't working.

    mainwindow.h
    @#ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    //Forward declaration of classes used
    class Object1;
    class Object2;

    private:
    Object1 *object1;
    Object2 *object2;
    void writeSettings();
    void readSettings() ;

    signals:
    void restoreCombo(int);
    @

    mainwindow.cpp
    @#include "mainwindow.h"
    #include "object1.h"
    #include "object2.h"

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    object1 =new Object1(this);
    object2 = new Object2 (this);

    connect(this,SIGNAL(restoreCombo(int)),object1, SLOT(restoreCombo(int)));
    ui->MainStackWidget->setCurrentIndex(0);//stacked widget with the objects loaded in the stackwidget item
    ui->MainStackWidget->addWidget(object1);
    ui->MainStackWidget->addWidget(Object2);
    readSettings();
    

    void MainWindow::readSettings()
    {

    QSettings settings ("MySoftware","Test System");
    int index=settings.value("lastName",0).toInt();
    emit restoreCombo(index);//so far so good!
    }
    

    }
    @

    object1.h
    @
    #ifndef OBJECT1_H
    #define OBJECT1_H

    class Object2;//forward declaration

    namespace Ui {
    class Object1;
    }

    class Object1 : public QWidget
    {
    Q_OBJECT

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

    private:
    Ui::Object1 *ui;
    Object2 *object2;

    signals:
    void cmbObject1Change(int);

    private slots:
    void restoreCombo(int);
    };

    #endif // OBJECT1_H@

    object1.cpp
    @
    #include "object1.h"
    #include "object2.h"

    Object1::Object1(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Object1)
    {
    ui->setupUi(this);
    connect(this, SIGNAL(cmbObject1Change(int)),object2,SLOT(restoreCombo(int)));//this seems to create the error.
    }

    void Object1::on_cmbBox_currentIndexChanged()
    {
    int id=(ui->cmbBox->itemData(ui->cmbBox->currentIndex()).toInt());
    emit cmbObject1IndexChange(id);
    }
    @

    object2.h
    @
    #ifndef OBJECT2_H
    #define OBJECT2_H

    namespace Ui {
    class Object2;

    }

    class Object2 : public QWidget
    {
    Q_OBJECT

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

    private:
    Ui::Object2 *ui;

    private slots:
    void restoreCombobox(int );
    };

    #endif // OBJECT2_H
    @

    object2.cpp
    @
    #include "object2.h"
    #include "ui_sectiontimer.h"

    Object2::Object2(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Object2)
    {
    ui->setupUi(this);
    }
    void Object2::restoreCombobox(int id)
    {

       if (id>=0)
        {
        int index = ui->cmbBox->findData(id);
        ui->cmbBox->setCurrentIndex(index);
        }
        else
            {
            ui->cmbBox->setCurrentIndex(0);
            }
    
    }
    

    @
    I still get a segmentation fault...and it seems to point to the location indicated
    Where did I go wrong?



  • Normally, you connect signals from outside.

    But first of all, you have the same C++ problem as in the "last post:":http://developer.qt.nokia.com/forums/viewthread/2632/ uninitialized pointers!

    • The member object2 of class Object1 is not initialized, it's anything!
    • ALWAYS initialize you member variables in the constructors, especially, if they are pointers!

    @
    class Obect1 : public QWidget
    {
    Q_OBJECT

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

    private:
    Ui::Object1 *ui;
    Object2 *object2;
    ...
    }

    Object1::Object1(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Object1),
    object1(0) // <<-- this should always be done with pointers in constructors!!!!!
    {
    ui->setupUi(this);
    ....
    }
    @

    Normally, connects are done from outside:

    @
    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    object1 =new Object1(this);
    object2 = new BikePage (this);
    section = Object1 (this);

    connect(this,SIGNAL(restoreCombo(int)),object1, SLOT(restoreCombo(int)));
    connect(object1, SIGNAL(cmbObject1Change(int)),object2,SLOT(restoreCombo(int)));
    ui->MainStackWidget->setCurrentIndex(0);//stacked widget with the objects loaded in the stackwidget item
    ui->MainStackWidget->addWidget(object1);
    ui->MainStackWidget->addWidget(Object2);
    readSettings();
    

    }
    @



  • You need to have an instance of Object2 to connect to. The object2 member seems to be uninitialized (and therefore it's value is undefined) at the time of connection. This will cause a crash.

    Signals and slots work between instances of classes, not between classes. That is an important thing to keep in mind.

    Whether or not connections are made from the outside depends on the setup. If Object1 is not to know about Object2, then you have to make the connection outside the Object1 and Object2 class.



  • And more

    @
    connect(this, SIGNAL(cmbObject1Change(int)),object2,SLOT(restoreCombo(int))); // <-- this is cmbObject1Change
    .....
    emit cmbObject1IndexChange(id); // <-- this is cmbObject1IndexChange
    @

    Signal name declarated and connected and in emit call do not match



  • Ok, time for some C++ basics:

    If you have this class:

    @
    // forward declaration
    class FancyClass;

    class FooBar {
    public:
    FooBar() {
    // empty
    }

    void doFoo() {
      fancy->blurb(); 
    }
    

    protected:
    FancyClass *fancy;
    int counter;
    }
    @

    So, what happens? Once you instantiate this class with

    @
    FooBar *fb = new FooBar;
    fb->doFoo();
    @

    You allocate some space on the heap, especially for the pointer and the int. Then the constructor will be called. Since it is empty nothing is done. So what's the state of the object? The int is some space in memory. The value of the int is unknown. It depends on the bits stored in the memory in its place before the class was instantiated, so if you print out the int value you get some more or less random value.

    Almost the same applies for the pointer. The value is also unknown/random. But if you access the pointer, as in calling doFoo(); you dereference it - that means, the random value is used as an address in the memory to access another object. It's obvious that this cannot go well. The address could point to anywhere in the memory, pointing to anything, but an actual object.[1] You get a crash.

    So, what is the conclusion?
    Pointers must be initialized before you use them! And you should initialize your int with some sane value too.

    You could change your FooBar constructor like this to achieve this:

    @
    FooBar(FancyClass *fancyClassPointer) {
    fancy = fancyClassPointer;
    counter = 0;
    }
    @

    And now it's time to head over to a book store and get you some good introduction to C++ to read for the holidays ;-)

    fn1. Yes, I know it can point to an object, even to a valid object. But thats purely by chance.



  • @
    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    object1 =new Object1(this);
    object2 = new BikePage (this);
    .....
    }//dont forget the brackets...
    @

    One of the problem is in object2, object2 is an instance of the class Object2 and where is the definition of the BikePage class?, it should be:

    @
    object2 = new Object2(this);
    @

    Btw, in the fuction readSettings, there is an extra bracket "{"



  • I am really trying to understand this and get a grasp. I appreciate you guys taking your time to help me. I had a typo in my post. This:
    @MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    object1 =new Object1(this);
    object2 = new BikePage (this);
    section = Object1 (this);@

    Should have been this:
    @MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    object1 =new Object1(this);
    object2 = new Object2 (this);
    @
    Gerolf, you stated that normally signals are created from the outside. I don't quite understand what you mean by this...was it simply moving the connect statement before the other functions or is there a better way? You also stated, The member object2 of class Object1 is not initialized, it’s anything! Did my typo correct this? Wait..I think I see the issue! I should have put @object2 = new Object2;@ in the constructor of Object1...is that correct?

    Franz, I think you were talking about me not putting the object2 = new Object2 in the constructor of Object1...correct? You also mention about making connections outside the classes....I don't understand how to do that.Would the connection definition be placed in the mainwindow class?

    Vass, Thanks...that was a typo. I appreciate your help.

    Volker, I think I am beginning to understand a bit(maybe only a little bit...maybe too many crashes on my motorcycle has blocked the signals!!) I think I understand your example. Let me see if this is correct...fancy was declared as a pointer to a FancyClass object but there was no FancyClass object created (there should have been fancy= new Fancyclass...in the constructor) Am I getting it? And another option for declaring the FooBar class could have been:
    @FooBar(FancyClass *fancyClassPointer) {
    fancy = fancyClassPointer;
    counter = 0;
    }@
    Lets see if I got this right. By using the constructor the way you did, the FooBar class is called with a pointer to FancyClass, and then you declare the actual pointer variable in the constructor...Am I close? I have purchased and read a few C++ books and I am just starting to try and program with the little bit of knowledge I have. Any suggestions on some good books? I have the new C++ GUI programming book and a C++ programming in 21 days and a couple of others. But I think I learn better by actual application. I didn't see what you were talking about at first. Now I understand what I did wrong(at least I think I do....I will make the changes when I get home and let you know if it works!) Any suggestions on some good books?
    Again, to all I REALLY appreciate your guidance in this issue!



  • I have another question. I was looking at the suggestion by Gerolf again and I think I realized what you were saying. But to be sure. Is it better practice to create an object of each class in mainwindow (and instantiate it) of ALL the classes that would require signals and slots and then to define all the connections there. In each class, only the signal and slot needs to be defined. This would eliminate the need to create a class (and instantiate it) in each individual class. Is that what you were saying? If so, this seems like a clean approach. I will try it when I get home!
    Something like this:
    @MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    object1 =new Object1(this);
    object2 = new Object2 (this);
    object3 = new Object3 (this);

    connect(this,SIGNAL(restoreCombo(int)),object1, SLOT(restoreCombo(int)));
    connect(object1, SIGNAL(cmbObject1Change(int)),object2,SLOT(restoreCombo(int)));
    

    connect(object3,SIGNAL(restoreCombo(int)),object1, SLOT(restoreCombo(int)));
    ui->MainStackWidget->setCurrentIndex(0);
    ui->MainStackWidget->addWidget(object1);
    ui->MainStackWidget->addWidget(Object2);
    ui->MainStackWidget->addWidget(object3);
    readSettings();
    }@
    And then in each of the classes (Object1, Object2...) I would only need to declare the signals and slots in the header file?



  • That's definitly better.
    But I suggest that you follow Volker's suggestion first,

    bq. And now it’s time to head over to a book store and get you some good introduction to C++ to read for the holidays ;-)

    This thing with pointzers instances, intialization etc. is really important in C++ and therefore in Qt. It's not neccassary to read the complete stroustroup (which is quite big) but any C++ introduction could help.



  • Hey,

    You should first go through this article then follow the above examples or suggestions.

    http://doc.qt.nokia.com/latest/signalsandslots.html

    Best regards,



  • [quote author="qtrahul" date="1293091094"]Hey,

    You should first go through this article then follow the above examples or suggestions.

    http://doc.qt.nokia.com/latest/signalsandslots.html

    Best regards,[/quote]

    Rahul,

    The author has mentioned that "I have been reading about signals and slots". And anyone reading about it will definitely have gone through the Qt documentation.



  • poporacer, in that code snippet:

    @
    FooBar(FancyClass *fancyClassPointer) {
      fancy = fancyClassPointer;
      counter = 0;
    }
    @

    the variable fancy was declared and defined in the class definition (class FooBar { .... }). In the constructor there is a value (that of fancyClassPointer) assigned to it.

    It is really, really important to know the differences between declaration, definition and assignment of things in C/C++. These kind of things are way better to learn with small, well commented sample programs than with a full sized application where on has to cover a huge bunch of other questions.

    Please, do yourself a favor and read some "Introduction to C++" (not necessarily covering Qt! You can add this later on!) from the beginning(!). And please do not try to abridge things - your programs will end like your motorcycle :-(



  • [quote author="QtK" date="1293093708"]
    The author has mentioned that "I have been reading about signals and slots". And anyone reading about it will definitely have gone through the Qt documentation.
    [/quote]

    Unfortunately not (although I do not know for poporacer, to be fair).



  • @
    void Object1::on_cmbBox_currentIndexChanged()
    {
    int id=(ui->cmbBox->itemData(ui->cmbBox->currentIndex()).toInt());
    emit cmbObject1IndexChange(id);
    }@
    it must put int the header as a slot
    @
    public slots:
    void on_cmbBox_currentIndexChanged();
    @



  • Thanks for all the help. Yes I have read about Signals and slots. My signals and slots were working, it was a basic C++ error. The documentation shows how signals and slots work in one class not accross different classes. I will continue to read and practice what I have read...Thanks for all your help!



  • A good starting place is the API docs on "QObject::connect() ":http://doc.qt.nokia.com/stable/qobject.html#connect. Basicall it's four parameters to connect():

    Pointer to the sender QObject

    Name and signature of the sender's signal

    Pointer to the receiver QObject

    Name and signature of the receiver's slot

    Neither of the two pointers needs to be "this". It's quite ok (and often done) to setup a connection between to other objects.

    By QObject is meant any QObject based class, i.e. almost all GUI classes (as QWidget is a QObject subclass and thus are all QWigets subclasses).


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.