Qt插槽在线程应用程序中从QtScript调用

时间:2014-07-17 09:36:16

标签: c++ multithreading qt signals-slots qthread

我通过创建派生的QThread类使我的应用程序可编写脚本,我在其中添加了多个QObject类,以便通过命令行访问它们的函数。

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));
}

在run()调用之后,类进入无限循环,使用m_scriptEngine来评估输入的每个命令行。

在我的(简化)主要内容中:

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

但是当我从我的GUI和我的脚本调用start()时,结果是不同的

我的应用程序架构如下:

  

核心 - &gt; StateMachine - &gt;机器 - &gt;通信

从UI开始工作很棒。

从命令行开始执行代码,但不启动QStateMachine并发出信号,但从不接收它们。

Communication通过发出Machine中收到的信号向Machine发送命令。如果我从我的UI调用core :: start(),它就可以工作 如果我使用命令行调用core :: start(),则信号会被激活但从未接收过。

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

输出:

来自UI的

start():

wl cst
no card
从命令行

start():

wl cst

正如您所看到的,状态机永远不会进入第一个状态,就像它从未启动一样。

所以我的问题是:

1 - 如果我从命令行调用它,那么线程正在执行start()吗?

2 - 如何调试信号? (best answer I've found

3 - 有没有办法在执行时't'看到每个信号连接

4 - 我怎么知道我在执行哪个帖子?

5 - 您是否知道我的代码仅在我使用命令行时无效?

1 个答案:

答案 0 :(得分:1)

您的问题是,在驻留在其他线程中的对象中直接调用线程不安全的方法是错误的。我假设simuCore对象是QThread并在其线程中创建了一堆其他对象。这些对象不能直接从其他线程访问,除非你让它们的方法线程安全(你明显没有)。

答案是:

  1. 你可以检查一下。在start()方法中,添加:

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

    你会看到它与qApp->thread()的线程相同,也称为主线程或gui线程。

  2. 我不知道调试信号是什么意思。信号槽机制显然有效,那么有什么可以调试的呢?

  3. 你为什么需要那个?你是那个正在建立(和破坏)连接的人,所以你当然可以在你创建和断开这些连接的位置添加调试输出。没有魔力。

  4. QThread::currentThread()

  5. 因为你是:

    • 从对象所在的线程以外的线程调用线程不安全的方法。

    • 当你不应该从QThread派生。

    您应该简单地将脚本引擎对象移动到普通的专用QThread(无子类),并使用线程安全方法调用来评估命令行。信号插槽连接是线程安全的,以及QMetaObject::invokeMethod

  6. 例如:

    #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"