3

QPluginLoader 機能を使用してプラグイン (.dll/.so) をロードする C++/Qt アプリケーションがあります。このプラグインは基本的に組み込みの Python インタープリターで、PyQt4 モジュールを介してメイン アプリケーションで Qt オブジェクトを検査できます。

問題は、プラグインされた Python インタープリターから実行されるコマンド PyQt4.QtCore.QCoreApplication.instance() が、C++ アプリケーションによって QCoreApplication インスタンスが作成されていても None を返すことです。

これは、デバッグ モードの Windows でのみ発生します。

Linux または Windows のリリース モードでは、コマンド PyQt4.QtCore.QCoreApplication.instance() は、C++ アプリケーションによって作成された QCoreApplication インスタンスを正しく返します。

以下は、問題を示す最小限のコードです。

リリース モードでコンパイルした場合:

$ ./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()

=> OK ではない (None を返す)

ファイル 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)
4

1 に答える 1

0

解説は以下。

デバッグ バージョンでは、C++ アプリケーションとプラグインは Qt ライブラリ (QtCored4.dll など) をデバッグするためにリンクしますが、インストールされた PyQt4 モジュール (QtCore.pyd など) は Qt ライブラリ (QtCore4.dll など) をリリースするためにリンクします。

C++ アプリケーションと PyQt4 モジュールは、それぞれ QCoreApplication インスタンスを認識しますが、それぞれ別のライブラリ (Qt ライブラリのデバッグ バージョンとリリース バージョン) に存在する別のインスタンスを認識します。

PyQt4 モジュールに存在するインスタンスは、C++ アプリケーションが初期化されるときに初期化されないため、null になります。

これは、PyQt4 モジュールをデバッグ Qt ライブラリにリンクするように指定してコンパイルすることで確認できます。デバッグ モードの C++ アプリケーションと PyQt4 モジュールが同じ Qt ライブラリにリンクすると、問題はなくなります。

于 2013-06-18T21:35:36.043 に答える