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);
}
}
}