• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            原文Another Look at Events 
            作者: Jasmin Blanchette  譯:清源游民 gameogre@gmail.com

            什么是自發(fā)事件?哪些類(lèi)型的事件可以被propagated 或compressed? posting and sending 事件之間有何不同?什么時(shí)候應(yīng)該調(diào)用 accept() 或是ignore() ? 如果這些問(wèn)題你還不是很了解,那么繼續(xù)看下去。

            事件起源:

            基于事件如何被產(chǎn)生與分發(fā),可以把事件分為三類(lèi):
            * Spontaneous 事件,由窗口系統(tǒng)產(chǎn)生,它們被放到系統(tǒng)隊(duì)列中,通過(guò)事件循環(huán)逐個(gè)處理。
            * Posted 事件,由Qt或是應(yīng)用程序產(chǎn)生,它們被Qt組成隊(duì)列,再通過(guò)事件循環(huán)處理。
            * Sent  事件,由Qt或是應(yīng)用程序產(chǎn)生,但它們被直接發(fā)送到目標(biāo)對(duì)象。
            當(dāng)我們?cè)趍ain()函數(shù)的末尾調(diào)用QApplication::exec()時(shí),程序進(jìn)入了Qt的事件循環(huán),大概來(lái)講,事件循環(huán)如下面所示:
            while (!exit_was_called)
            {
              while(!posted_event_queue_is_empty)
                   {
                     process_next_posted_event();
                   }
              while(!spontaneous_event_queue_is_empty)
                  {
                     process_next_spontaneous_event();
                  }
              while(!posted_event_queue_is_empty)
                  {
                    process_next_posted_event();
                  }
            }
            首先,事件循環(huán)處理所有的posted事件,直到隊(duì)列空。然后再處理所有的spontaneous事件,最后它處理所有的因?yàn)樘幚韘pontaneous事件而產(chǎn)生的posted事件。send 事件并不在事件循環(huán)內(nèi)處理,它們都直接被發(fā)送到了目標(biāo)對(duì)象。現(xiàn)在看一下實(shí)踐中的paint 事件是如何工作的。當(dāng)一個(gè)widget第一次可見(jiàn),或是被遮擋后再次變?yōu)榭梢?jiàn),
            窗口系統(tǒng)產(chǎn)生一個(gè)(spontaneous) paint事件,要求程序重畫(huà)widget,事件循環(huán)最終從事件隊(duì)列中撿選這個(gè)事件并把它分發(fā)到那個(gè)需要重畫(huà)的widget。
            并不是所有的paint事件都是由窗口系統(tǒng)產(chǎn)生的。當(dāng)你調(diào)用QWidget::update()去強(qiáng)行重畫(huà)widget,這個(gè)widget會(huì)post 一個(gè)paint 事件給自己。這個(gè)paint事件被放入隊(duì)列,最終被事件循環(huán)分發(fā)之。
            假如你很不耐煩,等不及事件循環(huán)去重畫(huà)一個(gè)widget, 理論上,你應(yīng)該直接調(diào)用paintEvent()強(qiáng)制進(jìn)行立即的重畫(huà)。但實(shí)際上這不總是可行的,因?yàn)閜aintEvent()函數(shù)是protected的(很可能訪問(wèn)不了)。它也繞開(kāi)了任何存在的事件過(guò)濾器。因?yàn)檫@些原因,Qt提供了一個(gè)機(jī)制,直接sending事件而不是posting 。
            QWidget::repaint()就使用了這個(gè)機(jī)制來(lái)強(qiáng)制進(jìn)行立即重畫(huà)。
            posting 相對(duì)于sending的一個(gè)優(yōu)勢(shì)是,它給了Qt一個(gè)壓縮(compress)事件的機(jī)會(huì)。假如你在一個(gè)widget上連續(xù)地調(diào)用update() 十次,因update()而產(chǎn)生的這十個(gè)事件,將會(huì)自動(dòng)地被合并為一個(gè)單獨(dú)的事件,但是QPaintEvents事件附帶的區(qū)域信息也合并了。可壓縮的事件類(lèi)型包括:paint,move,resize,layout hint,language change。
            最后要注意,你可以在任何時(shí)候調(diào)用QApplication::sendPostedEvent(),強(qiáng)制Qt產(chǎn)生一個(gè)對(duì)象的posted事件。

            人工合成的事件

            QT應(yīng)用程序可以產(chǎn)生他們自己的事件,或是預(yù)定義類(lèi)型,或是自定義類(lèi)型。 這可以通過(guò)創(chuàng)建QEvent類(lèi)或它的
            子類(lèi)的實(shí)例,并且調(diào)用QApplication:postEvent()或QApplication::sendEvent()來(lái)實(shí)現(xiàn)。
            這兩個(gè)函數(shù)需要一個(gè) QObject* 與一個(gè)QEvent * 作為參數(shù),假如你調(diào)用postEvent(),你必須用 new 操作符來(lái)創(chuàng)建事件對(duì)象,Qt會(huì)它被處理后幫你刪除它。假如你用sendEvent(), 你應(yīng)該在棧上來(lái)創(chuàng)建事件。下面舉兩個(gè)例子:
            一是posting 事件:
            QApplication::postEvent(mainWin, new QKeyEvent(QEvent::KeyPress,Key_X,'X',0));
            二是sending 事件:
                QKeyEvent event(QEvent::KeyPress, Key_X, 'X', 0);
                QApplication::sendEvent(mainWin, &event);
            Qt應(yīng)用程序很少直接調(diào)用postEvent()或是sendEvnet(),因?yàn)榇蠖鄶?shù)事件會(huì)在必要時(shí)被Qt或是窗口系統(tǒng)自動(dòng)產(chǎn)生
            。在大多數(shù)的情況下,當(dāng)你想發(fā)送一個(gè)事件時(shí),Qt已經(jīng)為了準(zhǔn)備好了一個(gè)更高級(jí)的函數(shù)來(lái)為你服務(wù)。(例如
            update()與repaint())。

            定制事件類(lèi)型

            qt允許你創(chuàng)建自己的事件類(lèi)型,這在多線(xiàn)程的程序中尤其有用。在單線(xiàn)程的程序也相當(dāng)有用,它可以作為
            對(duì)象間的一種通訊機(jī)制。為什么你應(yīng)該用事件而不是其他的標(biāo)準(zhǔn)函數(shù)調(diào)用,或信號(hào)、槽的主要原因是:事件既可用于同步也可用于異步(依賴(lài)于你是調(diào)用sendEvent()或是postEvents()),函數(shù)調(diào)用或是槽調(diào)用總是同步的。事件的另外一個(gè)好處是它可以被過(guò)濾。
            演示如何post一個(gè)定制事件的代碼片段:
            const QEvent::Type MyEvent = (QEvent::Type)1234;
              ...
            QApplication::postEvent(obj, new QCustomEvent(MyEvent));
            事件必須是QCustomEvent類(lèi)型(或子類(lèi))的。構(gòu)造函數(shù)的參數(shù)是事件的類(lèi)型,1024以下被Qt保留。其他可被程序使用。為處理定制事件類(lèi)型,要重新實(shí)現(xiàn)customEvent()函數(shù):
            void MyLineEdit::customEvent(QCustomEvent *event)
                {
                    if (event->type() == MyEvent) {
                        myEvent();
                    } else {
                        QLineEdit::customEvent(event);
                    }
                }
            QcustomEvent類(lèi)有一個(gè)void *的成員,可用于特定的目的。你也可以子類(lèi)化QCustomEvent,加上別的成員,但是你也需要在customEvent()中轉(zhuǎn)換QCustomeEvent到你特有的類(lèi)型。

            事件處理與過(guò)濾

            Qt中的事件可以在五個(gè)不同的層次上被處理
            1,重新實(shí)現(xiàn)一個(gè)特定的事件handler
             QObjectQWidget提供了許多特定的事件handlers,分別對(duì)應(yīng)于不同的事件類(lèi)型。(如paintEvent()對(duì)應(yīng)paint事件)
            2,重新實(shí)現(xiàn)QObject::event()
             event()函數(shù)是所有對(duì)象事件的入口,QObject和QWidget中缺省的實(shí)現(xiàn)是簡(jiǎn)單地把事件推入特定的事件handlers。
            3,在QObject安裝上事件過(guò)濾器
              事件過(guò)濾器是一個(gè)對(duì)象,它接收別的對(duì)象的事件,在這些事件到達(dá)指定目標(biāo)之間。
            4,在aApp上安裝一個(gè)事件過(guò)濾器,它會(huì)監(jiān)視程序中發(fā)送到所有對(duì)象的所有事件
            5,重新實(shí)現(xiàn)QApplication:notify(),Qt的事件循環(huán)與sendEvent()調(diào)用這個(gè)函數(shù)來(lái)分發(fā)事件,通過(guò)重寫(xiě)它,你可以在別人之前看到事件。

            一些事件類(lèi)型可以被傳遞。這意味著假如目標(biāo)對(duì)象不處理一個(gè)事件,Qt會(huì)試著尋找另外的事件接收者。用新的目標(biāo)來(lái)調(diào)用QApplication::notify()。舉例來(lái)講,key事件是傳遞的,假如擁有焦點(diǎn)的Widget不處理特定鍵,Qt會(huì)分發(fā)相同的事件給父widget,然后是父親的父親,直到最頂層widget。

            接受或是忽略?

            可被傳遞的事件有一個(gè)accept()函數(shù)和一個(gè)ignore()函數(shù),你可以用它們來(lái)告訴Qt,你“接收”或是
            “忽略”這個(gè)事件。假如事件handler調(diào)用accept(),這個(gè)事件將不會(huì)再被傳遞。假如事件handler調(diào)用
            ignore(),Qt會(huì)試著查找另外的事件接收者。
            像大多數(shù)的開(kāi)發(fā)者一樣,你可能不會(huì)被調(diào)用accept()或是ignore()所煩惱。缺省情況下是“接收”,在
            QWidget中的缺省實(shí)現(xiàn)是調(diào)用ignore(),假如你希望接收事件,你需要做的是重新實(shí)現(xiàn)事件handler,避免
            調(diào)用QWidget的實(shí)現(xiàn)。假如你想“忽略”事件,只需簡(jiǎn)單地傳遞它到QWidget的實(shí)現(xiàn)。下面的代碼演示了這一點(diǎn):
            void MyFancyWidget::keyPressEvent(QKeyEvent *event)
                {
                    if (event->key() == Key_Escape) {
                        doEscape();
                    } else {
                        QWidget::keyPressEvent(event);
                    }
                }
            在上面的例子里,假如用戶(hù)按了"ESC"鍵,我們會(huì)調(diào)用doEscape()并且事件被“接收”了(這是缺省的情況),
            事件不會(huì)被傳遞到父widget,假如用戶(hù)按了別的鍵,則調(diào)用QWidget的缺省實(shí)現(xiàn)。
            void QWidget::keyPressEvent(QKeyEvent *event)
                {
                    event->ignore();
                }
            應(yīng)該感謝ignore(),事件會(huì)被傳遞到父widget中去。
            討論到目前為至,我們都假設(shè)基類(lèi)是QWidget,然而,同樣的規(guī)則也可以應(yīng)用到別的層次中,只要用QWidget
            代替基類(lèi)即可。舉例來(lái)說(shuō):
             void MyFancyLineEdit::keyPressEvent(QKeyEvent *event)
                {
                    if (event->key() == Key_SysReq) {
                        doSystemRequest();
                    } else {
                        QLineEdit::keyPressEvent(event);
                    }
                }
            由于某些原因,你會(huì)在event()中處理事件,而不是在特定的handler中,如keyPressEvent(),這個(gè)過(guò)程會(huì)有些不同。event()會(huì)返回一個(gè)布爾值,來(lái)告訴調(diào)用者是否事件被accept或ignore,(true表示accept),從event()中調(diào)用accept()或是ignore()是沒(méi)有意義的。“Accept”標(biāo)記是event()與特定事件handler之間的一種通訊機(jī)制。而從event()返回的布爾值卻是用來(lái)與QApplication:notify()通訊的。在QWidgetk中缺省的event()實(shí)現(xiàn)是轉(zhuǎn)換“Accept”標(biāo)記為一個(gè)布爾值,如下所示:
            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;
                        ...
                    }
                    return true;
                }

            到現(xiàn)在為至,我們所說(shuō)的內(nèi)容不僅僅適用于key事件,也適用于mouse,wheel,tablet,context menu等事件
            Close事件有點(diǎn)不同,調(diào)用QCloseEvent:ignore()取消了關(guān)閉操作,而accept()告訴Qt繼續(xù)執(zhí)行正常的關(guān)閉操作。為了避免混亂,最好是在closeEvent()的新實(shí)現(xiàn)中明確地進(jìn)行accept()與ignore()的調(diào)用:
             void MainWindow::closeEvent(QCloseEvent *event)
                {
                    if (userReallyWantsToQuit()) {
                        event->accept();
                    } else {
                        event->ignore();
                    }
                }


             

            posted on 2007-06-13 22:42 清源游民 閱讀(6863) 評(píng)論(4)  編輯 收藏 引用 所屬分類(lèi): Qt

            FeedBack:
            # re: QT中的事件機(jī)制
            2007-06-14 00:17 | 黃大仙
            # re: QT中的事件機(jī)制
            2007-06-14 13:47 | eXile
            因?yàn)镾igal/Slot可以跨線(xiàn)程,還可以指定執(zhí)行的線(xiàn)程環(huán)境,,所以一般情況下沒(méi)有必要使用自定義事件。  回復(fù)  更多評(píng)論
              
            # re: QT中的事件機(jī)制
            2008-07-16 21:24 | NOTHING
            但了解一下事件也是好的,不是嗎  回復(fù)  更多評(píng)論
              
            # re: QT中的事件機(jī)制
            2008-09-11 14:00 | apple
            你好, 我有個(gè)問(wèn)題.
            因?yàn)槲易鲆粋€(gè)界面是沒(méi)有鼠標(biāo)的. 需要上下左右控制讓哪個(gè)wdiget來(lái)focus
            我想知道目前qt自帶的焦點(diǎn)機(jī)制是按add的順序來(lái)postevent的嗎?
            也就是按左/上方向鍵是聚焦當(dāng)前widget的上一個(gè)widget, 右/下是聚焦下一個(gè)嗎? 如何制定一個(gè)比較完善的焦點(diǎn)機(jī)制?
            比如說(shuō): 有20個(gè)widget按表格狀排列, 那么在中間的一個(gè)wdiget上, 我按向上鍵, 并不希望這個(gè)widget的左邊那個(gè)聚焦哦. 而是想讓這個(gè)widget上面的那個(gè)widget聚焦.
            十分感謝!  回復(fù)  更多評(píng)論
              
            <2009年5月>
            262728293012
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            留言簿(35)

            隨筆分類(lèi)(78)

            隨筆檔案(74)

            文章檔案(5)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            青草国产精品久久久久久| 久久青青草原精品国产软件| 久久亚洲精品成人av无码网站| 亚洲伊人久久精品影院| 99久久婷婷免费国产综合精品| 大美女久久久久久j久久| 无码人妻久久一区二区三区蜜桃 | 久久精品国产清自在天天线 | 久久国产美女免费观看精品| 99精品久久久久久久婷婷| 久久婷婷国产麻豆91天堂| 少妇人妻综合久久中文字幕| 四虎国产精品免费久久久| 久久强奷乱码老熟女网站 | 久久久久亚洲精品男人的天堂| 久久亚洲AV成人无码电影| 手机看片久久高清国产日韩| 99久久精品免费看国产一区二区三区| 亚洲国产日韩综合久久精品| 国产精品狼人久久久久影院| 国产一区二区三区久久精品| 国产A三级久久精品| 一97日本道伊人久久综合影院 | 国产毛片欧美毛片久久久| 久久天天躁狠狠躁夜夜2020老熟妇 | 欧美亚洲另类久久综合婷婷| 久久久精品免费国产四虎| 亚洲香蕉网久久综合影视| 久久夜色精品国产噜噜亚洲a| 久久最新免费视频| 久久久人妻精品无码一区| 国产福利电影一区二区三区久久老子无码午夜伦不 | 欧美性大战久久久久久| 色综合久久天天综线观看| 四虎亚洲国产成人久久精品| 久久国产视频99电影| 久久久人妻精品无码一区| 亚洲国产成人久久综合区| 久久91精品国产91| 性欧美丰满熟妇XXXX性久久久 | 久久精品国产清自在天天线|