Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Alternative signal and slot crashes if there is a widget to update in the slot



  • For weeks I have been trying to make qt signal work, however I can only get the following work:
    1-same class (widget to function)
    example:

    connect(button1, &QPushButton::clicked, this, &SecondForm::function1);
    

    2-lambda capture list, same class (widget to function or widget to widget)
    example:

    connect(ui->pushButton, &QPushButton::clicked, [this]() {
         //same class function
         function2();
       //or
         ui->pushButton->setText("Some text");
    
        });
    

    3-lambda capture list (different class, custom event )
    example:

            QObject::connect(ui->label_4, &ClickableLabels::clicked, [this]() {
    
                ui->stackedWidget_2->setCurrentIndex(2);
            });
    

    My problem is I can't get (function to function) work
    for example: I have a signal in class B and slot in class A, I want to emit the signal in class B after a button is clicked or on function execute, so it will execute the slot in class A and update something. Let's consider class A as MainWindow. I have tried all sort of connect, but the signal won't emit except if I place the emit in the main.cpp, however it won't update anything and I wouldn't want to place there. and it doesn't give any error.

    The solution I found, but not worked as expected. I have been looking for solutions and I found this SigSlot (Sigslot is a header-only, thread safe implementation of signal-slots for C++. The main goal was to replace Boost.Signals2. etc). This is easy, while qt signal and slot, connecting two functions from different classes that one happens to be the MainWindow has never worked for me. But SigSlot worked out of the box. The problem with this solution is: If the slot doesn't have anything in the function or it only has something to print like qDebug() it will run. However, if it has a widget to update like ui->pushButton->setText("SomeText") it will crash. I don't understand what is the relation between the signal/slot and the ui, if the emit works and function runs then why not update widget, but crash.

    If someone has knowledge on this I would appreciate any help. I will post a simple example bellow. Thanks

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    #include "secondform.h"
    #include <QMainWindow>
    #include <QObject>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    public slots:
        void sigRunFunction(QString text);
    
    private slots:
        void on_pushButton_clicked();
    
    private:
        SecondForm *captureWig;
        Ui::MainWindow *ui;
    };
    
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
    
    }
    
    void MainWindow::sigRunFunction(QString text){
    
        //uncomment the "ui->pushButton->setText("Some text")" and it will crash,
        //I want to update multiple widgets after the signal is emitted,
        //but it won't update.
    
        //ui->pushButton->setText("Some text");
        qDebug() <<"Signal Received" << text;
    }
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    
    void MainWindow::on_pushButton_clicked()
    {
        captureWig = new SecondForm(this);
        captureWig->bringfunction();
        QVBoxLayout *sendWig = new QVBoxLayout;
        sendWig = captureWig->getTreeto();
    
        QWidget *w = new QWidget();
        w->setLayout(sendWig);
    
        ui->tabWidget->addTab(w, "Newtab");
    }
    
    

    secondform.h
    signal.hpp file

    #ifndef SECONDFORM_H
    #define SECONDFORM_H
    #include "signal.hpp"
    //#include "qt.hpp"
    #include <QObject>
    #include <QWidget>
    #include <QtWidgets>
    #include <QEvent>
    #include <QLabel>
    
    namespace Ui {
    class SecondForm;
    }
    
    class SecondForm : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit SecondForm(QWidget *parent = nullptr);
        ~SecondForm();
        QVBoxLayout *getTreeto();
        QPushButton *button_first;
    
        void bringfunction(){
    
            multb();
                           }
    
      sigslot::signal<QString> sig;
    
    signals:
    
    private:
        void multb();
        void test();
        Ui::SecondForm *ui;
    };
    
    #endif // SECONDFORM_H
    
    

    secondform.cpp

    #include "secondform.h"
    #include "ui_secondform.h"
    #include "mainwindow.h"
    
    
    
    SecondForm::SecondForm(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::SecondForm)
    {
        ui->setupUi(this);
        
    //   MainWindow *w = new Mainwindow(); It won't run
    //   MainWindow w; It won't run
       MainWindow *w;
       sig.connect(&MainWindow::sigRunFunction, w);
    
    
    }
    
    QVBoxLayout *SecondForm::getTreeto(){
    
        return ui->verticalLayout_6;
    }
    
    void SecondForm::multb(){
    
        for (char letter = 'A'; letter <= 'M'; ++letter) {
    
            std::string s;
            s += letter;
            QString qstr = QString::fromStdString(s);
            button_first = new QPushButton(qstr);
            ui->horizontalLayoutfv->addWidget(button_first);
    
            connect(button_first, &QPushButton::clicked, this, &SecondForm::test);
    
       }
    
     }
    
    void SecondForm::test(){
        QPushButton* buttonSender = qobject_cast<QPushButton*>(sender());
        QString buttonText = buttonSender->text();
        qDebug() << "buttonSender" << buttonText;
        emit sig(buttonText);
    }
    
    SecondForm::~SecondForm()
    {
        delete ui;
    }
    
    

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
    
        QApplication a(argc, argv);
    
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    
    
    

    mainwindow.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>657</width>
        <height>663</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralWidget">
       <layout class="QVBoxLayout" name="verticalLayout">
        <item>
         <widget class="QTabWidget" name="tabWidget"/>
        </item>
        <item>
         <widget class="QPushButton" name="pushButton">
          <property name="text">
           <string>add</string>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menuBar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>657</width>
         <height>21</height>
        </rect>
       </property>
      </widget>
      <widget class="QToolBar" name="mainToolBar">
       <attribute name="toolBarArea">
        <enum>TopToolBarArea</enum>
       </attribute>
       <attribute name="toolBarBreak">
        <bool>false</bool>
       </attribute>
      </widget>
      <widget class="QStatusBar" name="statusBar"/>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>
    
    

    secondform.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>SecondForm</class>
     <widget class="QWidget" name="SecondForm">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>765</width>
        <height>508</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Form</string>
      </property>
      <widget class="QWidget" name="verticalLayoutWidget_2">
       <property name="geometry">
        <rect>
         <x>10</x>
         <y>30</y>
         <width>279</width>
         <height>164</height>
        </rect>
       </property>
       <layout class="QVBoxLayout" name="verticalLayout_6">
        <item>
         <widget class="QGroupBox" name="groupBox">
          <property name="title">
           <string>GroupBox</string>
          </property>
          <layout class="QVBoxLayout" name="verticalLayout_2">
           <item>
            <layout class="QHBoxLayout" name="horizontalLayoutfv"/>
           </item>
           <item>
            <layout class="QVBoxLayout" name="verticalLayout4"/>
           </item>
          </layout>
         </widget>
        </item>
       </layout>
      </widget>
     </widget>
     <resources/>
     <connections/>
    </ui>
    
    

  • Lifetime Qt Champion

    Hi
    The code is a bit confusing.
    You create a SecondForm from your exiting MainWindow

    void MainWindow::on_pushButton_clicked()
    {
        captureWig = new SecondForm(this);
        captureWig->bringfunction();
        QVBoxLayout *sendWig = new QVBoxLayout;
        sendWig = captureWig->getTreeto();
    
        QWidget *w = new QWidget();
        w->setLayout(sendWig);
    
        ui->tabWidget->addTab(w, "Newtab");
    }
    

    But in SecondForm, you seem to try to create yet another mainwindow which is not the one that created SecondForm and
    not shown on screen.

    SecondForm::SecondForm(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::SecondForm)
    {
        ui->setupUi(this);
        
    //   MainWindow *w = new Mainwindow(); It won't run --> it wont be the window u already have but new one
    //   MainWindow w; It won't run -> no as this runs out at scope and get deleted
       MainWindow *w; // this is dangling pointer 
       sig.connect(&MainWindow::sigRunFunction, w);
    }
    

    So do you want to talk to the existing mainwindow or do u really mean to make a new instance that then get the signal.

    If you mean to talk back to the existing window, you MUST use that instance and not go and create new ones.

    A good place would be in

    void MainWindow::on_pushButton_clicked()
    {
        captureWig = new SecondForm(this);
       connect ( SecondForm, THESIGNAL, this, SomeSlot in your MainWindow);
       
    

    if this in what you want.

    the https://github.com/palacaze/sigslot
    is completed not needed.
    Qt has anything you need for this to work. You just need to do it correctly :)


  • Lifetime Qt Champion

    Hi
    The code is a bit confusing.
    You create a SecondForm from your exiting MainWindow

    void MainWindow::on_pushButton_clicked()
    {
        captureWig = new SecondForm(this);
        captureWig->bringfunction();
        QVBoxLayout *sendWig = new QVBoxLayout;
        sendWig = captureWig->getTreeto();
    
        QWidget *w = new QWidget();
        w->setLayout(sendWig);
    
        ui->tabWidget->addTab(w, "Newtab");
    }
    

    But in SecondForm, you seem to try to create yet another mainwindow which is not the one that created SecondForm and
    not shown on screen.

    SecondForm::SecondForm(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::SecondForm)
    {
        ui->setupUi(this);
        
    //   MainWindow *w = new Mainwindow(); It won't run --> it wont be the window u already have but new one
    //   MainWindow w; It won't run -> no as this runs out at scope and get deleted
       MainWindow *w; // this is dangling pointer 
       sig.connect(&MainWindow::sigRunFunction, w);
    }
    

    So do you want to talk to the existing mainwindow or do u really mean to make a new instance that then get the signal.

    If you mean to talk back to the existing window, you MUST use that instance and not go and create new ones.

    A good place would be in

    void MainWindow::on_pushButton_clicked()
    {
        captureWig = new SecondForm(this);
       connect ( SecondForm, THESIGNAL, this, SomeSlot in your MainWindow);
       
    

    if this in what you want.

    the https://github.com/palacaze/sigslot
    is completed not needed.
    Qt has anything you need for this to work. You just need to do it correctly :)



  • @Ucn_ said in Alternative signal and slot crashes if there is a widget to update in the slot:

    : I have a signal in class B and slot in class A, I want to emit the signal in class B after a button is clicked or on function execute, so it will execute the slot in class A and update something.

    The idea with signals and slots is that a class B will emit a signal for whatever a reason, without knowing or caring what other class with make use of such event. Think of the signal as a radio station broadcast, it will broadcast whether there are thousands, one or no radio receivers turned on.

    So for a slot in another class A to be called when the signal is fired, that class A is responsible for making the proper "wiring", i.e. to connect signal from B to its desired slot. BUT for this to happen, class A should have an object B "visible", i.e. within scope.

    So as @mrjj already mentioned, the connect() is wrong placed, you need to make the connection in class MainWindows where the slot is defined and you have a SecondForm object available. Please pay attention to @mrjj suggestion for MainWindow::on_pushButton_clicked() method...



  • @mrjj and @Pablo-J-Rogina thanks for the help. This worked.

    void MainWindow::on_pushButton_clicked()
    {
        captureWig = new SecondForm(this);
        QObject::connect(captureWig, &SecondForm::clicked, this, &MainWindow::someSlotW);
    

    You are right about creating at the wrong place and having many instances.


Log in to reply