6007
Comentário:
|
6003
|
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 134: | Linha 135: |
sb = Scrollbar(master , orient = VERTICAL, command = text.yview) | sb = Scrollbar(app , orient = VERTICAL, command = text.yview) |
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