Qt connect signal to slot

21.5k Views Asked by At

I have a main window class which contains a QSplitter widget (among other widgets). The contents of this splitter are populated by the widgets that are in another class.

Inside this other Widgwt I have a layout with a QPushButton called test_btn added I also have a function defined in the header file under public slots: called test_function which has the corresponding code in the cpp file:

cout << "Test!" << endl;

I try and call this function using the following code:

connect(test_btn, SIGNAL(clicked()), SLOT(test_function()));

The widgets and buttons appear as expected in the application but when I click it nothing happens.

If I add the same connect code to the main window it works (for calling a test function from the main window) i.e.

connect(cc_point.test_btn, SIGNAL(clicked()), SLOT(main_win_test_function()));

I cant call the test function from the other class unless I call it from the main_win_test_function()

I know I'm missing the 'receiver widget' from the connect statement, although it does work without it in the main window class and I don't get any errors when compiling.

Do I need to define the parent or something in the other_class?

main_window.cpp:

main_window::main_window()
{
    add_splitter();

    QGridLayout *window_layout = new QGridLayout;

    window_layout->setSpacing(0);
    window_layout->setContentsMargins(0, 0, 0, 0);

    window_layout->addWidget(splitter1, 0, 0);

    set_layout(window_layout);
}

void main_window::add_splitter()
{
     other_class oc_point;

     splitter1 = new QSplitter(Qt::Horizontal);

     splitter1->addWidget(oc_point.oc_widget);
}

other_class.cpp:

other_class:other_class()
{
     oc_widget = new QWidget;

     QGridLayout *other_layout = new QGridLayout(oc_widget);

     test_btn = new QPushButton;
     test_btn->setText("Test Button");

     connect(test_btn, SIGNAL(clicked()), test_btn->parent(), SLOT(test_function());
     other_layout->addWidget(test_btn);
}

void other_class::test_function()
{
     cout << "Test output" << endl;
}

main.cpp:

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

QApplication app(argc, argv);

main_window window;

window.resize(1100, 700);
window.setWindowTitle("Qt Test");
window.setWindowIcon(QIcon (":/images/icon_1.png"));
window.setMinimumHeight(500);
window.show();

return app.exec();
}

As I mentioned above, this all works fine as far as the window getting created and the widgets being displayed goes, but the test_function doesn't get called when I click the button. Let me know if I need to include my header files as well.

2

There are 2 best solutions below

2
On

After your objects are initialized, you should connected them to each other. Explicitly naming whose slots are whose and whose signals are whose, will help a lot.

Sometimes I'll make a helper function void Widget::connectTo__Class__(Class * class_ptr);

Then in that function call I'll connect the slots that cross the class/parent/inheritance boundaries.

Here is an example implementation:

void Widget::connectTo__Class__(Class * class_ptr)
{
    QObject::connect(this,      SIGNAL(myWidgetSignal()), 
                     class_ptr, SLOT(myClassSlot()));

    QObject::connect(class_ptr, SIGNAL(myClassSignal()), 
                     this,      SLOT(myWidgetSlot()));
}

Also keep in mind that the runtime will complain when you are doing something wrong, such as if the class_ptr is null, or if myClassSignal doesn't exist. You can see these errors in the application output when you run it in Debug mode.

And give QDebug a try sometime.

http://qt-project.org/doc/qt-4.8/debug.html

EDIT: To fix any header class recursive definition problems, do the following:

In your header file for one of the classes only prototype the class, don't include it:

in widget.h:

// #include "myclass.cpp" // Should be commented out!

class MyClass; // This prototype avoids the recursion

// ...

class Widget
{
    // ...

    public:
        void connectToMyClass(Class * class_ptr);

    // ...
}

Then in the cpp file, you do the include.

#include "myclass.h"

void Widget::connectToMyClass(Class * class_ptr)
{
    // ...
}

Another way around this issue is to use QObject * instead of Class *.

Hope that helps.

4
On

In main_window::add_splitter(), the oc_point is destroyed as soon as the function returns. It's not the connection that is the problem, the problem is that your object vanishes before you can do anything useful with it.

Below is a self-contained example that demonstrates this problem in both Qt 4 and 5. As soon as you click "Delete Other", the Test Button doesn't work.

//main.cpp
#include <QApplication>
#include <QGridLayout>
#include <QSplitter>
#include <QPushButton>
#include <QMessageBox>

class Other : public QObject
{
    Q_OBJECT
    QWidget *m_widget;
    Q_SLOT void testFunction() {
        QMessageBox::information(NULL, "Test", "Slot works.");
    }
public:
    QWidget *widget() const { return m_widget; }
    Other(QObject *parent = 0) : m_widget(new QWidget), QObject(parent) {
        QGridLayout *l = new QGridLayout(m_widget);
        QPushButton *btn = new QPushButton("Test Button");
        l->addWidget(btn);
        connect(btn, SIGNAL(clicked()), this, SLOT(testFunction()));
    }
    ~Other() { if (!m_widget->parent()) delete m_widget; }
};

class Main : public QWidget
{
public:
    Main(QWidget *parent = 0, Qt::WindowFlags f = 0) : QWidget(parent, f) {
        QGridLayout *l = new QGridLayout(this);
        QPushButton *btn = new QPushButton("Delete Other");
        Other *o = new Other(this);
        QSplitter *spl = new QSplitter;
        spl->addWidget(o->widget());
        l->addWidget(spl);
        l->addWidget(btn);
        connect(btn, SIGNAL(clicked()), o, SLOT(deleteLater()));
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Main w;
    w.show();
    return a.exec();
}

#include "main.moc"