基于C++的图形界面开发工具包,类似于JDK。
开发环境,编译器,相当于Vistual studio。
.pro 为工程文件,是qmake自动生成的用于生产makfile的配置文件
QT += core gui //包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于QT4版本的才包含此模块
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \ //源文件
main.cpp \
widget.cpp
HEADERS += widget.h //头文件
FORMS += widget.ui
main.cpp
#include "widget.h" // 定义类对应的头文件
#include //标准类名头文件
int main(int argc, char *argv[])
{
QApplication a(argc, argv); //应用程序类,负责处理应用程序的初始化和结束,事件处理调度
Widget w; //实例化对象
w.show(); //显示图形界面
return a.exec(); //主事件循环,在exec函数中,QT接收并处理用户和系统的事件
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) //构造函数
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget() //析构函数
{
delete ui;
}
一般情况,窗口的属性和添加控件、对控件的操作都会在类的构造函数中定义。
按钮的创建通过QPushButton类。
#include //需要导入按钮类头文件
QPushButton *button = new QPushButton("OK",this);
可以显示文字、显示图片、显示动图。
创建方式
显示文字
//显示HTML
QLabel *label = new QLabel(this);
label->setText("百度
");
label->setOpenExternalLinks(true);
显示图片
QPixmap pix;
pix.load(":/img/wangyi");
ui->label->setPixmap(pix);
显示动图
QMovie *movie = new QMovie(":/img/gif");
ui->label_2->setMovie(movie);
movie->start();
###### 3. LineEdit 单行文本输入框
ui->lineEdit->setText("请输入密码");
QString str = ui->lineEdit->text();
qDebug()<<str;
ui->lineEdit_2->setEchoMode(QLineEdit::Password);
可以自己定义一个重复使用的控件,提高复用性。
复杂、头疼,不过很好用。
ui->stackedWidget->setCurrentIndex(0);
connect(ui->pushButton,&QPushButton::clicked,this,[=](){
ui->stackedWidget->setCurrentIndex(0);
});
connect(ui->pushButton_2,&QPushButton::clicked,this,[=](){
ui->stackedWidget->setCurrentIndex(1);
});
Qt中创建对象的时候会提供一个指向parent对象指针。每创建一个控件就会在QObject子对象指针列表中进行添加。Qwidget继承自QObject,因此也会继承这种对象树关系。在delete子控件的时候会从对象树中删除关系然后回收控件内存,在删除父控件的时候会先回收子控件,类似链表的删除。如果QObject在栈上创建,Qt保持同样的行为。new关键字会申请堆空间。
对象模型在一定程度上能够简化内存回收问题,在一定程度解决了内存问题。
在使用的时候需要注意:
QWidget window;
QPushButton button = QPushButton("quit",&window);
下面代码会重复析构,导致程序崩溃
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
信号和槽机制类似于观察者模式,当创建事件时,会发出广播信号,能够接收到信号的对象且对其感兴趣则会使用链接(connect)函数。例如当老师提问时,发出提问信号,认真听课的同学会接收到信号,感兴趣的同学会举起他的手相应信号并回答问题,其余的同学会假装没接收到默默干自己的事。
connect函数
connect(sender,signal,receiver,slot);
sender 信号发出者
signal 信号
receiver 接受者
slot 接受到信号执行任务
QT中内置的信号与槽机制
目的:通过点击按钮实现窗口的关闭。
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton * button = new QPushButton("退出",this);
connect(button,&QPushButton::clicked,this,&Widget::close); //信号发出者button 信号clicked 信号接收者Widget 收到信号后动作close
}
自定义信号与槽
需要定义信号接收者、信号接收者、信号、接收后发生的动作。
//添加老师类
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
void question(); // 声明信号,只需要声明不需要实现
};
//添加学生类
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
void answer(); // 高版本QT直接在public区声明接收到信号后的动作
signals:
};
函数实现
void Student::answer(){
qDebug()<<"学生回答问题";
}
实例化老师和学生,在connect函数中调用。在定义完所必须的元素后还需要确定信号发出的时刻,因此需要定义函数实现,这是与QT内置信号执行函数所不同的一点。调用实现
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void ClassStart();
Teacher *tea;
Student *stu;
private:
Ui::Widget *ui;
};
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *ask = new QPushButton("提问",this);
this->tea = new Teacher();
this->stu = new Student();
ask->move(200,100);
connect(tea,&Teacher::question,stu,&Student::answer);
ClassStart();
}
void Widget::ClassStart(){
emit tea->question();
}
执行上述代码后,会通过ClassStart函数实现信号的发出,然后通过connect实现信号与槽机制的后续调用。以上代码是全部需要执行的,即函数执行后便会发出信号,我们还可以通过点击按钮后选择性的调用信号,这样更便于理解。
方式一:通过内部函数调用
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *ask = new QPushButton("提问",this);
connect(ask,&QPushButton::clicked,this,&Widget::ClassStart);
ask->move(200,100);
this->tea = new Teacher(this);
this->stu = new Student(this);
connect(tea,&Teacher::question,stu,&Student::answer);
// ClassStart();
}
方式二:通过信号连接信号
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *ask = new QPushButton("提问",this);
ask->move(200,100);
this->tea = new Teacher();
this->stu = new Student();
connect(tea,&Teacher::question,stu,&Student::answer);
connect(ask,&QPushButton::clicked,tea,&Teacher::question);
// ClassStart();
}
重载自定义信号与槽
1、重新声明信号函数,带参数
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
void question();
void question(QString math);
};
2、重写槽函数声明及定义,带参数
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
void answer();
void answer(QString math);
signals:
};
void Student::answer(QString math){
qDebug().nospace()<<"学生回答问题"<<math.toUtf8().data();
}
3、由于函数重载了,因此需要通过函数指针指向函数地址,然后再做连接
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *ask = new QPushButton("提问",this);
ask->move(200,100);
this->tea = new Teacher();
this->stu = new Student();
void (Teacher:: *teachersignal)(QString) = &Teacher::question;
void (Student:: *studentslot)(QString) = &Student::answer;
connect(tea,teachersignal,stu,studentslot);
connect(ask,&QPushButton::clicked,this,&Widget::ClassStart);
// ClassStart();
}
信号与槽总结
1、发送者与接收者需要是QObject类的子类。
2、信号和槽函数的返回值都是void
3、信号需要声明不需要实现,槽函数都需要
4、槽函数是普通的成员函数,作为成员函数,会受到public、private、protected的影响
5、使用emit在恰当的位置发送信号
6、使用connect函数连接信号和槽
7、任何成员函数、static函数、全局函数和lambda表达式都可以作为槽函数
8、信号槽要求信号和槽的参数一致,即参数类型一致。
如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少。
举例:
signal(QString)和slot(QString)√
signal(QString,QString)和slot(QString)√
signal(QString)和slot(QString,QString)×
signal(QString,QString,int)和slot(QString,QString)√
signal(QString,QString,int)和slot(QString,int)×
9、信号与槽对应可以一对一,一对多(发出信号,槽函数一个一个执行,顺序不确定),多对一(任意信号发出,槽函数都会执行)
10、信号可以连接信号
11、槽可以被断开连接,也可以被取消连接。
12、可以使用c++中的lambda表达式。
C++中的Lambda表达式用于定义并创建匿名函数,以简化代码。
[capture](parameters) mutable->return-type{statement}
//[]是引出符,必须存在,不能省略;capture 捕获列表,捕获的是那些定义Lanbda为止时Lambda所在作用范围内可见的局部变量。
//parameters 参数列表,与普通函数的参数列表一致,如果不需要传递参数,()可以省略。
//mutable可修改标识符,按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝
//->return-type返回值类型,如不需要则可省略。
//{statement}函数体,内容与普通函数一致。
举例:
//通过lambda实现输出
auto fun = [](){
qDebug()<<"lambda表达式被调用";
};
fun();
//带参数的lambda表达式
auto fun = [](int a,QString b){
qDebug()<<b<<a;
};
fun(1,"有几个一");
//mutable提供可修改功能,否则m值为只读
int m = 1;
auto fun = [m](int a,QString b)mutable{
qDebug()<<b<<a;
m = 2;
qDebug()<<m;
};
fun(1,"有几个一");
信号与槽机制可以用来实现按钮事件,相比Java和pythonGUI还是复杂的多。
QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏,多个工具栏,多个铆接部件,一个中心部件、一个状态栏。
菜单栏类QmenuBar
菜单类Qmenu
QAction类:充当子菜单(菜单项)
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QMenuBar *menubar = menuBar(); //使用成员函数创建菜单栏,唯一的
this->setMenuBar(menubar);
QMenu *file_menu = new QMenu("文件");
QMenu *edit_menu = new QMenu("编辑");
QMenu *view_menu = new QMenu("视图");
menubar->addMenu(file_menu);
menubar->addMenu(edit_menu);
menubar->addMenu(view_menu);
QAction *open_file = new QAction("打开文件");
QAction *save_file = new QAction("保存文件");
QAction *exit = new QAction("退出");
file_menu->addAction(open_file);
file_menu->addAction(save_file);
file_menu->addAction(exit);
}
###### 6.2 工具栏
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QMenuBar *menubar = menuBar();
this->setMenuBar(menubar);
QMenu *file_menu = new QMenu("文件");
QMenu *edit_menu = new QMenu("编辑");
QMenu *view_menu = new QMenu("视图");
menubar->addMenu(file_menu);
menubar->addMenu(edit_menu);
menubar->addMenu(view_menu);
QAction *open_file = new QAction("打开文件");
QAction *save_file = new QAction("保存文件");
QAction *exit = new QAction("退出");
file_menu->addAction(open_file);
file_menu->addAction(save_file);
file_menu->addAction(exit);
connect(exit,&QAction::triggered,this,&QMainWindow::close);
QToolBar *toolbar = new QToolBar(this); //创建工具栏
toolbar->setAllowedAreas(Qt::LeftToolBarArea); //设置允许工具栏停放的位置
// toolbar->setMovable(false);
this->addToolBar(Qt::LeftToolBarArea,toolbar); // 设置工具栏的初始位置
toolbar->addAction(open_file); //为工具栏添加动作
toolbar->addAction(save_file);
}
状态栏QStatusBar
包括永久信息和临时信息
QStatusBar *statusbar =statusBar();
this->setStatusBar(statusbar);
// statusbar->showMessage("加载成功",3000); //添加临时信息,可以显示三秒
QLabel *time = new QLabel("时间",this);
statusbar->addWidget(time); // 添加正式信息,会被临时信息覆盖
QLabel *local = new QLabel("地址",this);
statusbar->addPermanentWidget(local); //添加永久信息
###### 6.4 铆接部件
QDockWidget * dockwidget = new QDockWidget("毁灭木星计划.docx",this);
this->addDockWidget(Qt::TopDockWidgetArea,dockwidget); //设置位置,添加铆接部件
添加一个文本编辑器
this->addDockWidget(Qt::TopDockWidgetArea,dockwidget);
QTextEdit *edit = new QTextEdit("文本编辑器",this);
this->setCentralWidget(edit);
Qt资源文件是一个跨平台的资源机制,用于将程序运行时所需要的资源以二进制的形式存储与可执行文件内部。
QPixmap pix; //定义图片组件
pix.load(":/mouse"); //加载图片
open_file->setIcon(QIcon(pix));
添加背景图片,需要注意的一点是添加的图片路径中不能包含有中文名
QPixmap pix = QPixmap(":/img/lunxun").scaled(this->size());
// pix.load(":/mouse");
open_file->setIcon(QIcon(pix));
this->setFixedSize(800,600);
this->setAutoFillBackground(true);
QPalette palette; // 调色板
palette.setBrush(QPalette::Window,QBrush(pix)); // 画刷
this->setPalette(palette);
ui功能:绘制界面
对话框QDialog。对话框分为模态对话框和非模态对话框。
模态对话框:会阻塞同一应用程序中其他窗口的输入。
非模态对话框: 不会。
注意事项:对话框要在堆上创建;由于对话框的特性,可以设置对话框关闭,自动销毁对话框。
QDialog *dialog = new QDialog; //非模态对话框
dialog->setWindowTitle(tr("hello"));
dialog->show();
标准对话框是QT中内置的一系列对话框;
对话框 | 功能 |
---|---|
QColorDialog | 选择颜色 |
QFileDialog | 选择文件或者目录 |
QFontDialog | 选择字体 |
QInputDialog | 允许用户输入一个值,并将其值返回 |
QmessageBox | 模态对话框,用于显示信息,询问问题等 |
(1)文件对话框 | |
通过对话框选取一个文件: |
void MainWindow::on_pushButton_clicked()
{
QString filename = QFileDialog::getOpenFileName(this,tr("打开文件"),"./",tr("Images(*.png *.jpg);;Text(*.txt)"));
if(!filename.isEmpty()){
ui->textEdit->append(filename);
}
}
“Images (*.png .xpm .jpg);;Text files (.txt);;XML files (.xml)”
通过对话框选取多个文件:
void MainWindow::on_pushButton_2_clicked()
{
QStringList filename = QFileDialog::getOpenFileNames(this,"打开文件","./","Images(*.png *.jpg)");
for(int i=0;i<filename.count();i++){
qDebug()<<filename.at(i);
}
}
(2)颜色对话框
通过点击按钮,生成颜色对话框,选择颜色为编辑框字体设置颜色。
void MainWindow::on_pushButton_3_clicked()
{
QPalette pal = ui->textEdit->palette(); // 获取现有调色板数据
QColor iniColor = pal.color(QPalette::Text);
QColor color = QColorDialog::getColor(iniColor,this,"选择颜色"); //返回值是一个颜色变量
if(color.isValid()){
pal.setColor(QPalette::Text,color);
ui->textEdit->setPalette(pal);
}
}
(3)字体对话框
通过点击按钮,生成字体对话框,选择字体为编辑框字体设置字体格式。
void MainWindow::on_pushButton_4_clicked()
{
bool ok;
QFont iniFont = ui->textEdit->font();
QFont font = QFontDialog::getFont(&ok,iniFont,this);
if(ok){
ui->textEdit->setFont(font);
}
}
(4)消息对话框
void MainWindow::on_pushButton_5_clicked()
{
// QMessageBox::critical(this,"error","报错");
// QMessageBox::warning(this,"warning","警告");
// QMessageBox::information(this,"消息","正常");
QMessageBox::StandardButton result = QMessageBox::question(this,"选择","好了没",QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,QMessageBox::NoButton);
if(result==QMessageBox::Yes){
qDebug()<<"保存中";
}
else if(result==QMessageBox::No){
qDebug()<<"不保存";
}
else{
qDebug()<<"取消";
}
}
(5)输入对话框
QInputDialog 输入对话框
void MainWindow::on_pushButton_6_clicked()
{
bool ok;
QString text = QInputDialog::getText(this,tr("输入对话框"),tr("请输入今天心情"),QLineEdit::Normal,"emo",&ok);
if(ok){
ui->textEdit->append(text);
}
}
Qt提供了两种组件定位机制:绝对定位和布局定位。
绝对定位:需要提供组件的长高,坐标值
**布局定位:**需要指定使用哪种布局
一般使用widget中的四种布局。
使用了水平布局、栅格布局、弹簧,使得页面能够保持相对比例,不会产生失调。
然后实现登陆界面的跳转,在点击登录后会跳转到主界面。void MainWindow::on_pushButton_clicked()
{
Form *new_window = new Form();
new_window->show();
}
事件流程:事件派发->事件过滤器->事件派发->事件处理。
事件:系统或者Qt本身在不同的时刻发出的
事件循环开始:exec()函数。
虚函数可以在子类中重写。
1、重写窗口关闭事件
void MainWindow::closeEvent(QCloseEvent *event){
int ret = QMessageBox::question(this,"提示","确定关闭?");
if(ret == QMessageBox::Yes){
event->accept();
}
else{
event->ignore();
}
}
2、重写窗口Resize函数
void MainWindow::resizeEvent(QResizeEvent *event){
qDebug()<<"oldsize="<<event->oldSize();
qDebug()<<"newsize="<<event->size();
}
子类声明要重写的事件函数
在子类的cpp中实现事件函数
3、鼠标事件
4、事件分发
案例:键盘事件,监听tab键。
bool MainWindow::event(QEvent *event){
if(event->type()==QEvent::KeyPress){
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key()==Qt::Key_Tab){
qDebug()<<"TAB被按下";
return true;
}
}
return QMainWindow::event(event);
}
5、事件过滤器(很少重写)
先判断是否是要过滤事件的组件,如果是要过滤事件的组件,再去判断事件是否过滤,如果过滤返回True,如果不过滤返回false,如果不是要过滤要过滤事件的组件,返回父类事件过滤函数。
QPainter:画笔
QPainterDevice:绘图设备(纸张、墙壁)
QPaintEngine
void MainWindow::paintEvent(QPaintEvent *p){
QPainter painter(this);
painter.drawLine(80,100,500,500);
}