2016-04-25 11 views
4

Der Aufruf von C++/Qt-Klassen über C++/CLI-Wrapper ist wie ein Spaziergang im Park.Wie schreibe Wrapper-Klassen für die Zuordnung Qt-Signale zu C# -Ereignissen (über C++/CLI)

Aber ich bin stecken stecken Zuordnung von C++/Qt-Signale zu C# -Ereignissen.

Ich habe versucht, einige der verfügbaren How-Tos/Antworten zu kombinieren, aber jede Arbeitsergebnis nicht bekommen:

Das Problem hier ist, dass diese How-Tos/Antworten ziemlich alt sind. Ich arbeite derzeit mit Qt5.5 (bald 5.6) und .NET 4.6. Ich habe versucht, alles dem aktuellen Stand der Technik anzupassen, aber vielleicht gescheitert.

Es kann sein, dass ich den Wald nicht sehen kann, weil zu viele Bäume, so würde Ich mag für ein Arbeits Beispiel fragen, die die Aufgabe mit aktuellen Framework-Versionen erreicht, so dass ich die Unterschiede erkennen kann und lerne aus den Fehlern.

[bearbeiten] Sie können dieses Github Repo für diese Quelle auschecken. Die Teile für QtSignal to C# Event sind auskommentiert, um diesen Code in einem funktionierenden Zustand zu haben.

Github Repo: https://github.com/qwc/QtSignalToCSharpEvent

Für diejenigen, die noch alles wollen, ohne spielen um lesen ... lesen Sie weiter ... Hier mein aktueller nicht-funktionierenden Code:

So habe ich eine Klasse in rein 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 

Mit folgenden eher einfachen Implementierung:

#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); 
} 

Dann habe ich einen verwalteten Wrapper mit C++/CLI implementiert, um den unmanaged Code von C# aufrufen zu können. Seien Sie sich bewusst, dass ich bereits versucht habe, Code hinzuzufügen, um das Ereignismanagement zu signalisieren.

#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 zum Verbindungssignal und Schlitz von Qt mit einem Rückruf Handhabung, die ein Ereignis in verwaltetem Code zu erhöhen Lage ist, einige werden eine Proxy-Klasse benötigen, wenn es keine Option ist den Basis-Code zu ändern (weil es auch in verwendet werden andere nicht verwaltete C++ - Projekte).

#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 

Mit Umsetzung:

#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. Nun, wie Sie diese beiden Welten, Qt-Signale -> verwaltete .NET-Ereignisse richtig zu verknüpfen?

Ich habe auch versucht, einige einfache Ansätze, die Fehler zu kompilieren führen, wie:

QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, &MangagedCppQtSpace::ManagedCppQt::signalCallback); 

anstelle der Proxy-Klasse oder mit einer Lambda-Funktion:

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... 
} 
+0

Können Sie genauer angeben, was Sie stolpern lässt? Erhalten Sie Kompilierungsfehler? Laufzeitfehler? Keine Fehler, aber die Ereignisse feuern nicht? –

+0

Sie müssen zwei Dinge tun: 1. Deklarieren Sie den Event-Handler als Mitglied Ihrer Klasse. 2. Rufen Sie den Event-Handler von einem Slot (ein Lambda, wirklich) auf, der mit Ihrem Signal verbunden ist. Das ist wirklich alles, was dazu gehört. –

+0

"könnte meinen aktuellen Beispielcode einfügen, aber ich denke, das würde Leser nur verwirren ..." Das ist genau das, was Sie tun müssen, um diese Frage zum Thema beizubehalten. Ansonsten ist es ein direktes Duplikat. –

Antwort

2

Problem hier ist Mischen von Qt mit C++ CLI. Um funktionelles Signal und Slots zu haben, benötigt Qt Prozess-Header-Dateien, um eigene Metadaten zu erzeugen. Problem ist, dass das Tool C++ CLI-Funktionen nicht verstehen kann.

Um dieses Problem zu lösen, müssen Sie zunächst auf C++ - Schnittstellen zurückgreifen und dort C++ - CLI-Operationen ausführen.

So müssen Sie Extraklasse wie diese, die nicht .net nicht kennt und schafft Brücke zu 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; 
}; 

Jetzt ist Ihre C++ CLI-Klasse diese implementieren sollten PureQtReceiverDelegate und es konvertieren Zeichenfolge .net Version und nach Benachrichtigung.

Beachten Sie, dass Sie Qt-spezifische Klassen in der C++ - CLI-Headerdatei weiterleiten können/sollten.


Die obige Lösung ist gut, wenn Sie Qt4 verwenden od verwenden nicht C++ 11.

Wenn Sie Qt 5 verwenden und C++ 11 verfügbar haben, gibt es eine praktikablere Lösung: Sie können den Lambda-Ausdruck verwenden, wenn Sie eine Verbindung zu einem QObject herstellen. So Ihr ManagedCppQt kann wie folgt aussehen:

Header:

#pragma once 
#include "conversion.h" 

using namespace System; 
using namespace System::Collections::Generic; 
using namespace System::Runtime::InteropServices; 

// forward declaration 
class PureQt; 

namespace ManagedCppQtSpace { 
    delegate void someFuncWasCalled(String^); 

    public ref class ManagedCppQt 
    { 
    public:  
     ManagedCppQt(String^ name); 
     ~ManagedCppQt(); 

     event someFuncWasCalled^someFuncCalled; 

     void someFunc(String^ string); 

     String^ getSomeString(); 

     void signalCallback(QString str); 

    private: 
     PureQt * pureQtObject; 
    }; 
} 

in cav:

#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; 
} 

Das ist viel einfacher, schneller und leichter zu pflegen.

+0

Während ich leider keine Zeit hatte, Ihre Lösung zu testen, ist sie schön erklärt, macht Sinn und sieht vielversprechend aus. Da ich erst nach Ende der Bounty hier sein werde, werde ich es im Voraus vergeben. –

+0

Wie kann ich diesen Compilerfehler umgehen, indem ich den letzten Hinweis mit einem Lambda verwende: 'ManagedCppQtSpace :: ManagedQt :: {ctor} :: : lokale Klassen-, Struktur- oder Vereinigungsdefinitionen sind in einer Memberfunktion von nicht erlaubt Eine verwaltete Klasse – qwc

+0

Verwenden des Qt4-Ansatzes: Das Ableiten einer verwalteten Klasse (ref-Klasse) von einer nativen Schnittstelle ist unmöglich -> Compiler-Fehler. Das Schreiben einer anderen Proxy-Klasse, die verwaltete Objekte über 'auto_gcroot ' verwendet, scheitert aufgrund von Compiler-Fehlern bezüglich unbekannter Namespaces/Klassen usw. TL; DR: Ich kann es nicht zum Laufen bringen. – qwc