通过 python 控制台对 PythonQt 库进行非锁定调用

Posted

技术标签:

【中文标题】通过 python 控制台对 PythonQt 库进行非锁定调用【英文标题】:Non locking calls via python console for PythonQt library 【发布时间】:2013-01-03 15:16:12 【问题描述】:

我的 Qt 应用程序有一个 Qt gui(基本上是一些按钮和一个绘制数据的 opengl 上下文)。我还利用 PythonQt 类添加了可编写脚本。这些命令是从 PythonQtScriptingConsole 内部评估的。

我已经明确地创建了包装类和工厂方法,以通过控制台通过当前 python 上下文发送 C++ 调用,但是当从控制台内部运行长任务时,gui 冻结,因为(我认为)事件循环没有被处理.所以第一个解决方案是用计时器处理事件循环,但我认为这既慢又有点愚蠢,所以我不喜欢它。一个

有人提示吗? Python 全局解释器锁在这里有问题吗?

【问题讨论】:

我想你可以用QThreadblog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong解决这个问题 【参考方案1】:

是的,GUI 正在冻结,因为对 Python 的长时间调用是通过 UI 线程执行的。为了解决这个问题,我能够继承 QThread 并通过命令模式向 Python 模块发出命令。

在您开始使用以下类调用多个 Python 模块之前,请务必通过调用 PyEval_InitThreads() 来初始化 Python 中的线程支持,正如您将在我的 main() 函数中看到的那样。

祝你好运!

int main( int argc, char **argv ) 

        QApplication qapp(argc, argv);

        PyEval_InitThreads(); // IMPORTANT
        PythonQt::init(PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut);

        PythonQtObjectPtr module = PythonQt::self()->createUniqueModule();

        ThreadedPythonContext context(module);
        context.start();

        # issue some commands into the module
        context.issue("import sys");
        context.issue("sys.path.append('C:\\Python27\\Lib\\site-packages')");
        context.issue("import time");
        context.issue("last = time.localtime().tm_sec");

        // Release the global interpreter lock (if it has been created and thread support 
        // is enabled) and reset the thread state to NULL, returning the previous thread 
        // state (which is not NULL). If the lock has been created, the current thread must 
        // have acquired it. (This function is available even when thread support is 
        // disabled at compile time.)

        // give up control of the GIL
        PyThreadState *state = PyEval_SaveThread();

        return qapp.exec()

ThreadedPythonContext.h

#ifndef THREADEDPYTHONCONTEXT_H
#define THREADEDPYTHONCONTEXT_H

#include "PythonQt.h"

#include <QtCore/QMutexLocker>
#include <QtCore/QQueue>
#include <QtCore/QThread>
#include <QtCore/QWaitCondition>

class ThreadedPythonContext : public QThread 

    Q_OBJECT
public:
    ThreadedPythonContext(const PythonQtObjectPtr &context) : 
        QThread(), 
        _context(context), 
        _running(true)
    
    

    ~ThreadedPythonContext() 
        _running = false;
        wait();
    
    void issue(const QString &code) 
        _lock.lock();
        _commands.enqueue(code);
        _lock.unlock();

        _CommandQueued.wakeOne();
    

    bool isCommandQueueEmpty() 
        QMutexLocker lock(&_lock);
        return _commands.isEmpty();
    

protected:

    QString dequeue() 
        QMutexLocker lock(&_lock);
        QString cmd( _commands.dequeue() );

        return cmd.isEmpty() ? "\n" : cmd;
    

    void run() 

        QMutex signal;
        PyGILState_STATE state;

        while(_running) 

            // wait to be signaled ... 
            signal.lock();
            _CommandQueued.wait(&signal,1);
            signal.unlock();

            if ( isCommandQueueEmpty() ) 
                continue;
            

            while ( !isCommandQueueEmpty() ) 

                PythonQtObjectPtr p;
                PyObject* dict = NULL;

                state = PyGILState_Ensure();

                if (PyModule_Check(_context)) 
                    dict = PyModule_GetDict(_context);
                 else if (PyDict_Check(_context)) 
                    dict = _context;
                

                if (dict) 
                    // this command blocks until the code has completed execution
                    emit python_busy(true);
                    p.setNewRef(PyRun_String(dequeue().toLatin1().data(), Py_single_input, dict, dict));
                    emit python_busy(false);
                

                // error in the kernel
                if (!p) 
                    PythonQt::self()->handleError();
                   
                PyGILState_Release(state);
            
        
    

    PythonQtObjectPtr _context;

    QMutex _lock;
    QQueue<QString> _commands;

    QWaitCondition _CommandQueued;  
    bool _running;

signals:
    void python_busy(bool);
;

#endif //THREADEDPYTHONCONTEXT_H

【讨论】:

以上是关于通过 python 控制台对 PythonQt 库进行非锁定调用的主要内容,如果未能解决你的问题,请参考以下文章

Windows下PythonQt编译(vs2015+Qt5.11.2+PythonQt 3.2)

PythonQt 不打印任何东西

PythonQt 是不是已弃用?

Windows下PythonQt3.2使用pandas.pivot_table

python qt在后台等待热键

Windows下编译PythonQt3.2正确姿势