Menus de Torta com Python e Cairo

Um tempinho atrás experimentei fazer uma interface do tipo pie menu, aqueles menus circulares, usando Python e Cairo. A idéia era usar num tipo de arena de combate usando marcadores para representar os lutadores, com ações escolhidas em menus de contexto circulares.

Melhor um screencast que palavras:

Veja o código aqui. Os arquivos são arena.py (execute este), p_token.py e piemenu.py.
Ou use o subversion:

svn checkout http://setanta-labs.googlecode.com/svn/trunk/pycairo piemenu

Funciona assim: a classe Arena é um widget que pode conter objetos da classe Token (os marcadores representando personagens), estes por sua vez contém um objeto da classe PieMenu, que é composto por vários itens.

Em código:

arena = Arena()

piemenu = PieMenu()
piemenu.add_item('icon1.png', do_callback, 2) # (icon, callback function, callback params...)
piemenu.add_item('icon2.png', do_callback)

token = Token((200, 100), 'token-icon.png', piemenu) # (position, icon, menu)

arena.add_token(token)

Então você adiciona a Arena num container qualquer e está pronto.

Anúncios

Cairo + Vala

Resolvi gastar 1 das minhas 5 horas de sono diárias para dar uma mexida no binding Vala pra biblioteca de gráficos vetoriais Cairo.

Minha idéia era portar algo pronto em outra linguagem, então peguei um exemplo interessante do binding Python PyCairo. Com as anotações do PyCairo informando quais tipos eram passados como parâmetro, ficou mais fácil que mastigar água de cabeça pra baixo. Em alguns momentos eu só acrescentei ponto-e-vírgula ao fim das linhas de código Python.

Cairo Vala Demo
Cairo desenhando sobre uma Gtk.DrawingArea
(Dublin ao fundo)

Faz um bom tempo que estou no regime Python & C, e estava perdendo o traquejo de como programar numa linguagem orientada a objetos com tipagem estática, por isso Vala está sendo algo bastante útil, além de divertido.

Uma das coisas que eu não estava sabendo fazer era passar um array de double como parâmetro para esta função da classe Context do módulo Cairo:

public void set_dash (double[] dashes, int num_dashes, double offset);

A solução:

ctx.set_dash (new double[] {SIZE / 4.0, SIZE / 4.0}, 2, 0);

E a forma a qual eu estava (mal 😉 ) acostumado em Python:

ctx.set_dash([SIZE/4.0, SIZE/4.0], 0)

Coloquei como exemplo na wiki do Vala, e o arquivo pode ser baixado aqui: cairo-demo.vala.

Aventuras no Cairo

Alguns dias atrás comecei a mexer na muito falada (e que será adotada pelo Firefox) biblioteca de desenho vetorial Cairo, com o propósito de fazer um widget em PyGTK representando um heptágono cujos vértices pudessem ser movidos. Eventualmente passei um tempo aprendendo o básico até estancar num problema irritante, que pra variar tinha uma solução simples.

Heptágono

Para criar meu widget, chamado ValueWheel, precisei extender GtkDrawingArea e criar meus próprios métodos para interceptar alguns eventos, como “expose-event”, que acontece toda vez que uma janela, ou parte dela, precisa ser redesenhada. Quando clicava numa das bolinhas nos vértices e a arrastava, meu método que cuidava do “motion-notify-event” reposicionava a tal bolinha e redesenhava tudo. O grande problema é que esses redesenhos estavam muito lentos, com direito a piscada e tudo.

Bastante tempo depois, procurando no Google Code Search, descobri que o jeito certo de fazer a coisa não era ordenar o redesenho diretamente, e sim forçar a emissão do evento expose. Coloquei essas quatro linhas no método de movimento:

alloc = self.get_allocation()
rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height)
self.window.invalidate_rect(rect, True)
self.window.process_updates(True)

Era necessário apenas “invalidar” a área que foi redesenhada e avisar que um update era necessário, que magicamente o widget se redesenhou e sem piscar.

Aos interessados em Cairo eu recomendo:

E o código sobre o qual este post fala é esse: ValueWheel.py