Primeiros passos de orientação a objetos: usando a classe Slider
No começo do curso os principais exemplos de código que vimos se valem, em geral, de estratégias de programação sem “Orientação a Objetos”. Agora veremos como Python, assim como diversas outras linguagens, permite usar esta maneira de programar, pomposamente chamada de “paradigma de programação”: a Orientação a objetos (Object Orientation, por vezes abreviada OO). Python permite misturar elementos de diversos paradigmas.
Vamos começar apresentando os primeiros elementos e vocabulários da orientação a objetos.
Ideias principais e vocabulário
Classe (class), tipo (type) ou uma “categoria de objetos”
Tratando como equivalentes os termos classe e tipo, quando falamos sobre os valores manipulados pelo nosso programa é comum mencionarmos a categoria a que pertencem, isto é, de que tipo ou classe são. Os valores mais fundamentais, ditos primitivos, como os números que manipulamos, são em geral do tipo float (ponto flutuante) ou int (abreviação de integer, inteiros), já os textos são da classe str (abreviação de string, uma cadeia de caracteres). Estruturas como listas são objetos do tipo list e assim por diante. Você pode não ter visto mas o Processing nos entrega os dados de imagens carregadas do disco na forma de um objeto PImage
. Cada tipo de objeto pode ter propriedades e funcionalidades específicas (atributos e métodos) que os tornam mais úteis em determinados contextos.
Note que fora os tipos embutidos (acima mencionamos int, float, str e list, mas há vários outros), as classes normalmente seguem a convenção de ter a primeira letra maiúscula no nome, com Slider
que veremos mais à frente, e é especialmente recomendável seguir essa convençao para as classes que você criar.
Atributos (propriedades ou campos)
Objetos tem “valores ou propriedades” chamadas de atributos, que podem ser consultados usando a “sintaxe do ponto” (objeto.atributo
).
Por exemplo, quando carregamos uma imagem no Processing podemos consultar as dimensões dela nos atributos .width
e .height
:
img = loadImage('a.png') # uma imagem PNG na pasta /data/
w = img.width # largura em pixels
h = img.height # altura em pixels
Métodos (ou funções associadas aos objetos)
Objetos tem funções associadas, conhecidas como métodos, que podem ser invocadas com a “sintaxe do ponto” (objeto.metodo()
).
Uma lista em Python, por exemplo, possui diversos métodos e já vimos pelo menos um deles, o .append()
que é chamado para incluir elementos na lista.
frutas = ['uva', 'banana']
frutas.append('kiwi')
print(frutas) # ['uva', 'banana', 'kiwi']
Instanciar (criar uma nova instância de um objeto)
Fora casos especiais em que podemos criar objetos diretammente no código (como a lista de frutas que acabamos de ver) ou com uma função ajudante, no caso de loadImage(nome_arquivo)
que cria um objeto PImage
, costumamos criar novos objetos chamando o nome da classe, e isso pode ou não demandar argumentos. No exemplo que veremos a seguir vamos criar um slider.
s1 = Slider(0, 90, 50, 'tamanho') # mínimo, máximo, valor_inicial, etiqueta
O que ficou de fora
Não vamos ver ainda neste momento em detalhes de como funciona a definição ou criação da classe (a parte que segue class Slider:
), que codifica como ela produz e inicializa os objetos ou como são definidos os métodos, nem trataremos do assunto mais avançado “herança” em que uma classe é baseada em outra, recebendo desta parte das suas características.
Exemplo de uso da classe Slider
Veja agora um exemplo comentado de como instanciar e usar objetos da classe Slider
que vão servir de interface gráfica para modificar um desenho de uma àrvore.
Note que os objetos slider tem os métodos .position()
para locá-los na tela depois de terem sido criados, e o método .update()
, que chamaremos dentro da função draw()
para fazer o duplo trabalho de desenhar o slider na tela e obter o valor indicado pelo slider naquele momento.
from __future__ import unicode_literals # para textos com acento sem por `u` antes das aspas
def setup():
global s1, s2, s3
global seed
seed = int(random(1000))
print(seed)
size(500, 500)
s1 = Slider(0, 90, 50, 'tamanho') # instanciando o slider s1
s1.position(20, 30) # posicionando s1 em x:20 y:30
s2 = Slider(0, 180, 45, 'ângulo')
s2.position(190, 30)
s3 = Slider(0, 10, 0, 'variação aleatória')
s3.position(360, 30)
def draw():
global angulo, rndvar
randomSeed(seed)
background(240, 240, 200)
translate(250, 440)
tamanho = s1.update() # atualizando, desenha s1 na tela e obtem um valor
angulo = radians(s2.update())
rndvar = s3.update() / 10
galho(tamanho)
def galho(tamanho):
reducao = 0.75
sw = tamanho / 10
strokeWeight(sw)
line(0, 0, 0, -tamanho)
if tamanho > 5:
pushMatrix()
translate(0, -tamanho)
rotate(angulo)
galho(tamanho * reducao - random(-sw, sw) * rndvar)
rotate(-angulo * 2)
galho(tamanho * reducao - random(-sw, sw) * rndvar)
popMatrix()
# ...
# Atenção: precisa colar aqui a definição da classe Slider que está mais abaixo nesta página.
# ou colar em uma nova aba chamada slider.py e acrescentar `from slider import Slider` no início do sketch
Como é a definição da classe Slider
? (a classe por dentro)
Atenção: o código abaixo faz parte do exemplo acima. A boa prática diz que, principalmente para projetos mais complexos, é interessante separar as classes em outro arquivo para facilitar a manipulação e leitura do código. Para isso, basta cria uma nova aba slider, que se torna um arquivo
slider.py
. Nesse caso é preciso usar a instruçãofrom slider import Slider
no começo do seu código. Se não quiser fazer isso, simplesmente cole-o na aba principal, após o código anterior`.
Veja uma primeira versão da classe Slider
class Slider:
def __init__(self, low, high, default, label=''):
self.low , self.high = low, high
self.value = default
self.label = label
self.w, self.h = 120, 20
self.position(20, 20) # default position
def position(self, x, y):
self.x, self.y = x, y
self.rectx = self.x + map(self.value, self.low, self.high, 0, self.w)
def update(self):
if mousePressed and dist(mouseX, mouseY, self.rectx, self.y) < self.h:
self.rectx = mouseX
self.rectx = constrain(self.rectx, self.x, self.x + self.w)
self.value = map(self.rectx, self.x, self.x + self.w, self.low, self.high)
self.display()
return self.value
def display(self):
push() # combina pushMatrix() and pushStyle()
resetMatrix()
camera()
rectMode(CENTER)
strokeWeight(4)
stroke(200)
line(self.x, self.y, self.x + self.w, self.y)
strokeWeight(1)
# stroke(0)
fill(255)
stroke(0)
rect(self.rectx, self.y, self.w / 12, self.h)
fill(0)
textAlign(CENTER, CENTER)
text("{:.1f}".format(self.value), self.rectx, self.y + self.h)
text(self.label, self.x + self.w / 2, self.y - self.h)
pop() # popStyle() and popMat
Páginas relacionadas
- Um botão com orientação a objetos
- Uma classe de partículas simples
- Introdução a orientação a objetos com bandeirinhas (página externa)
Extra: Uma segunda versão da classe Slider
Acrescentando alguns extras e comentários à classe Slider
. Permite o uso de sliders em sketchs com 3D.
class Slider:
template = "{:.1f}" # para formatar como mostra o valor
label_align = CENTER
def __init__(self, low, high, default, label=''):
"""
Slider needs range from low to high
and and a default value. Label is optional.
"""
self.low = low
self.high = high
self.value = default
self.label = label
self.w, self.h = 120, 20
self.position(20, 20) # Pos default
def position(self, x, y):
"""Define as coordenadas na tela, e calcula rectx, pos. do 'handle'"""
self.x = x
self.y = y
# the position of the rect you slide:
self.rectx = self.x + map(self.value, self.low, self.high, 0, self.w)
def update(self):
"""Atualiza o slider e devolve o valor (self.value). Chama display()"""
# mousePressed moves slider
if mousePressed and dist(mouseX, mouseY, self.rectx, self.y) < self.h:
self.rectx = mouseX
# constrain rectangle
self.rectx = constrain(self.rectx, self.x, self.x + self.w)
self.value = map(self.rectx,
self.x, self.x + self.w,
self.low, self.high)
self.display()
return self.value
def display(self):
"""Desenha o slider na tela, usando coordenadas sem transformar"""
push() # Combina pushMatrix() e pushStyle()
resetMatrix() # push(), seguido de resetMatrix() e camera() permitem...
camera() # ... desenhar o slider no sistema de coordenadas original
rectMode(CENTER)
# Linha cinza sob o slider
strokeWeight(4)
stroke(200)
line(self.x, self.y, self.x + self.w, self.y)
# O retângulo, elemento principal da interface do slider
strokeWeight(1)
stroke(0)
fill(255)
translate(0, 0, 1)
rect(self.rectx, self.y, self.w / 12, self.h)
# Mostra o valor (value) atual
fill(0)
textSize(10)
textAlign(CENTER, CENTER)
text(self.template.format(self.value), self.rectx, self.y + self.h)
# draw label
if self.label_align == LEFT:
textAlign(self.label_align)
text(self.label, self.x, self.y - self.h)
else:
text(self.label, self.x + self.w / 2, self.y - self.h)
pop() # equivale a popStyle() and popMatrix()