用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 文件。
写 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的更多相关文章
- 窗口部件-基础窗口部件 QWidget
		
1 基础窗口部件 QWidget QWidget 类是所有用户界面对象的基类,被称为基础窗口部件.不多废话直接看代码 main.cpp 如下 #include<QtWidgets> int ...
 - 用VS Code搞Qt6:编译源代码与基本配置
		
先说明一下,本水文老周仅讨论新版的 Qt 6,旧版的 Qt 不讨论. 尽管 Qt 有自己的开发环境,但老周必须说句不装逼的话:真的不好用.说起写代码,当然了,用记事本也能写.但是,有个高逼格的工具,写 ...
 - 用VS Code搞Qt6:编译附加模块
		
上一次水文中,老周所介绍的是编译 Qt 的基础模块-- qtbase.一次性编译所有代码可以一劳永逸,但体积相当大,编译时间较长,CPU负载大发热大,风扇转得猛,电费交得多.因此老周更喜欢分开来编译. ...
 - 用 VS Code 搞 Qt6:信号、槽,以及QObject
		
Qt 里面的信号(Signal)和槽(Slot)虽然看着像事件,但它实际上是用来在两个对象之间进行通信的.既然是通信,就会有发送者和接收者. 1.信号是发送者,触发时通过特有的关键字"emi ...
 - 用 VS Code 搞 Qt6:让信号和槽自动建立连接
		
Qt 具备让某个对象的信号与符合要求的槽函数自动建立连接.弄起来也很简单,只要调用这个静态方法即可: QMetaObject::connectSlotsByName(...); connectSlot ...
 - 用 VS Code 搞Qt6:使用 PySide 6
		
一般来说,用C++写 Qt 应用才是正宗的,不过,为了让小学生也能体验 Qt 的开发过程,或者官方为了增加开发者人数,推出了可用 Python 来编程的 Qt 版本.此版本命名比较奇葩,叫 PySid ...
 - [Qt Creator 快速入门] 第3章 窗口部件
		
从这一章开始正式接触Qt的窗口部件.在第2章曾看到 Qt Creator 提供的默认基类只有 QMainWindow.QWidget 和 QDialog 这3种.QMainWindow 是带有菜单栏和 ...
 - 用VS Code搞Qt 6:Gui基础类型——QGuiApplication和QWindow
		
在99.996%的情况下,我们弄 Qt 应用都会使用 QApplication 类和 QWidget 类,即直接用 Widgets 库中的组件/控件.为了方便开发人员自己造轮子,Qt 也提供了一套基础 ...
 - 【Qt学习笔记】窗口部件整理
		
关于Qt中窗口部件的学习 今天开始学习Qt的窗口部件,领略一下Qt的神奇之处,记得2012年的那年冬天,我还学Java呢,现在基本上和Java说再见了,不过对于嵌入式的开发Qt还是举足轻重的,我想趁着 ...
 
随机推荐
- 论文解读(GraphSMOTE)《GraphSMOTE: Imbalanced Node Classification on Graphs with Graph Neural Networks》
			
论文信息 论文标题:GraphSMOTE: Imbalanced Node Classification on Graphs with Graph Neural Networks论文作者:Tianxi ...
 - Simple, Fast Malicious Multiparty Private Set Intersection-解读
			
文本记录阅读该论文的笔记. 这是文章框架,来自视频. 介绍 本文主要解决恶意攻击下安全的多方PSI,主要用到两大技术OPPRF和OKVS,构造合谋和不合谋的协议. 基础知识 OPPRF 这部分在OPR ...
 - Linux Cgroup v1(中文翻译)(4):Block IO Controller
			
Block IO Controller 1 概览 cgroup子系统blkio实现了block io控制器.无论是对存储结构上的叶子节点和还是中间节点,它对各种IO控制策略(proportional ...
 - MAC NGINX PHP XDEBUG
			
1. 安装 homebrew 2. 安装nginx ; 终端运行 brew install nginx: 1)给nginx 设置管理员权限:如果不设置管理员权限,80端口是不能监听的: #这里的目录根 ...
 - Camunda如何适配国产数据库达梦
			
前言 camunda流程引擎官方支持的数据库有:MySQL .MariaDB .Oracle .DB2 .PostgreSQL .SQL Server.H2.对于其他类型的数据库如何支持,尤其是国产数 ...
 - Android  Jetpack Navigation基本使用
			
Android Jetpack Navigation基本使用 本篇主要介绍一下 Android Jetpack 组件 Navigation 导航组件的 基本使用 当看到 Navigation单词的时候 ...
 - SAP SD-Invoice 销售发票
			
针对销售订单的发票流程: 1. 事务码:VF01(个别生成系统发票) 创建开票凭证(发票)/ VF04 开具系统发票(可把多个item 合并成一张系统发票) 2. 事务码:VF02 修改发票, 释放 ...
 - WPF开发随笔收录-获取软件当前目录的坑
			
一.唠唠叨叨 软件开发过程中,经常需要使用到获取exe当前目录这个功能,前同事在实现这个需求时使用的是Directory.GetCurrentDirectory()这个方法,但再最近的测试中,突然发现 ...
 - RocketMQ 集群的搭建部署 以及rocketmq-console-ng仪表台的安装部署
			
在 RocketMQ 主要的组件如下. NameServerNameServer 集群,Topic 的路由注册中心,为客户端根据 Topic 提供路由服务,从而引导客户端向 Broker 发送消息.N ...
 - 高精度10m/30米NPP净初级生产力分布数据
			
数据下载链接:百度云下载链接 引言 第一性生产力是绿色植物呼吸后所剩下的单位面积单位时间内所固定的能量或所生产的有机物质,即是总第一性生产量减去植物呼吸作用所剩下的能量或有机物质.多种卫星遥感数据反 ...