簡體   English   中英

如何編寫用於將Qt信號映射到C#事件的包裝類(通過C ++ / CLI)

[英]How to write wrapper classes for mapping Qt Signals to C# events (through C++/CLI)

通過C ++ / CLI包裝器調用C ++ / Qt類就像在公園散步一樣。

但我堅持將C ++ / Qt信號映射到C#事件。

我試圖結合一些可用的how-tos / answers但沒有得到任何工作結果:

這里的問題是,這些方法/答案是相當陳舊的。 我目前正在使用Qt5.5(很快5.6)和.NET 4.6。 我試圖使一切都適應當前的技術水平但可能失敗了。

可能是因為樹木太多而無法看到森林,所以我想問一個能夠用當前框架版本完成任務的工作示例,這樣我就可以發現差異並從錯誤中吸取教訓。

[edit]您可以查看此github倉庫,以獲取此來源。 QtSignal到C#事件的部分已注釋掉,以使此代碼處於工作狀態。

Github回購: https//github.com/qwc/QtSignalToCSharpEvent

對於那些仍然想要閱讀所有內容而不玩游戲的人...繼續閱讀...這里是我當前的非工作代碼:

所以我有一個純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

通過以下相當簡單的實現:

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

然后我用C ++ / CLI實現了一個托管包裝器,以便能夠從C#調用非托管代碼。 請注意,我已經嘗試添加代碼以獲取事件管理信號。

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

因此,為了將來自Qt的信號和槽處理與能夠在托管代碼中引發事件的回調鏈接,當沒有更改基本代碼的選項時,某些將需要代理類(因為它也用於其他非托管C ++項目)。

#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

實施:

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

所以。 現在,如何正確地鏈接這兩個世界,Qt信號 - >托管.NET事件?

我也嘗試了一些簡單的方法,這會導致編譯錯誤,例如:

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

而不是代理類,或使用lambda函數:

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

這里的問題是將Qt與C ++ CLI混合使用。 要獲得功能信號和插槽,您需要處理頭文件以生成自己的元數據。 問題是該工具將無法理解C ++ CLI功能。

要首先解決此問題,您必須回退到C ++接口,並執行安全的C ++ CLI操作。

所以你需要像這樣的額外類,它不知道.net並創建標准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;
};

現在,您的C ++ CLI類應實現此PureQtReceiverDelegate ,並將字符串轉換為.net版本並發布通知。

請注意,您可以/應該在C ++ CLI頭文件中轉發聲明Qt特定的類。


如果您使用Qt4,請不要使用C ++ 11,以上解決方案是好的。

如果你使用Qt 5並且有可用的C ++ 11,那么有更方便的解決方案:你可以在連接到QObject時使用lambda表達式。 所以你的ManagedCppQt看起來像這樣:

標題:

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

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

這更容易,更快速,更容易維護。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM