March 11, 2012

Eus Eus
Lab Rat
138 posts

QPluginLoader и споделени ресурси

 

Здравейте.

Опитвам се да си направя базова система с Плъгин Мениджър който да зарежда динамично плъгини. Всичко е ОК, създавам си плъгин и плъгина работи, мога да си създам сигнали и слотове и да ги вържа към програмата при инстантирането на плъгините, но това е единствения начин за комуникация между плъгина и програмата. Опитах по всевъзможни начини да подам като параметър обекта на програмата и всевъзможни други варианти (примерно qApp property) с идеята да мога да викам методи или да вземам стойности директно от програмата.

Някой има ли идея как бих могъл да постигна това? Ако с плъгина не мога да достигна стойности в програмата, които са нужни като параметри (примерно връзка с база данни – ако не мога да ползвам основния обект, поне да вземам информацията от параметрите на програмата – потребител, парола, etc), то цялата идея от плъгина се губи. Дали може да се ползва нещо като QSharedMemory и някой има ли представа как бих могъл да го включа?

11 replies

March 11, 2012

task_struct task_struct
Ant Farmer
349 posts

Здравей,

Мисля, че идея на плъгините изобщо е да предоставят някаква разширена функционалност, като програмата вика техни функции, а не те функции от програмата. За това може би няма да можеш да извикаш нещо, което е в основната програма, а тя трябва да се погрижи да даде на плъгина всичко, което му трябва за работа.

Не знам, дали си видял това [doc.qt.nokia.com] , но мисля, че с Q_INTERFACES, може да си направиш интерфейс към плъгините, с който да им предаваш параметри.

 Signature 

“Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.”
- Linus Torvalds

March 11, 2012

Eus Eus
Lab Rat
138 posts

Може би аз не разбирам идеята на интерфейсите….. Въпросната страница я разглеждах редица пъти, моя плъгин работи с интерфейс, но допълнително добавям сигнали и слотове, понеже нали в интерфейсите не може да има декларации за сигнали и слотове…..

Именно че искам да разширя възможностите на програмата, като добавям нова функционалност, но в случая става въпрос за програма която има работеща стриймове. Как мога да кажа на плъгина да ползва вече работещия стрийм за приемане и изшращане на информация? Да, пращането на информация към плъгина не би трябвало да е проблем, но ако искам въпросния плъгин да запише нещо в база данни?

Не знам дали мога да се изразя правилно и дали би разбрал точно какво имам предвид.

March 12, 2012

Mart Mart
Lab Rat
47 posts

Аз може би не разбрах добре каква ти е идеята, а и с плъгини никога не съм се занимавал, но си мисля следното: щом няма проблем да предаваш сигнали между програмата и плъгина, защо просто не направиш един сигнал с параметър указател към главния ти обект. В момента на зареждане на програмата/плъгина, този сигнал се излъчва към плъгина и той вече разполага с указател към главния обект и съответно прави каквото си иска.

Това обаче предполага, че плъгина трябва да знае точно структурата на самия обект и става сложно защото човека пишещ плъгина трябва постоянно да си обновява структурата на обекта при всяка нова версия на програмата/обекта. Ако има разминаване ще изгърми някъде. Това предполага да се въведе параметър “версия на обекта” и ако версиите не съвпадат, плъгина да се игнорира.

Друг вариант е да се направи допълнителен, интерфейсен обект, който не се променя никога и който дава необходимите методи за връзка (комуникация) с главния обект. А после на плъгина да се изпраща указател към този интерфейсен обект.

March 12, 2012

task_struct task_struct
Ant Farmer
349 posts

За базата данни, ако не ти трябват някакви данни от програмата, то плъгина може сам да си създаде конекция. Другият варинат, но не съм сигурен дали ще работи, защото не знам дали плъгина вижда променливите в програмата е: При създаването на конкция може да и се даде име и после в плъгина може да я взимаш с QSqlDatabase::database() [qt-project.org]
Така може да имаш даже няколко разлини конецкии с към различни бази данни/потребители/ и плъгина да си ползва тази която му трябва.

 Signature 

“Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.”
- Linus Torvalds

March 12, 2012

Eus Eus
Lab Rat
138 posts
task_struct wrote:
не знам дали плъгина вижда променливите в програмата

Е там е проблема де :)

@M_3_T: опитах се и по тоя начин, направих метод който се пуска при зареждането на плъгините. Само че дава грешка при зареждането на плъгина и просто не го зарежда…..

March 13, 2012

task_struct task_struct
Ant Farmer
349 posts

Пробвай това с имената на конекциите. Това инфо трябва да се пази в библиотеката, а тя е шерната между плъгина и програмата, така че трябва Qt-то да се оправи само.

 Signature 

“Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.”
- Linus Torvalds

March 13, 2012

Mart Mart
Lab Rat
47 posts
Eus wrote:
@M_3_T: опитах се и по тоя начин, направих метод който се пуска при зареждането на плъгините. Само че дава грешка при зареждането на плъгина и просто не го зарежда…..

Няма логика да дава грешка. Ти му изпращаш указател – това е просто един реален адрес в паметта.
Да не би да се опитваш някъде в плъгина да използваш указателя преди да си получил стойността му, т.е. да не използваш неинициализиран указател? Знам че това е глупав въпрос, ама…

П.П. Смених си името на Mart (защото M_3_T ми беше много трудно за изписване).

March 13, 2012

Eus Eus
Lab Rat
138 posts

Мне, както казах, инициализира се плъгина, чак след това му подавам обекта с някой метод. На мястото където се опитвам да използвам обекта на програмата ми дава грешка. Ама чакай да се заиграя пак с кода и да постна направо повече инфо тук.

@task_struct – конекцията към базата я дадох като пример само, проблема не свършва със свързването с базата.

March 13, 2012

Eus Eus
Lab Rat
138 posts

Ето какво правя. Това е основния loop на програмата, не е в main.cpp, но е което е изпълнява от main.

  1.     qDebug() << "Init done.\nLoading plugins.";
  2.  
  3.     QDir pluginsDir = QDir(qApp->applicationDirPath());
  4.     pluginsDir.cdUp();
  5.     pluginsDir.cd("plugins");
  6.  
  7.     foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
  8.         QPluginLoader *loader = new QPluginLoader(pluginsDir.absoluteFilePath(fileName),this);
  9.         QObject *plugin = loader->instance();
  10.         if (plugin) {
  11.             connect(plugin,SIGNAL(msg(QString,QString,QString)),this,SLOT(msg(QString,QString,QString)));
  12.             pluginInterface *pl = qobject_cast(plugin);
  13.             pl->init(this);
  14.             plugindata plug;
  15.             plug.object = plugin;
  16.             plug.name = pl->Name();
  17.             plug.version = pl->Version();
  18.  
  19.             plugins.insert(plug.name,plug);
  20.         } else {
  21.             qDebug() << loader->errorString();
  22.         }
  23.     }

plugindata е обикновен struct който събира информация за плъгините, но за момента нищо не се прави с него.

  1. struct plugindata {
  2.     QString name;
  3.     QString version;
  4.     QObject *object;
  5. };

plugins е QMap за запазване на инфото за плъгините:

QMap<QString, plugindata> plugins;

Интерфейса е:

  1. class kernel;
  2.  
  3. class pluginInterface {
  4. public:
  5.     /**
  6.       * Returns the name of the Plugin
  7.       */
  8.     virtual QString Name() = 0;
  9.  
  10.     virtual QString Version() = 0;
  11.  
  12.     virtual void event(QStringList leftParams, QString rightSide) = 0;
  13.  
  14.     virtual void init(kernel *obj) = 0;
  15.  
  16. };

И съответния Q_DECLARE_INTERFACE за деклариране на интерфейса.

В cpp файла на плъгина имам следното:

  1. void Numerics::init(kernel *p)
  2. {
  3.     this->p = p;
  4. }
  5.  
  6. void Numerics::event(QStringList leftParams, QString rightSide)
  7. {
  8.     qDebug() << "msg from plugin via signal";
  9.     emit msg("#test","tralala","p");
  10.  
  11.     qDebug() << "msg from plugin parent object";
  12.     p->msg("test","test","p");
  13. }

като “p” е декларирано в хедъра имам:

  1.     void event(QStringList leftParams, QString rightSide);
  2.  
  3.     void init(kernel *obj);

“emit”-а работи ОК,
гърми на този ред:
p->msg(“test”,“test”,“p”);

а грешката е следната:

  1. msg from plugin parent object
  2. <абсолютен път до дебъг директорията>: symbol lookup error: <абсолютния път до директорията на плъгините>/libnumerics.so: undefined symbol: _ZN6kernel3msgE7QStringS0_S0_

Ако го оставя само със сигнала и слота, всичко работи ОК.

March 13, 2012

task_struct task_struct
Ant Farmer
349 posts

Аа мисля, че проблема ти е с имената. Имаш сигнал и функция с едно и същи име – msg

Сигналите също са функции на класа. Те се генерират в .moc файла. Така, че ти в момента имаш твоя си функция и сигнал с едни и същи параметри. Линкера не може да реши какво да прави в тоя момент.

 Signature 

“Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.”
- Linus Torvalds

March 14, 2012

Eus Eus
Lab Rat
138 posts

Нямам две функции, имам една функция която е обозначена като публичен слот. Но за да сме сигурни че не е това, ще ползвам друга функция от класа kernel и ще пиша за резултата.

Също така, проблема не е в линкъра, тъй като плъгина се компилира нормално, при зареждането му се получава гафа.

Едит: Проверих с друг публичен метод/функция наречен sendRawData (метода msg всъщност вика него с готови параметри), резулдата е абсолютно същия – плъгина се компилира, програмата се компилира, при стартирането хвърля грешката и излиза…. =/

 
  ‹‹ QTextEdit показва текста много бавно под Win7, Qt4.8.      Qt Creator - Autocomplete и разпознаване на класове ››

You must log in to post a reply. Not a member yet? Register here!