Создание плагина для расширения приложения 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()
, даже находясь внутри этого пространства имен по области действия, по-прежнему требует предоставления пространства имен, как если бы оно было глобальным.