2-事件与绘画
1、事件
1.1 简介
- QT程序是事件驱动的,程序的每个动作都是由内部某个事件所触发。
- QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期。
常见的QT事件类型如下: - 定时器事件、键盘事件、鼠标事件、绘图事件、
- 拖放事件、滚轮事件、焦点事件、进入和离开事件
- 移动事件、大小改变事件、显示和隐藏事件、窗口事件
QT将系统产生的消息转化为QT事件,QT事件被封装为对象,所有的QT事件均继承抽象类QEvent,用于描述程序内部或外部发生的动作,任意的QObject对象都具备处理QT事件的能力。
1.2 事件的产生
- 操作系统事件
- QT事件产生后会被立即发送到相应的QWidget对象
- 相应的QWidget中的event(QEvent*)进行事件处理
- event(QEvent*)根据事件类型调用不同的事件处理函数
- 在事件处理函数中发送QT预定义的信号
- 调用信号关联的槽函数
事件到来->QObject:event()-QWidget----获取不同的事件类型
鼠标事件:virtual void mousePressEvent(QMouseEvent *event)
- Qt应用程序自己产生
- QApplication::postEvent() – 异步
例如:QWidget::update() - QApplication::sendEvent() – 同步
例如:QWidget::repaint()
- QApplication::postEvent() – 异步
1.3 事件的处理
重写特定事件处理函数
- 在Qt中,事件被封装成对象,所有的事件对象类型都继承自抽象类QEvent
- 当事件发生时,首先被调用的是QObject类中的虚函数event(),其参数(QEvent)标识了具体的事件类型
- 在Qt桌面应用(Qt Widgets Application)开发中,QWidget类覆盖了其基类中的event()虚函数,并根据具体事件调用具体事件处理函数:
-void QWidget::mousePressEvent(QMouseEvent* e); //鼠标按下事件
-void QWidget::mouseReleaseEvent(QMouseEvent* e); //鼠标抬起事件
-void QWidget::mouseMoveEvent(QMouseEvent* e);// 鼠标移动事件
-void QWidget::paintEvent(QPaintEvent* e);//绘图事件 - 在自定义窗口事件处理函数的过程中,我们可以继承QWidget或者其子类,在自定义的窗口子类中重写事件处理函数,当事件发生的时候,执行的事件处理函数将是我们自定义的版本。
1.4 定时器事件
Qt通过两套机制为应用程序提供定时功能
1) 定时器事件,由QObject提供
2) 定时器信号,由QTimer提供
- 通过定时器事件实现定时器
int QObject:startTimer(int interval);// 启动定时器,返回定时器ID,以后每隔interval毫秒触发一次
void QObject::timerEvent(QTimerEvent*);//定时器事件处理函数
void QObject::killTimer(int id);//关闭参数id所标识的定时器
1.5 用定时器实现电子时钟和计时器
1: 设计UI
IcdNumber:
objectname:m_ladH
digitCount:2
IcdNumber:
objectname:m_ladM
digitCount:2
IcdNumber:
objectname:m_ladS
digitCount:2
IcdNumber:
objectname:m_lcdE
digitCount:12
pushbutton:
objectname:m_btnStart
default:v
text:开始
pushbutton:
objectname:m_btnReset
text:复位
2:实现电子时钟,依赖定时器信号,在TimerDialog.h中定义定时器相关的成员变量、声明相关的函数定义
#include <QTimer>
#include <QTime>
...
private slots:
// 定时器信号对应的函数
void on_m_clock_timeout();
private:
QTimer m_clock;// 声明定时器
...
3: 书写定时器信号槽函数,定义信号和槽的连接
TimerDialog::TimerDialog(QWidget *parent): QDialog(parent), ui(new Ui::TimerDialog){
ui->setupUi(this);
// 建立信号和槽的连接
connect(&m_clock,SIGNAL(timeout()),this,SLOT(on_m_clock_timeout()));
m_clock.start(1000); // 每隔1000ms触发一次定时器事件
}
// 定时器信号的槽函数
void TimerDialog::on_m_clock_timeout(){
// 获取当前时间
QTime now = QTime::currentTime();
ui->m_ladH->display(QString::number(now.hour()));
ui->m_ladM->display(QString::number(now.minute()));
ui->m_ladS->display(QString::number(now.second()));
}
以上就能实现在ui上显示电子时钟
4:实现计时器功能,生成开始、复位按钮的槽函数
// 开始按钮槽函数
void TimerDialog::on_m_btnStart_clicked(){}
// 复位按钮槽函数
void TimerDialog::on_m_btnRest_clicked(){}
5: 使用计时器,每1ms触发一次定时器事件,需要重写定时器事件函数
void TimerDialog::timerEvent(QTimerEvent *){}
6: 在TimerDialog.h中定义定时器相关的成员变量、声明相关的函数定义
private slots:
// 开始按钮槽函数
void on_m_btnStart_clicked();
// 复位按钮槽函数
void on_m_btnRest_clicked();
protected:
// 定时器事件函数
void timerEvent(QTimerEvent *);
private:
int m_TimerId;// 定时器ID 用于关闭定时器
QTime m_time;// 当前时间
7:书写定时器开始函数,实现点击开始按钮,开始计时功能
void TimerDialog::on_m_btnStart_clicked()
{
if(ui->m_btnStart->text()=="开始"){
m_TimerId=startTimer(1);// 每1ms触发一次定时器事件
ui->m_btnStart->setText("停止");
ui->m_btnRest->setEnabled(false);// 关闭复位键使能
}else{
// 关闭定时器事件
killTimer(m_TimerId);
ui->m_btnStart->setText("开始");
ui->m_btnRest->setEnabled(true);// 打开复位键使能
}
}
// 每1ms触发一次定时器事件
// 定时器事件函数
void TimerDialog::timerEvent(QTimerEvent *){
m_time=m_time.addMSecs(1);
ui->m_lcdE->display(m_time.toString("HH:mm:ss.zzz"));
}
8: 书写复位按钮,实现点击复位按钮时,能够清除计时
void TimerDialog::on_m_btnRest_clicked()
{
m_time.setHMS(0,0,0);
ui->m_lcdE->display(m_time.toString("HH:mm:ss.zzz"));
}
9:在构造函数中,初始化当前时间等自定义的成员变量
TimerDialog::TimerDialog(QWidget *parent): QDialog(parent), ui(new Ui::TimerDialog){
ui->setupUi(this);
on_m_btnRest_clicked();// 复位
}
1.6 鼠标事件
- QWidget类定义了以下虚函数提供对鼠标事件的处理,其参数QMouseEvent描述了鼠标事件的细节,如引发事件的鼠标按键、鼠标所在位置等
- virtual void mousePressEvent (QMouseEvent* e);// 鼠标按下
- virtual void mouseReleaseEvent(QMouseEvent* e); //鼠标抬起
- virtual void mouseDoubleClickEvent(QMouseEvent* e);//鼠标双击
- virtual void mouseMoveEvent (QMouseEvent* e);//鼠标移动
- 案例:通过鼠标左键实现拖拽label方块移动
1:设计UI
旋转label-palette -继承 -改变调色板-快速-选中颜色-OK
autofillBackGround- √
2:重写鼠标事件函数
// mouseDialog.h文件中引入QMouseEvent,声明以下事件函数
protected:
//按下事件
void mousePressEvent(QMouseEvent*);
// 移动事件
void mouseMoveEvent(QMouseEvent*);
// 松开
void mouseReleaseEvent(QMouseEvent*);
// mouseDialog.cpp文件中重写事件函数
// 鼠标按下事件
void mouseDialog::mousePressEvent(QMouseEvent * e){}
// 移动
void mouseDialog::mouseMoveEvent(QMouseEvent* e){}
// 松开
void mouseDialog:: mouseReleaseEvent(QMouseEvent* e){}
3:书写鼠标按下事件
① 需要获取到按下的鼠标键
② 查看鼠标键是否在label区域内
// 鼠标按下事件
// button - 获取按键
// frameRect - 获取组件的区域
// contains - 包含
// QPoint - 坐标
// pos()-鼠标的坐标
void mouseDialog::mousePressEvent(QMouseEvent * e){
if(e->button()==Qt::LeftButton){// 获取按下的键
qDebug("11");
QRect blockRect = ui->m_label->frameRect();// 获取m_label组件的区域
blockRect.translate(ui->m_label->pos());// 让滑块和鼠标保持同一个坐标系
if(blockRect.contains(e->pos())){// 获取当前按下的坐标是否位于当前区域中
m_dragging =true;// 鼠标在区域内额标志位
// 鼠标的偏移量
m_offset = ui->m_label->pos()-e->pos();
}
}
}
4:书写鼠标移动事件函数
void mouseDialog::mouseMoveEvent(QMouseEvent* e){
if(m_dragging){
QPoint labelPos = e->pos()+m_offset;
ui->m_label->move(labelPos);
}
}
5: 书写鼠标松开事件
void mouseDialog:: mouseReleaseEvent(QMouseEvent* e){
if(e->button()==Qt::LeftButton){
m_dragging = false;
}
}
6: 限制label滑块活动的区域
void mouseDialog::mouseMoveEvent(QMouseEvent* e){
if(m_dragging){
QPoint labelPos = e->pos()+m_offset;
// 因为鼠标可能移除窗口外,需要限定活动的区域范围
// 获取主窗口的大小
QSize windowSize = size();
// 获取label的大小
QSize labelSize = ui->m_label->size();
int xMin =0,xMax=windowSize.width()-labelSize.width();
int yMin=0,yMax=windowSize.height()-labelSize.height();
// 限制滑块活动范围
// x活动范围是否在允许范围
labelPos.setX(max(min(xMax,labelPos.x()),xMin));
// y活动范围是否在允许范围
labelPos.setY(max(min(yMax,labelPos.y()),yMin));
// 滑块移动
ui->m_label->move(labelPos);
}
}
1.7 键盘事件
QWidget类定义了以下虚函数提供对键盘事件的处理,其参数QKeyEvent描述了键盘事件的细节,如引发事件的键盘按键、文本等
virtual void keyPressEvent(QKeyEvent* e);//按键按下
virtual void keyReleaseEvent(QKeyEvent* e);//按键抬起
具体的使用同鼠标事件
1.8 绘图事件
通过绘图事件,可以实现自定义的图像绘制,当有下列情况之一发生时,将触发窗口的绘制事件,即QWidget类的paintEvent()虚函数会被调用:
- 窗口被创建以后第一次显示出来
- 窗口由隐藏状态转变为可见状态
- 窗口由最小化状态转变为正常或最大化状态
- 窗口因尺寸大小的变化需要呈现更多的内容
- QWidget类的update()/repaint()成员函数被调用
如果希望在自己的窗口中显示某个图像,在QWidget的窗口子类中可以重写绘图事件函数paintEvent,在其中可用QPainter(Qt二维图形引擎)实现指定的图像绘制、渲染等操作 - 案例:基于资源的图片浏览器
1:添加图片资源
1.将带有图片的images目录拷贝到项目目录下
2.右击项目 - add new... - Qt - Qt Resource File -
名称 - Pictures - 各种下一步
3.add prefix - 前缀 /
4.add files - 进入到images目录下 - 选中所有 - 打开 - ok
2:设计UI
QFrame
objecname - m_frmImage
sizePolicy - 垂直策略 - expanding
pushbutton
objecname - m_btnPrev
text - 上一张
pushbutton:
objecname - m_btnNext
text - 下一张
为上一张和下一张按钮添加槽函数,右单机 转到槽
上一张/下一张 - 右击 - 转到槽 - clicked -
3:重写绘图事件函数
// 绘图事件 引入的头文件有QPainter 和 QPaintEvent
void pictureDialog:: paintEvent(QPaintEvent *){
// QT二维图形引擎
QPainter painter(this);
// 获取当前区域
QRect qrect = ui->m_fraPicture->frameRect();
// 调整坐标系为同一个坐标系
qrect.translate(ui->m_fraPicture->pos());
// 获取图像
QImage image(":/images/" + QString::number(m_imageIndex) + ".jpg");
// 将图片贴到画框中
painter.drawImage(qrect,image);
}
4:书写上一张、下一张的槽函数
// 上一张槽函数
void pictureDialog::on_m_btnPrev_clicked()
{
m_imageIndex--;
enableButtons();
update();//触发绘图事件
}
// 下一张槽函数
void pictureDialog::on_m_btnNext_clicked()
{
m_imageIndex++;
enableButtons();
update();//触发绘图事件
}
void pictureDialog::enableButtons(void){
ui->m_btnPrev->setEnabled(m_imageIndex !=0); // 最小的索引下标
ui->m_btnNext->setEnabled(m_imageIndex!=7);// 最大的索引下标
}
5:头文件中的声明主要有
private slots:
void on_m_btnPrev_clicked(); // 上一页的槽函数
void on_m_btnNext_clicked(); // 下一页的槽函数
void enableButtons(void);// 按钮的使能
protected:
void paintEvent(QPaintEvent *); // 绘图事件
private:
int m_imageIndex;// 图片索引 需要在构造函数中初始化
2、绘画
2.1 画笔
- QPen:用于设置线条的颜色、宽度、线型等
- 常用函数
函数原型 | 功能 |
---|---|
void setColor(QColor& color) | 设置画笔颜色,即线条颜色 |
void setWidth(int width) | 设置线条宽度 |
void setStyle(Qt::PenStyle style) | 设置线条样式,参数为Qt:PenStyle枚举类型 |
void setCapStyle(Qt::PenCapStyle style) | 设置线条端点样式,参数为Qt:PenCapStyle枚举类型 |
void setJoinStyle(Qt::PenJoinStyle style) | 设置连接样式,参数为Qt:PenJoinStyle枚举类型 |
2.2 画刷
- QBrush:用于设置一个区域的填充特性,可以设置填充颜色、填充方式、渐变特性等,还可以采用图片做材质填充
- 常用函数
函数原型 | 功能 |
---|---|
void setColor(QColor& color) | 设置画刷颜色,实体填充时即为填充颜色 |
void setStyle(Qt::BrushStyle style) | 设置画刷样式,参数为Qt:BrushStyle枚举类型 |
void setTexture(QPixmap& pixmap) | 设置一个QPixmap:类型的图片作为画刷的图片, 画刷样式自动设置为Qt:TexturePalette |
void setTexturelmage(Qlmage& image) | 设置一个QImage:类型的图片作为画刷的图片, 画刷样式自动设置为Qt:TexturePalette |
2.3 字体
- QFont:用于设置绘制文本的字体
- 常用函数
函数原型 | 功能 |
---|---|
void setBold(bool enable) | 设置字体是否为粗体 QFont::Bold QFont::Normal) |
void setFamily(const QString &family) | 设置字体的字体名称 |
void setltalic(bool enable) | 设置字体是否为斜体 (QFont::Styleltalic QFont::StyleNormal) |
void setUnderline(bool enable) | 设置字体是否有下划线 |
void setWeight(int weight) | 设置字体的粗细,参数为QFont:Weight类型 |
void setPointSize(int pointSize) | 设置字体的大小 |
2.4 绘图系统
- QPainter:是用来进行绘图操作的类
- QPaintDevice是一个可以使用QPainter进行绘图的抽象的二维界面
- QWidget
- QPixmap
- QImage
- 案例 使用QPen 、QBrush、QPainter画画
// 绘画事件
oid PaintDialog:: paintEvent(QPaintEvent *){
QPainter painter(this); // 绘制一个矩形,填充为黄色,线条颜色为红色
QPen pen; // 画笔
pen.setColor(Qt::red); // 线条颜色
pen.setWidth(5);// 线条宽度
pen.setStyle(Qt::DashDotLine);// 线条样式
painter.setPen(pen);
// 画刷 内部填充的颜色
QBrush brush;
brush.setColor(Qt::yellow);
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
// 设置绘画区域 绘画
int w=width();
int h=height();
painter.drawRect(QRect(QPoint(w/5,h/5),QPoint(w/5*4,h/5*4)));
pen.setColor(Qt::blue); // 线条颜色
pen.setWidth(3);// 线条宽度
pen.setStyle(Qt::SolidLine);// 线条样式
painter.setPen(pen);
// 填充图片
brush.setTextureImage(QImage(":/images/1.png"));
brush.setStyle(Qt::TexturePattern);
painter.setBrush(brush);
painter.drawRect(QRect(QPoint(w/5*2,h/5*2),QPoint(w/5*3,h/5*3)));
}