在正题开始之前,老周照例扯点别的。嗯,咱们扯一下在 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();

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

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

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

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

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

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

// 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 文件所在的目录下。

现在,窗口的图标变了。

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

    // 位置
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)

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

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

用VS Code搞Qt6:至简窗口部件——QWidget的更多相关文章

  1. 窗口部件-基础窗口部件 QWidget

    1 基础窗口部件 QWidget QWidget 类是所有用户界面对象的基类,被称为基础窗口部件.不多废话直接看代码 main.cpp 如下 #include<QtWidgets> int ...

  2. 用VS Code搞Qt6:编译源代码与基本配置

    先说明一下,本水文老周仅讨论新版的 Qt 6,旧版的 Qt 不讨论. 尽管 Qt 有自己的开发环境,但老周必须说句不装逼的话:真的不好用.说起写代码,当然了,用记事本也能写.但是,有个高逼格的工具,写 ...

  3. 用VS Code搞Qt6:编译附加模块

    上一次水文中,老周所介绍的是编译 Qt 的基础模块-- qtbase.一次性编译所有代码可以一劳永逸,但体积相当大,编译时间较长,CPU负载大发热大,风扇转得猛,电费交得多.因此老周更喜欢分开来编译. ...

  4. 用 VS Code 搞 Qt6:信号、槽,以及QObject

    Qt 里面的信号(Signal)和槽(Slot)虽然看着像事件,但它实际上是用来在两个对象之间进行通信的.既然是通信,就会有发送者和接收者. 1.信号是发送者,触发时通过特有的关键字"emi ...

  5. 用 VS Code 搞 Qt6:让信号和槽自动建立连接

    Qt 具备让某个对象的信号与符合要求的槽函数自动建立连接.弄起来也很简单,只要调用这个静态方法即可: QMetaObject::connectSlotsByName(...); connectSlot ...

  6. 用 VS Code 搞Qt6:使用 PySide 6

    一般来说,用C++写 Qt 应用才是正宗的,不过,为了让小学生也能体验 Qt 的开发过程,或者官方为了增加开发者人数,推出了可用 Python 来编程的 Qt 版本.此版本命名比较奇葩,叫 PySid ...

  7. [Qt Creator 快速入门] 第3章 窗口部件

    从这一章开始正式接触Qt的窗口部件.在第2章曾看到 Qt Creator 提供的默认基类只有 QMainWindow.QWidget 和 QDialog 这3种.QMainWindow 是带有菜单栏和 ...

  8. 用VS Code搞Qt 6:Gui基础类型——QGuiApplication和QWindow

    在99.996%的情况下,我们弄 Qt 应用都会使用 QApplication 类和 QWidget 类,即直接用 Widgets 库中的组件/控件.为了方便开发人员自己造轮子,Qt 也提供了一套基础 ...

  9. 【Qt学习笔记】窗口部件整理

    关于Qt中窗口部件的学习 今天开始学习Qt的窗口部件,领略一下Qt的神奇之处,记得2012年的那年冬天,我还学Java呢,现在基本上和Java说再见了,不过对于嵌入式的开发Qt还是举足轻重的,我想趁着 ...

随机推荐

  1. CCPC、Petrozavodsk Camp、OpenCup 题解汇总

    省赛 \([\text{2021.11.30}]\) 2021 Jilin Collegiate Programming Contest 全部完成. \([\text{2021.12.25}]\) 2 ...

  2. [XJOI3529] 左右

    题目链接:左右 Description 给你一个s数组,一个t数组,你可以对s数组执行以下两种操作 L 操作:每个数等于其左边的数加上自己 R 操作:每个数等于其右边的数加上自己 第一个数的左边是最后 ...

  3. Linux下添加MySql组件后报无权限问题解决

    Tomcat日志报错如下: Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' (using pas ...

  4. React设置proxy后依旧报CROS错误

    1.判断表单数据是否为后端接收的类型 POST GET2.axios自动转换问题 手动添加标头这份表单数据包括了files (二进制数据)而标头显示是JSON格式 不符 所以报CROS 更多文章请移步 ...

  5. 27.MySQL 索引、事务与存储引擎

    MySQL 索引.事务与存储引擎 目录 MySQL 索引.事务与存储引擎 MySQL 索引 索引的概念 索引的作用及副作用 索引的作用 索引的副作用 创建索引的原则依据 索引的分类和创建 普通索引 唯 ...

  6. 关于Node.js 链接mysql超时处理(默认8小时)

    备注:这是在pm2配置node环境下,超过8小时mysql自动关闭的情况下出现的解决方法:1.封装mysql.js var mysql = require('mysql'); var connecti ...

  7. 我熬夜开发了一款简约实用、支持多平台的Markdown在线编辑器(开源)

    前言 之前,一直想开发一款属于自己的Markdown编辑器,主要是自己平常写文章可以更加灵活操作,另外扩宽自己的视野也是非常不错的选择啊!所以在周末就决定玩耍一番.首先我调研了很多线上热门的md编辑器 ...

  8. React与Koa一起打造一个功能丰富的全栈个人博客(业务篇)

    前言 豆哥的个人博客又改版了,本版主要技术栈是前台用的React,后台用的Koa.博客改版的初衷是自己可以练练React(公司的项目部分要用React,我也没法啊,再说早晚得学).本文主要介绍博客的业 ...

  9. dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门

    本文将告诉大家如何在 dotnet 的控制台模式下,采用 MAUI 自绘库 Microsoft.Maui.Graphics 进行绘图,设置 Microsoft.Maui.Graphics 底层调用 M ...

  10. Three.js系列: 在元宇宙看电影,享受 VR 视觉盛宴

    本文 gihtub 地址: https://github.com/hua1995116/Fly-Three.js 最近元宇宙的概念很火,并且受到疫情的影响,我们的出行总是受限,电影院也总是关门,但是在 ...