Appendiks til grafiske brukergrensesnitt
Innholdet på denne siden vil ikke komme på eksamen. Vi publiserer det her slik at dem som ønsker får se flere muligheter som finnes i
uib_inf100_graphics.event_app
-rammeverket.
Denne siden er appendiks til notatene om grafiske brukergrensesnitt.
- Innebygde tastaturbindinger
- Brukerhendelser (kontroller-funksjoner)
- Dialogbokser og popup
- Bilder
- Animerte bilder
- Modus (ulike skjermer)
- Scrolling
- Spille av lyder
Innebygde tastaturbindinger
from uib_inf100_graphics.event_app import run_app
def app_started(app):
app.counter = 0
def timer_fired(app):
app.counter += 1
def redraw_all(app, canvas):
canvas.create_text(200, 40, text='Tastaturbindinger', font='Arial 20')
canvas.create_text(200, 80, text='Trykk ctrl-p for pause')
canvas.create_text(200, 120, text='Trykk ctrl-s for å ta skjermbilde')
canvas.create_text(200, 260, text='Trykk ctrl-q for å lukke')
canvas.create_text(200, 200, text='Trykk ctrl-x for å avslutte alt')
canvas.create_text(200, 240, text=f'{app.counter}')
run_app(width=400, height=300) # ctrl-q fortsetter videre, ctrl-x gjør ikke
run_app(width=600, height=400)
Brukerhendelser (kontroller-funksjoner)
from uib_inf100_graphics.event_app import run_app
def app_started(app):
app.messages = ['app_started']
def app_stopped(app):
app.messages.append('app_stopped')
print('app_stopped!')
def key_pressed(app, event):
app.messages.append('key_pressed: ' + event.key)
def key_released(app, event):
app.messages.append('key_released: ' + event.key)
def mouse_pressed(app, event):
app.messages.append(f'mouse_pressed {(event.x, event.y)}')
def mouse_released(app, event):
app.messages.append(f'mouse_released {(event.x, event.y)}')
def mouse_moved(app, event):
app.messages.append(f'mouse_moved {(event.x, event.y)}')
def mouse_dragged(app, event):
app.messages.append(f'mouse_dragged {(event.x, event.y)}')
def size_changed(app):
app.messages.append(f'size_changed {(app.width, app.height)}')
def redraw_all(app, canvas):
font = 'Arial 20 bold'
canvas.create_text(app.width/2, 30, text='Brukerhendelser', font=font)
n = min(10, len(app.messages))
i0 = len(app.messages) - n
for i in range(i0, len(app.messages)):
canvas.create_text(
app.width / 2,
100 + 50 * (i - i0),
text=f'#{i}: {app.messages[i]}',
font=font
)
run_app(width=600, height=600)
Dialogbokser og popup
Rammeverket uib_inf100_graphics bygger på og er en forenklet versjon av tkinter. Dette gjør det er mulig å integrere komponenter som for eksempel dialogbokser og meldingsbokser fra tkinter.
from uib_inf100_graphics.event_app import run_app
from tkinter import simpledialog, messagebox
def app_started(app):
app.message = 'Klikk med musen for å starte!'
app.count = 0
def timer_fired(app):
app.count += 1
def mouse_pressed(app, event):
name = simpledialog.askstring('Navn', 'Hva heter du?', parent=app._root)
if name is None:
app.message = 'Du avbrøt!'
else:
messagebox.showinfo('Info her', 'Du skrev: ' + name, parent=app._root)
app.message = f'Hei, {name}!'
def redraw_all(app, canvas):
canvas.create_text(200, 30, text=app.message)
canvas.create_text(200, 70, text=f'{app.count}')
run_app(width=400, height=200)
Bilder
# Vis et bilde hentet fra internett
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http, scaled_image
def app_started(app):
url = 'https://tinyurl.com/inf100logo-png'
app.image1 = load_image_http(url)
app.image2 = scaled_image(app.image1, 0.5)
def redraw_all(app, canvas):
canvas.create_image(200, 150, pil_image=app.image1)
canvas.create_image(500, 150, pil_image=app.image2)
run_app(width=700, height=300)
For å vise et bilde lagret lokalt på datamaskinen, husk at filstien til bildet er relativ til mappen programmet kjøres fra, en mappe som ikke nødvendigvis er den samme mappen hvor programmet ligger. For å kjøre eksempelkoden under, last ned greensnake.png. Resultatet blir akkurat som over, men koden vil også fungere uten internett-tilgang.
# Vis et bilde lagret lokalt på datamaskinen
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image, scaled_image
def app_started(app):
app.image1 = load_image('greensnake.png')
app.image2 = scaled_image(app.image1, 0.5)
def redraw_all(app, canvas):
canvas.create_image(200, 150, pil_image=app.image1)
canvas.create_image(500, 150, pil_image=app.image2)
run_app(width=700, height=300)
# Ankeret bestemmer hvor bildet tegnes i forhold til koordinatet (x, y)
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http, scaled_image
def app_started(app):
url = 'https://tinyurl.com/inf100logo-png'
app.image1 = load_image_http(url)
app.image2 = scaled_image(app.image1, 0.5)
def redraw_all(app, canvas):
x, y = app.width/2, app.height/2
# Prøv ulike verdier for anchor:
# 'n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw', 'center'
canvas.create_image(x, y, pil_image=app.image2, anchor='e')
canvas.create_image(x, y, pil_image=app.image2, anchor='sw')
canvas.create_oval(x-8, y-8, x+8, y+8, fill='yellow')
run_app(width=700, height=300)
# Finn bildet sin størrelse
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http, scaled_image
def app_started(app):
url = 'https://tinyurl.com/inf100logo-png'
app.image1 = load_image_http(url)
app.image2 = scaled_image(app.image1, 0.5)
app.image3 = scaled_image(app.image1, 0.3)
def draw_image_with_size_below_it(app, canvas, image, cx, cy):
canvas.create_image(cx, cy, pil_image=image)
image_width, image_height = image.size
msg = f'Bildestørrelse: {image_width} x {image_height}'
canvas.create_text(cx, cy + image_height/2 + 20, text=msg, font='Arial 16')
def redraw_all(app, canvas):
draw_image_with_size_below_it(app, canvas, app.image2, 200, 150)
draw_image_with_size_below_it(app, canvas, app.image3, 500, 150)
run_app(width=700, height=300)
# Bruk transpose-metoden for å snu eller flippe et bilde
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http, scaled_image
from PIL import Image
def app_started(app):
url = 'https://tinyurl.com/inf100logo-png'
org_image = load_image_http(url)
org_image = scaled_image(org_image, 1/5)
# Rotere et bilde 90 grader
rotated_image = org_image.transpose(Image.ROTATE_90)
app.images = [org_image, rotated_image]
# Flere måter å rotere/flippe et bilde på
app.images += [org_image.transpose(tp_method) for tp_method in (
Image.ROTATE_180,
Image.ROTATE_270,
Image.FLIP_LEFT_RIGHT,
Image.TRANSPOSE,
Image.FLIP_TOP_BOTTOM,
Image.TRANSVERSE,
)]
def redraw_all(app, canvas):
allocated_width_per_image = app.width / len(app.images)
for i, img in enumerate(app.images):
x = (i + 0.5) * allocated_width_per_image
y = app.height / 2
canvas.create_image(x, y, pil_image=img)
run_app(width=700, height=200)
# Hent farger med getpixel, og manipulér eller tegn nye bilder med putpixel
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http
from PIL import Image
def app_started(app):
url = 'https://tinyurl.com/inf100logo-png'
app.image1 = load_image_http(url)
# la oss lage en kopi som bytter om blå og grønn -verdier
app.image1 = app.image1.convert('RGB')
app.image2 = Image.new(mode='RGB', size=app.image1.size)
for x in range(app.image2.width):
for y in range(app.image2.height):
r, g, b = app.image1.getpixel((x, y))
app.image2.putpixel((x, y), (r, b, g)) # merk: b og g byttet plass
def redraw_all(app, canvas):
canvas.create_image(200, 300, pil_image=app.image1)
canvas.create_image(500, 300, pil_image=app.image2)
run_app(width=700, height=600)
# Bruk ImageDraw for å lage nye bilder og tegne på dem
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import scaled_image
from PIL import Image, ImageDraw
def app_started(app):
image_width, image_height = app.width//3, app.height//2
bg_color = (0, 255, 255) # cyan
app.image1 = Image.new('RGB', (image_width, image_height), bg_color)
# Nå som vi har laget et nytt bilde, bruk ImageDraw for å tegne i det
# Se https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html
draw = ImageDraw.Draw(app.image1)
draw.line((0, 0, image_width, image_height), width=10, fill=(255, 0, 0))
draw.line((0, image_height, image_width, 0), width=10, fill=(0, 0, 255))
# Så lager vi en skalert kopi for å vise at dette er et helt vanlig bilde
app.image2 = scaled_image(app.image1, 2/3)
def redraw_all(app, canvas):
canvas.create_image(app.width/4, app.height/2, pil_image=app.image1)
canvas.create_image(app.width*3/4, app.height/2, pil_image=app.image2)
run_app(width=600, height=300)
# Bruk get_snapshot og save_snapshot for å ta bilde av programmet
# Merk: dette er litt ustabilt på noen operativsystemer, og kan
# kreve at du gir programmet spesiell tilgang for å ta screenshot
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import scaled_image
def app_started(app):
app.image = None
def key_pressed(app, event):
if (event.key == 'g'):
snapshotImage = app.get_snapshot()
app.image = scaled_image(snapshotImage, 0.45)
elif (event.key == 's'):
app.save_snapshot()
def redraw_all(app, canvas):
canvas.create_text(350, 20, text='Press g to get_snapshot', fill='black')
canvas.create_text(350, 40, text='Press s to save_snapshot', fill='black')
canvas.create_rectangle(50, 100, 250, 250, fill='blue')
if app.image is not None:
canvas.create_image(525, 160, pil_image=app.image)
run_app(width=700, height=300)
Animerte bilder
En sprite er et bilde/en tegning som representerer et objekt som utgjør en (liten) del av skjermbildet. I sammenheng med dataspill vil en sprite typisk være knyttet til et logisk element i spillet, slik som en figur, et hus, en fiende, en energidrikk, et stykke med gress eller lignende. Det er vanlig at sprites er animert; for eksempel en animasjon av at figuren går. En slik animasjon består av en sekvens med bilder som spilles av i ring. Under er et eksempel på en bildefil som representerer en animert figur.
Bilde: CC0, Walkin man figure av JayNick, via freesvg.org.
For å animere denne spriten, klipper vi opp bildet i 8 like store biter, og viser dem frem etter hverandre i et sirkulært mønster.
# En demo for å animere sprites med bruk av Pillow/PIL
# crop-metoden klipper ut en del av bildet
# Se her for flere detaljer:
# https://pillow.readthedocs.io/en/stable/reference/Image.html
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http
def app_started(app):
# Bilde av JayNick, CC0, https://freesvg.org/walking-man-figure
url = 'https://tinyurl.com/inf100-strekmann-png'
spritestrip = load_image_http(url)
# Vi lager en liste som inneholder hvert av de 8 bildene separat
app.sprites = []
image_width, image_height = spritestrip.size
sprite_width = image_width / 8
for i in range(8):
left_x = i * sprite_width
right_x = left_x + sprite_width
sprite = spritestrip.crop((left_x, 0, right_x, image_height))
app.sprites.append(sprite)
app.sprite_counter = 0
def timer_fired(app):
# Gå til neste bilde i bilde-sekvensen
app.sprite_counter = (1 + app.sprite_counter) % len(app.sprites)
def redraw_all(app, canvas):
# Bakgrunnsfarge
bg = 'papaya whip'
canvas.create_rectangle(0, 0, app.width, app.height, fill=bg, outline='')
# Tegn strekmann
sprite = app.sprites[app.sprite_counter]
canvas.create_image(200, 200, pil_image=sprite)
run_app(width=400, height=400)
Gif er en type bilder som er animert i utgangspunktet, slik som bildet under. Vi kan bruke animerte gif’er for å lage en animert sprite. Det er mulig å hente ut timing-informasjon for hvert stillbilde i gif’en, men det krever en del omtanke å få programmet ditt til å vise gif’en med riktig timing (det skjer ikke automatisk).
Bilde: CC0, «Traffic light» fra EditableGIFs.com
# En demo for å vise animert gif/apng sprites
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http
from datetime import datetime, timedelta
def app_started(app):
animation = load_image_http('https://files.stromme.me/inf100/traffic.gif')
app.frames = extract_frames(animation)
app.frame_index = 0
app.last_frame_switch = datetime.now()
def extract_frames(multiframe_image):
'''Given an image with multiple frames (such as a gif), return a list of
individual frames. The frames will be tagged with their original duration
in the 'duration' property of the 'info' dictionary.'''
frames = []
i = 0
while True:
try:
multiframe_image.seek(i)
except EOFError:
break
duration = multiframe_image.info['duration']
frames.append(multiframe_image.copy())
frames[-1].info['duration'] = duration
i += 1
return frames
def timer_fired(app):
maintain_animation(app)
def maintain_animation(app):
'''Ensure that the animation is displayed at the intended speed.
This method interprets the duration as a minimum display time for
the frame, and will hence drift over time.'''
current_frame = app.frames[app.frame_index]
current_time = datetime.now()
min_display_time = timedelta(milliseconds=current_frame.info['duration'])
time_since_last_frame_switch = current_time - app.last_frame_switch
if min_display_time < time_since_last_frame_switch:
# Go to next frame
app.frame_index = (1 + app.frame_index) % len(app.frames)
app.last_frame_switch = current_time
def redraw_all(app, canvas):
current_frame = app.frames[app.frame_index]
canvas.create_image(app.width/2, app.height/2, pil_image=current_frame)
canvas.create_text(app.width/2, 20, text=(
f'{app.frame_index}/{len(app.frames)} '
f'{current_frame.info["duration"]=} '
))
run_app(width=500, height=300)
Modus (ulike skjermer)
from uib_inf100_graphics.event_app import run_app
from uib_inf100_graphics.helpers import load_image_http, scaled_image
import random
##########################################
# Splash-skjerm -modus
##########################################
def splash_screen_mode_redraw_all(app, canvas):
canvas.create_image(
app.width / 2,
app.height / 2,
pil_image=app.splash_image,
)
canvas.create_text(
app.width / 2,
app.height / 2,
text=(
'Demo av en modal applikasjon!\n'
'Dette er en modal skjerm!\n'
'Trykk en tast for å starte!'
),
font=app.default_font,
justify='center',
)
def splash_screen_mode_key_pressed(app, event):
app.mode = 'game_mode'
##########################################
# Spill -modus
##########################################
def game_mode_redraw_all(app, canvas):
canvas.create_text(
app.width / 2,
10,
text=(
f'Poeng: {app.score}\n'
'Klikk på rundingen!\n'
'Trykk h for hjelpe-skjerm\n'
'Trykk v for å bryte MVC'
),
font=app.default_font,
justify='center',
anchor='n',
)
draw_dot(canvas, app.x, app.y, app.r, app.color)
if app.make_MVC_violation:
app.ohNo = 'Gjør et brudd med MVC skjer her!'
def game_mode_timer_fired(app):
move_dot(app)
def game_mode_mouse_pressed(app, event):
d = ((app.x - event.x)**2 + (app.y - event.y)**2)**0.5
if d <= app.r:
app.score += 1
randomize_dot(app)
elif app.score > 0:
app.score -= 1
def game_mode_key_pressed(app, event):
if event.key == 'h':
app.mode = 'help_mode'
elif event.key == 'v':
app.make_MVC_violation = True
##########################################
# Hjelpeskjerm -modus
##########################################
def help_mode_redraw_all(app, canvas):
canvas.create_text(
app.width / 2,
app.height / 2,
text=(
'Her er hjelpeskjermen!\n'
'(hjelpsom melding her)\n'
'Trykk en tast for å snu'
),
font=app.default_font,
justify='center',
)
def help_mode_key_pressed(app, event):
app.mode = 'game_mode'
##########################################
# Felles hjelpefunksjoner
##########################################
def randomize_dot(app):
app.x = random.randrange(20, app.width - 20)
app.y = random.randrange(20, app.height - 20)
app.r = random.randrange(10, 20)
app.color = random.choice(['red', 'orange', 'yellow', 'green', 'blue'])
app.dx = random.choice([+1, -1]) * random.randrange(3, 7)
app.dy = random.choice([+1, -1]) * random.randrange(3, 7)
def move_dot(app):
app.x += app.dx
if (app.x < 0) or (app.x > app.width):
app.dx = -app.dx
app.y += app.dy
if (app.y < 0) or (app.y > app.height):
app.dy = -app.dy
def draw_dot(canvas, x, y, r, color):
canvas.create_oval(x - r, y - r, x + r, y + r, fill=color)
##########################################
# Oppstart av applikasjonen
##########################################
def app_started(app):
splash_img_url = 'https://files.stromme.me/inf100/splashbox-alpha.png'
app.splash_image = load_image_http(splash_img_url)
app.splash_image = scaled_image(app.splash_image, 0.85)
app.default_font = 'Arial 20'
app.mode = 'splash_screen_mode'
app.score = 0
app.timer_delay = 50
app.make_MVC_violation = False
randomize_dot(app)
run_app(width=500, height=250)
Scrolling
# Sidelengs scrolling (hele verden beveger seg):
from uib_inf100_graphics.event_app import run_app
import random
def app_started(app):
app.scroll_x = 0
app.dots = []
for _ in range(50):
x = random.randrange(app.width)
y = random.randrange(60, app.height)
app.dots.append((x, y))
def key_pressed(app, event):
if event.key == 'Left':
app.scroll_x -= 5
elif event.key == 'Right':
app.scroll_x += 5
def redraw_all(app, canvas):
# Tegn spilleren (som alltid er midt på skjermen)
draw_dot(canvas, app.width/2, app.height/2, 10, 'cyan')
# Tegn prikkene, sideforskjøvet med et offset scroll_x
for (cx, cy) in app.dots:
cx -= app.scroll_x # <-- Her sideforskyver vi prikkene på lerretet
draw_dot(canvas, cx, cy, 10, 'lightGreen')
# Tegn x- og y-aksen
x = app.width/2 - app.scroll_x # <-- Her sideforskyver vi
y = app.height/2
canvas.create_line(x, 0, x, app.height)
canvas.create_line(0, y, app.width, y)
# Tegn instruksjoner og debug-informasjon
x = app.width/2
canvas.create_text(x, 20, text='Bruk piltaster for å flytte spilleren')
canvas.create_text(x, 40, text=f'{app.scroll_x = }')
def draw_dot(canvas, x, y, r, color):
canvas.create_oval(x - r, y - r, x + r, y + r, fill=color)
run_app(width=300, height=300)
# Sidelengs scrolling når figuren er på vei ut av skjermen
from uib_inf100_graphics.event_app import run_app
import random
def app_started(app):
app.scroll_x = -app.width/2 # Initiell scroll slik at x=0 er sentrert
app.scroll_margin = 50
app.player_x = 0 # spillerens initielle posisjon
app.dots = [(random.choice(range(-app.width//2, app.width//2)),
random.choice(range(60, app.height))) for _ in range(50)]
def make_player_visible(app):
# scroll skjermen så mye som nødvendig for at spilleren vises
if (app.player_x < app.scroll_x + app.scroll_margin):
app.scroll_x = app.player_x - app.scroll_margin
if (app.player_x > app.scroll_x + app.width - app.scroll_margin):
app.scroll_x = app.player_x - app.width + app.scroll_margin
def move_player(app, dx, dy):
app.player_x += dx
make_player_visible(app)
def key_pressed(app, event):
if (event.key == "Left"): move_player(app, -5, 0)
elif (event.key == "Right"): move_player(app, +5, 0)
def redraw_all(app, canvas):
# Tegn spilleren, sideforskjøvet med et offset scroll_x
cx, cy, r = app.player_x, app.height/2, 10
cx -= app.scroll_x # <-- Her sideforskyver vi spilleren
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill='cyan')
# Tegn prikkene, sideforskjøvet med et offset scroll_x
for (cx, cy) in app.dots:
cx -= app.scroll_x # <-- Her sideforskyver vi prikkene
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill='lightGreen')
# Tegn x- og y-aksen
x = -app.scroll_x # <-- Her sideforskyver vi y-aksen
y = app.height/2
canvas.create_line(x, 0, x, app.height)
canvas.create_line(0, y, app.width, y)
# Tegn instruksjoner og debug-informasjon
x = app.width/2
canvas.create_text(x, 20, text='Use arrows to move left or right',
fill='black')
canvas.create_text(x, 40, text=f'app.scroll_x = {app.scroll_x}',
fill='black')
run_app(width=300, height=300)
# Sidelengs scrolling når figuren er på vei ut av skjermen
# Kollisjonsdeteksjon for spilleren
from uib_inf100_graphics.event_app import run_app
def app_started(app):
# Scrolling
app.scroll_margin = 50
app.scroll_x = -app.scroll_margin
app.scroll_y = -app.height / 2
# Spiller - koordinate for hjørnet til venstre nederst
app.player_x = 0
app.player_y = 0
app.player_width = 10
app.player_height = 20
# Murer
app.walls = 5
app.wall_points = [0]*app.walls
app.wall_width = 20
app.wall_height = 40
app.wall_x_positions = [90 * (i + 1) for i in range(app.walls)]
app.current_wall_hit = -1 # -1 når ingen mur er truffet
def get_player_bounds(app):
# returnerer absolutt-posisjon til spiller på et uendelig lerret
# tar ikke hensyn til scrolling
(x0, y1) = (app.player_x, app.player_y)
(x1, y0) = (x0 + app.player_width, y1 - app.player_height)
return (x0, y0, x1, y1)
def get_wall_bounds(app, wall):
# returnerer absolutt-posisjon til en mur på et uendelig lerret
# tar ikke hensyn til scrolling
(x0, y1) = (app.wall_x_positions[wall], 0)
(x1, y0) = (x0 + app.wall_width, y1 - app.wall_height)
return (x0, y0, x1, y1)
def get_wall_hit(app):
# returnerer hvilken mur spilleren er over for øyeblikket
# merk: i større spill bør denne funksjonen optimeres til å kun
# sjekke de objektene som er synlige.
player_bounds = get_player_bounds(app)
for wall in range(app.walls):
wall_bounds = get_wall_bounds(app, wall)
if bounds_intersect(app, player_bounds, wall_bounds):
return wall
return -1
def bounds_intersect(app, bounds_a, bounds_b):
# return l2<=r1 and t2<=b1 and l1<=r2 and t1<=b2
(ax0, ay0, ax1, ay1) = bounds_a
(bx0, by0, bx1, by1) = bounds_b
return ((ax1 >= bx0) and (bx1 >= ax0) and
(ay1 >= by0) and (by1 >= ay0))
def check_for_new_wall_hit(app):
# sjekk om vi treffer en ny mur for første gang
wall = get_wall_hit(app)
if (wall != app.current_wall_hit):
app.current_wall_hit = wall
if (wall >= 0):
app.wall_points[wall] += 1
def make_player_visible(app):
# scroll for å gjøre spilleren synlig
m = app.scroll_margin
if (app.player_x < app.scroll_x + m):
app.scroll_x = app.player_x - m
if (app.player_x > app.scroll_x + app.width - m - app.player_width):
app.scroll_x = app.player_x - app.width + m + app.player_width
if (app.player_y < app.scroll_y + m + app.player_height):
app.scroll_y = app.player_y - m - app.player_height
if (app.player_y > app.scroll_y + app.height - m):
app.scroll_y = app.player_y - app.height + m
def move_player(app, dx, dy):
app.player_x += dx
app.player_y += dy
make_player_visible(app)
check_for_new_wall_hit(app)
def size_changed(app):
make_player_visible(app)
def mouse_pressed(app, event):
app.player_x = event.x + app.scroll_x - app.player_width/2
app.player_y = event.y + app.scroll_y + app.player_height/2
make_player_visible(app)
check_for_new_wall_hit(app)
def key_pressed(app, event):
if (event.key == "Left"): move_player(app, -5, 0)
elif (event.key == "Right"): move_player(app, +5, 0)
elif (event.key == "Up"): move_player(app, 0, -5)
elif (event.key == "Down"): move_player(app, 0, +5)
def redraw_all(app, canvas):
sx = app.scroll_x
sy = app.scroll_y
# Tegn x-aksen
line_y = -sy
line_height = 5
canvas.create_rectangle(0, line_y, app.width, line_y+line_height,
fill="black")
# Tegn murene
# (Merk: kan optimiseres til å kun tegne synlige murer)
for wall in range(app.walls):
(x0, y0, x1, y1) = get_wall_bounds(app, wall)
fill = "orange" if (wall == app.current_wall_hit) else "pink"
canvas.create_rectangle(x0-sx, y0-sy, x1-sx, y1-sy, fill=fill)
(cx, cy) = ((x0+x1)/2 - sx, (y0 + y1)/2 - sy)
canvas.create_text(cx, cy, text=str(app.wall_points[wall]),
fill='black')
cy = line_y + 5
canvas.create_text(cx, cy, text=str(wall), anchor=N, fill='black')
# Tegn spilleren
(x0, y0, x1, y1) = get_player_bounds(app)
canvas.create_oval(x0 - sx, y0 - sy, x1 - sx, y1 - sy, fill="cyan")
# Tegn instruksjonene
msg = "Bruk pilene eller musen for å flytte"
canvas.create_text(app.width/2, 20, text=msg, fill='black')
run_app(width=300, height=300)
Spille av lyder
For å spille av lyder må du først installere pygame, og så laste ned uib_inf100_music.py og legge den i samme mappe som python-filen du kjører. For å installere pygame:
- bruk kommandoen
pip install pygame
i terminalen (eksakt kommando kan variere litt mellom ulike operativsystem og hvordan python er installert på maskinen. Du kan også benytte samme skript du brukte for installasjon i grafikk, men bytt ut uib-inf100-graphics med pygame)
For at eksempelet under skal virke, må du også laste ned button.mp3 og music.mp3 i samme mappe programmet kjøres fra (som ikke nødvendigvis er samme mappe hvor programmet ligger).
from uib_inf100_graphics.event_app import run_app
from uib_inf100_music import load_sound_effect, load_looping_sound, stop_all_sounds
def app_started(app):
app.sound_effect = load_sound_effect('button.mp3')
app.background_music = load_looping_sound('music.mp3')
def app_stopped(app):
stop_all_sounds()
def key_pressed(app, event):
match event.key:
case 'b':
# toggle background music
if app.background_music.is_playing():
app.background_music.stop()
else:
app.background_music.play()
case 'e':
# play sound effect
app.sound_effect.play()
case 's':
# stop all sounds
stop_all_sounds()
case _ if event.key.isdigit():
# set volume
new_volume = float(event.key)/9
app.background_music.set_volume(new_volume)
def redraw_all(app, canvas):
text = (
f'{app.background_music.is_playing()=}\n'
f'{app.background_music.get_volume()=}\n\n'
'Trykk b for å starte/stoppe bakgrunnslyd\n'
'Trykk e for å spille en lydeffekt\n'
'Trykk s for å stoppe alle lyder\n'
'Trykk 0-9 for å sette volum for bakgrunnslyd'
)
canvas.create_text(app.width/2, app.height/2, text=text, font='Arial 20')
run_app(width=600, height=300)