Sexta-feira, 26 de Junho de 2009

Executors/FutureTask Vs. Oberver

Antes de começar, este post é específico pra Java. Se não se interessar por java, pule pro próximo post, ou não.

Pra quem não conhece, o padrão observer serve basicamente pra monitorar mudanças de estado em um objeto. Como todo padrão de projeto, o observer pode ser subvertido para fazer coisas onde existem opções melhores.

Exemplo: é preciso mostrar um diálogo com "OK" e "Cancel" para o usuário, e uma ação externa deve ser executada, dependendo da resposta do usuário.

Uma primeira idéia é cadastrar um listener no diálogo, e dependendo do botão clicado, um método do listener é notificado. Um exemplo de listener seria:

/**
 * Interface de um Listener.
 */
public interface Listener {

 /**
  * Método notificado ao cancelar o diálogo.
  */
 void onFailure();

 /**
  * Método notificado ao aceitar o diálogo.
  */
 void onSuccess();

}

Uma possivel implementação de diálog usando esse listener:

/**
 * Implementação de um diálogo onde a resposta da ação do usuário é capturada
 * através de um listener.
 */
class ListenerFrame {
 /** Frame principal. */
 private final JFrame frame;

 /** Ação do botão de ok. */
 private final ActionListener okAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   listener.onSuccess();
  }
 };

 /** Ação do botão de cancelar. */
 private final ActionListener cancelAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   listener.onFailure();
  }
 };

 /** Instância do listener que deve ser noticada. */
 private final Listener listener;

 /**
  * Constroi uma instância de ListenerFrame.
  * 
  * @param l
  *            o listener a ser notificado.
  */
 public ListenerFrame(final Listener l) {
  listener = l;
  frame = new JFrame();
  final JButton ok = new JButton("OK");
  ok.addActionListener(okAction);
  final JButton cancel = new JButton("Cancel");
  cancel.addActionListener(cancelAction);
  final JPanel panel = new JPanel();
  panel.add(ok);
  panel.add(cancel);
  frame.add(panel);
  frame.pack();
 }

 /**
  * Exibe o frame.
  */
 public void show() {
  frame.setVisible(true);
 }
}

Como podem ver, no botão ok "onSuccess()" é chamado, e em cancelar "onFailure()" é chamado.

Uma classe usando esse diálogo, a título de exemplo:

/**
 * Exemplo de utilização de um frame onde a resposta é dada por um listener.
 */
public class ListenerFrameExample {

 /**
  * O programa cria um novo ListenerFrame passando um listener como
  * argumento.
  * 
  * @param args
  */
 public static void main(final String[] args) {
  new ListenerFrame(new Listener() {
   @Override
   public void onSuccess() {
    System.out.println("YAY!");
    System.exit(0);
   }

   @Override
   public void onFailure() {
    System.out.println("OH NOES!");
    System.exit(0);
   }
  }).show();
 }
}

O problema aqui é que eu estou usando o padrão observer pra esperar uma resposta, e não uma mudança de estado. Se eu puder continuar com a execução do programa, isso até não tem tanto problema, mas a intenção aqui claramente não é essa. E como intensão em um código é (quase) tudo, deve haver um jeito melhor de fazer isso ;)

É ai que entra a FutureTask. Como podem na documentação, a FutureTask serve pra guardar um resultado que vai ser requisitado no futuro, e é exatamente isso que queremos: saber o futuro resultado da ação do usuário.

Primeiro, podemos esquecer a classe Listener. Toda a parde de resposta vai ser encapsulada no diálogo.

/**
 * Implementação de um diálogo onde a resposta da ação do usuário é capturada
 * por uma FutureTask.
 * 
 */
class ExecutorFrame {
 /** Frame principal. */
 private final JFrame frame;

 /** Variável auxiliar utilizada para capturar a resposta do usuário. */
 private Boolean success;

 /** FutureTask criada para encapsular a resposta do usuário. */
 private final FutureTask hadSuccess = new FutureTask(
   new Callable() {
    public Boolean call() throws Exception {
     return success;
    };
   });

 /** Ação do botão de ok. */
 ActionListener okAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   success = true;
   // FutureTask é executada, liberando hadSuccess()
   Executors.newSingleThreadExecutor().execute(hadSuccess);
  }
 };

 /** Ação do botão de cancelar. */
 ActionListener cancelAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   success = false;
   // FutureTask é executada, liberando hadSuccess()
   Executors.newSingleThreadExecutor().execute(hadSuccess);
  }
 };

 /**
  * Constroi uma instância de ExecutorFrame.
  */
 public ExecutorFrame() {
  frame = new JFrame();
  final JButton ok = new JButton("OK");
  ok.addActionListener(okAction);
  final JButton cancel = new JButton("Cancel");
  cancel.addActionListener(cancelAction);
  final JPanel panel = new JPanel();
  panel.add(ok);
  panel.add(cancel);
  frame.add(panel);
  frame.pack();
 }

 /**
  * Exibe o frame.
  */
 public void show() {
  frame.setVisible(true);
 }

 /**
  * Retorna a resposta do usuário. O método fica travado até que o usuário
  * clique em um botão.
  * 
  * @return {@code true} caso o usuario clique em OK, {@code false} caso
  *         clique em cancelar.
  */
 public boolean hadSuccess() {
  try {
   return hadSuccess.get();
  } catch (final InterruptedException e) {
  } catch (final ExecutionException e) {
  }
  return false;
 }
}

O método "get()" da FutureTask trava a execução até a tarefa seja executada. Uma FutureTask é criada para guardar o resultado, e as ações dos botões simplesmente executam a tarefa, fazendo assim com que "hadSuccess()" retorne o resultado correto.

A utlização desse dialogo fica assim:

/**
 * Exemplo de utilização de um frame onde a resposta é dada por uma FutureTask.
 */
public class ExecutorFrameExample {

 /**
  * O programa instancia um frame e aguarda a sua resposta. Note que
  * d.hadSuccess() fica travado até o usuário clicar em um botão.
  * 
  * @param args
  */
 public static void main(final String[] args) {
  final ExecutorFrame d = new ExecutorFrame();
  d.show();
  if (d.hadSuccess()) {
   System.out.println("YAY!");
  } else {
   System.out.println("OH NOES!");
  }
  System.exit(0);
 }
}

Eu particularmente acho bem mais interessante desse jeito. Obviamente cada implementação tem suas vantagens e desvantagens, mas a intenção da segunda opção está bem mais clara do que a primeira.

Outras leituras:
http://en.wikipedia.org/wiki/Swing_%28Java%29
http://en.wikipedia.org/wiki/Observer_pattern
http://sourcemaking.com/design_patterns

Terça-feira, 14 de Abril de 2009

Python: PyQt Model/View

Imagino que todos conheçam o padrão de projeto MVC, o famoso Model/View/Controller. A idéia é separar a visualização (interface com o usuário) do controle (os "comandos" que o usuário executa) e do modelo (os dados e a lógica de negócio).

Uma pequena variação disso é o model/view, quando não é necessário controle, ou o controle está em um outro lugar. O MVC completo é um padrão arquitetural, sendo que o "MV" é um padrão mais localizado. Acredito que isso fique mais claro ao final do post :-).

A partir do Qt 4, alguns widgets passaram a suportar o padrão MV. Os antigos QListWidget, QTableWidget, etc. foram substituídos pelo QListView, QTableView, etc. As classes de modelo foram adicionadas para completar o padrão, e para conectar ambos basta um simples someView.setModel(someModel).

Um exemplo usando algumas classes padrão:

import sys

from PyQt4.QtCore import *
from PyQt4.QtGui import *

app = QApplication([])

view = QListView()
model = QStringListModel("item1 item2 item3 item4 item5".split())

view.setModel(model)

view.show()

sys.exit(app.exec_())

Qualquer mudança no model é refletida automaticamente na view, e é isso que torna o padrão interessante. É possível ter várias views para um model, e também existe o conceito dos delegates, que servem para dar flexibilidade à entrada de dados. Para não me alongar muito, não vou entrar nos detalhes, mas isso tudo pode ser visto com mais profundidade em Qt Model/View Programming.

model.setData(model.index(0), QVariant("aaaa"))



Agora um exemplo de classe que implementa um modelo abstrato. Supondo que eu queira um modelo de uma série de resultados numéricos, como proceder?

O primeiro passo é criar uma classe que implemente uma QAbstractTableModel:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class ResultTableModel(QAbstractTableModel):
    def __init__(self, parent = None):
        super(QAbstractTableModel, self).__init__(parent)
        self.results = []
    

Como pode ser visto aqui, o meu modelo deve sobrescrever alguns métodos para que ele possa utilizado. Caso o modelo necessite apenas ser lido (read-only), os métodos flags(), data(), headerData() e rowCount() devem ser providos. Este quatro métodos são suficientes para um QAbstractListModel, mas como estou implementando uma tabela, columnCount() também deve ser provido.

Uma implementação básica seria:

def rowCount(self, parent = QModelIndex()):
    return len(self.results)

def columnCount(self, parent = QModelIndex()):
    return 1 # por enquanto não quero mais de uma coluna

def flags(self, index):
    # quero todos os itens habilitados e editáveis
    return Qt.ItemIsEnabled | Qt.ItemIsEditable

# Recupera um dado de um índice (QModelIndex)
def data(self, index, role):
    # índice não é válido, então retorno um dado vazio
    if not index.isValid():
        return QVariant()

    # se a linha está fora dos bounds da minha lista, resultado vazio
    if index.row() >= len(self.results):
        return QVariant()

    # se o role é o DisplayRole (ou seja, o valor que deve ser renderizado)
    # retorno o valor que esta na linha lista
    if role == Qt.DisplayRole:
        return QVariant(self.results[index.row()])
    else:
        # podemos retornar valores específicos para outros roles,
        # por exemplo ToolTipRole. Pela simplicidade, ignoramos isso.
        return QVariant()

# Recupera os cabeçalhos da tabela
def headerData(self, section, orientation, role):
    if role != Qt.DisplayRole:
        return QVariant()

    if orientation == Qt.Horizontal:
        # Results vai aparecer no topo da linha
        return QVariant("Results")
    else:
        # em cada linha, mostrar o numero dela no início
        return QVariant("%s it." % (section + 1,))

Pronto, agora já podemos exibir o modelo em uma QTableView :-).

No próximo post irei mostrar como fazer para um model ser editável e redimensionável.

Outros materiais:
MVC
Model/View Programming
Model Subclassing
QTableView
QAbstractTableModel
Qt Documentation

Quinta-feira, 26 de Março de 2009

Python: (des)empacotando argumentos

Packing/unpacking de argumentos é algo que as vezes é esquecido em Pytho e é bem útil em algumas situações.

Packing permite uma função receber um número arbitrário de argumentos:

In [1]: def a(*b):
   ...:     print len(b), b
   ...:
   ...:

In [2]: a(1,2,3,4,5)
5 (1, 2, 3, 4, 5)

O unpacking faz o serviço contrário, ou seja, ao chamar uma função, os argumentos de uma tupla podem ser "explodidos" (na falta de uma palavra melhor). Aqui vai um exemplo bem prático disso, usando o QColor do PyQt:

color = (255, 0, 0)
QColor.fromRgb(*color) # ao invés de QColor.fromRgb(color[0], color[1], color[2])

A função recebe 3 argumentos (r, g, b), e usando o unpacking economizamos alguns caracteres.

Tem mais algumas dicas na documentação oficial, incluindo a possibilidade de usar isso pra keyword arguments usando a notação "**".

Quinta-feira, 19 de Março de 2009

Internet Explorer 8 Final!

Firefox

'nuf said.

Domingo, 1 de Março de 2009

ZFS

O FLOSS Weekly entrevistou nessa semana Aaron Newcomb e David Brittle, falando sobre o ZFS. Pra quem não sabe, ZFS é um sistema de arquivos que vem sendo desenvolvido pela Sun há um tempo, com características bem interessantes.

A entrevista tem cerca de 75 minutos, e pode ser encontrada aqui.

Quinta-feira, 15 de Janeiro de 2009

O Arqueiro

Comecei a ler O Arqueiro, de Bernard Cornwell. Vou colocar dois quotes do livro aqui, pra passar minha impressão do livro.

- Filho da puta idiota - disse Jake. - Eu não ia machucá-lo. Não muito.
- Eu vou me embebedar, padre - disse Thomas, feliz. - Vou ficar tão bêbado que uma daquelas duas garotas vai parecer atraente. - Ele fez um gesto com a cabeça em direção às filhas da viúva.
O padre Hobbe inspecionou-as com ar crítico, e depois suspirou.
- Você vai se matar bebendo tanto assim, Thomas.
Ou seja, excelente!

Sábado, 20 de Dezembro de 2008

Livros

Com o ano quase acabando, começam as retrospectivas e previsões. A minha retrospectiva aqui vai ser de alguns livros que li esse ano. Vou dividir os livros em duas categorias. Neste primeiro post vou colocar alguns livros NÃO relacionados à computação, e no próximo post vou colocar alguns livros técnicos que achei interessante. Se alguém tiver algumas indicações de leitura pro próximo ano, por favor deixem um comentário :-).

Lembrando que a lista não está em nenhuma ordem específica.


  • Historias de Robôs Vol. 3 (Isaac Asimov) - Diversos contos de ficção científica, escritos por vários autores e editado pelo fantástico Isaac Asimov. O interessante é ver a visão do futuro que se tinha no passado, e como algumas coisas que hoje são normais foram vislumbradas pelas mentes criativas dos autores. O livro é ideal pra quem gosta de ler em qualquer lugar (no ônibus, fila de banco, etc.), pois os contos não são muito grandes, o que não demanda muita concentração.
  • Blink (Malcolm Gladwell) - Esse livro, na minha opinião, foi um pouco overhyped. Escutei falar bastante dele, e acabei comprando. Ele fala sobre a tomada rápida de decisões, e como as vezes perder muito tempo pensando e analisando um problema pode prejudicar um julgamento. Eu me decepcionei um pouco com o livro, mas apesar disso é interessante.
  •  Freakonomics ( Steven D. Levitt, Stephen J. Dubner) - Esse livro também teve muito hype, mas ao contrário do Blink, eu gostei bastante. A idéia é pegar vários temas, e fazer uma análise de causas e consequências. Um exemplo é a relação entre liberação do aborto nos "US and A" e a diminuição da criminalidade na década de 90. Vários temas polêmicos são abordados, e eu achei bem interessante, principalmente pra pessoas mais curiosas do que o normal.
  • O dia do Chacal (Frederick Forsyth) - O dia do Chacal conta a história de uma tentativa de assassinato contra o presidente francês Charles de Gaulle. O protagonista é o Chacal (d'oh), e a história vai desde a contratação do rapaz até o... na, não vou colocar spoilers aqui :-). Este livro foi adaptado para um filme homônimo em 1973, e em 1997 um outro filme foi baseado no livro, desta vez chamado de "O Chacal", com uma história adaptada para padrões estado-unidenses.
  •  3001: A Odisséia Final (Arthur Clark) - Pra quem não sabe, Arthur C. Clark é um dos mais fodásticos autores de ficção científica, um dos pais do gênero tão apreciado pelos nerds. 3001 é o livro final da série "Odisséia no Espaço", e conta como o ex-astronauta Frank Poole, que foi morto pelo HAL-9000 em "2001", foi encontrado flutuando no espaço, e foi revivido com uma técnica médica criada na época. O legal desse livro é que o futuro previsto não tem nada de muito absurdo, são coisas que se pensarmos bem são realmente possíveis (pelo menos em partes).
  •  The Fountainhead (Ayn Rand) - Fugindo um pouco do sci-fi, e partindo pra filosofia, entra "The Fountainhead", ou "A Nascente" em brasileiro. O livro conta a história de Howard Roark, um arquiteto modernoso e incorruptível, e segundo a Ayn Rand, um homem como deveria ser. O livro é um clássico, está sempre nas listas de livros que todo mundo deve ler, e eu fiz a minha parte :D. O plot se baseia na figura do Roark, e nos relacionamentos dele com os outros personagens. O livro é muito envolvente, principalmente pelas atitudes que são tomadas no decorrer da história. As atitudes, pelo menos pra mim, são normalmente angustiantes (mas plausíveis (ou não) no mundo real) e é essa angústia que faz com que você não queira parar de ler, até saber o que acontece depois. A idéia do título é que "o ego é a nascente do progresso humano".
  • Silmarillion (J. R. R. Tolkien) - A bíblia de Tolkien. Pra quem curte as  histórias de Tolkien, esse livro conta toda a criação do universo que ele imaginou, e vários eventos que precedem as histórias mais famosas dele. O livro é na verdade uma coleção de histórias, e o plot é centrado nas Silmarilli, as jóias criadas por Fëanor, que aprisionam a luz das duas árvores que ficavam no "paraíso" (Valinor). As tais jóias foram roubadas pelo Melkor / Morgoth, e a partir dai começa a desgraça toda. A comparação com a bíblia vem principalmente pelo tipo de narrativa, com um detalhamento absurdo das árvores genealógicas, e pouquíssimos detalhes. Eu li a versão em inglês, e me arrependi um pouco. A linguagem é bem rebuscada e de difícil compreensão, principalmente nas falas dos Valar. Pra quem quer se aprofundar no universo de Tolkien, é imperdível.
  • The Hobbit (J. R. R. Tolkien) - O hobbit conta a história de Bilbo Baggins, e a aventura pra recuperar o tesouro que uns anões malucos perderam pro Smaug. A história é bem fast paced, e conta desde a vida caseira do hobbit, a transformação dele em um aventureiro, o achado de um anel do poder, e vai além da recuperação do tesouro. Os personagens são bem carismáticos, e a história também mostra como o Gandalf é um porra-louca completo, que fica desaparecendo nas horas mais importantes :D. Lembrando também que está sendo produzido um filme sobre o Hobbit, e se não me engano vão ser 2 filmes pra cobrir a história inteira.

Segunda-feira, 3 de Novembro de 2008

Ordenar linhas no vim

Uma dica rápida pros machos (de todos os sexos) que usam vim:


Suponha que você tem um arquivo C / Python / Java / Whatever, e esse arquivo tem alguns includes / imports / whatevers no topo. No calor do momento (porque programar pode ser emocionante), ninguém se importa (sacou?) muito com a ordem das coisas. Claro que na hora do refactoring, é bom deixar a bagaça ordenadinha, porque é muito mais tranquilo de se achar assim. Como fazer isso do jeito fácil? Ai vão os passos no vim:

  1. Selecione as linhas pra ordenar apertando "V" para ir no modo visual, e com as setas direcionais escolha as linhas onde estão os includes (quem usa gVim pode usar o mouse aqui... n00bs!)
  2. Digite ":!sort" (sem aspas... mas acho que você já sabia disso :-)
  3. Sorria!
A dica é até batidinha, mas sempre é bom relembrar :-).
E depois dizem que vi / vim é complicado...

Segunda-feira, 27 de Outubro de 2008

LeechBlock

Eu confesso: sou um procrastinador. MAS, eu tenho lutado contra esse instinto, e tenho procurado armas pra me ajudar nisso.

Pra quem não sabe o que é procrastinar(!):

procrastinate
(prō-krăs'tə-nāt', prə-) pronunciation [essas pronuncias ajudam demais]

v.intr.
To put off doing something, especially out of habitual carelessness or laziness.

v.tr.
To postpone or delay needlessly.


Um dos meus pontos fracos é ficar "lendo" coisas pela Internet (nem sempre úteis, óbvio). E nesse ponto entra a supimpa extensão LeechBlock. Com ela você pode bloquear aqueles malditos sites que fazem o dia parecer ter 10 minutos.



As opções pra bloquear sites são bem flexíveis. Da pra escolher dias da semana, horários no dia e até limites máximos de tempo que se fica no site. Quem é viciado em checar os emails, por exemplo, pode setar uma configuração pro GMail ficar aberto 5 minutos a cada duas horas.

A interface não é a mais simples (como da pra ver no screenshot acima), mas quebra o galho. E quando um site proibido é acessado, isso é o que se vê:



Ótimo! Obviamente isso não impede ninguém de abrir outro browser ou desativar a extensão (cheater!), mas agora toda vez que se vê essa tela, um "Vai trabalhar vagabundo!" vem na mente.

Segunda-feira, 20 de Outubro de 2008

Introdução ao PyQt

Há algum tempo dei um pico-curso de PyQt pro pessoal do PET Computação (minha antiga bolsa). Resolvi então disponibilizar a apresentação pra quem quiser dar uma olhada.

Pra baixar, é só entrar aqui. Ela fala um pouco de Python e suas toolkits gráficas, um pouco de Qt (conceitos básicos, e.g. signals e slots), depois dá uma introdução em PyQt, com alguns exemplos de código. Se alguém quiser os exemplos completos, está tudo disponível no site do PET, é só entrar lá e ser feliz. Esses códigos foram usados só como exemplo, primando pela simplicidade na explicação, então não espere algo muito elaborado.

Lembro que a apresentação representa minha experiência e pensamentos na época, e eu não me responsabilizo por nenhum dano causado pelo mau uso dela. Algumas informações estão desatualizadas (a Nokia ainda não havia comprado a Trolltech), mas no geral (acho) que está ok.

Peço também que se alguém encontrar algo de errado com a apresentação, deixe um comentário que eu corrijo o problema (ou não).

analytics