i have application that load plugins dynamically. each plugin has it's own pluginname.qml file. i need to access the plugins methods from pluginname.qml files.
obviously i can't use QQmlContext::setContextPropert("" ,*object) because of abstract interface of plugins. so what is the proper way to create functionality like this. how can i access the methods of plugin directly from qml(expose plugin methods to qml)? sry for bad english :)
playerView.qml:
Component.onCompleted:
{
ViewManager.requestPlugin("playerPlugind.dll"); // ViewManager signal connected to PluginLoader void onPluginRequest() slot
}
PluginLoader.cpp:
bool PluginLoader::loadPlugin(QString plugin_name)
{
QDir pluginDirectory = QCoreApplication::applicationDirPath();
pluginDirectory.cd("Plugins");
QPluginLoader pluginLoader(pluginDirectory.filePath(plugin_name));
QObject* instance = pluginLoader.instance();
if(instance)
{
m_player_interface = qobject_cast<PlayerInterface *>(instance);
if(m_player_interface )
return true;
}
return 0;
}
void PluginLoader::onPluginRequest(QString plugin_name)
{
loadPlugin(plugin_name);
}
this answer is kind of silly ,but we can make a member function in interface class like this :
virtual void contextRegister(QQmlContext* rootContext) = 0;
then in the plugin we implement the member function:
void FirstPlugin::registerContext(QQmlContext* rootContext)
{
rootContext->setContextProperty("PlayerInterface" , this);
}
at the end ,when plugin loaded completely we can call this function :
playerInterface->contextRegister(m_rootContext);
Related
This is a plugin system which is based on the factory pattern.
Each plugin extends the Plugin class and implements a factory that returns the extended class to the kernel.
Plugins are compiled as shared libraries. Kernel checks the plugins/ directory and loads all libraries at runtime.
When the red button is clicked, the kernel calls call_on_red_click() on every loaded plugin.
// Plugin.h
class Plugin {
public:
virtual void call_on_blue_click() = 0;
virtual void call_on_red_click() = 0;
}
// MyPlugin.h
class MyPlugin : public Plugin {
public:
void call_on_blue_click() {/*[...]*/}
void call_on_red_click() {
// Does something
// Optionaly, activate teleporter
}
}
Plugin *plug_factory() { return new MyPlugin; }
// MyOtherPlugin.h
class MyOtherPlugin : public Plugin {
public:
void call_on_blue_click() {/*[...]*/}
void call_on_red_click() {
// Does something else
// Optionaly, activate teleporter
}
}
Plugin *plug_factory() { return new MyOtherPlugin; }
The teleporter is compiled separately and may not be present in every installation (It's shiped by itself).
Lets say that MyPlugin and MyOtherPlugin want to activate the teleporter if it is present.
Questions:
Should the Teleporter be a kernel Plugin or something else?
Is there a design pattern for this?
Sounds like you want the decorator pattern
Which will allow you to have a class that loads the dll, and then your plugins can then query your decorator if teleportation is enabled. You can then disable it for any reason, not just because the dll isn't there; or enable it even if the dll isn't there but implemented in another way.
The Situation
My company has a QML-based application which displays some content using a custom OpenGL-based render plugin (MyGame). This plugin has a few critical needs:
To be able to effect changes in the renderer in response to QML-based signals.
(e.g. change the position of an object rendered by the game)
To only process these changes at a specific spot in MyGame's redraw loop.
(This is very important; MyGame is very sensitive about when changes are allowed.)
To have the plugin redraw at 60Hz (at least).
The Problem
The code we have right now honors (1) and (2), but fails (3); the plugin does not get visually updated consistently. (The updates are erratic, at an estimated 5-10Hz.) I believe that the plugin we have created—based on QQuickFramebufferObject—is not taking proper advantage of how Qt/QML intended the scene graph to be updated.
How can I re-structure my plugin so that I get all three of the above?
The Code
Overview:
The plugin creates a QQuickFramebufferObject (MyPlugin) and a QQuickFramebufferObject::Renderer (MyRenderer).
When MyRenderer::render() is called it calls MyGame::Redraw() itself, and then calls update().
MyGame::Redraw() does what it needs to, and at the right spot where changes can be accepted, emits a timeToMakeChanges QML signal on MyPlugin.
QML listens for the onTimeToMakeChanges signal and invokes methods on the plugin that affect MyGame.
To workaround the problem of low-frequency visual updates, I've found that if I overlay a QML Canvas over my plugin and redraw the canvas frequently using a Timer, my plugin starts to get visually updated at what appears to be around 60Hz. Clearly this is a gross hack.
Following is a summary of the code setup. Please forgive missing/incorrect code; I'm trying to distill thousands of lines of glue code down to the essentials for this question.
MyPlugin.h
#include <QOpenGLFramebufferObject>
#include <QQuickFramebufferObject>
class MyPlugin : public QQuickFramebufferObject {
Q_OBJECT
public:
MyPlugin();
virtual ~MyPlugin();
virtual QQuickFramebufferObject::Renderer* createRenderer() const;
signals:
void timeToMakeChanges();
public slots:
void makeChanges(QVariant inValue);
void HandleWindowChanged(QQuickWindow *inWindow);
private:
MyGame* GetGame() { ... }
};
MyPlugin.cpp
#include "MyPlugin.h"
#include <MyGame.h>
// ******************************************************************
class MyRenderer:
public QObject,
public QQuickFramebufferObject::Renderer,
protected QOpenGLFunctions
{
Q_OBJECT
public:
virtual void render();
private:
static void RequestGameChanges();
};
void MyRenderer::render() {
if ( !m_Initialized ) {
QOpenGLFramebufferObject *theFbo = this->framebufferObject();
InitializeGl( theFbo ); // Not shown
m_MyGame = &MyGame::Create();
m_MyGame->RegisterCallback(
reinterpret_cast<qml_Function>(MyRenderer::RequestGameChanges)
);
m_Initialized = true;
}
m_MyGame->RestoreState();
m_MyGame->Redraw();
m_MyGame->SaveState();
m_PluginItem->window()->resetOpenGLState();
// Tell QML that we want to render again as soon as possible
update();
}
// This gets invoked in the middle of m_MyGame->Redraw()
void MyRenderer::RequestGameChanges() {
emit m_PluginItem->timeToMakeChanges();
}
// ******************************************************************
MyPlugin::MyPlugin() {
setMirrorVertically(true);
connect(
this, SIGNAL(windowChanged(QQuickWindow*)),
this, SLOT(HandleWindowChanged(QQuickWindow*))
);
}
void MyPlugin::HandleWindowChanged(QQuickWindow *inWindow) {
inWindow->setClearBeforeRendering(false);
}
void MyPlugin::makeChanges(QVariant inValue) {
MyGame *theGame = GetGame();
// Send the requested changes to theGame
}
QQuickFramebufferObject::Renderer* MyPlugin::createRenderer() const {
m_Renderer = new MyRenderer( *this );
}
MyApp.qml
import MyPlugin 1.0
Window {
MyPlugin {
property var queuedUpChanges: ([])
onSomeOtherSignal: queueUpChangesToMake();
onTimeToMakeChanges: makeChanges( queuedUpChanges );
}
Canvas { id:hack }
Timer {
interval:10; running:true; repeat:true
onTriggered: hack.changeWhatYouShow();
}
}
Bonus Points
The main question is "How do I modify my code so that I get 60Hz updates?" However, as seen in the QML, the setup above requires me to queue up all changes in QML so that they are able to be applied during the right spot in the MyGame::Render().
Ideally, I'd prefer to write QML without timeToMakeChanges, like:
MyPlugin {
onSomeOtherSignal: makeChanges( ... );
}
If there's a way to accomplish this (other than queuing up the changes in C++ instead)—perhaps something related to synchronize() I'd love to know about it.
I'd make a timer in QML that calls the makeChanges regularly. But store all the state in MyPlugin. Then, in Renderer::synchronize(), copy from MyPlugin to MyRenderer, so it can be used by the MyGame.
(although, I wouldn't do any gamelogic-related calculations in QML ever in the first place)
I'm trying to implement an easy way to enable and disable a Touch listener within my class. I tried writing a method within my class:
void HelloWorld::setTouchEnabled(bool enabled)
{
if (enabled)
{
auto _touchListener = EventListenerTouchAllAtOnce::create();
_touchListener->onTouchesBegan = CC_CALLBACK_2(HelloWorld::onTouchesBegan, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this);
}
else if (!enabled)
{
_eventDispatcher->removeEventListener(_touchListener);
}
}
I was hoping to be able to then call setTouchEnabled(true) or setTouchEnabled(false) from within any other methods in this class. However, this does not work since _touchListener is released at the end of the function. When I tried to declare EventListener *_touchListener in my header file, I received an error in XCode on this line:
_touchListener->onTouchesBegan = CC_CALLBACK_2(HelloWorld::onTouchesBegan, this);
The error said that no member named onTouchesBegan exists in cocos2d::EventListener.
I'm assuming there must be an easy way to do this.
You need to learn C++ first:)
Define _touchListener in your header file first, as a member of HelloWorld. Then modify your cpp file:
void HelloWorld::setTouchEnabled(bool enabled)
{
if (enabled)
{
_touchListener = EventListenerTouchAllAtOnce::create();
_touchListener->retain();
_touchListener->onTouchesBegan = CC_CALLBACK_2(HelloWorld::onTouchesBegan, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this);
}
else if (!enabled)
{
_eventDispatcher->removeEventListener(_touchListener);
_touchListener->release();
_touchListener = nullptr;
}
}
So basically i have a little application that loads two plugins and connect them. First plugin after it's loaded it creates a label without any title which will be added to main window. Second plugin creates an action which will be added to a menu. So my app need just to load these plugins and to connect them . What i mean by connecting them ? i mean that label plugin contains a slot which will modify label's title , and action plugin has a signal declared. Application should connect action plugin signal with label slot. I do not know how to do it exactly. My guess is that in action plugin class implementation is to connect custom signal with a standart signal (triggered). But anyway this ways my app is not working as i expected. How can i make a correct connection in my app for a signal from one plugin and slot from another plugin ??
Here is my code for Label Plugin :
#include "LabelInterface.h"
class LabelPlugin : public LabelInterface {
Q_OBJECT
Q_INTERFACES(LabelInterface)
public:
QLabel* label;
QLabel* newLabel();
LabelPlugin() {}
~LabelPlugin() {}
public slots:
void setTextforLabel();
};
#include <QtGui>
#include "LabelPlugin.h"
QLabel* LabelPlugin::newLabel() {
label = new QLabel("");
return label;
}
void LabelPlugin::setTextforLabel() {
label->setText("This plugin works fine");
}
// Exporta plugin-ul
Q_EXPORT_PLUGIN2 (labelplugin,LabelPlugin)
Action Plugin :
#include "ActionInterface.h"
class ActionPlugin : public ActionInterface {
Q_OBJECT
Q_INTERFACES (ActionInterface)
public :
QAction* myAction;
QAction* newAction();
~ActionPlugin () {}
ActionPlugin () {}
public slots:
void send_signal();
signals :
void pushMyAction();
};
#include <QtGui>
#include "ActionPlugin.h"
QAction* ActionPlugin::newAction() {
myAction = new QAction("Show text",this);
return myAction;
}
void ActionPlugin::send_signal() {
qDebug ()<<"Here";
QAction::connect (this,SIGNAL(pushMyAction()),this,SIGNAL(triggered()));
}
Q_EXPORT_PLUGIN2 (actionplugin,ActionPlugin)
In my app , where i try to load plugins i have :
foreach (QString fileName, appDir.entryList(QDir::Files)) {
qDebug()<<QString(fileName);
QPluginLoader pluginLoader(appDir.absoluteFilePath(fileName));
QObject* plugin = pluginLoader.instance();
if (plugin) {
ActionInterface* myAction= qobject_cast<ActionInterface*>(plugin);
if (myAction) {
action_ = myAction;
pluginMenu->addAction(action_->newAction());
}
LabelInterface* myLabel = qobject_cast<LabelInterface*>(plugin);
if (myLabel) {
label_=myLabel;
layout->addWidget(label_->newLabel());
}
if (action_ && label_) {
qDebug()<<"both loaded";
action_->send_signal();
connect(action_, SIGNAL(pushMyAction()),label_, SLOT(setTextforLabel()));
}
else qDebug() << "seems one plugin is not loaded";
}
}
You need to be able to access a QObject instance from each plugin so you can use it in the connect call. I would add methods to your interfaces to do this. One pattern I've seen is an operator to convert the interface to a QObject pointer, like:
class MyInterface {
public:
virtual operator QObject*() = 0;
};
Opinions may vary on whether that's good style, but it solves the problem (if you don't like the operator, use a method called asQObject() or similar).
I am refactoring some code to decouple GUI from some state.
#include <StateObject>
Class GUI{
...
StateObject A;
void doSomething() { A->hullaballoo();}
**void ReFreshMyGui() { //take state A and redraw }**
};
State object is being shared by multiple classes to update the state but this Gui Object specializes in displaying the state. So I would like to call the Refresh function via StateObject whenever its modified.
I am not sure how or if signals will provide the solution. Any hints?
In order to decouple GUI and Data you could use the MVC pattern. This means your GUI should register to your model's (data) object(s) and whenever the data changes the GUI will be notified and it will be the GUI to redraw itself.
But careful, the model should not have the notion of a concrete GUI, instead the GUI should implement an observer interface containing a method (e.g. void Update()) that will be called whenever changes occur (notification handler).
Just look for the MVC-pattern on google. You'll find thousands of useful tutorials.
You may also take in consideration the MVP and the MVVM patterns.
Example:
class Observer
{
virtual void Update(void* data) = 0;
}
class GUI : public Observer
{
public:
virtual void Update(void* data)
{
//Redraw method and some other things you may
//want to do with the new data
}
}
class Model
{
private:
int m_iData;
List<Observer> observers;
public:
void SetData(int iData)
{
m_iData = iData;
for(int i = 0; i < observers.Length; i++)
{
observers[i].Update(NULL);
}
}
}