简体   繁体   中英

Qt C++ signals and slots did not fire

I have programmed Qt a couple of times already and I really like the signals and slots feature. But now, I guess I'm having a problem when a signal is emitted from one thread, the corresponding slot from another thread is not fired. The connection was made in the main program.

This is also my first time to use Qt for ROS which uses CMake. The signal fired by the QThread triggered their corresponding slots but the emitted signal of my class UserInput did not trigger the slot in tflistener where it supposed to. I have tried everything I can. Any help? The code is provided below.

Main.cpp

#include <QCoreApplication>
#include <QThread>
#include "userinput.h"
#include "tfcompleter.h"
int main(int argc, char** argv)
{

    QCoreApplication app(argc, argv);

    QThread *thread1 = new QThread();
    QThread *thread2 = new QThread();
    UserInput *input1 = new UserInput();
    TfCompleter *completer = new TfCompleter();

    QObject::connect(input1, SIGNAL(togglePause2()), completer, SLOT(toggle()));
    QObject::connect(thread1, SIGNAL(started()), completer, SLOT(startCounting()));
    QObject::connect(thread2, SIGNAL(started()), input1, SLOT(start()));
    completer->moveToThread(thread1);
    input1->moveToThread(thread2);

    thread1->start();
    thread2->start();
    app.exec();
    return 0;
}

What I want to do is.. There are two seperate threads. One thread is for the user input. When the user enters [space], the thread emits a signal to toggle the boolean member field of the other thread. The other thread 's task is to just continue its process if the user wants it to run, otherwise, the user does not want it to run. I wanted to grant the user to toggle the processing anytime that he wants, that's why I decided to bring them into seperate threads.

The following codes are the tflistener and userinput.

tfcompleter.h

#ifndef TFCOMPLETER_H
#define TFCOMPLETER_H

#include <QObject>
#include <QtCore>

class TfCompleter : public QObject
{
    Q_OBJECT

private:
    bool isCount;

public Q_SLOTS:
    void toggle();
    void startCounting();

};

#endif

tflistener.cpp

#include "tfcompleter.h"
#include <iostream>

void TfCompleter::startCounting()
{
    static uint i = 0;

    while(true)
    {
        if(isCount)
            std::cout << i++ << std::endl;

    }
}
void TfCompleter::toggle()
{

//    isCount = ~isCount;
    std::cout << "isCount " << std::endl;

}

UserInput.h

#ifndef USERINPUT_H
#define USERINPUT_H

#include <QObject>
#include <QtCore>

class UserInput : public QObject
{
    Q_OBJECT

public Q_SLOTS:
    void start();   // Waits for the keypress from the user and emits the corresponding signal.

public:
Q_SIGNALS:
    void togglePause2();

};

#endif

UserInput.cpp

#include "userinput.h"
#include <iostream>
#include <cstdio>

// Implementation of getch
#include <termios.h>
#include <unistd.h>

/* reads from keypress, doesn't echo */
int getch(void)
{
    struct termios oldattr, newattr;
    int ch;
    tcgetattr( STDIN_FILENO, &oldattr );
    newattr = oldattr;
    newattr.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
    ch = getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
    return ch;
}


void UserInput::start()
{

    char c = 0;
    while (true)
    {
        c = getch();

        if (c == ' ')
        {
            Q_EMIT togglePause2();
            std::cout << "SPACE" << std::endl;
        }
        c = 0;
    }
}

Here is the CMakeLists.txt. I just placed it here also since I don't know maybe the CMake has also a factor here.

CMakeLists.txt

##############################################################################
# CMake
##############################################################################

cmake_minimum_required(VERSION 2.4.6)

##############################################################################
# Ros Initialisation
##############################################################################

include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
set(CMAKE_AUTOMOC ON)
#set the default path for built executables to the "bin" directory
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#set the default path for built libraries to the "lib" directory
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

# Set the build type.  Options are:
#  Coverage       : w/ debug symbols, w/o optimization, w/ code-coverage
#  Debug          : w/ debug symbols, w/o optimization
#  Release        : w/o debug symbols, w/ optimization
#  RelWithDebInfo : w/ debug symbols, w/ optimization
#  MinSizeRel     : w/o debug symbols, w/ optimization, stripped binaries
#set(ROS_BUILD_TYPE Debug)

##############################################################################
# Qt Environment
##############################################################################

# Could use this, but qt-ros would need an updated deb, instead we'll move to catkin
# rosbuild_include(qt_build qt-ros) 
rosbuild_find_ros_package(qt_build)
include(${qt_build_PACKAGE_PATH}/qt-ros.cmake)

rosbuild_prepare_qt4(QtCore) # Add the appropriate components to the component list here
ADD_DEFINITIONS(-DQT_NO_KEYWORDS)
##############################################################################
# Sections
##############################################################################

#file(GLOB QT_FORMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ui/*.ui)
#file(GLOB QT_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} resources/*.qrc)
file(GLOB_RECURSE QT_MOC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS include/rgbdslam_client/*.hpp)

#QT4_ADD_RESOURCES(QT_RESOURCES_CPP ${QT_RESOURCES})
#QT4_WRAP_UI(QT_FORMS_HPP ${QT_FORMS})
QT4_WRAP_CPP(QT_MOC_HPP ${QT_MOC})

##############################################################################
# Sources
##############################################################################

file(GLOB_RECURSE QT_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS src/*.cpp)

##############################################################################
# Binaries
##############################################################################

rosbuild_add_executable(rgbdslam_client ${QT_SOURCES} ${QT_MOC_HPP})
#rosbuild_add_executable(rgbdslam_client ${QT_SOURCES} ${QT_RESOURCES_CPP} ${QT_FORMS_HPP} ${QT_MOC_HPP})
target_link_libraries(rgbdslam_client ${QT_LIBRARIES})

It is hard to find bug from your posted code. I just want to point out one issue:

You are first making the connection and then moving the objects to new threads. Since they were created in same thread they had same Thread Affinity . So by default the connection type will be direct , which means, the slot will be executed from the same thread from which signal is emitted.

But after moving to new threads, thread affinity for both objects gets changed. Although you did not say how you could find out it is not working, I recommend to look at this matter. If you expect the slot to be executed in different thread, and tested that way, then you may not get desired output and think it is not working.

When signal and slot are meant to be executed in different thread, it is better to connect them after moving the corresponding objects to new threads. Qt::AutoConnection will by default use Qt::QueuedConnection when objects are in different thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM