Results 1 to 6 of 6

Thread: Call Slot from QtScript with Qt::QueuedConnection

  1. #1
    Join Date
    Jul 2015
    Location
    Innsbruck
    Posts
    3

    Default Call Slot from QtScript with Qt::QueuedConnection

    I have to provide a "linear" scripting interface that runs infinitely next to the GUI, which would block my eventloop. So I am running a QScriptEngine in a seperate thread (beeing aware of the consequences). There is one thing that keeps me puzzled: Usually using multiple threads the communication is safe as long as you use signals and slots, passing objects by value and connecting with Qt::QueuedConnection, right?
    So now I want to call a slot of an object which I made available within the scriptengine. The Documentation states:

    Emitting Signals from Scripts

    To emit a signal from script code, you simply invoke the signal function, passing the relevant arguments:

    Qt Code:
    1. myQObject.somethingChanged("hello");
    To copy to clipboard, switch view to plain text mode 
    It is currently not possible to define a new signal in a script; i.e., all signals must be defined by C++ classes.
    But in the Debugger I can see, that the calls are executed on the scriptengine thread instead of the object's owner thread. I found the workaround by calling
    Qt Code:
    1. QMetaObject::invokeMethod(object,"slotname",Qt::QueuedConnection)
    To copy to clipboard, switch view to plain text mode 
    in the slot, but i don't want to modify all objects i want to make accessible from within the scriptengine.
    Is there a way to force the scriptengine to use QueuedConnection for calling slots?

    I have also tried setProcessEventsInterval() for the engine without starting another thread, but couldn't get it to work. Further i don't want the Eventloop to be pumped by the script engine, feels creepy.
    Thanks, Lukas

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Call Slot from QtScript with Qt::QueuedConnection

    Can you describe your setup a bit more?

    That "myQObject", was it created by the script or created in C++ and passed into the script?
    Is it owned by the thread running the script engine?
    Where did you connect the signal?
    Which thread owns the receiver object?

    Cheers,
    _

  3. #3
    Join Date
    Jul 2015
    Location
    Innsbruck
    Posts
    3

    Default Re: Call Slot from QtScript with Qt::QueuedConnection

    the myObject line is from the Documentation.

    in my case it is a QTextEdit (inherited by Console) created in the GUI thread and registered in the scriptEngine (which is in the script thread).

    Mainwindow.cpp:
    Qt Code:
    1. console = new Console(this);
    2. script = new ScriptThread(this);
    3. script->attachConsole(console);
    To copy to clipboard, switch view to plain text mode 

    console.h:
    Qt Code:
    1. #ifndef CONSOLE_H
    2. #define CONSOLE_H
    3.  
    4. #include <QWidget>
    5. #include <QTextEdit>
    6.  
    7. Q_DECLARE_METATYPE(QTextCursor)
    8. Q_DECLARE_METATYPE(QTextCharFormat)
    9.  
    10. class Console : public QTextEdit
    11. {
    12. Q_OBJECT
    13.  
    14. public:
    15. explicit Console(QWidget *parent = 0);
    16. signals:
    17.  
    18. public slots:
    19. void write(QString string);
    20. void write(QString string, QString color);
    21. void clr();
    22.  
    23. };
    24.  
    25. #endif // CONSOLE_H
    To copy to clipboard, switch view to plain text mode 
    scriptthread.h:
    Qt Code:
    1. #ifndef SCRIPTTHREAD_H
    2. #define SCRIPTTHREAD_H
    3.  
    4. #include <QThread>
    5. #include <QtScript/QScriptEngine>
    6. #include <QPushButton>
    7. #include "console.h"
    8.  
    9. class ScriptThread : public QThread
    10. {
    11. Q_OBJECT
    12. public:
    13. explicit ScriptThread(QObject *parent = 0);
    14.  
    15. void run() Q_DECL_OVERRIDE;
    16.  
    17. void addScriptableObject(QString name, QObject* obj);
    18. void attachConsole(Console* console);
    19.  
    20. signals:
    21. void set(QString name, QVariant value);
    22. void scriptFinished();
    23.  
    24. public slots:
    25.  
    26. private:
    27. QScriptEngine* scriptEngine;
    28. QString code;
    29. Console* m_console;
    30. QScriptContext* ctx;
    31. };
    32.  
    33. #endif // SCRIPTTHREAD_H
    To copy to clipboard, switch view to plain text mode 

    console.cpp:
    Qt Code:
    1. Console::Console(QWidget *parent) :
    2. QTextEdit(parent)
    3. {
    4. qRegisterMetaType<QTextCursor>("QTextCursor");
    5. qRegisterMetaType<QTextCharFormat>("QTextCharFormat");
    6. setReadOnly(true);
    7. setFontFamily("monospace");
    8. setStyleSheet("QTextEdit { background-color: 'black'; }");
    9. setTextColor(QColor("white"));
    10. }
    11.  
    12. void Console::write(QString string)
    13. {
    14. setTextColor(QColor("White"));
    15. append(string);
    16. //QMetaObject::invokeMethod(this,"setTextColor",Qt::QueuedConnection,Q_ARG(QColor,QColor("white")));
    17. //QMetaObject::invokeMethod(this,"append",Qt::QueuedConnection,Q_ARG(QString, string));
    18. }
    19.  
    20. void Console::write(QString string, QString color)
    21. {
    22. setTextColor(QColor(color));
    23. append(string);
    24. //QMetaObject::invokeMethod(this,"setTextColor",Qt::QueuedConnection,Q_ARG(QColor,QColor(color)));
    25. //QMetaObject::invokeMethod(this,"append",Qt::QueuedConnection,Q_ARG(QString, string));
    26. }
    27.  
    28. void Console::clr()
    29. {
    30. //textEdit->clear();
    31. QMetaObject::invokeMethod(this,"clear",Qt::QueuedConnection);
    32. }
    To copy to clipboard, switch view to plain text mode 

    script.cpp:
    Qt Code:
    1. ScriptThread::ScriptThread(QObject *parent) :
    2. QThread(parent)
    3. {
    4. code = "console.write(\"testing console color capabilities\");\n"
    5. "button.text = \"This is a scriptable Button!\";\n"
    6. "button.show();\n"
    7. "for(i=0;i<100;i++){\n"
    8. "time.sleep_ms(20);console.write(i+\"%\")}\n"
    9. "console.write(\"OK\",\"red\");\n"
    10. "time.sleep_ms(1000);\n"
    11. "//console.clr();\n"
    12. "button.hide();"
    13. "console.write(\"exiting\");\n";
    14. scriptEngine = new QScriptEngine();
    15.  
    16. ctx = scriptEngine->pushContext();
    17. }
    18.  
    19. void ScriptThread::run()
    20. {
    21. if (m_console != NULL)
    22. {
    23. if (scriptEngine->canEvaluate(code))
    24. {
    25. m_console->write("Script seems to be ok, starting...\n");
    26. scriptEngine->evaluate(code);
    27. if (scriptEngine->hasUncaughtException())
    28. {
    29. m_console->write("Error at: " + scriptEngine->uncaughtException().toString());
    30. }
    31. } else {
    32. m_console->write("Script contains errors!\n");
    33. }
    34. scriptEngine->popContext();
    35. scriptEngine->collectGarbage();
    36. }
    37. }
    38.  
    39. void ScriptThread::addScriptableObject(QString name, QObject *obj)
    40. {
    41. ctx->activationObject().setProperty(name, scriptEngine->newQObject(obj));
    42.  
    43. }
    44.  
    45. void ScriptThread::attachConsole(Console *console)
    46. {
    47. m_console = console;
    48. ctx->activationObject().setProperty("console",scriptEngine->newQObject(m_console));
    49. }
    To copy to clipboard, switch view to plain text mode 

    In the console.cpp I set breakpoints inside the Console::write() slot. As far as I understood slots are directly called from the script as mentioned in the documentation, there is no "emit" command in javascript.
    As a workaround I changed the thread on which the slot is called by manually invoking the slot with:
    Qt Code:
    1. QMetaObject::invokeMethod(this,"setTextColor",Qt::QueuedConnection,Q_ARG(QColor,QColor("white")));
    To copy to clipboard, switch view to plain text mode 
    which works as expected. But i would assume that methods declared as slots are automatically invoked by the scriptengine instead of being called directly, especially since there is no emit function! Did i understand something wrong about the idea of the scriptengine?

    Thanks, Lukas

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Call Slot from QtScript with Qt::QueuedConnection

    Emit is not a function in C++ either, it is a preprocessor macro that expands to nothing. I.e. it is only there for improved readability, to distinguish between method calls and signal emits.

    So you are not emitting any signal at all, you are directly calling a slot.
    The feature you've described in your first post, i.e. the automatic Qt::QueuedConnection on cross-thread signal/slot connections, is for exactly that: signal/slot connections.

    Your setup is quite problematic actually:

    Your script engine object belongs to the main thread but you are calling its evaluate() method (and others) from another thread.
    Better have the script engine and all objects it works on be part of the thread that runs it.

    You are also calling methods on a widget (m_console) from a non-main-thread.
    Either call the methods via QMetaObject::invoke using Qt::QueuedConnection or emit a signal and let the cross-thread signal/slot connect handle that.

    Cheers,
    _

  5. #5
    Join Date
    Jul 2015
    Location
    Innsbruck
    Posts
    3

    Default Re: Call Slot from QtScript with Qt::QueuedConnection

    Thanks, I think I am starting to understand the problem. I will create the engine in the script thread and try again. Can the objects registered in the engine be from another thread and is their registering threadsafe (at least while the engine is not evaluating anything)? Will their methods be properly invoked then or will the engine do direct calls when accessing member functions within the script?
    Thanks for the explainations.

  6. #6
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Call Slot from QtScript with Qt::QueuedConnection

    Quote Originally Posted by lukasfischer View Post
    Thanks, I think I am starting to understand the problem. I will create the engine in the script thread and try again. Can the objects registered in the engine be from another thread and is their registering threadsafe (at least while the engine is not evaluating anything)?
    I personally wouldn't risk registering objects belonging to another thread but it might work if you do it beforehand.

    Quote Originally Posted by lukasfischer View Post
    Will their methods be properly invoked then or will the engine do direct calls when accessing member functions within the script?
    I don't think that script method invocation would stop working, they should be called just like when you call them from C++.

    Cheers,
    _

Similar Threads

  1. Call object method using qtscript
    By mvbhavsar in forum Newbie
    Replies: 4
    Last Post: 10th March 2013, 10:19
  2. Replies: 0
    Last Post: 22nd February 2011, 07:55
  3. Why is not possible call slot with parameters?
    By somename in forum Qt Programming
    Replies: 3
    Last Post: 28th May 2010, 13:17
  4. How to call painEvent for a slot
    By Jyoti.verma in forum Qt Programming
    Replies: 8
    Last Post: 25th August 2009, 09:49
  5. who call my slot?
    By mattia in forum Newbie
    Replies: 1
    Last Post: 28th November 2007, 12:44

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.