Qt之事件

1.获取鼠标事件

鼠标事件包括鼠标移动,按下,松开,以及鼠标双击。

#ifndef MOUSEEVENT_H
#define MOUSEEVENT_H

#include <QtGui>

class MouseEvent : public QMainWindow
{
    Q_OBJECT
public:
    MouseEvent();
    ~MouseEvent();
private:    
    QLabel *labelStatus; 
    QLabel *labelMousePos;     
protected :
    void mouseMoveEvent ( QMouseEvent * e );
    void mousePressEvent ( QMouseEvent * e );
    void mouseReleaseEvent ( QMouseEvent * e );
    void mouseDoubleClickEvent( QMouseEvent * e );
};


#endif 

#include "mouseevent.h"

MouseEvent::MouseEvent( )
    : QMainWindow()
{  
    setWindowTitle(tr("Get Mouse Event"));
    
    labelStatus = new QLabel();	
    labelStatus->setText(tr("Mouse Position:"));   
    labelStatus -> setFixedWidth (100);

    labelMousePos = new QLabel();
    labelMousePos->setText(tr(""));   
    labelMousePos -> setFixedWidth (100);
    
    statusBar()->addPermanentWidget(labelStatus);//在QMainWindow的状态栏中增加控件
    statusBar()->addPermanentWidget(labelMousePos);

    this->setMouseTracking ( true); //设置窗体是否追踪鼠标,true为追踪
}

MouseEvent::~MouseEvent()
{

}

void MouseEvent::mouseMoveEvent ( QMouseEvent * e )//鼠标移动事件响应函数
{
    labelMousePos ->setText("("+QString::number(e->x())+","+QString::number(e->y())+")");
	//x(),y()方法获取鼠标相对于事件接受窗体的位置
}

void MouseEvent::mousePressEvent ( QMouseEvent * e )//鼠标按下响应函数
{
    QString str="("+QString::number(e->x())+","+QString::number(e->y())+")";//str储存坐标值
    if(e->button()==Qt::LeftButton)//左键按下
    {
    	statusBar()->showMessage (tr("Mouse Left Button Pressed:")+str);
    }
    else if(e->button()==Qt::RightButton)//右键按下
    {
    	statusBar()->showMessage (tr("Mouse Right Button Pressed:")+str);
    }    
    else if(e->button()==Qt::MidButton)//中键按下
    {
    	statusBar()->showMessage (tr("Mouse Middle Button Pressed:")+str);
    }    

}

void MouseEvent::mouseDoubleClickEvent( QMouseEvent * e )//鼠标双击相应函数
{
    QString str="("+QString::number(e->x())+","+QString::number(e->y())+")";
    if(e->button()==Qt::LeftButton)
    {
    	statusBar()->showMessage (tr("Mouse Left Button Double Clicked:")+str);
    }
    else if(e->button()==Qt::RightButton)
    {
    	statusBar()->showMessage (tr("Mouse Right Button Double Clicked:")+str);
    }    
    else if(e->button()==Qt::MidButton)
    {
    	statusBar()->showMessage (tr("Mouse Middle Button Double Clicked:")+str);
    }    	
}

void MouseEvent::mouseReleaseEvent ( QMouseEvent * e )//鼠标松开函数
{
    QString str="("+QString::number(e->x())+","+QString::number(e->y())+")";
    statusBar()->showMessage (tr("Mouser Released:")+str,3000);
}

2.使用键盘控制移动

在图像处理和游戏中,有时需要通过键盘控制某个对象的移动

键盘事件的获取是通过重定义QWidget的keyPressEvent()和keyReleaseEvent()来实现的

#ifndef KEYEVENT_H
#define KEYEVENT_H

#include <QtGui>

class KeyEvent : public QWidget
{
    Q_OBJECT
public:
    KeyEvent(QWidget *parent=0);
    ~KeyEvent();
	
	void drawPix();
	void keyPressEvent(QKeyEvent *);
    void paintEvent(QPaintEvent *);

private:
    QPixmap *pix;	//作为一个绘图设备,使用双缓冲机制实现图形的绘制
    QImage image;	//界面中间的小图标
    int startX;
    int startY;		//左上角的顶点位置
    int width;
    int height;		//界面的高度和宽度
    int step;		//网格的大小,即移动的不进值
};


#endif

#ifndef KEYEVENT_H
#define KEYEVENT_H

#include <QtGui>

class KeyEvent : public QWidget
{
    Q_OBJECT
public:
    KeyEvent(QWidget *parent=0);
    ~KeyEvent();
	
	void drawPix();
	void keyPressEvent(QKeyEvent *);
    void paintEvent(QPaintEvent *);

private:
    QPixmap *pix;	//作为一个绘图设备,使用双缓冲机制实现图形的绘制
    QImage image;	//界面中间的小图标
    int startX;
    int startY;		//左上角的顶点位置
    int width;
    int height;		//界面的高度和宽度
    int step;		//网格的大小,即移动的不进值
};


#endif

#include "keyevent.h"

KeyEvent::KeyEvent(QWidget *parent)
	:QWidget(parent)
{  
    setWindowTitle(tr("Key Event"));
    
    setAutoFillBackground(true);
	
    QPalette palette = this->palette();
    palette.setColor(QPalette::Window,Qt::white);
    setPalette(palette);

    setMinimumSize(512,256);
    setMaximumSize(512,256);
    
    width=size().width();
    height=size().height();
    
    pix = new QPixmap(width,height);
    pix->fill(Qt::white);
    
    step=32;
    image.load(":/images/1.png");
    
    startX=0;
    startY=0;
    
    drawPix();
}

KeyEvent::~KeyEvent()
{
}
             
void KeyEvent::drawPix()
{
	pix->fill(Qt::white);//重新刷新pix对象为白色底色
	QPainter *painter = new QPainter(pix);//创建QPainter对象,并指定pix为绘图设备
	QPen pen(Qt::DotLine);//创建QPen对象,并设置画笔的线型,用于绘制网格
    painter->setPen(pen);
    for(int i=step;i<width;)//画竖线
    {
    	painter->drawLine(QPoint(i,0),QPoint(i,height));
    	i=i+step;
    }
    for(int j=step;j<height;)//画横线
    {
    	painter->drawLine(QPoint(0,j),QPoint(width,j));
    	j=j+step;
    }    
    painter->drawImage(QPoint(startX,startY),image);//绘制可移动的小图标
}             
              
void KeyEvent::keyPressEvent(QKeyEvent *event)//处理键盘按下事件
{
	if(event->modifiers() == Qt::ControlModifier)//判断Ctrl键是否被按下
	{
		if(event->key() == Qt::Key_Left)
		{
			startX=(startX-1<0)?startX:startX-1;
			
		}
		if(event->key() == Qt::Key_Right)
		{
			startX=(startX+1+image.width ()>width)?startX:startX+1;
			
		}
		if(event->key() == Qt::Key_Up)
		{
			startY=(startY-1<0)?startY:startY-1;
			
		}	
		if(event->key() == Qt::Key_Down)
		{
			startY=(startY+1+image.height()>height)?startY:startY+1;
			
		}		
	}
	else
	{
		startX=startX-startX%step;
		startY=startY-startY%step;
		
		if(event->key() == Qt::Key_Left)
		{
			startX=(startX-step<0)?startX:startX-step;		
		}
		if(event->key() == Qt::Key_Right)
		{
			startX=(startX+step+image.width()>width)?startX:startX+step;		
		}
		if(event->key() == Qt::Key_Up)
		{
			startY=(startY-step<0)?startY:startY-step;		
		}	
		if(event->key() == Qt::Key_Down)
		{
			startY=(startY+step+image.height()>height)?startY:startY+step;
		}		
		if(event->key() == Qt::Key_Home)//按下home键则重新复位
		{
			startX=0;
			startY=0;
		}	
		if(event->key() == Qt::Key_End)//图标至于右下的顶点
		{
			startX=width-image.width();
			startY=height-image.height();
		}			
	}
	drawPix();//重新绘制图像
	update();//触发界面重画	
}              
                                                                             
void KeyEvent::paintEvent(QPaintEvent *)//为界面的重画函数,将pix绘制在界面上
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(0,0),*pix);
}


3.事件过滤器实现动态图片按钮

Qt提供的QPushButton提供一个普通的按钮类,如果需要实现鼠标按下时候图片发送变化,同时响应鼠标的按下等事件,需要通过事件过滤器来实现。

Qt的事件模型中提供的事件过滤器的功能使得一个QObject对象可以监视另一个QObject对象中的事件,通过在一个QObject对象中安装事件过滤器可以在事件到达前捕获事件,从而起到监视该对象事件的效果。

(实现类似功能的另一种方式是通过分别继承不同的控件类,并重构各控件类的事件响应函数,但如果有大量控件的时候,没一个控件都要继承,重构,很复杂。)

#ifndef EVENTFILTER_H
#define EVENTFILTER_H

#include <QtGui>

class EventFilter : public QDialog
{
    Q_OBJECT
public:
    EventFilter( QWidget *parent=0, Qt::WindowFlags  f=0 );
    ~EventFilter();
public:
	QLabel* Label1;
	QLabel* Label2;
	QLabel* Label3;
	QLabel* LabelState;
	
	QImage Image1;
	QImage Image2;
	QImage Image3;

public slots:
	bool eventFilter(QObject*,QEvent*);//QObject的事件监视函数
};


#endif

构造函数通过installEventFilter()每一个图片安装事件过滤器。

步骤:

1.通过对目标对象调用installEventFilter()来注册监视对象。

2.在监视对象的eventFilter()函数中处理目标对象的事件。

函数原型

void QObject::installEventFilter(QObject* filterObj)

参数filterObj是监视事件的对象,此对象可以通过eventFilter()函数接收事件,这里指定为整个窗体,如果某个事件需要被过滤,即停止正常的事件响应,则在eventFilter()中返回true,否则返回false,QObject的removeEventFilter()可以解除以安装的事件过滤器。

#include "eventfilter.h"

EventFilter::EventFilter( QWidget *parent, Qt::WindowFlags  f )
    : QDialog( parent, f )
{  
    QFont font("ZYSong18030",12);
    setFont(font);	
    
    setWindowTitle(tr("Event Filter"));
    
    Image1.load(":/images/1.png");//载入图片
    Image2.load(":/images/2.png");
    Image3.load(":/images/3.png");
    
    Label1 = new QLabel( this );//带有图片按钮的布局
    Label1->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
	Label1 ->setPixmap(QPixmap::fromImage(Image1));
    Label2 = new QLabel( this );
    Label2->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
	Label2 ->setPixmap(QPixmap::fromImage(Image2));
    Label3 = new QLabel( this );
    Label3->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
	Label3 ->setPixmap(QPixmap::fromImage(Image3));
   
   	LabelState = new QLabel( this );//底部的状态栏
   	LabelState->setAlignment(Qt::AlignHCenter);
   
    QHBoxLayout *hbLayout = new QHBoxLayout();//水平布局
    hbLayout->addWidget( Label1 );
    hbLayout->addWidget( Label2 );
    hbLayout->addWidget( Label3 );

    QVBoxLayout *vbLayout = new QVBoxLayout(this);//竖直布局
    vbLayout->addLayout( hbLayout );
    vbLayout->addWidget( LabelState );

	Label1->installEventFilter(this);//构造函数通过此函数为每一个图片安装事件过滤器
	Label2->installEventFilter(this);
	Label3->installEventFilter(this);

}

EventFilter::~EventFilter()
{
}

bool EventFilter::eventFilter(QObject* watched,QEvent* event)
{
	if(watched == Label1)		//判断当前发生事件的对象
	{
		if(event->type() == QEvent::MouseButtonPress)	//判断发生的事件类型
		{
			QMouseEvent *mouseEvent = (QMouseEvent *)event;	//将事件event转化为鼠标事件
			if(mouseEvent->buttons() &Qt::LeftButton)
			{
				LabelState->setText(tr("Left mouse button pressed on left image"));
			}
			else if(mouseEvent->buttons() &Qt::MidButton)
			{
				LabelState->setText(tr("Middle mouse button pressed on left image"));
			}	
			else if(mouseEvent->buttons() &Qt::RightButton)
			{
				LabelState->setText(tr("Right mouse button pressed on left image"));
			}					
			QMatrix martix;
			martix.scale(0.8,0.8);	//显示缩小的图片  
			QImage tmp=Image1.transformed(martix);//将原来的图片大小转换成缩小的图片大小
			Label1->setPixmap (QPixmap::fromImage(tmp));

		}		
		if(event->type() == QEvent::MouseButtonRelease)
		{
			LabelState->setText(tr("Mouse button released from left image"));
			Label1->setPixmap (QPixmap::fromImage(Image1));//恢复原来的图片大小
		}	

	}
	if(watched == Label2)
	{
		if(event->type() == QEvent::MouseButtonPress)
		{
			QMouseEvent *mouseEvent = (QMouseEvent *)event;
			if(mouseEvent->buttons() &Qt::LeftButton)
			{
				LabelState->setText(tr("Left mouse button pressed on middle image"));
			}
			else if(mouseEvent->buttons() &Qt::MidButton)
			{
				LabelState->setText(tr("Middle mouse button pressed on middle image"));
			}	
			else if(mouseEvent->buttons() &Qt::RightButton)
			{
				LabelState->setText(tr("Right mouse button pressed on middle image"));
			}								
			QMatrix martix;
			martix.scale(0.8,0.8);  
			QImage tmp=Image2.transformed(martix);
			Label2->setPixmap (QPixmap::fromImage(tmp));
		}		
		if(event->type() == QEvent::MouseButtonRelease)
		{
			LabelState->setText(tr("Mouse button released from middle image"));
			Label2->setPixmap (QPixmap::fromImage(Image2));
		}
	}
	if(watched == Label3)
	{
		if(event->type() == QEvent::MouseButtonPress)
		{
			QMouseEvent *mouseEvent = (QMouseEvent *)event;
			if(mouseEvent->buttons() &Qt::LeftButton)
			{
				LabelState->setText(tr("Left mouse button pressed on right image"));
			}
			else if(mouseEvent->buttons() &Qt::MidButton)
			{
				LabelState->setText(tr("Middle mouse button pressed on right image"));
			}	
			else if(mouseEvent->buttons() &Qt::RightButton)
			{
				LabelState->setText(tr("Right mouse button pressed on right image"));
			}			
			QMatrix martix;
			martix.scale(0.8,0.8);  
			QImage tmp=Image3.transformed(martix);
			Label3->setPixmap (QPixmap::fromImage(tmp));
		}		
		if(event->type() == QEvent::MouseButtonRelease)
		{
			LabelState->setText(tr("Mouse button released from right image"));
			Label3->setPixmap (QPixmap::fromImage(Image3));
		}
	}
	return QDialog::eventFilter(watched,event);	//将事件交给上层对话框
}

4.处理密集时的响应保持

当调用QApplication::exec()时,就启动了Qt的事件循环,在开始的时候,Qt会发出一些事件命令来显示和绘制窗口部件,在这之后,事件循环就开始运行,它不断检查是否有事件发生,并且把这些事件发送给应用程序中的QObject。

当处理一个事件的时候,也会同时产生其他的事件并且会追加到Qt的事件队列中,如果在处理一个特定时间上耗费的时间过多,那么用户界面就会变得无法响应。比如保存文件,但文件还没保存完毕的时候,应用程序就不能响应来自窗口系统的重新绘制的请求。


在此情况下:

1.一种解决方式就是使用多线程,一个线程用于处理用户程序的应用界面,一个线程则执行文件保存操作,或者任意的其他的耗时操作。这样的话在保存文件的时候用户界面也能够响应。


2.而另一种更为简单的解决方法是在文件保存的代码中频繁调用QApplication::processEvent().这个函数告诉Qt处理所有那些还没有被处理的各类事件,然后再将控制权返回给调用者,事实上,QApplication::exec()就是一个不停调用processEvents()函数的while循环。

以下是一个如何使用processEvents()来保持用户界面响应的例子,基于spreadsheet的文件保存功能的程序代码:

通常情况下,当需要发送一个长时间运行的操作的时候,我们希望能够显示一个QPressDialog,它有一个进度条,以及还有一个cancel按钮,允许用户取消操作。

bool Spreadsheet::writeFile(const QString &fileName)

{

QFile file(fileName);

......

QPressDialog progress(this);

progress.setLabelText(tr("Saving %1"),arg(fileName));

progress.setRange(0,RowCount);

progress.setModal(true);

for(int row = 0;row<rowCount;++row)

{

progress.setValue(row); //更新进度条

qApp->progressEvents(); //处理任意的重绘事件或者用户任意的按键事件

if(progress.wasCanceled())

{

file.remove();

return false;

}

for(int column=0;column<ColumnCount;++column)

{

QString str=formula(row,column);

if(!str.isEmpty())

out<<quint16(row)<<quint16(colimn)<<str;

}

}

return true;

}


3.除了使用多线程和QProgressDialog之外,还有一种处理长时间运行操作的完全不同的方法,不是在用户请求的时候处理,而是一直推迟到应用程序空闲下来的时候才处理。

如果该处理可以被安全中断后继续,那么就可以使用这种方法,因为我们并不能事先知道应用程序要多久才能空闲下来。

在Qt中,通过使用一个0毫秒定时器就可以实现这种方法,只要没有其他未处理的事件,就可以触发这个定时器。以下为timerEvent()实现的例子,给出程序空闲时的处理方法

void Spreadsheet::timeEvent(QTimerEvent* event)

{

if(event->timerId()==myTimerId)

{

while(step<MaxStep&&!qApp->hasPendingEvents()) //当hasPendingEvents()的返回值是true,就停止处理并且把控制权交还给Qt,待Qt执行完所有事件后,就会重新恢复操作。

{

performStep(step);

++step;

}

else

{

QTableWidget::timerEvent(event);

}

}

}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值