associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Diferenças para "RenderizadorHtml"

Diferenças entre as versões de 1 e 5 (4 versões de distância)
Revisão 1e 2006-02-03 14:18:18
Tamanho: 6019
Comentário:
Revisão 5e 2006-10-04 17:35:33
Tamanho: 6003
Comentário:
Deleções são marcadas assim. Adições são marcadas assim.
Linha 47: Linha 47:
        self.__last = None
Linha 64: Linha 65:
        if tag == "dd": # hack para finalizar definicao
            self.handle_endtag("dt") # isso é feio, encontre outra solução
Linha 73: Linha 71:
            if self.__last in self.__open:
                self.handle_endtag(self.__last)
            self.__last = tag
Linha 115: Linha 116:
# E é isso aí: 105 linhas, incluindo comentários!!! # E é isso aí: 106 linhas, incluindo comentários!!!
Linha 124: Linha 125:
Linha 135: Linha 135:
sb = Scrollbar(master , orient = VERTICAL, command = self.text.yview)
text.configure(state = DISABLED, yscrollcommand = self.sb.set)
sb = Scrollbar(app , orient = VERTICAL, command = text.yview)
text.configure(state = DISABLED, yscrollcommand = sb.set)

Receita: Renderizador HTML em Tkinter

O seguinte código implementa um renderizador de textos HTML ultra-simples utilizando o toolkit Tkinter. Ele não é muito capaz, não se dá bem com textos com formatações muito complexas, não lida com imagens nem tabelas. No entanto, para textos simples, seu desempenho é bastante adequado, e é fácil extendê-lo para conter mais tags.

Além disso, é um bom exemplo de uma classe que não se daria bem sem herança múltipla. O renderizador é (precisa ser) uma widget que mostra textos, mas também precisa ser capaz de ler e entender HTML. Assim sendo, a classe é derivada tanto de um Text do Tkinter quanto de um HTMLParser, da biblioteca padrão.

Código

   1 from Tkinter import *
   2 from tkSimpleDialog import *
   3 from HTMLParser import HTMLParser
   4 
   5 class HTMLRender(Text, HTMLParser):
   6 
   7     # Aqui definimos as tags alinhadas:
   8     __inline = [ "b", "i", "strong" ]
   9 
  10     # Aqui definimos as tags que geram seus próprios blocos
  11     __block = [ "h1", "h2", "h3", "p", "dl", "dt", "dd" ]
  12 
  13     # Aqui algumas tags comuns em HTML que não vamos renderizar
  14     __ignore = [ "body", "html", "div" ]
  15 
  16     # Formatos/fontes aplicadas a cada tag:
  17     __formats = {
  18         'h1': { 'font': ("Helvetica", "16", "bold"), 'justify': CENTER },
  19         'h2': { 'font': ("Helvetica", "12", "bold"), 'justify': CENTER },
  20         'h3': { 'font': ("Helvetica", "10", "bold italic")} ,
  21         'dt': { 'font': ("Helvetica", "10", "bold") } ,
  22         'b': { 'font': ("Helvetica", "10", "bold") },
  23         'i': { 'font': ("Helvetica", "10", "italic") },
  24         'strong': { 'font': ("Helvetica", "10", "bold italic") },
  25         'default': { 'font': ("Helvetica", "10"), 'foreground': 'red'  },
  26     }
  27 
  28     def __init__(self, *cnf, **kw):
  29         """
  30         Inicializa o HTMLRender, inicializando pelas classes-mãe.
  31         O dicionário __tags contém uma lista das tags presentes no
  32         texto e suas posições, para podermos alocar as formatações.
  33         O __index é a posição de inserção do texto na widget.
  34         """
  35         Text.__init__(self, *cnf, **kw)
  36         HTMLParser.__init__(self)
  37         self.__last = None
  38         self.__tags = { }
  39         self.__index = 0
  40 
  41     def insert(self, text):
  42         """
  43         A classe Text utiliza a interface insert para inserir texto
  44         na widget, enquanto a classe HTMLParser utiliza feed. Este
  45         método se sobrepõe ao método de Text, de tal maneira que a
  46         inserção do texto causa a sua interpretação pelo parser.
  47         """
  48         self.feed(text)
  49 
  50     def handle_starttag(self, tag, attr):
  51         """
  52         Aqui manuseamos cada uma das tags abertas, aplicando a
  53         formatação adequada.
  54         """
  55         # Se a tag deve ser ignorada, nada deve ser feito.
  56         if tag in self.__ignore:
  57             pass
  58         # Se a tag cria um bloco, inserimos quebras de parágrafo
  59         # no texto para simular o efeito.
  60         elif tag in self.__block:
  61             if self.__last in self.__open:
  62                 self.handle_endtag(self.__last)
  63             self.__last = tag
  64             Text.insert(self, INSERT, "\n\n")
  65 
  66         # Inserimos a posição da tag no texto no dicionário para
  67         # marcarmos a posição em que a formatação deve ser aplicada.
  68         if tag in self.__tags:
  69             self.__tags[tag].append(self.index(INSERT))
  70         else:
  71             self.__tags[tag] = [ self.index(INSERT) ]
  72 
  73     def handle_data(self, data):
  74         """
  75         Este método recebe os dados de uma tag, que, tipicamente,
  76         é texto a ser renderizado. Simplesmente inserimos o texto
  77         na widget. No entanto, os renderizadores de HTML devem
  78         tratar espaços contíguos e caracteres de tabulação e quebra
  79         de página como um simples espaço em branco. Na primeira
  80         linha nós fazemos esse serviço.
  81         """
  82         data = ' '.join(data.split()) + ' '
  83         Text.insert(self, INSERT, data)
  84 
  85     def handle_endtag(self, tag):
  86         """
  87         Aqui as tags são fechadas. Suas posições são encontradas
  88         e as formatações são aplicadas. O processo consiste em
  89         recuperar a posição inicial em que a formatação deve ser
  90         aplicada (obtida em handle_starttag), obter a posição
  91         atual (após o texto ter sido inserido em handle_data),
  92         inserir os marcadores do Text e aplicar a formatação. O
  93         processo é extremamente simples.
  94         """
  95         try:
  96             start = self.__tags[tag].pop()
  97             end = self.index(INSERT)
  98             tag_name = "Tag%05d" % self.__index
  99             self.__index = self.__index + 1
 100             self.tag_add(tag_name, start, end)
 101             self.tag_config(tag_name, **self.__formats[tag])
 102             return
 103         except KeyError:
 104             pass
 105 
 106 # E é isso aí: 106 linhas, incluindo comentários!!!

Exemplo de uso

Eu criei esta widget para renderizar texto de ajuda em uma aplicação simples. O trecho de código que pode ser utilizado para testar esse renderizador está abaixo:

   1 app = Tk() # Criamos uma aplicacao Tk...
   2 
   3 # Note que todas as propriedades de um Text estão
   4 # disponíveis. Cuidado, pois alguns efeitos colaterais
   5 # meio esquisitos podem aparecer.
   6 text = HTMLRender(app, font = ("Helvetica", "10", ""), wrap = WORD)
   7 text.insert(open("docs/help.html").read())
   8 
   9 # Uma barra de rolagem pode ser bastante útil (mas os
  10 # detalhes fogem ao escopo deste receitinha:
  11 sb = Scrollbar(app , orient = VERTICAL, command = text.yview)
  12 text.configure(state = DISABLED, yscrollcommand = sb.set)
  13 
  14 # E, claro, precisamos colocar a widget em seu lugar.
  15 text.grid(row = 1, column = 1, sticky = N+W+E+S)
  16 sb.grid(row = 1, column = 2, sticky = N+S)
  17 
  18 # Por fim, executamos:
  19 app.mainloop()

Volta para CookBook.


por AlexandreNalon