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

Signal no being emitted from within slot



  • Hello!

    I am having a problem with a signal that's not being emitted from within a slot. I got the same signal being emmitted from another method and it works fine, but if I try to emmit the same signal from other part of the code, it simply does not work.

    I have 3 classes:

    OrderHandler:

    #ifndef ORDERHANDLER_H
    #define ORDERHANDLER_H
    
    #include <QObject>
    #include <QJsonDocument>
    #include <QJsonObject>
    #include <QVariantMap>
    #include <QTimer>
    #include <QNetworkAccessManager>
    #include <QNetworkRequest>
    #include <QNetworkReply>
    #include <QSettings>
    #include <QUrl>
    #include <QtCore/QDebug>
    
    #include "order.h"
    #include "dbhandler.h"
    
    
    //typedef QList<Order> OrderList;
    
    
    
    #define ORDERS_URL "http://usereserva.dyndns.org:8082/ReservaQuiosqueAPI/api/LojaPedido/ObterListaPedidos/2017-10-31"
    #define STATUS_URL "http://usereserva.dyndns.org:8082/ReservaQuiosqueAPI/api/LojaPedido/DefinirStatusPedido/"
    
    
    
    
    class OrderHandler : public QObject
    {
        Q_OBJECT
    public:
        explicit OrderHandler(QObject *parent = 0);
        ~OrderHandler();
        void  getNewOrders(void);
        void  processJsonOrder(QString);
        void  setOrderStatus(quint32, quint32);
    
        void storeOrder(Order *order) {
            orderList.append(order);
        }
    
        Order *getOrder(qint32 idx) {
            return orderList.at(idx);
        }
    
        size_t getOrderListSize() {
            return orderList.size();
        }
    
    
    
    
    private:
        QTimer                *timer;
        DbHandler     		  *dbhandler;
        QNetworkAccessManager *manager;
        QNetworkRequest       *request;
    
        OrderList orderList;
    
        enum PayStatus {
            NOT_PAID   = 10,
            PAID       = 20
        };
    
        enum ProdStatus {
            PENDING    = 10,
            PRODUCTION = 30,
            EXPEDITION = 40 //FINISHED
        };
    
        //INTERNAL CONTROL
    
        enum TermStatus {
            NOT_PRINT = 10,
            PRINT     = 20,
            COLLECTED = 30
        };
    
        enum DeliveryStatus {
            NOT_COLLECTED = 10,
            COLLECT       = 20,
            REJECTED      = 30,
            CANCELLED     = 50
        };
    
    
    public Q_SLOTS:
        bool checkNewOrders(void);
        void replyFinished (QNetworkReply *);
        bool receiveOrder(Order *);
    
    
    Q_SIGNALS:
        bool newOrderAvailable(Order *);
    
    
    };
    
    #endif // ORDERHANDLER_H
    

    This class emmits the signal newOrderAvailable(Order *) that is connected to the main window.

    MainWin:

    connect(order_handler, &OrderHandler::newOrderAvailable, this, &MainWin::addOrder);
    

    This is the MainWindow header file:

    #ifndef MAINWIN_H
    #define MAINWIN_H
    
    #include <QMainWindow>
    #include <QMessageBox>
    #include <QTableWidget>
    #include <QListWidgetItem>
    #include <QTextStream>
    #include <QFile>
    #include <QPrinter>
    #include <QDialog>
    #include <QPrintDialog>
    #include <QTextDocument>
    #include <QDebug>
    #include <QKeyEvent>
    #include <QSignalSpy>
    
    
    //#include "requestserver.h"
    #include "orderhandler.h"
    #include "order.h"
    //#include "dbhandler.h"
    
    
    namespace Ui {
    class MainWin;
    }
    
    class MainWin : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWin(QWidget *parent = 0);
        ~MainWin();
    
    private:
    
        Ui::MainWin   *ui;
        OrderHandler  *order_handler;
    
        //bool event(QEvent *event);
        enum Column {
            COD_ORDER            = 0,
            CLIENT_NAME          = 1,
            PAY_STATUS           = 2,
            NUM_ITEM_ORDER       = 3,
            PROD_STATUS          = 4,
            TERM_STAUS           = 5,
            DELIVERY_STATUS      = 6,
            PRED_DELIVERY_STATUS = 7
        };
    
        void addOrderFormmated(QTableWidget *, int, int, QTableWidgetItem *);
        void itemSelected(QListWidgetItem *);
        void printTerm(void);
    
    protected:
    
    
    public slots:
        void rowSelected(int, int);
        bool addOrder(Order*);
    };
    
    #endif // MAINWIN_H
    
    

    When I emmit newOrderAvailable from OrderHandler::processJsonOrder, it works fine, but I when I emmit the same signal from receiveOrder(Order *), the main windows does no receive it:

    bool OrderHandler::receiveOrder(Order *order) {
        qDebug() << "Order Received: " << order->id << order->client_name << order->delivery_status;
        storeOrder(order);
        emit newOrderAvailable(order);
    
        return true;
    }
    
    
    

    On the main window, the signal and slot is connected:

    connect(order_handler, &OrderHandler::newOrderAvailable, this, &MainWin::addOrder);
    

    Any help is appreciated! Thaks :D


  • Lifetime Qt Champion

    Hi
    Just to be sure , botch cases are in same class just different functions?
    And no threads involved ?

    Also is receiveOrder(Order *) involved in any kind of loop and processJsonOrder is not ?



  • Hi!
    Yes, both cases are in the same class, OrderHandler, no threads involved.
    processJsonOrder emmits the newOrderAvailable(Order*) signal from a for-loop, but receiveOrder has no loops. It only receives a signal from another class (DbHandler) and reemmits the received Order*.

    connect(dbhandler, &DbHandler::pendingOrders, this, &OrderHandler::receiveOrder);
    

    In DbHandler, I have a loop that emmits pendingOrders(Order *) and works fine, since the SLOT from OrderHandler is being called. The problem is when this SLOT tries to emmit the newOrderAvailable SIGNAL, nothing happens :(


  • Lifetime Qt Champion

    Hi
    Sounds very odd. ( as it so equal in use)
    So the working case have a for loop but the non working has not :)

    Could you try with

    connect(dbhandler, &DbHandler::pendingOrders, this, &OrderHandler::receiveOrder, Qt::DirectConnection);

    Ps. slots are just normal c++ member functions so it should be any different to emit from there than any place else.



  • @mrjj said in Signal no being emitted from within slot:

    connect(dbhandler, &DbHandler::pendingOrders, this, &OrderHandler::receiveOrder, Qt::DirectConnection);

    Yes, really odd, since it's working from another call. I tried your advice but nothing changed :( Take a look at the following code.
    emit newOrderAvailable(newOrder) works fine here and MainWin receives the data.

    void OrderHandler::processJsonOrder(QString orderRawData) {
    
        QJsonDocument jsonOrderData  = QJsonDocument::fromJson(orderRawData.toLocal8Bit());
    
        if(jsonOrderData.isNull()){
            qDebug() << "Failed to create JSON doc.";
            return;
        }
    
        if(!jsonOrderData.isObject()){
            qDebug() << "JSON is not an object.";
            return;
        }
    
        QJsonObject jsonObj = jsonOrderData.object();
    
        if (jsonObj.isEmpty()) {
            qDebug() << "JSON object is empty.";
            return;
        }
    
        QVariantMap   rootMap       = jsonObj.toVariantMap();
        QVariantList  infoResultado = rootMap["infoResultado"].toList();
    
        for (int i = 0; i < infoResultado.size(); i++) { //iterate over a list of orders
            QVariantMap orderInfo = infoResultado.at(i).toMap();
    
            quint32 orderId     = orderInfo["idPedido"].toInt();
            quint16 orderStatus = orderInfo["status"].toInt();
    
            bool orderExists = dbhandler->orderExists(orderId);
    
            if ((!orderExists) && (orderStatus == PayStatus::NOT_PAID)) {
                //qDebug() << "PEDIDO NOVO";
    
               //INSERT ORDER INTO DATABASE FROM HERE
                qDebug() << "Pedido: " << orderId;
    
                QVariantMap clientInfo = orderInfo["cliente"].toMap();
                qDebug() << "Cliente: " << clientInfo["nome"].toString().trimmed();
    
                QVariantList itemList = orderInfo["listaItensPreVenda"].toList();
    
                Order *newOrder = new Order(this);
    
                newOrder->id                   = orderId;
                newOrder->client_name          = clientInfo["nome"].toString().trimmed();
                newOrder->cpf                  = clientInfo["cpf"].toString();
                newOrder->payment_status       = PayStatus::NOT_PAID;
                newOrder->n_uniq_item          = itemList.size(); //numero itens unicos contidos no pedido
                newOrder->prod_status          = ProdStatus::PENDING;
                newOrder->term_status          = TermStatus::NOT_PRINT;
                newOrder->delivery_status      = DeliveryStatus::NOT_COLLECTED;
                //newOrder->pred_delivery_status = DeliveryStatus::NOT_COLLECTED;
    
                for(int j = 0; j < itemList.size(); j++) {
                    Item *newItem = new Item();
                    QVariantMap item = itemList.at(j).toMap();
                    QImage img, imgMini;
    
                    newItem->orderId       = newOrder->id;
                    newItem->prodCode      = item["codigoProduto"].toInt();
                    newItem->prodName      = item["descricao"].toString();
                    newItem->prodColorName = item["corDescricao"].toString();
                    newItem->prodSize      = item["grade"].toString();
                    newItem->numItens      = item["quantidade"].toInt();
    
                    //img.loadFromData(QByteArray::fromBase64(item["estampa"].toByteArray()));
                    //newItem->itemImg = img; //store big image
                    //qDebug() << "Image: " << item["miniatura"].toByteArray();
                    imgMini.loadFromData(QByteArray::fromBase64(item["miniatura"].toByteArray()), "PNG");
                    newItem->itemImgMini = imgMini; //store mini image
                    newOrder->addItem(newItem);
                }
    
                if(!dbhandler->insertOrder(newOrder))
                    return;
                else
                    storeOrder(newOrder);
    
                qDebug() << "Num. Itens: " << itemList.size();
                emit newOrderAvailable(newOrder);
    
            }
            else if (orderExists && (orderStatus == PayStatus::PAID) && (dbhandler->getPaymentStatus(orderId) == PayStatus::NOT_PAID)) {
                if (dbhandler->setPaymentStatus(orderId, PayStatus::PAID))
                    qDebug() << "Order " << orderId << " updated by POS and set to PAID";
            }
    
        }
    }
    
    

    Now, from bool OrderHandler::receiveOrder(Order *order), it does not emmit nothing or at least MainWin does not show anything.

    bool OrderHandler::receiveOrder(Order *order) {
        qDebug() << "Order Received: " << order->id << order->client_name << order->delivery_status;
        storeOrder(order);
        emit newOrderAvailable(order);
    
        return true;
    }
    

  • Lifetime Qt Champion

    ok
    That is odd. Must be related to a signal call the slot and in the slot we call emit but
    i dont see why. when using DirectConnection it should work like a function call.

    So whom send the signal that calls bool OrderHandler::receiveOrder(Order *order) ?

    As i understand it , the only difference is that
    in processJsonOrder, we emit directly newOrderAvailable .
    where as OrderHandler::receiveOrder is triggered via signal and then emit the newOrderAvailable ?
    Is that correctly understood?

    It seems this sends the signal ?
    connect(dbhandler, &DbHandler::pendingOrders, this, &OrderHandler::receiveOrder);
    can you try with QueuedConnection ? ( should not be needed. just testing if makes difference)



  • DbHandler calls bool OrderHandler::receiveOrder(Order *order) . Yes that'st the flow, you got it.

    DbHandler emits pendingOrder(Order *), OrderHandler receives the signal from DbHandler and emits newOrderAvailable(Order *) signal to MainWin::addOrder(Order *);

    connect(dbhandler, &DbHandler::pendingOrders, this, &OrderHandler::receiveOrder, Qt::DirectConnection);
    
    bool DbHandler::loadPendingOrders() {
    
        QSqlQuery orderQuery, itemQuery;
    
        // 10 == NOT_COLLECTED
        orderQuery.prepare("SELECT * from order_info WHERE delivery_status=10");
    
        if(!orderQuery.exec()) {
            qDebug() << orderQuery.lastError();
            return false;
        }
    
        int id              = orderQuery.record().indexOf("id");
        int client_name     = orderQuery.record().indexOf("client_name");
        int cpf             = orderQuery.record().indexOf("cpf");
        int n_uniq_itens    = orderQuery.record().indexOf("n_uniq_itens");
        int pag_status      = orderQuery.record().indexOf("pag_status");
        int prod_status     = orderQuery.record().indexOf("prod_status");
        int term_status     = orderQuery.record().indexOf("term_status");
        int delivery_status = orderQuery.record().indexOf("delivery_status");
    
        while (orderQuery.next()) {
            Order *order = new Order(this);
    
            order->id                   = orderQuery.value(id).toUInt();
            order->client_name          = orderQuery.value(client_name).toString();
            order->cpf                  = orderQuery.value(cpf).toString();
            order->payment_status       = orderQuery.value(pag_status).toUInt();
            order->n_uniq_item          = orderQuery.value(n_uniq_itens).toUInt();
            order->prod_status          = orderQuery.value(prod_status).toUInt();
            order->term_status          = orderQuery.value(term_status).toUInt();
            order->delivery_status      = orderQuery.value(delivery_status).toUInt();
    
            //qDebug() << "Load order from database: " << pendingOrder->id;
    
            //qDebug() << pendingOrder;
    
            emit pendingOrders(order);
        }
        return true;
    }
    

  • Lifetime Qt Champion

    Hi
    There must be something we are missing in regards to emit from a slot while
    a signal is in progress. ( i have not had this situation and it seems perfectly normal)

    I should be able to reproduce it if have
    a mainwindow and some widget i use for floating window
    that i show using ->show()
    in constructor (of floating window) i emit test("hello");

    and in the floating window i have a button and in its clicked, i also
    emit test("hello 2");

    Mainwindow should not get "hello 2" if something up.

    Does that sounds like same usage as you ?



  • Yes, looks like a similar case.
    Can I post my code somewhere? This way would be easier to debug or even see if the code has an explict error I'm not spotting.


  • Lifetime Qt Champion

    @Gabriel-Duarte
    Ok. super. I must do some shopping then i will try.
    You can use use google drive/dropbox and post link here (with zip file) or
    use https://www.filemail.com/
    ( you dont have to use real email adresses. just grab the URL in last page)
    and post url here. ( it expires in 6 days)

    Update:
    Tried a mock up example but in both cases it was emitted.

    The full source and data files would really help to spot it.


  • Lifetime Qt Champion

    Hi
    I asked for help/input and was suggested
    "my best guess is his DbHandler::loadPendingOrders simply is not called. make him break at the OrderHandler::receiveOrder at the qDebug statement and to pull the stack trace."

    Thats a good plan.

    Also full source code could also help to find out
    what goes wrong.
    It should work as far as i can see and understand from your code and
    explanations of the setup.



  • @mrjj REALLY thanks for your attention!
    You will post the source code.

    DbHandler::loadPendingOrders is being called, for sure, because my list of Orders is being increased and if I call qDebug() << "something" inside it, I can see some output coming from there but emit newOrderAvailable().

    emit pendingOrders(order) is being emitted from the method DbHandler:loadPendingOrders() at each order it gets from database.

    DbHandler->loadePendingOrders() is begin called inside OrderHandler constructor right after this:

    connect(dbhandler, &DbHandler::pendingOrders, this, &OrderHandler::receiveOrder, Qt::DirectConnection);
    
    dbhandler->loadPendingOrders();
    



  • Lifetime Qt Champion

    Hi
    Found it. ( i think) Logical error :)
    Would not have seen it without full code.
    In
    OrderHandler::OrderHandler(QObject *parent) : QObject(parent) {
    you hook up
    connect(dbhandler, &DbHandler::pendingOrders, this, &OrderHandler::receiveOrder, Qt::DirectConnection);
    // and call it
    dbhandler->loadPendingOrders();

    But since its inside the constructor of orderHandler,

    MainWin::MainWin(QWidget *parent) :
    {
    order_handler = new OrderHandler(this);// << loadPendingOrders() called here
    connect(order_handler, &OrderHandler::newOrderAvailable, this, &MainWin::addOrder); // but signal first hooked up here !

    so that is why nothing seems to happen.



  • @mrjj THANK YOU SO MUCH!

    Dumb logical error, thanks a lot for helping me out, for your attention and patience!


  • Lifetime Qt Champion

    @Gabriel-Duarte
    Well it was a sneaky one and easy to make.
    In fact, i mention as test case to emit in constructor and that
    would been same mistake.
    And you are welcome. happy saturday.


Log in to reply