QStateMachine과 QEventLoop 이벤트 순환의 관계와 차이를 깊이 이해하다
18904 단어 event
QCoreApplication::exec()
이미 내부에서 상태 순환에 들어갔다
int QCoreApplication::exec()
{
...
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;
}
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -1;
}
QEventLoop eventLoop;
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
int returnCode = eventLoop.exec();
...
}
위에서 우리는 다음과 같은 몇 가지 결론을 얻을 수 있다.
사실 QApplication뿐만 아니라 QDialog에 유사한 exec() 함수가 있다는 것을 우리는 알고 있다. 사실 내부도 부분적인 이벤트 순환에 들어간다.
int QDialog::exec()
{
...
QEventLoop eventLoop;
d->eventLoop = &eventLoop;
QPointer<QDialog> guard = this;
(void) eventLoop.exec(QEventLoop::DialogExec);
if (guard.isNull())
return QDialog::Rejected;
d->eventLoop = 0;
...
}
이를 통해 알 수 있듯이 QDialog의 이런 exec()는 내부도 결국 하나의 창고에 있는 QEventLoop을 만들어 이벤트 순환을 진행한다.이때 틀림없이 어떤 학우들이 다음과 같은 의문을 가지고 있을 것이다.
사실 답은 위에 있다. 한 라인에 대해 말하자면 그가 가진 이벤트 대기열은 유일하지만 그가 가진 이벤트 순환은 여러 개가 될 수 있지만 절대로 끼워 넣는 관계이며 현재 QEventLoop만 활성화된다.QEventLoop의 exec () 내부에서 무엇을 하는지 볼 수 있습니다.
int QEventLoop::exec(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
...#if defined(QT_NO_EXCEPTIONS)
while (!d->exit)
processEvents(flags | WaitForMoreEvents | EventLoopExec);
#else
try {
while (!d->exit)
processEvents(flags | WaitForMoreEvents | EventLoopExec);
} catch (...) {
...
}
그 내부가 바로while 순환을 통해 끊임없는 프로세스 이벤트()를 보이고 프로세스 이벤트()를 살펴보자.
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);
}
한 라인에 대해 말하자면, 그 사건의 순환이 내부 층에 끼워 넣든 외부 층에 끼워 넣든, 최종적으로 호출될 것이다
d->threadData->eventDispatcher
이것은 라인의 유일한 것이기 때문에 우리 위의 결론을 증명했다. 사건 대기열은 라인에 대해 일대일이다.그러면 어떻게 우리의 또 다른 관점을 검증할 수 있습니까? 즉, 같은 라인에서 이벤트 순환은 여러 개가 될 수 있고 끼워 넣는 관계입니다. 현재 하나만 활성화됩니까?작은 데모를 써서 검증해 봅시다.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QDialog dialog;
dialog.exec();
}
간단합니다. Main Window에 button을 넣으면 그의 클릭 함수에 다이얼로그가 나타나고 국부 이벤트 순환에 들어갑니다. 그 다음에 QEvent Loop::exec () 아래에서 다이얼로그를 열기 전과 열기 후 호출 창고의 차이를 확인합니다.
0 QEventLoop::processEvents qeventloop.cpp 144 0xb717dfc3
1 QEventLoop::exec qeventloop.cpp 204 0xb717e1cf
2 QCoreApplication::exec qcoreapplication.cpp 1225 0xb7181098
3 QApplication::exec qapplication.cpp 3823 0xb74c7eaa
4 main main.cpp 10 0x804a4ce
Dialog를 열기 전에 이벤트 순환이 QCoreApplication 내부에서 제공하는 QEventLoop임을 알 수 있습니다.Dialog를 켠 다음
0 QEventLoop::processEvents qeventloop.cpp 144 0xb717dfc3
1 QEventLoop::exec qeventloop.cpp 204 0xb717e1cf
2 QDialog::exec qdialog.cpp 562 0xb7a949c4
...
27 QEventDispatcherGlib::processEvents qeventdispatcher_glib.cpp 425 0xb71b7cc6
28 QGuiEventDispatcherGlib::processEvents qguieventdispatcher_glib.cpp 204 0xb7595140
29 QEventLoop::processEvents qeventloop.cpp 149 0xb717e061
30 QEventLoop::exec qeventloop.cpp 204 0xb717e1cf
31 QCoreApplication::exec qcoreapplication.cpp 1225 0xb7181098
32 QApplication::exec qapplication.cpp 3823 0xb74c7eaa
33 main main.cpp 10 0x804a4ce
이때의 이벤트 순환이 바로 QDialog의 exec()라는 것을 알 수 있다. 사실 내부의 exec()가 퇴장하지 않으면 외부의 exec()를 실행할 수 없다. 그러나 절대로 이때 이벤트가 막힌다고 생각하지 마라. 많은 사람들이 나와 마찬가지로 처음에 QDialog: exec()가 이벤트를 막는다고 생각했는데 사실 이벤트 순환은 계속 처리되고 있다. 유일한 차이점은 바로 이때의 이벤트 순환이 QDialog에 있다는 것이다.
기본적인 이벤트 순환과 이벤트 대기열을 이해한 후 QStateMachine과 이벤트 순환의 관련을 살펴보겠습니다.
먼저 QStateMachine의 Post Event() 를 살펴보겠습니다.
void QStateMachine::postEvent(QEvent *event, EventPriority priority)
{
...
switch (priority) {
case NormalPriority:
d->postExternalEvent(event);
break;
case HighPriority:
d->postInternalEvent(event);
break;
}
d->processEvents(QStateMachinePrivate::QueuedProcessing);
}
이를 통해 알 수 있듯이 그는 내부에서 두 개의 대기열을 유지했다. 하나는 보통 우선순위의 external Event Queue이고, 하나는 높은 우선순위의 인터넷 Event Queue이다.이로써 Qt 공식 문서에서 말한 상태기의 이벤트 순환과 대기열은 위에서 언급한 이벤트 대기열과 이벤트 순환은 근본적으로 별개의 일이니 절대 헷갈리지 마세요.프로세싱 이벤트 () 가 진행 중인 것을 확인할 수 있습니다.
void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
{
Q_Q(QStateMachine);
if ((state != Running) || processing || processingScheduled)
return;
switch (processingMode) {
case DirectProcessing:
if (QThread::currentThread() == q->thread()) {
_q_process();
break;
} // fallthrough -- processing must be done in the machine thread
case QueuedProcessing:
processingScheduled = true;
QMetaObject::invokeMethod(q, "_q_process", Qt::QueuedConnection);
break;
}
}
분명히 상태기의 실현 논리는q_프로세스 () 이 비동기 호출을 이벤트 대기열에 넣었습니다. 이것은 공식 문서에서 말한
Note that this means that it executes asynchronously, and that it will not progress without a running event loop.
이 말, 즉 상태기의 운행은 현재 라인의 이벤트 대기열에 하나를 잃어버리는 것이다q_프로세스 (), 그리고 이벤트 순환이 그에게 호출되기를 기다리기 때문에 다음 문제의 관건은qt_process()
void QStateMachinePrivate::_q_process()
{
...
Q_Q(QStateMachine);
Q_ASSERT(state == Running);
Q_ASSERT(!processing);
processing = true;
processingScheduled = false;
while (processing) {
if (stop) {
processing = false;
break;
}
QSet<QAbstractTransition*> enabledTransitions;
QEvent *e = new QEvent(QEvent::None);
enabledTransitions = selectTransitions(e);
if (enabledTransitions.isEmpty()) {
delete e;
e = 0;
}
...
enabledTransitions = selectTransitions(e);
if (enabledTransitions.isEmpty()) {
delete e;
e = 0;
}
}
if (!enabledTransitions.isEmpty()) {
q->beginMicrostep(e);
microstep(e, enabledTransitions.toList());
q->endMicrostep(e);
}#endif
if (stop) {
stop = false;
stopProcessingReason = Stopped;
...
}
이를 통해 알 수 있듯이 상태기의 프로세스 자체는 하나의 큰 순환이다. flag은 프로세스ing(이것도 여러 번 배달 q process()의 표기 위치를 피하는 것)이고 이 함수에 들어간 후 상태 이동표에 따라 상응하는 함수를 호출할 수 있다.이 안에는 사실 확장할 수 있는 부분도 있다. 바로 나의 상태기 자체가 호출한 함수가 되돌아오지 않는다는 것이다. 즉,QDialog::exec(), 이벤트 순환에 들어가면 나의 현재 상태 기회는
microstep(e, enabledTransitions.toList());
이 함수에서 exec() 함수는 우리가 정상적으로 이벤트 발송을 할 수 있다는 것을 알고 있기 때문에 이벤트 대기열이 상태기 이벤트를 호출할 때 위의 프로세스링이라는 flag의 존재로 인해 우리는
void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
{
Q_Q(QStateMachine);
if ((state != Running) || processing || processingScheduled)
return;
...
}
즉시 되돌아오기 때문에 상태기의 막힘과 효율 문제를 걱정할 필요가 없습니다. 왜냐하면 이 때 그는 대기열의post 유지보수만 하지만 프로세스 이벤트 ()는 실행할 수 없기 때문입니다.
이 문제는 또 하나 재미있는 점이 있다. 우리가 이전에 사용했던 언어 환경을 가지고 상태기 자체가 호출한 함수는 QDialog:::exec()를 호출한다. 그러면 다이아로그를 만든 후에 나의 이벤트 순환은 이 다이아로그의 QEvent Loop에서 시작되기 때문에 주의해야 할 점은 바로 나의q_process()
void QStateMachinePrivate::_q_process()
{
Q_Q(QStateMachine);
Q_ASSERT(state == Running);
Q_ASSERT(!processing);
processing = true;
processingScheduled = false;
#ifdef QSTATEMACHINE_DEBUG
qDebug() << q << ": starting the event processing loop";
#endif
while (processing) {
if (stop) {
processing = false;
break;
}
...
}
while 순환이 존재하기 때문에 제 대기열에는 3개의 이벤트가 있을 수 있습니다. A, B, C. 그 중에서 제가 A를 실행할 때 저는 Dialog를 만들었습니다. 이때 제 모든 이벤트 순환은 새로 만든 dialog의 내부에 있는 QEvent Loop에 세워져 있습니다. 그러면 이Dialog를 닫을 때 while는 계속 실행합니다. 그러나 이때 제가 있는 이벤트 순환은 QCore Application의 exec 내부에 있는 QEvent Loop입니다.이 점은 각별한 주의가 필요하다.
또 하나 주의해야 할 것은 만약에 컨디셔너가 시간 소모 함수를 실행할 때 즉시 되돌아오거나 윗글처럼 Dialog가 나타나게 하려면 컨디셔너가 계속 순환할 수 없지만, 컨디셔너가 다른 이벤트를 정상적으로 처리할 수 있도록 하려면 컨디셔너가 이벤트를 처리하는 내부에서 호출해야 한다.
bool QMetaObject::invokeMethod();
이 함수는 세 번째 인자를 통해 Qt::QueuedConnection을 선택하면 이 다이얼로그를 QEvent Loop의 이벤트 대기열에 쉽게 배달하고 현재 상태기를 정상적으로 되돌려줍니다. 그리고 QEvent Loop의 프로세스 Events () 는 이 다이얼로그를 처리하고, 생성된 후에 exec () 를 호출하여 국부 이벤트 순환을 형성합니다.
전체적으로 다음과 같은 사항에 유의해야 합니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
앱에서 KSP 출력 사용: 4부이제 생성된 클래스가 있으므로 코드에서 사용해 보겠습니다. Checkout other parts in this series: Android KSP guide for dummies by a Dummy: Part 1 (...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.