Создание плагина для расширения приложения Qt

Я решил переключить свое приложение проекта хобби (программу поиска по словарю) на архитектуру плагинов, чтобы в будущем можно было разрабатывать всевозможные словари для других языков. Приложение разработано в Visual C++ с использованием Qt (5.0.2). Я добавил этот заголовок в код приложения, чтобы определить интерфейс для плагинов словаря:

// dict_plugin.h
#ifndef DICT_PLUGIN_H
#define DICT_PLUGIN_H

#include <QtPlugin>

class PluginInterface
{
public:
    virtual ~PluginInterface() {}

    virtual QString language() const = 0;
    virtual class QWidget* ui() const = 0;
};

Q_DECLARE_INTERFACE(PluginInterface, "pl.ksmvision.winona.PluginInterface")

#endif // DICT_PLUGIN_H

Затем я создал новый проект из шаблона "Библиотека Qt" для самого плагина (используя надстройку Qt Visual Studio), который используется для создания библиотеки DLL. Основной заголовочный файл выглядит так:

#ifndef JP_PLUGIN_H
#define JP_PLUGIN_H

// created by the template to define Q_DECL_EXPORT 
// and _IMPORT macros but the plugin engine takes 
// care of that (I think)
//#include "jp_plugin_global.h"

#include <QObject>
#include <QtPlugin>

#include <dict_plugin.h>

class JpPlugin : public QObject, public PluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "pl.ksmvision.winona.JpPlugin")
    Q_INTERFACES(PluginInterface)

public:
    JpPlugin();
    virtual ~JpPlugin();

    virtual QString language() const;
    virtual QWidget* ui() const;
};

#endif // JP_PLUGIN_H

Когда я пытаюсь построить это, я получаю ошибку от moc в строке Q_INTERFACES, указывающей интерфейсы, которые должен реализовывать мой плагин:

3> ------ Началась сборка: Проект: jp_plugin, Конфигурация: Отладка Win32 ------
3> Moc'ing jp_plugin.h...
3> F: \ moje \ src \ cpp \ winona \ build \ jp_plugin \ jp_plugin.h (15): ошибка: неопределенный интерфейс
========== Построение: 2 успешно завершено, 1 не удалось, 2 обновлено, 0 пропущено ===========

Похоже, moc'ing происходит до того, как файл dict_plugin.h включен, потому что, когда я ввожу опечатку в имя включаемого файла, он не жалуется, что файл не существует, просто завершает сборку с тем же сообщением об ошибке о том, что интерфейс не определен.

Что я делаю неправильно? Благодарю.

2 ответа

Причиной сбоя moc было то, что объявление интерфейса было недоступно. Сбой директивы #include, поскольку файл не найден. Очевидно, moc может обрабатывать директивы #include самостоятельно, но не (по умолчанию?) Не печатает сообщение об ошибке или не останавливает обработку, если файл, который нужно включить, не может быть найден.

Причина, по которой файл заголовка с объявлением интерфейса не может быть найден, заключается в том, что пользовательские параметры сборки, вызывающие вызов moc, которые генерируются надстройкой Qt VS, не наследуют путь включения проекта. Мне удалось добавить требуемый путь вручную в командную строку moc, введя страницы свойств файла заголовка плагина, перейдя к Custom Build Tool->General->Command Line и добавив дополнительную опцию "-I..." в разделе конец. После этого moc обработал заголовок, и сборка прошла успешно.

Для тех, кто идет по этому пути, как я. Моя проблема была немного другой и была связана с пространствами имен. Я получал точно такую ​​же ошибку «неопределенный интерфейс», но разрешение пути на меня не повлияло.

У меня было что-то вроде следующего:

      namespace foo {

class Interface
{
    // ...
};

} // namespace foo

Q_DECLARE_INTERFACE(foo::Interface, "my.interface/1.0")

Неправильно

      namespace foo {

class Derived : public QObject, public Interface
{
    Q_OBJECT
    Q_INTERFACES(Interface)
};

}

Правильный

      namespace foo {

class Derived : public QObject, public Interface
{
    Q_OBJECT
    Q_INTERFACES(foo::Interface)  //!< Notice foo:: is still provided
};

}

Причина

Из документации:

Если вы хотите использовать Q_DECLARE_INTERFACE с классами интерфейса, объявленными в пространстве имен, вы должны убедиться, что Q_DECLARE_INTERFACE не находится внутри пространства имен.

Но они не упоминают, что Q_INTERFACES(), даже находясь внутри этого пространства имен по области действия, по-прежнему требует предоставления пространства имен, как если бы оно было глобальным.

Другие вопросы по тегам