= Receita: Renderizador HTML em PyGTK = O seguinte código implementa um renderizador de textos HTML ultra-simples utilizando o toolkit PyGTK. 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. Esta é a versão PyGTK do RenderizadorHtml em Tkinter neste mesmo CookBook. 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 Textview do PyGTK quanto de um HTMLParser, da biblioteca padrão. == Código == {{{ #!python import pygtk pygtk.require('2.0') import gtk import pango from HTMLParser import HTMLParser class HTMLRender(gtk.TextView, HTMLParser): # Aqui definimos as tags alinhadas: __inline = [ "b", "i", "strong" ] # Aqui definimos as tags que geram seus próprios blocos: __block = [ "h1", "h2", "h3", "p", "dl", "dt", "dd" ] # Aqui algumas tags que não vamos renderizar: __ignore = [ "body", "html", "div" ] # Aqui algumas tags que geralmente são deixadas abertas: __open = [ "dt", "dd", "p" ] # Formatos e fontes aplicadas às tags __formats = { 'h1': { 'font': "sans bold 16", 'justification': gtk.JUSTIFY_CENTER, 'pixels-above-lines': 8, 'pixels-below-lines': 4 }, 'h2': { 'font': "sans bold 12", 'justification': gtk.JUSTIFY_CENTER, 'pixels-above-lines': 6, 'pixels-below-lines': 3 }, 'h3': { 'font': "sans bold italic 10", 'pixels-above-lines': 4, 'pixels-below-lines': 0 }, 'dl': { 'font': "sans 10" }, 'dd': { 'font': "sans 10", 'left-margin': 10, 'right-margin': 10, 'pixels-above-lines': 2, 'pixels-below-lines': 2 }, 'dt': { 'font': "sans bold 10", 'pixels-above-lines': 3, 'pixels-below-lines': 2 }, 'p': { 'font': "sans 10", 'pixels-above-lines': 4, 'pixels-below-lines': 4 }, 'b': { 'font': "sans bold 10", }, 'i': { 'font': "sans italic 10", }, 'strong': { 'font': "sans bold italic 10" }, 'code': { 'font': "monospace 10" } } def __init__(self, *cnf, **kw): """ Inicializamos o HTMLParser e o TextView. O TextView deve ser configurado não editável e com 'word-wraping', ou seja com quebra de linha nos limites de palavras. A formatação das tags também é inicializada. O dicionário __tags contém uma lista das tags presentes no texto e suas posições, para podermos alocar as formatações. """ gtk.TextView.__init__(self, *cnf, **kw) HTMLParser.__init__(self) self.set_editable(False) self.set_wrap_mode(gtk.WRAP_WORD) self.__tb = self.get_buffer() self.__last = None self.__tags = { } for tag in self.__formats: self.__tb.create_tag(tag, **self.__formats[tag]) def set_text(self, txt): """ O widget TextView do PyGTK é desnecessariamente complicado. Para a inserção de texto, é necessário indicar um buffer de texto; para formatar o texto, é preciso encontrar marcas e tags no texto, etc. Para simplificar, este método alimenta o texto ao parser HTML que faz a formatação automaticamente. O nome é para seguir a aparente convenção de nomes do PyGTK. """ self.feed(txt) def handle_starttag(self, tag, attr): """ Aqui manipulamos a abertura das tags. Ao ser aberta, a tag tem sua posição registrada, para que a formatação seja a- plicada posteriormente, no fechamento. """ # Se a tag deve ser ignorada, nada deve ser feito. if tag in self.__ignore: pass # Se a tag deve criar um bloco, adicionamos uma quebra de # linha ao parágrafo, para simular o efeito de blocagem. # Adicionalmente, blocos 'fecham' tags previamente abertas. elif tag in self.__block: if self.__last in self.__open: self.handle_endtag(self.__last) self.__last = tag end_iter = self.__tb.get_end_iter() self.__tb.insert(end_iter, "\n") # Marcamos a posição da tag para posterior aplicação da # formatação. end_iter = self.__tb.get_end_iter() mark = self.__tb.create_mark(None, end_iter, True) if tag in self.__tags: self.__tags[tag].append(mark) else: self.__tags[tag] = [ mark ] def handle_data(self, data): """ Este método recebe os dados de uma tag, que, tipicamente, é texto a ser renderizado. Simplesmente inserimos o texto na widget. No entanto, os renderizadores de HTML devem tratar espaços contíguos e caracteres de tabulação e quebra de página como um simples espaço em branco. Na primeira linha nós fazemos esse serviço. """ data = ' '.join(data.split()) + ' ' end_iter = self.__tb.get_end_iter() self.__tb.insert(end_iter, data) def handle_endtag(self, tag): """ Aqui as tags são fechadas. Suas posições são encontradas e as formatações são aplicadas. O processo consiste em recuperar a posição inicial em que a formatação deve ser aplicada (obtida em handle_starttag), obter a posição atual (após o texto ter sido inserido em handle_data), inserir os marcadores do Text e aplicar a formatação. O processo é extremamente simples. """ try: if tag not in self.__ignore: start_mark = self.__tags[tag].pop() start = self.__tb.get_iter_at_mark(start_mark) end = self.__tb.get_end_iter() self.__tb.apply_tag_by_name(tag, start, end) return except KeyError: pass }}} == Exemplo de uso == Como antes, esta widget foi criada para a apresentação de texto de ajuda em uma pequena aplicação. O trecho de código a seguir pode ser utilizado para a apresentação: {{{ #!python if __name__ == "__main__": q = gtk.Window() # Criamos a janela t = HTMLRender() # Criamos o renderizador s = open("docs/help.html").read() # O texto precisa ser UTF-8!!! t.set_text(s) t.show() sb = gtk.ScrolledWindow() # Criamos um visualizador com barra de rolagem... sb.add(t) # e adicionamos o texto sb.show() q.add(sb) # Por fim, vamos para o loop principal. q.show() gtk.main() }}} Volta para CookBook. ---- Por AlexandreNalon