Qt 에서의 이벤트 처리 > QT 강좌/팁

본문 바로가기
사이트 내 전체검색

QT 강좌/팁

QT/Win32 Qt 에서의 이벤트 처리

페이지 정보

작성자 no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 댓글 5건 조회 10,469회 작성일 14-10-09 09:52

본문

티스토리에서 작성한 걸 옮겨오다 보니 보기는 나쁘지만 내용 이해에는 크게 지장이 없을 듯 하니 참고 봐주세요 ^^

---------------------------------------------------------------------------------------------------------------------------------

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

원문 출처 : http://doc.qt.digia.com/qq/qq11-events.html

Qt !!!

추천0

댓글목록

JJIN님의 댓글

no_profile JJIN 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 작성일

잘몰라서 질문하는데요...
마우스 계속 이곳저곳 클릭하면 큐에 이벤트가 계속 싸이자나요.
그럼 그 큐에 쌓인 이벤트를  비울수 있는 방법은 없나요?

korone님의 댓글의 댓글

no_profile korone 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 작성일

1. 마우스, 키보드등에서 발생한 이벤트는 Qt내부 이벤트 큐에 계속 누적이 됩니다.
2. 누적된 이벤트를 강제로 비울려면 QCoreApplication::removePostedEvents 함수를 이용해서 꺼내서 버릴 수 있습니다.

JJIN님의 댓글

no_profile JJIN 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 작성일

또...질문이 있습니다..

버튼을 클릭하는데 빠르게 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 198건 1 페이지
QT 강좌/팁 목록
번호 제목 글쓴이 조회 추천 날짜
198 QT/Win32 no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 3886 0 09-10
197 QT/X11 no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 5451 0 12-03
196 QT/X11 no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 6847 1 03-07
195 QT/Embedded no_profile 요원009 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 11228 0 11-13
194 QT/Win32 no_profile tmdwn 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 6413 0 05-18
193 QT/Embedded no_profile 잉농 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 6172 0 05-14
192 QT/Embedded no_profile korone 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 6549 0 04-01
191 QT/Embedded no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 21872 1 03-31
190 QT/Win32 no_profile korone 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 13203 0 03-17
189 QT/Win32 no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 5844 0 02-13
188 QT/Win32 no_profile 인라이너 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 5762 1 02-12
187 QT/Embedded no_profile 김아무개 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 16067 0 11-12
186 QT/Embedded no_profile korone 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 5824 0 11-11
185 QT/Embedded no_profile korone 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 5884 0 11-11
184 QT/Win32 no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 10509 0 10-22
183 QT/Win32
Qt와 C++11 댓글+ 2
no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 10844 1 10-12
열람중 QT/Win32 no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 10470 0 10-09
181 QT/Win32 no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 7525 0 10-02
180 QT/Win32 no_profile devilqoo 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 8610 0 10-01
179 QT/X11 no_profile 별님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 5411 0 09-25
178 QT/Win32 no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 6047 0 09-16
177 QT/Win32 no_profile tmdwn 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 7522 0 09-03
176 QT/Win32 no_profile tmdwn 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 7448 0 09-02
175 QT/Win32 no_profile tmdwn 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 7934 2 09-02
174 QT/X11 no_profile 나무나무나무 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 8355 0 07-25
173 QT/Win32 no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 6751 2 07-11
172 QT/Win32 no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 9357 2 07-09
171 QT/Win32 no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 6558 2 07-08
170 QT/Win32 no_profile 구름님 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 13029 1 07-07
169 QT/X11 no_profile korone 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 8218 0 07-02
게시물 검색

  • 게시물이 없습니다.

회원로그인

설문조사

새로운 홈페이지에 대한 평가

접속자집계

오늘
64
어제
497
최대
3,878
전체
4,081,904

Copyright © korone.net. All rights reserved.