커뮤니티 | 큐티/임베디드/큐토피아 | PODO | SDL | 마이크로윈도우 | 리눅스프로그래밍 | 기타 | 포인트순위 | 최근게시물최근게시물 RSS  
korone.net에 다루었으면 하는것은요?
 
 
 
 
 
35
1,243
3,878
3,701,556
  현재접속자 : 56 (회원 0)






배너 신청 문의

 
작성일 : 14-10-09 09:52
[QT/Win32] Qt 에서의 이벤트 처리
 글쓴이 : devilqoo
조회 : 8,072   추천 : 0  
   http://kbdyj.tistory.com/entry/Qt-%EC%97%90%EC%84%9C-%EC%9D%B4%EB%B2%A… [1331]
티스토리에서 작성한 걸 옮겨오다 보니 보기는 나쁘지만 내용 이해에는 크게 지장이 없을 듯 하니 참고 봐주세요 ^^


Qt 에서 이벤트는 3가지 카테고리로 나눌 수 있다.

- Spontaneous 이벤트는 윈도우 시스템에 의해 생성된다. 이 이벤트는 시스템 큐에 들어간 후 이벤트 루프에서 차례대로 처리된다.
- Posted 이벤트들은 Qt 또는 응용 프로그램에 의해 생성된다. Qt에 의해 큐에 들어간 후 이벤트 루프에서 처리된다.
- Sent 이벤트들은 Qt 또는 응용 프로그램에 의해 생성된다. 이 이벤트들은 큐를 거치지 않고 직접 대상이 되는 오브젝트에 전달되어 즉시 처리된다.

우리가 흔히 Qt 용 프로젝트를 생성하면 main() 안에서 QApplication::exec()가 호출되는 것을 볼 수 있는 데 이때 비로소 응용프로그램은 이벤트 루프에 들어가게 된다.

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

    return a.exec(); // 이벤트 루프 시작
}

한마디로 이 함수가 호출 되기 전까지 응용프로그램은 유저의 입력을 받을 수 없다.
(예외적으로 QMessageBox 와 같은 Modal 위젯은 자체적으로 이벤트 루프를 실행하기 때문에 exec() 호출 전이라도 이벤트를 받을 수 있다.)
개념적으로 이벤트 루프는 다음과 같이 처리된다고 생각할 수 있다.

while ( !exit ) {
   while ( !posted_event_queue_is_empty) { ////////// (1)
      process_next_posted_event();
   }
   while ( !spontaneous_event_queue_is_empty) { ///////////// (2)
      process_next_spontaneous_event();
   }
   while ( !posted_event_qeueu_is_empty) { /////////////// (3)
      process_next_posted_event();
   }
}


먼저 (1) 에서 큐가 빌 때 까지 포스트 된 이벤트들이 처리된다.
처리가 완료되면 다음 단계(2)로 넘어가 역시 큐가 빌 때 까지 Spontaneous 이벤트들이 처리된다.
이때 이 이벤트들이 처리(Dispatch)되면서 새로운 이벤트들로 변환이 되어 다시 Post 큐에 들어가게 된다.
마지막으로(3) Spontaneous 이벤트들에 의해 다시 채워진 이벤트 큐를 처리하며 한번의 이벤트 루프를 완료하게 된다.

Sent 로 보내진 이벤트들은 즉시 처리되기 때문에 이벤트 루프에서는 존재하지 않게 된다.

페인트 이벤트 처리를 예로 들며 자세히 살펴 보도록 하자.
위젯이 만들어 지고 처음 Visible이 되거나 Hidden 상태에서 Visible이 될 때 윈도우 시스템은 프로그램에게 위젯을 redraw하기 위해 (Spontaneous) 페인트 이벤트를 생성하게 된다.
이벤트 루프에서 이 이벤트를 꺼내어 디스패치를 하면 위젯이 redraw 된다.

그런데 윈도우 시스템에 의해서만 페인트 이벤트가 발생하는 것은 아니다.
우리가 흔히 사용하는 update()를 호출하면 위젯은 그 자신에게 페인트 이벤트를 Post하여 큐에 집어 넣고 이후 이벤트 루프에서 디스패치 되게 된다.
(디스패치 되면 그 자신에게 QPaintEvent 객체를 생성하여 QWidget::paintEvent(QPaintEvent*)의 인자로 넘겨 호출할 것이다.)
물론 이벤트루프에서의 처리를 기다리지 않고 즉시 위젯을 redraw하고자 한다면 paintEvent()를 직접 호출하는 방법도 있다.
다만 이 맴버함수는 protected로 선언되었기 때문에 자신이 아닌 외부에서 호출은 불가능하다.
어쨌든 직접호출하게 되면 이벤트 필터에 의해 해당 이벤트가 걸러지는 부분도 피할 수 있다.

이런 이유로 Qt에서는 Post하지 않고 즉시 이벤트 처리를 하기 위한 매커니즘(Send event)을 만들어 놓았는데 QWidget::repaint()가 대표적이다.
Qt 소스를 들여다 보았지만 딱히 Send 되는 부분을 찾을 수 없었다. 좀 더 확인이 필요

그렇다면 Post 된 이벤트들이 즉시 처리되는 Send 이벤트에 비해 가지는 장점은 무엇일까?

이벤트를 Post 했을 때의 장점은 이벤트들을 최적화 할 수 있다는 것이다.

우리가 자주 사용하는 update()가 좋은 예가 되는 데 10번의 update()를 호출했다고 하면 이것들을 최적화 하여 갱신되는 영역을 union연산으로 합쳐서 하나로 만든 후 하나의 이벤트로 압축할 수 있다는 것이다.

paint, move, resize, layout hint, 그리고 language changed 와 같은 이벤트들이 이 범주에 들어간다.

이벤트 생성(Synthetic Events)

응용 프로그램은 직접 이벤트(객체)를 생성할 수도 있다.
QEvent 또는 이를 상속한 클래스의 인스턴스를 생성하고 QApplication::poseEvent(QObject *receiver, QEvent *e), QApplication::sendEvent(QObject *receiver, QEvent *e)를 호출해 처리 할 수 있는 것이다.
이 두함수들은 QObject와 QEvent 각각의 포인터를 인자로 받는다.

즉시 또는 지연 처리할 수 있느냐의 차이점 외에 호출 시의 차이점도 있는 데

postEvent()를 호출 할 때 QEvent 를 new로 생성하여 넘기면 이후의 이벤트 루프에서 삭제를 책임지게 된다.
sendEvent()는 즉시 호출되어 제어가 되돌아 오므로 스택에 이벤트 객체를 생성하면 된다.

[Post 방식]
QApplication::postEvent(mainWindow, new QKeyEvent(QEvent::KeyPress, Qt::Key_X, 'X', 0));

[Send 방식]
QKeyEvent e(QEvent::KeyPress, Qt::Key_X, 'X', 0);
QApplication::sendEvent(mainWindow, &e);

개발자가 postEvent(), sendEvent()를 호출하는 경우는 그렇게 많지 않다.
왜냐하면 대부분의 경우 Qt 또는 윈도우 시스템에 의해 필요 시 자동으로 생성되기 때문이다.

사용자 정의 이벤트 타입(Custom Event Types)

Qt는 미리 정의되어 있는 이벤트 외에 개발자들이 직접 이벤트를 만들 수 있도록 하고 있다.
이 방법은 특히 쓰레드를 만들어 GUI 쓰레드와 통신하고자 할 때 유용하다.
물론 멀티쓰레드가 아니더라도 단일 쓰레드에서도 유용하게 사용될 수 있다.

일반 함수 호출도 있고 signal/slot 처리도 있는 데 굳이 사용자 처리 이벤트를 선택하는 건 동기(send)/비동기(post) 처리 모두에서 사용될 수 있기 때문이다.
일반 함수 호출 및 signal/slot에 의한 처리는 늘 동기적(synchronous)이다. 호출 즉시 처리되어 호출 지점으로 되돌아 오는 것이다.

또 다른 장점은 필요한 경우 이벤트 필터에 의해 필터링 되어 처리를 생략될 수 있다는 점이다.

자세한 건 다음에 보기로 하고

사용자 정의 이벤트 처리의 간단한 코드 샘플을 보자.

const QEvent::Type MyEvent = (QEvent::Type)1234;
...
QApplication::postEvent(obj, new QCustomEvent(MyEvent));

위의 예에서 이벤트는 QCustomEvent 이거나 이를 상속받은 클래스 이어야 한다.

이 이벤트를 처리하려면 obj 객체에 해당하는 클래스는 customEvent(QCustomEvent*)를 오버라이딩 하여야 한다.

void MyLineEdit::customEvent(QCustomEvent *e)
{
   if (e->type() == MyEvent) {
      myEvent();
   } else {
      QLineEdit::customEvent(e);
   }
}

QCustomEvent 클래스에는 범용적으로 사용될 수 있는 void* 형의 맴버가 준비되어 있다.
필요하면 추가정보를 void*에 설정해 customEvent()에서 캐스팅 해서 사용할 수 있다.
하지만 QCustomEvent를 상속받아 필요한 맴버 변수나 함수를 추가하여 처리하는 편이 타입안정성면에서 더 좋을 수 있다.

[참고] QCustomEvent는 Qt3에 해당하는 내용이며 Qt4에서는 Qt3 support 라이브러리를 이용해사용할 수 있는 듯 하다.Qt5 부터는 이를 사용할 수 없다.

이벤트 핸들링과 필터링

Qt에서 이벤트들은 5가지 방법으로 처리할 수 있다.

- 특정 이벤트 핸들러를 오버라이딩
   QObject와 QWidget에는 각기 다른 이벤트들을 처리하기 위한 많은 가상함수들이 준비되어 있다.
   예) QPaintEvent 를 처리하는 paintEvent()

- QObject::event()를 오버라이딩.
   이 함수는 오브젝트가 이벤트를 처리하는 진입점이다. QObject와 QWidget는 간단히 해당 이벤트들을 이벤트 핸들러에 전달하도록 기본 구현되어 있다.

- QObject 에 이벤트 필터 등록하기
   이벤트 필터를 등록하면 오브젝트가 처리하기 전에 이벤트 필터가 먼저 이벤트를 받을 수 있다.

- qApp에 이벤트 필터 등록하기
   특별히 qApp에 이벤트 필터를 등록하면 응용프로그램 내에서 오브젝트들에게 가는 모든 이벤트를 모니터링 할 수 있다.

- QApplication::notify() 오버라이딩 하기
   Qt 이벤트 루프와 sendEvent()는 이벤트를 디스패치하기 위해 이 함수를 호출한다.
   이 함수를 오버라이딩 하면 가장 앞서 이벤트를 받아 처리할 수 있는 우선순위를 가지게 된다.

어떤 이벤트들은 이벤트 처리 대상 객체가 처리를 하지 않을 경우(거부) 전파(Propergated)되는 특성을 가지는 데 이 때 Qt는 이를 처리할 객체를 찾을 때 까지 새로운 대상 오브젝트에서 QApplication::notify()를 호출하게 된다.

예를 들어 키 이벤트를 처리하고자 할 때 포커스를 가진 대상 오브젝트가 처리하지 않으면 최상위 오브젝트에 도달할 때 까지 계속 부모에게 전파하게 된다.

Accept or Ignore ?

전파될 수 있는 이벤트들은 accept()와 ignore() 맴버함수를 가진다.
만약 이벤트 핸들러안에서 accept()가 호출 된다면 이 이벤트는 더이상 전파되지 않을 것이다.
반대로 ignore()를 호출하면 처리할 수 있는 receiver를 찾을 때 까지 계속 시도할 것이다.

대부분의 Qt 개발자들 처럼 accept()와 ignore()를 사용하는 경우는 드물다.

왜냐하면 기본적으로 QEvent 는 "accept"를 기본 값으로 가지고 있고 QWidget 의 이벤트 핸들러들은 기본적으로 ignore()를 호출하기 때문이다.

아래 예를 보자.

void MyObject::keyPressEvent(QKeyEvent *e) // e는 기본적으로 "accept"
{
   if (e->key() == Qt::Key_Escape)
      doEscape();
   else
      QWidget::keyPressEvent(e); // ignore
}

위의 이벤트 핸들러에서 QWidget::keyPressEvent(e); 처럼 QWidget(또는 이를 상속받은 Qt의 위젯들)의 기본 이벤트 핸들러를 호출 하지 않는다면 이벤트 핸들러가 이벤트를 처리 했다고 판단하여 더이상 전파하지 않는다.
아래 코드를 보면 더 이해가 쉬울 것이다.

void QWidget::keyPressEvent(QKeyEvent *e)
{
   e->ignore();
}

이벤트 핸들러가 아닌 QWidget::event()를 오버라이딩 하여 구현하고 있다면 bool 값을 리턴하여 처리 완료할지 아니면 계속 전파를 할지 결정할 수 있다.
true는 "accept"를 의미하고 false는 "ignore"를 의미한다.

QWidget::event() 안에서 QEvent의 accept(), ignore()를 호출하는 것은 무의미 하다.
"accpet" 플래그는 특정 이벤트 핸들러와 event()사이에 처리여부를 판단하기 위한 용도이기 때문이다.

또 event()에서 리턴되는 bool값은 QApplication::notify() 내에서 처리여부를 판단하기 위한 용도로 쓰여진다.

아래 예를 보면서 event()의 기본 구현이 어떤식으로 되어 있는 살펴보자.

bool QWidget::event(QEvent *e)
{
   switch (e->type()) {
   case QEvent::KeyPress:
      keyPressEvent((QKeyEvent*)e);
      if (!e->isAccepted())
         return false;

   case QEvent::KeyRelease:
      keyReleaseEvent((QKeyEvent*)e);
      if (!e->isAccepted(e))
         return false;
   ...
   }
   return true;
}

예외적으로 close 이벤트의 경우 구현이 조금 다른데 QCloseEvent::ignore() 를 호출하면 응용프로그램 닫기를 취소하고 accept()를 하면 Qt 가 closing 처리를 계속 이어갈 수 있도록 한다.

이로인한 혼란을 피하기 위해서는 명시적으로 accept(), ignore()를 직접 호출하는 코드를 작성하면 된다.

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

원문 출처 : <A HREF="http://doc.qt.digia.com/qq/qq11-events.html" TARGET='_blank'>http://doc.qt.digia.com/qq/qq11-events.html</A>;
Qt !!!



구름님 14-10-10 10:24
 
수고하셨습니다아~~
JJIN 14-10-31 20:02
 
잘몰라서 질문하는데요...
마우스 계속 이곳저곳 클릭하면 큐에 이벤트가 계속 싸이자나요.
그럼 그 큐에 쌓인 이벤트를  비울수 있는 방법은 없나요?
     
korone 14-10-31 22:51
 
1. 마우스, 키보드등에서 발생한 이벤트는 Qt내부 이벤트 큐에 계속 누적이 됩니다.
2. 누적된 이벤트를 강제로 비울려면 QCoreApplication::removePostedEvents 함수를 이용해서 꺼내서 버릴 수 있습니다.
JJIN 14-11-01 15:46
 
앗!!! 감사감사 합니다~~~~
JJIN 14-11-01 18:51
 
또...질문이 있습니다..

버튼을 클릭하는데 빠르게 2번 클릭하면 2번이 동작해서요.
한번만 동작하게 다음 시그널은 버리고 싶은데요..

QEvent::StateMachineSignal 192 A signal delivered to a state machine (QStateMachine::SignalEvent).

이렇게 되어 있어서

QCoreApplication::removePostedEvents(QApplication::activeWindow(), QEvent::StateMachineSignal);
이것을 사용했는데요
error: 'StateMachineSignal' is not a member of 'QEvent'
이게 떠서요...
 
 

Total 201
번호 제   목 글쓴이 날짜 조회 추천
201 [QT/Win32] MFC에서 Qt 사용하기 팁. (2) devilqoo 09-10 1147 0
200 [QT/X11] QML 모델을 C++ 코드로 구현하는 방법 중 하나 (1) 구름님 12-03 3513 0
199 [QT/X11] QWT for qt android (1) 구름님 03-07 5251 1
198 [QT/Win32] QtSingleApplication 이용하기 (1) starbig 01-18 5109 0
197 [QT/Win32] Qt 에서 Daum Maps Api geo [ 주소->좌표 변환 ] 사… starbig 01-15 5639 0
196 [QT/Win32] Qt에서 윈도우 서비스 이용하기, Qt Windows Servic starbig 01-15 4468 0
195 [QT/Embedded] QTcpSocket사용 시 플러그 뽑힘 증상 체크 방법 요원009 11-13 8087 0
194 [QT/Win32] QML 에서 CustomModule 을 import 하여 사용하기 tmdwn 05-18 5127 0
193 [QT/Embedded] Qt update함수/repaint함수 사용법좀 알려주세여.. (1) 잉농 05-14 4869 0
192 [QT/Embedded] C++ Exception을 활용한 런타임 콜스택 정보 얻기 (1) korone 04-01 5217 0
191 [QT/Embedded] Qt 5.4.1 raspberry Pi 프로그램 개발하기 (5) 구름님 03-31 17333 1
190 [QT/Win32] Qt 다국어 처리 (2) korone 03-17 10048 0
189 [QT/Win32] signal to signal devilqoo 02-13 4615 0
188 [QT/Win32] Qt x64 바이너리 제공 사이트 (1) 인라이… 02-12 4520 1
187 [QT/Embedded] 우분투 Qt5 크로스 컴파일 (14/11/26 2차 수정) (9) 김아무… 11-12 12842 0
186 [QT/Embedded] Qt 5.3.2에서 Mouse Double Click시 이벤트 문제 (2) korone 11-11 4687 0
185 [QT/Embedded] QtWinExtras korone 11-11 4605 0
184 [QT/Win32] Qt 코딩 스타일 (편집 편) (4) devilqoo 10-22 8393 0
183 [QT/Win32] Qt와 C++11 (2) devilqoo 10-12 8434 1
182 [QT/Win32] Qt 에서의 이벤트 처리 (5) devilqoo 10-09 8073 0
181 [QT/Win32] QList와 QVector의 비교. (2) devilqoo 10-02 5937 0
180 [QT/Win32] Qt에서 사용되는 컨테이너들을 정리해 보았습… (4) devilqoo 10-01 6730 0
179 [QT/X11] qmake DEFINES 변수에 공백문자가 포함된 값을 설… (1) 별님 09-25 4397 0
178 [QT/Win32] 데이터 압축 관련 qCompress(), qUncompress() (9) 구름님 09-16 4831 0
177 [QT/Win32] QML 로 One-Source Multi-Platform (iOS, Android) 실행 tmdwn 09-03 6259 0
176 [QT/Win32] QML 로 iOS Application 제작하기 tmdwn 09-02 6254 0
175 [QT/Win32] QML 로 Android Application 제작하기 (4) tmdwn 09-02 6592 2
174 [QT/X11] qt에서 메모리 조작하기. (2) 나무나… 07-25 6675 0
173 [QT/Win32] Q_DECLARE_FLAGS 매크로 (2) 구름님 07-11 5438 2
172 [QT/Win32] Q_PROPERTY 매크로 (6) 구름님 07-09 7289 2
 1  2  3  4  5  6  7  


About korone.net | Copyright 2003 korone.net. All rights reserved.