Mais DrawingArea, Orientação a objetos e herança (e glade)

Posted in Programação, pygtk, Python with tags , , , , , on 30 / junho / 2008 by medeubranco

Este artigo mistura um pouco de tudo o que foi visto nos artigos anteriores:

Criando uma simples janela em PyGTK
Criando uma simples janela em PyGTK – parte II
DrawingArea – Desenhando na tela com PyGTK
Orientação a Objetos com python
Orientação a Objetos – Herança

Como aqui vamos usar o Glade, recomendo também uma visita ao blog do OgMaciel:

Vídeo Aula: Programando com Python e Glade

Vídeo Aula: Trabalhando com o Glade

Não deixe de ver também Mantendo A Sanidade Com O Glade.

Neste artigo vamos criar um programa em que você pode desenhar um polígono desenhando com o mouse.

Para isso, vamos estender a classe gtk.DrawingArea através da herança. Nossa classe estendida vai ser um DrawingArea com recursos que nós vamos adicionar.

Paralelamente, vamos fazer uma janela com o glade, um código que vai exibir a janela inserindo nela a nossa classe filha de DrawingArea.

A nossa classe tela herdando de DrawingArea:


import gtk

# do modulo gtk, importamos DrawingArea e vamos chamar de 'DA'
from gtk import DrawingArea as DA

class tela(DA):
    def __init__(self):
        # executanto o construtor da classe pai (DrawingArea)
        DA.__init__(self)

Agora temos uma classe que faz tudo o que um DrawingArea faz. Podemos acrescentar recursos a ela.

Enquanto isso, vamos fazer a GUI no glade:
– crie uma janela;

– adicione uma “Caixa Vertical” (Vbox) com três itens;

– adicione um Rótulo (Label) na primeira seção da Caixa Vertical;

– em Empacotamento, mude para “não” as opções Expandir e Preencher;

– adicione um botão e em Geral mude o nome para ‘botão1’ (é, poderia ter ficado Button1 mesmo);

– em Empacotamento, mude para “não” as opções Expandir e Preencher;

– no espaço do meio, adicione uma ‘Porta de Visualização’ (ViewPort) ;

– mude o nome da ViewPort para ‘caixatela’

– explore as outras opções de configuração dos widgets

Sua janela deve ficar quase assim:


Voltemos à nossa classe.

O nosso widget será usado da seguinte forma:

Você pressiona o botão esquerdo do mouse sobre o widget e mantém pressionado. Arrasta então o ponteiro do mouse pela tela; uma linha será desenhada por onde passar o ponteiro. Ao soltar o botão do mouse, a linha se fechará em um polígono.

Ao Rolar a ‘rodinha’ do mouse, os últimos pontos do polígono serão removidos; continue rolando a rodinha que o polígono desaparecerá.

Temos então três eventos envolvidos na criação do desenho: botão pressionado, movimento do mouse e botão solto (‘button-press-event’, ‘button-release-event’ e ‘motion-notify-event’ ). Um quarto evento, o de rolagem da rodinha (‘scroll-event’ ), está envolvido na operação de remover pontos do polígono.


class tela(DA):
    def __init__(self):
        DA.__init__(self)
        self.definecores()

        # atributo que determina se o botão está pressionado
        self.apertou=False

        # conectando o expose-event a uma função
        # que vai pedir que a tela seja redesenhada
        self.connect('expose-event',self.expose)

        # adicionando os eventos de mouse
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
            gtk.gdk.BUTTON_RELEASE_MASK |
            gtk.gdk.MOTION_NOTIFY |
            gtk.gdk.POINTER_MOTION_MASK
            )

        # conectando cada evento à sua função correspondente

        # botão de mouse pressionado
        self.connect('button-press-event',self.clica)
        # soltando o botão
        self.connect('button-release-event',self.solta)
        # movimento de mouse
        self.connect('motion-notify-event',self.arrasta)
        # rolando a 'rodinha'
        self.connect('scroll-event',self.rolou)

        # criando uma lista vazia
        # que vai conter os pontos da linha a ser desenhada
        # ou o polígono
        # cada ponto será uma tupla
        # com as coordenadas (x,y)
        self.linha=[]

Olhe o código acima, nas linhas 23, 25, 27 e 29. Quatro funções terão que ser definidas. Vejamos o que cada uma deve fazer:

self.clica

  • mudar a variável self.apertou para True
  • adicionar o primeiro ponto (as coordenadas do clique) à lista self.linha

self.arrasta

  • se self.apertou é True, adiciona mais um ponto À lista e desenha o último segmento.

self.solta

  • mudar a variável self.apertou para False

– self.rolou

  • remover o último ponto da lista e mandar a tela ser redesenhada
    def solta(self, tela, evento):
        self.apertou=False
        self.desenha()

    def clica(self, tela, evento):
        if evento.button==3:
            self.linha.pop()
            return
        self.linha.append((evento.x,evento.y))
        self.apertou=True

    def rolou(self,tela,evento):
        try:
            self.linha.pop()
            self.desenha()
        except:
            pass

    def arrasta(self,tela,evento):
        if self.apertou:
            self.linha.append((evento.x,evento.y))

Movendo o mouse com o botão pressionado

Após soltar o botão

O código completo da classe:


import gtk
from gtk import DrawingArea as DA

class tela(DA):
    def __init__(self):
        DA.__init__(self)
        self.definecores()

        self.apertou=False
        self.connect('expose-event',self.expose)
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
            gtk.gdk.BUTTON_RELEASE_MASK |
            gtk.gdk.MOTION_NOTIFY |
            gtk.gdk.POINTER_MOTION_MASK
            )
        self.connect('button-press-event',self.clica)
        self.connect('button-release-event',self.solta)
        self.connect('motion-notify-event',self.arrasta)
        self.connect('scroll-event',self.rolou)
        self.linha=[]

    def definecores(self):
        self.verde=gtk.gdk.Color(0,48255,0,0)
        self.preto=gtk.gdk.Color(0,0,0,0)
        self.branco=gtk.gdk.Color(65535,65535,65535,0)
        self.vermelho=gtk.gdk.Color(65535,0,0,0)
        self.amarelinho=gtk.gdk.Color(35535,65333,15000,0)
        self.outra=gtk.gdk.Color(0,65535,65535,0)
        self.azul=gtk.gdk.Color(0,0,65535,0)

    def desenhaultimo(self):
        if len(self.linha)>1:
            x1=int(self.linha[-2][0])
            y1=int(self.linha[-2][1])
            x2=int(self.linha[-1][0])
            y2=int(self.linha[-1][1])

            self.gc.set_rgb_fg_color(self.vermelho)
            self.window.draw_line(self.gc,x1,y1,x2,y2)

            self.gc.set_rgb_fg_color(self.preto)
            self.window.draw_rectangle(self.gc,False,x1-1,y1-1,2,2,)
            self.window.draw_rectangle(self.gc,False,x2-1,y2-1,2,2,)

    def solta(self, tela, evento):
        self.apertou=False
        self.desenha()

    def clica(self, tela, evento):
        if evento.button==3:
            self.linha.pop()
            return
        self.linha.append((evento.x,evento.y))
        self.apertou=True

    def rolou(self,tela,evento):
        try:
            self.linha.pop()
            self.desenha()
        except:
            pass

    def arrasta(self,tela,evento):
        if self.apertou:
            self.linha.append((evento.x,evento.y))
            self.desenhaultimo()

    def apaga(self):
        for pt in self.linha:
            print pt
        self.linha=[]
        self.desenha()

    def desenha(self):
        a,b,c,d = self.get_allocation()
        self.gc.set_rgb_fg_color(self.branco)
        self.window.draw_rectangle(self.gc,True,0,0,c-1,d-1)

        if len(self.linha)>0:
            self.gc.set_rgb_fg_color(self.amarelinho)
            self.window.draw_polygon(self.gc,True, self.linha)
            self.gc.set_rgb_fg_color(self.preto)
            self.window.draw_polygon(self.gc,False, self.linha)

        self.gc.set_rgb_fg_color(self.preto)
        self.window.draw_rectangle(self.gc, False,2,2,c-4,d-4 )
        for pt in self.linha:
            self.window.draw_rectangle(self.gc, False,pt[0]-1, pt[1]-1,2,2)

    def expose(self,tela, evento):
        self.gc=self.get_style().fg_gc[gtk.STATE_NORMAL]
        self.gc.set_rgb_fg_color(self.branco)
        self.desenha()

Para entender as operações de desenho, veja o artigo anterior: DrawingArea – Desenhando na tela com PyGTK

Salve este código como desenha.py

Salve seu projeto no Glade como parapintar.glade

E execute o código abaixo, que vai reunir tudo:


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Código parcialmente reaproveitado
do código exemplo do OgMaciel
em http://blog.ogmaciel.com/?p=413
"""

import desenha
try:
    import pygtk
    pygtk.require( "2.0" )
except:
    pass
try:
    import gtk
    import gtk.glade
except:
    sys.exit(1)

class Janelao(object):
    """
    Janela criada com o Glade
    """

    def __init__(self):
        self.xml = gtk.glade.XML( "parapintar.glade" )
        self.xml.signal_autoconnect(self)

        self.mainWindow = self.xml.get_widget( 'window1' )
        self.mainWindow.set_title( "Pintor" )

        # puxando a ViewPort criado no glade
        self.caixa=self.xml.get_widget( 'caixatela' )
        self.mainWindow.connect('destroy', gtk.main_quit)

        self.botao = self.xml.get_widget( 'botao1' )

        # criando uma instancia do nosso objeto
        self.tela=desenha.tela()
        #inserindo nosso objeto na viewport
        self.caixa.add(self.tela)

        self.botao.connect("clicked",self.apagar_tela)
        self.tela.show()
        self.mainWindow.show_all()

    def apagar_tela(self,botao):
        self.tela.apaga()

if __name__ == "__main__":
    w = Janelao()
    gtk.main()
Anúncios

Definindo a codificação em um código python

Posted in Linux, Programação, Python with tags , , , , , , , , , , , , on 25 / junho / 2008 by medeubranco

Imagino que você chegou até este artigo porque, ao executar seu código em python, obteve o seguinte erro:

SyntaxError: Non-ASCII character '\xc3' in file <arquivo> on line <numero de linha>, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

Sei que você está com um tanto de pressa para resolver seu problema; se é esse o caso, insira no seu código, na primeira ou segunda linha, o código abaixo:

# -*- coding: utf-8 -*-

Pronto, seu problema já deve estar resolvido.

Se tiver um pouquinho mais de tempo, vamos juntos tentar entender o que foi que aconteceu.

Primeiro vamos analisar a mensagem de erro. Meu inglês trôpego me permitiu chegar à seguinte tradução da mensagem de erro:

Erro de sintaxe: no arquivo <nome de arquivo.py>, na linha <numero da linha>, tem um caractere não ASCII ('\xc3'), mas não foi declarada nenhuma codificação de caracteres no seu arquivo fonte.

Dê uma olhadinha em http://www.python.org/peps/pep-0263.html para mais detalhes.

Bem, para nós que não somos bilíngües, qualquer tradução capenga vai bem, até mesmo a minha. 😉

Entendi que você colocou um caractere inexistente na tabela ASCII, usada pelo interpretador python ao analisar o código fonte, e esqueceu-se de avisar ao interpretador para usar outra tabela.

O link sugerido na mensagem de erro nos leva à pagina do Python Enhancement Proposals (PEPs) 0263 – pep-0263 (Propostas de Melhoria para o Python, na minha tradução).

Olhando nesta página, obtive as seguintes informações:

  • a pep-0263 propõe introduzir uma sintaxe para declarar a codificação usada no arquivo fonte.
  • na versão 2.1 do python, o único jeito de escrever ‘literais’ Unicode era ranheta demais.
  • neste ‘pep’ é proposto usar um comentário especial no topo da linha par indicar o ‘encoding’ usado.
  • o interpretador python irá usar ASCII como codificação se nenhuma outra for informada

O documento indica pelo menos três formas de se indicar a codificação:
# coding=<encoding name>

ou, (usando formatos reconhecidos pelos editores mais populares):

#!/usr/bin/python
# -*- coding: <encoding name> -*-

ou

#!/usr/bin/python
# vim: set fileencoding=<encoding name> :

Testei as três formas e funcionou – uso o editor gvim. Testei também substituir utf-8 por iso8859-1, e o interpretador não reclamou.

O que é Linux?

Posted in O que é? with tags , , , on 23 / junho / 2008 by medeubranco

Linux é um Sistema Operacional livre e de código fonte aberto.

Foi criado por Linus Torvalds em 1991 e desde então vem recebendo colaboração de programadores em todo o mundo, muitos deles mantidos por grandes empresas como IBM, Google, Intel, etc.

O Linux é hoje usado por empresas como uma robusta e confiável solução em informática. Você usa Linux mesmo sem saber quando navega na Internet, pois muitas páginas estão hospedadas em máquinas que funcionam com um Linux.

Linux também é utilizado como sistema Desktop (computador doméstico ou de escritório) por muitos profissionais e usuários entusiastas.

O que o Linux não é:

Imitação do Windows. Não é.

Windows do Paraguai. Não é.

Sistema de segunda linha para computadores baratos. Não é.

Herança – orientação a objetos com python

Posted in Linux, Programação, Python with tags , , , , , on 22 / junho / 2008 by medeubranco

Atenção: se você ainda não sabe nada sobre orientação a objetos, leia o artigo anterior.

Uma característica legal de programação orientada a objetos é a Herança. Uma classe pode herdar características de outra (superclasse) e incluir suas próprias. A superclasse é mais genérica, a subclasse, que herda da superclasse, é mais especializada.

Usando o exemplo clássico dos livros e tutoriais de programação, imaginemos a superclasse “Animal”; “Mamífero” é uma subclasse de “Animal” e herda suas características. Mas “Mamífero” tem suas próprias características. “Felino” é uma subclasse de “Mamífero”; “Gato” é uma subclasse de “Felino”.

Herdando as características de “felino”, “Gato” herda também as de “Mamífero” e “Animal”. Mas gato mia e arranha o sofá, coisa que nem todo animal faz.

Em Python seria mais ou menos assim:

# definindo a classe Animal:
class Animal:
    def __init__(self):
        # codigo  para o init aqui
    # metodos de Animal aqui

#definindo a classe Mamifero, herdando de Animal
class Mamifero(Animal):
    def __init__(self):

# definindo Felino
class Felino(Mamifero):

# definindo Gato
class Gato(Felino):

No artigo sobre orientação a objetos, nós criamos a classe TiaVelha.

Agora vamos criar uma classe nova. É a Fofoqueira. Uma Fofoqueira é basicamente uma Tia Velha, mas que faz fofocas – nem toda tia velha faz fofocas. A Fofoqueira é especializada no leva-e-traz.

Acho que ficou implícito que, para criar uma Fofoqueira, basta pegar uma Tia Velha e adicionar a ela o leva-e-traz.

Vamos ao código. Se quiser executar o exemplo, primeiro crie uma pasta para nosso exercício. Salve nela o código da TiaVelha, do outro artigo, com o nome tiavelha.py.

Os métodos da Fofoqueira são leva(fofoca) e traz().

O método leva(fofoca) adiciona uma fofoca qualquer ao repertório de fofocas da Fofoqueira. A Fofoqueira não inventa nada; só repassa o que viu ou ouviu.

No método traz(), a fofoqueira solta de uma vez todas as fofocas do seu repertório.

from tiavelha import *
class fofoqueira(TiaVelha):
    def __init__(self,nome,idade):
        TiaVelha.__init__(self,nome, idade)
        self.fofocas=[]

    def leva(self,fofoca):
        self.fofocas.append(fofoca)

    def traz(self):
        for fofoca in self.fofocas:
            print fofoca

Na linha 1 importamos o conteudo do ‘módulo’ tiavelha. A classe TiaVelha agora está disponível no nosso código.

Na linha 2 a classe fofoqueira é definida; TiaVelha entre parêntesis indica que fofoqueira herda de TiaVelha.

Na linha 3 precisamos chamar o método __init__ da superclasse, pois __init__ sendo redefinido em fofoqueira substitui o método com o mesmo nome da superclasse.

Das linhas 7 a 12 colocamos os métodos típicos da fofoqueira.

Os métodos apresentar_se() e falar(), da TiaVelha estão disponíveis para fofoqueira.

Salve o códico como fofoqueira.py na mesma pasta onde salvou tiavelha.py.

Para concluir, vamos fazer um código que usa a classe fofoqueira:

import fofoqueira
lina=fofoqueira.fofoqueira('Alvelina',32)
lina.apresentar_se()
lina.falar()
lina.traz()
lina.leva( "A Luiza trocou de namorado de novo" )
lina.leva( "O Pedro terminou com a amante" )
lina.traz() 

Orientação a objetos com python para iniciantes

Posted in Linux, Programação, Python with tags , , , , , on 22 / junho / 2008 by medeubranco

Python é uma linguagem orientada a objetos. Nos artigos anteriores, não usei essa característica da linguagem para manter o “direto ao ponto”.

Este artigo é para quem ainda não sabe nada de OOP (ou POO), como é o caso do colega que o motivou.

Indo direto ao ponto, vamos pegar um “objeto” conhecido e criar uma classe que o represente.

A nossa classe vai contemplar seus atributos e seu comportamento (o que o nosso objeto faz).

Nosso objeto vai ser a tia velha. Seus atributos serão o nome e idade (que faz dela tia velha), além das coisas que ela fala (coisas típicas de tia). Seu comportamento, ou ação, é falar aleatoriamente as coisas que toda tia velha fala.

Repare que o que foi dito sobre a classe “tia velha” se aplica a toda e qualquer tia velha, independente de nome, idade ( pode ter tia velha de vinte e poucos anos, acredite ) ou de quem é o sobrinho.

vamos ao código

import random

# inicio da definicao da classe
class TiaVelha:
    def __init__(self, nome, idade):
        """
        inicializador da classe.
        cada tia velha criada
        serah criada por este metodo init
        """

        #definindo os atributos
        self.nome=nome
        self.idade=idade
        self.frases=[]

        #criando o repertorio de frases
        self.frases.append( "Como voce cresceu!" )
        self.frases.append( "Voce precisa arrumar uma namoraaaada!" )
        self.frases.append( "Voce ainda nao arrumou emprego?!!" )
        self.frases.append( "Respeita sua mae, menino!" )
        self.frases.append( "Gracinha!" )

    def falar(self):
        """
        metodo principal da tia velha
        """
        x=len(self.frases)
        n=int(random.random()*x-1)
        print self.frases[n]

    def apresentar_se(self):
        print   """
                Ola.
                Meu nome eh """ + self.nome + """
                e tenho """ + str(self.idade)  + """ anos.
                Sou uma tia velha e o que mais faco eh falar
                """

# o codigo abaixo somente
# sera executado se este
# arquivo for executado
# diretamente.
#
# nao o serah se a classe
# TiaVelha for importada
# para dentro de outro
# programa python
if __name__=='__main__':

    # criando uma 'nova' tia velha
    # com nome 'Odila' e idade 67
    # eh neste momento que __init__()
    # serah chamado
    odila=TiaVelha('Odila',67)

    # executando um metodo
    odila.apresentar_se()

    print
    print
    print

    # fazendo a tia odila falar 15 vezes
    for x in range(15):
        odila.falar()

A linha 1 import random só entrou neste programa porque no método falar(self) foi preciso usar números aleatórios.

Explicar o resto acho que é redundância.

Este artigo teve a intenção de apresentar orientação a objetos a quem ainda nada sabia a respeito.

Pesquisa e treinamento são necessários para se aprofundar no assunto.

lexadrez – programa exemplo com pygtk

Posted in gtk, Linux, Programação, pygtk, Python, Uncategorized with tags , , , , , , , , , , on 17 / junho / 2008 by medeubranco

Para quem acompanhou os tutoriais de PyGTK deste blog e queria ver um programa mais completo usando drawingArea e outras graças do GTK, eu posso oferecer o programa que escrevi um tempo atrás para rever as partidas de xadrez que eu jogava no yahoo!.

Enquanto escrevia, resolvi que o software poderia ser também uma ferramenta para estudar aberturas, e preparei ele para isso.

Coloquei o programa no codigolivre :
lexadrez.codigolivre.org.br

Como o codigolivre vira e mexe está inacessível, passo o link para baixar direto o código:
lexadrez-0.001.tar.gz

O programa exibindo a Ruy Lopes:

Exibindo a Ruy Lopes

O código foi todo orientado a objetos. As classes estão mais ou menos documentadas, experimente usar o pydoc pelo terminal, no diretório do programa.

Acho que poderia ter sido melhor escrito em vários aspectos: o parser para leitura do jogo está muito POG, as classes são muito dependentes umas das outras, entre outras coisas. Não coloquei o recurso de gravar as aberturas diretamente no programa; se alguém quiser fazê-lo, não está difícil.

Recursos funcionando:

  • exibe jogos e aberturas no formato do yahoo!
  • permite movimentar as peças de acordo com as regras do xadrez
  • edita e salva comentários nas partidas e aberturas

Se você tem mais experiência em OOP, gostaria saber quais “regras” da boa programação eu violei, e de que forma o código poderia ter sido melhor escrito. Juro que não fico chateado. Use e abuse dos comentários.

DrawingArea – Desenhando na tela com PyGTK

Posted in gtk, Linux, Programação, pygtk, Python with tags , , , , , , on 15 / junho / 2008 by medeubranco

Nos artigos anteriores sobre PyGTK, eu apresentei quatro widgets básicos:

  • gtk.Window – uma janela
  • gtk.VBox – um container para acomodar os widgets
  • gtk.Button – botão
  • gtk.Label – uma etiqueta para texto

Muitos outros widgets básicos não foram nem mencionados, como o gtk.Entry (caixa de texto), gtk.TreeView (lista em árvore), gtk.Toolbar (barra de ferramentas), entre outros.

Acontece que não vou mostrar nenhum deles aqui ainda.

Vamos falar de um componente que é menos usado nas aplicações em geral mas é muito útil. É o gtk.DrawingArea, Uma simples janela X onde podemos desenhar qualquer coisa, inclusive quadrados verdes com borda preta, de 50×50 pixels. E é exatamente isso que vamos fazer aqui.
Vamos construir, passo a passo, uma janela que contem uma área de desenho (DrawingArea) e um botão. Vamos chamar a área de desenho de tela.
A cada clique de mouse na tela, um quadrado verde é desenhado, tendo como centro o ponto clicado.

O botão vai limpar a tela.

Se você ainda não sabe como criar uma janela em PyGTK, leia primeiro esses dois pequenos artigos:

Criando uma simples janela com pygtk
Criando uma simples janela em pygtk – parte II

O grande astro da noite é a gtk.DrawingArea. Então vamos criar nossa tela:

tela=gtk.DrawingArea()

Como deveríamos saber, interfaces gráficas trabalham com eventos, como cliques em um botão, cliques e movimentos de mouse, teclas pressionadas, janelas arrastadas, minimizadas, escondidas por outras, etc. O usuário simplesmente dispara os eventos, o programador cuida de criar respostas a eles. Para uma DrawingArea um evento importante é o expose-event, disparado quando a tela é reexibida. Se a tela estava oculta, quando ela volta a aparecer precisa ser redesenhada, então vamos conectar o expose-event à função que vai desenhar os nossos preciosos quadrados verdes:

tela.connect('expose-event',desenha)

Outro evento importante para o nosso programa é o button-press-event, disparado quando você clica na tela com o mouse; mas este evento não está configurado, então vamos adicioná-lo à nossa tela, e então fazer a conexão com a função que vai criar nossos imprescindíveis quadrados verdes:

tela.add_events( gtk.gdk.BUTTON_PRESS_MASK )
tela.connect('button-press-event',cria_quadrado)

Não nos esqueçamos do objetivo deste programa: criar quadrados verdes. Os quadrados serão armazenados em uma lista. Listas são um tipo de dados que o Python nos provê. Criaremos a lista de quadrados:

quadrados=[]

Agora a coisa começa a ficar mais interessante. Dê uma olhada na função criar_quadrado(), que será executada a cada clique na tela:

def cria_quadrado(tela,evento):
    global quadrados
    coor=evento.get_coords()
    x=coor[0]
    y=coor[1]
    novo_quadrado=[ x-25 , y-25 , 50 , 50 ]
    quadrados.append(novo_quadrado)
    desenha(tela,None)

A função recebe dois parâmetros: a tela onde ocorreu o evento, e o evento propriamente dito.

A segunda linha da função declara como global a lista de quadrados, ou seja, um objeto criado fora da função pode ser acessado de dentro dela sem ter sido passado como parâmetro.

A terceira linha pega as coordenadas do evento. O ponto exato onde ocorreu o clique. O método get_coords() retorna uma tupla com as coordenadas X e Y. Assim: (X,Y).

A quarta e quinta linhas criam duas variáveis para conter os valores da tupla.

A sexta linha finalmente cria o nosso quadrado (não desenha, só cria) e a sétima o adiciona à lista de quadrados. O quadrado, então, nada mais é que uma outra lista contendo as coordenadas de inicio, a largura e a altura.

Vamos analisar melhor o quadrado criado:

x-25 : é o primeiro item da lista. Indica a coordenada X do canto superior esquerdo do quadrado.

y-25: é o segundo item da lista e indica a coordenada Y do canto superior esquerdo do quadrado.
Os valores x e y vieram do evento de mouse. Subtraímos 25 (alguém reparou que 25 é metade de 50?) de cada para que o ponto clicado fosse o centro do quadrado.

Em uma DrawingArea, as coordenadas são orientadas de cima para baixo e da esquerda para a direita.

50: terceiro e quarto itens da lista; respectivamente altura e largura do quadrado.
A oitava linha da função ‘cria_quadrado’ chama a função ‘desenha’. A tela é passada como parâmetro. None é o segundo parâmetro, e só está aí porque a função pede um segundo parâmetro (ela também é disparada quando ocorre o expose-event).

Já sabemos como criar os quadrados na memória. O próximo passo é desenhá-los na tela:


def desenha( tela , evento ):
    global quadrados
    gc=tela.get_style().fg_gc[gtk.STATE_NORMAL]

    verde=gtk.gdk.Color(0,48255,0,0)
    preto=gtk.gdk.Color(0,0,0,0)
    fundo=gtk.gdk.Color(65535,65535,65535,0)

    #pintando o fundo da nossa tela
    gc.set_rgb_fg_color(fundo)
    tela.window.draw_rectangle(gc,True,0,0,800,600 )

    for q in quadrados:
        gc.set_rgb_fg_color(verde)
        tela.window.draw_rectangle(gc,True,q[0],q[1],q[2],q[3])
        gc.set_rgb_fg_color(preto)
        tela.window.draw_rectangle(gc,False,q[0],q[1],q[2],q[3])

Dissecando a função:

Aqui vamos usar a lista de quadrados, que foi criada fora do contexto desta função e é modificada por outra função. Por isso o global quadrados.
Precisamos também criar um graphics context (gc):

    gc=tela.get_style().fg_gc[gtk.STATE_NORMAL]

Um graphics context armazena informações como cor de fundo, de primeiro plano e espessura da linha de desenho.

Precisamos de algumas cores para fazer nossa arte:

    verde=gtk.gdk.Color(0,48255,0,0)
    preto=gtk.gdk.Color(0,0,0,0)
    fundo=gtk.gdk.Color(65535,65535,65535,0)

Vamos dar um fundo branco para a nossa tela:

    gc.set_rgb_fg_color(fundo)
    tela.window.draw_rectangle(gc,True,0,0,800,600 )

Os dois comandos acima serão usados novamente agora, pois vamos percorrer a lista de quadrados e desenhar cada um deles. Se a lista estiver vazia, nenhum quadrado será desenhado:

    for q in quadrados:
        # desenhando um quadrado preenchido de verde
        gc.set_rgb_fg_color(verde)
        tela.window.draw_rectangle(gc,True,q[0],q[1],q[2],q[3])
    #desenhando a borda preta do quadrado
        gc.set_rgb_fg_color(preto)
        tela.window.draw_rectangle(gc,False,q[0],q[1],q[2],q[3])

Em gc.set_rgb_fg_color( cor ) definimos a cor a ser usada ao desenhar.

O método draw_rectangle precisa ser compreendido. Vejamos os parâmetros usados:

gc : o graphics context criado lá em cima.

False ou True : indica se o quadrado será preenchido.

q[0] até q[3]: são os quatro valores do quadrado criado lá em cima.

Faltou o botão para apagar a tela. Ele está presente no código completo mais abaixo. Por enquanto basta dizer que apaga-se a tela simplesmente limpando a lista de quadrados e disparando novamente a função ‘desenha()’.

Vamos ver como ficou o nosso programa:

#!/usr/bin/env python
import pygtk
import gtk

#declarando a lista de quadrados
quadrados=[]

def cria_quadrado(tela,evento):
    global quadrados

    #obtendo as coordenadas do clique de mouse
    coor=evento.get_coords()
    x=coor[0]
    y=coor[1]

    #criando um novo quadrado e incluindo ele na lista
    novo_quadrado=[x-25,y-25,50,50]
    quadrados.append(novo_quadrado)

    # disparando a funcao 'desenha()'
    # para incluir o nosso novo quadrado
    desenha(tela,None)

# a funcao desenha() deve ser chamada sempre que
# quisermos redesenhar a tela
# ou quando a tela precisar ser redesenhada
# por ter sido encoberta por outra janele, por
# exemplo
def desenha(tela,evento ):
    global quadrados
    #criando um "graphics context"
    gc=tela.get_style().fg_gc[gtk.STATE_NORMAL]

    #definindo as cores
    verde=gtk.gdk.Color(0,48255,0,0)
    preto=gtk.gdk.Color(0,0,0,0)
    fundo=gtk.gdk.Color(65535,65535,65535,0)

    #pintando o fundo da nossa tela
    gc.set_rgb_fg_color(fundo)
    tela.window.draw_rectangle(gc,True,0,0,800,600 )

    # desenhando cada um dos quadrados
    for q in quadrados:

        #desenhando o quadrado preenchido
        gc.set_rgb_fg_color(verde)
        tela.window.draw_rectangle(gc,True,q[0],q[1],q[2],q[3])

        #desenhando a borda do quadrado
        gc.set_rgb_fg_color(preto)
        tela.window.draw_rectangle(gc,False,q[0],q[1],q[2],q[3])

def limpar(botao):
    global quadrados
    global tela
    #limpando a lista de quadrados
    quadrados=[]

    #disparando novamente a funcao "desenha()"
    #para desenhar a tela branca sem nenhum quadrado
    desenha(tela, None)

win=gtk.Window()
win.set_title( "medeubranco.wordpress.com - Desenhando na tela com PyGTK" )
win.set_size_request(800,600)
win.connect('destroy',gtk.main_quit)

box=gtk.VBox()

#aqui a nossa tela sendo criada
tela=gtk.DrawingArea()

tela.connect('expose-event',desenha)
tela.add_events( gtk.gdk.BUTTON_PRESS_MASK )
tela.connect('button-press-event',cria_quadrado)

botao=gtk.Button( "limpar" )

botao.connect("clicked",limpar)

win.add(box)
box.set_border_width(10)

box.pack_start(tela)
box.pack_start(botao,False)
win.show_all()
gtk.main()

Olha o resultado:

Na tentativa de manter este artigo o menos complexo possível, muita coisa foi omitida aqui. Há outros métodos de desenho além do draw_rectangle, e há outras formas de lidar com uma DrawingArea.

Para se aprofundar, você pode começar por aqui:

http://www.pygtk.org/pygtk2tutorial/sec-DrawingAreaWidgetAndDrawing.html
Espero que este tutorial tenha sido útil a você e que você encontre formas criativas de usar uma DrawingArea.

Até o próximo artigo deste blog.