Qt Remote Objects 静态Replica

        在QtRO中Replica实际上有两种:静态的和动态的。所谓静态,就是Source和Replica都通过rep文件作为接口定义,进而连接通信;而所谓动态是指Replica这边不再需要rep文件,而是运行时动态获取接口定义。

实例讲解

       在这个例子中我们创建两个工程:Server和Directconnect。在Server中我们将实现功能类,一个能接收消息并发送消息的类;而在Directconnect中我们将使用该功能类。

定义rep文件

        首先我们定义接口。创建一个rep文件transfernews.rep,内容很简单,一个属性和两个信号、一个槽函数:

#include 

POD Foo(QList bar);

class TransferNews
{
    PROP(QString lastMessage = "");

    SIGNAL(serverTime(QString));
    SIGNAL(testPod(Foo foo));

    SLOT(void reveiceMessage(QString));
};

        lastMessage是最新的消息;serverTime是一个server定时发射的信号;testPod也是一个信号,为了测试POD类型;receiveMessage是一个槽函数,连接server接收到的消息。

创建Source功能

        首先在.pro文件中加入QtRO模块:

QT       += remoteobjects 

        然后以SOURCE的解析方式加入rep文件:

REPC_SOURCE = transfernews.rep

        repc能够根据transfernews.rep内容自动生成rep_transfernews_source.h。其中有多个类定义,我们一般选择类名以Source结尾的那个类作为我们的基类,它的定义大概是:


class TransferNewsSource : public QObject
{
    Q_OBJECT
    Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "TransferNews")
    Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_SIGNATURE, "4a72fbe23576cac923df24c7a74d1bce42a97db6")
    Q_PROPERTY(QString lastMessage READ lastMessage WRITE setLastMessage NOTIFY lastMessageChanged)

public:
    explicit TransferNewsSource(QObject *parent = nullptr) : QObject(parent)
    {
        qRegisterMetaType();
        qRegisterMetaTypeStreamOperators();
        qRegisterMetaType>();
        qRegisterMetaTypeStreamOperators>();
    }

public:
    virtual ~TransferNewsSource() {}

    virtual QString lastMessage() const = 0;
    virtual void setLastMessage(QString lastMessage) = 0;

Q_SIGNALS:
    void lastMessageChanged(QString lastMessage);
    void serverTime(QString __repc_variable_1);
    void testPod(Foo foo);

public Q_SLOTS:
    virtual void pushLastMessage(QString lastMessage)
    {
        setLastMessage(lastMessage);
    }
    virtual void reveiceMessage(QString __repc_variable_1) = 0;

private:
    friend class QT_PREPEND_NAMESPACE(QRemoteObjectNode);
};

        可以看到,repc已经自动帮我们生成了各种Qt元信息。我们只需要派生这个类,然后把那几个虚函数实现了就可以了:

#ifndef TRANSFERNEWS_H
#define TRANSFERNEWS_H

#include 
#include 
#include "rep_transfernews_source.h"

class TransferNews : public TransferNewsSimpleSource
{
    Q_OBJECT
public:
    explicit TransferNews(QObject *parent = nullptr);
    ~TransferNews();

public slots:
    void reveiceMessage(QString msg) override;
    void sendServerTime();

private:
    QTimer *m_timer;
};

#endif // TRANSFERNEWS_H

源文件

#include "transfernews.h"
#include 
#include 

TransferNews::TransferNews(QObject *parent)
    : TransferNewsSimpleSource(parent)
{
    m_timer = new QTimer(this);
    m_timer->start(5000);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(sendServerTime()));
}

TransferNews::~TransferNews()
{
    m_timer->stop();
}

void TransferNews::reveiceMessage(QString msg)
{
    setLastMessage(msg);

    qDebug() << msg;
}

void TransferNews::sendServerTime()
{
     QString message = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");

     emit serverTime(message);

     QList list;
     list << "123" << "456" << "789";

     emit testPod(Foo(list));

     qDebug() << message;
}

        这样我们的功能类算是写好了。接下去我们需要把它分享出去,代码写在main.cpp里:

#include "transfernews.h"
#include 

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    TransferNews transferNews;

    QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica")));
    srcNode.enableRemoting(&transferNews);

    return app.exec();
}

        这样我们的服务端算是写好了。

创建客户端

        同样我们需要在.pro中加入QtRO模块,然后加入rep文件。但是注意这里我们要用REPLICA的模式处理该rep文件:

REPC_REPLICA = transfernews.rep

        客户端我们使用Qt Widget来写。所以我们在main.cpp中获取Replica,然后将它传进我们的Widget环境中:

#include "mainwindow.h"
#include 

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QRemoteObjectNode repNode;
    repNode.connectToNode(QUrl(QStringLiteral("local:replica")));

    QSharedPointer ptr;
    ptr.reset(repNode.acquire());

    MainWindow w(ptr);
    w.show();

    return app.exec();
}

          然后在mainwindow中构建我们的界面

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 
#include 
#include 

#include "directconnect.h"

class MainWindow : public QWidget
{
    Q_OBJECT
public:
    explicit MainWindow(QSharedPointer ptr, QWidget *parent = nullptr);

public Q_SLOTS:
    void onSend();
    void onReceive(QString);

protected:
    void setupUi();

private:
    QTextEdit *m_textEdit;
    QLineEdit *m_lineEdit;

    QToolButton *m_btn;

    DirectConnect *m_dc;
};

#endif // MAINWINDOW_H
#include "mainwindow.h"

#include 
#include 
#include 

MainWindow::MainWindow(QSharedPointer ptr, QWidget *parent)
    : QWidget(parent)
{
    setMinimumSize(600, 400);
    setupUi();

    m_dc = new DirectConnect(ptr);
    connect(m_dc, SIGNAL(sig_newMessage(QString)), this, SLOT(onReceive(QString)));
}

void MainWindow::setupUi()
{
    m_textEdit = new QTextEdit;
    QTextCursor textCursor = m_textEdit->textCursor();
    QTextBlockFormat textBlockFormat;
    textBlockFormat.setBottomMargin(10);
    textCursor.setBlockFormat(textBlockFormat);
    m_textEdit->setTextCursor(textCursor);
    m_textEdit->setFocusPolicy(Qt::NoFocus);

    m_lineEdit = new QLineEdit;
    m_lineEdit->setFixedHeight(30);

    m_btn = new QToolButton;
    m_btn->setFixedSize(80, 30);
    m_btn->setText("Send");
    m_btn->setFocusPolicy(Qt::NoFocus);
    connect(m_btn, &QToolButton::clicked, this, &MainWindow::onSend);

    QHBoxLayout *hlayout = new QHBoxLayout;
    hlayout->addWidget(m_lineEdit);
    hlayout->addWidget(m_btn);
    hlayout->setContentsMargins(0, 0, 0, 0);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(m_textEdit);
    layout->addLayout(hlayout);
    layout->setSpacing(8);
    layout->setContentsMargins(10, 10, 10, 10);

    setLayout(layout);
}

void MainWindow::onSend()
{
    QString msg = m_lineEdit->text();
    m_lineEdit->clear();

    emit m_dc->sig_sendMessage(msg);
}

void MainWindow::onReceive(QString msg)
{
    m_textEdit->append(msg);
}
#ifndef DIRECTCONNECT_H
#define DIRECTCONNECT_H

#include 
#include 

#include "rep_transfernews_replica.h"

class DirectConnect : public QObject
{
    Q_OBJECT
public:
    DirectConnect(QSharedPointer ptr);
    ~DirectConnect() override;

signals:
    void sig_sendMessage(QString);
    void sig_newMessage(QString);

protected slots:
    void slot_lastMessageChanged(QString);
    void slot_serverTimeChanged(QString);
    void slot_testPod(Foo);
    void slot_stateChanged(QRemoteObjectReplica::State, QRemoteObjectReplica::State);

private:
    QSharedPointer m_ptr;
};

#endif // DIRECTCONNECT_H
#include "directconnect.h"

DirectConnect::DirectConnect(QSharedPointer ptr)
    : QObject(nullptr), m_ptr(ptr)
{
     QObject::connect(this, SIGNAL(sig_sendMessage(QString)), m_ptr.data(), SLOT(reveiceMessage(QString)));

     QObject::connect(m_ptr.data(), SIGNAL(lastMessageChanged(QString)), this, SLOT(slot_lastMessageChanged(QString)));
     QObject::connect(m_ptr.data(), SIGNAL(serverTime(QString)), this, SLOT(slot_serverTimeChanged(QString)));
     QObject::connect(m_ptr.data(), SIGNAL(testPod(Foo)), this, SLOT(slot_testPod(Foo)));
     QObject::connect(m_ptr.data(), SIGNAL(stateChanged(QRemoteObjectReplica::State,QRemoteObjectReplica::State)),
                      this, SLOT(slot_stateChanged(QRemoteObjectReplica::State,QRemoteObjectReplica::State)));
}

DirectConnect::~DirectConnect()
{

}

void DirectConnect::slot_lastMessageChanged(QString msg)
{
    //pros test
    QString text = "Pros: " + msg;

    emit sig_newMessage(text);
}

void DirectConnect::slot_serverTimeChanged(QString msg)
{
    //signal test
    QString text = "Signal: " + msg;

    emit sig_newMessage(text);
}

void DirectConnect::slot_testPod(Foo foo)
{
    //Pod test
    QList list = foo.bar();

    for(auto itor = list.begin(); itor != list.end(); itor++)
    {
        QString text = "POD: " + *itor;
        emit sig_newMessage(text);
    }
}

void DirectConnect::slot_stateChanged(QRemoteObjectReplica::State new_state, QRemoteObjectReplica::State old_state)
{
    //State test
    QMetaEnum metaEnum = QMetaEnum::fromType();
    QString text = "State: ";

    text.append(metaEnum.valueToKey(new_state));
    text.append("->");
    text.append(metaEnum.valueToKey(old_state));

    emit sig_newMessage(text);
}

        可以看到,我们通过connect将功能类的信号槽进行绑定,用起来就好像功能类在我们当前客户端工程本地一样,非常直观。

运行效果

        Server和Directconnect的启动顺序是可以任意的,一般是Server先启动,然后Directconnect。但是QtRO允许Directconnect先启动,然后等待Server。

        启动Directconnect后我们发送消息,可以看到成功显示从功能类返回的消息,如下图:

                                       Qt Remote Objects 静态Replica_第1张图片

        如果运行多个Directconnect程序我们可以看到所有的Directconnect的“消息内容”都是同步的,后启动的Directconnect能够自动收到功能类的属性值。这也是QtRO一个非常重要的特性,即:后连接的Directconnect能够收到所有server改变了的属性值,即使该属性是在该server创建之前就改变的。

你可能感兴趣的:(qt)