QT:第五章:事件系统

1. 事件的概念

        事件是对各种应用程序需要知道的由应用程序内部或外部产生的事情或动作的统称。Qt中所有事件都有对应的类,他们继承自QEvent类。

        当用户按下鼠标、敲下键盘,或者窗口需要重新绘制的时候,会发生一个重绘事件。一些事件是在用户操作时出,例如鼠标左键按下事件,另一些事件可以由系统自动发出,如定时器事件。

1. 事件与信号槽的区分

从信号和事件的产生来区分两者的不同

        事件与信号槽不相同,例如,我们使用鼠标点击界面上的按钮时,会产生鼠标事件QMouseEvent(不是按钮产生的),由于你按下了按钮,按钮会发出clicked信号(信号是按钮产生的)。

        事件与信号是两个不同层面的东西,它们的发出者不同,作用也不同。事件由外部实体生成,事件更底层。

        信号需要关注的是产生信号的对象,槽函数需要对信号做出相应的响应动作。

        通常,使用Qt提供的控件时,我们一般关心的是信号和槽函数,如果我们自定义控件,一般关心的是事件,在事件中实现目标的需要,例如自定义MyLabel显示圆形图片。

从使用角度区分信号和事件的不同

        信号槽:signal由具体对象发出,然后交给connect关联的槽函数去处理;

        事件:Qt中使用事件队列对所有发出的事件进行维护,每当有新事件产生时,会被追加到事件队列的末尾,前一个事件处理完毕后,取出后一个事件处理。

        有些事件可以不进入事件队列,事件产生后直接进行处理,并且事件可以使用“事件过滤器”进行过滤。

2. 事件的处理方法

Qt中所有从QObject继承的子类都可以接收和处理时间;

  1. 重写控件的事件方法,例如paintEvent、mousePressEvent、mouseReleaseEvent,这是最简单处理事件的方法,但是只能处理特定的事件

  2. 在对象上面安装事件过滤器,使用过滤器可以在一个界面类中同时处理不同类型、不同子控件的事件

  3. 重新实现notify()函数,可以在事件过滤器得道事件前对事件进行处理;

  4. 给QApplication安装事件过滤器,同时处理一个应用程序中的不同事件;

  5. 重新实现event()方法

3. 鼠标和滚轴事件

1. 窗口任意位置拖动

窗口任意位置按下鼠标左键拖动窗口(不仅是在标题栏按下鼠标左键)

//鼠标坐标和窗口位置的差值 QPoint m_offset;----在.h文件中声明为全局变量

void EventSystem10::mousePressEvent(QMouseEvent *pEvent)
{
	//如果是鼠标左键按下
	if (pEvent->button() == Qt::LeftButton) {
		//pEvent->globalPos()左键按下时鼠标的全局坐标
		//鼠标左键按下时敞口左上角坐标
		m_offset = pEvent->globalPos() - this->pos();
		//改变鼠标光标形状
		QCursor cursor;
		cursor.setShape(Qt::ClosedHandCursor);
		QApplication::setOverrideCursor(cursor);
	}
}

void EventSystem10::mouseMoveEvent(QMouseEvent * pEvent)
{
	//如果是按下左键拖动鼠标
	//在鼠标移动事件中,button()方法始终返回Qt::NoButton,而Buttons()方法返回的是所有键,用位与运算判断是否按下左键同时拖动鼠标
	if (pEvent->buttons() & Qt::LeftButton) {
		QPoint temp;
		//窗口需要移动到的位置坐标(鼠标移动后的全局坐标减去差值)
		temp = pEvent->globalPos() - m_offset;
		//move()移动窗口到计算出的位置
		//this->move(temp);
		this->move(temp.rx(), temp.ry());
	}
}

2. 改变鼠标光标形状

右键改变鼠标光标

void EventSystem10::mousePressEvent(QMouseEvent *pEvent)
{
	if (pEvent->button() == Qt::RightButton)
	{
		//设置鼠标光标为指定图片
		QCursor picCursor(QPixmap("D:\\cccccc\\QtApp\\itemProgect07\\image\\1.png"));
		QApplication::setOverrideCursor(picCursor);
	}
}

void EventSystem10::mouseReleaseEvent(QMouseEvent * pEvent)
{
	//恢复成应用程序原来的光标
	QApplication::restoreOverrideCursor();
}

3. 窗口最大化及还原

 双击鼠标使窗口最大化和最小化

void EventSystem10::mouseDoubleClickEvent(QMouseEvent *pEvent)
{
	if (pEvent->button() == Qt::RightButton)//如果按下的是右键
	{
		if (this->windowState() != Qt::WindowMaximized)//当前窗口状态不是最大化
		{
			this->setWindowState(Qt::WindowMaximized);//设置为最大化
		}
		else
		{
			this->setWindowState(Qt::WindowNoState);//窗口恢复到之前状态
		}
	}
}

4. 放大字体缩小字体

使用滚轴让字体放大缩小

void EventSystem10::wheelEvent(QWheelEvent * pEvent)
{
	if (pEvent->angleDelta().y() > 0)//滚轴远离使用者的方向移动
	{
		ui.textEdit->zoomIn();//放大
	}
	else
	{
		ui.textEdit->zoomOut();//缩小
	}

}

4. 键盘事件

键盘事件

  • keyPressEvent()

  • keyReleaseEvent()

1. 单个键

例如:按下m弹出提示信息

void KeyTimerEvent11::keyReleaseEvent(QKeyEvent * pEvent)
{
	if (pEvent->key() == Qt::Key_M)
	{
		QMessageBox::information(nullptr, "key", "user release key M");
	}
	else
	{
		//QWidget::keyReleaseEvent(pEvent);
		__super::keyReleaseEvent(pEvent);
	}
}

2. 组合键

例如:按下Ctrl+m键放大窗口

void KeyTimerEvent11::keyPressEvent(QKeyEvent * pEvent)
{
	// 满足按键需求完成实际功能,pEvent->modifiers()获取功能键(例如ctrl、alt、shift)
	if ( (pEvent->modifiers() == Qt::ControlModifier) &&  (pEvent->key()== Qt::Key_M) )
	{
		this->setWindowState(Qt::WindowMaximized);
	}
	else
	{
		// 否则执行父类中该方法
		QWidget::keyPressEvent(pEvent);
	}
}

5. 定时器事件

Qt中可以使用两种方式实现定时器事件

头文件(.h):

class keyTimerEvent11 : public QWidget
{
    Q_OBJECT

public:
    keyTimerEvent11(QWidget *parent = nullptr);
    ~keyTimerEvent11();
public slots:
	void on_pushButton_clicked();
	void updateTextSlot();//定时器超时信号对应的槽函数
protected:
	void keyPressEvent(QKeyEvent *pEvent);//按下
	void keyReleaseEvent(QKeyEvent *pEvent);//抬起

	void timerEvent(QTimerEvent *pEvent);

private:
    Ui::keyTimerEvent11Class ui;
	//用于保存定时器id
	int m_nTimerID1, m_nTimerID2, m_nTimerID3;
	QTimer *pTimer = nullptr;
};

1. 使用QTimerEvent类

步骤:

  • 开启定时器startTimer(),

  • TimerEvent中完成具体操作,

  • 结束定时器killTimer()

例如:点击结束按钮 ,结束定时器


// 开启定时器并指定时间间隔,第一个定时器时间间隔1秒
m_nTimerID1 = this->startTimer(1000);
m_nTimerID2 = startTimer(1500);
m_nTimerID3 = startTimer(3000);

void KeyTimerEvent11::on_pushButton_clicked()
{
	killTimer(m_nTimerID1);
	killTimer(m_nTimerID2);
	killTimer(m_nTimerID3);
}

void KeyTimerEvent11::timerEvent(QTimerEvent *pTimerEvent)
{
	if (pTimerEvent->timerId() == m_nTimerID1)
	{
		qDebug() << "timer1";
		qDebug() << m_nTimerID1;
	}
	else if (pTimerEvent->timerId() == m_nTimerID2)
	{
		qDebug() << "timer2";
		qDebug() << m_nTimerID2;
	}
	else
	{
		qDebug() << "timer3";
		qDebug() << m_nTimerID3;
	}
}

2. 使用QTimer类

步骤:

  • 创建QTimer对象

  • 关联该对象的超时信号和槽函数并开启定时器

  • 完成槽函数

例如:通过定时器改变时间LED颜色

// 创建定时器对象
	m_pTimer = new QTimer(this);
	// 超时信号和槽函数建立关联
	connect(m_pTimer, &QTimer::timeout, this, &KeyTimerEvent11::updateTextSlot);
	// 开启定时
	m_pTimer->start(1000);

    void keyTimerEvent11::updateTextSlot()
    {
        QTime time = QTime::currentTime();
        QString strText = time.toString("hh:mm:ss");
        if (time.second() % 2 == 0)
        {
            strText[3] = ' ';
            strText[5] = ' ';
        }
        ui.lcdNumber->display(strText);

        //生成三个随机值,表示rgb
        int red = QRandomGenerator::global()->bounded(256);//0-255的值
        int green = QRandomGenerator::global()->bounded(256);
        int blue = QRandomGenerator::global()->bounded(256);
        //构造颜色对象
        QColor color(red, green, blue);
        //获取调色板并设置颜色
        QPalette pal = ui.lcdNumber->palette();
        pal.setColor(QPalette::WindowText, color);
        ui.lcdNumber->setPalette(pal);

    }

 

6. 事件的传递及事件过滤器

1. 事件的传递

当在MyLineEdit中按下键盘时,输出如下信息:

“MyLineEdit 中键盘按下事件”

“Widget中的键盘按下事件”

事件先传递给当前获得焦点的子控件MyLineEdit,输出“MyLineEdit 中键盘按下事件”

然后时间又传递给了父控件的键盘按下事件,输出“Widget中的键盘按下事件”

2. 事件的经过

当窗口上产生事件时,事件会经过:事件派发-->事件过滤-->事件分发-->事件处理,这四个阶段。

每个Qt应用程序都对应一个唯一的QApplication对象,然后调用这个对象的exec函数开启事件循环,应用程序内部开启一个循环来检测事件。

  • 当事件产生后,QCoreaApplication中的notify()方法将事件发送给指定的窗口

  • 事件在派发过程中可以通过事件过滤器进行过滤(只处理指定类型的事件),默认不对任何事件进行过滤

    bool QObject::eventFilter()

  • 当事件分给指定窗口后,窗口的事件分发器会对收到的事件进行分类

    QWidget::event()

  • 事件分发器会将分类后的事件(鼠标事件、键盘事件、绘制事件...)分发给对应的事件处理函数进行处理

"Widget事件过滤器"

"MyLineEdit的event函数"

MyLineEdit 中键盘按下事件"

"Widget中的键盘按下事件"

键盘按下从QApplication的notify函数派发出来,事件先到达窗口的事件过滤器,然后到达子控件lineEdit的event函数,最后到达lineEdit的keyPressEvent(),由于其没有accept键盘按下事件,最后事件传递到其父窗口

3. 事件过滤器的应用

事件过滤器不只是过滤事件,也可以对事件进行捕捉并做出相应的处理操作,通常用来完成一些特殊效果。

例如:

  • 当鼠标移入QTextEdit时会给控件设置焦点并绘制红色边框,当鼠标移出QTextEdit时,移出焦点并且设置边框透明

  • 禁用QLineEdit的

3.1 密码框禁用复制

//头文件声明方法

protected:
	bool eventFilter(QObject *obj, QEvent *e);

//cpp文件进行方法实现

ui.lineEdit->installEventFilter(this);
// 不显示右键弹出菜单
ui.lineEdit->setContextMenuPolicy(Qt::NoContextMenu);	

bool EventFilterUse13::eventFilter(QObject * obj, QEvent * e)
{
	if (obj == ui.lineEdit)
	{
		switch (e->type())
		{
			case QEvent::MouseMove://鼠标拖动事件
			case QEvent::MouseButtonDblClick://鼠标左键双击事件
				//返回true表示上面case分支列出的回见不会向后传递
				return true;
			case QEvent::KeyPress://键盘事件
			{
				//停止全选,赋值和粘贴 static_cast、const_cast、dynamic_cast和reinterpret_cast
				QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(e);
				if (pKeyEvent->matches(QKeySequence::SelectAll) || pKeyEvent->matches(QKeySequence::Copy) || pKeyEvent->matches(QKeySequence::Paste))
				{
					return true;
				}
				//return false:表示某个类型的事件需要向后传递
			}
		}
	}
	return QWidget::eventFilter(obj, e);
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值