QPluginLoader и споделени ресурси
-
Здравейте.
Опитвам се да си направя базова система с Плъгин Мениджър който да зарежда динамично плъгини. Всичко е ОК, създавам си плъгин и плъгина работи, мога да си създам сигнали и слотове и да ги вържа към програмата при инстантирането на плъгините, но това е единствения начин за комуникация между плъгина и програмата. Опитах по всевъзможни начини да подам като параметър обекта на програмата и всевъзможни други варианти (примерно qApp property) с идеята да мога да викам методи или да вземам стойности директно от програмата.
Някой има ли идея как бих могъл да постигна това? Ако с плъгина не мога да достигна стойности в програмата, които са нужни като параметри (примерно връзка с база данни - ако не мога да ползвам основния обект, поне да вземам информацията от параметрите на програмата - потребител, парола, etc), то цялата идея от плъгина се губи. Дали може да се ползва нещо като QSharedMemory и някой има ли представа как бих могъл да го включа?
-
Здравей,
Мисля, че идея на плъгините изобщо е да предоставят някаква разширена функционалност, като програмата вика техни функции, а не те функции от програмата. За това може би няма да можеш да извикаш нещо, което е в основната програма, а тя трябва да се погрижи да даде на плъгина всичко, което му трябва за работа.
Не знам, дали си видял "това":http://doc.qt.nokia.com/4.7-snapshot/plugins-howto.html , но мисля, че с Q_INTERFACES, може да си направиш интерфейс към плъгините, с който да им предаваш параметри.
-
Може би аз не разбирам идеята на интерфейсите..... Въпросната страница я разглеждах редица пъти, моя плъгин работи с интерфейс, но допълнително добавям сигнали и слотове, понеже нали в интерфейсите не може да има декларации за сигнали и слотове.....
Именно че искам да разширя възможностите на програмата, като добавям нова функционалност, но в случая става въпрос за програма която има работеща стриймове. Как мога да кажа на плъгина да ползва вече работещия стрийм за приемане и изшращане на информация? Да, пращането на информация към плъгина не би трябвало да е проблем, но ако искам въпросния плъгин да запише нещо в база данни?
Не знам дали мога да се изразя правилно и дали би разбрал точно какво имам предвид.
-
Аз може би не разбрах добре каква ти е идеята, а и с плъгини никога не съм се занимавал, но си мисля следното: щом няма проблем да предаваш сигнали между програмата и плъгина, защо просто не направиш един сигнал с параметър указател към главния ти обект. В момента на зареждане на програмата/плъгина, този сигнал се излъчва към плъгина и той вече разполага с указател към главния обект и съответно прави каквото си иска.
Това обаче предполага, че плъгина трябва да знае точно структурата на самия обект и става сложно защото човека пишещ плъгина трябва постоянно да си обновява структурата на обекта при всяка нова версия на програмата/обекта. Ако има разминаване ще изгърми някъде. Това предполага да се въведе параметър "версия на обекта" и ако версиите не съвпадат, плъгина да се игнорира.
Друг вариант е да се направи допълнителен, интерфейсен обект, който не се променя никога и който дава необходимите методи за връзка (комуникация) с главния обект. А после на плъгина да се изпраща указател към този интерфейсен обект.
-
За базата данни, ако не ти трябват някакви данни от програмата, то плъгина може сам да си създаде конекция. Другият варинат, но не съм сигурен дали ще работи, защото не знам дали плъгина вижда променливите в програмата е: При създаването на конкция може да и се даде име и после в плъгина може да я взимаш с "QSqlDatabase::database()":http://qt-project.org/doc/qt-4.8/qsqldatabase.html#database
Така може да имаш даже няколко разлини конецкии с към различни бази данни/потребители/ и плъгина да си ползва тази която му трябва. -
[quote author="task_struct" date="1331567365"]не знам дали плъгина вижда променливите в програмата[/quote]
Е там е проблема де :)
@M_3_T: опитах се и по тоя начин, направих метод който се пуска при зареждането на плъгините. Само че дава грешка при зареждането на плъгина и просто не го зарежда.....
-
Пробвай това с имената на конекциите. Това инфо трябва да се пази в библиотеката, а тя е шерната между плъгина и програмата, така че трябва Qt-то да се оправи само.
-
[quote author="Eus" date="1331581476"]
@M_3_T: опитах се и по тоя начин, направих метод който се пуска при зареждането на плъгините. Само че дава грешка при зареждането на плъгина и просто не го зарежда.....[/quote]Няма логика да дава грешка. Ти му изпращаш указател - това е просто един реален адрес в паметта.
Да не би да се опитваш някъде в плъгина да използваш указателя преди да си получил стойността му, т.е. да не използваш неинициализиран указател? Знам че това е глупав въпрос, ама...П.П. Смених си името на Mart (защото M_3_T ми беше много трудно за изписване).
-
Мне, както казах, инициализира се плъгина, чак след това му подавам обекта с някой метод. На мястото където се опитвам да използвам обекта на програмата ми дава грешка. Ама чакай да се заиграя пак с кода и да постна направо повече инфо тук.
@task_struct - конекцията към базата я дадох като пример само, проблема не свършва със свързването с базата.
-
Ето какво правя. Това е основния loop на програмата, не е в main.cpp, но е което е изпълнява от main.
@
qDebug() << "Init done.\nLoading plugins.";QDir pluginsDir = QDir(qApp->applicationDirPath()); pluginsDir.cdUp(); pluginsDir.cd("plugins"); foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { QPluginLoader *loader = new QPluginLoader(pluginsDir.absoluteFilePath(fileName),this); QObject *plugin = loader->instance(); if (plugin) { connect(plugin,SIGNAL(msg(QString,QString,QString)),this,SLOT(msg(QString,QString,QString))); pluginInterface *pl = qobject_cast<pluginInterface *>(plugin); pl->init(this); plugindata plug; plug.object = plugin; plug.name = pl->Name(); plug.version = pl->Version(); plugins.insert(plug.name,plug); } else { qDebug() << loader->errorString(); } }@
plugindata е обикновен struct който събира информация за плъгините, но за момента нищо не се прави с него.
@struct plugindata {
QString name;
QString version;
QObject *object;
};@plugins е QMap за запазване на инфото за плъгините:
QMap<QString, plugindata> plugins;
Интерфейса е:
@class kernel;
class pluginInterface {
public:
/**
* Returns the name of the Plugin
*/
virtual QString Name() = 0;virtual QString Version() = 0; virtual void event(QStringList leftParams, QString rightSide) = 0; virtual void init(kernel *obj) = 0;
};@
И съответния Q_DECLARE_INTERFACE за деклариране на интерфейса.
В cpp файла на плъгина имам следното:
@void Numerics::init(kernel *p)
{
this->p = p;
}void Numerics::event(QStringList leftParams, QString rightSide)
{
qDebug() << "msg from plugin via signal";
emit msg("#test","tralala","p");qDebug() << "msg from plugin parent object"; p->msg("test","test","p");
}@
като "p" е декларирано в хедъра имам:
@ void event(QStringList leftParams, QString rightSide);void init(kernel *obj);@
"emit"-а работи ОК,
гърми на този ред:
p->msg("test","test","p");а грешката е следната:
@msg from plugin parent object
<абсолютен път до дебъг директорията>: symbol lookup error: <абсолютния път до директорията на плъгините>/libnumerics.so: undefined symbol: ZN6kernel3msgE7QStringS0_S0@Ако го оставя само със сигнала и слота, всичко работи ОК.
-
Аа мисля, че проблема ти е с имената. Имаш сигнал и функция с едно и същи име - msg
Сигналите също са функции на класа. Те се генерират в .moc файла. Така, че ти в момента имаш твоя си функция и сигнал с едни и същи параметри. Линкера не може да реши какво да прави в тоя момент.
-
Нямам две функции, имам една функция която е обозначена като публичен слот. Но за да сме сигурни че не е това, ще ползвам друга функция от класа kernel и ще пиша за резултата.
Също така, проблема не е в линкъра, тъй като плъгина се компилира нормално, при зареждането му се получава гафа.
Едит: Проверих с друг публичен метод/функция наречен sendRawData (метода msg всъщност вика него с готови параметри), резулдата е абсолютно същия - плъгина се компилира, програмата се компилира, при стартирането хвърля грешката и излиза.... =/