python

Um tutorial passo a passo para SQLite em Python

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.
Esta é uma tradução livre do artigo Python: A Simple Step-by-Step SQLite Tutorial e modifiquei sutilmente algumas coisas, incluindo os exemplos. Agradeço ao Mike pelos excelentes artigos e me desculpo pelas liberdades que tomei na tradução (incluindo a mudança no nome do artigo). Vamos a ele!


SQLite é uma engine de banco de dados transacional autocontida que dispensa servidor e configuração. Python ganhou o módulo sqlite3 desde a versão 2.5, o que significa que você pode criar bancos de dados SQLite em qualquer Python, sem precisar baixar qualquer dependência adicional. A Mozilla usa bancos de dados SQLite para o Firefox armazenar marcadores e outras informações. Neste artigo, você aprenderá:

  • Como criar um banco de dados SQLite
  • Como inserir dados em uma tabela
  • Como editar os dados
  • Como deletar os dados
  • Consultas básicas de SQL

Este artigo será similar em função ao recente SQLAlchemy tutorial, que apareceu no site The Mouse vs. the Python um mês desses. Se você quiser inspecionar seus bancos de dados visualmente, você pode usar o plugin SQLite Manager para Firefox, ou se preferir pode partir para a linha de comando shell do SQLite.

Como criar um banco de dados e inserir qualquer coisa

Criar um banco de dados no SQLite é realmente muito simples, mas o processo requer que você conheça um pouco de SQL. Aqui está o código para criar um banco de dados de gerenciamento de albuns musicais:
import sqlite3

conn = sqlite3.connect(
"mydatabase.db") # ou use :memory: para botá-lo na memória RAM

cursor = conn.cursor()

# cria uma tabela
cursor.execute("""CREATE TABLE albums
                 (title text, artist text, release_date text,
                  publisher text, media_type text)
              """
)
Antes de mais nada, precisamos importar a biblioteca sqlite3 e criar uma conexão com o banco de dados. Você pode passar para ele um caminho para o arquivo, o nome do arquivo ou usar simplesmente a string especial “:memory:” e criar um banco de dados na memória. No nosso caso, criamos um arquivo no disco chamado mydatabase.db. Em seguida, criamos um objeto cursos, que lhe permite interagir com o banco de dados e adicionar registro, entre outras coisas. Usamos aqui a sintaxe SQL para criar uma tabela chamada albums e contendo 5 campos de texto: title, artist, release_date, publisher e media_type. SQLite só suporta cinco tipos de dados: null, integer, real, text and blob. Vamos agora fazer o código para inserir alguns dados na tabela!
# insere alguns dados
cursor.execute("INSERT INTO albums VALUES ('Glow', 'Andy Hunter', '7/24/2012', 'Xplore Records', 'MP3')")

# salva dados no banco
conn.commit()

# insere múltiplos registros de uma só vez usando o método "?", que é mais seguro
albums = [('Exodus', 'Andy Hunter', '7/9/2002', 'Sparrow Records', 'CD'),
         (
'Until We Have Faces', 'Red', '2/1/2011', 'Essential Records', 'CD'),
         (
'The End is Where We Begin', 'Thousand Foot Krutch', '4/17/2012', 'TFKmusic', 'CD'),
         (
'The Good Life', 'Trip Lee', '4/10/2012', 'Reach Records', 'CD')]
cursor.executemany(
"INSERT INTO albums VALUES (?,?,?,?,?)", albums)
conn.commit()

Aqui usamos o comando INSERT INTO, do SQL, para inserir um registro dentro do banco de dados. Note que cada item tem que estar envolto entre aspas simples. Isso pode ficar complicado quando você precisar inserir strings já com aspas simples incluídas. De qualquer forma, para salvar o registro no banco, nós chamamos o commit. O código que vem em seguida mostra como adicionar múltiplos registros de uma só vez usando o método executemany do cursor. Note que usamos a interrogação (?) ao invés da substituição de string (%s) para inserir valores. Usar a substituição de strings NÃO é seguro e não deve ser usado, pois pode permitir ataques de SQL injection. O método da interrogação é muito melhor e usando o SQLAlchemy é sempre melhor, pois ele faz todo o tratamento para você, e você não precisará se preocupar em converter aspas simples em algo que o SQLite aceite.

Atuializando e deletando registros

Estar apto a atualizar seus registros no banco de dados é a chave para manter seus dados nos conformes. Se você não atualizar, seus dados se tornarão rapidamente obsoletos e sem utilidade. Algumas vezes você também desejará deletar linhas dos seus dados. Cobriremos esses dois tópicos nesta sessão. Primeiro, hora de atualizar!
import sqlite3

conn = sqlite3.connect(
"mydatabase.db")
cursor = conn.cursor()

sql =
"""
UPDATE albums
SET artist = 'John Doe'
WHERE artist = 'Andy Hunter'
"""

cursor.execute(sql)
conn.commit()

Aqui usamos o comando UPDATE do SQL para atualizar nossa tabela albums. Você pode usar SET para mudar um campo, neste caso mudamos o campo artista para ser “John Doe” em qualquer registro onde o campo artist era “Andy Hunter”. Não é fácil? Note que se você não confirmar as mudanças com o commit, suas mudanças não serão escritas no banco de dados e não terão efeito. O comando DELETE é simples também. Vamos a ele!
import sqlite3

conn = sqlite3.connect(
"mydatabase.db")
cursor = conn.cursor()

sql =
"""
DELETE FROM albums
WHERE artist = 'John Doe'
"""

cursor.execute(sql)
conn.commit()

Deletar chega a ser mais fácil do que atualizar. O SQL só tem 2 linhas! Neste caso, tudo o que temos que fazer é dizer pro SQLite de que tabela deletar (albums) e quais registros deletar, usando a cláusula WHERE. O comando do exemplo apaga todo e qualquer registro que tenha “John Doe” no campo artist.

Consultas básicas do SQLite

Consultas no SQLite são basicamente as mesmas que você usa para outros bancos de dados, como o MySQL ou o Postgres. Basta você usar a sintaxe SQL normal para elaborar as consultas e executá-las com o cursor. Aqui estão alguns exemplos:
import sqlite3

conn = sqlite3.connect(
"mydatabase.db")
#conn.row_factory = sqlite3.Row
cursor = conn.cursor()

sql =
"SELECT * FROM albums WHERE artist=?"
cursor.execute(sql, [("Red")])
print cursor.fetchall()  # ou use fetchone()

print "\nAqui a lista de todos os registros na tabela:\n"
for row in cursor.execute("SELECT rowid, * FROM albums ORDER BY artist"):
   
print row

print "\nResultados de uma consulta com LIKE:\n"
sql = """
SELECT * FROM albums
WHERE title LIKE 'The%'"""

cursor.execute(sql)
print cursor.fetchall()

A primeira consulta que executamos é um SELECT * , o que significa que estamos buscando todos os campos, e buscamos todos os registros cujo nome do artista (campo artist) bata com o que informamos - no nosso caso, “Red”. Em seguida, executamos o SQL e usamos fetchall() para pegar todos os resultados. Você também pode usar fetchone() para pegar o apenas primeiro resultado. Você também notará que há uma sessão comentada relacionada com um misterioso row_factory. Se você descomentar aquela linha, os resultados serão retornados como objetos Row, que são como os dicionários do Python. Entretanto, você não pode fazer associação de item com um objeto Row.

A segunda consulta é bem parecida com a primeira, mas retorna os registros ordenados pelo nome do artista, em ordem ascendente. Esse código também demonstra como podemos percorrer o resultado executando ações. A última consulta mostra como usar o comando LIDE do SQL para buscar frases parciais.Neste caso, fizemos uma busca pela tabela por títulos que comecem com "The". O sinal de porcentagem (%) é o operador coringa.

Pra terminar

Agora você sabe como usar o Python para criar um banco de dados SQLite. Você também pode criar, atualizar e deletar registros, bem como executar consultas em seu banco de dados. Vá em frente e comece a fazer seus próprios bancos / ou compartilhe suas experiências nos comentários!

P. S.: Foto usada no post: water moccassin (poisonous) or western hognose (not poisonous), de Bikes And Books.

Special: 

wxPython: Como criar um assistente genérico

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.

Esta é uma tradução livre do artigo wxPython: How to Create a Generic Wizard e modifiquei sutilmente algumas coisas, incluindo os exemplos. Agradeço ao Mike pelos excelentes artigos e me desculpo pelas liberdades que tomei na tradução (incluindo a mudança no nome do artigo). Vamos a ele!


Outro dia eu vi no StackOverflow alguém brigando com o widget Wizard, do wxPython. O wizard não permite muita personalização quando se trata dos seus botões, assim eu decidi ver quão difícil seria escrever meu próprio assistente. Este código é bastante limitado, mas aqui está minha primeira versão beta:

# coding: utf-8
import wx
 
########################################################################
class WizardPage(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent, title=None):
        """Construtor"""
        wx.Panel.__init__(self, parent)
 
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
 
        if title:
            title = wx.StaticText(self, -1, title)
            title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
            sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
            sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)
 
 
########################################################################
class WizardPanel(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Construtor"""
        wx.Panel.__init__(self, parent=parent)
        self.pages = []
        self.page_num = 0
 
        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.panelSizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
 
        # adiciona os botões de voltar/avançar
        self.prevBtn = wx.Button(self, label="Voltar")
        self.prevBtn.Bind(wx.EVT_BUTTON, self.onPrev)
        btnSizer.Add(self.prevBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5)
 
        self.nextBtn = wx.Button(self, label="Avançar")
        self.nextBtn.Bind(wx.EVT_BUTTON, self.onNext)
        btnSizer.Add(self.nextBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5)
 
        # conclui o leiaute
        self.mainSizer.Add(self.panelSizer, 1, wx.EXPAND)
        self.mainSizer.Add(btnSizer, 0, wx.ALIGN_RIGHT)
        self.SetSizer(self.mainSizer)
 
 
    #----------------------------------------------------------------------
    def addPage(self, title=None):
        """"""
        panel = WizardPage(self, title)
        self.panelSizer.Add(panel, 2, wx.EXPAND)
        self.pages.append(panel)
        if len(self.pages) > 1:
            # esconde todos os painéis depois do primeiro
            panel.Hide()
            self.Layout()
 
    #----------------------------------------------------------------------
    def onNext(self, event):
        """"""
        pageCount = len(self.pages)
        if pageCount-1 != self.page_num:
            self.pages[self.page_num].Hide()
            self.page_num += 1
            self.pages[self.page_num].Show()
            self.panelSizer.Layout()
        else:
            print "Fim das páginas!"
 
        if self.nextBtn.GetLabel() == "Concluir":
            # fecha o aplicativo
            self.GetParent().Close()
 
        if pageCount == self.page_num+1:
            # muda a etiqueta
            self.nextBtn.SetLabel("Concluir")
 
    #----------------------------------------------------------------------
    def onPrev(self, event):
        """"""
        pageCount = len(self.pages)
        if self.page_num-1 != -1:
            self.pages[self.page_num].Hide()
            self.page_num -= 1
            self.pages[self.page_num].Show()
            self.panelSizer.Layout()
        else:
            print "Você já está na primeira página!"
 
 
########################################################################
class MainFrame(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Construtor"""
        wx.Frame.__init__(self, None, title="Assistente Genérico", size=(800,600))
 
        self.panel = WizardPanel(self)
        self.panel.addPage("Página 1")
        self.panel.addPage("Página 2")
        self.panel.addPage("Página 3")
 
        self.Show()
 
if __name__ == "__main__":
    app = wx.App(False)
    frame = MainFrame()
    app.MainLoop()

O frame principal instancia nosso painel principal (WizardPanel). Aqui é onde entra a maior parte do nosso código. Ele controla a paginação para a frente e para trás pelas páginas do assistente. Você pode definir as páginas do assistente da maneira que desejar. De fato, o que posso fazer numa segunda versão é fazer com que trabalhe com uma classe Panel de minha própria autoria, já que usar páginas simples como eu fiz o torna bastante limitado. De qualquer maneira, adicionei 3 páginas e testei as iterações entre elas. Espero que outros achem isso interessante também. Divirtam-se!

Painel autodestrutivo em wxPython

 

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.

Esta é uma tradução livre do artigo wxPython: Making a panel self-destruct e modifiquei sutilmente algumas coisas, incluindo os exemplos.


Outro dia vi uma pergunta no StackOverflow sobre como destruir e criar dinamicamente painéis depois de passado um certo tempo. Eu sugeri ao questionador que usasse os exemplos dos artigos do meu blog, onde eu destruia e criava botões, mas ele não entendeu. Desta forma, escrevi um exemplo simples onde o painel mostra um contador regressivo e então se destrói e é substituído por um outro painel.

Aqui está o código para o seu deleite:

# coding: utf-8
import wx
 
########################################################################
class PanelOne(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
 
        self.countdown = wx.StaticText(self, label="Esta mensagem se autodestruirá em 10 segundos")
 
 
########################################################################
class PanelTwo(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
 
        txt = wx.StaticText(self, label="Pow!")
 
 
########################################################################
class MainFrame(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Painel do Inspetor Bugiganga")
        self.panelOne = PanelOne(self)
        self.time2die = 10
 
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.update, self.timer)
        self.timer.Start(1000)
 
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.panelOne, 1, wx.EXPAND)
        self.SetSizer(self.sizer)
 
    #----------------------------------------------------------------------
    def update(self, event):
        """"""
        if self.time2die < 0:
            self.panelOne.Destroy()
            self.panelTwo = PanelTwo(self)
            self.sizer.Add(self.panelTwo, 1, wx.EXPAND)
            self.Layout()
            self.timer.Stop()
        else:
            msg = "Esta mensagem se autodestruirá em %s segundos" % self.time2die
            self.panelOne.countdown.SetLabel(msg)
        self.time2die -= 1
 
if __name__ == "__main__":
    app = wx.App(False)
    frame = MainFrame()
    frame.Show()
    app.MainLoop()

Quando você executar este código, você verá algo parecido com isso:

Tela em contagem

A contagem durará 10 segundos, então você verá isso:

Tela após a contagem

Se você quiser aprender mais sobre contadores, eu escrevi um artigo que trata do assunto. Divirta-se!

Special: 

Como baixar um arquivo com Python

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.

Esta é uma tradução livre do artigo Python 101: How to Download a File e modifiquei sutilmente algumas coisas, incluindo os exemplos.


Baixar arquivos da internet é o tipo de coisa que a maioria dos programadores, mais cedo ou mais tarde, vai terminar tendo que fazer. Python oferece várias formas de fazer exatamente isso, usando a biblioteca padrão. Provavelmente a forma mais popular de baixar um arquivo é sobre HTTP, usando o módulo urllib ou urllib2. Python também vem com ftplib para downloads via FTP. Para concluir, há um módulo de terceiros chamado requests que tem causando alvoroço (e está disponível no repositório padrão do Trisquel - e do Ubuntu - no pacote python-requests). Neste artigo, focaremos os módulos urllib e request.

Uma vez que se trata de uma tarefa bastante simples, mostraremos apenas um script rápido e sapecado, que baixa o mesmo arquivo com cada um dos módulos e nomeia o resultado de maneira levemente diferente. Baixaremos um arquivo PDF do cordel Fantasma d Opera, daqui do Bardo WS mesmo, para usar no nosso exemplo. Vamos dar uma sacada

# Python 2 code
import urllib
import urllib2
import requests
 
url = 'http://www.carlissongaldino.com.br/modules/pubdlcnt/pubdlcnt.php?file=http://www.carlissongaldino.com.br/sites/default/files/o-fantasma-da-opera.pdf&nid=1287'
 
print "baixando com urllib"
urllib.urlretrieve(url, "o-fantasma-da-opera-u.pdf")
 
print "baixando com urllib2"
f = urllib2.urlopen(url)
data = f.read()
with open("o-fantasma-da-opera-u2.pdf", "wb") as code:
    code.write(data)
 
print "baixando com requests"
r = requests.get(url)
with open("o-fantasma-da-opera-r.pdf", "wb") as code:
    code.write(r.content)

Como você pode ver, urllib precisa de apenas uma linha. Essa simplicidade o torna muito fácil de usar. Por outro lado, as outras duas bibliotecas também são muito simples. Para urllib2, você precisa apenas abrir a url, lê-la e escrever seus dados. Na verdade, você pode siplificar ainda mais esse script - em uma linha - fazendo o seguinte:

f = urllib2.urlopen(url)
with open("o-fantasma-da-opera-u2.pdf", "wb") as code:
    code.write(f.read())

De um jeito ou de outro, o código funciona muito bem. O método para o módulo requests é get, que corresponde ao HTTP GET. Você então pega o objeto requests e chama a propriedade content para escrever os dados onde você quiser. Nós optamos pelo statement with por ele fechar automaticamente o arquivo e simplificar o código. Note que usando siplesmente o "read()" pode ser perigoso, se o arquivo for muito grande. É melhor lê-lo aos poucos, passando um tamanho ao read.

Atualizado (8 de junho de 1012)

Como apontado por um dos leitores do The Mouse vs. The Python, o uso da urllib muda consideravelmente quando a utilizamos através do 2to3.py, no formato do Python 3. Assim, para complementar, aqui está como o código se parece:

# Python 3 code
import urllib.request, urllib.parse, urllib.error
 
url = 'http://www.carlissongaldino.com.br/modules/pubdlcnt/pubdlcnt.php?file=http://www.carlissongaldino.com.br/sites/default/files/o-fantasma-da-opera.pdf&nid=1287'
 
print("baixando com urllib")
urllib.request.urlretrieve(url, "o-fantasma-da-opera-u.pdf")
 
print("baixando com urllib2")
f = urllib.request.urlopen(url)
data = f.read()
with open("o-fantasma-da-opera-u2.pdf", "wb") as code:
    code.write(data)

Você nottará que urllib2 não existe mais e que urllib.urlretrieve e urllib2.urlopen mudou para urllib.request.urlretrieve e urllib.request.urlopen, respectivamente. O resto continua o mesmo, de modo que eu removi as outras partes.

Então está feito! Agora você pode baixar arquivos programando em Python 2 ou 3!

P. S.: Imagem do post: Python vert / Morelia Viridis, de raym5

Special: 

Criando QR Codes com Python

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.

Esta é uma tradução livre do artigo Creating QR Codes with Python e modifiquei sutilmente algumas coisas, incluindo os exemplos.

QR Code, para quem não sabe, é um estilo de código de barras bidimensional, podendo conter um fragmento de texto, o endereço de um site, um email ou mesmo uma ficha de informações pessoais básicas (reunindo tudo isso). Veja mais sobre esses códigos na Wikipedia.


QR Porco

Outro dia eu pensei que seria divertido criar em wxPython um pequeno programa que gerasse códigos QR e os mostrasse na tela. Eu queria fazer tudo com Python, assim eu procurei e encontrei 3 candidatos:

Testei o python-qrcode e o pyqrnative uma vez já que ambos funcionam tanto no Windows quanto no Mac e no Linux. Eles também não requerem nada a não ser o Python Imaging Library. O projeto pyqrcode requer vários outros pré-requisitos e não funciona em Windows, assim eu nem quis mexer nele. Terminei pegando algum código antigo baseado na minha aplicação Photo Viewer e o modificando levemente para torná-lo um visualizador de QR Code. Se isso te interessa, então continue lendo!

Começando

Como eu disse no início, você precisará da Python Imaging Library. Usaremos o wxPython para fazer a parte gráfica, assim você precisará deles também. E você vai ter que usar o python-qrcode ou o pyqrnative. A principal diferença que achei é que o python-qrcode é muito mais rápido na geração de imagens e gera o tipo que provavelmente você vai ver por aí. Por alguma razão, pyqrnative leva mais tempo pra executar e gera QR codes de aspecto muito mais denso. Deve haver opções para esses projetos que nos permitam gerar diferentes tipos de códigos, mas a documentação para ambos é abismal. Terminei usando o código e a IDE Wingware para navegar pelo código mais do que qualquer outra coisa.

Gerando QR Codes

De qualquer forma, uma vez que você tenha os pré-requisitos, você pode executar o seguinte código e ver o que o Python pode fazer:

# coding: utf-8
import os
import wx
 
try:
    import qrcode
except ImportError:
    qrcode = None
 
try:
    import PyQRNative
except ImportError:
    PyQRNative = None
 
########################################################################
class QRPanel(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)
        self.photo_max_size = 240
        sp = wx.StandardPaths.Get()
        self.defaultLocation = sp.GetDocumentsDir()
 
        img = wx.EmptyImage(240,240)
        self.imageCtrl = wx.StaticBitmap(self, wx.ID_ANY,
                                         wx.BitmapFromImage(img))
 
        qrDataLbl = wx.StaticText(self, label="Texto a transformar no QR Code:")
        self.qrDataTxt = wx.TextCtrl(self, value="http://www.mousevspython.com", size=(200,-1))
        instructions = "Nome do arquivo do QR Code"
        instructLbl = wx.StaticText(self, label=instructions)
        self.qrPhotoTxt = wx.TextCtrl(self, size=(200,-1))
        browseBtn = wx.Button(self, label='Change Save Location')
        browseBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
        defLbl = "Default save location: " + self.defaultLocation
        self.defaultLocationLbl = wx.StaticText(self, label=defLbl)

        qrcodeBtn = wx.Button(self, label="Criar QR com qrcode")
        qrcodeBtn.Bind(wx.EVT_BUTTON, self.onUseQrcode)
        pyQRNativeBtn = wx.Button(self, label="Criar QR com PyQRNative")
        pyQRNativeBtn.Bind(wx.EVT_BUTTON, self.onUsePyQR)
 
        # Create sizer
        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
        qrDataSizer = wx.BoxSizer(wx.HORIZONTAL)
        locationSizer = wx.BoxSizer(wx.HORIZONTAL)
        qrBtnSizer = wx.BoxSizer(wx.VERTICAL)

        qrDataSizer.Add(qrDataLbl, 0, wx.ALL, 5)
        qrDataSizer.Add(self.qrDataTxt, 1, wx.ALL|wx.EXPAND, 5)
        self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY),
                           0, wx.ALL|wx.EXPAND, 5)
        self.mainSizer.Add(qrDataSizer, 0, wx.EXPAND)
        self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
        locationSizer.Add(instructLbl, 0, wx.ALL, 5)
        locationSizer.Add(self.qrPhotoTxt, 0, wx.ALL, 5)
        locationSizer.Add(browseBtn, 0, wx.ALL, 5)
        self.mainSizer.Add(locationSizer, 0, wx.ALL, 5)
        self.mainSizer.Add(self.defaultLocationLbl, 0, wx.ALL, 5)
 
        qrBtnSizer.Add(qrcodeBtn, 0, wx.ALL, 5)
        qrBtnSizer.Add(pyQRNativeBtn, 0, wx.ALL, 5)
        self.mainSizer.Add(qrBtnSizer, 0, wx.ALL|wx.CENTER, 10)
 
        self.SetSizer(self.mainSizer)
        self.Layout()

    #----------------------------------------------------------------------
    def onBrowse(self, event):
        """"""
        dlg = wx.DirDialog(self, "Escolher  um diretório:",
                           style=wx.DD_DEFAULT_STYLE)
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.defaultLocation = path
            self.defaultLocationLbl.SetLabel("Local de salvamento: %s" % path)
        dlg.Destroy()
 
    #----------------------------------------------------------------------
    def onUseQrcode(self, event):
        """

https://github.com/lincolnloop/python-qrcode

        """

        qr = qrcode.QRCode(version=1, box_size=10, border=4)
        qr.add_data(self.qrDataTxt.GetValue())
        qr.make(fit=True)
        x = qr.make_image()
 
        qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
        img_file = open(qr_file, 'wb')
        x.save(img_file, 'JPEG')
        img_file.close()
        self.showQRCode(qr_file)
 
    #----------------------------------------------------------------------
    def onUsePyQR(self, event):
        """

http://code.google.com/p/pyqrnative/

        """

        qr = PyQRNative.QRCode(20, PyQRNative.QRErrorCorrectLevel.L)
        qr.addData(self.qrDataTxt.GetValue())
        qr.make()
        im = qr.makeImage()
 
        qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
        img_file = open(qr_file, 'wb')
        im.save(img_file, 'JPEG')
        img_file.close()
        self.showQRCode(qr_file)
 
    #----------------------------------------------------------------------
    def showQRCode(self, filepath):

        """"""
        img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
        # redimensionar imagem, preservando proporção
        W = img.GetWidth()
        H = img.GetHeight()
        if W > H:
            NewW = self.photo_max_size
            NewH = self.photo_max_size * H / W
        else:
            NewH = self.photo_max_size
            NewW = self.photo_max_size * W / H
        img = img.Scale(NewW,NewH)
 
        self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
        self.Refresh()
 
 
########################################################################
class QRFrame(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Visualizador de QR Code", size=(550,500))
        panel = QRPanel(self)
 
if __name__ == "__main__":
    app = wx.App(False)
    frame = QRFrame()
    frame.Show()
    app.MainLoop()

O código para modificar e exibir imagens é tratado no artigo anterior que eu escrevi (e com link acima), assim as únicas partes com que você provavelmente terá que se preocupar são dois métodos para gerar códigos QR: onUseQrcode e onUsePyQR. Eu apenas peguei alguns exemplos de seus respectivos sites e os modifiquei para criar as imagens de códigos QR. Eles são muito simples, mas não são bem documentados, de modo que eu não posso realmente lhe dizer o que está acontecendo neles. Infelizmente, no momento em que escrevia este texto, o código desses projetos carece seriamente de comentários, com apenas alguns aqui e ali. Ainda assim, eu estava apto a gerar códigos QR decentes. O código a seguir foi feito usando python-qrcode.

Tela do código em execução, modo qrcode

Como vovcê podever, é um código padrão bastante simples. O próximo foi criado com PyQRNative e tem um aspecto muito mais denso:

Imagem do código em execução, modo PyQRNative

Tentei escanear ambas as imagens com o aplicativo leitor de códigos debarra do meu celular com android e ambos os códigos QR foram corretamente lidos por ele. Assim, se você precisa gerar códigos QR para o seu projeto, tenho esperanças de que ao menos um dos dois atenda às suas necessidades!

ATUALIZAÇÃO 21/05/2012

Um dos meus leitores (Mike Farmer) me contactou recentemente sobre seus experimentos com PyQRNative e me disse que "o primeiro parâmetro é o tamanho do container e o segundo é a correção de redundância/erro". Eu meio que adivinho o que o primeiro parâmetro quer dizer, mas realmente não entendi isso de correção de erro. Felizmente, Sr. Farmer me explicou isso: Se a correção de erros é menor, manchas e marcas não serão toleradas na leitura. Se aumentar o nível de erro, obviamente o código qr será aumentado, mas o que foi feito foi a duplicação dos dados dentro da etiqueta. Assim, se a etiqueta for manchada ou rasgada, ainda poderá sr possível ler e recuperar os dados restantes. Deste modo, se sua aplicação estiver criando etiquetas que podem ser danificadas, é sábio aumentar a correção de erros. Você também pode fazer coisas legais com isso, como sobrepor imagens ou textos na tag, aumentando a correção de erros, fazendo com que os dados redundantes tolerem o "dano". De qualquer forma, se você mudar o primeiro número, você pode expandir o tamanho do da imagem do código QR. Por que você faria isso? Bem, quanto mais informações você precisar guardar na imagem, maior ela precisará ser. Sr. Farmer veio com um código de testes divertido que nos ajuda a descobrir qual o tamanho mínimo que um código QR deve ter. Estou reproduzindo o código a seguir:

import PyQRNative
 
def makeQR(data_string,path,level=1):
    quality={1: PyQRNative.QRErrorCorrectLevel.L,
             2: PyQRNative.QRErrorCorrectLevel.M,
             3: PyQRNative.QRErrorCorrectLevel.Q,
             4: PyQRNative.QRErrorCorrectLevel.H}
    size=3
    while 1:
        try:
            q = PyQRNative.QRCode(size,quality[level])
            q.addData(data_string)
            q.make()
            im=q.makeImage()
            im.save(path,format="png")
            break
        except TypeError:
            size+=1

P. S.: foto usada no post: QRCode Piggy Bank, de B3OK.

Special: 

wxPython: Tudo sobre menus

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.

Esta é uma tradução livre do artigo wxPython: All about menus e modifiquei sutilmente algumas coisas, incluindo os exemplos. Agradeço ao Mike pelos excelentes artigos e me desculpo pelas liberdades que tomei na tradução (incluindo a mudança no nome do artigo). Vamos a ele!


Menus são onipresentes. Eles estão praticamente em quase os programas para desktop. Você os usa para editar preferências ou configurar seu programa. Em wxPython, há várias opções de menu para se escolher. A mais familiar é provavelmente wx.Menu. Mas há menus popup e uma implementação própria do Python conhecida como FlatMenu. Nós cobriremo aqui o wx.Menu e os menus popup porque ambos estão relacionados. FlatMenu inclui a API toolbar, assim você vai ter que esperar por um outro artigo que trate exclusivamente desses widgets. Então vamos começar a festa dos menus!

Um Exemplo Simples de Menu

Um exemplo simples de menu

Vamos começar com algo realmente simples. Um menu que só tem uma opção: Sair. Veja o código:

import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        menuBar = wx.MenuBar()
        fileMenu = wx.Menu()
        exitMenuItem = fileMenu.Append(wx.NewId(), "Sair",
                                       "Sair do programa")
        menuBar.Append(fileMenu, "&Arquivo")
        self.Bind(wx.EVT_MENU, self.onExit, exitMenuItem)
        self.SetMenuBar(menuBar)
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """"""
        self.Close()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Vamos detalhar melhor isso mais adiante. Para criarmos a barra de menu, instanciamos wx.MenuBar. Então criamos uma instância de wx.Menu, che chamamos de filemenu. Finalmente, adicionamos o item "Sair". Em essência, nós empilhamos wx.MenuItem, mas isso foi um tipo de atalho, já que não chegamos a instanciar um wx.MenuItem primeiro. Nós mostraremos como fazer isso no nosso próximo exemplo. Note que ao adicionarmos um item, temos que passar um id, uma string de etiqueta e uma string de estado. Esta última será mostrada quando você posicionar o mouse sobre o item de menu, desde que você tenha uma barra de estado. Not que para anexar um manipulador de eventos ao item de menu, você precisa usar o evento EVT_MENU e vinculá-lo ao frame. A seguir adicione o Menu propriamente ao objeto MenuBar e passe uma string também, que é neste caso "Arquivo". Finalmente, chamamos o método SetMenuBar do frame para anexar a barra de menu ao frame.

Adicionando uma imagem ao menu

Isso é tudo o que é preciso para criarmos um menu! Agora vamos mostrar um exemplo mais complex! Nota: para seguir o exemplo abaixo, você precisará usar seu próprio arquivo de imagem.

wx.Menu Imagem

# coding: utf-8
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        # create the menubar
        menuBar = wx.MenuBar()
 
        # create the first menu (starting on left)
        carMenu = wx.Menu()
        carMenu.Append(101, "&Ford", "Fabricante estadunidense")
        carMenu.Append(102, "&Nissan", "")
        carMenu.Append(103, "&Toyota", "Japoneses!")
        carMenu.Append(104, "&Sair", "Fechar a aplicação")
 
        # add a picture to a menu
        picMenu = wx.Menu()
        item = wx.MenuItem(picMenu, wx.ID_ANY, "Cobra", "Este menu tem uma imagem!")
        img = wx.Image('snake32.png', wx.BITMAP_TYPE_ANY)
        item.SetBitmap(wx.BitmapFromImage(img))
        picMenu.AppendItem(item)
 
        # add menus to menubar
        menuBar.Append(carMenu, "&Veículos")
        menuBar.Append(picMenu, "&Imagens")
        self.SetMenuBar(menuBar)
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """"""
        self.Close()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Este exemplo é similar ao primeiro. A principal diferença é que nós adicionamos itens no lugar do menu File. Note que desta vez, especificamos nossos números ID explicitamente. Isso não é geralmente recomendado porque você pode terminar substituindo algum ID requerido pelo wx. Porém, você deve ter visto exemplos assim pela Internet. A próxima grande diferença não vem antes do picMenu. Aqui nós criamos um wx.MenuItem e adicionamos uma imagem a ele via wx.Image e o método SetBitmap do MenuItem. O resto é basicamente o mesmo.

Agora vamos investir algum tempo vendo como adicionar botões de marcar e selecionar (check e radio, respectivamente) ao nosso menu.

Adicionando Botões de Marcar e de Selecionar (Radio)

Tela demonstrando uso de radio e check boxes em menus com wxPython

Adicionar um botão de marcar ou selecionar ao seu menu é bastante simples. Vamos dar uma olhada em como isso é feito!

# coding: utf-8
# radiocheck.py
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        # Create menu bar
        menuBar = wx.MenuBar()
 
        # Create radio menu
        radioMenu = wx.Menu()
        idleItem = radioMenu.Append(wx.NewId(), "IDLE",
                                   "um shell para Python feito em Tcl/Tk",
                                   wx.ITEM_RADIO)
        pyCrustItem = radioMenu.Append(wx.NewId(),"PyCrust",
                                      "um shell para Python feito em wxPython",
                                      wx.ITEM_RADIO)
        psiItem = radioMenu.Append(wx.NewId(), "psi",
                                  "um shell simples para Python feito em wxPython",
                                  wx.ITEM_RADIO)
        menuBar.Append(radioMenu, "&Radio")
 
        # create check menu
        checkMenu = wx.Menu()
        wgItem = checkMenu.Append(wx.NewId(), "Wells Fargo", "", wx.ITEM_CHECK)
        citiItem = checkMenu.Append(wx.NewId(), "Citibank", "", wx.ITEM_CHECK)
        geItem = checkMenu.Append(wx.NewId(), "GE Money Bank", "", wx.ITEM_CHECK)
        menuBar.Append(checkMenu, "&Check")
 
        # Attach menu bar to frame
        self.SetMenuBar(menuBar)
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Sim, como você pode ver, tudo o que você precisa fazer é adicionar uma flag wx.ITEM_RADIO ou wx.ITEM_CHECK como um tipo de parâmetro, que é o quarto parâmetro. Por que é chamado de "tipo" ao invés de "estilo" como nos outros componentes visuais? Bem, enquanto discutia sobre isso no canal de IRC do wxPython, Robin Dunn (criador do wxPython) disse que provavelmente é assim por se tratarem de tipos de itens de menu diferentes.

Sub-Menus

Tela de exemplo do uso de submenus

A biblioteca wxPython suporta sub-menus. Aqui está um exemplo realmente simples que nos mostra como se faz iso.

# coding: utf-8
# submenu.py
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        menuBar = wx.MenuBar()
        fileMenu = wx.Menu()
        openMenuItem = fileMenu.Append(wx.NewId(), "Abrir")
 
        # create a submenu
        subMenu = wx.Menu()
        historyMenuItem = subMenu.Append(wx.NewId(), "Mostrar Histórico")
        fileMenu.AppendMenu(wx.NewId(), "Histórico", subMenu)
 
        exitMenuItem = fileMenu.Append(wx.NewId(), "Sair",
                                       "Deixar a aplicação")
        menuBar.Append(fileMenu, "&Arquivo")
        self.Bind(wx.EVT_MENU, self.onExit, exitMenuItem)
        self.SetMenuBar(menuBar)
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """"""
        self.Close()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

O segredo aqui é que ao invés de usarmos o método Append do filemenu, usamos o método AppendMenu. Como o novo sugere, ele permite ao programador acrescentar um menu ao invés de um item de menu. Yup, é isso aí!

Menus Flutuantes (ou Pop-up ou ContextMenus)

Tela de exemplo de uso dos menus flutuantes

Menus flutuantes são menus geralmente acessados através do botão direito do mouse em um navegador ou em um arquivo. Eles também são conhecidos como Menus de Contexto (ou menus pop-up). Aqui temos um exemplo bastante trivial para você estudar.

# coding: utf-8
# submenu.py
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial de Menu Flutuante")
 
        panel = wx.Panel(self, wx.ID_ANY)
 
        lbl = wx.StaticText(panel, label="Clique com o botão direito em qualquer lugar por aqui!")
        self.Bind(wx.EVT_CONTEXT_MENU, self.onContext)
 
    #----------------------------------------------------------------------
    def onContext(self, event):
        """
        Create and show a Context Menu
        """
 
        # only do this part the first time so the events are only bound once 
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()
            self.itemTwoId = wx.NewId()
            self.itemThreeId = wx.NewId()
            self.Bind(wx.EVT_MENU, self.onPopup, id=self.popupID1)
            self.Bind(wx.EVT_MENU, self.onPopup, id=self.itemTwoId)
            self.Bind(wx.EVT_MENU, self.onExit, id=self.itemThreeId)
 
        # build the menu
        menu = wx.Menu()
        itemOne = menu.Append(self.popupID1, "ItemUm")
        itemTwo = menu.Append(self.itemTwoId, "ItemDois")
        itemThree = menu.Append(self.itemThreeId, "Sair")
 
        # show the popup menu
        self.PopupMenu(menu)
        menu.Destroy()
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """
        Exit program
        """
        self.Close()
 
    #----------------------------------------------------------------------
    def onPopup(self, event):
        """
        Print the label of the menu item selected
        """
        itemId = event.GetId()
        menu = event.GetEventObject()
        menuItem = menu.FindItemById(itemId)
        print menuItem.GetLabel()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Para começar, ligamos wx.EVT_CONTEXT_MENU ao frame. Isso nos permite clicar com o botão direito em qualquer lugar e acionar o evento de menu de contexto, que criará e mostrará o menu flutuante. O código no método onContext é baseado no demo do wxPython para menus flutuantes. Como você pode ver, usamos uma checagem condicional, testando s o evento de menu já foi ativado. Se ele já foi ativado, não o será novamente. Em seguida, criamos nosso menu de um modo bem parecido com os anteriores. Finalmente, chamamos o método PopupMenu do frame e passamos para ele o nosso novo menu. Ele então mostra o menu ao usuário. Quando o usuário clica em um item do menu, o evento associado ao item será ativado e o menu será destruído.

Os primeiros dois itens do menu estão ligados ao método onPopup. Isso nos permite ver como podemos acessar o Menu e os atributos de um MenuItem. Você pode obter o id do menu com o evento e do Menu em si com o método GetEventObject do evento. Assim você pode usar o método FindItemById do menu para pegar um controlador para o item de menu propriamente. Finalmente, imprimimos a etiqueta do item de menu.

E pra terminar

Now you should know most of the menu methods and how to create them, bind events and make different kinds of menu items. You even know how to create popup menus! Now you can make your applications have fancy menus too.

Agora você já conhece grande parte dos métodos de menu e como criar menus, controlar eventos e fazer diferentes tipos de itens de menu. Você também sabe como criar menus flutuantes! Agora você pode fazer suas aplicações também terem menus originais.

Foto do post: Black Rat Snake, de cotinis.

Convertendo centenas de imagens em alguns PDFs com Python

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.

Esta é uma tradução livre do artigo Reportlab: Converting Hundreds of Images Into PDFs e modifiquei sutilmente algumas coisas, incluindo os exemplos. Agradeço ao Mike pelos excelentes artigos e me desculpo pelas liberdades que tomei na tradução (incluindo a mudança no nome do artigo). Vamos a ele!


Recentemente me pediram para converter algumas centenas de imagens em páginas de um PDF. Um amigo meu desenha comics e meu irmão queria poder lê-lo em um tablet. Acontece que se você tem um monte de arquivos nomeados assim:

'Jia_01.Jpg', 'Jia_02.Jpg', 'Jia_09.Jpg', 'Jia_10.Jpg', 'Jia_11.Jpg', 'Jia_101.Jpg'

o tablet Android vai reordená-los mais ou menos assim:

'Jia_01.Jpg', 'Jia_02.Jpg', 'Jia_09.Jpg', 'Jia_10.Jpg', 'Jia_101.Jpg', 'Jia_11.Jpg'

E se torna muito confuso ler com os arquivos fora de ordem desse jeito. Infelizmente, até mesmo o Python ordena os arquivos desse modo. Tentei usar o módulo glob diretamente e então ordenar o resultado e resultou no mesmo problema. Assim a primeira coisa que eu tinha que fazer era achar algum tipo de ordenação que organizasse os arquivos corretamente. Deve ser notado que o Windows 7 pode ordenar os arquivos corretamente no sistema de arquivos, mas o Python não.

Depois de uma pequena busca no Google, encontrei o seguinte script no StackOverflow:

import re
 
#----------------------------------------------------------------------
def sorted_nicely( l ):
    """
    Sort the given iterable in the way that humans expect.
    """
    convert = lambda text: int(text) if text.isdigit() else text
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
    return sorted(l, key = alphanum_key)

Funcionou perfeitamente! Agora eu só tinha que arrumar um jeito de colocar cada página da HQ em sua própria página dentro do arquivo PDF. Felizmente, a biblioteca reportlab torna essa tarefa muito fácil de realizar. Você só precisa percorrer as imagens e inserí-las uma a uma em uma página. É mais fácil dar uma olhada no código, assim vejamos:

import glob
import os
import re
 
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Image, PageBreak
from reportlab.lib.units import inch
 
#----------------------------------------------------------------------
def sorted_nicely( l ):
    """
    # http://stackoverflow.com/questions/2669059/how-to-sort-alpha-numeric-set-in-python
 
    Sort the given iterable in the way that humans expect.
    """
    convert = lambda text: int(text) if text.isdigit() else text
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
    return sorted(l, key = alphanum_key)
 
#----------------------------------------------------------------------
def create_comic(fname, front_cover, back_cover, path):
    """"""
    filename = os.path.join(path, fname + ".pdf")
    doc = SimpleDocTemplate(filename,pagesize=letter,
                            rightMargin=72,leftMargin=72,
                            topMargin=72,bottomMargin=18)
    Story=[]
    width = 7.5*inch
    height = 9.5*inch
 
    pictures = sorted_nicely(glob.glob(path + "\\%s*" % fname))
 
    Story.append(Image(front_cover, width, height))
    Story.append(PageBreak())
 
    x = 0
    page_nums = {100:'%s_101-200.pdf', 200:'%s_201-300.pdf',
                 300:'%s_301-400.pdf', 400:'%s_401-500.pdf',
                 500:'%s_end.pdf'}
    for pic in pictures:
        parts = pic.split("\\")
        p = parts[-1].split("%s" % fname)
        page_num = int(p[-1].split(".")[0])
        print "page_num => ", page_num
 
        im = Image(pic, width, height)
        Story.append(im)
        Story.append(PageBreak())
 
        if page_num in page_nums.keys():
            print "%s created" % filename
            doc.build(Story)
            filename = os.path.join(path, page_nums[page_num] % fname)
            doc = SimpleDocTemplate(filename,
                                    pagesize=letter,
                                    rightMargin=72,leftMargin=72,
                                    topMargin=72,bottomMargin=18)
            Story=[]
        print pic
        x += 1
 
    Story.append(Image(back_cover, width, height))
    doc.build(Story)
    print "%s created" % filename
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    path = r"C:\Users\Mike\Desktop\Sam's Comics"
    front_cover = os.path.join(path, "FrontCover.jpg")
    back_cover = os.path.join(path, "BackCover2.jpg")
    create_comic("Jia_", front_cover, back_cover, path)

Vamos detalhar isso um pouco. Como sempre ocorre, você tem alguns imports necessários que são exigidos para que o código funcione. Você vai notar que nós também temos dentro do código a função sorted_nicely de que falamos anteriormente. A função principal se chama create_comic e recebe quatro parâmetros: fname, front_cover, back_cover, path. Se você já usou o reportlab antes, deve ter reconhecido o SimpleDocTemplate e o Story, que são mostrados no referido tutorial

De qualquer forma, você faz um laço (loop) nas imagens ordenadas e vai adicionando as imagens ao Story, sempre com um objeto PageBreak. A razão de haver um condicional dentro do laço é que eu descobri que se eu tentar construir o PDF com todas as 400+ imagens, termina ocorrendo um erro de memória. Assim eu quebrei o trabalho em uma série de documentos onde cada um tem 100 páginas ou menos. Ao final do documento, você tem que chamar o método build do objeto doc para criar o documento PDF propriamente.

Agora você sabe como eu converti um monte de imagens em alguns documentos PDF. Teoricamente, você pode usar o PyPdf para juntar todos os PDFs resultantes em um PDF único, mas eu não tentei fazer isso. Pode ser que apareça um outro erro de memória. Deixo esse teste como um exercício para o leitor.

P. S.: A foto deste post é de NobMouse.

Special: 

Criando PDFs em Python com Reportlab

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá. Artigos estilo receita de bolo. Já traduzi um deles em outros tempos, falando sobre edição de registros do Windows: Editando o Registro do Windows em Python com o _winreg.

No final do ano passado, o Mike publicou um Top 10 de seus artigos. Aproveitei a lista e traduzi o artigo mais visitado de seu blog em 2011. Não é somente questão de popularidade, o artigo é muito bom e fala sobre como gerar arquivos PDF em Python: A Simple Step-by-Step Reportlab Tutorial.

Esta é uma tradução livre e modifiquei sutilmente algumas coisas, incluindo os exemplos. Agradeço ao Mike pelos excelentes artigos e me desculpo pelas liberdades que tomei na tradução (incluindo a mudança no nome do artigo). Vamos a ele!


O subtítulo deste artigo poderia facilmente ser "Como criar PDFs com Python", mas o Wordpress não suporta isso. De qualquer forma, a melhor biblioteca é a Reportlab.. Ela não é distribuída junto com a biblioteca padrão, de modo que você precisará baixá-la para poder executar com sucesso os exemplos deste tutorial. Você vai se deparar com pelo menos um exemplo de como colocar uma imagem no PDF, que quer dizer que você também precisará da Python Imaging Library (PIL). Pelo que entendi, Reportlab é compatível com Python 2.x, IronPython e Jython. Eles Eles estão trabalhando em um port para Python 3.x (ou estarão muito em breve)

Instalação

Reportlab suporta a maioria dos métodos de instalação em Python. Você tem a opção de baixar o código-fonte e rodar “python setup.py install” ou rodar um instalador de binário (em Windows). Havia uma discussão recente na lista de email que indicava que eles adicionariam suporte a pip também. A discussão que eu li sobre suporte do Reportlab ao easy_install é confusa, de modo que eu não estou certo se eles já dão suporte àquele método ou não. (Nota do Tradutor: em Trisquel ou outra distribuição GNU/Linux baseada em Debian, você pode instalar facilmente o Reportlab e o PIL com o comando "apt-get install python-reportlab python-imaging")

Criando um PDF simples

Reportlab tem uma documentação decente. O que eu quero dizer com isso é que a documentação nos dá aquilo de que precisamos para começar, mas quando você acha alguma coisa um tanto mais complexa para fazer, você tem correr com suas próprias pernas. Só recentemente eles adicionaram uma sessão Code Snippets ao site, que espero que venha a agrupar receitas, dicas e truques, de modo que melhore essa questão. Mas basta disso. Vamos ver como se cria alguma coisa.

No Reportlab, o componente de nível mais baixo que é usado regularmente é o objeto canvas do pacote pdfgen. As funções nesse pacote lhe permitem "pintar" um documento com seu texto, suas imagens, linhas ou o que você quiser. Eu

In Reportlab, the lowest-level component that’s used regularly is the canvas object from the pdfgen package. The functions in this package allow you to “paint” a document with your text, images, lines or whatever. Já ouvi algumas pessoas descreverem isso como escrever em Postscript. Eu tenho minhas dúvidas se isso é realmente tão ruim assim. Pela minha experiência, é bem parecido com você usar um toolkit para fazer interface gráfica em regiões específicas, com posicionamentos absolutos. Vejamos como o objeto canvas funciona:

from reportlab.pdfgen import canvas
 
c = canvas.Canvas("ola.pdf")
c.drawString(100,750,"Bem-vindo ao Reportlab!")
c.save()

Você terminará com um arquivo PDF parecido com isso:

PDF do exemplo, de Olá Mundo

A primeira coisa a se notar sobre esse código é que se quisermos salvar o PDF, precisamos fornecer um nome de arquivo ao objeto Canvas. O endereço para o arquivo pode ser um caminho tanto absoluto como relativo. Nesse exemplo, o PDF será criado no mesmo lugar onde você está quando chamar o script. A próxima peça do quebra-cabeças é o método drawString. Ele desenhará o texto do jeito que você mandar. Quando usamos o objeto canvas, ele começa no canto inferior esquerdo da página. Assim, para este exemplo, pedimos para desenhar o texto a 100 pontos da margem esquerda e a 750 pontos do canto inferior da página (1 ponto equivale a 1/72 polegada). Você pode mudar esse padrão no construtor do Canvas passando um zero ao parâmetro bottomup. Entretanto, eu não tenho muita certeza do que acontecerá se você fizer isso, já que o guia de usuário do Reportlab não é muito claro a esse respeito. A peça final do código que foi apresentado é a que salva o seu PDF.

Isso foi fácil! Você já criou um PDF simples! Note que o tamanho por padrão do Canvas já é A4, de modo que se você for estadunidense provavelmente desejará mudá-lo para o formato letter. É fácil fazer isso no Reportlab. Tudo o que você precisa fazer é o seguinte:

from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
 
canvas = canvas.Canvas('arquivo.pdf', pagesize=letter)
width, height = letter

A principal razão para pegar a largura e altura da página é que você pode definir margens, além de poder usá-las para fazer cálculos e decidir quando adicionar uma quebra de página. Vamos dar uma olhada por alto no construtor da classe Canvas para ver que opções nós temos mais:

def __init__(self,filename,
    pagesize=letter,
    bottomup = 1,
    pageCompression=0,
    encoding=rl_config.defaultEncoding,
    verbosity=0
    encrypt=None):

Esse código foi tirado diretamente do Guia de Usuário do Reportlab, página 11. Você pode ler sobre outras opções nesse guia se você quiser os detalhes completos.

Agora vamos fazer algo um pouco mais difícil e útil.

Um Pequeno Formulário, uma Pequena Função

Formulário em PDF

Neste exemplo, criaremos um formulário imprimível parcial. Até onde eu sei, o Reportlab não suporta aqueles formulários preenchíveis na tela, os que foram adicionados aos produtos Adobe no decorrer dos anos. De qualquer modo, vamos ao código!

from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
 
canvas = canvas.Canvas("form.pdf", pagesize=letter)
canvas.setLineWidth(.3)
canvas.setFont('Helvetica', 12)
 
canvas.drawString(30,750,'COMUNICADO OFICIAL')
canvas.drawString(30,735,'EMPRESAS ACME')
canvas.drawString(500,750,"12/12/2011")
canvas.line(480,747,580,747)
 
canvas.drawString(275,725,'SALDO DEVEDOR:')
canvas.drawString(500,725,"R$ 1.000,00")
canvas.line(378,723,580,723)
 
canvas.drawString(30,703,'RECEBIDO POR:')
canvas.line(130,700,580,700)
canvas.drawString(130,703,"JOHN DOE")
 
canvas.save()

Isso é baseado no recibo atual que eu criei no trabalho. A principal diferença entre este e o exemplo anterior é o uso do método canvas.line. Você pode usá-lo para desenhar linhas no seu documento passando pares X-Y. Eu usei esta funcionalidade para criar grades, embora isso seja um saco. Outros pontos interessantes nesse código incluem o comando setLineWidth(.3), que diz ao Reportlab quão grossas devem ser as linhas; e o comando setFont('Helvetica', 12), que nos permite especificar a fonte e tamanho.

Nosso próximo exemplo vai envolver o que aprendemos até agora e nos introduzirá nos fluidos.

Indo com o Fluxo

Se você é um publicitário ou faz algum tipo de trabalho com cartas, então Reportlab será um excelente acréscimo ao seu arsenal. Nós o usamos para criar cartas de formulário para pessoas com bilhetes de estacionamento vencidos. O exemplo a seguir é baseado em algum código que eu escrevi para essa aplicação, embora a carta seja um tanto diferente. (Note que o código abaixo não rodará se você não tiver a Python Imaging Library instalada).

# -*- coding: UTF-8 -*-
import time
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
 
doc = SimpleDocTemplate("form_letter.pdf",pagesize=letter,
                        rightMargin=72,leftMargin=72,
                        topMargin=72,bottomMargin=18)
Story=[]
logo = "python-logo.png"
magName = "Pythonista"
issueNum = 12
subPrice = "99.00"
limitedDate = "03/05/2010"
freeGift = "Bisão de pelúcia"
 
formatted_time = time.ctime()
full_name = "Mike Driscoll"
address_parts = ["411 State St.", "Marshalltown, IA 50158"]
 
im = Image(logo, 2*inch, 2*inch)
Story.append(im)
 
styles=getSampleStyleSheet()
styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
ptext = '<font size=12>%s</font>' % formatted_time
 
Story.append(Paragraph(ptext, styles["Normal"]))
Story.append(Spacer(1, 12))
 
# Create return address
ptext = '<font size=12>%s</font>' % full_name
Story.append(Paragraph(ptext, styles["Normal"]))
for part in address_parts:
    ptext = '<font size=12>%s</font>' % part.strip()
    Story.append(Paragraph(ptext, styles["Normal"]))
 
Story.append(Spacer(1, 12))
ptext = '<font size=12>Caro(a) %s:</font>' % full_name.split()[0].strip()
Story.append(Paragraph(ptext, styles["Normal"]))
Story.append(Spacer(1, 12))
 
ptext = '<font size=12>Gostaríamos de recebê-lo na nossa base de assinantes da Revista %s! \
        Você receberá %s edições pelo excelente preço inicial de $%s. Por favor responda até\
        %s para começar a receber nossa publicação e ainda levar esse maravilhoso presente: %s.</font>' % (magName,
                                                                                                issueNum,
                                                                                                subPrice,
                                                                                                limitedDate,
                                                                                                freeGift)
Story.append(Paragraph(ptext, styles["Justify"]))
Story.append(Spacer(1, 12))
 
 
ptext = '<font size=12>Agradecemos muito e esperamos seu retorno para lhe servir.</font>'
Story.append(Paragraph(ptext, styles["Justify"]))
Story.append(Spacer(1, 12))
ptext = '<font size=12>Atenciosamente,</font>'
Story.append(Paragraph(ptext, styles["Normal"]))
Story.append(Spacer(1, 48))
ptext = '<font size=12>Ima Sucker</font>'
Story.append(Paragraph(ptext, styles["Normal"]))
Story.append(Spacer(1, 12))
doc.build(Story)

Bem, aqui teve muito mais código do que nos nossos exemplos anteriores. Precisaremos olhá-lo com calma para entendermos tudo o que está sendo feito. Quando você estiver pronto, simplesmente continue lendo. (N. T. Como traduzi o código do exemplo e o adaptei sutilmente, fez-se necessário o uso daquela primeira linha de comentário no código, que diz ao Python que o código a seguir está em Unicode. Se você salva seus arquivos em ISO-8859-1, basta colocar esse código no lugar de UTF-8 que está tudo bem).

A primeira parte que precisamos olhar são as novas linhas de importação:

from reportlab.lib.enums import TA_JUSTIFY
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch

De enums, importamos “TA_JUSTIFY”, que permite que nossas strings tenham formatação justified. Há várias outras constantes que podemos usar: alinhamento à direita, à esquerda e algumas outras coisas divertidas. Em seguida temos o módulo platypus (que vem de Page LAyout and TYPography Using Scripts. N. T.: platypus também significa "ornitorrinco"). Ele contem um monte de módulos, mas provavelmente os mais importantes deles são os fluidos, como o Paragraph. Um fluido tem tipicamente os seguintes atributos: wrap, draw e algumas vezes split. Eles são usados para escrever com facilidade parágrafos, tabelas e outras construções de múltiplas páginas.

A classe SimpleDocTemplate nos permite definir em só lugar margens, tamanho da página, nome do arquivo e uma pá de outras configurações para o nosso documento. Um "Spacer" é bom para adicionar uma linha em branco, como uma quebra de parágrafo. A classe Image utiliza a Python Image Library para nos permitir inserir e manipular facilmente uma imagem no nosso PDF.

O getSampleStyleSheet pega um conjunto de estilos padrão que podemos usar no nosso PDF.  ParagraphStyle é usado para definir o alinhamento do nosso parágrafo nesse exemplo, mas pode fazer muito mais do que isso (veja a página 67 do guia de usuário). Pra terminar, inch (polegada) é uma unidade de medida para ajudar no posicionamento de itens no seu PDF. Você pode vê-lo em ação quando nós posicionamos o logo: Image(logo, 2*inch, 2*inch). Isso quer dizer que o logotipo estará a duas polegadas do topo e a duas polegadas da esquerda.

Não recordo a razão para que os exemplos do Reportlab usem uma lista Story, mas é como nós faremos aqui também. Basicamente você cria uma linha de texto, uma tabela e imagem ou o que você quiser e as anexa à Story list. Você verá isso por todo o nosso exemplo. A primeira vez que fizemos isso foi quando adicionamos a imagem. Antes de prosseguir, precisamos olhar como adicionamos um estilo ao nosso objeto de estilos.

styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))

A razão para isso é importante e é para que você possa utilizar a lista de estilo para vários parágrafos de texto do seu documento. No código anterior, criamos um estilo de parágrafo chamado "Justify". Tudo o que ele faz é justificar nosso texto. Você verá um exemplo disso depois no texto. Por ora, vamos ver um exemplo rápido

ptext = '<font size=12>%s</font>' % formatted_time
Story.append(Paragraph(ptext, styles["Normal"]))

Para a nossa primeira linha de texto, usamos a classe Paragraph. Como você pode ver, a classe Paragraph aceita algumas tags estilo HTML. Aqui nós definimos o tamanho da fonte para 12 e usamos o estilo normal (que é alinhado à esquerda, entre outras coisas). O resto do exemplo é basicamente a mesma coisa, só que com espaçadores (Spacers) colocados aqui e ali. No fim, nós chamamos doc.build para criar o documento.

Resumindo

Agora você sabe o básico sobre criação de PDFs em Python usando Reportlab. Nós nem sequer arranhamos a superfície daquilo tudo que podemos fazer com Reportlab. Alguns exemplos incluem tabelas, gráficos, paginação, sobreposição de cores, links, gráficos e muito mais. Eu recomendo bastante que você baixe o módulo junto com o guia de usuário e mande ver!

Conheça o Ren'Py

Visual Novel é um tipo de jogo de videogame baseado em diálogo, como uma história interativa. São histórias que o jogador acompanha (geralmente lendo e vendo imagens associadas), sendo responsável por decidir que rumo a história tomará em alguns momentos.

Ren'Py é um software livre para criar jogos desse tipo. Feito em Python, como o nome sugere, ele cria a estrutura para uma Visual Novel, sendo capaz de gerar executáveis para Windows, Mac OS X, GNU/Linux e Android.

O código-fonte do jogo é escrito em uma sintaxe Python-like, facilmente carregando elementos de arquivos externos, como imagens e sons.

Os recursos que o Ren'Py oferecem incluem:

  • Menu principal que dá opções de iniciar o jogo, continuar um jogo salvo e ajustar configurações
  • Um menu de jogo durante o jogo propriamente, permitindo abrir um jogo salvo, salvar e ajustar configurações
  • Salvamento automático
  • O jogador pode retornar para telas anteriores, mudando sua decisão
  • Predição de imagens a serem carregadas, fazendo com que as imagens sejam carregadas em segundo plano, evitando que o usuário tenha que se deparar com momentos de "Loading"
  • Permite controlar o jogo usando mouse, teclado ou joystick
  • Permite escolher se o jogo funcionará em tela cheia ou dentro de uma janela
  • Auto-avanço de texto sem ter que usar o teclado, com ajuste baseado na quantidade de texto mostrada por tela
  • Pode ocultar textos para que o usuário veja a imagem que está trás
  • Habilidade de mudar música, efeito sonoro e volume de voz.
  • Facilidade de personalização e localização (tradução)
  • Permite vários efeitos de transição de cena/imagem
  • Permite que se expanda suas funcionalidades através de código-fonte em Python

É um excelente software livre, que pode ser baixado do site http://www.renpy.org/

Páginas