3

用VS Code搞Qt6:至简窗口部件——QWidget - 东邪独孤

 1 year ago
source link: https://www.cnblogs.com/tcjiaan/p/16585969.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

用VS Code搞Qt6:至简窗口部件——QWidget

在正题开始之前,老周照例扯点别的。嗯,咱们扯一下在 VS 2022 下结合 CMake 开发 Qt6 时的环境变量设置问题。在VS Code 中,通够通过 CMake Tools 扩展的配置来设置环境,但在VS 里面,CMake 项目只是一个文件夹,然后通过 .json 文件来配置一些参数,不能像 VS Code 那样设置环境变量。

当然,如果你嫌麻烦,最简单粗暴的方法,就是在系统级或用户级别直配置全局环境变量。这样所有开发工具都能共享这些环境变量。但有时候,你不希望把 Qt 库的路径放上去,因为配置的环境变量多,怕搞得乱。

好了,F话不多说了。下面老周就说说如何在单个项目中配置环境变量。

方法一:千古传统做法,打开命令行或写个脚本,先设置环境变量,set PATH=k:\Libs\Qtlib;%PATH%,然后在命令行中启动 VS。

方法二:这个好一点。先在VS中建以 CMake 为基础的项目。然后会产生一个预设配置文件—— CMakePresets.json。打开此文件,大概这样:

{
  "version": 3,
  "configurePresets": [
    {
      "name": "windows-base",
      "hidden": true,
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "installDir": "${sourceDir}/out/install/${presetName}",
      "cacheVariables": {
        "CMAKE_C_COMPILER": "cl.exe",
        "CMAKE_CXX_COMPILER": "cl.exe"
      },
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Windows"
      }
    },
    {
      "name": "x64-debug",
      "displayName": "x64 Debug",
      "inherits": "windows-base",
      "architecture": {
        "value": "x64",
        "strategy": "external"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug"
      }
    },
    {
      "name": "x64-release",
      "displayName": "x64 Release",
      "inherits": "x64-debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    },
    {
      "name": "x86-debug",
      "displayName": "x86 Debug",
      "inherits": "windows-base",
      "architecture": {
        "value": "x86",
        "strategy": "external"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug"
      }
    },
    {
      "name": "x86-release",
      "displayName": "x86 Release",
      "inherits": "x86-debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    }
  ]
}

看它的鬼样子好像很复(可)杂(怕),其实就是一组生成/调试运行的配置。配置和 VS 标准一样,都是“F5 双侠” —— Debug 或 Release。但是,这两种调试运行有许多配置项是共享的,于是,此配置文档先弄个叫“windows-base”的配置,把一些共用的选项放在这儿,然后其他配置会继承它。比如,“x64-debug”,它通过 "inherits": "windows-base" 明确了它是继承 “windows-base” 配置的。

咱们直奔重点,把环境变量设置在 “windows-base” 配置中,这样后面的所有配置都能继承,因为环境是必须的。在“windows-base”中加入“environment” 节点,它的值是一个 JObject,其中,Key 是环境变量的名称,Value 是环境变量的值。

我们的目标是配置 PATH 环境变量。

      "name": "windows-base",
     ……
      "environment": {
        "PATH": "G:\\Kits\\Qt6\\installed;G:\\Kits\\Qt6\\installed\\bin;$penv{PATH}"
      },
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Windows"
      }
    },

需要添加两个目录,一个是 Qt 所在的目录,一个是 Qt 目录下的 bin 子目录(加这个后在调用运行时就不会找不到 .dll 了)。注意最后的 $penv 宏,此处不能用 $env 宏来引用变量,因为我们要的效果是在 PATH 变量中添加路径,而不能把原有的路径替换,等同于把这两个目录追加到现有的目录列表中。CMake 的变量不能循环引用自身(PATH引用了自己),所以要用 $penv 宏,而不用 $env 宏来引用原有的 PATH 变量。

保存文件,这样在按【F5】运行项目时就不会报错了,写代码时也不会找不到头文件了。

以上是占了大段篇幅的题外话,因为网上没有相关的内容,所以老周就写出来,以供大伙伴们参考。

===================================================================================

今天咱们的主角是一个名为 QWidget 的类。它表示一个最最最基本的窗口部件(控件)类,它也是所有部件的公共基类。Qt 里面 99.993% 的类名都有 “Q” 开头,比如 QObject、QString。QWidget 类在 Widgets 库中,属于 QtBase 模块的一部分,铁三角之一。

QWidget 君虽然是公共基类,但它是可以直接使用的。

咱们用一个例子来看看直接用 QWidget 类会在用户界面上呈现什么。

CMake 文件:

cmake_minimum_required(VERSION 3.20.0)

project(myApp LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 导入库
find_package(Qt6 REQUIRED COMPONENTS
        Core
        Gui
        Widgets)
# 添加源代码
add_executable(myApp WIN32 app.cpp)
# 链接库
target_link_libraries(myApp PRIVATE
        Qt6::Core
        Qt6::Gui
        Qt6::Widgets)

新建一个文件,命名为 app.cpp,它是主代码文件。

包含 QApplication、QWidget 两个头文件。

#include <QApplication>
#include <QWidget>

Qt 为兼顾 C++ 新标准,提供不带 .h 后缀的头文件。当然,你用带扩展名的头文件也可以。

#include <qapplication.h>
#include <qwidget.h>

其实 QApplication 头文件里面就是写了一行 include ,把带扩展名的文件包含。

#include "qapplication.h"

其他的头文件的结构类似,说白了就是不带扩展名的头文件里包含了带扩展名的头文件。照这么看来,直接写 qapplication.h 可能更省事。

Qt 有一点让我不太高兴。它每个类一个头文件,要是代码里用到 20 个类,就要写 20 条 include。所以,如果要包含的头文件多的话,可以单独写在一个头文件中,然后项目的代码文件包含它。比如,我建个头文件叫 comm.h。

#pragma once

#include <QApplication>
#include <QWidget>

老周在第一行加了个 once 指令,这个是告诉编译器,如果头文件出现重复包含,那么只用一次就行,这样重复包含不会报错。但,#pragma once 在 VC++ 编译器中能用,在 g++ 或其他编译器中不一定能用。如果你写的是跨平台应用,可以改为:

#ifndef _COMM_H_
    #define _COMM_H_
    #include <QApplication>
    #include <QWidget>
#endif

_COMM_H_ 是个符号,你可以自由定义。它的意思就是如果没有定义 _COMM_H_ 宏那就执行后面的代码,如果已经定义了就不执行了。因为后面的代码中加了一行 #define ... 所以,这段代码也能保证头文件只会包含一次。

重点:很多朋友发现头文件中的代码报错,并且找不 QApplication、QWidget 等头文件。这是 CMake 在你家后院放火导致的。这时候你要打开 CMakeLists.txt 文件,在 add_executable 命令中,把 comm.h 文件加入到代码文件列表中。

add_executable(myApp WIN32 app.cpp comm.h)

现在打开 app.cpp,包含 comm.h 文件。

#include "comm.h"

写 main 函数,要带命令行参数的(不带也行,但一般会带上)。

int main(int argc, char **argv)
{
    
}

argc 是命令行参数的个数,argv 才是命令参数,是一个 char* 数组,一个指针表示字符串,再加个指针,char** 表示字符串数组。所以,你也可以这样写:

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

}

这样更容易看出它是个数组了。

接着实例化 QApplication 类。所有 Qt 应用程序都需要这个,需要它不会链接到 Widgets 上。毕竟窗口程序不是控制台,不能一执行就完了,它要停下来,不断接收和响应用户的操作。exec 方法启动消息循环,不断读取和处理消息,直接应用程序退出。

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

    return app.exec();
}

命令行参数会传递给 app 实例。

在 exec 方法调用之前初始化 QWidget 对象。不要在 exec 调用之后写,那样是不会执行的,因为 exec 方法开启的就是个死循环。

    QApplication app(argc, argv);

    QWidget wdg;
    wdg.show();

    return app.exec();

这样的程序特简单,那么,运行之后会出啥呢。看看。

367389-20220814184434014-713820658.png

哦,懂了,原来直接实例化 QWidget,出来的是一个空白的、最简单的窗口。

那,咱们改一下标题栏文本。

    QWidget wdg;
    wdg.setWindowTitle("一款高大上逼格全开的智障应用");
    wdg.show();

再运行一下,标题栏变了。

367389-20220814184809735-1339501011.png

咦,窗口左上角那个默认图标怎么像个橱柜似的,不好看,能不能换一个。好,说换就换。

// comm.h
#ifndef _COMM_H_
    #define _COMM_H_
    #include <QApplication>
    #include <QWidget>
    #include <QIcon>
#endif

// app.cpp
    QWidget wdg;
    wdg.setWindowTitle("一款高大上逼格全开的智障应用");
    // 设置图标
    wdg.setWindowIcon(QIcon("534.png"));
    wdg.show();

找一张图片,PNG 或 ico 格式都行,放在可执行文件所在目录下,命名为 534。上面代码中用的是相对路径,所以图片文件不是放在项目根目录下,而是 .exe 文件所在的目录下。

现在,窗口的图标变了。

367389-20220814185947266-1176748884.png

我们还可以把窗口移动到指定的坐标上,再设置它的大小。

    // 位置
    wdg.move(340, 400);
    // 尺寸
    wdg.resize(336, 286);
    wdg.show();

还有个叫 setGeometry 的方法,直接一步到位,同时设置窗口的位置和大小。

 wdg.setGeometry(521, 219, 320, 265);

四个值依次是 X坐标、Y坐标、宽度、高度。

我们也可以从 QWidget 类派生,然后将它封装为一个窗口,里面放着其他控件。

比如,写一个 MyForm 类,从 QWidget 派生,里面有一个标签和一个按钮。

// MyForm.h
#ifndef _MYFORM_
#define _MYFORM_

#include "comm.h"
#include <QLabel>
#include <QPushButton>

class MyForm : public QWidget
{
public:
    explicit MyForm(QWidget *parent = nullptr);
    ~MyForm();      //析构函数

private:
    QLabel *lb;
    QPushButton *btn;
    void initUI(void);
};
#endif

// MyForm.cpp
#include "MyForm.h"

MyForm::MyForm(QWidget *parent)
    : QWidget::QWidget(parent)
{
    // 标题
    setWindowTitle("主窗口");
    // 大小
    resize(300, 200);
    initUI();
}

MyForm::~MyForm()
{
    
}

// 初始化UI
void MyForm::initUI()
{
    lb = new QLabel("一些文本", this);
    btn = new QPushButton("一只按钮", this);
    // 定位控件
    lb -> move(25,24);
    btn -> move(25, 68);
}

构造函数加上 explicit 关键字,要求实例化 MyForm 类是必须调用构造函数,不能隐式赋值。隐式赋值就是当构造函数只有一个参数时,可以在实例化时用参数的值直接赋值。比如

class Pig
{
public:
    Pig(int v);
};

那么在实例化时可以这样:

Pig pp = 100;

我们这里,MyForm 不允许这样做,所以加个 explicit 关键字。

在 app.cpp 中,用 MyForm 类来呈现窗口。

#include "comm.h"
#include "MyForm.h"

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

    MyForm fw;
    fw.show();

    return app.exec();
}

show 方法是从 QWidget 类继承的,不是咱们自己写的。

CMakeLists.txt 文件也要加上代码文件。

add_executable(myApp WIN32
                app.cpp
                comm.h
                MyForm.h
                MyForm.cpp)

行了,看看,这个窗口不空白了。

367389-20220814194608263-640733867.png

好了,今天老周就水到这儿了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK