Qt例子学习笔记 - Examples/Qt-6.2.0/corelib/ipc/localfortuneclient

71 篇文章 4 订阅

main.cpp

#include <QApplication>

#include "client.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    //[since 5.0] applicationDisplayName : QString
    //此属性包含此应用程序的用户可见名称
    //该名称显示给用户,例如在窗口标题中。 如果需要,它可以被翻译。
    //如果未设置,应用程序显示名称默认为应用程序名称。
    QGuiApplication::setApplicationDisplayName(Client::tr("Local Fortune Client"));
    Client client;
    client.show();
    return app.exec();
}

client.h

#ifndef CLIENT_H
#define CLIENT_H

#include <QDialog>
#include <QDataStream>
#include <QLocalSocket>

QT_BEGIN_NAMESPACE
//QLabel 用于显示文本或图像。
//不提供用户交互功能。
//标签的视觉外观可以通过多种方式配置
//它可以用于为另一个小部件指定一个焦点助记键
//QLabel 可以包含以下任何内容类型:
// 纯文本将 QString 传递给 setText()。
// 富文本将包含富文本的 QString 传递给 setText()。
// 一个像素图将一个 QPixmap 传递给 setPixmap()。
// 一个电影通过一个 QMovie 到 setMovie()。
// 数字将 int 或 double 传递给 setNum(),它将数字转换为纯文本。
// 没有与空的纯文本相同的内容。 这是默认设置。 由 clear() 设置。
//警告:当将 QString 传递给构造函数或调用 setText() 时,
//请确保清理您的输入
//,因为 QLabel 会尝试猜测它是将文本显示为纯文本还是富文本(HTML 4 标记的子集)
//您可能希望显式调用 setTextFormat()
//例如 以防您希望文本为纯格式但无法控制文本源(例如,当显示从 Web 加载的数据时)
//当使用这些功能中的任何一个更改内容时,任何先前的内容都会被清除。
//默认情况下,标签显示左对齐、垂直居中的文本和图像
//其中要显示的文本中的任何选项卡都会自动展开
//但是,可以通过多种方式调整和微调 QLabel 的外观。
//可以使用 setAlignment() 和 setIndent() 调整 QLabel 小部件区域内内容的定位。
//文本内容还可以使用 setWordWrap() 沿单词边界换行。
//例如,此代码在右下角设置了一个带有两行文本的下沉面板(两行与标签右侧齐平):
/*
    QLabel *label = new QLabel(this);
    label->setFrameStyle(QFrame::Panel | QFrame::Sunken);
    label->setText("first line\nsecond line");
    label->setAlignment(Qt::AlignBottom | Qt::AlignRight);
*/
//QLabel 继承自 QFrame 的属性和函数也可用于指定要用于任何给定标签的小部件框架。
//QLabel 通常用作交互式小部件的标签。
//为此,QLabel 提供了一种有用的机制来添加助记符
//该助记符会将键盘焦点设置到其他小部件(称为 QLabel 的“伙伴”)
/*
    QLineEdit *phoneEdit = new QLineEdit(this);
    QLabel *phoneLabel = new QLabel("&Phone:",this);
    phoneLabel->setBuddy(phoneEdit);
*/
//在此示例中,当用户按下 Alt+P 时,键盘焦点将转移到标签的伙伴(QLineEdit)。
//如果好友是一个按钮(从 QAbstractButton 继承)
//触发助记符将模拟按钮点击。

class QLabel;
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE

class Client : public QDialog
{
    Q_OBJECT

public:
    explicit Client(QWidget *parent = nullptr);

private slots:
    void requestNewFortune();
    void readFortune();
    void displayError(QLocalSocket::LocalSocketError socketError);
    void enableGetFortuneButton();

private:
    QLineEdit *hostLineEdit;
    QPushButton *getFortuneButton;
    QLabel *statusLabel;

    //在 Windows 上这是一个命名管道,在 Unix 上这是一个本地域套接字。
    //如果发生错误,则 error() 返回错误类型
    //并且可以调用 errorString() 以获取对发生的事情的人类可读描述
    //尽管 QLocalSocket 是为与事件循环一起使用而设计的
    //但也可以在没有事件循环的情况下使用它。
    //在这种情况下,您必须使用 waitForConnected()、waitForReadyRead()、
    //waitForBytesWritten() 和 waitForDisconnected(),它们会阻塞直到操作完成或超时到期。
    QLocalSocket *socket;
    //数据流是编码信息的二进制流,它 100% 独立于主机的操作系统、CPU 或字节顺序。
    //例如,Windows 下的 PC 写入的数据流可以被运行 Solaris 的 Sun SPARC 读取
    //您还可以使用数据流来读取/写入未编码的原始二进制数据
    //如果您想要“解析”输入流,请参阅 QTextStream。
    //QDataStream 类实现了 C++ 基本数据类型的序列化
    //如 char、short、int、char * 等
    //更复杂数据的序列化是通过将数据分解为原始单元来实现的。
    //数据流与 QIODevice 密切合作
    //QIODevice 代表一种可以从中读取数据和向其写入数据的输入/输出介质。
    //QFile 类是 I/O 设备的一个示例。
    /*
        QFile file("file.dat");
        file.open(QIODevice::WriteOnly);
        QDataStream out(&file); //we will serialize the data into the file
        out<<QString("the answer is"); //serialize a string
        out<<(qint32)43; //serialze an integer
    */
    //示例(从流中读取二进制数据):
    /*
        QFile file("file.dat");
        file.open(QIODevice::ReadOnly);
        QDataStream in(&file); //read the data serialized from the file
        QString str;
        qint32 a;
        in>>str>>a; //extra "the answer is" and 42
    */
    //写入流的每个项目都以预定义的二进制格式写入
    //该格式因项目的类型而异
    //支持的 Qt 类型包括 QBrush、QColor、QDateTime、QFont、
    //QPixmap、QString、QVariant 等
    //有关支持数据流的所有 Qt 类型的完整列表
    //请参阅序列化 Qt 数据类型。
    //对于整数,最好始终转换为 Qt 整数类型进行写入
    //然后读回相同的 Qt 整数类型
    //这可确保您获得所需大小的整数,并使您免受编译器和平台差异的影响。
    //枚举可以通过 QDataStream 进行序列化,而无需手动定义流操作符。
    //枚举类使用声明的大小进行序列化。
    //举一个例子,一个 char * 字符串被写成一个 32 位整数
    //等于包含 '\0' 字节的字符串的长度,后跟包含 '\0' 字节的字符串的所有字符
    //读取 char * 字符串时,读取 4 个字节以创建 32 位长度值,然后读取包含 '\0' 终止符的 char * 字符串的许多字符。
    //初始 I/O 设备通常在构造函数中设置,但可以使用 setDevice() 更改
    //如果您已经到达数据的末尾(或者如果没有设置 I/O 设备)atEnd() 将返回 true。

    //QDataStream 的二进制格式自 Qt 1.0 以来一直在发展
    //并且可能会继续发展以反映 Qt 中所做的更改。
    //在输入或输出复杂类型时,确保使用相同版本的流 (version()) 进行读取和写入非常重要
    //如果您需要向前和向后兼容,您可以在应用程序中硬编码版本号:
    //stream.setVersion(QDataStream::Qt_4_0);
    //如果您正在生成新的二进制数据格式
    //例如应用程序创建的文档的文件格式
    //您可以使用 QDataStream 以可移植格式写入数据。
    //常,您会编写一个简短的标题,其中包含一个魔法字符串和一个版本号,以便为自己未来的扩展留出空间
    /*
        QFile file("file.xxx");
        file.open(QIODevice::WriteOnly);
        QDataStream out(&file);

        //Write a header with  a "magic number" and a version
        out<<(quint32)0xA0B0C0D0;
        out<<(qint32)123;

        out.setVersion(QDataStream::Qt_4_0);

        //Write the data
        out<<lots_of_interesting_data;
    */
    //Then read it in with:
    /*
        QFile file("file.xxx");
        file.open(QIODevice::ReadOnly);
        QDataStream in(&file);

        //Read and check the header
        quint32 magic;
        in>>magic;
        if(magic != 0xA0B0C0D0)
            return XXX_BAD_FILE_FORMAT;

        //Read the version
        qint32 version;
        in >> version;
        if(version < 100)
            return XXX_BAD_FILE_TOO_OLD;
        if(version > 123)
            return XXX_BAD_FILE_TOO_NEW;

        if(version <= 100)
            in.setVersion(QDataStream::Qt_3_2);
        else
            in.setVersion(QDataStream::Qt_4_0);

        //Read the data
        in>>lots_of_interesting_data;
        if(version >= 120)
            in>>data_new_in_XXX_version_1_2;
        in>>other_interesting_data;
    */
    //您可以选择在序列化数据时使用的字节顺序
    //默认设置为大端(MSB 优先)。
    //将其更改为小端会破坏可移植性(除非读者也更改为小端)
    //此设置,除非您有特殊要求。您可以选择序列化数据时使用的字节顺序。
    //Reading and Writing Raw Binary Data
    //您可能希望直接从数据流读取/写入您自己的原始二进制数据
    //可以使用 readRawData() 从流中将数据读取到预分配的 char * 中。
    //类似地,可以使用 writeRawData() 将数据写入流
    //请注意,数据的任何编码/解码都必须由您完成。

    //一对类似的函数是 readBytes() 和 writeBytes()。
    //它们不同于它们的原始对应物如下:
    //readBytes() 读取一个 quint32,它被视为要读取的数据的长度
    //然后将该字节数读入预分配的 char *;
    //writeBytes() 写入包含数据长度的 quint32,后跟数据。 请注意
    //数据的任何编码/解码(除了长度 quint32)都必须由您完成。

    //Reading and Writing Qt Collection Classes
    //Qt 容器类也可以序列化为 QDataStream。
    //其中包括 QList、QSet、QHash 和 QMap。 流运算符被声明为类的非成员

    //Reading and Writing Other Qt Classes
    //除了此处记录的重载流运算符之外,您可能想要序列化为
    //QDataStream 的任何 Qt 类都将具有声明为非类成员的适当流运算符:
    /*
        QDataStream &operator<<(QDataStream& const Qxxx &);
        QDataStream &operator>>(QDataStream&, QXxx&);
    */    
    //例如,以下是声明为 QImage 类的非成员的流运算符:
    /*
        QDataStream & operator<<(QDataStream& stream, const QImage& image);
        QDataStream & operator>>(QDataStream& stream, QImage& image);
    */
    //要查看您最喜欢的 Qt 类是否定义了类似的流运算符
    //请查看类文档页面的相关非成员部分。


    //使用读取事务
    //当数据流在异步设备上运行时,数据块可以在任意时间点到达。
    //QDataStream 类实现了一种事务机制,
    //该机制提供了使用一系列流操作符以原子方式读取数据的能力。
    //例如,您可以通过在连接到 readyRead() 信号的插槽中使用事务来处理来自套接字的未完成读取:
    /*
        in.startTransaction();
        QString str;
        qint32 a;
        in>>str>>a;//try to read packet atomically

        if(!in.commitTransction())
            return; //wait for more data
    */
    
    QDataStream in;
    quint32 blockSize;

    QString currentFortune;
};

#endif

client.cpp

#include <QtWidgets>
#include <QtNetwork>

#include "client.h"

Client::Client(QWidget *parent)
    : QDialog(parent),
      hostLineEdit(new QLineEdit("fortune")),
      getFortuneButton(new QPushButton(tr("Get Fortune"))),
      statusLabel(new QLabel(tr("This examples requires that you run the "
                                "Local Fortune Server example as well."))),
      socket(new QLocalSocket(this))
{
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
    QLabel *hostLabel = new QLabel(tr("&Server name:"));
    hostLabel->setBuddy(hostLineEdit);

    statusLabel->setWordWrap(true);

    getFortuneButton->setDefault(true);
    QPushButton *quitButton = new QPushButton(tr("Quit"));
    //void QButtonGroup::addButton(QAbstractButton *button, int id = -1)
    //将给定的按钮添加到按钮组
    //如果 id 为 -1,则会为按钮分配一个 id。
    //自动分配的 ID 保证为负,从 -2 开始。 如果您要分配自己的 ID
    //请使用正值以避免冲突。
    //对话框和消息框通常以符合该平台界面指南的布局呈现按钮
    //不同平台的对话框总是有不同的布局
    //QDialogButtonBox 允许开发人员向其添加按钮
    //并将自动为用户的桌面环境使用适当的布局。
    //对话框的大多数按钮都遵循某些角色。 此类角色包括:
    //接受或拒绝对话。
    //寻求帮助。
    //对对话框本身执行操作(例如重置字段或应用更改)。
    //也可以有其他方式关闭对话框,这可能会导致破坏性结果。
    //大多数对话框都有几乎可以被视为标准的按钮(例如确定和取消按钮)。
    //有时以标准方式创建这些按钮会很方便。
    //有几种使用 QDialogButtonBox 的方法
    //一种方法是自己创建按钮(或按钮文本)并将它们添加到按钮框
    //指定它们的作用。
    /*
        findButton = new QPushButton(tr("&Find"));
        findButton->setDefault(true);

        moreButton = new QPushButton(tr("&More"));
        moreButton->setCheckable(true);
        moreButton->setAutoDefault(false);
    */
    //或者,QDialogButtonBox 提供了几个您可以使用的标准按钮
    //(例如确定、取消、保存)。 它们作为标志存在,因此您可以在构造函数中将它们组合在一起。
    /*
        buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
                                        | QDialogButtonBox::Cancel);
        connect(buttonBox,&QDialogButtonBox::accept,this,&QDialog::accept);
        connect(buttonBox,&QDialogButtonBox::rejected,this,&QDialog::reject);
    */
    //您可以混合搭配普通按钮和标准按钮。
    //目前,如果按钮框是水平的,按钮的布局方式如下:

    QDialogButtonBox *buttonBox = new QDialogButtonBox;
    buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
    //void QDataStream::setDevice(QIODevice *d)
    //将 I/O 设备设置为 d,可以为 nullptr 以取消设置为当前 I/O 设备。
    in.setDevice(socket);
    //void QDataStream::setVersion(int v)
    //将数据序列化格式的版本号设置为 v,即 Version 枚举的值。
    in.setVersion(QDataStream::Qt_5_10);

    connect(hostLineEdit, &QLineEdit::textChanged,
            this, &Client::enableGetFortuneButton);
    connect(getFortuneButton, &QPushButton::clicked,
            this, &Client::requestNewFortune);
    connect(quitButton, &QPushButton::clicked, this, &Client::close);
    connect(socket, &QLocalSocket::readyRead, this, &Client::readFortune);
    connect(socket, &QLocalSocket::errorOccurred, this, &Client::displayError);

    QGridLayout *mainLayout = new QGridLayout(this);
    mainLayout->addWidget(hostLabel, 0, 0);
    mainLayout->addWidget(hostLineEdit, 0, 1);
    mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
    mainLayout->addWidget(buttonBox, 3, 0, 1, 2);

    setWindowTitle(QGuiApplication::applicationDisplayName());
    hostLineEdit->setFocus();
}

void Client::requestNewFortune()
{
    getFortuneButton->setEnabled(false);
    blockSize = 0;
    //中止当前连接并重置套接字。
    //与 disconnectFromServer() 不同,
    //此函数会立即关闭套接字,清除写入缓冲区中的所有挂起数据。
    socket->abort();
    //[since 5.1] void QLocalSocket::connectToServer(QIODeviceBase::OpenMode openMode = ReadWrite)
    //尝试与 serverName() 建立连接。
    //在打开连接之前必须调用 setServerName()。
    //或者,您可以使用 connectToServer(const QString &name, OpenMode openMode);    
    //套接字在给定的 openMode 中打开并首先进入 ConnectingState。
    //如果建立了连接,QLocalSocket 进入 ConnectedState 并发出 connected()。
    //调用此函数后,套接字可以发出 errorOccurred() 以表示发生了错误。
    socket->connectToServer(hostLineEdit->text());
}

//[virtual] qint64 QIODevice::bytesAvailable() const
//返回可供读取的字节数。
//此函数通常与顺序设备一起使用,以确定读取前要在缓冲区中分配的字节数。
//重新实现此函数的子类必须调用基本实现以包含 QIODevice 的缓冲区大小。 例子:
/*
    qint64 CustomDevice::bytesAvailable() const
    {
        return buffer.size() + QIODevice::bytesAvailable();
    }
*/
void Client::readFortune()
{
    if (blockSize == 0) {
        // Relies on the fact that QDataStream serializes a quint32 into
        // sizeof(quint32) bytes
        if (socket->bytesAvailable() < (int)sizeof(quint32))
            return;
        in >> blockSize;
    }

    if (socket->bytesAvailable() < blockSize || in.atEnd())
        return;

    QString nextFortune;
    in >> nextFortune;

    if (nextFortune == currentFortune) {
        QTimer::singleShot(0, this, &Client::requestNewFortune);
        return;
    }

    currentFortune = nextFortune;
    statusLabel->setText(currentFortune);
    getFortuneButton->setEnabled(true);
}

void Client::displayError(QLocalSocket::LocalSocketError socketError)
{
    switch (socketError) {
    case QLocalSocket::ServerNotFoundError:
        QMessageBox::information(this, tr("Local Fortune Client"),
                                 tr("The host was not found. Please make sure "
                                    "that the server is running and that the "
                                    "server name is correct."));
        break;
    case QLocalSocket::ConnectionRefusedError:
        QMessageBox::information(this, tr("Local Fortune Client"),
                                 tr("The connection was refused by the peer. "
                                    "Make sure the fortune server is running, "
                                    "and check that the server name "
                                    "is correct."));
        break;
    case QLocalSocket::PeerClosedError:
        break;
    default:
        QMessageBox::information(this, tr("Local Fortune Client"),
                                 tr("The following error occurred: %1.")
                                 .arg(socket->errorString()));
    }

    getFortuneButton->setEnabled(true);
}

void Client::enableGetFortuneButton()
{
    getFortuneButton->setEnabled(!hostLineEdit->text().isEmpty());
}

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值