qt事件机制及使用

Qt事件机制,使用

对以下链接的整理笔记

使用https://blog.csdn.net/qq_29912325/article/details/117767972?app_version=5.6.0&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22117767972%22%2C%22source%22%3A%22qq_35667743%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app

https://blog.csdn.net/weixin_42837024/article/details/119669713?app_version=5.6.0&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22119669713%22%2C%22source%22%3A%22qq_35667743%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app

源码分析https://blog.csdn.net/fanyun_01/article/details/79604344

https://www.cnblogs.com/newjiang/p/8419693.html#:~:text=Qt%E4%B8%AD%2C%E4%BA%8B%E4%BB%B6%E7%9A%84%E6%B4%BE%E5%8F%91%E6%98%AF%E4%BB%8EQApplication%3A%3Anotify%20%28%29%20%E5%BC%80%E5%A7%8B%E7%9A%84%2C,%E5%9B%A0%E4%B8%BAQAppliction%E4%B9%9F%E6%98%AF%E7%BB%A7%E6%89%BF%E8%87%AAQObject%2C%20%E6%89%80%E4%BB%A5%E5%85%88%E6%A3%80%E6%9F%A5QAppliation%E5%AF%B9%E8%B1%A1%2C%20%E5%A6%82%E6%9E%9C%E6%9C%89%E4%BA%8B%E4%BB%B6%E8%BF%87%E6%BB%A4%E5%99%A8%E5%AE%89%E8%A3%85%E5%9C%A8qApp%E4%B8%8A%2C%20%E5%85%88%E8%B0%83%E7%94%A8%E8%BF%99%E4%BA%9B%E4%BA%8B%E4%BB%B6%E8%BF%87%E6%BB%A4%E5%99%A8.

机制

qt的事件从a.exec()启动事件循环开始

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
 
    return a.exec();
}

由notify函数派发给焦点部件
[virtual] bool QCoreApplication::notify(QObject *receiver, QEvent *event)
Sends event to receiver: receiver->event(event). Returns the value that is returned from the receiver’s event handler. Note that this function is called for all events sent to any object in any thread.
For certain types of events (e.g. mouse and key events), the event will be propagated to the receiver’s parent and so on up to the top-level object if the receiver is not interested in the event (i.e., it returns false).
There are five different ways that events can be processed; reimplementing this virtual function is just one of them. All five approaches are listed below:
将事件发送给接收方:receiver->event(event)。返回从接收器的事件处理程序返回的值。请注意,对于发送到任何线程中任何对象的所有事件,都会调用此函数。

对于某些类型的事件(例如鼠标和按键事件),如果接收方对事件不感兴趣(即返回false),则事件将传播到接收方的父对象,以此类推,直到顶层对象。

QCoreApplication::postEvent和QCoreApplication::SendEvent最终调用了QCoreApplication::notify函数,然后由QCoreApplication::notify去派发消息,

Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环. 该循环可以简化的描述为如下的代码:

while ( !app_exit_loop )

{

  while( !postedEvents ) { processPostedEvents() }

  while( !qwsEvnts ){ qwsProcessEvents();  }

  while( !postedEvents ) { processPostedEvents() }

}

先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息, 直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理.
调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节.
以下是源码

int QCoreApplication::exec()  
{  
    if (!QCoreApplicationPrivate::checkInstance("exec"))  
        return -1;  
    //获取线程数据  
    QThreadData *threadData = self->d_func()->threadData;  
    //判断是否在主线程创建  
    if (threadData != QThreadData::current()) {  
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());  
        return -1;  
    }  
    //判断eventLoop是否已经创建  
    if (!threadData->eventLoops.isEmpty()) {  
        qWarning("QCoreApplication::exec: The event loop is already running");  
        return -1;  
    }  
    threadData->quitNow = false;  
    QEventLoop eventLoop;  
    self->d_func()->in_exec = true;  
    //创建eventLoop  
    int returnCode = eventLoop.exec();  
    threadData->quitNow = false;  
    if (self) {  
        self->d_func()->in_exec = false;  
        //退出程序  
        emit self->aboutToQuit();  
        sendPostedEvents(0, QEvent::DeferredDelete);  
    }  
    return returnCode;  
}  
再来到qeventloop.cpp中。  
int QEventLoop::exec(ProcessEventsFlags flags)  
{  
    Q_D(QEventLoop);  
    if (d->threadData->quitNow)  
        return -1;  
    //已经调用过exec了。  
    if (d->inExec) {  
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);  
        return -1;  
    }  
    d->inExec = true;  
    d->exit = false;  
    ++d->threadData->loopLevel;  
    //将事件类对象压入线程结构体中  
    d->threadData->eventLoops.push(this);  
    // remove posted quit events when entering a new event loop  
    // 这句不用翻译了把!  
    if (qApp->thread() == thread())  
        QCoreApplication::removePostedEvents(qApp, QEvent::Quit);  
#if defined(QT_NO_EXCEPTIONS)  
    while (!d->exit)  
        //这里才是关键,我们还要继续跟踪进去。  
        processEvents(flags | WaitForMoreEvents);  
#else  
    try {  
        while (!d->exit)  
            processEvents(flags | WaitForMoreEvents);  
    } catch (...) {  
        //如果使用了EXCEPTION,则继续对下一条时间进行处理。  
        qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"  
                 "exceptions from an event handler is not supported in Qt. You must\n"  
                 "reimplement QApplication::notify() and catch all exceptions there.\n");  
        throw;  
    }  
#endif  
    //退出eventloop前,将时间对象从线程结构中取出。  
    QEventLoop *eventLoop = d->threadData->eventLoops.pop();  
    Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");  
    Q_UNUSED(eventLoop); // --release warning  
 
    d->inExec = false;  
    --d->threadData->loopLevel;  
    //退出事件循环。  
    return d->returnCode;  
}  
 
来到了processEvents函数:  
bool QEventLoop::processEvents(ProcessEventsFlags flags)  
{  
    Q_D(QEventLoop);  
    //判断事件分派器是否为空。  
    if (!d->threadData->eventDispatcher)  
        return false;  
    if (flags & DeferredDeletion)  
        QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);  
    //调用不同平台下的事件分派器来处理事件。  
    return d->threadData->eventDispatcher->processEvents(flags);  
}  
processEvents是在QAbstractEventDispatcher类中定义的纯虚方法。在QEventDispatcherWin32类有processEvents的实现。  
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)  
{  
    Q_D(QEventDispatcherWin32);  
    //内部数据创建。registerClass注册窗口类,createwindow创建窗体。  
    //注册socket notifiers,启动所有的normal timers  
    if (!d->internalHwnd)  
        createInternalHwnd();  
    d->interrupt = false;  
    emit awake();  
 
    bool canWait;  
    bool retVal = false;  
    do {  
        QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);  
        DWORD waitRet = 0;  
        HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];  
        QVarLengthArray<MSG> processedTimers;  
        while (!d->interrupt) {  
            DWORD nCount = d->winEventNotifierList.count();  
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);  
            MSG msg;  
            bool haveMessage;  
            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {  
                // process queued user input events处理用户输入事件,放入队列中。  
                haveMessage = true;  
                msg = d->queuedUserInputEvents.takeFirst();  
            } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {  
                // process queued socket events  处理socket事件,放入队列中。  
                haveMessage = true;  
                msg = d->queuedSocketEvents.takeFirst();  
            } else {  
                //从消息队列中取消息,同PeekMessage  
                haveMessage = winPeekMessage(&msg, 0, 0, 0, PM_REMOVE);  
                if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)  
                    && ((msg.message >= WM_KEYFIRST  
                         && msg.message <= WM_KEYLAST)  
                        || (msg.message >= WM_MOUSEFIRST  
                            && msg.message <= WM_MOUSELAST)  
                        || msg.message == WM_MOUSEWHEEL)) {  
                    // queue user input events for later processing  
                    haveMessage = false;  
                    d->queuedUserInputEvents.append(msg);  
                }  
                if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)  
                    && (msg.message == WM_USER && msg.hwnd == d->internalHwnd)) {  
                    // queue socket events for later processing  
                    haveMessage = false;  
                    d->queuedSocketEvents.append(msg);  
                }  
            }  
            if (!haveMessage) {  
                // no message - check for signalled objects  
                for (int i=0; i<(int)nCount; i++)  
                    pHandles[i] = d->winEventNotifierList.at(i)->handle();  
                //注册signal--slot。  
                waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);  
                if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {  
                    // a new message has arrived, process it  
                    continue;  
                }  
            }  
            //事件队列中有事件需要处理。  
            if (haveMessage) {   
                //处理timer事件  
                if (msg.message == WM_TIMER) {  
                    // avoid live-lock by keeping track of the timers we've already sent  
                    bool found = false;  
                    for (int i = 0; !found && i < processedTimers.count(); ++i) {  
                        const MSG processed = processedTimers.constData()[i];  
                        found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);  
                    }  
                    if (found)  
                        continue;  
                    processedTimers.append(msg);  
                } else if (msg.message == WM_QUIT) {  
                    if (QCoreApplication::instance())  
                        QCoreApplication::instance()->quit();  
                    return false;  
                }  
                //消息分发处理。  
                if (!filterEvent(&msg)) {  
                    TranslateMessage(&msg);  
                    QT_WA({  
                        DispatchMessage(&msg);  
                    } , {  
                        DispatchMessageA(&msg);  
                    });  
                }  
            } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {  
                //处理signal--slot  
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));  
            } else {  
                // nothing todo so break  
                break;  
            }  
            retVal = true;  
        }  
        // still nothing - wait for message or signalled objects  
        QThreadData *ddata = d->threadData;  
        canWait = (!retVal  
                   && data->canWait  
                   && !d->interrupt  
                   && (flags & QEventLoop::WaitForMoreEvents));  
        if (canWait) {  
            DWORD nCount = d->winEventNotifierList.count();  
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);  
            for (int i=0; i<(int)nCount; i++)  
                pHandles[i] = d->winEventNotifierList.at(i)->handle();  
            emit aboutToBlock();  
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);  
            emit awake();  
            if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {  
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));  
                retVal = true;  
            }  
        }  
    } while (canWait);  
    return retVal;  
}

notify是通过Q_Qbject对象树找到焦点部件派发事件的

Q_Object源码分析
blog.csdn.net/weixin_39609623/article/details/82966753
自定义部件想要使用qt事件机制需要声明Q_Object宏

class  
#if defined(__INTEL_COMPILER) && defined(Q_OS_WIN)  
Q_CORE_EXPORT  
#endif  
QObjectData {  
public:  
  virtual ~QObjectData() = 0;  
  QObject *q_ptr;                               // 当前指向的QOBject  
  QObject *parent;                              // 指向的QObject父类   
  QObjectList children;                         // 孩儿们  
  uint isWidget : 1;                                // 是否为widget的标记   
  uint pendTimer : 1;                           // 开启时钟  
  uint blockSig : 1;                                // 阻塞信号标记  
  uint wasDeleted : 1;                          // 是否参数标记  
  uint ownObjectName : 1;                       // 是否含有对象名标记  
  uint sendChildEvents : 1;                     // 发送到子对象时间标记  
  uint receiveChildEvents : 1;                  // 接受子对象时间标记  
  uint inEventHandler : 1;                      // 是否有事件句柄标记  
  uint inThreadChangeEvent : 1;                 // 线程更改时间标记  
  //是否有守护标记  
  uint hasGuards : 1; //true iff there is one or more QPointer attached to this object  
  uint unused : 22;                             // 保留  
  int postedEvents;                             // 发送的数据  
  QMetaObject *metaObject; // assert dynamic            // 元对象  
};  
        QObjectData保留一些了基本的数据信息

声明Q_Object宏会创建一个指针绑定自己的父指针并创建一个子控件列表存储其他子控件

这样的树形结构把所有对象都绑在了一棵树上,实现了事件的派发

#include<iostream>
using namespace std;
#include<vector>

//qt事件派发工作机理Q

class mypushbutton1;
class mypushbutton2;
class pusbutton;

class widget {
public:
    vector<void*>childqueue;
    //用来记录子控件指针
    virtual void exec();
};
class mywidget :public widget {
public:
    mywidget();
};
class pusbutton {
public:
    pusbutton() {};
    pusbutton(mywidget*parent) {
        //通过传进来的这个指针调用
        //好像是通过Q_object把自己挂载到父类上的
        parent->childqueue.push_back(this);
        //这样父类可通过q_object给子控件分发事件
    }
    //virtual void event();
    virtual void event();
};
class mypushbutton1 :public pusbutton {
public:
    mypushbutton1(mywidget* parent) {
        //通过传进来的这个指针调用
        //好像是通过Q_object把自己挂载到父类上的
        parent->childqueue.push_back(this);
        //这样父类可通过q_object给子控件分发事件
    }
    void event();
};
class mypushbutton2 :public pusbutton {
public:
    mypushbutton2(mywidget* parent ) {
        //通过传进来的这个指针调用
        //好像是通过Q_object把自己挂载到父类上的
        parent->childqueue.push_back(this);
        //这样父类可通过q_object给子控件分发事件
    }

    void event();

};
void widget::exec() {
    //循环给chidque里分发事件即调用event函数
    for (auto a : this->childqueue) {
        static_cast<pusbutton*>(a)->event();//父指针可以调用子类对象的虚函数
    }
}
mywidget::mywidget() {
    mypushbutton1* myp1 = new mypushbutton1(this);
    mypushbutton2* myp2 = new mypushbutton2(this);
    this->exec();
}
void pusbutton::event() { cout << "pushbutton" << endl; };
void mypushbutton1::event() { cout << "mypushbutton1" << endl; };
void mypushbutton2::event() { cout << "mypushbutton2" << endl; };
int main() {
    mywidget mywt;
}

使用

如果我们使用组件,我们关心的是信号槽;如果我们自定义组件,我们关心的是事件。因为我们可以通过事件来改变组件的默认操作。比如,如果我们要自定义一个 QPushButton,那么我们就需要重写它的鼠标点击事件和键盘处理事件,并且在恰当的时候发出 clicked()信号。

   Qt的事件传递方向:先传递给获得焦点的部件,只有在此部件忽略掉了该事件,才会传递给这个部件的父部件。重新实现事件处理函数的时候,要调用父类的相应事件处理函数来实现默认操作。
void MyLabel::mousePressEvent(QMouseEvent * event)
{
        if(event->button() == Qt::LeftButton) {
                // 处理该事件
        } else {
                QLabel::mousePressEvent(event);//忽略掉该事件并调用父事件处理函数把事件传递给父对象
        }
}

我们可以把 Qt 的事件传递看成链状:如果子类没有处理这个事件,就会继续向其他类传递。其实,Qt的事件对象都有一个 accept()函数和 ignore()函数。正如它们的名字,前者用来告诉 Qt,事件处理函数“接收”了这个事件,不要再传递;后者则告诉 Qt,事件处理函数“忽略”了这个事件,需要继续传递,寻找另外的接受者。在事件处理函数中,可以使用 isAccepted()来查询这个事件是不是已经被接收了。

事实上,我们很少使用 accept()和 ignore()函数,而是像上面的示例一样,如果希望忽略事件,只要调用父类的响应函数即可。

Qt 中的事件大部分是 protected 的,因此,重写的函数必定存在着其父类中的响应函数,这个方法是可行的。为什么要这么做呢?因为我们无法确认父类中的这个处理函数没有操作,如果我们在子类中直接忽略事件,Qt 不会再去寻找其他的接受者,那么父类的操作也就不能进行,这可能会有潜在的危险。

在一个情形下,我们必须使用 accept()和 ignore()函数,那就是在窗口关闭的时候。如果你在窗口关闭时需要有个询问对话框,那么就需要这么去写:

void MainWindow::closeEvent(QCloseEvent * event)
{
        if(continueToClose()) {
                event->accept();
        } else {
                event->ignore();
        }
}

bool MainWindow::continueToClose()
{
        if(QMessageBox::question(this,
                                            tr("Quit"),
                                            tr("Are you sure to quit this application?"),
                                            QMessageBox::Yes | QMessageBox::No,
                                            QMessageBox::No)
                == QMessageBox::Yes) {
                return true;
        } else {
                return false;
        }
}


这样,我们经过询问之后才能正常退出程序。

event()函数
事件对象创建完毕后,Qt 将这个事件对象传递给 QObject的 event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。

event()函数主要用于事件的分发,所以,如果你希望在事件分发之前做一些操作,那么,就需要注意这个 event()函数了。为了达到这种目的,我们可以重写 event()函数。

例如,如果你希望在窗口中的tab 键按下时将焦点移动到下一组件,而不是让具有焦点的组件处理,那么你就可以继承 QWidget,并重写它的 event()函数,已达到这个目的:

bool MyWidget::event(QEvent *event) {
        if (event->type() == QEvent::KeyPress) {
                QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
                if (keyEvent->key() == Qt::Key_Tab) {
                        // 处理 Tab 鍵
                        return true;
                }
        }
        return QWidget::event(event);
}

event()函数接受一个 QEvent 对象,也就是需要这个函数进行转发的对象。为了进行转发,必定需要有一系列的类型判断,这就可以调用 QEvent 的 type()函数,其返回值是 QEvent::Type 类型的枚举。

我们处理过自己需要的事件后,可以直接 return 回去,对于其他我们不关心的事件,需要调用父类的 event()函数继续转发,否则这个组件就只能处理我们定义的事件了。

event()函数返回值是 bool 类型,如果传入的事件已被识别并且处理,返回 true,否则返回 false。如果返回值是 true,QApplication 会认为这个事件已经处理完毕,会继续处理事件队列中的下一事件;如果返回值是 false,QApplication 会尝试寻找这个事件的下一个处理函数。

event()函数的返回值和事件的 accept()和 ignore()函数不同。accept()和ignore()函数用于不同的事件处理器之间的沟通,例如判断这一事件是否处理;event()函数的返回值主要是通知QApplication 的 notify()函数是否处理下一事件。

为了更加明晰这一点,我们来看看 QWidget 的event()函数是如何定义的:

bool QWidget::event(QEvent *event) {
        switch (e->type()) {
        case QEvent::KeyPress:
                 keyPressEvent((QKeyEvent *)event);
                if (!((QKeyEvent *)event)->isAccepted())
                        return false;
                break;
        case QEvent::KeyRelease:
                keyReleaseEvent((QKeyEvent *)event);
                if (!((QKeyEvent *)event)->isAccepted())
                        return false;
                break;
                // more...
        }
        return true;//事件被当前对象处理了返回true,如果为false会将事件重新加入队列
}

6.事件过滤器
Qt 创建了 QEvent 事件对象之后,会调用 QObject 的 event()函数做事件的分发。有时候,你可能需要在调用 event()函数之前做一些另外的操作,比如,对话框上某些组件可能并不需要响应回车按下的事件,此时,你就需要重新定义组件的 event()函数。如果组件很多,就需要重写很多次 event()函数,这显然没有效率。为此,你可以使用一个事件过滤器,来判断是否需要调用 event()函数。

QOjbect 有一个 eventFilter()函数,用于建立事件过滤器。这个函数的签名如下:
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event )
如果 watched 对象安装了事件过滤器,这个函数会被调用并进行事件过滤,然后才轮到组件进行事件处理。在重写这个函数时,如果你需要过滤掉某个事件,例如停止对这个事件的响应,需要返回 true。

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
 {
         if (obj == textEdit) {
                 if (event->type() == QEvent::KeyPress) {
                         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
                         qDebug() << "Ate key press" << keyEvent->key();
                         return true;
                 } else {
                         return false;
                 }
         } else {
                 // pass the event on to the parent class
                 return QMainWindow::eventFilter(obj, event);
         }
 }

上面的例子中为 MainWindow 建立了一个事件过滤器。为了过滤某个组件上的事件,首先需要判断这个对象是哪个组件,然后判断这个事件的类型。

例如,我不想让 textEdit 组件处理键盘事件,于是就首先找到这个组件,如果这个事件是键盘事件,则直接返回 true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回 false。对于其他组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数。

在创建了过滤器之后,下面要做的是安装这个过滤器。安装过滤器需要调用 installEventFilter()函数。这个函数的签名如下:
void QObject::installEventFilter ( QObject * filterObj )
这个函数是 QObject 的一个函数,因此可以安装到任何 QObject 的子类,并不仅仅是 UI 组件。这个函数接收一个 QObject 对象,调用了这个函数安装事件过滤器的组件会调用 filterObj 定义的eventFilter()函数。

例如,textField.installEventFilter(obj),则如果有事件发送到textField 组件是,会先调用 obj->eventFilter()函数,然后才会调用 textField.event()。

当然,你也可以把事件过滤器安装到 QApplication 上面,这样就可以过滤所有的事件,已获得更大的控制权。不过,这样做的后果就是会降低事件分发的效率。

如果一个组件安装了多个过滤器,则最后一个安装的会最先调用,类似于堆栈的行为。

注意: 如果你在事件过滤器中 delete 了某个接收组件,务必将返回值设为 true。否则,Qt 还是会将事件分发给这个接收组件,从而导致程序崩溃。

事件过滤器和被安装的组件必须在同一线程,否则,过滤器不起作用。另外,如果在 install 之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

事件的调用最终都会调用 QCoreApplication 的 notify()函数,因此,最大的控制权实际上是重写QCoreApplication 的 notify()函数。由此可以看出,Qt 的事件处理实际上是分层五个层次:

重定义事件处理函数
重定义 event()函数
为单个组件安装事件过滤器
为 QApplication 安装事件过滤器
重定义 QCoreApplication 的 notify()函数
这几个层次的控制权是逐层增大的。

7.自定义事件
Qt 允许创建自己的事件类型,这在多线程的程序中尤其有用,当然,也可以用在单线程的程序中,作为一种对象间通讯的机制。那么,为什么需要使用事件,而不是使用信号槽呢?主要原因是,事件的分发既可以是同步的,又可以是异步的,而函数的调用或者说是槽的回调总是同步的。事件的另外一个好处是,它可以使用过滤器。

Qt 中的自定义事件很简单,同其他类似的库的使用很相似,都是要继承一个类进行扩展。在 Qt 中,你需要继承的类是 QEvent。

继承 QEvent 类,你需要提供一个QEvent::Type 类型的参数,作为自定义事件的类型值。这里的QEvent::Type 类型是 QEvent 里面定义的一个 enum,因此,你是可以传递一个 int 的。重要的是,你的事件类型不能和已经存在的 type 值重复,否则会有不可预料的错误发生!因为系统会将你的事件当做系统事件进行派发和调用。

在 Qt 中,系统将保留0 - 999的值,也就是说,你的事件 type 要大于999. 具体来说,你的自定义事件的 type 要在 QEvent::User 和 QEvent::MaxUser 的范围之间。其中,QEvent::User 值是1000,QEvent::MaxUser 的值是65535。从这里知道,你最多可以定义64536个事件,相信这个数字已经足够大了!

但是,即便如此,也只能保证用户自定义事件不能覆盖系统事件,并不能保证自定义事件之间不会被覆盖。为了解决这个问题,Qt 提供了一个函数:registerEventType(),用于自定义事件的注册。该函数签名如下:
static int QEvent::registerEventType ( int hint = -1 );
函数是 static 的,因此可以使用 QEvent 类直接调用。函数接受一个 int 值,其默认值为-1,返回值是创建的这个 Type 类型的值。如果 hint 是合法的,不会发生任何覆盖,则会返回这个值;如果hint 不合法,系统会自动分配一个合法值并返回。因此,使用这个函数即可完成 type 值的指定。这个函数是线程安全的,因此不必另外添加同步。

你可以在 QEvent 子类中添加自己的事件所需要的数据,然后进行事件的发送。Qt 中提供了两种发送方式:
static bool QCoreApplication::sendEvent(QObjecy receiver, QEvent event):事件被 QCoreApplication 的 notify()函数直接发送给 receiver 对象,返回值是事件处理函数的返回值。使用这个函数必须要在栈上创建对象,例如:

QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
QApplication::sendEvent(mainWindow, &event);

static bool QCoreApplication::postEvent(QObject receiver, QEvent event):事件被 QCoreApplication 追加到事件列表的最后,并等待处理,该函数将事件追加后会立即返回,并且注意,该函数是线程安全的。另外一点是,使用这个函数必须要在堆上创建对象,例如:

QApplication::postEvent(object, new MyEvent(QEvent::registerEventType(2048)));

这个对象不需要手动 delete,Qt 会自动 delete 掉!因此,如果在 post 事件之后调用 delete,程序可能会崩溃。另外,postEvent()函数还有一个重载的版本,增加一个优先级参数,具体请参见API。通过调用 sendPostedEvent()函数可以让已提交的事件立即得到处理。
如果要处理自定义事件,可以重写 QObject 的 customEvent()函数,该函数接收一个 QEvent 对象作为参数。可以像前面介绍的重写 event()函数的方法去重写这个函数:

void CustomWidget::customEvent(QEvent *event) {
        CustomEvent *customEvent = static_cast<CustomEvent *>(event);
        // ....
}

另外,你也可以通过重写 event()函数来处理自定义事件:

bool CustomWidget::event(QEvent *event) {
        if (event->type() == MyCustomEventType) {
                CustomEvent *myEvent = static_cast<CustomEvent *>(event);
                // processing...
                return true;
        }
        return QWidget::event(event);
}

这两种办法都是可行的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值