前两篇博客简单的阐述了一下Qt的入门用法,这篇博客继续跟着视频学习。
Qt入门系列:
Qt学习之C++基础
Qt学习之Qt安装
Qt学习之Qt基础入门(上)
Qt学习之Qt基础入门(中)
Qt学习之Qt基础入门(下)
本文原创,创作不易,转载请注明!!!
本文链接
个人博客: https://ronglin.fun/archives/222
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/120668605
有时候,我们希望一个控件是我们自己的设计的,比如将一个Label和进度条结合起来作为一个整体来使用
我们将一个Widget面板中加入自己设计好的控件,然后将面板作为 一个整体插入总ui中。
一个自定义功能:
设计两个按钮,为 开启–关闭 互补 状态,当点击开始按钮时,关闭按钮可用,开始按钮不可点击,当点击关闭按钮时,开启按钮可用,关闭按钮不可点击
然后就是选择基类,因为我们是在Widget面板上进行自定义,所以就选择Widget
然后就是自定义的类,取名叫做SwitchButtonWidget
然后下一步,完成就行了
之后会生成三个文件,一个是ui文件,然后就是类的.h .cpp文件。
我们先在ui界面设计控件,我这里就是两个按键,然后用了垂直布局,同时调整Widget面板大小
然后就在类中设计其逻辑功能,当点击开始按钮时,关闭按钮变成可用,开始按钮变得不可点击,当点击关闭按钮时,状态相反。
在文件switchbuttonwidget.cpp
中
#include "switchbuttonwidget.h"
#include "ui_switchbuttonwidget.h"
SwitchButtonWidget::SwitchButtonWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::SwitchButtonWidget)
{
ui->setupUi(this);
ui->pushButton_2->setEnabled(false); //关闭按钮默认不可用
//当点击开始按钮的时候
connect(ui->pushButton,&QPushButton::clicked,[=](){
ui->pushButton->setEnabled(false); //关闭按钮设置可用
ui->pushButton_2->setEnabled(true); //开始按钮设置不可用
});
//当点击关闭按钮的时候
connect(ui->pushButton_2,&QPushButton::clicked,[=](){
ui->pushButton->setEnabled(true); //关闭按钮设置不可用
ui->pushButton_2->setEnabled(false); //开始按钮设置可用
});
}
SwitchButtonWidget::~SwitchButtonWidget()
{
delete ui;
}
到此,自定义的控件设计完毕
设计完毕,就要在主ui中应用控件
因为我们的自定义控件的基类是Widget,所以我们在主ui中,拖入一个Widget面板,作为我们自定义框架的区域
然后 右键 -> 提升为
然后我们将这个普通的widget提升为我们自己自定义的控件面板,所以说提升的类名称就是刚才定义好的SwitchButtonWidget
类,这里注意大小写,.h文件全是小写的,而类名是大小写都有的,然后全局包含,全局包含勾选的话就是以后用到这个自定义控件的时候可以直接使用,因为我们这里只用一次,就不勾选了
然后,点击 添加 ,然后点击 提升
之后虽然在ui中看不出来,但是在右侧,widget类已经变成SwitchButtonWidget
类了,这就说明已经提升成功,运行查看结果
效果和预期一样,应用成功,只有一点不太方便的是,当提升之后,主ui中只是显示一个框框,不显示自定义控件的内容
事件和Java中类似,当用户对程序互交的时候,就会产生很多事件,例如点击鼠标,键盘输入,改变窗口大小等等
鼠标进入事件 enterEvent
鼠标离开事件 leaveEvent
鼠标按下 mousePressEvent ( QMouseEvent event)
鼠标释放 mouseReleaseEvent
鼠标移动 mouseMoveEvent
设置鼠标追踪方法:setMouseTracking(true);
同时在事件中封装了事件的状态,比如获取鼠标移动的坐标
event->x(); //x坐标
event->y(); //y坐标
而且也可以判断左右键,比如
if(event->button() == Qt::LeftButton)
{
qDebug() << "点击了鼠标左键";
}
else if(event->button() == Qt::RightButton)
{
qDebug() << "点击了鼠标右键";
}
同时,还有一个buttons方法可以同时判断多个,用法稍有不同
if(event->buttons() & Qt::LeftButton )
{
qDebug() << "点击了鼠标左键";
}
可以用event->buttons()
判断组合按键 判断move时候的左右键,要结合 & 操作符
小tips:
格式化字符串用法:QString( "%1 %2 ").arg( 111 ).arg(222);
在一些常用事件的上边还有一个层event事件,实现事件分发,返回值是bool,如果返回是true就代表用户要处理这个事件,不在向下分发。
函数原型是
bool event( QEvent * ev);
主要用于事件的分发,也可以做拦截操作,但是不建议,用法如下:
bool MyWidget::event(QEvent *e)
{
//如果是鼠标按下 ,在event事件分发中做拦截操作
if(e->type() == QEvent::MouseButtonPress)
{
QMouseEvent * ev = static_cast(e);
QString str = QString( "Event函数中::鼠标按下了 x = %1 y = %2 globalX = %3 globalY = %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
return true; //true代表用户自己处理这个事件,不向下分发
}
//其他事件 交给父类处理 默认处理
return QLabel::event(e);
}
事件过滤器
同时在event之上还有一个事件过滤器,也可以做拦截,要重写eventfilter
函数
在程序将时间分发到事件分发器前,可以利用过滤器做拦截
步骤
1、给控件安装事件过滤器
ui->label->installEventFilter(this);
2、重写 eventFilter函数 (obj , ev)
bool MyWidget::eventFilter(QObject * obj , QEvent * e)
{
if(obj == ui->label)
{
if(e->type() == QEvent::MouseButtonPress)
{
QMouseEvent * ev = static_cast(e);
QString str = QString( "事件过滤器中::鼠标按下了 x = %1 y = %2 globalX = %3 globalY = %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
return true; //true代表用户自己处理这个事件,不向下分发
}
}
//其他默认处理
return QWidget::eventFilter(obj,e);
}
定时器是一个很常用的用法,比如3s后关闭窗口等等
定时器有两种用法
3.1 利用事件 void timerEvent ( QTimerEvent * ev)
3.2 启动定时器 startTimer( 1000) 毫秒单位
3.3 timerEvent 的返回值是定时器的唯一标示 可以和ev->timerId 做比较
用法如下:
在.h文件的public下,先定义要重写定时器的事件,然后如果要有多个定时器,也要指定标识,例如这里有两个
public:
explicit Widget(QWidget *parent = 0);
~Widget();
//重写定时器的事件
void timerEvent(QTimerEvent *);
int id1; //定时器1的唯一标示
int id2; //定时器2的唯一标示
然后在.cpp文件中,先初始化定时器标识,比如是要1s还是2s,然后在timerEvent
中判断即可
#include "widget.h"
#include "ui_widget.h"
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//启动定时器
id1 = startTimer(1000); //参数1 间隔 单位 毫秒
id2 = startTimer(2000);
}
void Widget::timerEvent(QTimerEvent * ev)
{
if(ev->timerId() == id1)
{
static int num = 1;
//label2 每隔1秒+1
ui->label_2->setText( QString::number(num++));
}
if(ev->timerId() == id2)
{
//label3 每隔2秒 +1
static int num2 = 1;
ui->label_3->setText( QString::number(num2++));
}
}
Widget::~Widget()
{
delete ui;
}
只在一个事件中写多个定时器实在不方便,所以Qt提供了一个QTimer定时器类
利用定时器类 QTimer
创建定时器对象 QTimer * timer = new QTimer(this)
启动定时器 timer->start(毫秒)
每隔一定毫秒,发送信号 timeout ,进行监听
暂停 timer->stop
用法如下,用connect函数,当定时器到时间时要执行的操作
//定时器第二种方式
QTimer * timer = new QTimer(this);
//启动定时器
timer->start(500);
connect(timer,&QTimer::timeout,[=](){
static int num = 1;
//label4 每隔0.5秒+1
ui->label_4->setText(QString::number(num++));
});
//点击暂停按钮 实现停止定时器
connect(ui->btn,&QPushButton::clicked,[=](){
timer->stop();
});
绘图事件 void paintEvent()
声明一个画家对象 QPainter painter(this) this指定绘图设备
可以画线、画圆、画矩形、画文字
设置画笔 QPen 设置画笔宽度 、风格,设置画刷 QBrush 设置画刷 风格
直接看代码用法,需要注意的时,如果需要重画的时候,调用update();
即可,就能重画paintEvent
内的内容
有关具体 用法可查询API
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象 this指定的是绘图设备
QPainter painter(this);
//设置画笔
QPen pen(QColor(255,0,0));
//设置画笔宽度
pen.setWidth(3);
//设置画笔风格
pen.setStyle(Qt::DotLine);
//让画家 使用这个笔
painter.setPen(pen);
//设置画刷
QBrush brush(Qt::cyan);
//设置画刷风格
brush.setStyle(Qt::Dense7Pattern);
//让画家使用画刷
painter.setBrush(brush);
painter.drawLine(QPoint(0,0) , QPoint(100,100)); //画线
painter.drawEllipse( QPoint(100,100) , 50,50); //画圆 椭圆
painter.drawRect(QRect(20,20,50,50)); //画矩形
painter.drawText(QRect(10,200,150,50) , "好好学习,天天向上"); //画文字
}
抗锯齿 效率低
painter.setRenderHint(QPainter::Antialiasing);
对画家进行移动
painter.translate(100,0);
保存状态 save
还原状态 restore
如果想手动调用绘图事件 利用update
利用画家画图片 painter.drawPixmap( x,y,QPixmap( 路飞) )
用法见下,不再仔细说明
void Widget::paintEvent(QPaintEvent *)
{
//高级设置 ///
QPainter painter(this);
painter.drawEllipse(QPoint(100,50) , 50,50);
//设置 抗锯齿能力 效率较低
painter.setRenderHint(QPainter::Antialiasing);
painter.drawEllipse(QPoint(200,50) , 50,50);
画矩形
painter.drawRect(QRect(20,20,50,50));
//移动画家
painter.translate(100,0);
//保存画家状态
painter.save();
painter.drawRect(QRect(20,20,50,50));
painter.translate(100,0);
//还原画家保存状态
painter.restore();
painter.drawRect(QRect(20,20,50,50));
/利用画家 画资源图片 ///
// QPainter painter(this);
// QPixmap pix = QPixmap(":/Image/Luffy.png");
// //如果超出屏幕 从0开始
// if(posX >= this->width())
// {
// posX = -pix.width();
// }
// painter.drawPixmap(posX,0,pix);
}
QPixmap QImage QBitmap(黑白色) QPicture QWidget
有关绘图设备这里不过多说明,API中有详细的说明
QPixmap 对不同平台做了显示的优化
QPixmap pix( 300,300) pix.fill( 填充颜色 )
利用画家 往pix上画画 QPainter painter( & pix)
保存 pix.save( “路径”)
Qimage 可以对像素进行访问
使用和QPixmap差不多 QImage img(300,300,QImage::Format_RGB32);
其他流程和QPixmap一样
可以对像素进行修改 img.setPixel(i,j,value);
QPicture 记录和重现 绘图指令
QPicture pic
painter.begin(&pic);
保存 pic.save( 任意后缀名 )
重现 利用画家可以重现painter.drawPicture(0,0,pic);
文件操作十分常见了,Qt的文件操作和C++的类似,十分简便,不像Java文件操作复杂
对文件进行读写操作
QFile进行读写操作
QFile file( path 文件路径)
file.open(打开方式) QIODevice::readOnly
全部读取 file.readAll() 按行读 file.readLine() atend()判断是否读到文件尾
认支持编码格式 utf-8
利用编码格式类 指定格式 QTextCodeC
QTextCodec * codec = QTextCodec::codecForName(“gbk”);
//ui->textEdit->setText( codec->toUnicode(array) );
文件对象关闭 close
file.open( QIODevice::writeOnly / Append)
file.write(内容)
file.close 关闭
读取文件信息 用法 至此,Qt基础入门结束,跟着视频学也挺好的,=w=
QFileInfo info(路径)
qDebug() << “大小:” << info.size() << " 后缀名:" << info.suffix() << " 文件名称:"<
qDebug() << “最后修改日期:”<#include "widget.h"
#include "ui_widget.h"
#include
7. 总结