Как запустить разные экземпляры QApplication в разных потоках - PullRequest
0 голосов
/ 16 марта 2020

У меня есть приложение Qt, которое должно загружать плагины. Когда плагин загружен (я использую boost :: dll), я вызываю метод фабрики плагинов для извлечения класса с последующим (упрощенным) интерфейсом:

#ifdef _WIN32
using WindowHandle = HWND;
#else
using WindowHandle = void*;
#endif

class PluginInterface {
public:

  /**
   * @brief Retrieve the window handle of the plugin.
   *
   * The plugin should have a single main window, that's the main window of the
   * plugin itself. The method retrieve this window handler and pass it to the
   * caller, in order to allow it to attach this window to its main gui
   * interface. The pointer ownership is on the plugin, so the caller mustn't
   * delete it for any reason.
   *
   * @return Window handle.
   */
  virtual WindowHandle getWindowHandle() const = 0;
};

Интерфейс позволяет получить HWND-указатель на окно, которое затем прикрепляю к заявке. Основная идея c заключается в создании с моим приложением контейнера, в который можно загружать и запускать другие библиотеки.

В плагине я реализую этот интерфейс, создав еще одно приложение QApplication. Я создаю предварительную декларацию, чтобы при загрузке плагина приложение ничего не знало о том, что плагин использует Qt, создавая непрозрачный указатель:

class Application;

class MyPluginInterface final : public PluginInterface {
public:

  MyPluginInterface();
  virtual ~MyPluginInterface();

public:
  WindowHandle getWindowHandle() const override;

private:

  Application* m_application;
};


////// CPP file

#include "Application.hpp"

MyPluginInterface::MyPluginInterface() :
  PluginInterface() {
  m_application = new Application();
}

MyPluginInterface ::~MyPluginInterface () {

}

WindowHandle MyPluginInterface ::getWindowHandle() const {
  return m_application->getWindowHandle();
}

Application создает QApplication и QMainWindow, тогда я запускаю QApplication::exec() в другом потоке. Затем я создаю метод для извлечения дескриптора HWND для QMainWindow, который должен быть передан через интерфейс плагина к приложению-контейнеру, который затем будет отображать его.

#ifndef APPLICATION_HPP_
#define APPLICATION_HPP_

#include "WindowHandle.hpp"
#include <QApplication>
#include <QMainWindow>
#include <memory>
#include <thread>

class Application final {
public:

  Application();
  ~Application();
  WindowHandle getWindowHandle() const;

private:

  void createMainWindow();

private:

  std::unique_ptr<QApplication> m_application;
  std::thread m_applicationThread;
  QMainWindow* m_mainWindow;
};

#endif // !APPLICATION_HPP_

/////////////// CPP FILE

#include "Application.hpp"
#include <qpa/qplatformnativeinterface.h>
#include <QLabel>


///////////////////////////////////////////////////////////////////////////////
// USING SECTION                                                             //
///////////////////////////////////////////////////////////////////////////////

using WindowHandle;
using std::thread;

///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION                                                            //
///////////////////////////////////////////////////////////////////////////////

Application::Application() {
  int argc = 1;
  char* argv[] = { "plugin" };
  QCoreApplication::addLibraryPath("./");
  m_application = std::make_unique<QApplication>(argc, argv);
  m_applicationThread = thread([this]() {
    createMainWindow();
    m_application->exec();
  });
}

Application::~Application() {
  if (m_applicationThread.joinable()) {
    m_applicationThread.join();
  }
}

WindowHandle Application::getWindowHandle() const {
  WindowHandle handle{ nullptr };
  if (QWindow* w = m_mainWindow->windowHandle()) {
    auto nativeInterface = QGuiApplication::platformNativeInterface();
    handle = static_cast<HWND>(nativeInterface->nativeResourceForWindow(QByteArrayLiteral("handle"), w));
  }
  return handle;
}

///////////////////////////////////////////////////////////////////////////////
// PRIVATE SECTION                                                           //
///////////////////////////////////////////////////////////////////////////////

void Application::createMainWindow() {
  m_mainWindow = new QMainWindow();
  QLabel* label = new QLabel(m_mainWindow);
  label->setText("Questo e' un plugin");
  m_mainWindow->setCentralWidget(label);
  m_mainWindow->show();
}

Когда я запускаю приложение, контейнер загружает плагин, но затем у меня есть утверждение в Application.cpp, в следующей строке:

m_application = std::make_unique<QApplication>(argc, argv);

Утверждение говорит:

ASSERT failure in QCoreApplication: "there should be only one application object", file H:\...\corelib\kernel\qcoreapplication.cpp, line 792

Поскольку приложение контейнера делает теперь знаю что-нибудь о содержимом плагина (интерфейс не предоставляет классы Qt и логи c) Я ожидал, что смогу запустить оба QApplication.

Что вызывает проблему и как я могу решить это?

...