artigo

Frameworks PHP da Década de 2010

O PHP já foi a linguagem mais usada para aplicações web. Mesmo com todo o sucesso e crescimento de Java, Ruby e Python, creio que muitos ainda sejam dependentes da boa e velha derivada do Perl.

Também é sabido que a cultura de framework em PHP não existia, quando Ruby se tornou popular. Juntando-se a grande quantidade de desenvolvedores com a falta de direção e um desejo de "se modernizar", aconteceu um fenômeno: uma infinidade de projetos foram criados. Quase todos partindo de um mesmo objetivo: criar um framework MVC.

O tempo passou e muitos não resistiram. Fiz uma pesquisa, que compartilho com vocês, dos frameworks PHP que estão ativos na nossa década atual. Para entender melhor o critério, dizendo de uma maneira bem simples: projetos que tiveram release de 2010 para cá.

Espero que aproveitem!

FrameworkVersão mais recenteÚltimo lançamento emLicençaDetalhes
Adventure1.62012GNU LGPL 
Agile4.2.32012GNU AGPL 
Alloy0.7.4 Beta2011BSD 
ATK6.5.02010GNU LGPL 
Bitweaver3.12012GNU LGPLFramework e CMS
CakePHP2.2.52013MIT 
CodeIgniter2.1.32012Licença própria (software livre) 
Halo0.0.22010BSD 
Horde5.0.32013GNU GPL/LGPL 
IrisMVC2.0 RC2011Nova BSD 
Jelix1.4.22012GNU LGPL 
Kohana3.3.02013BSD 
MiMViC0.9.92010MIT 
MochiPHP0.22011Apache 
Nette2.0.82013Nova BSD / GNU GPL 
P4A3.8.42011GNU LGPLBaseado no Zend
Pronto0.62010MIT / GNU GPL 
Qcodo0.4.222011Licença própria (software livre) 
Raxan1.02011GNU GPL / MIT 
Solar1.1.22010Nova BSD 
Symfony2.2.0 Beta2013MITDrupal 8 usa seus componentes
Thin PHP0.562012MIT 
Yii1.1.132012BSD 
Zend2.0.62012Nova BSDMVC
HTML5

E você? Ainda programa em PHP? O que você usa?

Special: 

Livros Digitais com o FBReader

FBReader mostrando a última edição do CyanZine

O formato ePub foi criado especificamente para livros digitais. Internamente ele parece HTML, mas incorpora certas características próprias dos livros, como quebra de página.

Claro que o ePub não é o único formato para isso, há vários outros, incluindo o próprio PDF, mas se você tentar ler a Revista Espírito Livre, por exemplo, numa tela de 3 polegadas, vai entender o porque de termos um formato próprio pra livros. O texto de um ePub é fluido, adaptando-se à tela do dispositivo utilizado para ler. Não é, também, só o ePub que faz isso, mas ele é o formato mais popular nessa linha. E é um formato aberto (apesar do ponto negativo de suportar DRM).

Pois bem, creio que todo dispositivo para leitura de livros digitais de hoje em dia (ebook readers) tenha suporte a ePub naturalmente. E há softwares para os dispositivos de uso geral. FBReader é um deles.

O FBReader suporta outros formatos além do ePub, e está disponível para diversos dispositivos: para Android, MacOS X, Windows, Linux, Blackberry e diversas outras plataformas móveis.

Além de ser eficiente no trato com os livros digitais, o FBReader tem um recurso de acesso a algumas bibliotecas de livros digitais sob domínio público. Infelizmente não encontrei muitas opções em nossa língua.

Você também pode personalizar sua experiência de leitor, definindo fonte, tamanho e cores, para que a leitura fique o mais adaptado possível ao seu gosto.

Se você tem Android, o FBReader é fácil de achar no FDroid. Se usa GNU/Linux, provavelmente haverá um pacote fbreader nos repositórios oficiais. Se usa outra plataforma, pode baixar lá no site do projeto. O FBReader também é distribuído junto com o CyanPack.

Special: 

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: 

Star Wars: The Force Unleashed (o jogo)

Star Wars: The Force Unleashed

Star Wars: The Force Unleashed foi lançado em 2008 e traz uma história paralela ao que vemos nas trilogias. Darth Vader recruta secretamente um órfão, filho de um jedi, que se torna o Starkiller. É com ele que você joga.

Quer dizer, existe um prólogo antes de você realmente mergulhar no jogo. No prólogo, você joga com o próprio Darth Vader! Isso por si só já vale o jogo.

O cenário é bacana e a história é muito boa também. Apesar de haver muitas diferenças, o jogo me lembrou um bocado God of War. Talvez pelas peculiaridades gênero: é um jogo de aventura (tipo plataforma 3D), com traços de RPG (ganhamos experiência, que gastamos para desenvolver e aperfeiçoar certas habilidades) e há momentos resolvidos com quick time event (a cena vai acontecendo e parando pra você apertar um botão em pouco tempo ou algo assim).

O jogo foi lançado para um monte de plataformas: iOS, MacOS X, Nintendo DS, Nintendo Wii, PlayStation 2, PlayStation 3, PSP, Xbox 360, Windows e alguns outros mais (os de console podem ser comprados na eStarland). Tive oportunidade de conhecer a versão Wii.

Uma coisa interessante na versão Wii é que foi nesses quick time events que eu descobri que o numchuck também tem um sensor de inclinação relativamente preciso.

Apesar de o cenário não ser tão interativo assim, há muitos elementos espalhados sobre os quais Starkiller pode usar a Força. Ele pode levantar pedras, peças e até inimigos. Habilidades extra incluem uma explosão de choque partindo do personagem, uma onda de choque, choque elétrico e arremesso do sabre de luz.

Enfim, é um jogo que vale a pena. Depois dele, ainda foi lançado - em 2010 - The Force Unleashed II. Esse ainda não tenho, mas já está na minha lista.

Special: 

Mapas do Acaso - 45 variações sobre um mesmo tema, de Humberto Gessinger

Mapas do Acaso, capa do livro de Humberto Gessinger

Engenheiros do Hawaii é uma banda que dividia opiniões. Muitos os criticavam e outros tantos os adoravam. Quem gostava (e gosta) dos Engenheiros do Hawaii certamente curtia/curte a mente do seu líder Humberto Gessinger, sua forma de ver o mundo, de sintetizar conceitos, transformando elementos um tanto obscuros em cultura pop e "filosofia fast-food".

Engenheiros do Hawaii acabou, mas o trabalho de Humberto continua. Primeiro, no âmbito musical, com o duo Pouca Vogal, onde divide o palco com o também talentoso Duca Leindecker. Depois, no âmbito literário. O livro Pra Ser Sincero - 123 variações sobre um mesmo tema traz uma biografia sua e da banda (mais da banda do que sua), mostrando uma visão interessante de tudo isso, muito bom para os fãs.

Depois veio o livro Mapas do Acaso. Este já não é mais tão biográfico quanto o outro. Assim como o anterior, toma emprestado o título de uma de suas músicas mas honra bem ao título. Mapas do Acaso é como um blog. Um conjunto de artigos não tão extensos, feitos por um cara que entende do Pop dos anos 80, mas não se dobrou ao lugar comum. Um cara que preferiu se recriar depois do fim de uma banda que durou 23 anos. Quando poderia simplesmente parar ou continuar se sustentando por shows repetecos, financiados por seus fãs de todo o país, preferiu o caminho arriscado de um duo "acústico" onde os dois tocam vários instrumentos e cantam. E com músicas novas! Como o Duca vinha de uma outra banda gaúcha de rock, decidiram que 1/3 do repertório seria de músicas dos Engenheiros, 1/3 de músicas do Cidadão Quem e 1/3 de músicas inéditas, do Pouca Vogal mesmo.

Mapas do Acaso - 45 variaões sobre um mesmo tema traz várias "notas mentais para uma próxima vida", filosofando sobre o dia a dia (o que a meu ver ele sempre fez muito bem) e algumas republicações de artigos que saíram há muitos anos para públicos mais restritos geograficamente, como o artigo publicado em 1990 no Jornal do Brasil, onde ele falou sobre sua viagem para shows em Moscou no declínio da Guerra Fria.

Ah, tanto o Pra Ser Sincero quanto o Mapas do Acaso trazerm metade do livro composta de letras de Humberto, algumas com explicações e curiosidades interessantes. Mapas do Acaso chega a trazer algumas folhas escaneadas de um caderno, onde podemos ver músicas que nunca foram gravadas mas que renderam versos, que foram reaproveitados em músicas de sucesso.

Enfim, é um livro que eu recomendo. Sinceramente gostei bem mais do Mapas do Acaso do que do anterior e já estou ansioso pelo próximo: Nas Entrelinhas do Horizonte.

Special: 

Final Fantasy: 25 anos

Final Fantasy - chocobo rider

Havia uma empresa. Eles criavam jogos de videogame, mas até aquele momento nenhum tinha emplacado. Então o desenvolvedor Hironobu Sakaguchi decidiu: esta vai ser minha última tentativa. Se não der certo eu vou fazer outra coisa da vida. Assim nascia a última esperança daquela empresa, com um nome que refletia muito bem essa condição: Final Fantasy.

Mas Final Fantasy deu certo e teve sequência, depois outro jogo e outro... Hoje já são 14 jogos na contagem principal e vários spinoffs nesta bela "saideira de bêbo", que também é uma das maiores referências em RPG eletrônico até os dias de hoje.

Final Fantasy foi lançado para Square há exatos 25 anos para NES. No jogo você controlava os 4 guerreiros da luz, personagens profetizados que chegavam com a missão de salvar o mundo pela restauração de cristais, o que exige enfrentar vários inimigos, o que inclui os demônios do caos. Particularidade deste jogo é que os jogadores é que decidiam os nomes dos quatro personagens, bem como suas classes: guerreiro, monge, ladrão, mago branco, mago negro, mago vermelho.

Com o passar dos tempos e dos jogos, a série foi mudando e criando histórias cada vez mais elaboradas e com personagens próprios e bem definidos. Um revolução, em especial, aconteceu quando a Square deixou a Nintendo e se aliou à Sony. Com isso, veio Final Fantasy VII para Playstation, provavelmente o título mais marcante da franquia. Nele tínhamos o personagem Cloud e seus aliados tentando restaurar a paz no mundo, desta vez um mundo tridimensional. Aquelas imagens infelizmente envelheceram mal, mas isso não tira o mérito do jogo.

Com tanto sucesso, Final Fantasy terminou aparecendo no cinema também. Começando com o filme Final Fantasy: The Spirits Within. Um fracasso total. O filme tinha grande qualidade gráfica e uma história boazinha (nada que chamasse muita atenção). O problema é que não era um Final Fantasy, era só uma animação 3D genérica. Não vimos no filme nenhum elemento dos jogos da franquia e isso com certeza decepcionou muitos fãs.

O filme seguine veio para corrigir isso. Em Final Fantasy VII: Advent Children, vemos uma continuação dos eventos do jogo Final Fantasy VII, trazendo os personagens marcantes em cenas de ação altamente dinâmicas. O filme está disponível (como já falei no blog outro dia) para ser assistido gratuitamente no Crackle.

Temos ainda outros jogos marcantes. Em especial, o Final Fantasy XII (PS2) e o Final Fantasy XIII-2 (PS3) conseguiram nota máxima na revista japonesa Famitsu. E mais recentemente, a franquia entrou nos jogos de ritmo com Theatrhythm Final Fantasy, lançado para 3DS e agora também no iOS.

Existem algumas características interessantes de se notar na franquia Final Fantasy. Primeiro que as histórias são meio que fechadas, com personagens próprios, exclusivos de cada uma. Nada de continuar a história de personagens carismáticos (ao menos atualmente, ao menos na numeração padrão, o que não impede que eles fujam a essa regra em jogos como Final Fantasy X-2). Outro elemento é o mundo onde as histórias se passam. Depois de amadurecido (lá pelo quarto jogo em diante), Final Fantasy passou a apresentar um mundo de fantasia misturando elementos tecnológicos diferenciados, cuja base energética é vapor e a própria magia, como um mundo fantapunk. Somando-se a isso o cuidado que a Square tem com os gráficos, frequentemente explorando ao máximo as capacidades dos consoles para os quais os jogos são feitos, costumamos ter cenários muito agradáveis de se ver.

E claro que não podemos esquecer dois dos elementos mais marcantes da franquia, capazes de identificar a franquia para qualquer gamer: chocobos (montarias que parecem galinhas gigantes) e phoenix down (o item capaz de ressuscitar um personagem morto em combate).

Final Fantasy é uma franquia incrível e não está aí à toa, indo para seu 15º título numerado. Rendeu frutos como o Kingdom Hearts. Infelizmente seu criador não está mais na Square Enix (nome que a empresa ganhou após assimilar sua principal concorrente, a Enix, criadora do Dragon Quest). Após problemas lá na empresa, ele saiu e criou a Mistwalker. Inclusive, em 2011 ele lançou um outro jogo e dizem que é o último do qual participará ativamente de todos os estágios. O nome: The Last Story, para Wii. Será que o título dessa vez se concretiza?

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!

Torrentflux: baixando/propagando torrents e arquivos

Torrentflux: seus torrents na web

Esta dica é dividida em duas partes: uma mais simples, para uso pessoal apenas; outra mais avançaca, para quem quer compartilhar os arquivos baixados.

Tudo isso tem a ver com torrents, pequenos arquivos-índices que permitem compartilhamento de arquivos (esses sim, grandes) pela Internet. Se você não conhece a tecnologia, ela é usada para compartilhar músicas, filmes, albuns, imagens de CD e DVD, livros digitais e, o que me motivou a configurar o Torrentflux, distribuições GNU/Linux!

Clientes torrents existem vários. Desde o básico bittorrent, em linha de comando, até outros mais sofisticados como o Deluge e o qBittorrent (este inclusive vem no CyanPack e tem versão para Windows e Linux). Torrentflux é um cliente também, mas que oferece uma interface web, funcionando independente de você fazer ou não login na máquina. Isso é ótimo para máquinas de uso compartilhado ou servidores!

A primeira parte falará sobre a instalação do Torrentflux no Trisquel (ou Ubuntu, ou Mint ou outro similar), enquanto a segunda entra no Apache.

Primeira parte: Instalando o Torrentflux

Vá no menu do Trisquel e escolha Configuração do Sistema. Na janela que aparece, procure o aplicativo Gerenciador de Pacotes Synaptic e clique nele. No Synaptic, clique em Procurar e digite torrentflux. Clique com o botão direito e escolha Marcar para Instalação. Agora basta Aplicar.

Método alternativo: digite em um terminal apt-get install torrentflux.

Agora é deixar sua distribuição trabalhar e instalar o tal programa.

Detalhe importante aqui: o Torrentflux utiliza o MySQL, ou seja, se ele já estiver instalado você precisará da senha de administrador dele. Se não, você cria uma durante a instalação agora.

Vai ser pedida uma senha para a configuração de banco do Torrentflux. Não é tão importante termos contato com essa senha, afinal, na situação ideal, você não precisará conhecê-la. Por isso, pode deixar em branco que o instalador cria uma senha aleatória.

Pronto! Já está instalado! Aqui, pelo menos, o Apache não reiniciou automaticamente e tive que fazer isso por minha conta. Basta rodar o comando /etc/init.d/apache2 restart e pronto!

Agora abra o navegador e digite http://localhost/torrentflux. Aparecerá uma tela de login. Embora a tela não diga, não há usuário criado ainda. Você deve digitar um login e uma senha que deseje (com cuidado, já que não há opção para digitar a senha duas vezes, previnindo erros de digitação) e o primeiro usuário será criado. Usuários adicionais são criados em Admin: New user.

Intervalo: como funciona o Torrentflux

Na tela principal (home) você tem três opções particularmente úteis: envio de um torrent via upload; envio de torrent via URL (será baixado diretamente do site); busca. A busca, em especial, permite escolher entre PirateBay, Google, Mininova e outros.

Mais embaixo, estatísticas, incluindo a lista dos torrents que estão aí, baixados, baixando ou para baixar.

Quando os arquivos são baixados, você pode pegá-los do Torrentflux indo na opção directory.

Bem, isso é o básico. Quando você adiciona um torrent, ele não começa a baixar automaticamente, você tem que ativá-lo. Cada vez que a máquina for reiniciada, você terá que reativar os torrents (não vi uma forma de autoativação, mas seria uma boa e talvez até já tenha um jeito...) Você vai querer dar uma fuçada na opção admin. Também dá pra mudar tema (skin) e idioma padrão.

Parte 2: compartilhando o conteúdo baixado.

A instalação do Torrentflux via Trisquel (ou Ubuntu, ou...) tem a peculiaridade de "esconder" os arquivos: eles não ficam em /var/www. Isso não é ruim, é que o pacote distribui arquivos e diretórios pelo sistema de arquivos da forma que se considera mais organizada e adequada. Os arquivos PHP ficam /usr/share/torrentflux/web, enquanto os baixados ficarão em /var/cache/torrentflux.

Tudo muito legal, bonito e organizado, mas... E se eu quiser compartilhar esses arquivos de forma mais fácil pela web? Bom, você pode simplesmente alterar a configuração do Torrentflux, mudando o diretório dos arquivos baixados para dentro do /var/www ou pode alterar o apache!

Criei dois diretórios virtuais para compartilhamento: /downloads e /torrents. Cada um exige suas próprias regras dentro da tag <VirtualHost *:80> no arquivo /etc/apache2/sites-available/default (ou em outro canto, se sua configuração do Apache for mais sofisticada. Claro que se for esse o caso e você precisou/conseguiu configurar o apache de um jeito mais exótico, você saberá qual o lugar certo ;-)

Abra o arquivo /etc/apache2/sites-available/default e acrescente, lá no final, imediatamente antes da linha que tem </VirtualHost> o seguinte:

Alias /download/ /var/cache/torrentflux/bardo/
    <Directory "/var/cache/torrentflux/bardo">
        Options +Indexes +MultiViews
        HeaderName /download_header.html
        ReadmeName /download_footer.html       
        Order allow,deny
        Allow from all
    </Directory>

    Alias /torrents/ /var/cache/torrentflux/.torrents/
    <Directory "/var/cache/torrentflux/.torrents">
        Options +Indexes +MultiViews
        HeaderName /download_torrents.html
        ReadmeName /download_footer.html
        IndexIgnore queue *.stat *.pid
        Order allow,deny
        Allow from all
    </Directory>

Substitua "bardo" pelo login de usuário que você criou. O primeiro bloco cria a lista dos arquivos baixados, enquanto o segundo cria a dos torrents. Como existem dentro da pasta .torrents do Torrentflux outros arquivos, criados e usados por ele próprio, é preciso colocar a regra de ignorar todos os *.pid e *.stat, além da pasta queue.

As linhas HeaderName e ReadmeName eu criei para adicionar um estilo mais agradável à listagem. Você pode simplesmente dispensá-las ou então criar os três arquivos:

  • download_header.html: coloque o cabeçalho HTML, o cabeçalho do corpo da página, até quando estiver prestes a mostrar o conteúdo central. Aí o arquivo tem que ser truncado.
  • download_torrents.html: basicamente um variante do download_header. Fiz os dois porque coloquei na parte de cima da página um link de um para o outro.
  • download_footer.html: depois do conteúdo, você vai precisar fechar as tags que ficaram abertas. Talvez colocar um rodapé na página. Faça isso neste arquivo.

Bom, é isso. O que eu fiz está na Leeloo, da UFAL. Se quiserem ver como ficou, podem ver o resultado em leeloo.arapiraca.ufal.br/downloads/.

Special: 

Como seriam poemas para Twitter?

Pássaros

Já se foi o tempo dos poemas longos, narrando aventuras épicas em centenas de estrofes. O mundo mudou muito. Pessoas persistentes sempre aparecem, aqui e ali, mas hoje quem quer perder horas e horas lendo uma só poesia.

Acho que o sentimento é de que há muita coisa lá fora esperando por nós. Um sentimento que nos faz acompanhar vários feeds RSS (que a maioria já abandonou) e nos faz seguir mais pessoas nas redes sociais do que podemos acompanhar. Também tenho a impressão de que esse sentimento casa muito bem com o Capitalismo, nos fazendo ficar de olho nos lançamentos. Sejam filmes, livros, jogos ou a tecnologia da próxima TV. Ou o próximo iPhone. Vivemos em estado de alerta, como bombeiros esperando um alerta de incêndio a qualquer momento. Com a diferença que esse incêndio em particular é nas nossas carteiras...

Mas estávamos falando de poesia, não é? Qual seu papel na nossa nova sociedade? No mundo 2.0? Ou seria já o mundo "pós 2.0"?

A poesia tem muitas formas. As poesias épicas se tornaram cordéis com o tempo. Soneto se estabilizou como forma prática. Há ainda o Haikai e seus três versos. A poesia deve se curvar ao Twitter? Qual seria a metrificação pós 2.0? Ao invés de publicar uma poesia e usarmos tags "soneto" e "decassílabo" vamos escrever palavras em um só verso-tweet resumido com hashtags tipo #poem #3/4?

Nesses novos tempos parece que tudo precisa ser resumido. Fico me perguntando às vezes se é uma necessidade real ou inventada em nossas cabeças, reforçadas pelo frenético efeito manada, pelo clima de urgência em que vivemos. Resumir, resumir...

Pra que escrever ensaios complexos se "tudo já foi escrito"? Não precisamos falar sobre os temas, basta passarmos um link e está tudo bem. No Twitter tem até quem se limite a postar o link, sem título ou assunto...

Ei, sabe que essa ideia de poesia-tweet pode ser interessante? Saca só:

  • Ela anda todo dia pela casa. Quase morre quando vê uma barata. E nem sonha que a recíproca acontece

O que seria isso? Uma poesia de 3 versos em métrica 4/4! É! Você lê (até porque ninguém tem tempo pra declamar hoje em dia) ta-ta-TA-ta ta-ta-TA-ta ta-ta-TA-ta. Ela anda todo dia pela casa. Parece meio maluco, né? É, poesia é assim mesmo.

Hmmm... Vou pensar melhor no assunto. Talvez termine levando a sério essa ideia. Ou será que a moda do Twitter tá com os dias contados e eu deveria pensar em um video-poema?

Imagem do post: birds, de Sharon Drummond.

Special: 

Páginas