Pular para o conteúdo

Pega essa arroba e enfia no teu código!

06/08/2010

Eu gosto muito de Python. Quase todos os meus projetos pessoais estão sendo desenvolvidos usando esta maravilhosa linguagem de programação, por diversos motivos que fogem do tema deste artigo, exceto um: portabilidade… e é divertido. Dois motivos. 😉

Python vem com baterias inclusas. Junto com o interpretador vem módulos e pacotes para fazer uma infinidade de coisas, desde funções matemáticas – o feijão com arroz – até coisas pouco comuns como manipulação de áudio, que estão disponíveis para as várias plataformas suportadas.

Por isso existe uma regra de ouro sobre programas em Python, que diz que

utilizar somente a biblioteca padrão faz seu código rodar na grande maioria das plataformas suportadas

Sendo uma pessoa enrolada e acreditando que um bom programa deve(ria) ser multiplataforma resolvi criar o d10r para gerenciar meu tempo, e um dos meus desejos era que ele funcionasse também no Windows – já que lá a quantidade de enrolados é bem maior (números, pessoal, números…). Naturalmente, optei por uma biblioteca que já vem junto com o Python para construir a interface gráfica: o Tkinter.

Uma pequena introdução sobre o Tkinter: tem poucos componentes pra construir as janelas e a aparência lembra o Windows 95, entretanto ele simplifica um bocado o desenvolvimento e distribuição dos programas. O Tkinter (de “Tk interface”) traduz código em Python para Tcl, este usado para controlar a biblioteca Tk, que é quem faz as coisas no fim das contas. Meio POG, não?

Você que leu até aqui e ainda lembra do título: isso tudo foi uma introdução para eu poder contar…

…o que aconteceu

Logo de cara tive um probleminha envolvendo threads, que felizmente foi fácil de resolver. Tkinter não é thread-safe. Para ser mais preciso, ter mais de uma thread com um loop respondendo aos eventos da interface causa erros aleatórios e intermitentes. A solução é, basicamente, repensar a lógica do programa para ter somente uma thread rodando o loop da GUI.

não ficou lá essas coisas...

não ficou lá essas coisas...

Semana passada, quando desenhei um logo para o meu projeto e tive a infeliz ideia de por o ícone nas janelas do programa quase desisti de vez do Tkinter. Qual o primeiro passo lógico para colocar o ícone? Procurar um método que pudesse “setar” o caminho do arquivo na janela, pensei. Apelei pro Google, porque a documentação não é lá essas coisas, e descobri aqui (¹) o nome da maldita: Tk.iconbitmap().

Segundo minha fonte, no Linux teria de ser usado um ícone no obscuro formato XBM. Abri o GIMP para converter o SVG do logo no tal XBM (ele salva imagens até em HTML!!1!), mas infelizmente a imagem convertida ficava monocromática. Achei que fosse falha do GIMP, até descobrir na Wikipédia que (pasmem!) XBM não suporta cores! Pra você ter uma noção do quão idiota esse formato é, vou mostrar o conteúdo do arquivo de ícone usado no d10r:

#define d10r_width 16
#define d10r_height 16
static unsigned char d10r_bits[] = {
0xf8, 0x1f, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xf8, 0x1f,
0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x3f, 0xfe, 0x7f,
0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xf8, 0x1f } ;

Isso mesmo, as imagens são descritas em texto plano, é código escrito em C. ¬¬’ Me recusando a acreditar que este era o único formato suportado pelo Tkinter testei vários tipos de arquivo para o ícone (todos com 16×16 pixeis) e nada, nenhum funcionou, sempre com a mesma mensagem de erro:

_tkinter.TclError: bitmap "d10r.xbm" not defined

É isso aí, nem mesmo o XBM funcionava! O engraçado é que eu vi vários códigos exatamente iguais ao meu, que segundo seus autores funcionavam normalmente. A mensagem de erro me levou a procurar uma forma de definir o ícone dentro do código, e o fiz usando uma classe que descobri, BitmapImage, mas não adiantou muita coisa:

>>> b = BitmapImage(file='d10r.xbm')
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 3347, in __init__
Image.__init__(self, 'bitmap', name, cnf, master, **kw)
File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 3229, in __init__
raise RuntimeError, 'Too early to create image'
RuntimeError: Too early to create image

Quando vi essa mensagem olhei para o relógio e me perguntei que hora seria mais apropriada para criar uma imagem. Talvez os macacos treinados que leem os arquivos de imagem e transformam em objetos BitmapImage vivam em outro fuso-horário e estavam dormindo na hora. Como não queria ser rude com eles recorri novamente ao pai Google.

Lá pela terceira página de resultados da chifronésima busca que fiz encontrei um artigo de um cidadão na mesma situação que eu, que havia conseguido, usando o método Tk.iconbitmap(). Eis uma tradução livre do que ele escreveu:

No final eu encontrei uma fonte dizendo para salvar o ícone como um .XBM e colocar um ‘@’ na frente do nome do arquivo para que funcionasse. […] Porque? Eu não sei…

Eu coloquei a arroba. E funcionou. Mas porque? Eu queria entender, então tentei encontrar a fonte dessa informação e me deparei com esta thread.

Pus uma máscara porque a conversão do GIMP ficou ininteligível. Não adiantou muito

Pus uma máscara porque a conversão do GIMP ficou ininteligível. Não adiantou muito

Em resumo: a culpa não é TODA do Tkinter, o Tk é que só suporta ícones monocromáticos (em XBM) no Linux, mas a documentação do Tkinter não conta a história toda, o que te força a fuçar na documentação do Tk, o que por sua vez te força a entender uma linguagem que não tem quase nenhum uso hoje em dia, a Tcl, e conhecer o funcionamento interno do Tkinter, sinal de que tem algo muito errado nessa arquitetura.

Eu tenho plena certeza que NUNCA descobriria que colocar a arroba faria o ícone aparecer no Linux sem antes entender qual a função de “@” no Tcl. O cara que explicou o problema até criou um módulo que permite utilizar ícones coloridos, mas ele depende de um código em C, o que me obrigaria a escrever algum script de instalação para meu programa, coisa que não pretendo fazer tão cedo.

Vou continuar escrevendo a interface em Tkinter, por causa da portabilidade e para manter a simplicidade do programa, mas estou torcendo profundamente para que o wxPython tome o lugar do moribundo Tkinter em um futuro muito próximo.

¹Notem como o cara que dá a solução é sucinto e vai direto ao ponto ownando o outro, que só enrola, enrola e no final diz que não existe solução.

8 Comentários leave one →
  1. 22/06/2011 11:42

    Eu consigo utilizar imagens ‘.ico’, mas utilizo o Python 3.2 no Windows….

    Ex:
    master.iconbitmap(default=’icone.ico’)

    Dica pro pessoal que reclama (e tem razão!):
    “Tkinter: tem poucos componentes pra construir as janelas e a aparência lembra o Windows 95…”

    Dêem uma procurada: tkinter.ttk

    • 09/07/2011 3:28

      Eu até dei uma olhada nesse ttk, mas não consegui encontrar ele nos repositórios do Ubuntu. Ainda assim, o visual e a falta de controles não são o único problema do Tkinter, como comentado no artigo.

  2. 11/04/2011 6:56

    Thanks for quoting me in your article 🙂

  3. MLessa permalink
    07/08/2010 12:51

    Pois sim, eu já li em algum sites que o wxRuby ainda apresenta muitos erros principalmente em se tratando de controle da Thread da interface e também na falta de alguns widgets importantes.
    De qualquer forma ainda acho válido testar até pra reportar os bugs pra com isso evoluir o toolkit.
    Vou baixar, usar e assim que possivel até escrevo um post esclarecendo mais isso.

  4. 07/08/2010 11:09

    http://www.awaretek.com/toolkits.html

    Este site te ajuda a escolher um toolkit “pra chamar de seu”. Não é a toa que o wxPython ganha na maioria dos critérios.

  5. MLessa permalink
    06/08/2010 12:26

    Bom post. Eu mesmo já tive muitos problemas com a manipulação de Threads no Gtk e sempre é uma coisa insuportavel de resolver.Quanto a portatibilidade andei lendo coisas muito boas sobre o wxWidgets e estou ponderando até começar a usar, na verdade só ainda não o fiz devido a ainda não total compatibilidade com o Ruby, meu xodó! =D

Deixe um comentário

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.