首页 > 编程语言 >Qt入门(C++)

Qt入门(C++)

时间:2024-07-10 23:30:35浏览次数:13  
标签:控件 widget Qt Widget C++ label ui include 入门

创建项目基类的选择

在这里插入图片描述

对于基类的选择有三个选项,分别是QMainWindow、QWidget、QDialog

基类说明
QMainWindow主窗⼝类,⼀般⽤于较为复杂的应⽤程序,除了中央客⼾区界⾯,还包括菜单栏、⼯具栏、状态栏以及多个可停靠的⼯具对话框等
QWidget最简单、最基本的窗体程序,⾥⾯可以放置多个控件实现程序功能
QDialog基于对话框的程序,对话框⼀般⽤于弹窗,也可以⽤于主界⾯显⽰。对话框是从QWidget继承⽽来的,并丰富了⼀些功能,如模态显⽰和返回值等

项目结构

在这里插入图片描述

main.cpp

主函数
在这里插入图片描述
1.mian函数的形参是命令行参数,具体在Linux中会讲到
2.Qapplication 编写QT程序自带的类
3.创建一个控件对象并且显示出来
4.返回Qapplicaion类的对象a中的exec函数的返回值,此exec是用于让程序执行起来的

widget.h

在这里插入图片描述
1.这里的#ifndef #define 是header gurad,保证头文件只被包含一次,更推荐#pragma once,因为不知道在大工程项目中会不会有与文件名重名的现象
2.class Widget: public Qwidget 创建项目时,选择的父类 ps:qt中的类和头文件名字一样.
3.QT_OBJECT是qt内置的宏,本质上是文本替换,如果某个类要使用信号和槽就需要写入这个
4.Widget(Qwidget *parent = nullptr) QT中引入了“对象树”机制,创建Qt的对象,就可以把这个对象挂到对象树上,需要通过制定父节点的方式去挂。
5.Ui:: Widget *ui是用于与form file交互的,form file 是指Forms文件夹中的文件

widget.cpp

在这里插入图片描述
1.#include "widget.h"创建项目时的头文件
2.#include "ui_widget.h" form file被qmake生成的头文件
3.后面的ui(new Ui::Widget)ui->setupUi(this)用于将form file生成的界面和当前的widget关联起来

widget.ui(form flie)

用于设计界面。
在这里插入图片描述
在代码层面用的是xml文件的形式编写。
在这里插入图片描述
xml的标签含义是由程序员自定义的,这里看到的标签是qt开发者制定的,类似于linux网络原理中的自定义应用层协议。

.pro(类似于linux的Makefile文件)

在这里插入图片描述
QT项目的工程文件,也是qmake工具构建的依据(qmake:一种元编程)
QT += core gui:要引入qt的模块,后面学习到一些内容的时候可能要回修改这里
CONFIG += c++11:编译选项

SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui

这里描述了当前项目中,参与构建的文件都有什么。会自动生成

编译过程中生成的中间文件

在这里插入图片描述
在运行一次程序之后,就会在项目目录并列的地方,多出来一个“build-xxx”目录,这个目录里面就是该项目运行过程中,生成的一些临时文件。
在这里插入图片描述

ui_widget.h

创建项目时候自动生成的,里面包含了一个 ui_widget.h的类,在widget.h和widget.cpp中都用使用到,也是用于生成界面的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在widget.cpp中widget类的构造方法使用setipUi去生成界面

.exe文件

最终生成的可执行的程序,如果直接运行,效果就是和之前的在Qt Creater中运行时相同的效果

对象树

在这里插入图片描述
问:我把代码改成这样还能够正常显示吗?答:不能
这里就不得不提出对象树这样的概念。
假设我们有现在这样的一个界面:
在这里插入图片描述
对应的对象树就是这个样子(他是一个n叉树)
在这里插入图片描述

用label实现hello world

在这里插入图片描述
代码实现:
在这里插入图片描述
问:这里的代码会不会导致内存泄露?答:不会
因为:label对象会在合适的时候被析构函数释放

在qt中用n叉树来管理控件对象,方便于在窗口关闭的时候统一释放。所以在这句代码中QLabel* label = new QLabel(this); 在new调用构造传参传this就是交给qt的对象树去管理。这里也解释了为什么上述代码会不能显示hello world,因为这里的label的作用域是在栈上的,生命周期只在构造函数中,出了构造函数就会自动销毁,所以,在窗口关闭之前就销毁了。我们就看不见。

我们可以利用以下这个小例子去证实,我们自己去新建一个mylabel类去继承Qlabel类,然后在mylabel类中实现析构函数去输出日志信息,观看是否自动调用析构函数(在关闭窗口的时候)

mylabel.h

#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
#include <iostream>

class mylabel : public QLabel
{
public:
    mylabel(QWidget* parent);
    ~mylabel();
};

#endif // MYLABEL_H

这里继承了 QLabel,在构造函数中加入传参QWidget* parent这个参数,参数用于将自己新建的对象挂到qt的对象树上,统一管理析构

mylabel.cpp

#include "mylabel.h"

mylabel::mylabel(QWidget* parent):QLabel(parent)
{

}

mylabel:: ~mylabel()
{
    std::cout<<"mylabel 被销毁了"<<std::endl;
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <mylabel.h>
#include <QLabel>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

//    QLabel* label = new QLabel(this);
//    label->setText("hello world");
//    QLabel label(this);
//    label.setText("hello world");
    mylabel* label = new mylabel(this);//传入widget的this作为parent,统一析构
    label->setText("hello world");
}

Widget::~Widget()
{
    delete ui;
}

对象树, Qt 中通过对象树, 来统一的释放界面的控件对象.Qt 还是推荐使用 new 的方式在堆上创建对象,通过对象树, 统一释放对象.创建对象的时候,在构造函数中,指定父对象(此时才会挂到对象树上)如果你的对象没有挂到对象树上,就必须要记得手动释放!!
面向对象“继承”,本质上是对现有代码进行的“扩展”

乱码解决问题

但是这里出现一个问题就是在应用程序输出的文字出现了乱码
在qt中打印日志一般都不会用cout,而是用qDebug()工具
在这里插入图片描述
后续再 Qt 中,如果想通过打印日志的方式, 输出一些调试信息,都优先使用 qDebug.虽然使用 cout 也行,但是 cout 对于编码的处理不太好,在windows 上容易出现乱码(如果是 Linux 使用 Qt Creator,一般就没事了,Linux 默认的编码一般都是 utf8)
qDebug()还可以统一关闭,因为qDebug()是一个宏封装了QDebug()对象,所以可以通过编译开关,来实现一键式关闭。

在 Qt 中打印日志,作为调试信息,使用 cout 固然可以, 但是并不是上策 (字符编码处理的不好,也不方便统一进行关闭)Qt 中推荐使用 qDebug()完成日志的打印.

初步认识connet函数

用Push button实现hello world

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    void handleClick();
    ~Widget();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <mylabel.h>
#include <QLineEdit>
#include <QLabel>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
}

void Widget::handleClick()
{
    //当按钮被点击后,改变按钮文本
    if(ui->pushButton->text()== QString("hello world"))
    {
        ui->pushButton->setText("hello qt");
    }
    else
    {
        ui->pushButton->setText("hello world");
    }
}

Widget::~Widget()
{
    delete ui;
}

如何让按钮点击后有效果呢?使用qt中的信号槽机制(本质上就是给按钮点击操作,关联上一个处理函数,当用户点击的时候,就会执行这个处理函数)
使用QT中的connect函数来实现按钮点击逻辑(connet是QObject这个类提供的静态函数.这个函数的作用就是“连接信号”和“槽”)
connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
第一个参数的作用:谁发出的信号(对象)
第二个参数的作用:发出了个什么信号(这个对象的成员函数)
第三个参数的作用:谁来处理这个信号(处理信号的对象)
第四个参数的作用:具体怎么处理(处理信号对象的成员函数)

ui->pushButton代码解释:可以访问到 form fil(ui文件中的控件),在Qt designer 中创建一个控件的时候,此时就会给这个控件分配一个objectName属性,这个属性的值,要求是在界面中得到是唯一的。qmake在预处理.ui文件的时候,就会根据这里的objectName生成对应的C++代码。C++代码中改QPushButton对象的变量名字就是这里的objectName.

问:这里的objectName是怎么变成ui里面的属性(为什么ui可以->pushButtom)
在工程文件中找到该工程生成的中间文件夹里面的ui_widget.h,这个文件就是根据我们的widget.ui生成的文件,里面是一个类,而我们在UI界面中添加控件,就在这里类里面添加成员变量(成员变量的名字就是该控件的Objectname)这里值得注意的是这里的ui_widget.h中的类ui_widget被一个widget类继承了(仅仅是封装而已,并没有做出扩展,在ui_widget.h后面看到),所以我一开始看项目中的widget类时,不禁迷惑为什么是同一个名字,但是其实是两个不同类ui_widget.h的那个类被ui namespace包住了。

ui_widget.h

/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 5.14.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_WIDGET_H
#define UI_WIDGET_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_Widget
{
public:
    QLabel *label;
    QLineEdit *lineEdit;
    QPushButton *pushButton;

    void setupUi(QWidget *Widget)
    {
        if (Widget->objectName().isEmpty())
            Widget->setObjectName(QString::fromUtf8("Widget"));
        Widget->resize(800, 600);
        label = new QLabel(Widget);
        label->setObjectName(QString::fromUtf8("label"));
        label->setGeometry(QRect(360, 230, 181, 161));
        label->setLayoutDirection(Qt::LeftToRight);
        label->setAlignment(Qt::AlignCenter);
        lineEdit = new QLineEdit(Widget);
        lineEdit->setObjectName(QString::fromUtf8("lineEdit"));
        lineEdit->setGeometry(QRect(230, 160, 151, 61));
        lineEdit->setAlignment(Qt::AlignCenter);
        pushButton = new QPushButton(Widget);
        pushButton->setObjectName(QString::fromUtf8("pushButton"));
        pushButton->setGeometry(QRect(150, 320, 141, 71));

        retranslateUi(Widget);

        QMetaObject::connectSlotsByName(Widget);
    } // setupUi

    void retranslateUi(QWidget *Widget)
    {
        Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));
        label->setText(QCoreApplication::translate("Widget", "hello world", nullptr));
        lineEdit->setText(QCoreApplication::translate("Widget", "hello world", nullptr));
        pushButton->setText(QCoreApplication::translate("Widget", "hello world", nullptr));
    } // retranslateUi

};

namespace Ui {
    class Widget: public Ui_Widget {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_WIDGET_H

初步认识Qt窗口坐标体系

在这里插入图片描述
规则一:坐标系的原点(0,0)就是屏幕的左上角/窗口的左上角
规则二:给Qt的某个控件,设置位置,就需要指定坐标,对于这个控件来说,坐标系原点就是相对于父窗口/控件的。如果该控件上根节点那么它的坐标系原点就是相对于整个显示器桌面。
在这里插入图片描述

代码练习:
widget.h

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    button->move(200,300);
}

Widget::~Widget()
{
    delete ui;
}

总结:通过控件的成员函数中move成员函数来确定控件位置,move(水平方向偏移(向右),垂直方向偏移(向下))单位是像素。

标签:控件,widget,Qt,Widget,C++,label,ui,include,入门
From: https://blog.csdn.net/qq_45262721/article/details/140279337

相关文章

  • C++函数模板学习
    函数模板是C++中的一个强大特性,允许编写通用函数来处理不同的数据类型。学习函数模板有助于理解泛型编程的概念,提高代码的可重用性和可维护性。以下是一些学习函数模板时可以关注的方面:1.模板的基本概念模板定义:了解如何定义模板函数和模板类。模板参数:掌握模板参数的使......
  • [C++] C++20约束表达式和requires子句
    约束约束是逻辑操作和操作数的序列,它指定了对模板实参的要求。合取两个约束的合取是用&&运算符。template<typenameT>conceptluser=std::integral<T>&&std::signed_integral<T>;需要约束同时满足两个要求。合取判断的时候,使用短路检测,即对std::integra......
  • 「字符串」Manacher算法(马拉车)/ LeetCode 05(C++)
    给你一个字符串 s,找到 s 中最长的回文子串。示例1:输入:s="babad"输出:"bab"解释:"aba"同样是符合题意的答案。示例2:输入:s="cbbd"输出:"bb"思路我们回想中心扩散法:某字符处的中心扩散完毕后,其实已经将它身前身后的字符段落都搜索过了,那么如果我们搜索其后的字......
  • Qt信号与槽
    信号和槽是QT自行定义的一种通信机制,独立于标准的C/C++语言。信号与槽可以一对一、一对多、多对一。 信号(signals:)是无返回值、无函数体、可有参的函数(声明),被emit发出后无序地被对应的槽接收然后执行槽函数。槽(slots:)是普通的类成员函数。 信号与槽的绑定函数原型:boolQOb......
  • c++ protobuf安装记录
    googleprotobuf是一个灵活的、高效的用于序列化数据的协议。相比较XML和JSON格式,protobuf更小、更快、更便捷。googleprotobuf是跨语言的,并且自带了一个编译器(protoc),只需要用它进行编译,可以编译成Java、python、C++、C#、Go等代码,然后就可以直接使用,不需要再写其他代码,自带有......
  • 【C++修行之道】string类练习题
    目录387.字符串中的第一个唯一字符125.验证回文串 917.仅仅反转字母415.字符串相加(重点)541.反转字符串II387.字符串中的第一个唯一字符字符串中的第一个唯一字符-力扣(LeetCode)给定一个字符串 s ,找到它的第一个不重复的字符,并返回它的索引 。如果不存......
  • MyBatis Plus - 简介及入门实例
    简介及入门实例前言最开始,要在Java中使用数据库时,需要使用JDBC,创建Connection、ResultSet等,然后我们又对JDBC的操作进行了封装,创建了许多类似于DBUtil等工具类。再慢慢的,出现了一系列持久层的框架:Hibernate、JPA,Mybatis等。各个框架的特点如下:Hibernate:一个全......
  • 零基础STM32单片机编程入门(十二) HC-SR04超声波模块测距实战含源码
    文章目录一.概要二.HC-SR04主要参数1.模块引脚定义2.模块电气参数3.模块通讯时序4.模块原理图三.STM32单片机超声波模块测距实验四.CubeMX工程源代码下载五.小结一.概要HC-SR04超声波模块常用于机器人避障、物体测距、液位检测、公共安防、停车场检测等场所。HC-SR......
  • Linux-shell编程入门基础
    目录前言Shell编程bash特性shell作用域变量环境变量$特殊变量$特殊状态变量$特殊符号(很重要)其他内置shell命令shell语法的子串截取统计指令执行时间练习shell特殊扩展变量父子shell的理解内置和外置命令区别数值计算双括号(())运算letexprexpr模式匹配bcawk中括号shell的条件判......
  • 【C++知识点总结全系列 (08)】:面向对象编程OOP
    这里写目录标题1、OOP概述(1)面向对象四大特征A.抽象B.封装C.继承D.多态(2)构造函数A.What(什么是构造函数)B.Why(构造函数的作用)C.Which(有哪些构造函数)(3)析构函数A.What(什么是析构函数)B.Why(析构函数的作用)(4)=default和=deleteA.WhyB.How2、继承(1)What(什么是继......