Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Help with timer and JavaScript slot.
Forum Updated to NodeBB v4.3 + New Features

Help with timer and JavaScript slot.

Scheduled Pinned Locked Moved Solved General and Desktop
43 Posts 4 Posters 5.4k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K KroMignon
    30 Dec 2021, 09:32

    @SPlatten I am always surprised how complicated you are trying to solve common issues.

    What is the real issue here?
    You want to create a timer in a specific thread and start it. I am right?
    QTimer muss be started, and stopped from the owned thread.
    That's not so complicated to solve:

    auto myTimer = new  QTimer();
    connect(myTimer; &QTimer::timeout, ...);
    // move to thread
    myTimer->moveToThread(theWorkingThread);
    
    // start the timer
    QTimer::singleShot(0, myTimer, [myTimer]() { myTimer->start(); } );
    
    S Offline
    S Offline
    SPlatten
    wrote on 30 Dec 2021, 09:33 last edited by SPlatten
    #41

    @KroMignon , thank you, will try this now, right now before trying your suggestion the output is:

    S000000000027E000000000715T09:32:03.904DL00000202Fsimon2.js[void clsScriptHelper::log]
    setupForm() !!!!
    S000000000028E000000000715T09:32:03.904DL00000207Fsimon2.js[void clsScriptHelper::log]
    setupForm() !!!!
    S000000000029E000000003405T09:32:06.594W:QObject::startTimer: Timers cannot be started from another thread
    

    @KroMignon, thank you so much, this now works, here are the prototypes:

    Q_INVOKABLE void createTimer(const QString& crstrID, const uint cuintInterval
                                ,const QString& crstrFunction);
    Q_INVOKABLE void startTimer(const QString& crstrID);
    

    And the final implementations:

    /**
     * @brief clsScriptHelper::createTimer
     * @param crstrID : Constant reference to Timer ID
     * @param cuintInterval : Constant millisecond interval
     * @param crstrFunction : Constant reference to JSfile@Function
     */
    void clsScriptHelper::createTimer(const QString& crstrID, const uint cuintInterval
                                     ,const QString& crstrFunction) {
        if ( crstrID.trimmed().isEmpty() == true || cuintInterval == 0
          || crstrFunction.trimmed().isEmpty() == true
          || crstrFunction.indexOf(clsXMLnode::msccScriptDelimiter) == -1 ) {
        //One or more of the parameters are invalid, do nothing
            return;
        }
        //Get Application instance
        clsMainWnd* pAppInstance(clsMainWnd::pobjGetAppWnd());
        if ( pAppInstance == nullptr ) {
        //Cannot get application instance!
            return;
        }
        //Look for registered timer
        QTimer* ptmrScript(pAppInstance->ptmrGetTimer(crstrID));
        if ( ptmrScript != nullptr ) {
        //Already registered, do nothing
            return;
        }
        //No timer locatd with that ID, its safe to proceed
        QStringList slstScript = crstrFunction.split(clsXMLnode::msccScriptDelimiter);
        mpScripts* pmpScriptsMap;
        if ( slstScript.length() == SSF_PARAM_COUNT
        && (pmpScriptsMap = clsMainWnd::pmpGetScriptsMap()) != nullptr ) {
        //Look for the script in the scripts map
            mpScripts::iterator itrScript(pmpScriptsMap->find(slstScript[SFF_FILE_IDX]));
            if ( itrScript != pmpScriptsMap->end() ) {
        //Found it, build the script up ready for the signal connection
                QString strScript(itrScript->second);
        //Get pointer to script engine
                QJSEngine* pobjScriptEng(clsJSON::pobjGetScriptEng());
        //Create new timer
                ptmrScript = new QTimer();
                ptmrScript->setInterval(cuintInterval);
        //Register the new timer using the supplied ID
                pAppInstance->registerTimer(ptmrScript, crstrID);
        //Add any globals to the script
                clsScriptHelper::pobjGetInstance()->addGlobals(pobjScriptEng);
        //Build up script function call
                QString strFile(slstScript[SFF_FILE_IDX])
                       ,strCall(slstScript[SSF_FUNCTION_IDX])
                       ,strScriptWithCall(strScript)
                       ,strFunc(strCall
                              + QString(clsDebugService::msccBrktOpen)
                              + QString(clsDebugService::msccBrktClose)
                              + QString(clsXMLnode::msccScriptFunctionTerminator));
                 if ( strScriptWithCall.endsWith(clsXMLnode::msccScriptFunctionTerminator) != true ) {
                     strScriptWithCall += clsXMLnode::msccScriptFunctionTerminator;
                 }
                 strScriptWithCall += strFunc;
        //Create connection to timer and script
                 QMetaObject::Connection cn = QObject::connect(ptmrScript
                                                             ,&QTimer::timeout
                                                             ,pobjScriptEng
                                                             ,[pobjScriptEng
                                                              ,ptmrScript
                                                              ,strFile
                                                              ,strFunc
                                                              ,strScriptWithCall]() {
                     QJSValue objResult(pobjScriptEng->evaluate(strScriptWithCall));
                     QString strError;
                     if ( objResult.isError() == true ) {
                         strError = QString("%1\n%2\n%3").arg(strFile).arg(strFunc)
                                                         .arg(objResult.toString());
                     } else {
                         QString strResult(objResult.toString());
                         if ( strResult.compare("undefined") != 0 ) {
                             strError = strResult;
                         }
                     }
                     if ( strError.isEmpty() != true ) {
                         clsXMLnode* pobjRoot(clsXMLnode::spobjGetRoot());
                         emit pobjRoot->error(strError);
                         ptmrScript->deleteLater();
                     }
                });
                QThread* pobjScriptEngThread(pobjScriptEng->thread());
                ptmrScript->moveToThread(pobjScriptEngThread);
            }
        }
    }
    /**
     * @brief clsScriptHelper::startTimer
     * @param crstrID : Constant reference to timer ID
     */
    void clsScriptHelper::startTimer(const QString& crstrID) {
        //Get Application instance
        clsMainWnd* pAppInstance(clsMainWnd::pobjGetAppWnd());
        if ( pAppInstance == nullptr ) {
        //Cannot get application instance!
            return;
        }
        QTimer* ptmrScript(pAppInstance->ptmrGetTimer(crstrID));
        if ( ptmrScript != nullptr ) {
            QTimer::singleShot(0, ptmrScript, [ptmrScript]() { ptmrScript->start(); } );
        }
    }
    

    Kind Regards,
    Sy

    K 1 Reply Last reply 30 Dec 2021, 09:37
    0
    • S SPlatten
      30 Dec 2021, 09:31

      @kshegunov I would provide the call stack, but execution doesn't halt, so where would you like me to break to then get the stack?

      K Offline
      K Offline
      kshegunov
      Moderators
      wrote on 30 Dec 2021, 09:34 last edited by
      #42

      @SPlatten said in Help with timer and JavaScript slot.:

      I would provide the call stack, but execution doesn't halt, so where would you like me to break to then get the stack?

      Before doing the connect for the timer timeout. Provide the following things (in one post please):

      1. The whole source of the enclosing function.
      2. The call stack.
      3. The output of this added just before doing the connect:
      qDebug() << ptmrScript->thread() << QThread::currentThread() << ptmrScript->parent();
      

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      0
      • S SPlatten
        30 Dec 2021, 09:33

        @KroMignon , thank you, will try this now, right now before trying your suggestion the output is:

        S000000000027E000000000715T09:32:03.904DL00000202Fsimon2.js[void clsScriptHelper::log]
        setupForm() !!!!
        S000000000028E000000000715T09:32:03.904DL00000207Fsimon2.js[void clsScriptHelper::log]
        setupForm() !!!!
        S000000000029E000000003405T09:32:06.594W:QObject::startTimer: Timers cannot be started from another thread
        

        @KroMignon, thank you so much, this now works, here are the prototypes:

        Q_INVOKABLE void createTimer(const QString& crstrID, const uint cuintInterval
                                    ,const QString& crstrFunction);
        Q_INVOKABLE void startTimer(const QString& crstrID);
        

        And the final implementations:

        /**
         * @brief clsScriptHelper::createTimer
         * @param crstrID : Constant reference to Timer ID
         * @param cuintInterval : Constant millisecond interval
         * @param crstrFunction : Constant reference to JSfile@Function
         */
        void clsScriptHelper::createTimer(const QString& crstrID, const uint cuintInterval
                                         ,const QString& crstrFunction) {
            if ( crstrID.trimmed().isEmpty() == true || cuintInterval == 0
              || crstrFunction.trimmed().isEmpty() == true
              || crstrFunction.indexOf(clsXMLnode::msccScriptDelimiter) == -1 ) {
            //One or more of the parameters are invalid, do nothing
                return;
            }
            //Get Application instance
            clsMainWnd* pAppInstance(clsMainWnd::pobjGetAppWnd());
            if ( pAppInstance == nullptr ) {
            //Cannot get application instance!
                return;
            }
            //Look for registered timer
            QTimer* ptmrScript(pAppInstance->ptmrGetTimer(crstrID));
            if ( ptmrScript != nullptr ) {
            //Already registered, do nothing
                return;
            }
            //No timer locatd with that ID, its safe to proceed
            QStringList slstScript = crstrFunction.split(clsXMLnode::msccScriptDelimiter);
            mpScripts* pmpScriptsMap;
            if ( slstScript.length() == SSF_PARAM_COUNT
            && (pmpScriptsMap = clsMainWnd::pmpGetScriptsMap()) != nullptr ) {
            //Look for the script in the scripts map
                mpScripts::iterator itrScript(pmpScriptsMap->find(slstScript[SFF_FILE_IDX]));
                if ( itrScript != pmpScriptsMap->end() ) {
            //Found it, build the script up ready for the signal connection
                    QString strScript(itrScript->second);
            //Get pointer to script engine
                    QJSEngine* pobjScriptEng(clsJSON::pobjGetScriptEng());
            //Create new timer
                    ptmrScript = new QTimer();
                    ptmrScript->setInterval(cuintInterval);
            //Register the new timer using the supplied ID
                    pAppInstance->registerTimer(ptmrScript, crstrID);
            //Add any globals to the script
                    clsScriptHelper::pobjGetInstance()->addGlobals(pobjScriptEng);
            //Build up script function call
                    QString strFile(slstScript[SFF_FILE_IDX])
                           ,strCall(slstScript[SSF_FUNCTION_IDX])
                           ,strScriptWithCall(strScript)
                           ,strFunc(strCall
                                  + QString(clsDebugService::msccBrktOpen)
                                  + QString(clsDebugService::msccBrktClose)
                                  + QString(clsXMLnode::msccScriptFunctionTerminator));
                     if ( strScriptWithCall.endsWith(clsXMLnode::msccScriptFunctionTerminator) != true ) {
                         strScriptWithCall += clsXMLnode::msccScriptFunctionTerminator;
                     }
                     strScriptWithCall += strFunc;
            //Create connection to timer and script
                     QMetaObject::Connection cn = QObject::connect(ptmrScript
                                                                 ,&QTimer::timeout
                                                                 ,pobjScriptEng
                                                                 ,[pobjScriptEng
                                                                  ,ptmrScript
                                                                  ,strFile
                                                                  ,strFunc
                                                                  ,strScriptWithCall]() {
                         QJSValue objResult(pobjScriptEng->evaluate(strScriptWithCall));
                         QString strError;
                         if ( objResult.isError() == true ) {
                             strError = QString("%1\n%2\n%3").arg(strFile).arg(strFunc)
                                                             .arg(objResult.toString());
                         } else {
                             QString strResult(objResult.toString());
                             if ( strResult.compare("undefined") != 0 ) {
                                 strError = strResult;
                             }
                         }
                         if ( strError.isEmpty() != true ) {
                             clsXMLnode* pobjRoot(clsXMLnode::spobjGetRoot());
                             emit pobjRoot->error(strError);
                             ptmrScript->deleteLater();
                         }
                    });
                    QThread* pobjScriptEngThread(pobjScriptEng->thread());
                    ptmrScript->moveToThread(pobjScriptEngThread);
                }
            }
        }
        /**
         * @brief clsScriptHelper::startTimer
         * @param crstrID : Constant reference to timer ID
         */
        void clsScriptHelper::startTimer(const QString& crstrID) {
            //Get Application instance
            clsMainWnd* pAppInstance(clsMainWnd::pobjGetAppWnd());
            if ( pAppInstance == nullptr ) {
            //Cannot get application instance!
                return;
            }
            QTimer* ptmrScript(pAppInstance->ptmrGetTimer(crstrID));
            if ( ptmrScript != nullptr ) {
                QTimer::singleShot(0, ptmrScript, [ptmrScript]() { ptmrScript->start(); } );
            }
        }
        
        K Offline
        K Offline
        KroMignon
        wrote on 30 Dec 2021, 09:37 last edited by
        #43

        @SPlatten said in Help with timer and JavaScript slot.:

        right now before trying your suggestion the output is:

           setupForm() !!!!
           S000000000029E000000003405T09:32:06.594W:QObject::startTimer: Timers cannot be started from another thread
        

        Same issue, you are trying to start a QTimer from the wrong thread.
        QTimer::singleShot(0, myTimer, [myTimer]() { myTimer->start(); } ); will solve this.

        It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

        1 Reply Last reply
        0

        41/43

        30 Dec 2021, 09:33

        • Login

        • Login or register to search.
        41 out of 43
        • First post
          41/43
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved