0

I Started learning Qt (5.5) a couple of days ago, and I recently got stuck on something when working with the connect function, specifically the SLOT parameter. I'm calling a member function from the same class that the connect function is called in, but when the SLOT function is triggered it acts like it's creating a new class object. It worked initially when I kept everything in the same class, but this problem popped up when I tried implementing a hierarchy. I wrote a short program to demonstrate my problem.

Main.cpp

#include <QApplication>

#include "MainWindow.h"

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

  MainWindow QtWindow;

  QtWindow.show();

  return app.exec();
}

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QWidget>
#include <QGridLayout>

#include "TopWidget.h"

class MainWindow : public QMainWindow
{
  Q_OBJECT
  public:
    MainWindow(QMainWindow *parent = 0);
  private:
    QWidget *mainWidget;
    QGridLayout *mainLayout;
};

#endif // MAINWINDOW_H

MainWindow.cpp

#include "MainWindow.h"

MainWindow::MainWindow(QMainWindow *parent) : QMainWindow(parent){

  mainWidget = new QWidget(this);
  mainLayout = new QGridLayout(mainWidget);
  setCentralWidget(mainWidget);

  TopWidget tWidget(this);

  mainLayout->addWidget(tWidget.topWidget, 0, 0);
}

TopWidget.h

#ifndef TOPWIDGET_H
#define TOPWIDGET_H

#include <stdlib.h>

#include <QWidget>
#include <QPushButton>
#include <QGridLayout>

#include <QDebug>
#include <QErrorMessage>

class TopWidget : public QWidget
{
  Q_OBJECT
  public:
    TopWidget(QWidget *parent);
    QWidget *topWidget;
  private:
    QGridLayout *wLayout;
    QPushButton *Button;

    int memVar1;
    int memVar2;
  private slots:
    void testConnect();
    //void SlotWithParams(int a, int b);
};

#endif // TOPWIDGET_H

TopWidget.cpp

#include "TopWidget.h"

TopWidget::TopWidget(QWidget *parent) : QWidget(parent){
  topWidget = new QWidget(parent);
  wLayout = new QGridLayout(topWidget);

  memVar1 = 123;
  memVar2 = 321;

  Button = new QPushButton("Click Me", topWidget);
  connect(Button, &QPushButton::clicked, [=](){ TopWidget::testConnect(); });
}

void TopWidget::testConnect(){
  qDebug("Button worked");
  if(memVar1 != 123 || memVar2 != 321){
    qDebug("Linking failed");
  }else{
    qDebug("Linking success");
  }
}

Since I just started with Qt, I don't have a good feel for what's "proper" Qt code, and what I should avoid, so tips in that direction are also appreciated. The following is the qmake file, if that's important.

CONFIG += c++11
CONFIG += debug
CONFIG += console

QT += widgets
QT += testlib

SOURCES += main.cpp
SOURCES += MainWindow.cpp
SOURCES += TopWidget.cpp
HEADERS += MainWindow.h
HEADERS += TopWidget.h

Release:DESTDIR = bin/Release
Release:OBJECTS_DIR = obj/Release
Release:MOC_DIR = extra/Release
Release:RCC_DIR = extra/Release
Release:UI_DIR = extra/Release

Debug:DESTDIR = bin/Debug
Debug:OBJECTS_DIR = obj/Debug
Debug:MOC_DIR = extra/Debug
Debug:RCC_DIR = extra/Debug
Debug:UI_DIR = extra/Debug

When I run the program in debug mode and press the button, it outputs "Button worked" indicating the link to the function was successful, but then outputs "Linking failed" indicating that a new object was created rather than taking the old one. My knowledge of C++ is patchy, since I only pick up what I need to, and I spent hours yesterday trying to fix this, so forgive me if the fix is something ridiculously easy, but I've mentally exhausted myself over this.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
TakingItCasual
  • 771
  • 1
  • 7
  • 22

1 Answers1

3

The problem comes from this line:

TopWidget tWidget(this);

You are allocating tWidget on the stack, and it gets destroyed just at the end of the MainWindow constructor.

Replace by:

TopWidget * tWidget = new TopWidget(this);

Also, you should replace your connect line by this one

connect(Button, &QPushButton::clicked, this, &TopWidget::testConnect);

It appears that your slot is called even after the TopWidget is destroyed. Qt normally disconnects connections when sender or receiver are destructed, but it's not able to do that when you connect to a lambda.

And finally, you are doing something weird. What is the purpose of your TopWidget class besides just creating another widget and receiving signals on its slot? You never add the TopWidget to any layout, but just its child. TopWidget is never shown, so it should rather derive from QObject only.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
galinette
  • 8,896
  • 2
  • 36
  • 87
  • I did that, and it gave me the error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say '&TopWidget::testConnect' [-fpermissive]. I then changed it to connect(Button, &QPushButton::clicked, this, &TopWidget::testConnect); and I never got the "Button worked" message. – TakingItCasual Dec 04 '15 at 12:36
  • A note to others who see this, my first comment was made before the answer was edited to what it is now. You can safely ignore these comments. And instead of having TopWidget *tWidget in the constructor, it should be in the header file, just defining tWidget in the constuctor. – TakingItCasual Dec 04 '15 at 13:01