在Qt中,感觉事件无处不在,只要用户进行了操作,就会发生事件,发出各种信号.就算用户没有操作,有时候也会有定时器在其中运作,当然定时器也是一种事件.
首先,我们要知道哪些属于事件?常见的事件主要有:绘图事件(paintEvent),定时器事件(timerEvent),进入事件(enterEvent),离开事件(leaveEvent),鼠标事件(QMouseEvent),键盘事件(QKeyEvent),焦点事件(QFocusEvent),关闭事件(closeEvent)等等.
这些事件全部都在QEvent这个事件类中被枚举.由于事件实在太多..所以有兴趣的可以去Qt 助手里去看看,去研究下…
每当用户进行一些操作时,都会发出一些信号,用来提示控件被用户所操作了.这就是发出信号.Qt中给我们提供了emit关键字来发出信号.像最常见的clicked()点击信号.就是通过底层的鼠标或键盘事件检测到,然后发出了这个信号.
每个控件都有属于自己的事件,窗口也是一个控件,它其实算是一个层把,当然也是有事件的.大部分事件都有相应的虚函数.当发生此事件时,会调用对应的虚函数方法.
说的挺多,没代码是肯定不行的,下面有一个小场景来教会我们如何自定义事件发生后的操作:
c::c(QWidget *parent)
: QWidget(parent)
{
//ui.setupUi(this);
//构建布局.
QHBoxLayout *layout = new QHBoxLayout(this);
m_lineEdit = new QLineEdit(this);
m_pushButton = new QPushButton(this);
layout->addWidget(m_lineEdit);
layout->addWidget(m_pushButton);
//设置填充黄色.
QPalette palette;
palette.setColor(QPalette::Base, Qt::yellow);
m_lineEdit->setPalette(palette);
}
void c::leaveEvent(QEvent *event)
{
//当鼠标离开后,变为红色.
QPalette palette;
palette.setColor(QPalette::Base, Qt::red);
m_lineEdit->setPalette(palette);
}
我们会发现,当鼠标离开窗口后,编辑框由黄色变成了红色,这样我们就实现了离开事件的重写,但要知道:该重写是的对象是整个窗口,而不是窗口中的某个控件…那么我们如何让鼠标离开编辑框时,让编辑框改变成蓝色呢?
这里主要有两种方法:
第一种:派生一个QLineEdit类,然后重写离开事件即可…
第二种:使用事件过滤器…
第一种方法:比较麻烦,但是当要重写的事件特别多的时候,使用比较好:
//重写一个QLineEdit的子类,
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
MyLineEdit(QWidget *parent);
~MyLineEdit();
protected:
void leaveEvent(QEvent *event);
};
MyLineEdit::MyLineEdit(QWidget *parent)
: QLineEdit(parent)
{
}
MyLineEdit::~MyLineEdit()
{
}
void MyLineEdit::leaveEvent(QEvent *event)
{
//重写离开事件即可.
QPalette palette;
palette.setColor(QPalette::Base, Qt::cyan);
this->setPalette(palette);
}
第二种方法:Qt也想到了如果一个个去派生类来实现自定义的事件,那也太麻烦了把..所以Qt有一个叫做事件过滤器的家伙.它可以用来监测控件的事件.用于目标对象的所有事件都会首先发送给这个对象的eventFilter()函数.
//让编辑框安装事件过滤器到当前窗口上.所以我们就要重写当前窗口的事件过滤器的方法.
m_lineEdit->installEventFilter(this);
bool c::eventFilter(QObject *o, QEvent *e)
{
if (o == m_lineEdit)
{
if (e->type() == QEvent::Leave)
{
QPalette palette;
palette.setColor(QPalette::Base, Qt::cyan);
m_lineEdit->setPalette(palette);
return true;
}
}
return QObject::eventFilter(o,e);
}
但是我们要注意:由于事件非常的多,很容易被触发,而事件又是根据发生的时间进入到事件队列中的,所以在时间上会有一些误差,但是如果在处理事件的过程中,花费过多的时间,这样就会造成堵塞,实际上程序明明是在处理事件,但用户却认为是程序卡死了..所以会造成程序的终止,讲这个的目的主要是说:处理事件不应该花费过长的时间,如果事件确实很长,比如用来保存很多图片,文件等,那么就创建其他线程来并行完成这个工作.
每个控件都有事件,那么当一个控件的事件发生时,如果事件在到达它的目标对象之前没有得到处理,或者也没有被他自己的目标对象处理,那么就会把这个事件传递给控件的父对象,如果还是没有处理,则继续传递到父对象的父对象,直到被处理或者到达最顶层的对象为止.
那么现在也有一个挺经典的场景.我们都知道QTextEdit这个Qt提供的多行文本编辑的类.当有文件拖拽进去的时候,会形成这个文件的当地路径.现在我们想要在窗口中对这个拖拽放下的事件进行处理.所以我们在窗口中重写了这几个事件…
但是仍会发现如果我们把文件拖拽进来还是形成文件的当地路径.这是为什么呢?这就要考虑到事件处理的顺序了.默认是层级关系处理事件的.也就是说:由于QTextEdit已经把拖拽的事件给处理掉了,所以当前窗口无法接收到这个事件了,所以不会进行处理,也处理不了啊,因为根本没接受到这个命令…
有很多方法解决这个问题:
最常见的方法是:调用QTextEdit的基类QWidget的一个setAcceptDrops()方法设置是否接受拖拽,也就是是否处理了.然后窗口对这个事件进行处理即可.
还有种方法就是:之前也讲到了事件过滤器可以把目标对象的所有时间首先发送给窗口,所以直接在窗口的事件过滤器中去判断事件,然后处理事件即可.
void c::dropEvent(QDropEvent *event)
{
//获取拖入的文件的路径.
QList<QUrl> s = event->mimeData()->urls();
QString filePath = s[0].toLocalFile();
//读取文件内容.
//记录新打开文件的路径.
QFile *file = new QFile(filePath);
bool ok = file->open(QIODevice::ReadOnly);
if (ok)
{
QTextStream stream(file);
this->setWindowTitle(getFileName() + QString::fromLocal8Bit(" - 记事本"));
m_textEdit->setPlainText(stream.readAll());
file->close();
}
}
当然每一个控件自身都带有一个bool event(QEvent *event);的虚函数方法.它也可以用来自定义或者覆盖自身的一些事件的处理方法.但是一般使用的不太多,它可以在事件到大特定的时间处理器之前处理他们.和事件过滤器相比,最显著的特点就是:这个只能处理自身的事件,而事件过滤器还可以处理其他的控件.