入门
创建一个项目
步骤
Create Project > Application(Qt) > Qt Widgets Application
generate form
:创建界面,暂不选
Base class
这里选择个
QWidget
(非窗口的普通类可以选顶级类QObject
,以便加入对象树)
QWidget
QDialog
和QMainClass
的父类,是个空窗口QDialog
对话框QMainClass
一些菜单栏、工具栏、状态栏之类的
main.cpp
#include "widget.h"
//应用程序类头文件
#include <QApplication>
/**
* 程序入口
* @brief qMain
* @param argc 命令行参数个数
* @param argv 命令行参数数组
* @return
*/
int main(int argc, char *argv[])
{
//实例化出一个应用程序对象a,该对象有且只有一个
QApplication a(argc, argv);
//窗口类(QWidget的子类)
Widget w;
//显示窗口
w.show();
/*
* 进入消息循环机制(阻塞),关闭窗口时退出
*/
return a.exec();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT //用于支持信号和槽的宏
public:
Widget(QWidget *parent = nullptr);
~Widget();
};
#endif // WIDGET_H
命名规范
同
java
常用快捷键
- 查询:
ctrl + f
- 注释:
ctrl + /
- 帮助:
选中 + F1
- 缩放:
ctrl + 滚轮
- 行移动:
ctrl + shift + 上|下
- 编码格式化:
ctrl + i
- 源文件与头文件切换:
f4
QPushButton
widget.cpp
,须引入头文件#include <QPushButton>
// widget.cpp
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//设置窗口名称
setWindowTitle("测试QPushButton");
//设置当前窗口大小:用户可缩放
//resize(600, 400);
//设置当前窗口大小:用户不可缩放
setFixedSize(600, 400);
/**
* 创建一个按钮(对象)
*/
/*方式一*/
QPushButton* btn1 = new QPushButton;
//设置父窗口
btn1->setParent(this);
//设置内容
btn1->setText("按钮1");
/*方式二*/
QPushButton* btn2 = new QPushButton("btn2", this);
//移动按钮
btn2->move(100, 0);
}
Widget::~Widget()
{
}
对象树
无需关心对象的释放
在
Qt
中对于new
出来的对象,如果是QObject
及其衍生类,都无需关心其内存的释放
QObject
是以对象树的形式组织起来的
当创建一个
QObject
对象时,QObject
的构造函数会接收一个QObject
指针作为参数,名为parent
,也就是对象指针
所以,在创建QObject
对象时,可以提供一个它的父对象,而我们即将创建的这个QObject
,就会被加入到父对象的children()
列表中
当父对象析构的时候,这个列表中的所有对象也都会被调用析构
这里的父子对象指的是父子窗口的含义
当一个
QObject
对象在堆上创建时,Qt
会同时为其创建一个对象树,要注意的是,对象树中的对象的顺序时没有定义的,所以,销毁这些对象的顺序也是未定义的
任何对象树种的
QObject
对象delete
的时候,如果这个对象有parent
,那么会同时将这个对象从它的parent
的children
中移除;
同时,如果它自身的children()
中的对象也会被delete
如果
QObject
在栈上被创建,Qt
也一样会这样操作
构造与析构顺序
构造:
QObject
最先开始
析构:QObject
最后执行
注意:对于打印的顺序,会先打印父对象中的析构函数中,因为这时候并没有进行释放操作,而只是追踪操作,即判断是否还有下级子对象,只有在确定没有子对象时才开始由子到父的顺序进行对象的释放
信号和槽
connect
语法
connect(信号发送者, 发送的信号, 信号接收者, 处理的槽函数)
案例:点击按钮关闭窗口
#include "widget.h"
#include <QPushButton>
#include "mypushbutton.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
MyPushButton* btn = new MyPushButton;
btn->setText("点我关闭");
btn->setParent(this);
connect(btn, &QPushButton::clicked, this, &QWidget::close);
}
Widget::~Widget()
{
qDebug() << "Widget 析构函数调用";
}
自定义信号和槽
书写规范
信号
- 写在
signals
下,只需要声明,不需要实现 - 返回值为
void
,可以有参数,且可以重载
槽
- 写在
public slots
下
5.0开始可以写成全局函数 或public
下成员函数 或直接用lambda
表达式 - 需要声明,同时也要有实现
- 返回值一般用
void
,可以有参数,也可以重载
案例
connect(班长, 起立, 学生, 老师好)
班长类
classmonitor.h
#ifndef CLASSMONITOR_H #define CLASSMONITOR_H #include <QObject> class ClassMonitor : public QObject { Q_OBJECT public: explicit ClassMonitor(QObject *parent = nullptr); signals: void standUp(); }; #endif // CLASSMONITOR_H
classmonitor.cpp
#include "classmonitor.h" ClassMonitor::ClassMonitor(QObject *parent) : QObject{parent} { }
学生类
student.h
#ifndef STUDENT_H #define STUDENT_H #include <QObject> class Student : public QObject { Q_OBJECT public: explicit Student(QObject *parent = nullptr); signals: //槽函数除了写在这的public slots中,也可以写在全局函数或者public下 public slots: void greeting();//需要在源文件中实现 }; #endif // STUDENT_H
student.cpp
#include "student.h" #include <QDebug> Student::Student(QObject *parent) : QObject{parent} { } void Student::greeting() { qDebug() << "老师好 ~~ "; }
Widget
widget.h
#ifndef WIDGET_H #define WIDGET_H #include "classmonitor.h" #include "student.h" #include <QWidget> class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); ClassMonitor * monitor; Student * student; void classBegin(); }; #endif // WIDGET_H
widget.cpp
#include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { //传入this,将其放入对象树中 this->monitor = new ClassMonitor(this); this->student = new Student(this); //创建信号槽:班长喊起立,学生喊老师好 connect(monitor, &ClassMonitor::standUp, student, &Student::greeting); //上课:触发信号槽 classBegin(); } Widget::~Widget() { } /** * 上课:触发班长的《起立》信号 * 班长:实例对象要和connect的一致 * @brief Widget::classBegin */ void Widget::classBegin() { emit this->monitor->standUp(); }
重载
班长类
classmonitor.h
//... signals: void standUp(); void standUp(QString surname); }; //...
学生类
student.h
//... public slots: void greeting();//需要在源文件中实现 void greeting(QString surname); }; //...
student.cpp
//... void Student::greeting(QString surname) { /* * QString是带引号的,如果要去除,需要将其转为char * * 1、surname.toUtf8() 将QString转为QByteArray * 2、.data() 将QByteArray转为char * */ qDebug() << surname.toUtf8().data() << "老师好 ~~ "; } //...
Widget
widget.h
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//传入this,将其放入对象树中
this->monitor = new ClassMonitor(this);
this->student = new Student(this);
/*
* 此时需要通过函数指针来指定函数地址
* 返回值(作用域::*函数指针名)(参数类型列表) = &作用域::函数名
*/
void(ClassMonitor::*monitorSignal)(QString) = &ClassMonitor::standUp;
void(Student::*studentSignal)(QString) = &Student::greeting;
connect(monitor, monitorSignal, student, studentSignal);
classBegin();
}
void Widget::classBegin() {
emit this->monitor->standUp("黄");
}
信号的连接与断开
连接
connect(btn, &QPushButton::clicked, monitor, monitorSingal) //此时无需再调用emit去触发信号
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->monitor = new ClassMonitor(this);
this->student = new Student(this);
//构建无参信号和槽
void(ClassMonitor::*monitorSignal)() = &ClassMonitor::standUp;
void(Student::*studentSignal)() = &Student::greeting;
connect(monitor, monitorSignal, student, studentSignal);
QPushButton* btn = new QPushButton("上课", this);
connect(btn, &QPushButton::clicked, monitor, monitorSignal);
resize(400, 600);
}
Widget::~Widget()
{
}
断开
disconnect(btn, &QPushButton::clicked, monitor, monitorSignal);
连接的对应关系
- 一个信号可以连接多个槽函数
- 多个信号可以连接同个槽函数
参数的对应关系
- 信号函数和槽函数的参数类型必须能对应
- 信号函数的参数个数可以有多余,但槽函数不可以
//可以 void(ClassMonitor::*monitorSignal)(Qstring) = &ClassMonitor::standUp; void(Student::*studentSignal)() = &Student::greeting; connect(monitor, monitorSignal, student, studentSignal); //不可以 void(ClassMonitor::*monitorSignal)() = &ClassMonitor::standUp; void(Student::*studentSignal)(Qstring) = &Student::greeting; connect(monitor, monitorSignal, student, studentSignal);
Qt5
之前版本的写法
不推荐使用
优势:参数直观
劣势:不会对参数类型做匹配
无参
connect(monitor, SIGNAL(standUp()), student, SLOT(greeting()));
emit monitor->standUp();
有参
connect(monitor, SIGNAL(standUp(QString)), student, SLOT(greeting(QString)));
emit monitor->standUp("张");
lambda
表达式
c++ 11
加入的匿名函数对象,用于简化编程
[capture](parameters)mutable -> return-type
{
//...
}
[capture]
[]
标识一个lambda
的开始,capture
表示函数对象参数
函数对象参数时传递给编译器自动生成的函数对象类的构造函数的
函数对象参数只能使用:当前lambda
所在范围内的可见的局部变量(包括lambda
所在类的this
)
函数对象参数的几种形式
空
表示没有使用任何函数对象参数=
表示lambda
函数体内可以使用其所在作用域范围内所有可见的局部变量(包括所在类的this
),并且是值传递(相当于编译器自动按值传递的方式传递了所有局部变量)QPushButton* btn = new QPushButton("上课", this); [=]() { btn->setText("上课了"); //如果为空,这里是无法用到btn变量的 }(); //用()来进行调用
&
同=
,只不过是用的引用传递的方式(相当于编译器自动按引用传递的方式传递了所有局部变量)this
表示函数体内可以使用lambda
所在类中的成员变量a
表示将指定的局部变量a
(其他变量将无法使用)按值传递的方式进行传递,这种方式函数体内不能直接去修改a
的拷贝,因为默认时const
的,需要先改造a
的拷贝构造&a
将a
以引用的方式进行传递=, &a, &b
表示a
和b
用引用传递,其余用值传递&, a, b
表示a
和b
用值传递,其余用引用传递
使用&
可能引起的问题
//点击按钮,改变按钮的文本值
QPushButton *btn = new QPushButton("aaa", this);
/*
* 当使用信号和槽的使用,其内部会会进入锁状态,即只读,
* 所以如果使用&引用传递的方式的话可能会出问题
* 所以一般使用=值传递的方式即可
*/
connect(btn, &QPushButton, this, [&](){
btn->setText("bbb"); //可能会引起报错
})
mutable
该关键字可以省略
按值传递函数对象参数时,加上
mutable
关键字之后,可以修改按值传递进来的拷贝(注意是修改拷贝,而非值本身)
/*
* 操作:先点击a按钮,再点击b按钮
* 输出:
* 10
* btn1: 20 //因为使用了mutable关键字,所以可以对m进行修改
* btn2: 10 //因为是值传递,所以改的其实是拷贝,原值未被修改
*/
QPushButton* btn1 = new QPushButton("a", this);
QPushButton* btn2 = new QPushButton("b", this);
btn2->move(100, 0);
int m = 10;
connect(btn1, &QPushButton::clicked, this, [m]()mutable {
m = 20;
qDebug() << "btn1: " << m;
});
connect(btn2, &QPushButton::clicked, this, [=](){
qDebug() << "btn2: " << m;
});
qDebug() << m;
返回值
-> 返回值类型
,如果返回值为void
则可以省略
this
的省略
对于信号和槽,如果槽函数使用的是
lambda
的形式,那么如果信号接收者是this
的话,可以省略
QPushButton* btn = new QPushButton("点击关闭窗口", this);
connect(btn, &QPushButton::clicked, [=](){
this->close();
});
QMainWindow
介绍
提供主窗口程序的类,包含一个菜单栏(
menu bar
)、多个工具栏(tool bars
)、多个锚接部件(dock widgets
)、一个状态栏(status bar
)以及一个中心部件(center widget
),是许多应用程序的基础(如文本编辑器、图片编辑器等)
菜单栏
一个窗口中最多只能有一个
#include "mainwindow.h"
#include <QMenuBar>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
resize(600, 400);
//创建一个菜单栏对象
QMenuBar *bar = menuBar();
//往菜单栏上添加菜单
QMenu *fileMenu = bar->addMenu("文件");
QMenu *editMenu = bar->addMenu("编辑");
QMenu *viewMenu = bar->addMenu("View");
//往菜单上添加菜单项
QAction * newProject = fileMenu->addAction("新建项目");
//添加一个分割线
fileMenu->addSeparator();
QAction * newFile = fileMenu->addAction("新建文件");
//将菜单栏放入窗口中
setMenuBar(bar);
}
MainWindow::~MainWindow()
{
}
工具栏
一个窗口中允许有多个
#include "mainwindow.h"
#include <QMenuBar>
#include <QToolBar>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
resize(600, 400);
//创建一个菜单栏对象
QMenuBar *bar = menuBar();
//往菜单栏上添加菜单
QMenu *fileMenu = bar->addMenu("文件");
QMenu *editMenu = bar->addMenu("编辑");
QMenu *viewMenu = bar->addMenu("View");
//往菜单上添加菜单项
QAction * newProject = fileMenu->addAction("新建项目");
//添加一个分割线
fileMenu->addSeparator();
QAction * newFile = fileMenu->addAction("新建文件");
//将菜单栏放入窗口中
setMenuBar(bar);
//创建一个工具栏对象
QToolBar *toolBar = new QToolBar(this);
//设置可停靠的区域(默认上下左右都可以)
toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
//设置浮动性(默认可浮动)
toolBar->setFloatable(false);
//往工具栏中放置部件
toolBar->addAction(newProject);
//添加分割线
toolBar->addSeparator();
toolBar->addAction(newFile);
//将工具栏放入窗口中(默认放窗口上方)
addToolBar(Qt::LeftToolBarArea, toolBar); //放到窗口的左侧
}
状态栏
一个窗口最多有一个
#include <QStatusBar>
#include <QLabel>
//...................................
//创建一个状态栏对象
QStatusBar *staBar = statusBar();
//往状态栏中添加部件
QLabel *label1 = new QLabel("label1", this); //存文本标签部件
QLabel *label2 = new QLabel("label2", this);
staBar->addWidget(label1); //从左侧开始添加
staBar->addPermanentWidget(label2); //从右侧开始添加
//将菜单栏放入窗口中
setStatusBar(staBar);
锚接部件与中心部件
锚接部件也叫浮动窗口,可以有多个
中心部件可以是各种形式的部件,如文本编辑部件,最多只能有一个
#include <QDockWidget>
#include <QTextEdit>
//........................................
//创建一个锚接部件对象
QDockWidget *dock = new QDockWidget("dock1", this);
//设置停靠位置
dock->setAllowedAreas(Qt::TopDockWidgetArea|Qt::RightDockWidgetArea);
//创建和一个文本编辑类的中心部件(中心部件只能有一个)
QTextEdit *edit = new QTextEdit(this);
//将中心部件放入窗口中
setCentralWidget(edit);
//将锚接部件放入窗口中(停靠位置依赖于中心部件)
addDockWidget(Qt::RightDockWidgetArea, dock);
对话框
自定义对话框
自定义对话框可以分为模态对话框和非模态对话框,区别在于对话框期间是否允许对其他的窗口进行操作,其中模态对话框禁止操作
模态对话框
点击新建文件弹出对话框
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->actionfile->setIcon(QIcon(":/image/aaa.png"));
connect(ui->actionfile, &QAction::triggered, this, [=](){
QDialog dialog(this); //创建模态对话框对象
dialog.resize(200, 300); //设置对话框窗口大小
dialog.exec(); //弹出对话框(该方法会阻塞,所以虽然是栈上创建的对话框对象,创建出来的对话框也不会立马消失)
});
}
MainWindow::~MainWindow()
{
delete ui;
}
非模态对话框
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->actionfile->setIcon(QIcon(":/image/aaa.png"));
connect(ui->actionfile, &QAction::triggered, this, [=](){
//创建非模态对话框对象,不能再创建在栈上,因为lambda直接结束对象消失,会导致对话框窗口一闪而逝
QDialog* dialog = new QDialog(this);
//设置对话框窗口大小
dialog->resize(200, 300);
/*
* 由于是创建在堆上,所以对象只有大窗口关闭才会删除,只是关闭对话框不会被删除
* 所以每次点击都会创建一个对象
* 通过设置Qt::WA_DeleteOnClose属性,可以使得每次关闭对话框时删除其中的对象
*/
dialog->setAttribute(Qt::WA_DeleteOnClose);
//通过show方法弹出的对话框为非模态对话框
dialog->show();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
系统标准对话框
QColorDialog
选择颜色
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QColorDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//弹出拾色器并自定义默认选中的颜色
QColor color = QColorDialog().getColor(Qt::red);
qDebug() << color.rgb();
qDebug() << color.red() << color.green() << color.blue();
}
MainWindow::~MainWindow()
{
delete ui;
}
QFileDialog
选择文件或目录
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//弹出打开文件窗口并设置默认路径、以及指定文件格式
//返回:文件全路径
QString fileName = QFileDialog::getOpenFileName(this, "打开文件", ".", "(*.cpp)");
qDebug() << fileName;
}
MainWindow::~MainWindow()
{
delete ui;
}
QFontDialog
选择字体
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QFontDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
bool flag;
//弹出字体选择框并指定默认字体和字号大小等
QFont font = QFontDialog::getFont(&flag, QFont("仿宋", 16));
qDebug() << font.family().toUtf8().data(); //字体
qDebug() << font.pointSize(); //字号
qDebug() << font.bold(); //是否有加粗
}
MainWindow::~MainWindow()
{
delete ui;
}
QInputDialog
允许用户输入一个值,并将其值返回
QMessageBox
模态对话框,常用语显示信息、询问问题等
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QMessageBox::critical(this, "错误", "错误对话框");
QMessageBox::warning(this, "警告", "警告对话框");
QMessageBox::information(this, "信息", "信息对话框");
/*
* 参数4:关联的按钮,默认YES和NO,可自定义
* 参数5:默认选中的按钮
* 返回值:选中的按钮放
*/
QMessageBox::StandardButton res = QMessageBox::question(this, "询问", "询问对话框", QMessageBox::Save|QMessageBox::Cancel, QMessageBox::Save);
if(res == QMessageBox::Save) {
qDebug("save");
} else {
qDebug("Cancel");
}
}
MainWindow::~MainWindow()
{
delete ui;
}
QPageSetupDialog
为打印机提供纸张相关的选项
QPrintDialog
打印机配置
QPrintPreviewDialog
打印预览
QProgressDialog
显示操作过程
widget.ui
布局
Verticl Layout
垂直布局
Horizontl Layout
水平布局
Grid Layout
网格布局,即自动几行几列的排好
按钮
Push Button
普通按钮,可以通过
icon
属性设置图标
Tool Button
工具按钮,一般用来用作图标按钮
QToolButton/toolButtonStyle
如果想同时显示图标和文字,或者其他显示形式,可以修改此属性QToolButton/autoRaise
鼠标退出消散按钮(透明)外框,放入则显示 的特效
Radio Button
单选按钮
- 分组
默认所有单选项为一组,可通过容器Group Box
进行分组 - 默认选中
//先设置好单选项的objectname ui->btn_man->setChecked(true);
- 选中时输出信息
connect(ui->btn_woman, &QRasioButton::clicked, [=](){ qDebut() << "选中了woman"; });
Check Box
多选按钮
- 分组
一样可以用Group Box
进行分组 - 监听选中事件
默认未开启半选中,通过QCheckBox/tristate
属性来开启connect(ui->check_item1, &QCheckBox::stateChanged, [=](int state){ if (state == 0) qDebug() << "未选中"; else if(state == 1) qDebug() << "半选中"; else if(state == 2) qDebug() << "选中"; })
Item Widgets
List Widget
以列表的形式来展示内容
#include "widget.h"
#include "ui_widget.h"
#include <QListWidgetItem>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
/*
* 一次添加一个列表项:这种方式可以设置对齐方式(默认左对齐)
*/
QListWidgetItem *item1 = new QListWidgetItem("床前明月光");
//居中对齐
item1->setTextAlignment(Qt::AlignCenter);
QListWidgetItem *item2 = new QListWidgetItem("疑是地上霜");
//放入ListWidget中
ui->listWidget->addItem(item1);
ui->listWidget->addItem(item2);
/*
* 一次添加多个列表项:这种方式不能设置对齐方式
*/
//可以看做是List<String>
QStringList list;
list << "举头望明月" << "低头思故乡";
ui->listWidget->addItems(list);
}
Widget::~Widget()
{
delete ui;
}
Tree Widget
用树的形式来展示内
#include "widget.h"
#include "ui_widget.h"
#include <QListWidgetItem>
#include <QList>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置头部
ui->treeWidget->setHeaderLabels(QStringList() << "Hero" << "Hero briefing");
//设置根结点
QTreeWidgetItem *powerHeros = new QTreeWidgetItem(QStringList() << "武力" << "法力");
QTreeWidgetItem *iqHeros = new QTreeWidgetItem(QStringList() << "智力");
QTreeWidgetItem *speedHeros = new QTreeWidgetItem(QStringList() << "速度");
//设置二级结点
QTreeWidgetItem *p1 = new QTreeWidgetItem(QStringList() << "张三丰" << "武力100;法力0");
QTreeWidgetItem *p2 = new QTreeWidgetItem(QStringList() << "法海" << "武力20;法力90");
QTreeWidgetItem *i1 = new QTreeWidgetItem(QStringList() << "诸葛亮" << "智力100");
QTreeWidgetItem *i2 = new QTreeWidgetItem(QStringList() << "孙膑" << "智力95");
QTreeWidgetItem *s1 = new QTreeWidgetItem(QStringList() << "盗跖" << "速度90");
QTreeWidgetItem *s2 = new QTreeWidgetItem(QStringList() << "金鹏鸟" << "速度100");
//将二级结点放入根结点
powerHeros->addChild(p1);
powerHeros->addChild(p2);
iqHeros->addChild(i1);
iqHeros->addChild(i2);
speedHeros->addChild(s1);
speedHeros->addChild(s2);
//将结点放入树中
ui->treeWidget->addTopLevelItem(powerHeros);
ui->treeWidget->addTopLevelItem(iqHeros);
ui->treeWidget->addTopLevelItem(speedHeros);
}
Widget::~Widget()
{
delete ui;
}
Table Widget
用表格的形式来展示内
#include "widget.h"
#include "ui_widget.h"
#include <QRandomGenerator>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置列数
ui->tableWidget->setColumnCount(3);
//设置头
ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "姓名" << "性别" << "年龄");
//设置行数
ui->tableWidget->setRowCount(5);
//设置内容
QList<QString> names;
names << "张三" << "李四" << "王五" << "赵六" << "周七";
QStringList genders;
genders << "男" << "女" << "男" << "男" << "女";
for(int i = 0; i < 5; ++i) {
int col = 0;
ui->tableWidget->setItem(i, col++, new QTableWidgetItem(names[i]));
ui->tableWidget->setItem(i, col++, new QTableWidgetItem(genders.at(i)));
ui->tableWidget->setItem(i, col, new QTableWidgetItem(QString::number(QRandomGenerator::global()->bounded(20, 25))));
}
}
Widget::~Widget()
{
delete ui;
}
容器
Group Box
可以给单选按钮、多选按钮进行分组
Scroll Area
滚动条
Tool Box
类似
Tab Widget
标签页
Stacked Widget
ui
设计界面类似轮播图,内容可以是任意的组件,不过实际使用界面没有轮播按钮,所以一般要配合按钮使用,即点击按钮进入对应的内容页,所以可以理解为是个可纵可横的标签页
#include "widget.h"
#include "ui_widget.h"
#include <QRandomGenerator>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置默认选项
ui->stackedWidget->setCurrentIndex(0);
//绑定点击事件
connect(ui->btn_tree, &QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(0);
});
connect(ui->btn_table, &QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(1);
});
connect(ui->btn_list, &QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(2);
});
}
Widget::~Widget()
{
delete ui;
}
Frame
Widget
可以对放置在里面的部件进行布局
MDI Area
Dock Widget
锚接部件
QAxWidget
Input Widgets
Combo Box
下拉框
ui->comboBox->addItem("选项1");
ui->comboBox->addItem("选项2");
ui->comboBox->addItem("选项3");
//默认选中项
ui->comboBox->setCurrentIndex(0); //或setCurrentText("选项1")
Font Combo Box
字体下拉框
Line Edit
类似<input>标签
Text Edit
类似<textarea>标签,其中文字可设置粗细、斜体、大小等样式
Plain Text Edit
同上,只不过没有样式
Spin Box
类似购物车商品数的加减
Double Spin Box
和上面的相比,数值为
Double
类型
Time Edit
时间选择器
Date Edit
日期选择器
Date/Time Edit
时间日期选择器
Dial
Horizontal Scroll Bar & Horizontal Slider
横向滚动条
Vertical Scroll Bar & Vertical Slider
纵向滚动条
Key Sequence Edit
Display Widgets
Label
标签,可以显示文本或图片(包括
gif
动图)
//放图片
ui->label_img->setPixmap(QPixmap(":/image/butterfly.png"));
//放动图
QMovie *movie = new QMovie(":/image/demo.gif");
ui->label_movie->setMovie(movie);
movie->start();
Text Browser
Graphics View
Calendar Widget
LCD Number
Progress Bar
Horizontal Line
Vertical Line
控件的封装:SpinBox和滚动条的联动
项目基类选择空部件的
QWidget
即可
一、新建Qt
文件
- 新建文件(选
Qt
) - 设计师界面类(除了
.h
和.cpp
还包括了ui
文件) - 选择界面模板(
Widget
)
二、界面设计
四、联动逻辑编写
smallwidget.h
#ifndef SMALLWIDGET_H #define SMALLWIDGET_H #include <QWidget> namespace Ui { class SmallWidget; } class SmallWidget : public QWidget { Q_OBJECT public: explicit SmallWidget(QWidget *parent = nullptr); ~SmallWidget(); private: Ui::SmallWidget *ui; }; #endif // SMALLWIDGET_H
smallwidget.cpp
#include "smallwidget.h" #include "ui_smallwidget.h" #include <QSpinBox> #include <QSlider> SmallWidget::SmallWidget(QWidget *parent) : QWidget(parent), ui(new Ui::SmallWidget) { ui->setupUi(this); //数值改变,进度条随之改变 void(QSpinBox::*spinBoxP)(int) = &QSpinBox::valueChanged; connect(ui->spinBox, spinBoxP, [=](int spinBoxVal) { ui->horizontalSlider->setValue(spinBoxVal); }); //进度条改变,数值随之改变 connect(ui->horizontalSlider, &QSlider::valueChanged, [=](int sliderVal) { ui->spinBox->setValue(sliderVal); }); } SmallWidget::~SmallWidget() { delete ui; }
smallwidget.ui
三、引入设计好的界面
- 选择一个
Widget
容器 - 右键提升为
- 基类:
QWidget
- 提升的类名称:新建文件的类名
- 全局包含:选中
- 添加
- 提升
- 基类:
事件
常用的鼠标事件
mylabel.h
#ifndef MYLABEL_H #define MYLABEL_H #include <QLabel> class MyLabel : public QLabel { Q_OBJECT public: explicit MyLabel(QWidget *parent = nullptr); //鼠标进入MyLabel void enterEvent(QEnterEvent *event); //鼠标离开MyLabel void leaveEvent(QEvent *event); //鼠标在MyLabel中(按下后)移动 void mouseMoveEvent(QMouseEvent *event); //鼠标在MyLabel中压下 void mousePressEvent(QMouseEvent *event); //鼠标在MyLable中放开 void mouseReleaseEvent(QMouseEvent *event); //鼠标在MyLabel中双击 void mouseDoubleClickEvent(QMouseEvent *event); signals: }; #endif // MYLABEL_H
mylabel.cpp
#include "mylabel.h" #include <QDebug> #include <QMouseEvent> MyLabel::MyLabel(QWidget *parent) : QLabel{parent} { } void MyLabel::enterEvent(QEnterEvent *event){ qDebug() << "鼠标进入MyLabel"; } void MyLabel::leaveEvent(QEvent *event){ qDebug() << "鼠标离开MyLabel"; } void MyLabel::mouseMoveEvent(QMouseEvent *event){ //鼠标移动中获取按下的键需要用buttons(),因为移动过程中可能同时按下了多个键 if (event->buttons() & Qt::LeftButton) { qDebug() << "移动过程中按下了鼠标左键"; } qDebug() << "鼠标在MyLabel中(按下后)移动"; } void MyLabel::mousePressEvent(QMouseEvent *event){ Qt::MouseButton btn = event->button(); if (btn == Qt::LeftButton) { qDebug() << "按下了鼠标左键"; } //x, y:在控件MyLabel的位置 qDebug() << QString("鼠标在MyLabel中压下:x=%1, y=%2").arg(event->x()).arg(event->y()); } void MyLabel::mouseReleaseEvent(QMouseEvent *event){ qDebug() << "鼠标在MyLable中放开"; } void MyLabel::mouseDoubleClickEvent(QMouseEvent *event) { qDebug() << "鼠标在MyLable中双击"; }
- 将
ui
中的Label
提升为MyLabel
定时器
实现方式一:定时器事件
-
ui
-
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); ~Widget(); //定时器事件 void timerEvent(QTimerEvent *event); private: Ui::Widget *ui; //每1秒定时器标识 int timer1; //每2秒定时器标识 int timer2; }; #endif // WIDGET_H
-
widget.cpp
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //开启定时器(单位毫秒) this->timer1 = startTimer(1000); this->timer2 = startTimer(2000); } Widget::~Widget() { //删除定时器 killTimer(this->timer1); killTimer(this->timer2); delete ui; } void Widget::timerEvent(QTimerEvent *event) { int timerId = event->timerId(); static int timer1num = 1; static int timer2num = 1; if (timerId == this->timer1) { ui->label_per1->setText(QString::number(timer1num++)); } else if (timerId == this->timer2) { ui->label_per2->setText(QString::number(timer2num++)); } }
实现方式而:定时器类
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建定时器类的对象
QTimer *timer1 = new QTimer(this);
QTimer *timer2 = new QTimer(this);
//启动并设置interval,当达到事件时会抛出timeout信号
timer1->start(1000);
timer2->start(2000);
//绑定timeout信号
connect(timer1, &QTimer::timeout, [=](){
static int num = 1;
ui->label_per1->setText(QString::number(num++));
});
connect(timer2, &QTimer::timeout, [=](){
static int num = 1;
ui->label_per2->setText(QString::number(num++));
});
}
事件的传递过程
- 事件被触发
- 事件过滤器
- 作用:过滤事件
- 步骤
- 给控件安装过滤器
- 重写过滤器事件
bool eventFilter(Object* obj, QEvent *e);
- 事件分发器
/* * 作用:可以对事件进行拦截处理 * 返回值:是否继续往下分发事件,true表示不继续 */ bool event(QEvent *event);
- 事件
如鼠标点击、定时器事件、自定义事件等
事件过滤器
widget.h
class Widget: ... public: bool eventFilter(QObject *, QEvent *);
widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { //给控件安装过滤器 ui->mylabel->installEventFilter(this); } /* * obj: 空间对象,用于区分控件 * e: 事件对象 */ bool Widget::eventFilter(QObject *obj, QEvent *e) { //拦截mylabel中的事件 if(obj == ui->mylabel) { //拦截其中的鼠标按下事件 if(e->type() == QEvent::MouseButtonPress) { //... return true; } } //其他对象的时间交给父类处理 return QWidget::eventFilter(obj, e); }
事件分发器
bool MyLabel::event(QEvent *e) {
//拦截鼠标按下事件
if(e->type() == QEvent::MouseButtonPress) {
QMouseEvent *ev = static_cast<QMouseEvent *>(e);
qDebug() << ev->x() << ev->y();
return true; //不在继续往下分发
}
//其他事件交给父类处理
return QLabel::event(e);
}
绘图
会自动触发事件
绘图事件
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); ~Widget(); void paintEvent(QPaintEvent *); private: Ui::Widget *ui; int posX; }; #endif // WIDGET_H
widget.cpp
#include "widget.h" #include "ui_widget.h" #include <QPainter> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); this->posX = 0; connect(ui->btn, &QPushButton::clicked, [=](){ posX += 20; if (posX > this->width()) { posX = 0; //如果超出窗口宽度,置0 } //手动调用绘图事件 //repaint(); 效率比update低 update(); }); } Widget::~Widget() { delete ui; } void Widget::paintEvent(QPaintEvent *) { /* * 创建一个绘画者对象 * 构造器参数表示绘画设备,即在哪里画画 * this表示在当前窗口中画 */ QPainter painter(this); /* * 设置画笔 * 用于自定义画笔风格,可以不设置 * 构造器可以指定画笔颜色 */ QPen pen(QColor("#f99")); //设置画笔宽度(默认1) pen.setWidth(2); //设置画笔风格 pen.setStyle(Qt::DotLine); //添加画笔 painter.setPen(pen); /* * 设置画刷 * 用于填充封闭形状,可不设 * 构造器可以指定画刷颜色 */ QBrush brush(Qt::cyan); //设置画刷风格 brush.setStyle(Qt::Dense1Pattern); //添加画刷 painter.setBrush(brush); //画线 painter.drawLine(QPoint(0, 0), QPoint(200, 200)); //画(椭)圆(圆心,max x半径,max y半径) painter.drawEllipse(QPoint(100, 100), 100, 50); /* * 画矩形 * QRect(左上角坐标, 宽高) */ painter.drawRect(QRect(QPoint(20, 20), QSize(50, 50))); /* * 写字(在指定的区域内) */ painter.drawText(QRect(100, 10, 150, 20), "hello qt6"); //画资源图片(绘画起点|指定区域,资源图片位置) painter.drawPixmap(this->posX, 0, QPixmap(":/image/luffy.png")); }
- 手动触发绘图事件
点击移动按钮,移动图片位置
高级设置
抗锯齿
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPen pen(QColor("#f99"));
painter.setPen(pen);
QBrush brush(Qt::cyan);
painter.setBrush(brush);
painter.drawEllipse(QPoint(100, 100), 50, 50);
//设置抗锯齿能力(会降低成图效率)
painter.setRenderHint(QPainter::Antialiasing);
painter.drawEllipse(QPoint(300, 100), 50, 50);
}
绘画起点
//画一个矩形
painter.drawRect(QRect(20, 20, 50, 50));
//移动画家的绘画起点(默认(0, 0))
painter.translate(100, 0);
//在画一个一样的矩形(由于绘画起点不一样,所以不会被覆盖)
painter.drawRect(QRect(20, 20, 50, 50));
//保存当前的画家状态(绘画起点位置)
painter.save();
//再次移动画家的绘画起点((100, 0) -> (200, 0))
painter.translate(100, 0);
//恢复画家状态((200, 0) -> (100, 0))
painter.restore();
绘图设备
指的是继承
QPainterDevice
的子类,Qt
提供了很多这样的类,如:QPixmap, QBitmap, QImage, QPicture
QPixmap
专门为图像在屏幕上显示做了优化,往窗口绘画示例
//写入文件示例
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
/*用QPixmap做绘图设备*/
//创建设备对象并设置设备大小
QPixmap pix(300, 300);
//设置背景色(默认为黑色)
pix.fill(Qt::white);
QPainter painter(&pix);
painter.setPen(QPen(QColor("#f99")));
painter.drawEllipse(QPoint(150, 150), 100, 100);
//将绘好的图保存
pix.save("D:/data/demo.png");
}
Widget::~Widget()
{
delete ui;
}
QBitmap
是
QPixmap
的一个子类,色深限定为1(即只有黑白色),可以使用QPimap.isQBitmap()
函数来确定一个QPixmap
是不是QBixmap
QImage
专门为图像的像素级访问做了优化
//写入文件示例
//创建设备对象并设置设备大小以及色位类型
QImage img(300, 300, QImage::Format_RGB32);
//设置背景色(默认为黑色)
img.fill(Qt::white);
QPainter painter(&img);
painter.setPen(QPen(QColor("#f99")));
painter.drawEllipse(QPoint(150, 150), 100, 100);
//将绘好的图保存
img.save("D:/data/demo.png");
//往窗口绘图 & 修改像素点 示例
void Widget::paintEvent(QPaintEvent *) {
QImage img;
img.load(":/image/demo.png");
QPinter painter(this);
//修改像素点
for(int i = 100; i < 150; ++i) {
for(int j = 100; j < 150; ++j) {
QRgb p = qRgb(255, 0, 0);
img.setPixel(i, j, p); //将坐标为(i, j)的像素改为p
}
}
painter.drawImage(0, 0, img);
}
QPicture
可以记录和重现
QPainter
的各条命令
//记录命令
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPicture>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPicture pic;
QPainter painter;
//开始往设备pic上绘图
painter.begin(&pic);
painter.drawEllipse(QPoint(150, 150), 100, 100);
//结束绘图
painter.end();
//保存绘图命令
pic.save("D:/data/pic");
}
Widget::~Widget()
{
delete ui;
}
//重现命令
void Widget::paintEvent(QPaintEvent *) {
QPicture pic;
pic.load("D:/data/pic");
painter.drawPicture(0, 0, pic);
}
文件操作
资源文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
//在这行代码之后写自己的代码
ui->setupUi(this);
//使用绝对路径方式:根据ui设计中的变量名进行图标设置
//ui->actionfile->setIcon(QIcon("C:\\Users\\Administrator\\Desktop\\aaa.png"));
/*
* 使用资源路径方式
* 1.可以在项目根路径中添加一个image目录
* 2.创建qrc文件:文件 -> new file -> Qt -> Qt Resource File(一般起名res)
* 3.添加前缀:即资源文件子目录,如/
* 4.添加文件:将image中的图片选中
* 5.build
* 6.可以看到rec.qrc -> / -> image -> aaa.png 这样的目录结构
* 7.冒号 + 前缀名 + 文件名
*/
ui->actionfile->setIcon(QIcon(":/image/aaa.png"));
}
MainWindow::~MainWindow()
{
delete ui;
}
QFile
文件读写类
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QFile>
#include <QMessageBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->check_btn, &QPushButton::clicked, [=](){
QString filePath = QFileDialog::getOpenFileName(this, "打开文件", "A:\\Tools\\WinRAR", "(*.txt)");
if (!filePath.isEmpty()) {
ui->title_line->setText(filePath);
//创建文件对象
QFile file(filePath);
/*读取文件*/
//设置打开方式为只读
file.open(QIODevice::ReadOnly);
//一次全部读取
//QByteArray array = file.readAll();
//一次读取一行
QByteArray array;
while(!file.atEnd()) {
array += file.readLine();
}
//QByteArray到QString存在隐式转换
ui->content_text->setText(array);
//关闭流
file.close();
/*写入文件*/
//WriteOnly为覆盖,Append为追加
file.open(QIODevice::Append);
file.write("abc");
file.close();
} else {
QMessageBox::warning(this, "警告", "未选择文件");
}
});
}
Widget::~Widget()
{
delete ui;
}
QFileInfo
文件信息类
QFileInfo info(filePath);
//后缀名
info.suffix(); //txt
//文件大小
info.size();
//文件名
info.fileName();
//文件路径
info.filePath();
//创建时间(birthTime)
info.created().toString("yyyy-MM-dd hh:mm:ss");
//最后修改时间
info.lastModified().toString("yyyy/MM/dd hh:mm:ss");