Calling C++/Qt classes through C++/CLI wrapper is a like a walk in the park.
But I'm stuck mapping C++/Qt signals to C# events.
I tried to combine some available how-tos/answers but did not get any working result:
The problem here is, that these how-tos/answers are quite old. I am currently working with Qt5.5 (soon 5.6) and .NET 4.6. I tried to adapt everything to current state of the art but may have failed.
It may be, that I can't see the forest because of too much trees, so I would like to ask for a working example which accomplishes the task with current framework versions, so that I can spot the differences and learn from the mistakes.
[edit] You can checkout this github repo, for this source. The parts for QtSignal to C#Event are commented out to have this code in a working state.
Github repo: https://github.com/qwc/QtSignalToCSharpEvent
For those who still want to read everything without playing around... read on... Here my current non-working code:
So I have a class in pure Qt5
#ifndef PUREQT_H
#define PUREQT_H
#include <QObject>
#include <QString>
#include "PureQt_global.h"
class PUREQTSHARED_EXPORT PureQt : public QObject {
Q_OBJECT
public:
PureQt(QString name);
~PureQt(){}
QString getSomeVar();
void someFunc(const QString & string);
public slots:
signals:
void someFuncWasCalled(const QString &string);
private:
QString someVar;
};
#endif // PUREQT_H
With the following rather simple implementation:
#include "PureQt.h"
PureQt::PureQt(QString name) {
this->someVar = "ctor("+name+")";
}
QString PureQt::getSomeVar() {
return this->someVar;
}
void PureQt::someFunc(const QString &string) {
this->someVar += "someFunc("+string+")";
emit someFuncWasCalled(this->someVar);
}
Then I've implemented a managed wrapper with C++/CLI to be able to call the unmanaged code from C#. Be aware that I've already tried to add code to get signal to event management.
#pragma once
#include "conversion.h"
#include "../pureqt/PureQt.h"
#include "SignalProxy.h"
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
namespace ManagedCppQtSpace {
// different variants... from tinkering around.
delegate void someFuncWasCalled(String^);
delegate void someFuncWasCalledU(QString str);
[StructLayoutAttribute(LayoutKind::Sequential)]
public ref struct DelegateWrapper {
[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
someFuncWasCalledU^ delegate;
};
public ref class ManagedCppQt
{
public:
ManagedCppQt(String^ name){
pureQtObject = new PureQt(StringToQString(name));
proxy = new SignalProxy(pureQtObject);
wrapper = gcnew DelegateWrapper();
wrapper->delegate = gcnew someFuncWasCalledU(this, ManagedCppQt::signalCallback);
signalCallbackProxy callbackproxy;
Marshal::StructureToPtr(wrapper, callbackproxy, false); // currently im stuck here with a compile error, but the problem may lie somewhere else...
proxy->setCallback(callbackproxy);
};
~ManagedCppQt(){
delete pureQtObject;
};
event someFuncWasCalled ^ someFuncCalled;
void someFunc(String^ string){
pureQtObject->someFunc(StringToQString(string));
};
String^ getSomeString() {
return QStringToString(pureQtObject->getSomeVar());
}
void signalCallback(QString str) {
someFuncCalled(QStringToString(str));
}
DelegateWrapper ^ wrapper;
private:
PureQt * pureQtObject;
SignalProxy * proxy;
};
}
So to link signal and slot handling from Qt with a callback which is able to raise an event in managed code some will need a proxy class when there's no option to change the basis code (because it's also used in other unmanaged C++ projects).
#ifndef SIGNALPROXY_H
#define SIGNALPROXY_H
#include <QObject>
#include "../pureqt/PureQt.h"
typedef void (*signalCallbackProxy) (QString str);
class SignalProxy : public QObject
{
Q_OBJECT
public:
explicit SignalProxy(PureQt* pqt);
~SignalProxy();
void setCallback(signalCallbackProxy callback);
signals:
public slots:
void someFuncSlot(QString str);
private:
PureQt* pureQt;
signalCallbackProxy scallback;
};
#endif // SIGNALPROXY_H
With implementation:
#include "SignalProxy.h"
SignalProxy::SignalProxy(PureQt* pqt){
pureQt = pqt;
this->connect(pureQt, SIGNAL(PureQt::someFuncWasCalled(QString)), this, SLOT(someFuncSlot(QString)));
}
SignalProxy::~SignalProxy()
{}
void SignalProxy::setCallback(signalCallbackProxy callback){
this->scallback = callback;
}
void SignalProxy::someFuncSlot(QString str){
if(this->scallback != NULL)
this->scallback(str);
}
So. Now, how to correctly link these two worlds, Qt signals -> managed .NET events?
I've also tried some simple approaches, which lead to compile errors, like:
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, &MangagedCppQtSpace::ManagedCppQt::signalCallback);
instead of the proxy class, or with a lambda function:
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, [] (QString str) {
signalCallback(str);// or ManagedCppQt::signalCallback, but for this the method has to be static, and it isn't possible to raise events from static methods...
}
Problem here is mixing Qt with C++ CLI. To have functional signal and slots you Qt needs process header files to generate own meta data. Problem is that tool will be unable to understand C++CLI features.
To overcome this problem first you have to do fallback to C++ interfaces and there perform safely C++ CLI operations.
So you need extra class like this which doesn't know .net and creates bridge to standard C++:
class PureQtReceiverDelegate { // this should go to separate header file
virtual void NotifySomeFunc(const char *utf8) = 0;
};
class PureQtReceiver : public QObject {
Q_OBJECT
public:
PureQtReceiver(PureQtReceiverDelegate *delegate, PureQt *parent)
: QObject(parent)
, mDelegate(delegate)
{
bool ok = connect(parent, SIGNAL(PureQt::someFuncWasCalled(QString)),
this, SLOT(someFuncReceiver(QString)));
Q_ASSERT(ok);
}
public slots:
void someFuncReceiver(const QString & string)
{
delegate->NotifySomeFunc(string.toUtf8().data());
}
private:
PureQtReceiverDelegate *delegate;
};
Now your C++CLI class should implement this PureQtReceiverDelegate
and there convert string to .net version and post notification.
Note you can/should forward declare Qt specific classes in C++CLI header file.
If you are using Qt 5 and have C++11 available than there is more handy solution: you can use lambda expression in when making a connection to a QObject. So your ManagedCppQt
can look like this:
header:
#include "../pureqt/PureQt.h"
using namespace ManagedCppQtSpace;
ManagedCppQt:ManagedCppQt(String^ name) {
pureQtObject = new PureQt(QStringFromString(name));
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled,
[this](const QString &string){
if (this->someFuncCalled) {
String^ s = StringFromQString(string);
this->someFuncCalled(s);
}
});
}
ManagedCppQt::~ManagedCppQt(){
delete pureQtObject;
}
in cpp:
#include "../pureqt/PureQt.h" using namespace ManagedCppQtSpace; ManagedCppQt:ManagedCppQt(String^ name) { pureQtObject = new PureQt(QStringFromString(name)); QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, [this](const QString &string){ if (this->someFuncCalled) { String^ s = StringFromQString(string); this->someFuncCalled(s); } }); } ManagedCppQt::~ManagedCppQt(){ delete pureQtObject; }
This is much easier, faster and easier to maintain.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.