Veremos como é simples criar uma aplicação gráfica rapidamente usando Tkinter.
Vamos começar a a partir de um boilerplate:
Vamos começar a a partir de um boilerplate:
#!/usr/bin/env python
# coding: UTF-8
from __future__ import absolute_import, division, print_function, unicode_literals
#-------------------------------------------------------------
# Cabeçalhos
#
# Aqui vão entrar os imports
__all__ = ['Editor']
#-------------------------------------------------------------
class Editor(object):
def mainloop(self):
pass
#-------------------------------------------------------------
# Rodapé
if __name__ == '__main__':
app = Editor()
app.mainloop()
A primeira coisa que precisamos fazer é criar nossa janela raiz. Para tanto, adicione aos cabeçalhos o
import
:
from Tkinter import *
Método construtor
Em seguida, no começo da classe
Editor
, vamos começar a escrever nosso construtor:
class Editor(object):
def __init__(self):
root = self.root = Tk()
root.title('EditPy')
Precisamos então de um frame para colocar os botões de abrir, salvar e salvar-como:
fbuttons = Frame(root)
Button(fbuttons, text='Open', command=self.open) \
.pack(side=LEFT)
Button(fbuttons, text='Save', command=self.save) \
.pack(side=LEFT)
Button(fbuttons, text='Save As', command=self.save_as) \
.pack(side=LEFT)
O pai (master) do frame
Agora precisamos de uma caixa de texto para editar e exibir o conteúdo dos arquivos. Primeiro vá aos cabeçalhos novamente e acrescente o
fbuttons
é nossa janela raiz e o pai dos botões é o frame.Agora precisamos de uma caixa de texto para editar e exibir o conteúdo dos arquivos. Primeiro vá aos cabeçalhos novamente e acrescente o
import
:
from ScrolledText import ScrolledText
Então, continuando o construtor, crie a caixa de texto com rolagem:
steditor = self.steditor = ScrolledText(root)
Para fechar o construtor só falta exibir o frame e a caixa de texto, e colocar o foco na caixa:
fbuttons.pack(expand=True, fill=X)
steditor.pack(expand=True, fill=BOTH)
steditor.focus()
Loop principal
Se você tentar rodar o código agora, nada acontece. Isso porque o método
Precisamos então, neste método, chamar o loop principal do Tkinter. Para isso, altere o método:
mainloop()
faz isso: absolutamente nada.Precisamos então, neste método, chamar o loop principal do Tkinter. Para isso, altere o método:
def mainloop(self):
self.root.mainloop()
Chamamos então o loop principal através da janela raiz (
root
).
Abrir um arquivo
Porém coisas estranhas e erros esdrúxulos podem acontecer, pois ainda não implementamos nenhum dos métodos chamados pelos botões –
Parece-me justo que comecemos pelo método de abertura de arquivo. Criemos a assinatura do método:
self.open
, self.save
e self.save_as
.Parece-me justo que comecemos pelo método de abertura de arquivo. Criemos a assinatura do método:
def open(self):
Para abrir um arquivo, precisamos de uma ferramenta chamada
askopenfilename
que precisamos importar, então nos cabeçalhos acrescente:
from tkFileDialog import askopenfilename
Então, de volta ao método
open()
, podemos pegar o nome do arquivo e carregá-lo:
filename = askopenfilename(title='EditPy | Open')
if filename:
self.filename = filename
self.load_file()
O método
load_file()
é quem vai de fato carregar o arquivo, mas precisamos antes apagar o conteúdo da caixa de texto:
def load_file(self):
filename = self.filename
steditor = self.steditor
steditor.delete('@0,0', 'end')
with open(filename) as fd:
steditor.insert('@0,0', fd.read())
O método
delete()
da caixa de texto apaga parte do conteúdo da caixa e os parâmetros são os índices de começo e fim:
@0,0
significa linha 0, coluna 0 (começo do texto);end
significa o final do texto.
O método
insert()
inserte a string (lida do descritor de arquivo) na posição indicada (@0,0
).
Salvando o texto
Para salvar o texto, precisamos implementar o método
save()
. Ele precisa:
- Verificar se temos um nome de arquivo – se não tivermos, é um
save_as
. - Abrir o arquivo para escrita.
- Obter o texto da caixa de texto.
- Gravar o texto no arquivo.
Simples, indolor e o código é intuitivo:
def save(self):
filename = self.filename
if not filename:
return self.save_as()
with open(filename, 'w') as fd:
fd.write(
self.steditor.get('@0,0', 'end')
)
Repare nos índices usados no método
Em seguida é preciso saber o que fazer quando não temos o nome do arquivo ou quando escolhemos Save As.
Primeiro a assinatura do método:
get()
.Em seguida é preciso saber o que fazer quando não temos o nome do arquivo ou quando escolhemos Save As.
Primeiro a assinatura do método:
def save_as(self):
Caso haja um nome de arquivo, é honesto usá-lo como ponto de partida para caixa de diálogo de salvamento. Para isso precisamos separar o nome de diretório do nome base do arquivo e uma forma de fazer isso é com o método de string
Para sabermos qual o separador de diretórios usado, adicione aos
rfind
.Para sabermos qual o separador de diretórios usado, adicione aos
import
s:
from os import path
Então continue no método:
filename, directory = self.filename, None
if filename and path.sep in filename:
index = filename.rfind(path.sep) + 1
directory = filename[:index]
filename = filename[index:]
[update 2014-04-12]
Optei por usarrfind
, mas poderia ter usadopath.basename
epath.dirname
. É uma questão de escolha e estilo.
[/update]
Feito o slice, já podemos exibir a caixa de diálogo de salvamento. Para isso precisamos da ferramenta
Modifique o
asksaveasfilename
.Modifique o
import
do askopenfilename
para:
from tkFileDialog import askopenfilename, asksaveasfilename
Continuando agora no método
save_as()
, podemos exibir a caixa de diálogo e, se algum arquivo for informado, chamar o método save()
:
filename = asksaveasfilename(
title = 'EditPy | Save As',
initialfile = filename,
initialdir = directory,
)
if filename:
self.filename = filename
self.save()
E pronto! Já pode testar seu editor de texto!
Lapidando
Uma forma de melhorar um pouco o visual da aplicação é usando a extensão ajulejada do
Para tanto, basta adicionar ao cabeçalho, no final dos
tk
, chamada ttk
.Para tanto, basta adicionar ao cabeçalho, no final dos
import
s:
from ttk import *
Você também pode usar os métodos
wm_protocol()
e bind_all()
da janela raiz para adicionar funcionalidades interessantes, mas isso é história pra outro dia. ;-)
[]’s
Cacilhας, La Batalema