Qt君


  • 首页

  • 关于

  • 归档

  • 搜索

获取Linux网卡信息

发表于 2019-08-23

代码示例获取网卡信息。

通过命令获取

  • ARP(Address Resolution Protocol)地址解析协议。
  • 执行cat /proc/net/arp得到以下信息:
    1
    2
    3
    4
    ubuntu:~$ cat /proc/net/arp
    IP address HW type Flags HW address Mask Device
    192.168.72.2 0x1 0x2 00:50:56:f4:70:28 * ens33
    192.168.72.254 0x1 0x2 00:50:56:ed:51:f7 * ens33

其中,HW type为硬件类型

值 类型
0x01 ether (Ethernet)
0xf dlci (Frame Relay DLCI)
0x17 strip (Metricom Starmode IP)

通过代码获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <list>

using namespace std;

struct NetworkInfo {
string deviceName;
string ip;
string mask;
int hardwareType; /* 硬件类型 */
string macAddress;
};

list<NetworkInfo> getAllNetworkInfo()
{
list<NetworkInfo> result;
char buf[128] = {0};

FILE *fp = fopen("/proc/net/arp", "r");
if (fp == NULL)
return result;

/* 移除第一行无关内容 */
if (fgets(buf, sizeof(buf), fp) == NULL)
return result;

memset(buf, 0, sizeof(buf)); // 重置缓冲区
while(fgets(buf, sizeof(buf), fp) != NULL) {

char ip[16] = {0};
char macAddress[32] = {0};
char mask[16] = {0};
char deviceName[512] = {0};
int hardwareType = 0;
int flags = 0;
/* 读数据 */
sscanf(buf, "%s 0x%x 0x%x %s %s %s\n",
ip,
&hardwareType,
&flags,
macAddress,
mask,
deviceName);

/* 装载数据 */
NetworkInfo info;
info.deviceName = deviceName;
info.ip = ip;
info.mask = mask;
info.hardwareType = hardwareType;
info.macAddress = macAddress;

result.push_back(info);

memset(buf, 0, sizeof(buf)); // 重置缓冲区
}

fclose(fp);

return result;
}

windows系统不能创建的文件名

发表于 2019-08-21

使用git clone一个仓库怎么也克隆不成功,一查原因竟是windows系统下不能创建某些文件(夹)。

重现问题

  • 执行git clone xxx报以下错误。

    1
    2
    3
    4
    fatal: cannot create directory at 'CON': Invalid argument
    warning: Clone succeeded, but checkout failed.
    You can inspect what was checked out with 'git status'
    and retry the checkout with 'git checkout -f HEAD'
  • 按照指示执行git checkout -f HEAD依然报下列错误。

    1
    fatal: cannot create directory at 'CON': Invalid argument

问题分析

  • 由于git clone操作会将远程仓库文件下载到本地,如果远程仓库存在CON文件,导致git创建不了windows系统保留的关键词,导致克隆失败。
  • 本地试了一下创建一个CON文件结果报以下错误:
    插图

问题解决

  • 将远程仓库的CON文件重命名即可解决问题。

总结

  • 避免创建windows系统保留的文件名字。
  • 从微软官方文档找到以下不可使用的预留名称:
    CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9。
  • 同时还要避免这些名称后面紧跟着一个扩展名。
    1
    https://docs.microsoft.com/zh-cn/windows/win32/fileio/naming-a-file

关于更多

  • Qt君公众号后台回复”git“获取更多相关信息。

简易创建绿色安装包方法(图文)

发表于 2019-08-20

将一堆的库和程序打包成一个exe执行文件的方法。

所需工具

  • WinRAR压缩工具(版本:5.61.0)

步骤1

  • 准备需要打包的程序和库。
    插图

步骤2

  • ctrl + a全选程序和库;
  • 右键点击”添加到压缩文件“。
    插图

步骤3

  1. 设置压缩文件名字;
  2. 勾选“创建自解压格式压缩文件“;
  • 注意: 程序名字后要加exe后缀。
    插图

步骤4

  • 选择”高级“ -> “自解压选项(X)“。
    插图

步骤5

  • 进入”高级自解压选项“界面。
    插图

步骤6

  1. 选择”设置“;
  2. 设置解压后程序运行名字(在被压缩的文件目录里选)。
    插图

步骤7

  1. 选择“模式”;
  2. 勾选“解压到临时文件夹”;
  3. 选中“全部隐藏(H)”。
    插图

步骤8

  1. 选择“更新”;
  2. 选中“解压并替换文件”;
  3. 选中“覆盖所有文件”。
    插图

步骤9

  1. 选择“文本和图标”;
  2. 添加压缩后的图标。
  3. 最后确定生成。
    插图

验证

  • 双击运行即可验证是否压缩成功。
    插图

关于更多

  • Qt君公众号后台回复”打包工具“获取更多相关信息。

10进制与16进制字符串互转

发表于 2019-08-19

使用sprintf与strtoull函数实现互转操作。

10进制转16进制字符串

1
2
3
4
unsigned long long ullValue = 18446744073709551615;
char buf[18] = {0};
sprintf(buf, "0x%llX", ullValue);
// result: 0xFFFFFFFFFFFFFFFF

16进制字符串转10进制

1
2
3
const char *buf = "0xFFFFFFFFFFFFFFFF";
unsigned long long result = strtoull(buf, NULL, 16);
// result: 18446744073709551615

嵌入代码的二维码字符

发表于 2019-08-18

方便地将需要转换的字符转换为二维码字符。方便开发者可以直接将二维码以字符的方式放到代码中,扫一扫就可以快速打开内容或网址。

1.转换代码

  • 输入需要转换的字符返回二维码字符。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    QString toQRcode(const QString &plain)
    {
    /* Create the QR code */
    QRCode qrcode;
    uint8_t qrcodeData[qrcode_getBufferSize(3)] = {0};
    qrcode_initText(&qrcode,
    qrcodeData,
    3,
    0,
    plain.toStdString().c_str());

    QString result;
    for (uint8_t y = 0; y < qrcode.size; y++) {
    /* Each horizontal module */
    for (uint8_t x = 0; x < qrcode.size; x++) {
    /* Print each module (UTF-8 \u2588 is a solid block) */
    result += qrcode_getModule(&qrcode, x, y) ?
    QString("\u2588\u2588") : QString(" ");
    }

    result.append(QString("\r\n"));
    }

    return result;
    }

1
2
3
4
5
6
int main(int argc, char *argv[])
{
QString result = toQRcode("http://weixin.qq.com/r/p0xudjXEUmgtrXEV9xm1");
qDebug().noquote()<<result;
return 0;
}

2.转换效果

  • 扫描二维码试试吧。
    插图

使用qmake为程序添加版本信息

发表于 2019-08-17

使用qmake自动创建自定义宏(版本,git提交ID,提交分支,编译时间)。在程序发布时使用特别有效。不多说直接上代码。

项目文件(.pro)

1
2
3
4
5
6
7
8
9
10
DEFINES += APP_VERSION=\\\"'v1.0.0'\\\"

win32 {
DEFINES += APP_COMMIT_ID=\\\"'$$system(cmd /c git rev-parse HEAD)'\\\"
DEFINES += APP_BRANCH_NAME=\\\"'$$system(cmd /c git symbolic-ref --short -q HEAD)'\\\"
}
else {
DEFINES += APP_COMMIT_ID=\\\"'$(shell git rev-parse HEAD)'\\\"
DEFINES += APP_BRANCH_NAME=\\\"'$(shell git symbolic-ref --short -q HEAD)'\\\"
}

在main.cpp中使用自定义宏

1
2
3
4
5
6
7
8
9
10
#include <QDebug>

int main(int argc, char *argv[])
{
qDebug() << "App version: " << APP_VERSION;
qDebug() << "App build time: " << __DATE__ << __TIME__;
qDebug() << "App commit id: " << APP_COMMIT_ID;
qDebug() << "App branch name: " << APP_BRANCH_NAME;
return 0;
}

相关知识

  • 使用qmake的DEFINES变量可以自定义宏,而需要宏提供具体的值可以这样做:

    1
    2
    DEFINES  += MY_MACRO=\\\"'{value}'\\\"
    注意:"MY_MACRO=\"之间不能带有空格
  • 一些qmake平台宏

平台 语法
Windows win32
Mac macx
Linux unix:!macx
Unix unix
  • 程序中__DATE__和__TIME__为编译内置宏;

关于更多

  • Qt君公众号后台回复”qmake“获取更多相关知识。

你这样做是错的…(翻译文)

发表于 2019-08-16

本文翻译自: https://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
作者: Bradley T. Hughes

  我们广泛使用IRC(网上交谈)与我们自己以及社区进行沟通。我在Freenode网络的Qt频道上闲逛,尽我所能帮助别人。

  我看到的一个常见问题(这让我同时感到害怕)与理解Qt的线程以及如何编写一些他们编写的代码有关。人们根据他们的代码展示他们的代码或示例,我经常最终会想到:

你这样做是错的

  我知道这有点大胆,或许有点挑衅,但与此同时,我不禁认为下面的(假设的)类是面向对象原则的不正确应用以及Qt的使用不正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyThread : public QThread
{
public:
MyThread()
{
moveToThread(this);
}

void run();

signals:
void progress(int);
void dataReady(QByteArray);

public slots:
void doWork();
void timeoutHandler();
};

  我对这段代码的最大的不满是moveToThread(this),我看到很多人在不了解它的情况下使用它。你问,它做了什么?moveToThread()函数告诉Qt确保从指定的线程上下文中调用事件处理程序以及扩展的信号和槽。

  QThread是线程接口,所以我们告诉线程“自己”运行代码。我们也在线程运行之前进行此操作。尽管这看起来可行,但它让人困惑,而不是QThread的设计用途(QThread中的所有函数都是从创建线程而不是QThread启动的线程中编写和调用的)。

  我的印象是,moveToThread(this)。因为他们在某个地方看到了一些使用它的博客。一个快速的网络搜索出现了这些博客中的几个,所有这些都遵循上面类中的模式:

  • 子类QThread
  • 添加信号和插槽以完成工作
  • 测试代码,看到插槽没有“从正确的线程”调用
  • 问Google,找到moveToThread(this),并评论“当我添加这个时它似乎工作”

  在我看来,问题始于moveToThread(this)。QThread的设计目的是用作操作系统线程的接口或控制点,而不是用于放置要在线程中运行的代码的位置。我们面向对象的程序是子类,因为我们想要扩展或专门化基类功能。

  我可以想到的继承QThread的唯一有效理由是添加QThread没有的功能,例如可能提供指向内存的指针以用作线程的堆栈,或者可能添加实时接口支持。不应将用于下载文件,查询数据库或进行任何其他类型处理的代码添加到QThread的子类中,它应该封装在它自己的对象中。

  通常,这意味着只需将类更改为继承自QObject而不是QThread,并且可能更改类名。QThread有一个started()信号,您可以在需要执行某些初始化时连接到该信号。要实际让代码在新线程上下文中运行,您需要实例化QThread并使用该moveToThread()函数将对象分配给该线程。

  即使您仍在使用moveToThread()告诉Qt在特定线程上下文中运行代码,我们仍然将线程接口保持独立。如有必要,现在可以将您的类的多个实例分配给单个线程,或者将多个不同类的多个实例分配给单个线程。换句话说,没有必要将类的单个实例绑定到单个线程。

  我对编写线程化Qt代码所带来的困惑负有很大责任。原始的QThread类是抽象的,因此子类化是必要的。直到Qt4.4,QThread::run()才获得默认实现。

  以前,使用QThread的唯一方法是子类。随着线程关联的增加以及对不同关联对象之间的信号和槽连接的支持,我们突然有了一种处理线程的方便方法。我们喜欢方便,我们想用它。不幸的是,我后来才意识到,强迫人们将继承QThread实际上使它变得比需要的更难。

最后

  • 文章有删减
  • 公众号后台回复”翻译“获取更多相关内容。

C/C++/Qt屏蔽输出流技巧

发表于 2019-08-15

教大家如何屏蔽printf,cout,qDebug等打印信息。

1. MSVC编译器

  • 将两个“/”连接成“//”实现注释是MSVC编译器扩展功能。

    1
    2
    3
    #define cout /##/
    #define printf /##/
    #define qDebug /##/
  • 启用宏替换后变为注释语句

    1
    2
    printf("Hello world!");
    相当于: //("Hello world!")
1
2
cout << "Hello world!"; 
相当于: // <<("Hello world!")
1
2
qDebug() << "Hello world!"; 
相当于: // <<("Hello world!")

2. 直接关闭流

2.1 POSIX标准的编译器

1
2
#include <unistd.h>
close(STDOUT_FILENO);

2.2 调用stdio标准库

  • 注意:调用fclose()之后对流的任何使用导致了未定义的行为,不建议使用。
    1
    2
    #include <stdio.h>
    fclose(stdout);

2.3 使用注意

  • 关闭输出流后下列类似的打印将不会输出
  • 注意:关闭输出流后没有方法重新为它打开。
    1
    2
    3
    printf("Hello world!");
    cout << "Hello world!";
    qDebug() << "Hello world!";

3. Qt库实现

  • 在Qt项目文件添加以下定义:
  1. 屏蔽qInfo()

    1
    DEFINES += QT_NO_INFO_OUTPUT
  2. 屏蔽qDebug()

    1
    DEFINES += QT_NO_DEBUG_OUTPUT
  3. 屏蔽qWarning()

    1
    DEFINES += QT_NO_WARNING_OUTPUT

关于更多

  • 公众号后台回复”技巧“获取相关文章。

接口思考小笔记

发表于 2019-08-14

学习上的笔记。

具体性接口

  • 从代码上看问题不大,接口也够直观。
  • 缺点是,如果需要添加多个服务接口需要额外增加相应的接口。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Media {
    public:
    bool audioService();
    void setAudioService(bool enable);

    bool videoService();
    void setVideoService(bool enable);

    bool tvService();
    void setTvSerrvice(bool enable);

    bool broadcastService();
    void setBroadcastService(bool enable);

    ...
    };

统一性接口

  • 这里有一个好处就是避免了上一个接口操作所带来的多接口的问题。
  • 相同服务类型的可以使用一个枚举来包含,如果不同的服务类型使用这个方法就会觉得很怪(意义上的不明确)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    enum MediaServices {
    Audio,
    Video,
    TV,
    Broadcast
    };

    bool services(MediaServices service);
    void setServices(MediaServices service, bool enable);

简洁的接口

  • 下面这个例子它不再需要维护一个枚举类型,接口更简洁。
  • 但付出的代价是,时刻需要避免字符串容易写错的问题。
    1
    2
    3
    /* like: Audio, Video, TV, Broadcast */
    bool services(const char *service);
    void setServices(const char *service, bool enable);

进一步的思考

  • 无论上面的哪一种方式,都需要保存对应的变量值。
  • 常规做法是分别用四个变量保存(AudioService,VideoService,TVService,BoardcastService)。
  • 同样地思考,如果存在多个变量则需要添加多个变量。
    想了想这里我们可以这样做(通过操控整型异或来存储四个布尔变量):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class Media {
    public:
    enum MediaServices {
    Audio = 0x00000001,
    Video = 0x00000002,
    TV = 0x00000004,
    Broadcast = 0x00000008
    };

    bool services(MediaServices service)
    {
    return service & m_services;
    }

    void setServices(MediaServices service, bool enable)
    {
    if (enable)
    m_services |= service;
    else {
    m_services &= ~service;
    }
    }

    private:
    int m_services;
    };

最后

  • 无论是哪一种方法都有优缺点,没有谁更好,只有更适合你。
  • 公众号后台输入”接口“获取相关文章。

串口示例(带折线图数据绘制)

发表于 2019-08-13

一个简单的串口示例,并将数据予以图表化显示,让数据体现得更直观。

插图

串口部分代码

  • 查找可用串口列表

    1
    2
    3
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
    ...
    }
  • 设置串口名

    1
    m_serialPort.setPortName(ui->PortBox->currentText());
  • 设置波特率

    1
    m_serialPort.setBaudRate(ui->BaudBox->currentText().toInt());
  • 设置数据位数

    1
    m_serialPort.setDataBits(QSerialPort::Data8);
  • 设置奇偶校验

    1
    m_serialPort.setParity(QSerialPort::NoParity);
  • 设置停止位

    1
    2
    3
    4
    5
    switch(ui->StopBox->currentIndex()) {
    case 1: m_serialPort.setStopBits(QSerialPort::OneStop); break;
    case 2: m_serialPort.setStopBits(QSerialPort::TwoStop); break;
    default: break;
    }
  • 设置流控制

    1
    m_serialPort.setFlowControl(QSerialPort::NoFlowControl);
  • 打开串口

    1
    m_serialPort.open(QIODevice::ReadWrite);

图表代码部分

  折线图基于qcustomplot库绘制。
插图

  • 向绘图区域添加一条曲线

    1
    ui->qCustomPlot->addGraph();
  • 设置坐标轴标签名称

    1
    2
    ui->qCustomPlot->xAxis->setLabel("次数");
    ui->qCustomPlot->yAxis->setLabel("温度");
  • 设置坐标轴显示范围

    1
    2
    ui->qCustomPlot->xAxis->setRange(0, 10);
    ui->qCustomPlot->yAxis->setRange(0, 100);
  • 图表数据更新函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void onUpdateChart()
    {
    ...
    m_xs.append(m_xLength);
    m_ys.append(m_serialUpdateData);
    m_serialUpdateData = 0;

    ui->qCustomPlot->replot();
    ui->qCustomPlot->graph(0)->setData(m_xs, m_ys);

    m_xLength++;
    }

关于更多

  • 源码地址:
    https://github.com/aeagean/SerialPort

  • 公众号回复”串口“即可获取串口相关的文章。

1…91011…32
Qt君

Qt君

313 日志
41 标签
© 2019 Qt君
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4
粤ICP备 - 16070052号