У меня есть приложение C ++ / Qt, которое загружает плагин (.dll / .so), используя средства QPluginLoader.
Этот плагин по сути является встроенным интерпретатором Python, который позволяет проверять объекты Qt в основном приложении через модуль PyQt4.
Проблема в том, что команда PyQt4.QtCore.QCoreApplication.instance (), выполненная из подключенного интерпретатора python, возвращает None, даже если экземпляр QCoreApplication был создан приложением C ++.
Это только на окнах в режиме отладки.
В Linux или в режиме выпуска в Windows команда PyQt4.QtCore.QCoreApplication.instance () правильно возвращает экземпляр QCoreApplication, созданный приложением C ++.
Ниже приведен минималистский код, показывающий проблему.
При компиляции в режиме релиза:
$ ./a.out
1+1
2
import PyQt4
import PyQt4.QtCore
PyQt4.QtCore.QCoreApplication.instance()
<PyQt4.QtCore.QCoreApplication object at 0x00C69198>
=> Хорошо
При компиляции в режиме отладки:
$ ./a.out
import PyQt4
import PyQt4.QtCore
PyQt4.QtCore.QCoreApplication.instance()
=> Не в порядке (возвращается Нет)
Файл main.cpp
#include <QCoreApplication>
#include <QPluginLoader>
#include <QDebug>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QPluginLoader loader("plugin.dll");
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
loader.load();
if(!loader.isLoaded()) {
qDebug() << loader.errorString();
return 1;
}
(void)loader.instance();
return app.exec();
}
Файл plugin.h
#ifndef PLUGIN_H
#define PLUGIN_H
#include <QObject>
#include <Python.h>
class Plugin : public QObject
{
public:
Plugin();
~Plugin();
private:
PyThreadState *m_ts;
};
class InterpInput : public QObject
{
Q_OBJECT
public:
InterpInput(QObject *parent = 0) : QObject(parent) { }
public slots:
void monitorInput();
signals:
void done();
void inputReady();
};
class InterpOutput : public QObject
{
Q_OBJECT
public:
InterpOutput(QObject *parent = 0) : QObject(parent) { }
public slots:
void processLine();
public:
PyThreadState *m_ts;
};
#endif
Файл plugin.cpp
#include "plugin.h"
#include <QCoreApplication>
#include <QThread>
#include <QtPlugin>
#include <QPluginLoader>
Q_EXPORT_PLUGIN2(Plugin, Plugin)
Plugin::Plugin()
{
Py_Initialize();
PyEval_InitThreads();
InterpInput *in = new InterpInput();
InterpOutput *out = new InterpOutput(this);
in->connect(in, SIGNAL(inputReady()), out, SLOT(processLine()));
in->connect(in, SIGNAL(done()), QCoreApplication::instance(), SLOT(quit()));
QThread *thr = new QThread(this);
in->moveToThread(thr);
thr->connect(thr, SIGNAL(started()), in, SLOT(monitorInput()));
m_ts = PyEval_SaveThread();
out->m_ts = m_ts;
thr->start();
}
Plugin::~Plugin()
{
PyEval_RestoreThread(m_ts);
Py_Finalize();
}
void InterpInput::monitorInput()
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
int ret = PyRun_SimpleString("import sys\nimport code\nic = code.InteractiveConsole()");
assert(ret == 0);
while(true) {
ret = PyRun_SimpleString("line = ic.raw_input()");
if(ret) { break; }
inputReady();
}
done();
PyGILState_Release(gstate);
}
void InterpOutput::processLine()
{
PyEval_RestoreThread(m_ts);
int ret = PyRun_SimpleString("ic.push(line)");
PyRun_SimpleString("sys.stdout.flush()");
PyRun_SimpleString("sys.stderr.flush()");
(void)PyEval_SaveThread();
assert(ret == 0);
}
Файл Makefile
MOC=/cygdrive/c/Qt/4.8.4/bin/moc
GCC=/cygdrive/c/MinGW/bin/mingw32-g++.exe
FLAGS=-Ic:/Qt/4.8.4/include -Ic:/Qt/4.8.4/include/QtCore -Lc:/Qt/4.8.4/lib -Lc:/Qt/4.8.4/bin -lQtCore4 -Lc:/Python27/libs -lpython27 -Ic:/Python27/include -DQT_NO_DEBUG
#FLAGS=-Ic:/Qt/4.8.4/include -Ic:/Qt/4.8.4/include/QtCore -Lc:/Qt/4.8.4/bin -lQtCored4 -Lc:/Python27/libs -lpython27 -Ic:/Python27/include -g
LIBFLAGS=-shared
all:
$(MOC) plugin.h > plugin_moc.cpp
$(GCC) -o a.out main.cpp $(FLAGS)
$(GCC) -o plugin.dll $(LIBFLAGS) plugin.cpp plugin_moc.cpp $(FLAGS)
Объяснение следующее.
В отладочной версии приложение и плагин C ++ ссылаются на библиотеки отладки Qt (QtCored4.dll и т. Д.)
Принимая во внимание, что установленные модули PyQt4 (QtCore.pyd и т. Д.) Ссылаются на выпуск библиотек Qt (QtCore4.dll и т. Д.)
Из этого следует, что приложение C ++ и модуль PyQt4, каждый видит экземпляр QCoreApplication, но каждый видит свой экземпляр, который находится в другой библиотеке (соответственно, отладка и выпуск, версия библиотеки Qt).
Отсюда следует, что экземпляр, находящийся в модуле PyQt4, не инициализируется при инициализации приложения C ++ и, таким образом, является нулевым.
Это проверяется путем компиляции модулей PyQt4, указывающих, что они должны быть связаны с отладочными библиотеками Qt: проблема затем исчезает, когда приложение C ++ в режиме отладки и модули PyQt4 ссылаются на одну и ту же библиотеку Qt.
Других решений пока нет …