我的Qt应用程序有一个Qt gui(基本上是一些按钮和一个绘制数据的opengl上下文)。我还添加了利用PythonQt类的脚本性。这些命令是从PythonQtScriptingConsole内部评估的。
我已经显式创建了包装类和工厂方法,通过控制台通过当前的python上下文发送C ++调用,但是当从控制台内部运行长任务时,gui冻结,因为(我认为)事件循环未被处理。因此,第一个解决方案是使用计时器处理事件循环,但我认为这既慢又有点愚蠢,所以我不喜欢它。一个
有人提示吗? Python Global Interpreter Lock是否存在问题?
答案 0 :(得分:4)
是的,GUI正在冻结,因为通过UI线程正在执行对Python的长调用。为了解决这个问题,我能够通过命令模式对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