Grafikk 2
- Tegne sentrerte figurer
- Hjelpefunksjoner for grafikk
- Dynamisk tekststørrelse
- Bonus: Tigonometri og sirkulære mønster
- Bonus: Klokker
Tegne sentrerte figurer
from uib_inf100_graphics import *
def redraw_all(app, canvas):
# Tilnærming #1: Legg til margin venstre/topp, trekk fra margin høyre/bunn:
# Gir enkelt kontroll på avstand til kanten
margin = 50
width = app.width
height = app.height
canvas.create_rectangle(margin, margin, width-margin, height-margin,
fill='darkred', outline='black')
# Tilnærming #2: Regn ut sentrum (cx, cy). Så, legg til/trekk fra halve
# bredden for venstre/høyre, og legg til/trekk fra halve høyden for
# topp/bunn. Gir enkel kontroll på størrelsen til elementet som tegnes.
(cx, cy) = (app.width/2, app.height/2)
(rectWidth, rectHeight) = 60, 30
canvas.create_rectangle(cx - rectWidth/2, cy - rectHeight/2,
cx + rectWidth/2, cy + rectHeight/2,
fill='gold', outline='black')
run_app(width=400, height=200)
Hjelpefunksjoner for grafikk
from uib_inf100_graphics import *
def draw_belgian_flag(canvas, x0, y0, x1, y1):
""" Tegner et Belgisk flagg i området som er avgrenset av (x0, y0)
i hjørnet til venstre oppe, og av hjørnet (x1, y1) i hjørnet
til høyre nede."""
width = (x1 - x0)
canvas.create_rectangle(x0, y0, x0+width/3, y1, fill='black', width=0)
canvas.create_rectangle(x0+width/3, y0, x0+width*2/3, y1,
fill='yellow', width=0)
canvas.create_rectangle(x0+width*2/3, y0, x1, y1, fill='red', width=0)
def redraw_all(app, canvas):
# Tegn et stort flagg
draw_belgian_flag(canvas, 25, 25, 175, 150)
# Og et mindre flagg under
draw_belgian_flag(canvas, 75, 160, 125, 200)
# La oss tegne et rutenett med flagg
flag_width = 30
flag_height = 25
margin = 5
for row in range(4):
for col in range(6):
left = 200 + col * flag_width + margin
top = 50 + row * flag_height + margin
right = left + flag_width - margin
bottom = top + flag_height - margin
draw_belgian_flag(canvas, left, top, right, bottom)
run_app(width=400, height=200)
La oss tegne det japanske flagget. Her må vi sentrere en rød (med rgb-verdi #BC002C) sirkel i et hvitt flagg. Se kravene til flagget under
Figur CC-BY-SA-3.0 av Zscout370 via Wikimedia Commons Wikipedia.
from uib_inf100_graphics import *
def draw_japanese_flag(canvas, x0, y0, x1, y1):
""" Tegner et Japansk flagg i området som er avgrenset av (x0, y0)
i hjørnet til venstre oppe, og av hjørnet (x1, y1) i hjørnet
til høyre nede."""
height = (y1 - y0)
r = (height * 3 / 5) / 2
cx = (x0 + x1) / 2
cy = (y0 + y1) / 2
canvas.create_rectangle(x0, y0, x1, y1, outline="black")
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="#BC002C", width=0)
def redraw_all(app, canvas):
# Tegn et stort flagg
draw_japanese_flag(canvas, 25, 25, 25 + 150, 25 + 100)
# Og et mindre flagg under
draw_japanese_flag(canvas, 75, 150, 75 + 60, 150 + 40)
# La oss tegne et rutenett med flagg
margin = 5
flag_width = 27 + margin
flag_height = 18 + margin
for row in range(4):
for col in range(6):
left = 200 + col * flag_width + margin
top = 50 + row * flag_height + margin
right = left + flag_width - margin
bottom = top + flag_height - margin
draw_japanese_flag(canvas, left, top, right, bottom)
run_app(width=400, height=200)
Dynamisk tekststørrelse
from uib_inf100_graphics import *
def redraw_all(app, canvas):
# Dynamisk størrelse for tekst er ikke like rett frem som
# geometriske figurer med koodinater basert på vinduets høyde
# og bredde, men det er fremdeles mulig.
# Regn font-størrelse basert på f. eks. vinduets bredde
# Litt prøving og feiling for å finne en god formel.
textSize = app.width // 15
text = 'Endre vindustørrelse!'
canvas.create_text(app.width/2, app.height/2, text=text,
font=f'Arial {textSize} bold', fill='black')
run_app(width=400, height=200)
Tigonometri og sirkulære mønster
Dette avsnitt om trigonometri er ikke pensum i INF100, men kan være nyttig om du ønsker å tegne figurer elle mønstre som «går i ring,» slik som stjerner, klokker, n-kanter og lignende.
De aller fleste rammeverk for grafikk, inkludert tkinter og uib_inf100_graphics
, benytter et koordinatsystem hvor y vokser nedover. Når vi jobber med trigonometri og vinkler må vi derfor huske å snu opp ned også her. Legg merke til at gradvis økende vinkel \(\theta\) vil snurre med klokken, og ikke mot klokken slik vi er vant til fra grunnskolematematikken.
from uib_inf100_graphics import *
import math
def draw_clock_face(canvas, cx, cy, r):
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill='yellow', outline='black')
r *= 0.85 # mindre radius for å tegne tallene på klokken
for hour in range(12):
# Rett opp tilsvarer en vinkel på -pi/2 radianer
# (husk, y-aksen vokser nedover)
hour_theta = -math.pi/2 + (2*math.pi)*(hour/12)
hour_x = cx + r * math.cos(hour_theta)
hour_y = cy + r * math.sin(hour_theta)
label = str(hour if hour > 0 else 12)
canvas.create_text(hour_x, hour_y, text=label,
font='Arial 14 bold', fill='black')
def redraw_all(app, canvas):
draw_clock_face(canvas, cx=app.width/2, cy=app.height/2,
r=min(app.width, app.height)/3)
run_app(width=300, height=200)
Klokker
Dette avsnittet om klokker er ikke pensum i INF100, men kan være et fint eksempel å referere til om man ønsker å tegne figurer med sirkulære mønstre.
from uib_inf100_graphics import *
import math
def draw_clock_face(canvas, cx, cy, r):
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill='yellow', outline='black')
def draw_clock_hands(canvas, cx, cy, r, hour, minute):
# Juster timene til 12-tallsystemet
hour %= 12
# Juster timene slik at de tar hensyn til minuttene også
hour += minute / 60
# Time-viseren
hour_r = r * 0.5
hour_theta = -math.pi/2 + (2*math.pi)*(hour/12)
hour_x = cx + hour_r * math.cos(hour_theta)
hour_y = cy + hour_r * math.sin(hour_theta)
canvas.create_line(cx, cy, hour_x, hour_y, fill="black")
# Minutt-viseren
minute_r = r * 0.9
minute_theta = -math.pi/2 + (2*math.pi)*(minute/60)
minute_x = cx + minute_r * math.cos(minute_theta)
minute_y = cy + minute_r * math.sin(minute_theta)
canvas.create_line(cx, cy, minute_x, minute_y, fill="black")
def draw_clock(canvas, x0, y0, x1, y1, hour, minute):
canvas.create_rectangle(x0, y0, x1, y1, outline="black")
(cx, cy) = (x0 + x1) / 2, (y0 + y1) / 2
width = abs(x0 - x1)
height = abs(y0 - y1)
r = min(width, height) / 2
draw_clock_face(canvas, cx, cy, r)
draw_clock_hands(canvas, cx, cy, r, hour, minute)
def redraw_all(app, canvas):
# En stork klokke som viser tiden 14:30
draw_clock(canvas, 25, 25, 175, 150, 14, 30)
# En mindre klokke under som viser tiden 07:45
draw_clock(canvas, 75, 160, 125, 200, 7, 45)
# Et helt rutenett med klokker
width = 40
height = 40
margin = 5
hour = 0
for row in range(3):
for col in range(4):
left = 200 + col * width + margin
top = 50 + row * height + margin
right = left + width - margin
bottom = top + height - margin
hour += 1
draw_clock(canvas, left, top, right, bottom, hour, 0)
run_app(width=400, height=230)
La klokken komme til live med hjelp av app_started
og timer_fired
. Dette er konsepter som kommer om en uke eller to, men en liten forsmak skader ikke :)
from uib_inf100_graphics import *
import math
def app_started(app):
# app_started kalles én gang når programmet starter
# vi kan lage variabler som hører hjemme inne i "app"
app.hour = 0
app.minute = 0
def timer_fired(app):
# Denne metoden kalles periodisk med et gitt intervall (standard: 100ms)
# Vi skrur tiden frem med ett minutt
app.minute += 1
if app.minute >= 60:
app.minute = 0
app.hour = (app.hour + 1) % 24
def draw_clock_face(canvas, cx, cy, r):
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill='yellow', outline='black')
r *= 0.85 # mindre radius for å tegne tallene på klokken
for hour in range(12):
# Rett opp tilsvarer en vinkel på -pi/2 radianer (husk, y-aksen vokser nedover)
hour_theta = -math.pi/2 + (2*math.pi)*(hour/12)
hour_x = cx + r * math.cos(hour_theta)
hour_y = cy + r * math.sin(hour_theta)
label = str(hour if hour > 0 else 12)
canvas.create_text(hour_x, hour_y, text=label,
font='Arial 14 bold', fill='black')
def draw_clock_hands(canvas, cx, cy, r, hour, minute):
# Juster timene til 12-tallsystemet
hour %= 12
# Juster timene slik at de tar hensyn til minuttene også
hour += minute / 60
# Time-viseren
hour_r = r * 0.5
hour_theta = -math.pi/2 + (2*math.pi)*(hour/12)
hour_x = cx + hour_r * math.cos(hour_theta)
hour_y = cy + hour_r * math.sin(hour_theta)
canvas.create_line(cx, cy, hour_x, hour_y, fill="black")
# Minutt-viseren
minute_r = r * 0.9
minute_theta = -math.pi/2 + (2*math.pi)*(minute/60)
minute_x = cx + minute_r * math.cos(minute_theta)
minute_y = cy + minute_r * math.sin(minute_theta)
canvas.create_line(cx, cy, minute_x, minute_y, fill="black")
def draw_clock(canvas, x0, y0, x1, y1, hour, minute):
# Tegn en rektangel rundt området hvor klokken skal være
canvas.create_rectangle(x0, y0, x1, y1, outline="black")
# Regn ut sentrum og radius av klokken
(cx, cy) = (x0 + x1) / 2, (y0 + y1) / 2
width = abs(x0 - x1)
height = abs(y0 - y1)
r = min(width, height) / 2
# Tegn først klokken, så tegnes viserne oppå
draw_clock_face(canvas, cx, cy, r)
draw_clock_hands(canvas, cx, cy, r, hour, minute)
def redraw_all(app, canvas):
draw_clock(canvas, app.width/5, app.height/5, app.width*4/5, app.height*4/5, app.hour, app.minute)
run_app(width=300, height=200)