11
Processamento de Eventos Reimplementando Manipuladores de Eventos Instalando Filtros de Eventos Manter Respondendo durante Processamento Intensivo Eventos são gerados pelo gerenciador de janelas ou pelo próprio Qt em resposta a várias ocorrências. Quando o usuário pressiona uma tecla ou o botão do mouse, um evento de tecla (KeyPress) ou um evento do mouse (MouseDown) é gerado. Quando uma janela é exibida pela primeira vez, um evento paint é gerado para dizer a janela recém visível que ela precisa ser desenhada. A maioria dos eventos são gerados em resposta às ações de usuários, mas alguns, como os eventos de timer, são gerados de forma independente pelo sistema. Quando se programa com Qt, raramente é preciso pensar sobre os eventos, porque os widgets do Qt emitem sinais quando algo de significativo acontece. Eventos tornamse úteis quando escrevemos nossos próprios widgets personalizados ou quando queremos modificar o comportamento dos widgets já existentes do Qt. Os eventos não devem ser confundidos com sinais. Como regra, os sinais são úteis ao utilizar um widget, enquanto que os eventos são úteis na implementação de um widget. Por exemplo, quando estamos usando QPushButton, estamos mais interessados no sinal clicked() do que nos eventos do mouse em baixo nível ou nos eventos do teclado que causaram o sinal emitido. Mas se estamos implementando uma classe como QPushButton, precisamos escrever um código para lidar com os eventos do mouse e do teclado. Assim, devemos emitir o sinal clicked(), quando necessário. Reimplementando Manipuladores de Eventos No Qt, um evento é uma instância de uma subclasse QEvent. Qt trata mais de uma centena de tipos de eventos, cada uma identificada por um valor de enumeração. Por exemplo, QEvent::type() retorna QEvent::MouseButtonPress para eventos de pressionamento do botão do mouse. Muitos tipos de eventos exigem mais informações do que pode ser armazenado em um simples objeto QEvent; por exemplo: eventos de pressionamento do mouse necessitam armazenar a informação de qual botão do mouse foi pressionado, bem como onde o ponteiro do mouse estava posicionado quando o evento ocorreu. Esta informação adicional é armazenado em uma subclasse derivada da QEvent denominada QMouseEvent. Os eventos são comunicados aos objetos através da função event(), herdada de QObject.A implementação de event() em QWidget encaminha os tipos mais comuns de eventos para os manipuladores específicos de eventos, como mousePressEvent(), keyPressEvent() e paintEvent(). Já vimos vários manipuladores de eventos ao implementar MainWindow, IconEditor e Plotter em

Cap7

Embed Size (px)

Citation preview

Processamento de Eventos

Reimplementando Manipuladores de Eventos

Instalando Filtros de Eventos

Manter Respondendo durante Processamento Intensivo

Eventos são gerados pelo gerenciador de janelas ou pelo próprio Qt em resposta a váriasocorrências. Quando o usuário pressiona uma tecla ou o botão do mouse, um evento de tecla(KeyPress) ou um evento do mouse (MouseDown) é gerado. Quando uma janela é exibida pelaprimeira vez, um evento paint é gerado para dizer a janela recém visível que ela precisa serdesenhada. A maioria dos eventos são gerados em resposta às ações de usuários, mas alguns,como os eventos de timer, são gerados de forma independente pelo sistema.

Quando se programa com Qt, raramente é preciso pensar sobre os eventos, porque os widgetsdo Qt emitem sinais quando algo de significativo acontece. Eventos tornam­se úteis quandoescrevemos nossos próprios widgets personalizados ou quando queremos modificar ocomportamento dos widgets já existentes do Qt.

Os eventos não devem ser confundidos com sinais. Como regra, os sinais são úteis ao utilizar umwidget, enquanto que os eventos são úteis na implementação de um widget. Por exemplo, quandoestamos usando QPushButton, estamos mais interessados no sinal clicked() do que nos eventosdo mouse em baixo nível ou nos eventos do teclado que causaram o sinal emitido. Mas seestamos implementando uma classe como QPushButton, precisamos escrever um código paralidar com os eventos do mouse e do teclado. Assim, devemos emitir o sinal clicked(), quandonecessário.

Reimplementando Manipuladores de Eventos

No Qt, um evento é uma instância de uma subclasse QEvent. Qt trata mais de uma centena detipos de eventos, cada uma identificada por um valor de enumeração. Por exemplo, QEvent::type()retorna QEvent::MouseButtonPress para eventos de pressionamento do botão do mouse.

Muitos tipos de eventos exigem mais informações do que pode ser armazenado em um simplesobjeto QEvent; por exemplo: eventos de pressionamento do mouse necessitam armazenar ainformação de qual botão do mouse foi pressionado, bem como onde o ponteiro do mouse estavaposicionado quando o evento ocorreu. Esta informação adicional é armazenado em umasubclasse derivada da QEvent denominada QMouseEvent.

Os eventos são comunicados aos objetos através da função event(), herdada de QObject. Aimplementação de event() em QWidget encaminha os tipos mais comuns de eventos para osmanipuladores específicos de eventos, como mousePressEvent(), keyPressEvent() epaintEvent().

Já vimos vários manipuladores de eventos ao implementar MainWindow, IconEditor e Plotter em

capítulos anteriores. Muitos outros tipos de eventos são listados na documentação de referênciade QEvent. Também é possível criar tipos de eventos personalizados e despachar eventos nósmesmos. Aqui, vamos analisar dois tipos de eventos comuns que merecem mais explicações: oseventos de tecla e eventos de timer.

Os eventos de tecla são manipulados por reimplementação dos eventos keyPressEvent() ekeyReleaseEvent(). O widget Plotter reimplementa keyPressEvent(). Normalmente, precisamosapenas reimplementar keyPressEvent(), uma vez que o código da tecla liberada normalmente émodificado por Ctrl, Shift e Alt, e estes podem ser checados no evento keyPressEvent() usandoQKeyEvent()::modifiers. Por exemplo, se estivéssemos implementando um widget CodeEditor,seu evento keyPressEvent() implementado para distinguir entre o pressionamento da tecla Homeou o pressionamento das teclas Ctrl + Home ficaria assim:

void CodeEditor::keyPressEvent(QKeyEvent *event) switch (event­>key()) case Qt::Key_Home: if (event­>modifiers() & Qt::ControlModifier) goToBeginningOfDocument(); else goToBeginningOfLine(); break; case Qt::Key_End: ... default: QWidget::keyPressEvent(event);

As teclas Tab e Backtab (Shift+Tab) são casos especiais. Elas são tratadas por QWidget::event()antes que o evento keyPressEvent() seja chamado. Essas teclas passam o foco do widget atualpara o próximo widget ou para o widget anterior da cadeia. Este comportamento é normalmente oque desejamos para a tecla Tab, mas para um widget CodeEditor podemos preferir que a tecla Tabfaça recuar uma linha. A reimplementação de event() ficaria assim:

bool CodeEditor::event(QEvent *event) if (event­>type() == QEvent::KeyPress) QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent­>key() == Qt::Key_Tab) insertAtCurrentPosition('\t'); return true; return QWidget::event(event);

Se o evento foi um pressionamento de tecla, fazemos um cast do objeto QEvent para oQKeyEvent e verificamos qual tecla foi pressionada. Se a tecla for Tab, fazemos algumprocessamento e retornamos true para informar ao Qt que nós tratamos o evento. Se retornarmosfalse, o Qt irá tratar o evento, propagando para o widget pai.

Uma abordagem de alto nível para tratamento de teclas é usar um QAction. Por exemplo, segoToBeginningOfLine() e goToBeginningOfDocument() são slots públicos do widget CodeEditor eo CodeEditor é usado como o elemento central em uma classe MainWindow, podemos adicionar

os atalhos de teclado com o seguinte código:

MainWindow::MainWindow() editor = new CodeEditor; setCentralWidget(editor);

goToBeginningOfLineAction = new QAction(tr("Go to Beginning of Line"), this); goToBeginningOfLineAction­>setShortcut(tr("Home")); connect(goToBeginningOfLineAction, SIGNAL(activated()), editor, SLOT(goToBeginningOfLine()));

goToBeginningOfDocumentAction = new QAction(tr("Go to Beginning of Document"), this); goToBeginningOfDocumentAction­>setShortcut(tr("Ctrl+Home")); connect(goToBeginningOfDocumentAction, SIGNAL(activated()), editor, SLOT(goToBeginningOfDocument())); ...

Isto torna fácil adicionar comandos ao menu ou a barra de ferramentas, como foi visto no Capítulo3. Se os comandos não aparecem na interface do usuário, poderíamos substituir o objeto QActionpelo objeto QShortcut, que é usado internamente pelo QAction para suporte a teclas de atalho.

Por padrão, atalhos de tecla configurados usando QAction ou QShortcut em um widget, sãohabilitados sempre que a janela que contém o widget está ativa. Isso pode ser mudado usandoQAction::setShortcutContext() ou QShortcut::setContext().

Outro tipo comum de evento é o evento timer. Enquanto a maioria dos outros eventos ocorrematravés de uma ação do usuário, o evento timer ocorre em um intervalo regular de tempo,permitindo que o aplicativo faça algum processamento periódico. Os eventos de timer podem serusados para implementar um cursor piscando e outras animações, ou simplesmente paraatualizar a tela.

Para demonstrar eventos de timer, vamos implementar um widget Ticker mostrado na Figura 7.1.Este widget mostra um banner de texto que rola um pixel para a esquerda a cada 30milissegundos. Se o widget é maior do que o texto, o texto é repetido tantas vezes quantonecessário para preencher toda a largura do widget.

Figura 7.1. O widget Ticker

Este é o arquivo de cabeçalho:

#ifndef TICKER_H#define TICKER_H

#include <QWidget>

class Ticker : public QWidget Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText)

public: Ticker(QWidget *parent = 0);

void setText(const QString &newText); QString text() const return myText; QSize sizeHint() const;

protected: void paintEvent(QPaintEvent *event); void timerEvent(QTimerEvent *event); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event);

private: QString myText; int offset; int myTimerId;;

#endif

Nós reimplementamos quatro manipuladores de eventos para o Ticker, sendo que três deles aindanão vimos antes, que são: timerEvent(), showEvent(), e hideEvent().

Agora vamos analisar a implementação:

#include <QtGui>

#include "ticker.h"

Ticker::Ticker(QWidget *parent) : QWidget(parent) offset = 0; myTimerId = 0;

O construtor inicializa a variável offset com zero. A coordenada x em que o texto é desenhado éderivada do valor de offset. Os IDs do timer são sempre diferentes de zero, por isso usamos zeropara indicar que nenhum timer foi iniciado.

void Ticker::setText(const QString &newText) myText = newText; update(); updateGeometry();

A função setText() define o texto a ser exibido. Ele chama a função update() para solicitar oredesenho do widget e updateGeometry() para notificar algum gerenciador de layout responsávelpelo widget Ticker, sobre a sugestão da mudança de tamanho.

QSize Ticker::sizeHint() const return fontMetrics().size(0, text());

A função sizeHint() retorna o espaço necessário para o texto como tamanho ideal do widget.QWidget::fontMetrics() retorna um objeto QFontMetrics que pode ser consultado para obterinformações relativas à fonte do widget. Neste caso, solicitamos o tamanho necessário para umdeterminado texto. (O primeiro parâmetro para QFontMetrics::size() é um flag não necessário parauma string simples, então passamos apenas zero).

void Ticker::paintEvent(QPaintEvent * /* event */) QPainter painter(this);

int textWidth = fontMetrics().width(text()); if (textWidth < 1) return; int x = ­offset; while (x < width()) painter.drawText(x, 0, textWidth, height(), Qt::AlignLeft | Qt::AlignVCenter, text()); x += textWidth;

A função paintEvent() desenha o texto usando QPainter::drawText(). Ele usa fontMetrics() paraverificar o espaço horizontal que o texto precisa e em seguira desenha o texto quantas vezesforem necessárias para preencher toda a largura do widget levando em conta o offset.

void Ticker::showEvent(QShowEvent * /* event */) myTimerId = startTimer(30);

A função showEvent() inicia o timer. A chamada para QObject::startTimer() retorna um número IDque pode ser usado mais tarde para identificar o timer. QObject suporta vários timersindependentes, cada um com o seu respectivo intervalo de tempo. Após a chamada destartTimer(), o Qt irá gerar um evento de timer a cada 30 milissegundos aproximadamente. Aprecisão depende do sistema operacional usado.

Poderíamos ter chamado startTimer() logo no construtor de Ticker, mas poupamos algunsrecursos fazendo o Qt gerar os eventos de timer somente quando o widget está realmente visível.

void Ticker::timerEvent(QTimerEvent *event) if (event­>timerId() == myTimerId) ++offset; if (offset >= fontMetrics().width(text())) offset = 0; scroll(­1, 0); else QWidget::timerEvent(event);

O sistema chama a função timerEvent() periodicamente. Isto incrementa a variável offset em 1para simular o movimento, envolvendo a largura do texto. Em seguida ele rola o conteúdo dowidget 1 pixel para a esquerda, usando QWidget::scroll(). Poderíamos ter chamado update() emvez de scroll() que já seria suficiente, porém scroll() é mais eficiente porque ele simplesmente

move os pixels existentes na tela e gera um evento paint somente para a área recém­exibida dowidget (uma faixa de 1­pixel­wide neste caso).

Se o evento do timer não é referente ao timer que nos interessa, passamos ele para a classebase.

void Ticker::hideEvent(QHideEvent * /* event */) killTimer(myTimerId); myTimerId = 0;

A função hideEvent() chama QObject::killTimer() para parar o timer.

Os eventos de timer são de baixo nível e se for preciso usar vários timers, pode ficar complicadomanter o controle de todos os IDs de timer. Nestas situações é mais fácil criar um objeto QTimerpara cada timer. QTimer emite um sinal de timeout() a cada intervalo de tempo. QTimer tambémfornece uma interface conveniente para single­shot timer (timer de um único intervalo), comovimos no Capítulo 6 (p. ?x?).

Instalando Filtros de Eventos

Uma característica muito poderosa do modelo de eventos do Qt é que uma instância de QObjectpode ser definida para monitorar os eventos de outra instância de QObject antes que o últimoobjeto ainda possa enxerga­lo.

Vamos supor que temos um widget CustomerInfoDialog composto de vários QLineEdits e quequeremos usar a tecla de Space para mover o foco para o próximo QLineEdit. Este comportamentonão­padrão pode ser apropriado para uma aplicação interna cujos usuários são treinados para seuuso. Uma solução simples é herdar QLineEdit e reimplementar keyPressEvent() para chamar afunção focusNextChild() desta forma:

void MyLineEdit::keyPressEvent(QKeyEvent *event)

if (event­>key() == Qt::Key_Space) focusNextChild(); else QLineEdit::keyPressEvent(event);

Esta abordagem tem uma desvantagem. Se usarmos vários tipos diferentes de widgets (porexemplo, QComboBoxes e QSpinBoxes), teremos que tornar a fazer subclasses deles para queapresentem o mesmo comportamento. Uma solução melhor seria fazer CustomerInfoDialogmonitorar os eventos de seu widgets filhos e implementar o comportamento desejado no códigode monitoramento. Isso pode ser conseguido usando filtros de evento. Criar um filtro de eventoenvolve duas etapas:

1. Registrar o objeto de monitoramento com o objeto alvo chamando installEventFilter() nodestino.

2. Manipular os eventos do objeto alvo na função eventFilter() do monitor.

Um bom lugar para registrar o objeto de monitoramento é no Construtor:

CustomerInfoDialog::CustomerInfoDialog(QWidget *parent): QDialog(parent)

...

firstNameEdit­>installEventFilter(this); lastNameEdit­>installEventFilter(this); cityEdit­>installEventFilter(this); phoneNumberEdit­>installEventFilter(this);

Uma vez que o evento de filtro é registrado, os eventos enviados para os widgets firstNameEdit,lastNameEdit, cityEdit, e phoneNumberEdit são primeiro enviados para a função eventFilter() deCustomerInfoDialog antes de serem enviados para seu destino alvo.

Esta é a função eventFilter() que recebe os eventos:

bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event)

if (target == firstNameEdit || target == lastNameEdit|| target == cityEdit || target == phoneNumberEdit) if (event­>type() == QEvent::KeyPress)

QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);if (keyEvent­>key() == Qt::Key_Space)

focusNextChild(); return true;

return QDialog::eventFilter(target, event);

Primeiro verificamos se o widget alvo é um dos QLineEdits. Se o evento foi uma teclapressionada, fazemos um cast de QKeyEvent e verificamos qual tecla foi pressionada.

Se a tecla pressionada foi Space, chamamos focusNextChild() para passar o foco ao próximowidget da cadeia de foco e retornamos true para avisar ao Qt que tratamos o evento. Setivéssemos retornado false, Qt iria enviar o evento para seu alvo, resultando em um espaçoespúrio sendo inserido no QLineEdit.

Se o widget alvo não é um QLineEdit, ou se o evento não é um pressionamento da tecla deespaço, podemos passar o controle para implementação da classe base de eventFilter(). O widgetalvo também poderia ser algum widget que a classe base QDialog está monitorando. (Em Qt 4. 3,este não é o caso de QDialog. No entanto, outras classes de widget do Qt, tais como QScrollArea,monitoram algumas das suas widgets filhas por diversos motivos.)

Qt oferece cinco níveis em que eventos podem ser processados e filtrados:

1. Podemos reimplementar um manipulador de eventos específicos.

Reimplementação de manipuladores de eventos, tais como mousePressEvent(),keyPressEvent(), e paintEvent() é de longe a forma mais comum de processar eventos.Nós já vimos vários exemplos disso.

2. Podemos reimplementar QObject::event().

Ao reimplementar a função event(), nós podemos processar os eventos antes que elesatinjam os manipuladores de eventos específicos. Esta abordagem é necessária parasubstituir o comportamento padrão da tecla Tab como foi visto anteriormente (p. ?x?). Estaabordagem também é necessária para manipular tipos raros de eventos, os quais nãoexistem manipuladores específicos de eventos (por exemplo, QEvent::HoverEnter).Quando reimplementamos event(), temos que chamar a função event() da classe basepara tratar os casos que não tratamos explicitamente.

3. Podemos instalar um filtro de eventos em um único QObject.

Depois que o objeto foi registrado usando installEventFilter(), todos os eventos para oobjeto alvo são primeiro enviados para a função de monitoramento de objetos, eventFilter().Se vários eventos de filtro são instalados no mesmo objeto, os filtros são ativados por suavez do mais recentemente instalado voltando para o primeiro instalado.

4. Podemos instalar um filtro de eventos no objeto QApplication.

Depois que um filtro de evento foi registrado para o qApp (o único objeto QApplication),todos os eventos de cada objeto do aplicativo são enviados para a função eventFilter()antes de serem enviados para qualquer outro filtro de eventos. Essa abordagem é mais útilpara depuração. Ela também pode ser usada para manipular eventos de mouse enviadospara widgets desabilitados, que QApplication normalmente descarta.

5. Podemos herdar QApplication e reimplementar notify().

O Qt chama QApplication::notify() para enviar um evento. Reimplementar esta função é aúnica maneira de obter os eventos antes que qualquer filtro de eventos os receba. Os filtrosde eventos são normalmente mais úteis, porque podem existir vários filtros de eventossimultâneos, enquanto que somente uma função notify().

Muitos tipos de eventos, incluindo os de mouse e os de teclado podem ser propagados. Se oevento não for tratado no caminho para seu objeto alvo ou pelo próprio objeto alvo, todo oprocessamento do evento é repetido, mas desta vez com o objeto pai sendo o novo objeto alvo.Isso continua se repetindo passando de filho para pai até que o evento seja tratado ou que o objetotop­level seja alcançado.

A Figura 7.2 mostra como um evento de pressionamento de tecla é propagado de filho para pai emuma caixa de diálogo. Quando um usuário pressiona uma tecla, o evento primeiro é enviado para owidget com o foco, neste caso o QCheckBox inferior direito (1). Se o QCheckBox não tratar oevento, o Qt vai enviá­lo para QGroupBox (2), e finalmente para o objeto QDialog (3).

Figura 7.2. Propagação de evento em uma caixa de diálogo

Manter Respondendo durante Processamento Intensivo

Quando chamamos Qapplication::exec(), iniciamos o event loop do Qt. O Qt emite alguns eventosna inicialização para mostrar e desenhar os widgets. Depois disso o event loop é executado,checando constantemente se ocorreram eventos e despachando esses eventos para os Qobjectsdo aplicativo.

Enquanto um evento está sendo processado, eventos adicionais podem ser gerados e anexados afila de eventos do Qt. Se gastamos muito tempo processando um evento específico, a interface dousuário ficará sem responder. Por exemplo, todos os eventos gerados pelo sistema de janelasenquanto o aplicativo está salvando um arquivo em disco não serão processados até que oarquivo seja salvo. Durante a gravação, o aplicativo não responderá às solicitações do sistemajanelas para se redesenhar.

Uma solução é usar múltiplos threads: um thread para a interface do usuário e outro para osalvamento do arquivo (ou qualquer outra operação demorada). Desta forma a interface do usuáriocontinuará respondendo enquanto o arquivo estiver sendo salvo. Veremos como fazer isso noCapítulo 14.

Uma solução mais simples é fazer chamadas frequentes para a funçãoQApplication::processEvents() no código de gravação do arquivo. Esta função fiz para o Qtprocessar todos os eventos pendentes e depois retornar o controle para seu chamador. Naverdade Qapplication::exec() é um pouco mais do que um loop while em torno de uma chamadade função processEvents().

Aqui está um exemplo de como podemos manter a interface do usuário respondendo usandoprocessEvents(), baseado no código de gravação do arquivo usado em Spreadsheet (p. ?x?):

bool Spreadsheet::writeFile(const QString &fileName) QFile file(fileName); ... QApplication::setOverrideCursor(Qt::WaitCursor); for (int row = 0; row < RowCount; ++row) for (int column = 0; column < ColumnCount; ++column) QString str = formula(row, column); if (!str.isEmpty()) out << quint16(row) << quint16(column) << str; qApp­>processEvents(); QApplication::restoreOverrideCursor(); return true;

Um perigo desta abordagem é que o usuário pode fechar a janela principal enquanto o arquivoainda está sendo gravado, ou clicar em File|Save uma segunda vez, resultando em umcomportamento indefinido. A solução mais fácil para este problema é substituir

qApp­>processEvents();

por

qApp­>processEvents(QEventLoop::ExcludeUserInputEvents);

dizendo assim para o Qt ignorar os eventos do mouse e do teclado.

Muitas vezes queremos mostrar um QprogressDialog enquanto é executada uma operaçãodemorada. O QprogressDialog tem uma barra de progresso que mantém o usuário informadosobre o progresso da operação. QprogressDialog também fornece um botão Cancel que permiteao usuário cancelar a operação. Aqui está um código para salvar um arquivo do Spreadsheetusando esta abordagem.

Veja o Código:

bool Spreadsheet::writeFile(const QString &fileName) QFile file(fileName); ... QProgressDialog progress(this); progress.setLabelText(tr("Saving %1").arg(fileName)); progress.setRange(0, RowCount); progress.setModal(true);

for (int row = 0; row < RowCount; ++row) progress.setValue(row); qApp­>processEvents(); if (progress.wasCanceled()) file.remove(); return false; for (int column = 0; column < ColumnCount; ++column) QString str = formula(row, column); if (!str.isEmpty()) out << quint16(row) << quint16(column) << str; return true;

Criamos um QprogressDialog com NumRows indicando o número total de etapas. Então paracada linha, chamamos setValue() para atualizar a barra de prograsso. QprogressDialog calculaautomaticamante a porcentagem, dividindo o valor atual do prograsso pelo número total de etapas.Chamamos Qapplication::processEvents() para processar algum evento rapaint, cliques epressionamento de teclas (por exemplo, permitir que o usuário clique em Cancel). Se o usuárioclicar em Cancel, podemos cancelar o salvamento e remover o arquivo.

Não chamamos show() em QprogressDialog porque diálogos de progresso fazem isso por símesmos. Se a operação acaba rapidamente, provavelmente pelo arquivo a salvar ser muitopequeno, ou porque a máquina é muito rápida, QProgressDialog irá detectar isto e não serámostrado totalmente.

Além disso, para multithreading e usando QProgressDialog, há uma maneira completamentediferente de lidar com operações de longa duração: em vez de executar o processamento quandoo usuário solicita, nós podemos adiar o tratamento até que o aplicativo fique ocioso. Isso podefuncionar se o processo puder ser interrompido e retomado com segurança, pois não podemosprever por quanto tempo o aplicativo ficará ocioso.

No Qt, esta abordagem pode ser implementada usando um timer de 0­milissegundos. Essestimers esgotam o tempo sempre que não existem eventos pendentes. Aqui está um exemplo deimplementação de timerEvent() que mostra uma abordagem de processamento ocioso:

void Spreadsheet::timerEvent(QTimerEvent *event) if (event­>timerId() == myTimerId) while (step < MaxStep && !qApp­>hasPendingEvents()) performStep(step); ++step; else QTableWidget::timerEvent(event);

Se hasPendingEvents() retorna true, paramos o processamento e damos o controle de volta parao Qt. O processamento será retomado quando o Qt tiver tratado todos os seus eventospendentes.