Embutindo imagens nos scripts Python para aplicações Tkinter
Arquivos gráficos geram uma dependência na aplicação cujo tratamento (de existência ou MD5) pode não valer a pena dependendo do tamanho da aplicação. O que fazer para evitar que o usuário exclua, renomeie, corrompa ou modifique um arquivo de imagem? Este artigo visa introduzir uma solução para este problema.
Parte 2: Criando uma aplicação básica
Um aplicativo básico em Tkinter seria:
Esse formato de aplicativo é o mais comum, mas está sujeito a uma série de adversidades como:
Para resolver esses problemas existe um recurso poderoso, porém pouco utilizado: o parâmetro "data". Assim como páginas HTML e CSS é possível embutir imagens em diversos formatos no script Python utilizando o Here Document (wikipedia).
Com o Here Document é possível referenciar uma imagem que não está no disco rígido, mas sim dentro da própria aplicação, como um dado constante. Da mesma forma que HTML e CSS (usando DataURI) a imagem precisa estar encodificada no formato base64. Dessa forma o script apresentado anteriormente ficaria mais ou menos assim:
Com esse formato a imagem se transforma numa string constante que pode ser organizada num novo módulo ou ser persistida com shelve (picke) ou mesmo num banco de dados. Existem muitas opções online para converter sua imagem para base64. Podemos citar:
Mas também podemos utilizar a biblioteca base64 do próprio Python:
Depois de criado o arquivo de texto, podemos colocar a imagem no nosso script anterior. Como exemplo vou utilizar uma imagem que está disponível neste endereço utilizando o Python Image Library - PIL:
É importante notar que no PIL é necessário desencodificar a imagem de volta porque essa biblioteca recebe o próprio conteúdo binário da imagem, enquanto que no próprio PhotoImage do Tkinter isso não é necessário.
from Tkinter import *
top = Tk()
button_image = PhotoImage(file="images/okbutton.gif") # aponta para o caminho no disco rígido
ok_button = Button(top, text="ok",image=button_image,compound=LEFT)
ok_button.grid()
top.mainloop()
top = Tk()
button_image = PhotoImage(file="images/okbutton.gif") # aponta para o caminho no disco rígido
ok_button = Button(top, text="ok",image=button_image,compound=LEFT)
ok_button.grid()
top.mainloop()
Esse formato de aplicativo é o mais comum, mas está sujeito a uma série de adversidades como:
- O usuário exclui o arquivo da pasta: o script dará um erro na terceira linha
- O usuário muda o nome do arquivo: o script dará um erro na terceira linha
- O usuário corrompe o arquivo: o script dará um erro na terceira linha
- O usuário edita a imagem num editor: o design da sua aplicação pode ser desconfigurado
Para resolver esses problemas existe um recurso poderoso, porém pouco utilizado: o parâmetro "data". Assim como páginas HTML e CSS é possível embutir imagens em diversos formatos no script Python utilizando o Here Document (wikipedia).
Com o Here Document é possível referenciar uma imagem que não está no disco rígido, mas sim dentro da própria aplicação, como um dado constante. Da mesma forma que HTML e CSS (usando DataURI) a imagem precisa estar encodificada no formato base64. Dessa forma o script apresentado anteriormente ficaria mais ou menos assim:
from Tkinter import *
top = Tk()
IMAGE = ""
button_image = PhotoImage(data=IMAGE)
ok_button = Button(top, text="ok",image=button_image,compound=LEFT)
ok_button.grid()
top.mainloop()
top = Tk()
IMAGE = "
button_image = PhotoImage(data=IMAGE)
ok_button = Button(top, text="ok",image=button_image,compound=LEFT)
ok_button.grid()
top.mainloop()
Com esse formato a imagem se transforma numa string constante que pode ser organizada num novo módulo ou ser persistida com shelve (picke) ou mesmo num banco de dados. Existem muitas opções online para converter sua imagem para base64. Podemos citar:
- http://www.greywyvern.com/code/php/binary2base64
- http://base64online.org/
- http://www.base64-image.de/
- http://www.base64-image.net/
Mas também podemos utilizar a biblioteca base64 do próprio Python:
import base64
my_image_file = open("meuarquivo.png", "rb") # le o arquivo no modo binario
file_content = my_image_file.read() # le o conteudo do arquivo
my_image_file.close()
IMAGE_ENCODED = base64.b64encode(file_content) # encodifica na base64
# entao criamos o arquivo com a imagem no formato base64
output = open("image.txt","w")
output.write(IMAGE_ENCODED)
output.close()
my_image_file = open("meuarquivo.png", "rb") # le o arquivo no modo binario
file_content = my_image_file.read() # le o conteudo do arquivo
my_image_file.close()
IMAGE_ENCODED = base64.b64encode(file_content) # encodifica na base64
# entao criamos o arquivo com a imagem no formato base64
output = open("image.txt","w")
output.write(IMAGE_ENCODED)
output.close()
Depois de criado o arquivo de texto, podemos colocar a imagem no nosso script anterior. Como exemplo vou utilizar uma imagem que está disponível neste endereço utilizando o Python Image Library - PIL:
from Tkinter import *
import base64
import ImageTk # importa o PIL
top = Tk()
IMAGE = "iVBORw0KGgoAAAANSUhEUgAAADcAAAA3CAYAAACo29JGAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29\
mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJsSURBVHja7JrrbYMwEIAvqAu4I7Qj0BHMCGQEGAF\
GgBGaEcIIZYRkBVZgBArSIbmubTAPc0Y56X7EhITP9wYuXdfBWSWAE8sl/D4Vz0DT9Fo+kg7eTgIVIxj\
rte21GiB9h2MIFQtrFVrPa8uJ1gK0WIpw4DPcXbLWs9fraDFfs2XY60Phhl8ymG9wMYKFwlqOFlOKL25\
Z9JpJa1Gvtekk6nAM44sLaw1a6zl1MmW4DwQLJbBIFV8+wQ1AP0KaHzNihCnf296SY+JYBUYRjqPFRKk\
x1be2P0YJLtGARb6PPAm2UqDoOrye53Rg0RJXpAQXK8DGOtau/fHg4ORxl9ZamzpGFS5UJA/YEuwoOB3\
YrJaKMtzYUjFpPReHTF/h7ggI0jxW7vFngWOwcOtaRgEuk6bnMTNe9/xTF3AxDpuypFtmxiPgPhRFGjD\
Gqr13NXAQZ0wRZ7mLWAgUY/0QHx2qKrvZ3PcIXceZbhLnip2O8QJtO4cY/t/QGd2xcQUXSLMU08SN7ph\
NnO1Wz0xwDLWZuOBsRZy1ruJMhmtxRz8ndjZTxNDc7zh1R11CyScAiwnrqo7Xrt3RVApyQ3fO4e8NUlF\
0jzFzOEh0de464XqqNa5xxyc1uMbgSlyKK12yaY9yxzkdSmm4j5FIVmOW5x8OZ9r5BIG4BDrH8iTgBrl\
NWK8wWB2ow7WG7r3Q1LQGN4U83BIrkLDaXLjGYvYiYzWbee628fdIwdUze0Mv4eZc+O3ourYWznTxFRA\
TGzhTWUhh4rUJ6nAq6wyf36nF2lK4WnDNFDZ6jraXLHlVo0TIJxCXy+sd5xccPfkVYADDOZXimxZ6tAA\
AAABJRU5ErkJggg=="
button_image = ImageTk.PhotoImage(data=base64.b64decode(IMAGE)) # desencodifica a imagem
ok_button = Button(top, text="ok",image=button_image,compound=LEFT)
ok_button.grid()
top.mainloop()
import base64
import ImageTk # importa o PIL
top = Tk()
IMAGE = "iVBORw0KGgoAAAANSUhEUgAAADcAAAA3CAYAAACo29JGAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29\
mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJsSURBVHja7JrrbYMwEIAvqAu4I7Qj0BHMCGQEGAF\
GgBGaEcIIZYRkBVZgBArSIbmubTAPc0Y56X7EhITP9wYuXdfBWSWAE8sl/D4Vz0DT9Fo+kg7eTgIVIxj\
rte21GiB9h2MIFQtrFVrPa8uJ1gK0WIpw4DPcXbLWs9fraDFfs2XY60Phhl8ymG9wMYKFwlqOFlOKL25\
Z9JpJa1Gvtekk6nAM44sLaw1a6zl1MmW4DwQLJbBIFV8+wQ1AP0KaHzNihCnf296SY+JYBUYRjqPFRKk\
x1be2P0YJLtGARb6PPAm2UqDoOrye53Rg0RJXpAQXK8DGOtau/fHg4ORxl9ZamzpGFS5UJA/YEuwoOB3\
YrJaKMtzYUjFpPReHTF/h7ggI0jxW7vFngWOwcOtaRgEuk6bnMTNe9/xTF3AxDpuypFtmxiPgPhRFGjD\
Gqr13NXAQZ0wRZ7mLWAgUY/0QHx2qKrvZ3PcIXceZbhLnip2O8QJtO4cY/t/QGd2xcQUXSLMU08SN7ph\
NnO1Wz0xwDLWZuOBsRZy1ruJMhmtxRz8ndjZTxNDc7zh1R11CyScAiwnrqo7Xrt3RVApyQ3fO4e8NUlF\
0jzFzOEh0de464XqqNa5xxyc1uMbgSlyKK12yaY9yxzkdSmm4j5FIVmOW5x8OZ9r5BIG4BDrH8iTgBrl\
NWK8wWB2ow7WG7r3Q1LQGN4U83BIrkLDaXLjGYvYiYzWbee628fdIwdUze0Mv4eZc+O3ourYWznTxFRA\
TGzhTWUhh4rUJ6nAq6wyf36nF2lK4WnDNFDZ6jraXLHlVo0TIJxCXy+sd5xccPfkVYADDOZXimxZ6tAA\
AAABJRU5ErkJggg=="
button_image = ImageTk.PhotoImage(data=base64.b64decode(IMAGE)) # desencodifica a imagem
ok_button = Button(top, text="ok",image=button_image,compound=LEFT)
ok_button.grid()
top.mainloop()
É importante notar que no PIL é necessário desencodificar a imagem de volta porque essa biblioteca recebe o próprio conteúdo binário da imagem, enquanto que no próprio PhotoImage do Tkinter isso não é necessário.