SlideShare uma empresa Scribd logo
1 de 11
Baixar para ler offline
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 tornam­se ú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
capítulos anteriores. Muitos outros tipos de eventos são listados na documentação de referência
de QEvent. Também é possível criar tipos de eventos personalizados e despachar eventos nós
mesmos. Aqui, vamos analisar dois tipos de eventos comuns que merecem mais explicações: os
eventos de tecla e eventos de timer.
Os eventos de tecla são manipulados por reimplementação dos eventos keyPressEvent() e
keyReleaseEvent(). O widget Plotter reimplementa keyPressEvent(). Normalmente, precisamos
apenas 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() usando
QKeyEvent()::modifiers. Por exemplo, se estivéssemos implementando um widget CodeEditor,
seu evento keyPressEvent() implementado para distinguir entre o pressionamento da tecla  Home
ou 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 atual
para o próximo widget ou para o widget anterior da cadeia. Este comportamento é normalmente o
que desejamos para a tecla Tab, mas para um widget CodeEditor podemos preferir que a tecla Tab
faç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 o
QKeyEvent e verificamos qual tecla foi pressionada. Se a tecla for Tab, fazemos algum
processamento e retornamos true para informar ao Qt que nós tratamos o evento. Se retornarmos
false, 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, se
goToBeginningOfLine() e goToBeginningOfDocument() são slots públicos do widget CodeEditor e
o 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ítulo
3. Se os comandos não aparecem na interface do usuário, poderíamos substituir o objeto QAction
pelo 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ão
habilitados sempre que a janela que contém o widget está ativa. Isso pode ser mudado usando
QAction::setShortcutContext() ou QShortcut::setContext().
Outro tipo comum de evento é o evento timer. Enquanto a maioria dos outros eventos ocorrem
atravé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 ser
usados para implementar um cursor piscando e outras animações, ou simplesmente para
atualizar 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 30
milissegundos. Se o widget é maior do que o texto, o texto é repetido tantas vezes quanto
necessá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 ainda
nã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 zero
para 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 o
redesenho do widget e updateGeometry() para notificar algum gerenciador de layout responsável
pelo 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 obter
informações relativas à fonte do widget. Neste caso, solicitamos o tamanho necessário para um
determinado texto. (O primeiro parâmetro para QFontMetrics::size() é um flag não necessário para
uma 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() para
verificar o espaço horizontal que o texto precisa e em seguira desenha o texto quantas vezes
forem 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 ID
que pode ser usado mais tarde para identificar o timer. QObject suporta vários timers
independentes, cada um com o seu respectivo intervalo de tempo. Após a chamada de
startTimer(), o Qt irá gerar um evento de timer a cada 30 milissegundos aproximadamente. A
precisão depende do sistema operacional usado.
Poderíamos ter chamado startTimer() logo no construtor de Ticker, mas poupamos alguns
recursos 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 1
para simular o movimento, envolvendo a largura do texto. Em seguida ele rola o conteúdo do
widget 1 pixel para a esquerda, usando QWidget::scroll(). Poderíamos ter chamado update() em
vez 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 do
widget (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 classe
base.
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 complicado
manter o controle de todos os IDs de timer. Nestas situações é mais fácil criar um objeto QTimer
para cada timer. QTimer emite um sinal de timeout() a cada intervalo de tempo. QTimer também
fornece uma interface conveniente para single­shot timer (timer de um único intervalo), como
vimos 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 QObject
pode ser definida para monitorar os eventos de outra instância de QObject antes que o último
objeto ainda possa enxerga­lo.
Vamos supor que temos um widget CustomerInfoDialog composto de vários QLineEdits e que
queremos usar a tecla de Space para mover o foco para o próximo QLineEdit. Este comportamento
não­padrão pode ser apropriado para uma aplicação interna cujos usuários são treinados para seu
uso. Uma solução simples é herdar QLineEdit e reimplementar keyPressEvent() para chamar a
funçã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 (por
exemplo, QComboBoxes e QSpinBoxes), teremos que tornar a fazer subclasses deles para que
apresentem o mesmo comportamento. Uma solução melhor seria fazer CustomerInfoDialog
monitorar os eventos de seu widgets filhos e implementar o comportamento desejado no código
de monitoramento. Isso pode ser conseguido usando filtros de evento. Criar um filtro de evento
envolve duas etapas:
1. Registrar o objeto de monitoramento com o objeto alvo chamando installEventFilter() no
destino.
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() de
CustomerInfoDialog 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 tecla
pressionada, 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óximo
widget da cadeia de foco e retornamos true para avisar ao Qt que tratamos o evento. Se
tivéssemos retornado false, Qt iria enviar o evento para seu alvo, resultando em um espaço
espúrio sendo inserido no QLineEdit.
Se o widget alvo não é um QLineEdit, ou se o evento não é um pressionamento da tecla de
espaço, podemos passar o controle para implementação da classe base de eventFilter(). O widget
alvo 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 eles
atinjam os manipuladores de eventos específicos. Esta abordagem é necessária para
substituir o comportamento padrão da tecla Tab como foi visto anteriormente (p. ?x?). Esta
abordagem também é necessária para manipular tipos raros de eventos, os quais não
existem manipuladores específicos de eventos (por exemplo, QEvent::HoverEnter).
Quando reimplementamos event(), temos que chamar a função event() da classe base
para 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 o
objeto 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 sua
vez 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 útil
para depuração. Ela também pode ser usada para manipular eventos de mouse enviados
para 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 filtros
de eventos são normalmente mais úteis, porque podem existir vários filtros de eventos
simultâneos, enquanto que somente uma função notify().
Muitos tipos de eventos, incluindo os de mouse e os de teclado podem ser propagados. Se o
evento não for tratado no caminho para seu objeto alvo ou pelo próprio objeto alvo, todo o
processamento 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 objeto
top­level seja alcançado.
A Figura 7.2 mostra como um evento de pressionamento de tecla é propagado de filho para pai em
uma caixa de diálogo. Quando um usuário pressiona uma tecla, o evento primeiro é enviado para o
widget com o foco, neste caso o QCheckBox inferior direito (1). Se o QCheckBox não tratar o
evento, 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 eventos
na 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 Qobjects
do aplicativo.
Enquanto um evento está sendo processado, eventos adicionais podem ser gerados e anexados a
fila de eventos do Qt. Se gastamos muito tempo processando um evento específico, a interface do
usuário ficará sem responder. Por exemplo, todos os eventos gerados pelo sistema de janelas
enquanto o aplicativo está salvando um arquivo em disco não serão processados até que o
arquivo seja salvo. Durante a gravação, o aplicativo não responderá às solicitações do sistema
janelas para se redesenhar.
Uma solução é usar múltiplos threads: um thread para a interface do usuário e outro para o
salvamento do arquivo (ou qualquer outra operação demorada). Desta forma a interface do usuário
continuará respondendo enquanto o arquivo estiver sendo salvo. Veremos como fazer isso no
Capítulo 14.
Uma solução mais simples é fazer chamadas frequentes para a função
QApplication::processEvents() no código de gravação do arquivo. Esta função fiz para o Qt
processar todos os eventos pendentes e depois retornar o controle para seu chamador. Na
verdade Qapplication::exec() é um pouco mais do que um loop while em torno de uma chamada
de função processEvents().
Aqui está um exemplo de como podemos manter a interface do usuário respondendo usando
processEvents(), 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 arquivo
ainda está sendo gravado, ou clicar em File|Save uma segunda vez, resultando em um
comportamento 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ção
demorada. O QprogressDialog tem uma barra de progresso que mantém o usuário informado
sobre o progresso da operação. QprogressDialog também fornece um botão Cancel que permite
ao usuário cancelar a operação. Aqui está um código para salvar um arquivo do Spreadsheet
usando 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 para
cada linha, chamamos setValue() para atualizar a barra de prograsso. QprogressDialog calcula
automaticamante a porcentagem, dividindo o valor atual do prograsso pelo número total de etapas.
Chamamos Qapplication::processEvents() para processar algum evento rapaint, cliques e
pressionamento de teclas (por exemplo, permitir que o usuário clique em Cancel). Se o usuário
clicar 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 muito
pequeno, 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 completamente
diferente de lidar com operações de longa duração: em vez de executar o processamento quando
o usuário solicita, nós podemos adiar o tratamento até que o aplicativo fique ocioso. Isso pode
funcionar se o processo puder ser interrompido e retomado com segurança, pois não podemos
prever por quanto tempo o aplicativo ficará ocioso.
No Qt, esta abordagem pode ser implementada usando um timer de 0­milissegundos. Esses
timers esgotam o tempo sempre que não existem eventos pendentes. Aqui está um exemplo de
implementaçã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 para
o Qt. O processamento será retomado quando o Qt tiver tratado todos os seus eventos
pendentes.

Mais conteúdo relacionado

Destaque

Nokia Strategy - Smartphone Wars
Nokia Strategy - Smartphone WarsNokia Strategy - Smartphone Wars
Nokia Strategy - Smartphone WarsMurali Erraguntala
 
Building New Product - Product Managers Checklist
Building New Product -  Product Managers ChecklistBuilding New Product -  Product Managers Checklist
Building New Product - Product Managers ChecklistMurali Erraguntala
 
Pragmatic Approach for Building GREAT Product Roadmap
Pragmatic Approach for Building GREAT Product RoadmapPragmatic Approach for Building GREAT Product Roadmap
Pragmatic Approach for Building GREAT Product RoadmapMurali Erraguntala
 
Building Enterprise Product - For Moving Targets of Customer Needs and Outcomes
Building Enterprise Product - For Moving Targets of Customer Needs and OutcomesBuilding Enterprise Product - For Moving Targets of Customer Needs and Outcomes
Building Enterprise Product - For Moving Targets of Customer Needs and OutcomesMurali Erraguntala
 
How to get what you REALLY want
How to get what you REALLY wantHow to get what you REALLY want
How to get what you REALLY wantCresswellWalker
 
Emergency Services advertising campaign
Emergency Services advertising campaignEmergency Services advertising campaign
Emergency Services advertising campaignDr. Haitham Ibrahim
 

Destaque (9)

Nokia Strategy - Smartphone Wars
Nokia Strategy - Smartphone WarsNokia Strategy - Smartphone Wars
Nokia Strategy - Smartphone Wars
 
Building New Product - Product Managers Checklist
Building New Product -  Product Managers ChecklistBuilding New Product -  Product Managers Checklist
Building New Product - Product Managers Checklist
 
Pragmatic Approach for Building GREAT Product Roadmap
Pragmatic Approach for Building GREAT Product RoadmapPragmatic Approach for Building GREAT Product Roadmap
Pragmatic Approach for Building GREAT Product Roadmap
 
Building Enterprise Product - For Moving Targets of Customer Needs and Outcomes
Building Enterprise Product - For Moving Targets of Customer Needs and OutcomesBuilding Enterprise Product - For Moving Targets of Customer Needs and Outcomes
Building Enterprise Product - For Moving Targets of Customer Needs and Outcomes
 
Nokia Strategy Presentation
Nokia Strategy PresentationNokia Strategy Presentation
Nokia Strategy Presentation
 
Lecture 3
Lecture 3Lecture 3
Lecture 3
 
Lectura Hamlet TIC
Lectura Hamlet TICLectura Hamlet TIC
Lectura Hamlet TIC
 
How to get what you REALLY want
How to get what you REALLY wantHow to get what you REALLY want
How to get what you REALLY want
 
Emergency Services advertising campaign
Emergency Services advertising campaignEmergency Services advertising campaign
Emergency Services advertising campaign
 

Mais de Cedemir Pereira (9)

cdr-intro.pdf
cdr-intro.pdfcdr-intro.pdf
cdr-intro.pdf
 
Livro Cidades Inteligentes(2)-31-66.pdf
Livro Cidades Inteligentes(2)-31-66.pdfLivro Cidades Inteligentes(2)-31-66.pdf
Livro Cidades Inteligentes(2)-31-66.pdf
 
slidesWtisc(1).pptx
slidesWtisc(1).pptxslidesWtisc(1).pptx
slidesWtisc(1).pptx
 
Cap11
Cap11Cap11
Cap11
 
Cap9
Cap9Cap9
Cap9
 
Cap6
Cap6Cap6
Cap6
 
Cap4
Cap4Cap4
Cap4
 
Cap12
Cap12Cap12
Cap12
 
c-gui-programming-with-qt-4-2ndedition -Cap1e2
c-gui-programming-with-qt-4-2ndedition -Cap1e2c-gui-programming-with-qt-4-2ndedition -Cap1e2
c-gui-programming-with-qt-4-2ndedition -Cap1e2
 

Processamento de Eventos no Qt