简体   繁体   中英

Qt slots called from QtScript in a threaded application

I've made my application scriptable by creating a derived QThread class, where I add multiples QObject class in order to access their functions via the command line.

void commandLine::addObject(QObject *obj, QString name)
{
  QScriptValue sv = m_scriptEngine.newQObject(obj);
  m_scriptEngine.globalObject().setProperty(name, sv);

  qObjectMap.insert(std::pair<QString, QObject *>(name, obj));
}

After the run() call, the class enter in an infinite loop, using m_scriptEngine to evaluate every command line entered.

In my (simplified) main I do :

simuCLI cli;
simuCore core;
cli.addObject(&core, "core");
simuUI ui;
connect(&ui, SIGNAL(start()), &core, SLOT(start()));

But when I call start() from my GUI and from my script, results are differents

My application architecture is like the following :

Core -> StateMachine -> Machine -> Communication

Start from UI works great.

Start from command line execute the code, but don't launch the QStateMachine and it emit signals, but never receive them.

Communication sends commands to Machine by emiting signals received in Machine . It works if I've call core::start() from my UI If I call core::start() using my command lines signal is emited but never received.

void WL::WL()
{
  std::cout << "wl cst" << std::endl;
  mLectMotStateMachine.setInitialState(sNoCard);
  mLectMotStateMachine.start();
}
void WL::enterNoCard()
{
  std::cout << "no card" << std::endl;
}

Ouput :

start() from UI :

wl cst
no card

start() from command line :

wl cst

As you can see, the state machine never enter in it first state, like it never launch.

So my questions are :

1 - In wich thread is executing start() if I call it from command line ?

2 - How can I debug signals ? ( best answer I've found )

3 - Is there a way to see every Signals connection at a time 't' on execution

4 - How can I know in which thread I am on execution ?

5 - Have you any ideas of why my code isn't working only when I use the command line ?

Your problem is that it is an error to directly call thread-unsafe methods in the objects that reside in other threads. I presume that the simuCore object is a QThread and creates a bunch of other objects in its thread. Those objects cannot be accessed from other threads directly unless you make their methods thread safe (you demonstrably don't).

The answers are:

  1. You can check it. Within the start() method, add:

     qDebug() << QThread::currentThread(); 

    You'll see that it's the same thread as qApp->thread() , also known as the main thread or the gui thread.

  2. I don't know what you mean by debugging signals. The signal-slot mechanism obviously works, so what is there to debug?

  3. Why would you need that? You're the one who is making (and breaking) the connections, so you can certainly add debug output at the point where you make and break those connections. No magic to it.

  4. QThread::currentThread() .

  5. Because you're:

    • Calling thread-unsafe methods from threads other than the thread where the object resides.

    • Deriving from QThread when you shouldn't.

    You should, instead, simply move the script engine object to a plain dedicated QThread (no subclassing), and use thread-safe method invocation to evaluate the command lines. Signal-slot connections are thread-safe, as well as QMetaObject::invokeMethod .

For example:

#include <QCoreApplication>
#include <QScriptEngine>
#include <QThread>

class ScriptEngine : public QScriptEngine {
   Q_OBJECT
   Q_SIGNAL void evaluateSignal(const QString &);
public:
   Q_SLOT void evaluate(const QString & str) { QScriptEngine::evaluate(str); }
   /// A thread-safe evaluate()
   Q_SLOT void safeEvaluate(const QString & str) { emit evaluateSignal(str); }
   explicit ScriptEngine(QObject * parent = 0) : QScriptEngine(parent) {
      connect(this, &ScriptEngine::evaluateSignal, this, &ScriptEngine::evaluate);
   }
};

class Thread : public QThread {
   // A thread that's safe to destruct, like it ought to be
   using QThread::run; // final
public:
   ~Thread() { quit(); wait(); }
};

int main(int argc, char ** argv) {
  QCoreApplication app(argc, argv);
  ScriptEngine engine;
  Thread worker;
  engine.globalObject().setProperty("qApp", engine.newQObject(qApp));
  engine.moveToThread(&worker);
  worker.start();

  QMetaObject::invokeMethod(&engine, "evaluate", Q_ARG(QString, "print('Hi!')"));
  engine.safeEvaluate("print('And hello!')");
  engine.safeEvaluate("qApp.quit()");
  return app.exec();
}

#include "main.moc"

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