Qt教程,Qt5编程入门教程(非常详细) (biancheng.net)
B站视频:最新QT从入门到实战完整版——传智教育
Qt帮助文档路径:E:\Qt\Qt5.9.9\5.9.9\mingw53_32\bin assistant.exe
main.cpp:与C++程序固定格式一致
#include "IFT_platform_for_C_program.h"
#include
//main中定义了QApplication,IFT_platform_for_C_program类的对象,因此引入QApplication和IFT_platform_for_C_program.h头文件
int main(int argc, char *argv[])
{
//a为应用程序对象,有且仅有一个
QApplication a(argc, argv);
//窗口对象,IFT_platform_for_C_program父类->Qweight
IFT_platform_for_C_program w;
//默认情况下,Qt提供的所有组件都是隐藏的,调用窗口类的show()方法即可显示w窗口
w.show();
//使应用程序对象进入消息循环机制
//让代码阻塞到这一行
return a.exec();
}
IFT_platform_for_C_program.h和IFT_platform_for_C_program.cpp
//IFT_platform_for_C_program.h
#include
#include "ui_IFT_platform_for_C_program.h"
//继承自QMainWindow窗口类
//初始状态下IFT_platform_for_C_program类由Q——OBJECT、构造函数和析构函数组成
class IFT_platform_for_C_program : public QMainWindow
{
//Q_OBJECT宏,允许类种使用信号和槽的机制
Q_OBJECT
public:
//构造函数:QWidget是所有组件的基类,借助parent指针,可以为当前窗口指定父窗口
IFT_platform_for_C_program(QWidget *parent = Q_NULLPTR);
private:
Ui::IFT_platform_for_C_programClass ui;
};
//IFT_platform_for_C_program.cpp
#include "IFT_platform_for_C_program.h"
IFT_platform_for_C_program::IFT_platform_for_C_program(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
}
命名规范:
出现中文乱码问题的解决方案:要用到中文字符的头文件和源文件开头加上MSVC的一个宏。
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
在IFT_platform_for_C_program.cpp中进行创建按钮:
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include "IFT_platform_for_C_program.h"
#include
IFT_platform_for_C_program::IFT_platform_for_C_program(QWidget* parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//创建第一个按钮,构造函数:QPushButton(const QString &text, QWidget *parent = Q_NULLPTR)
//parent指针指向this当前窗口,也可通过btn->setParent(this)指定,btn->setText()设置文字
QPushButton* btn = new QPushButton("第一个按钮", this);
//创建第二个按钮
QPushButton* btn2 = new QPushButton("第二个按钮", this);
//移动第二个按钮:btn2->mov
btn2->move(100, 100);
//重新设置第二个按钮大小
btn2->resize(50, 50);
//重置窗口大小
resize(600, 400);
//设置固定窗口大小
setFixedSize(600, 400);
//设置窗口标题
setWindowTitle("设置第一个窗口大小");
}
在Qt中创建对象时会提供一个Parent对象指针,下面解释这个Parent指针到底是干什么的。
右键项目名称->添加->Add Qt Class->Qt Widgets Class,填写Class Name,Base class处没有QPushButton,填写默认QWidgt。
查看新增的mypushbutton.cpp与bypushbutton.h
//MyPushButton.h
#pragma once
//修改1 #include ->#include
#include
#include "ui_mypushbutton.h"
//修改2 class MyPushButton : public QWidget->public QPushButton
class MyPushButton : public QPushButton
{
Q_OBJECT
public:
//构造函数和析构函数
MyPushButton(QWidget *parent = Q_NULLPTR);
~MyPushButton();
private:
Ui::MyPushButton ui;
};
//MyPushButton.cpp
#include "MyPushButton.h"
//QDebug模块观察构造函数与析构函数的调用情况
#include
//修改1 QWidget(parent)->QPushButton(parent)
MyPushButton::MyPushButton(QWidget *parent)
: QPushButton(parent)
{
ui.setupUi(this);
qDebug() << "我的按钮类构造调用";
}
MyPushButton::~MyPushButton()
{
qDebug() << "我的按钮类析构调用";
}
//IFT_platform_for_C_program.cpp:
//添加头文件
#include "mypushbutton.h"
//创建一个自己的按钮的对象
MyPushButton* myBtn = new MyPushButton;
myBtn->setText("我自己的按钮");
myBtn->move(200, 0);
myBtn->setParent(this);//设置对象树中
运行:可以看到成功创建了新的按钮,且消息栏中提示构造函数与析构函数被调用
myBtn->setParent(this);将myBtn设置在对象树中,关闭主窗口,myBtn也被析构。此外,局部对象的实际析构顺序是创建顺序的反顺。
左上角(0,0),X向右增加,Y向下增加。
关联某个信号函数和槽函数,需要搞清楚以下 4 个问题:
Qt5 版本中,connect() 函数的语法格式是:
QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)
connect() 函数指定信号函数和槽函数的语法格式是**&函数所在类::函数名**,信号和槽查找signal和slot
//IFT_platform_for_C_program.cpp:
//需求,点击我自己的按钮,关闭窗口
connect(myBtn, &QPushButton::clicked, this, &QWidget::close);
新建项目Widget
自定义功能:下课后,老师->饿了->学生->请客
首先定义Teacher,Student两个类,将其设为QObject类型。
设置信号函数:自定义信号写到signals下,无返回值有参数,只需声明无需实现。
//teacher.h声明
#pragma once
#include
class Teacher : public QObject
{
Q_OBJECT
signals:
//自定义信号,写到signals下
//没有返回值void,只需要声明不需要实现
//可以有参数,可以重载
void hungry();
public:
Teacher(QObject *parent);
~Teacher();
};
设置槽函数:写在Public或全局下,无返回值有参数,既需声明又需实现
//student.h声明
#pragma once
#include
class Student : public QObject
{
Q_OBJECT
public:
//槽函数,写在public下,或全局下
//返回值void,需要声明也需要实现
//可以有参数,可重载
void treat();
Student(QObject *parent);
~Student();
};
//student.c实现
#include "student.h"
#incldue
void Student::treat()
{
qDebug() << "请老师吃饭";
}
创建老师、学生对象,connect连接信号槽
//Widget.h
#pragma once
#include
#include "ui_Widget.h"
//头文件
#include "teacher.h"
#include "student.h"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = Q_NULLPTR);
private:
Ui::WidgetClass ui;
//定义Teacher类、Student类的对象指针
Teacher* t;
Student* s;
//定义触发函数
void classIsOver();
};
创建老师学生对象并连接,设置下课函数,emit触发饿了信号
//Witget.c
#include "Widget.h"
//Teacher类
//Student类
//场景,下课后老师触发信号->饿了,学生响应->请客吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
//创建老师对象,学生对象
this->t = new Teacher(this);
this->s = new Student(this);
//连接:老师饿了,学生请客
connect(t, &Teacher::hungry, s, &Student::treat);
//调用下课
classIsOver();
}
void Widget::classIsOver()
{
//下课函数,调用后触发老师饿了的信号,emit触发
emit t->hungry();
}
运行后:
可以看到classIsOver函数被调用后,触发信号槽,学生执行qDebug,输出字符串。
对hungry()和treat()函数重载:带参数foodName
//teacher.h
void hungry(QString foodName);
//student.h
void treat(QString foodName);
//student.cpp
void Student::treat(QString foodName)
{
qDebug() << "请老师吃饭,老师要吃" << foodName;
}
//Widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent)
{
//指针->地址 函数指针->函数地址 !!!重载后,设置函数指针使connect区分原函数与重载函数!!!
//定义函数指针需要添加作用域
void(Teacher:: * teacherSignal)(QString) = &Teacher::hungry;
void(Student:: * studentSlot)(QString) = &Student::treat;
connect(t, teacherSignal, s, studentSlot);
classIsOver();
}
void Widget::classIsOver()
{
//下课函数,调用后触发老师重载后带参数的饿了信号,emit触发
emit t->hungry("宫保鸡丁");
}
运行后可以看到成功调用了重载函数,传输了参数foodName:
这里有一个问题,输出的foodName加了引号,将QString转化成cahr *类型可以去掉引号
//Qstring->char *,先转成QByteArray(.toUtf8),查看帮助文档QByteArray通过data()转成cahr*
qDebug() << "请老师吃饭,老师要吃" << foodName.toUtf8().data();
需求:添加一个按钮,点击按钮触发下课,触发老师饿了
//widget.cpp
//点击下课按钮,再触发下课
QPushButton* btn = new QPushButton("下课",this);
this -> resize(600, 400);
connect(btn, &QPushButton::clicked, this, &Widget::classIsOver);
继续尝试调用无参信号和槽,同时去除classIsOver,直接信号连接信号:下课按钮->hungry
//widget.cpp
//无参信号和槽
void(Teacher:: * teacherSignal2)(void) = &Teacher::hungry;
void(Student:: * studentSlot2)(void) = &Student::treat;
connect(t, teacherSignal2, s, studentSlot2);
//点击按钮,触发信号,信号连接信号
connect(btn, &QPushButton::clicked, t,teacherSignal2);
扩展:
分析信号和槽函数的参数必须类型一一对应:前文信号连接信号时间,将clicked信号与hungry(void)连接无异常,但将其与hungry(QString)连接报错,这是因为clicked的函数原型有一个bool型参数,只能与无参hungry连接,要与有参hungry连接,需要经过classIsOver()
void clicked(bool checked = false)
看不懂,得看一下C++再写,弹幕说可以简化代码结构,有些场景无需重新定义。
//widget.cpp
//用lamada表达式实现点击按钮关闭窗口,使用参槽函数无需调用classIsOver
QPushButton* btn2 = new QPushButton("关闭",this);
btn2->move(100, 0);
connect(btn2, &QPushButton::clicked, this, [=]() {
this->close();
emit t->hungry("宫保鸡丁");
两个按钮:open,close,点击open弹出一个窗口,点击close,关闭窗口。
//Widget.h
#pragma once
#include
#include "ui_Widget.h"
#include "mywindow.h"//头文件
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = Q_NULLPTR);
void open_new_win();//槽函数:打开窗口
void close_new_win();//槽函数:关闭窗口
private:
Ui::WidgetClass ui;
MyWindow *new_win;//声明Window类对象指针
};
//Widget.cpp
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include "Widget.h"
#include
#include "mywindow.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
QPushButton* btn1 = new QPushButton("open", this);
connect(btn1, &QPushButton::clicked, this, &Widget::open_new_win);//打开窗口
QPushButton* btn2 = new QPushButton("close", this);
btn2->move(100, 0);
connect(btn2, &QPushButton::clicked, this, &Widget::close_new_win);//关闭窗口
}
void Widget::open_new_win() {
this->new_win = new MyWindow();//构造函数,构造窗口
new_win->show();//显示窗口
}
void Widget::close_new_win() {
new_win->close();//关闭窗口
}
一个按钮:初始为open,点击open弹出窗口,变为close;点击close关闭窗口,变为open。
//Widget.cpp
QPushButton* btn1 = new QPushButton("open", this);
QPushButton* btn2 = new QPushButton("close", this);
btn2->hide();//初始状态btn2隐藏
connect(btn1, &QPushButton::clicked, this, [=]() {
open_new_win();
btn1->hide();
btn2->show();
});
connect(btn2, &QPushButton::clicked, this, [=]() {
close_new_win();
btn2->hide();
btn1->show();
});
QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个铆接部件(dock widgets)、一个状态栏(status bar)以及一个中心部件(central widgets),是许多应用程序的基础,如文本编辑器、图片编辑器等。
新建Qt项目,类型选择QMainWindow
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include "My_window.h"
#include
#include
#include
#include
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//重置窗口大小
resize(600, 400);
//--------------------菜单栏 最多只能有一个-----------------------------
//创建菜单栏
QMenuBar* bar = menuBar();
//菜单栏放入窗口中
setMenuBar(bar);
//创建菜单:QMenu接收
QMenu *fileMenu = bar->addMenu("文件");
QMenu* editMenu = bar->addMenu("编辑");
//创建菜单项:QAction接收
QAction *newAction = fileMenu->addAction("新建");
//添加分割线
fileMenu->addSeparator();
QAction *openAction = fileMenu->addAction("打开");
//---------------------工具栏 可以有多个-------------------------------
QToolBar* toolbar = new QToolBar(this);//挂在对象树上
//设置工具栏默认停靠区域
addToolBar(Qt::LeftToolBarArea,toolbar);
//后期设置:只允许左右停靠
toolbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
//设置浮动
toolbar->setFloatable(false);
//设置移动(总开关)
toolbar->setMovable(false);
//创建工具项,和菜单项共用QAction
toolbar->addAction(newAction);
//添加分割线
toolbar->addSeparator();
toolbar->addAction(openAction);
//工具栏中添加控件,如QPushButton
QPushButton* btn = new QPushButton("aa",this);
toolbar->addWidget(btn);
}
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include "My_window.h"
#include
#include
#include
#include
#include
#include
#include
#include
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
...
//----------------------------状态栏 最多有一个---------------------------
QStatusBar* stBar = statusBar();
//设置到窗口中
setStatusBar(stBar);
//添加标签控件
QLabel* label = new QLabel("提示信息", this);
stBar->addWidget(label);
QLabel* label2 = new QLabel("右侧提示信息", this);
stBar->addPermanentWidget(label2);
//----------------------铆接部件(浮动窗口) 可以有多个--------------------
QDockWidget* dockwidget = new QDockWidget("浮动",this);
addDockWidget(Qt::BottomDockWidgetArea, dockwidget);
//设置后期停靠区域:只允许上下停靠
dockwidget->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
//---------------------------中心部件 只有一个----------------------------
QTextEdit* edit = new QTextEdit(this);
setCentralWidget(edit);
}
只能有一个的——菜单栏、状态栏、核心部件:set
//菜单栏
QMenuBar* bar = menuBar();
setMenuBar(bar);
//状态栏
QStatusBar* stBar = statusBar();
setStatusBar(stBar);
//核心部件
QTextEdit* edit = new QTextEdit(this);
setCentralWidget(edit);
可以有多个——工具栏、铆接部件:add//工具栏 可以有多个
//工具栏
QToolBar* toolbar = new QToolBar(this);//挂在对象树上
addToolBar(Qt::LeftToolBarArea,toolbar);
//铆接部件(浮动窗口)
QDockWidget* dockwidget = new QDockWidget("浮动",this);
addDockWidget(Qt::BottomDockWidgetArea, dockwidget);
打开.ui进行界面设计,通过简单拖拽实现2.1中界面
保存后,返回VS界面,右键.ui文件点击编译,可以看到成功运行。
接下为设计添加资源文件:为QAction类型的新建添加icon图标
//My_window.cpp
ui.actionnew->setIcon(QIcon("C:/Users/lenovo/Desktop/Qt学习/test.png"));
编译运行可以看到成功添加了图标:
接下来需要将本地资源上传工程,才能实现其他用户正常读取。
工程下新建Image文件夹->将图标文件导入->点击工程目录.qrc->Add添加文件->添加后双击图片查看Resource URL->推出后编译.qrc,此时成功导入。Qt添加资源格式: ":+前缀名+文件名"
//My_window.cpp
//使用Qt添加资源 ":+前缀名+文件名"
ui.actionnew->setIcon(QIcon(":/My_window/Image/test.PNG"));
ui.actionfile->setIcon(QIcon(":/My_window/Image/test.PNG"));
//My_window.cpp
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//点击新建按钮弹出对话框
connect(ui.actionnew, &QAction::triggered, [=]() {
//对话框有两种分类:模态、非模态
//模态:不可以对其他窗口操作
//非模态:可以对其他窗口操作
//模态创建
/*QDialog dlg(this);
dlg.resize(400, 200);
dlg.exec();//阻塞
qDebug() << "模态对话框弹出来";*/
//非模态创建
QDialog *dlg2 = new QDialog(this);
dlg2->resize(400, 200);
dlg2->show();
dlg2->setAttribute(Qt::WA_DeleteOnClose);//55号属性 关闭即释放对象
qDebug() << "非模态对话框弹出来";
});
}
分类:
注意:
Qt内置对话框:选择颜色(QCloloeDialog)、选择文件目录(QFileDialog)、选择字体(QFontDialog)、用户输入值返回(QInputDialog)、模态对话框(QMessageBox)等。
静态成员函数的访问:通过类名直接调用、创建对象访问。
Qt助手搜索QMessageBox,查看静态成员函数Static Public Members,通过静态成员函数创建对话框。
//My_window.cpp 消息对话框QMessageBox
#include
...
//错误对话框
QMessageBox::critical(this, "critial", "错误");
//信息对话框
QMessageBox::information(this, "info", "信息");
//问题对话框
//参数4:选项类型,跟进question->StandardButtons查看可选参数 参数5:默认关联回车的按键
if (QMessageBox::Save == QMessageBox::question(this, "ques", "提问", QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Cancel)) {
qDebug() << "选择的是保存";
}
else {
qDebug() << "选择的是取消";
}
//警告对话框
QMessageBox::warning(this, "warning", "警告");
颜色对话框
//颜色对话框
QColor color = QColorDialog::getColor(QColor(255, 0, 0), this, "color");
qDebug() << "r = " << color.red() << "g = " << color.green() << "b = " << color.blue();
//文件对话框
QString str = QFileDialog::getOpenFileName(this, "打开文件", "E:\Qtprojcets\code\02My_window", "(*.txt)");
qDebug() << "目标路径: " << str;
//字体对话框
bool flag;
QFont font = QFontDialog::getFont(&flag, QFont("宋体", 36), this, "字体选择");
qDebug() << "字体:" << font.family() << " 字号:" << font.pointSize() << " 是否加粗:" << font.bold() << " 是否倾斜:" << font.italic();
用户名:Label;输入框:Line Edit;登录/取消:Push Button
对界面布局
水平对齐:
垂直布局:
Horizontal Spacer:水平弹簧,在登录左侧,取消右侧,登录/取消之间添加弹簧
打破布局,删除弹簧和Widget
添加Widget,将要素拖拽进去,选择上方栅格布局
对整个MainWidget窗口垂直布局
在用户名/密码左右两侧添加弹簧
去除Widget上下边缘间隙,选择Widget,sizePolicy,垂直策略->fixed
去除Wigdget窗口与弹簧间隙,选择Widget,Layout,layoutLeftMaegin->0
密码输入框不显示:echoMode->password
主窗口大小:设置不可拖拽
//My_window.cpp
//单选按钮
//设置默认值
ui.radioButton->setChecked(true);
//获取选择结果,选中后触发信息,可以使用一个bool值接收选择
connect(ui.radioButton, &QRadioButton::clicked, [=]() {
qDebug() << "选择radioButton1";});
//多选按钮,选中state=2,未选中state=0,半选state=1
connect(ui.checkBox_3, &QCheckBox::stateChanged, [=](int state) {
qDebug() << state;});
//My_window.cpp
//利用listWidget写诗,listWidget每行都是一个QListWidgetItem
QListWidgetItem* item = new QListWidgetItem("锄禾日当午");
ui.listWidget->addItem(item);
//设置水平居中
item->setTextAlignment(Qt::AlignHCenter);
//QStringList QList
QStringList list;
list << "锄禾日当午" << "汗滴禾下土" << "谁知盘中餐" << "粒粒皆辛苦";
ui.listWidget->addItems(list);
设置头:ui.treeWidget->setHeaderLabels(QStringList()<<“英雄”<<“英雄介绍”);
设置根:QTreeWidgetItem* item1 = new QTreeWidgetItem(QStringList() << “力量”);
添加根到树控件:ui.treeWidget->addTopLevelItem(item1);
//My_window.cpp Tree Widget控件使用
//设置头
ui.treeWidget->setHeaderLabels(QStringList()<<"英雄"<<"英雄介绍");
//设置根节点
QTreeWidgetItem* item1 = new QTreeWidgetItem(QStringList() << "力量");
QTreeWidgetItem* item2 = new QTreeWidgetItem(QStringList() << "敏捷");
QTreeWidgetItem* item3 = new QTreeWidgetItem(QStringList() << "智力");
//加载顶层节点
ui.treeWidget->addTopLevelItem(item1);
ui.treeWidget->addTopLevelItem(item2);
ui.treeWidget->addTopLevelItem(item3);
//为根节点追加子节点
QTreeWidgetItem* item11 = new QTreeWidgetItem(QStringList()<< "猪八戒" << "前排坦克,能在吸收伤害的同时造成可观的范围输出");
item1->addChild(item11);
//My_window.cpp Table Widget控件使用
//设置列数
ui.tableWidget->setColumnCount(3);
//设置水平表头
ui.tableWidget->setHorizontalHeaderLabels(QStringList() << "姓名" << "性别" << "年龄");
//设置行数
ui.tableWidget->setRowCount(5);
//设置正文,0行0列
ui.tableWidget->setItem(0, 0, new QTableWidgetItem("亚瑟"));
QStringList nameList;
nameList << "亚瑟" << "赵云" << "张飞" << "曹操" << "妲己";
QList sexList;
sexList << "男" << "男" << "男" << "男" << "女";
for (int i = 0; i < 5; i++) {
int col = 0;
ui.tableWidget->setItem(i, col++, new QTableWidgetItem(nameList[i]));
ui.tableWidget->setItem(i, col++, new QTableWidgetItem(sexList.at(i)));
//int转QString
ui.tableWidget->setItem(i, col++, new QTableWidgetItem(QString::number(i+18)));
}
GroupBox:分组,单选多选Button时使用
Scroll Area:滚动区域
Tool Box:类似QQ好友分组列表
Tab Widget:类似浏览器网页切换
Stacked Widget:窗口切换,需要通过PushButton设置信号槽切换,ui.stackedWidget->setCurrentIndex(0);
Widget:设置对齐
DockWidget:浮动窗口
ComboBox:下拉框,ui.comboBox->addItems();
Line Edit:单行输入框,可修改输入模式(密码)
Text Edit:可编辑的文本框,类似记事本
Label:标签,可显示文本/图片/动图,ui.label->setPixmap(“图片”);
显示动图:QMovie* movie = new QMovie(“动图”);ui.label_2->setMovie(movie);movie->start();
//My_window.cpp 其他控件
//栈控件使用
//设置默认到Scroll Area
ui.stackedWidget->setCurrentIndex(0);
//Scroll Area按钮
connect(ui.btn_scrollArea, &QPushButton::clicked, [=]() {
ui.stackedWidget->setCurrentIndex(0);
});
//Tool Box按钮
connect(ui.btn_toolBox, &QPushButton::clicked, [=]() {
ui.stackedWidget->setCurrentIndex(2);
});
//Tab Widget按钮
connect(ui.btn_tabWidget, &QPushButton::clicked, [=]() {
ui.stackedWidget->setCurrentIndex(1);
});
//comnoBox下拉菜单
ui.comboBox->addItems(QStringList() << "奔驰" << "宝马" << "拖拉机");
//点击按钮选中拖拉机
connect(ui.btn_select, &QPushButton::clicked, [=]() {
ui.comboBox->setCurrentIndex(2);
//ui.comboBox->setCurrentText("拖拉机");
});
//QLabel标签
//利用QLabel显示图片
ui.label->setPixmap(QPixmap(":/My_window/Image/test.PNG"));
//利用QLabel显示gif动图
QMovie* movie = new QMovie(":/My_window/Image/test.PNG");
ui.label_2->setMovie(movie);
//播放动图
movie->start();
封装Spin Box和Horizontal Slider控件
添加设计器界面类:添加->add Qt Class->Qt Widgets Class->命名smallwidget即可。
//smallwidget.cpp
//QSpinBox移动 QSlider跟着移动
void(QSpinBox::* spSignal)(int) = &QSpinBox::valueChanged;//发生重载定义函数指针
connect(ui.spinBox, spSignal, ui.horizontalSlider, &QSlider::setValue);
//QSlider滑动 QSpingBox数字跟着改变
connect(ui.horizontalSlider, &QSlider::valueChanged, ui.spinBox, &QSpinBox::setValue);
新增两个button:获取当前值、设置到一半
//smallwidget.h
//设置数字
void setNum(int num);
//获取数字
int getNum();
//smallwidget.cpp 函数实现
void smallwidget::setNum(int num){
ui.spinBox->setValue(num);
}
int smallwidget::getNum(){
return ui.spinBox->value();
}
//My_widget.cpp
//点击获取当前值 获取控件当前值
connect(ui.btn_get, &QPushButton::clicked, [=]() {
qDebug() << ui.widget->getNum();
});
//点击设置到一半 设置到一般
connect(ui.btn_set, &QPushButton::clicked, [=]() {
ui.widget->setNum(50);
});
运行效果如下:点击设置到一般均变为50,点击获取当前值打印当前值。
!!!问题:运行报错无法打开包含文件"smallwidget.h"
!!!解决方案:双击错误跳转到ui_My_window.h,修改头文件,将尖括号改为双引号
//#include
#include "smallwidget.h"
右键.ui文件->打开方式->添加->路径为Qt安装路径下的bin/designer.exe->自定义名称->设为默认值
[virtual protected] void QWidget::enterEvent(QEvent *event)//鼠标进入事件
[virtual protected] void QWidget::leaveEvent(QEvent *event)//鼠标离开事件
新建类myLabel,无需界面选择Qt Class即可,设置基类为QWedget
//mylabel.h
//鼠标进入事件
void enterEvent(QEvent* event);
//鼠标离开事件
void leaveEvent(QEvent* event);
//mylabel.cpp
//鼠标进入事件
void myLabel::enterEvent(QEvent* event) {
qDebug() << "鼠标进入了";
}
//鼠标离开事件
void myLabel::leaveEvent(QEvent* event) {
qDebug() << "鼠标离开了";
}
因为要将QLabel升级为myLabel,二者类型需要一致,因此修改myLabel中的QWidget为QLabel(三处)
//mylabel.h
#include
class myLabel : public QLabel
//mylabel.cpp
myLabel::myLabel(QWidget *parent) : QLabel(parent)
升级QLabel为mylabel,方法与3.1中相同。
QLabel中的其他鼠标事件:帮助QLabel->Reimplemented Protected Functions(重新实现保护函数)
//QLabel->Reimplemented Protected Functions中与鼠标相关的事件
virtual void mouseMoveEvent(QMouseEvent *ev)
virtual void mousePressEvent(QMouseEvent *ev)
virtual void mouseReleaseEvent(QMouseEvent *ev)
在头文件中声明:
//mylabel.h
//鼠标按下事件
void mousePressEvent(QMouseEvent* event);
//鼠标释放事件
void mouseReleaseEvent(QMouseEvent* event);
//鼠标移动事件
void mouseMoveEvent(QMouseEvent* event);
在.cpp文件中实现:
判断按下的鼠标键为左/右键:if (event->button() == Qt::LeftButton)
下示代码鼠标移动事件move需要点击鼠标键才能开始捕获,若需直接捕获,在构造函数中声明
//mylabel.cpp 设置鼠标追踪
setMouseTracking(true);
move比较特殊:if (event->buttons() & Qt::LeftButton)
坐标:event->x(),event->y() 全局坐标:event->global_X(),event->global_Y()
Qt格式化字符串输出:QString(“x=%1 y=%2”).arg(1),arg(2);
//mylabel.cpp
//鼠标按下事件
void myLabel::mousePressEvent(QMouseEvent* event) {
//鼠标左键按下时,输出
if (event->button() == Qt::LeftButton) {
QString str = QString("鼠标按下了 x = %1 y=%2 globle_x=%3 globle_y=%4").arg(event->x()).arg(event->y()).arg(event->globalX()).arg(event->globalY());
qDebug() << str;
}
}
//鼠标释放事件
void myLabel::mouseReleaseEvent(QMouseEvent* event) {
//鼠标左键按下时,输出
if (event->button() == Qt::LeftButton) {
QString str = QString("鼠标释放了 x = %1 y=%2 globle_x=%3 globle_y=%4").arg(event->x()).arg(event->y()).arg(event->globalX()).arg(event->globalY());
qDebug() << str;
}
}
//鼠标移动事件
void myLabel::mouseMoveEvent(QMouseEvent* event) {
//鼠标左键按下时,输出
if (event->buttons() & Qt::LeftButton) {
QString str = QString("鼠标移动了 x = %1 y=%2 globle_x=%3 globle_y=%4").arg(event->x()).arg(event->y()).arg(event->globalX()).arg(event->globalY());
qDebug() << str;
}
}
定时器事件:
[virtual protected] void QTimer::timerEvent(QTimerEvent *e)
实现两个文本框分别计时,一个间隔1s计数,另一个间隔2s计数。
//My_window.h
//重写定时器事件
void timerEvent(QTimerEvent* e);
int id1;//定时器1的唯一标识
int id2;//定时器2的唯一标识
//My_window.cpp
#include "My_window.h"
#include
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//启动定时器 参数1:间隔 单位ms
id1 = startTimer(1000);
id2 = startTimer(2000);
}
void My_window::timerEvent(QTimerEvent* e) {
if (e->timerId() == id1) {
static int num = 1;
//label_2每个1s加1
ui.label_2->setText(QString::number(num++));
}
if (e->timerId() == id2) {
//label_3每隔2s加1
static int num2 = 1;
ui.label_3->setText(QString::number(num2++));
}
}
实现定时器间隔0.5s计数,点击btn计数停止,再次点击恢复计数。
//定时器第二种实现方式
QTimer* timer = new QTimer(this);
//启动定时器
timer->start(500);
connect(timer, &QTimer::timeout, [=]() {
static int num3 = 1;
ui.label_4->setText(QString::number(num3++));
});
//点击btn停止定时,再次点击恢复
connect(ui.btn, &QPushButton::clicked, [=]() {
if (timer->isActive())timer->stop();
else timer->start();
});
//事件分发器
//返回值为true时,代表用户要处理这个事件,不向下分发,起到了过滤作用
[virtual] bool QObject::event(QEvent *e)
拦截鼠标按下事件
//通过事件分发器拦截鼠标按下事件
//mylabel.h
bool event(QEvent* e);
//mylabel.cpp
bool myLabel::event(QEvent* e) {
//如果鼠标按下,在event事件分发中做拦截操作
if (e->type() == QEvent::MouseButtonPress) {
//将event从QEvent*转为QMouseEvent*
QMouseEvent* event = static_cast(e);
QString str = QString("Events函数中:鼠标按下了 x = %1 y=%2 globle_x=%3 globle_y=%4").arg(event->x()).arg(event->y()).arg(event->globalX()).arg(event->globalY());
qDebug() << str;
return true;//trued代表用户自行处理这个事件,不向下分发
}
else return QLabel::event(e);
}
效果如下:可以看到成功拦截了鼠标按下事件,其他事件正常分发。
在程序将事件分发到事件分发器前,可以利用过滤器拦截
实现步骤1:给控件安装事件过滤器ui.label->installEventFilter(this);
实现步骤2:重写事件过滤器事件bool eventFilter(QObject* obj, QEvent* e);
重写eventfilter时:obj判断控件类型,event.type()判断控件操作类型
//事件过滤器:可以在程序分发到event事件之前再进行一次高级拦截
//使用:1、给控件安装事件过滤器;2、重写eventfilter事件
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
过滤鼠标按下事件:
//通过事件过滤器过滤鼠标按下事件
//My_window.h
//步骤2:重写事件过滤器事件
bool eventFilter(QObject* obj, QEvent* e);
//My_window.cpp
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//步骤1:给label1安装事件过滤器
ui.label->installEventFilter(this);
}
//步骤2:重写eventfilter事件
bool My_window::eventFilter(QObject* obj, QEvent* e) {
if (obj == ui.label) {
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent* event = static_cast(e);
QString str = QString("事件过滤器中:鼠标按下了 x = %1 y=%2 globle_x=%3 globle_y=%4").arg(event->x()).arg(event->y()).arg(event->globalX()).arg(event->globalY());
qDebug() << str;
return true;
}
else return false;
}
else {
return QWidget::eventFilter(obj, e);
}
}
效果如下:可以看到成功过滤了鼠标按下事件,其他事件正常分发。
//My_window.h
//绘图事件
void paintEvent(QPaintEvent*);
//My_window.cpp
#include //画家类
void My_window::paintEvent(QPaintEvent*) {
//实例化画家对象 this->指定绘图设备
QPainter painter(this);
//设置画笔
QPen pen(QColor(255, 0, 0));
//设置画笔宽度,默认1
pen.setWidth(5);
//设置画笔风格,默认:Qt::SolidLine
pen.setStyle(Qt::DotLine);
//画家使用画笔
painter.setPen(pen);
//设置画刷:封闭图形填充颜色
QBrush brush(Qt::cyan);
//设置画刷风格,默认:Qt::SolidPattern
brush.setStyle(Qt::Dense7Pattern);
//让画家使用画刷
painter.setBrush(brush);
//设置画刷风格,默认:Qt::SolidPattern
//画直线
painter.drawLine(QPoint(0, 0), QPoint(100, 100));
//画(椭)圆
painter.drawEllipse(QPoint(100, 100), 100, 50);
//画矩形
painter.drawRect(QRect(20, 20, 50, 50));
//画文字
painter.drawText(QRect(10, 200, 200, 100),"好好学习天天向上");
}
//My_window.cpp
void My_window::paintEvent(QPaintEvent*) {
//实例化画家对象 this->指定绘图设备
QPainter painter(this);
painter.drawEllipse(QPoint(100, 100), 50, 50);
//设置 抗锯齿能力 效率较低
painter.setRenderHint(QPainter::Antialiasing);
painter.drawEllipse(QPoint(250, 100), 50, 50);
}
//My_window.cpp
void My_window::paintEvent(QPaintEvent*) {
//实例化画家对象 this->指定绘图设备
QPainter painter(this);
painter.drawRect(QRect(20,20,50,50));
//移动画家1
painter.translate(100, 0);
//保存画家状态
painter.save();
painter.drawRect(QRect(20, 20, 50, 50));
//移动画家2
painter.translate(100, 0);
//还原画家状态
painter.restore();
painter.drawRect(QRect(20, 20, 50, 50));
}
效果如下:上示代码绘制了三个矩形,第一次移动画家,使得第一二矩形不重合,第二次移动后还原画家状态,导致第三个矩形和第二个矩形重合。
利用画家绘制图片,设置按钮,点击按钮图片右移,移动到窗口最右侧后返回
//My_window.h
int pos_X = 0;
//My_window.cpp
#include //画家类
#include
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//点击移动按钮,移动图片
connect(ui.pushButton, &QPushButton::clicked, [=]() {
//如果要手动调用绘图时间,用updata更新
update();
});
}
void My_window::paintEvent(QPaintEvent*) {
//实例化画家对象 this->指定绘图设备
QPainter painter(this);
if (pos_X > this->width()) {
pos_X = 0;
}
pos_X += 3;
painter.drawPixmap(pos_X, 0, QPixmap(":/My_window/Image/test.PNG"));
}
//My_window.cpp
#include //画家类
#include
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
QTimer* timer = new QTimer(this);
timer->start(500);
connect(timer, &QTimer::timeout, [=]() {
update();
});
}
void My_window::paintEvent(QPaintEvent*) {
//实例化画家对象 this->指定绘图设备
QPainter painter(this);
if (pos_X > this->width()) {
pos_X = 0;
}
pos_X += 20;
painter.drawPixmap(pos_X, 0, QPixmap(":/My_window/Image/test.PNG"));
}
Pixmap绘图设备:专门为平台做了显示优化
//My_window.cpp
QPixmap pix(300, 300);
//填充颜色
pix.fill(Qt::white);
//声明画家
QPainter painter(&pix);
painter.setPen(QPen(Qt::green));
painter.drawEllipse(QPoint(150, 150), 100, 100);
//保存
pix.save("E:\\pix.png");
QImage绘图设备:可以对像素进行访问
//My_window.cpp
QImage img(300, 300, QImage::Format_RGB32);
img.fill(Qt::white);
QPainter painter(&img);
painter.setPen(QPen(Qt::blue));
painter.drawEllipse(QPoint(150, 150), 100, 100);
img.save("E:\\img.png");
//My_window.h
//public声明绘图事件
void paintEvent(QPaintEvent *);
//My_window.cpp
void My_window::paintEvent(QPaintEvent*) {
QPainter painter(this);
//利用QImage对像素进行修改
QImage img;
img.load(":/My_window/Image/test.PNG");
//修改像素点
for (int i = 50; i < 100; i++) {
for (int j = 50; j < 100; j++) {
QRgb value = qRgb(255, 0, 0);
img.setPixel(i, j, value);
}
}
painter.drawImage(0, 0, img);
}
//My_window.cpp
QPicture pic;
QPainter painter;
painter.begin(&pic);//开始在pic上画图
painter.setPen(QPen(Qt::cyan));
painter.drawEllipse(QPoint(150, 150), 100, 100);
painter.end();//结束画图
//记录绘图指令,后缀名可自定义
pic.save("E:\\pic.txt");
void My_window::paintEvent(QPaintEvent*) {
//重现绘图指令
QPicture pic;
pic.load("E:\\pic.txt");
QPainter painter(this);
painter.drawPicture(0, 0, pic);
}
QFile进行文件读写操作,文件路径:QFile file(path);
设置打开方式,只读:file.open(QIODevice::ReadOnly);
全部读取:file.readAll(); 按行读:file.readLine();判断是否读到尾file.atEnd()
指定编码格式(默认utf-8):
QTextCodec* codec = QTextCodec::codecForName(“utf-8”);
ui.textEdit->setText(codec->toUnicode(array));
关闭文件:file.close();
追加方式写:file.open(QIODevice::Append);file.write("");
点击pushButton弹出文件选择对话框,将文件路径放在lineEdit中,文本内容放在textEdit中。
//My_window.cpp
#include "My_window.h"
#include
#include
#include
#include
#include
My_window::My_window(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//点击文件选择按钮
connect(ui.pushButton, &QPushButton::clicked, [=]() {
//弹出对话框
QString path = QFileDialog::getOpenFileName(this, "选择文件", "E:/QFile");
//将路径放在lineEdit中
ui.lineEdit->setText(path);
//编码格式类:用来对读取内容类型转化
QTextCodec* codec = QTextCodec::codecForName("utf-8");
//读取内容放在textEdit中
QFile file(path);//参数即为读取的文件的路径
//设置打开方式:只读,默认格式:utf-8
file.open(QIODevice::ReadOnly);
QByteArray array;
while (!file.atEnd()){
array += file.readLine();//按行读
}
//array = file.readAll();//全部读
ui.textEdit->setText(codec->toUnicode(array));
file.close();
//写文件
file.open(QIODevice::Append);//以追加的方式写
file.write("aaaaaaaaaaa");
file.close();
});
}
效果如下:
查看读取的文件:可以看到文件末尾被添加了预设内容
//My_window.cpp
#include
#include
QFileInfo info(path);
qDebug() << "文件大小:" << info.size() << "文件后缀名:" << info.suffix() << "文件名称:" << info.fileName() << "文件路径:" << info.filePath();
qDebug() << "创建日期:" << info.created().toString("yyyy/MM/dd hh:mm:ss");
qDebug() << "最后修改日期:" << info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
2022-1-19到2022-1-29,比较认真看的时间应该有三四天,课程入门友好型,内容比较基础。
纪念一下第一次看完B站的视频教程,后面考虑完成一下课程后附加的项目实现和额外的一两个项目实现。
(详细记录视频的进度可以有效提高效率,减少摸鱼