ImagemRasterTkinter (por JoaoBueno)
Python é famoso, entre outras coisas, por ser simples para fazer coisas rápidas. Tkinter é um toolkit que acompanha essa "fama", especialmente por não exigir a instalação de nenhum módulo adicional ao Python.
No entanto, se alguém quer desenhar - quer com elementos vetoriais, quer usando imagens raster (compostas por pixels), anos de história do desenvolvimento da Python Imaging Libray de um lado, e do toolkit Tk de outro entram no caminho da simplicidade.
Um objeto Canvas do Tkinter é praticamente um programa de edição vetorial auto-contido, em que elementos como linhas, retângulos, imagens e mesmo outros widgets de Tkinter podem ser dispostos, eventos de entrada tratados, e até é possível gerar um arquivo postscript com uma versão vetorial do conteúdo do Canvas.
No entanto, a documentação para criação desses elementos é escassa - e a documentação oficial do TKinter bastante rasa, chata de entender e com quase nenhum exemplo.
Esta receita contém uma classe para de se criar desenhos raster em tkinter, que faz toda a burocracia necessária - você cria uma instância da classe ScreenImage (uma subclasse de uma imagem da PIL), e tem disponíveis, entre outros, os métodos imagem.putpixel((x,y), (r,g,b))) para plotar um pixel, os métodos de desenho como rectangle, polygon, text - disponíveis numa instâncias de ImageDraw em imagem.draw, e sobretudo, deve chamar o método imagem.update() para sincronizar o buffer em memória com a imagem exibida na tela pelo TKinter.
Esta receita é necessária por que para se exibir uma imagem raster em TKinter é preciso ter um Tkinter.Canvas que contém uma instância de ImagemTk.PhotoImage, adicionada com o parcamente documentado Canvas.create_image. A PhotoImage, por sua vez, não pode ser objeto dos métodos de desenho disponibilizados em ImageDraw, nem tem "putpixel" - então é necessária uma imagem do módulo Image que é copiada para a PhotoImage através do update()
Código
1 #! /usr/bin/env python
2
3 import Tkinter
4 import Image, ImageTk, ImageDraw
5
6 class ScreenImage(Image.Image):
7 """
8 Helper Class to get a drawable raster surface in a tkinter app.
9 Tries to bypass most bureacracy.
10 """
11 def __init__(self, widget=None, size=(640,480), color=(255,255,255)):
12 """
13 Widget should be the container Tkinter widget for the canvas contaning this
14 image
15 """
16 width, height = size
17 #Inheriting from PIL's image class is not very clean.
18 #we have to re-issue the statements found in its "_new" constructor:
19 Image.Image.__init__(self)
20 self.im = Image.core.fill("RGB", size, color)
21 self.mode = "RGB"
22 self.size = size
23 self.draw = ImageDraw.Draw(self)
24 self.photoimage = ImageTk.PhotoImage("RGB", (width, height))
25 self.canvas = Tkinter.Canvas(widget, width=width, height=height)
26 self.canvas.create_image(width // 2, height // 2, image=self.photoimage)
27 self.canvas.pack()
28 self.update()
29
30 def update(self):
31 """Updates memory buffer to Tkinter screen"""
32 self.photoimage.paste(self)
33
34
35 if __name__ == "__main__":
36 tk = Tkinter.Tk()
37
38 i = ScreenImage(tk)
39
40 import math
41
42 for x in range(640):
43 y = 240 + 120 * math.sin((x - 320) / 320.0 * 4 * math.pi)
44 i.putpixel((x, y) , (0,0,0))
45 i.update()
46 tk.mainloop()