Qt君


  • 首页

  • 关于

  • 归档

  • 搜索

Qmake设置输出文件路径与名字

发表于 2019-06-14

通过设置Qt项目文件来设置程序/库的生成路径与名字。

设置输出名字

  • Test为设置输出名字;
  • 如果是执行文件则自动为程序添加后缀(.exe),如果是库文件则自动为库添加后缀(.dll)和libxxx.a(这里是libTest.a)名字。
    1
    TARGET = Test

设置输出路径

  • 输出为.pro项目所在的Test目录下。
  • $$PWD意思为当前目录。
    1
    DESTDIR = $$PWD/Test

另外

1
2
TEMPLATE = app # 设置为执行文件工程
TEMPLATE = lib # 设置为库文件工程

Qt开源网络库[6]-超时功能

发表于 2019-06-13

距离上一系列篇已经有半年没有更新了。本次介绍该网络库最近新增的超时功能(超时中断请求)。由于Qt的网络请求不能设置超时时间,故只能额外封装了。

接口

  • timeout通过msec参数设置超时时间;
  • 当msec <= 0则禁用超时功能;
  • 当msec > 0则使能超时功能,并将超时时间设置为msec毫秒。
    1
    2
    3
    4
    5
    /**
    * @brief msec <= 0, disable timeout
    * msec > 0, enable timeout
    */
    HttpRequest &timeout(const int &msec = -1);

实现

  • HttpResponseTimeout构造函数传递QNetworkReply与timeout参数用于超时中断设置;
  • QTimer::singleShot为单次定时器;
  • 当定时器超时后则会执行onTimeout函数;
  • 而onTimeout函数会执行QNetworkReply的abort和deleteLater来完成请求中断。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class HttpResponseTimeout : public QObject {
    Q_OBJECT
    public:
    HttpResponseTimeout(QNetworkReply *parent = NULL, const int timeout = -1) : QObject(parent) {
    if (timeout > 0)
    QTimer::singleShot(timeout, this, SLOT(onTimeout()));
    }

    private slots:
    void onTimeout() {
    QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
    if (reply->isRunning()) {
    reply->abort();
    reply->deleteLater();
    }
    }
    };

示例

  • 使用示例设置30秒的超时时间,超时结束后将会强制中断当前请求。
    1
    2
    3
    4
    5
    6
    7
    static HttpService http;
    http.get("http://www.qtbig.com")
    .onResponse(this, SLOT(finish(QByteArray))) /* 接收数据 */
    .onResponse(this, SLOT(downloadProgress(qint64,qint64))) /* 接收进度 */
    .onError(this, SLOT(error(QNetworkReply::NetworkError, QNetworkReply*))) /* 错误处理 */
    .timeout(30*1000) /* 30s */
    .exec();

Http请求不阻塞ui操作

发表于 2019-06-12

利用QEventLoop与QNetworkAccessManager实现网络请求不阻塞ui的操作。

使用场景

  • 当我们发送一个网络请求时,持续等待发送回来的数据,再进行下一步操作,但是期间ui不能阻塞的情况。

示例

  • QEventLoop类提供一种进入和退出事件循环的方法。
  • exec与quit分别为进入和退出事件循环。
    1
    2
    3
    4
    5
    6
    7
    8
    QNetworkAccessManager manager;
    QNetworkRequest request(QUrl("http://www.qtbig.com"));
    QNetworkReply* reply = manager.get(request);
    QEventLoop eventLoop;
    QObject::connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
    eventLoop.exec(); // 进入等待,但ui事件循环依然进行。

    QByteArray result = reply->readAll();

Vim技巧-光标定位

发表于 2019-06-11

Vim的光标是经常使用的功能。常见的光标定位有鼠标移动,操控上下左右键盘,除了这些还有更多的光标定位方法。

操作 功能
h 光标左移
j 光标下移
k 光标上移
l 光标右移
shift + 4 光标移至行首
大写I 光标移至行首并插入模式
0 光标移至行尾
大写A 光标移至行尾并插入模式
gg或[[ 光标移至文件开头
大写G或]] 光标移至文件结尾
数字 + gg 光标移至指定行
ctrl + b 光标移至上翻页处
ctrl + f 光标移至下翻页处

解决cpp添加QObject派生类的问题

发表于 2019-06-10

一般我们继承QObject类在头文件中添加,但是有时候需要在源文件中添加。这样就会不可避免地出现编译错误。那么我们该究竟怎么解决它呢?

问题重现

  • 执行下列源码会报以下错误:

    1
    2
    error: undefined reference to `vtable for Object'
    错误: 未定义引用'Object'的虚函数表
  • 问题源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /* main.cpp */
    #include <QCoreApplication>

    class Object : public QObject {
    Q_OBJECT
    public:
    Object() { }
    };

    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);
    Object object;
    return a.exec();
    }

问题分析

  • 这是Qt的moc工具不识别cpp导致。
  • 由于moc工具不识别cpp文件中继承于QObject的类,这样就不能自动为该类添加一些实现函数(元对象函数),就会导致函数未定义的错误。
  • 当qmake运行起来时,moc工具会自动识别处理头文件中所有继承于QObject的类和一系列Qt元对象属性。
  • 要想让moc识别cpp文件,只需要包含#include 当前文件.moc就可以了。

解决步骤(方法)

  1. 包含当前文件.moc到其类定义下或最后一行(注意:不能Object类先于定义);
  2. 清除项目->qmake->编译。
  • 修正源码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /* main.cpp */
    #include <QCoreApplication>

    class Object : public QObject {
    Q_OBJECT
    public:
    Object() { }
    };

    #include "main.moc"

    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);
    Object object;
    return a.exec();
    }

QML教程-属性绑定与非绑定

发表于 2019-06-09

写QML界面会经常使用到很多的属性,其中属性的绑定与解绑尤其重要,决定着该界面属性是否能动态更新的功能。本文介绍属性的绑定与非绑定特性。

1.属性绑定

  • 使用:操作符,作用是左值绑定右值。
  • 例:

    1
    2
    3
    4
    5
    6
    7
    8
    Item {
    property color myColor: "white"
    ...
    Rectangle {
    color: myColor
    ...
    }
    }
  • color绑定父控件myColor属性。

2.属性非绑定(解除绑定)

  • 使用=操作符,作用是左值解除右值绑定
  • 例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Item {
    property color myColor: "white"
    ...
    Rectangle {
    color: myColor
    ...
    Component.onCompleted: color = "blue"
    }
    }
  • 当color = "blue"被执行时会解除color: myColor的绑定

3.属性重新绑定

  • 使用Binding控件对已经解除绑定的属性重新绑定。
  • 例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Item {
    property color myColor: "white"
    ...
    Rectangle {
    color: myColor
    ...
    Component.onCompleted: color = "blue"
    }

    Binding {
    target: rect
    property: "color"
    value: root.myColor
    }
    }
  • target为被绑定(左值)的id;

  • property为被绑定(左值)的属性,注意类型为字符串;
  • value为绑定(右值)的属性。

4.类比原理

  • 绑定原理相当于C++的引用或指针。
  • 非绑定原来相当于C++的赋值,将值复制一份。

5.什么情况下使用绑定与非绑定?

  • 使用绑定特性会导致不同对象的属性之间存在依赖关系,对界面动态刷新会有影响。
  • 比如:翻译刷新问题。 如果翻译字段被绑定了,那么翻译字段刷新将会更新所有的文字,这样会导致界面的突然卡顿(如果翻译字段过多)。这时候建议是解除绑定可以降低界面的突然卡顿。
  • 绑定容易影响性能,但刷新界面方便。
  • 非绑定虽然要做些额外的工作(赋值)但可以降低对界面的刷新,特别是图表类。

Qt程序打包库瘦身的方法

发表于 2019-06-08

使用windeployqt程序生成的库都很大,即使是一个空的程序。对比了一下程序,有些库根本就没有用到却也是打包进来了。本文提供一种剔除多余程序库以达到瘦身的方法。

  当一个程序打开运行时候,如果是缺少了库,系统将会弹出对应库找不到的提示对话框。为什么会这样?因为程序运行前先会加载动态库部分到内存以供程序运行调用。

  使用反向思维,我们同样地将程序运行起来,然后将程序目录下的库删除,如果能删除的就证明程序没有使用到该库,如果正在被程序使用的库是不能被删除的。利用这一特性可以将一部分库剔除。

  当然,这一方法其实并不靠谱,不建议操作。我们需要学习的是反向操作的思维,编程有时候需要那么一点点的反式操作却是能在其中找到乐趣。一方面思维定性是学编程的最大优点,另一方面也是最大的缺点。反向操作在生活中常常能有奇效,比如倒立洗头,看似荒唐(想着这一场景却是有些好笑),但他能完美解决洗头需要闭上眼睛的问题。

正确方法

  • 使用windows接口CreateToolhelp32Snapshot,它可以通过获取进程信息为指定的进程、进程堆、模块、线程建立一个快照。
  • 同过获取进程调用的动态库从而进行准确的剔除多余的库。

浅谈警告即错误

发表于 2019-06-07

编译警告给人的感觉是让人注意这个问题。更有甚者认为,编译警告只不过是给过分小心的人看。事实上为什么会有警告这一个问题本身值得我们去思考?

  昨天,新来不久的同事调试一个准备上线的功能,可是怎么也调试不出来。当时刚好在喝水,看到他几百个警告。就和他说你还是处理一下编译警告吧。他一脸焦虑地说:没空弄啊?!功能快要上线了,来帮我看看。他还说,如果是很严重的警告,会导致编译错误,能编译过就可以了。

  看着有些无奈,对此非常地不认同。看着他焦虑的表情,先解决问题先。
浏览代码看到有一条警告进入法眼,是这样的use '==' to turn this assignment into an equality comparison.,意思是使用'=='将该赋值转换为相等比较。找到相应的代码:

1
2
3
if (isEmpty = true) {
...
}

  同事看了看代码,我知道问题出在哪里了。今晚大吉大利,请你吃鸡腿。既然解决问题了,你也应该解决一下你那些编译警告,这些警告里面可能隐藏着很多bug。

  比如呢?同事一脸好学的样子。看到你程序里面有很多变量未使用的警告,你应该清理一下它。因为变量可能真的没使用,又可能是你错误使用了其他变量导致。后者带来的后果可能让你的程序刚好能运行,但某些情况下可能出现致命的错误。

  正当我转身准备下班的时候,他又问,那我调一下编译警告提升为编译错误这样可以吗?

  可以是可以,但是要分情况。如果你是新写的程序你可以这样做,如果是已经写了一大部分的项目,如果你这样做可能会造成颠覆性的后果,导致难以控制。因为编译器能轻易地将编译警告变为编译错误,可是你不能轻易地解决它。

  那我应该怎么做?
  一步一步来吧。新加的警告你就在编写的时候解决它,而旧的警告也要谨慎地解决。对于GCC编译器你可以使用-Werror参数来让警告不能忽略。让过是MSVC编译器,你可以改变项目的设置。另外一般IDE都有设置怎么将编译警告作为编译错误的操作。

QList的at与[]10亿次运行速度比较

发表于 2019-06-06

看看谁的速度快?

环境

  • windows10系统
  • Qt4.8.7(gcc 4.9.2)
  • Qt5.12.3(gcc 7.3.0)
  • Qt Debug构建
  • 10亿次操作比较

对比

  • 单位ms
  • 源码最后附录
Qt版本 参考 at const at [] const []
4.8.7 14 278 279 639 629
5.12.3 14 325 322 418 411

分析

  • Qt5.12.3整体运行速度快于Qt4.8.7;
  • 无论是Qt4.8.7或是Qt5.12.3的运行速度at都优于[];
  • 在Qt4.8.7版本下at和[]运行速度相差不大;
  • 在Qt5.12.3版本下at和[]运行速度相差不大。

附录

  • Qt4.8.7与Qt5.12.3的相同QList源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template <typename T>
    inline const T &QList<T>::at(int i) const
    { Q_ASSERT_X(i >= 0 && i < p.size(), "QList<T>::at", "index out of range");
    return reinterpret_cast<Node *>(p.at(i))->t(); }

    template <typename T>
    inline const T &QList<T>::operator[](int i) const
    { Q_ASSERT_X(i >= 0 && i < p.size(), "QList<T>::operator[]", "index out of range");
    return reinterpret_cast<Node *>(p.at(i))->t(); }

    template <typename T>
    inline T &QList<T>::operator[](int i)
    { Q_ASSERT_X(i >= 0 && i < p.size(), "QList<T>::operator[]", "index out of range");
    detach(); return reinterpret_cast<Node *>(p.at(i))->t(); }
  • 测试源码

    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
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    #include <QList>
    #include <QElapsedTimer>
    #include <QUuid>
    #include <QDebug>

    #define ForLoop for (unsigned int i = 0; i < 10*1000*1000; i++)
    static QList<int> list;
    static QElapsedTimer timer;

    qint64 runningTime()
    {
    timer.start();

    ForLoop {
    int count = 1 + 2 + 3;
    }

    return timer.elapsed();
    }

    qint64 atRunningTime()
    {
    timer.start();

    ForLoop {
    int count = list.at(0) + list.at(1) + list.at(2);
    }

    return timer.elapsed();
    }

    qint64 constAtRunningTime()
    {
    timer.start();

    ForLoop {
    const int count = list.at(0) + list.at(1) + list.at(2);
    }

    return timer.elapsed();
    }

    qint64 squareOperationRunningTime()
    {
    timer.start();

    ForLoop {
    int count = list[0] + list[1] + list[2];
    }

    return timer.elapsed();
    }

    qint64 constSquareOperationRunningTime()
    {
    timer.start();

    ForLoop {
    const int count = list[0] + list[1] + list[2];
    }

    return timer.elapsed();
    }

    int main(int argc, char *argv[])
    {
    list<<0<<1<<2;

    qDebug()<<"SourceRunningTime: "<<runningTime()<<"milliseconds.";
    qDebug()<<"atRunningTime: "<<atRunningTime()<<"milliseconds.";
    qDebug()<<"constAtRunningTime: "<<constAtRunningTime()<<"milliseconds.";
    qDebug()<<"squareOperationRunningTime: "<<squareOperationRunningTime()<<"milliseconds.";
    qDebug()<<"constSquareOperationRunningTime: "<<constSquareOperationRunningTime()<<"milliseconds.";

    return 0;
    }

Qml失焦问题

发表于 2019-06-05

Qml常见诡异失焦情况一般为明明设置了某一控件的焦点, 实际却是不生效。这到底时什么情况呢?用例子来分析这种情况。

1.常见失焦情况

  • 被其他控件抢夺;
  • 误以为设置成功。

2.关于控件被抢夺的情况

2.1 先看下列例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Dialog {
id: dialog
width: 100; height: 100
onHidden: {
dialog.forceReset() // restore focus to 'yes' button
}
}

Rectangle {
width: 100; height: 200
color: activeFocus ? "red" : "lightblue"

MouseArea {
anchors.fill: parent
onClicked: {
parent.forceActiveFocus()
dialog.hide();
}
}
}

2.2 为什么这样会导致Rectangle失焦呢?

  • 当按钮按下,Dialog被关闭并恢复其原来的焦点状态;
  • 因为dialog.hide()调用后dialog重新获得了焦点。

2.3 应该这种情况应该怎么做?

  • 确保设置焦点后的逻辑不能再有其他设置焦点的行为;
  • 将焦点设置放到最后执行;
  • 可以使用定时器延时执行,确保其他焦点被还原的同时自己却又是最后获得焦点。

3.一般控件获得焦点的情况

3.1 直接设置获得焦点

  • 当onClicked被触发调用Rectangle的forceActiveFocus,以至于获得焦点;
  • 也可以直接设置focus的值来获得焦点。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Rectangle {
    width: 100; height: 200
    focus: true
    color: activeFocus ? "red" : "lightblue"

    MouseArea {
    anchors.fill: parent
    onClicked: parent.forceActiveFocus()
    }
    }

3.2 间接获得焦点

  • 当FocusScope获得了焦点,由于rect事先设置了focus:true间接地获得焦点了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    FocusScope {
    id: scope
    width: 100; height: 200
    focus: false

    Rectangle {
    id: rect
    anchors.fill: parent
    focus: true
    color: activeFocus ? "red" : "lightblue"
    }
    }
1…161718…32
Qt君

Qt君

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