Denne laben er fullstendig frivillig og vil ikke bli rettet, men spør gjerne en gruppeleder om hjelp hvis du sitter fast! Meningen med disse oppgavene er å bli litt kjent med brukergrensesnitt (interaktiv grafikk) før lab5.

Forberedelser

  • En lab er en samling med én eller flere oppgaver.
  • Hver lab kan gi opp til 25 poeng.
    • Oppvarmingsfeltet gir 1 poeng per oppgave, maksimalt 5 poeng.
    • Hovedfeltet gir 15 poeng dersom du består alle oppgavene, og ellers 0.
    • Ekstrafeltet gir 1 poeng per oppgave, maksimalt 5 poeng.
  • Alle oppgaver rettes automatisk. For å bestå en oppgave, må du bestå alle testene for oppgaven.
  • De automatiske testene for grafikk-oppgaver sjekker bare at programmet ditt faktisk kjører, og så laster den opp bildet du lager i galleriet – den sjekker ikke om du faktisk har løst riktig oppgave. Vi gjør derfor en rask manuell sjekk på de grafiske oppgavene.
  • Vi vil trekke poeng manuelt dersom vi oppdager at oppgaven åpenbart ikke er løst, eller man forsøker å trikse seg gjennom de automatiske testene.
  • Hver lab kan leveres så mange ganger du vil; det er siste innlevering som teller. Du kan umiddelbart se resultatene på de automatiske testene etter innlevering.

Hver lab utgjør 2.5% av den endelige karakteren din i emnet. Du må få til sammen 100 poeng eller mer på labene for å kunne ta eksamen.

Det er veldig viktig at du siterer alle kilder og eventuelle samarbeid. Se mitt.uib for mer informasjon om vår policy på plagiat og samarbeid.

Det er lurt å levere inn mange ganger underveis. Du får umiddelbart en automatisk tilbakemelding på oppgavene, og du kan prøve igjen hvis noe ikke virker.

Innlevering av lab’er foregår via verktøyet Codegrade som du kommer til via mitt.uib.

  • Gå til «Oppgåver» i menyen til venstre
  • Klikk deg videre til CodeGrade ved å klikke på «Last lab i eit nytt vindauge» Innlevering
  • Last opp filene du har laget for lab’en og trykk på «Submit.»

Fremdeles problemer? Se

(merk at videoen er litt gammel, så det kan være noen små forskjeller i hvordan det ser ut nå.)

Ser du ikke knappen du skal trykke på i mitt.uib?

  • Dobbeltsjekk at du er innlogget på mitt.uib.
  • Prøv å laste siden i nettleseren på nytt.
  • Prøv en «force refresh» (F5 eller Ctrl+F5 eller Ctrl+Shift+F5 eller Ctrl+Shift+R eller Command+R eller Command+Shift+R avhengig av nettleser og operativsystem).
  • Prøv å tøm cache i nettleseren din (se refreshyourcache.com for hvordan du gjør dette) og prøv på nytt.
  • Prøv en annen nettleser.
Endre farge med tastetrykk

I denne oppgaven skal vi få en boks på skjermen til å endre farge når vi trykker på tastaturet. Vi skal også gjøre slik at spesifikke tastetrykk endrer til spesifikke farger. Begynn med å kopiere denne koden inn i din egen fil colorchange_keypress.py:

from uib_inf100_graphics.event_app import run_app

def app_started(app):
    ...

def key_pressed(app, event):
    ...

def redraw_all(app, canvas):
    # tegn firkanten
    canvas.create_rectangle(
        25, 25, app.width - 25, app.height - 25,
        fill='yellow',
    )

run_app(width=400, height=150)

Kjør programmet allerede nå for å se hvordan det ser ut. Foreløpig viser koden bare et gult rektangel. Men når vi er ferdige vil vi kunne endre fargen med å klikke på tastaturet.

Del A: Fargevariabel

Siden vi ønsker å endre fargen på firkanten ved tastetrykk, kan det ikke være bestemt i redraw_all at den skal være «yellow», slik det nå er i koden. Vi må derfor lage en variabel som holder på fargen, og som vi kan endre på. Fargen kan begynne som 'yellow'. Deretter må vi gjøre slik at firkanten tegnes med fargen som angitt av variabelen.

  • Opprett en variabel for fargen i app_started ved å skrive app.color = 'yellow'.
  • Endre på redraw_all slik at den bruker fargevariabelen vi nettopp laget når du tegner rektangelet.

Tenk på app som en beholder for alle variablene som til sammen beskriver tilstanden for programmet ditt. Når du skriver app.color = 'yellow' i app_started-funksjonen oppretter du en variabel som heter color i app og setter den til å være 'yellow'.

Når funksjonen er implementert, skal programmet se ut som det gjorde fra starten av:

Illustrasjon del A fullført

Vi må først opprette en variabel for å holde på fargen i app_started, slik som dette:

def app_started(app):
    app.color = 'yellow'

Deretter endrer vi på redraw_all slik at den bruker fargevariabelen til app i stedet for å bruke en bestemt farge for å tegne rektangelet:

def redraw_all(app, canvas):
    # tegn firkanten
    canvas.create_rectangle(
        25, 25, app.width - 25, app.height - 25,
        fill=app.color,
    )

Del B: Tastetrykk

Nå skal vi gjøre slik at vi kan endre fargen på firkanten ved å trykke på tastene. Vi skal bruke funksjonen key_pressed til dette. Denne funksjonen kalles hver gang vi trykker på en tast. Til å begynne med skal vi endre fargen til svart om en hvilken som helst tast blir trykket inn.

Når du har gjort dette riktig, vil du kunne kjøre programmet og gjøre rektangelet svart ved å trykke på en hvilken som helst tast.

For å endre fargen til svart, må vi endre på fargevariabelen når det skjer et tastetrykk. Vi kan gjøre dette slik med key_pressed:

def key_pressed(app, event):
    app.color = 'black'

Del C: Flere farger

Nå skal vi gjøre det slik at noen taster kan endre fargen på rektangelet til spesielle farger. I key_pressed trenger vi da at:

  • Når vi trykker på r skal fargen bli rød
  • Når vi trykker på g skal fargen bli grønn
  • Når vi trykker på b skal fargen bli blå
  • Når vi trykker på y skal fargen bli gul
  • Når vi trykker på alle andre taster skal fargen bli svart.

Du kan gjerne legge til flere farger om du vil.

PS: Hvilken tast som ble trykket er lagret i event.key. For eksempel vil event.key være strengen 'r' dersom brukeren trykket på r-tasten.

Slik bruker vi key_pressed til å endre fargevariabelen til app:

def key_pressed(app, event):
    if event.key == 'r':
        app.color = 'red'
    elif event.key == 'g':
        app.color = 'green'
    elif event.key == 'b':
        app.color = 'blue'
    elif event.key == 'y':
        app.color = 'yellow'
    else:
        app.color = 'black'

Når du er ferdig skal du kunne endre fargene ved å trykke på tastaturet:

Illustrasjon av ferdig program
Teksteditor

I denne oppgaven skal vi sette opp en veldig enkel tekst-editor. Vi skal kunne trykke på tastaturet og se tegnene komme opp i rekkefølge, slik som skjer når man skriver noe inn i et tekstfelt. Vi skal også kunne slette tegn ved å trykke på backspace, og legge inn mellomrom. Bruk denne koden som utgangspunkt:

from uib_inf100_graphics.event_app import run_app

def app_started(app):
    ...

def key_pressed(app, event):
    ...

def redraw_all(app, canvas):
    # tegn teksten
    canvas.create_text(
        20, 20,
        anchor='nw',
        text='Hello world!',
        font='Arial 14',
    )

run_app(width=400, height=400)

Kjør programmet og se at teksten «Hello world!» vises i skjermvinduet.

Del A: Tekstvariabel

I stedet for at vi alltid tegner akkurat «Hello World!», trenger vi en variabel som holder på teksten vi skal tegne. Vi skal bruke denne variabelen i redraw_all for å tegne teksten.

  • I app_started: opprett og initier en variabel i app for teksten som skal tegnes.
  • I redraw_all: benytt overnevnte variabel når du angir hvilken tekst som skal tegnes.

I app_started lager vi en variabel for strengen som skal vises. Vi initierer den her til å være den tomme strengen, men det kan i prinsippet være en hvilken som helst streng:

def app_started(app):
    app.text = ''

Deretter endrer vi på redraw_all slik at den bruker variabelen vi opprettet i app i stedet for å bruke en bestemt tekst for å tegne teksten:

def redraw_all(app, canvas):
    canvas.create_text(
        20, 20,
        anchor='nw',
        text=app.text,
        font='Arial 14',
    )

Nå vil du få opp en tom skjerm når du kjører programmet. Hvis du initierte app.text til noe annet i app_started, skal det være den teksten som vises når programmet kjører.

Del B: Skrive inn symboler

Nå skal vi gjøre slik at vi kan skrive inn symboler ved å trykke på tastaturet. Vi skal bruke funksjonen key_pressed til dette. Til å begynne med gjør vi slik at tegnet som ble trykket legges til på slutten av teksten. Når du er ferdig burde tegnene komme opp en etter en ettersom du trykker på tastaturet.

  • I key_pressed: legg til tegnet som ble trykket (event.key) på slutten av teksten.
def key_pressed(app, event):
    app.text += event.key

Kjør programmet, og skriv inn noen bokstaver. Prøv å trykk på mellomrom, linjeskift og backspace i programmet når du er ferdig. Hva skjer?

Del C: Mellomrom, linjeskift og backspace

Nå skal vi fikse oppførselen til mellomrom, linjeskift og backspace. Når ‘Space’ tastes, skal det legges til et mellomrom i teksten. Når linjeskift tastes, legg til et linjeskift i teksten. Når ‘Backspace’ tastes, skal det siste tegnet i teksten slettes (med mindre teksten er den tomme strengen). Når du er ferdig burde mellomrom, linjeskift og backspace oppføre seg som forventet, og programmet skal ikke krasje om du trykker på backspace når det ikke er noe tekst.

  • Linjeskift-knappen har ulike navn på Windows og Mac – respektivt 'Enter' og 'Return'. For å lage et program som virker på begge plattformer, må du håndetere begge.
  • Backspace er også ulik på Windows og Mac – respektivt 'Backspace' og 'BackSpace'. For å lage et program som virker på begge plattformer, må du håndetere begge.
  • Benytt en if-setning for å sjekke om 'Space' ble trykket: hvis det skjedde, legg til et mellomrom (' ') i teksten.
  • Fortsett med flere elif-ledd på if-setningen:
    • Sjekk om det var 'Enter' eller 'Return' som trykket. Hvis det skjedde, legg til et linjeskift i teksten.
    • Sjekk om det var 'Backspace' eller 'BackSpace' som ble trykket. Hvis det skjedde, slett det siste tegnet i teksten.

For å slette det siste tegnet i teksten kan du bruke app.text = app.text[:-1]. Dette er en måte å skrive ut en del av en streng på som bruker beskjæring. app.text[:-1] betyr «alle tegn i strengen app.text bortsett fra det siste».

  • Avslutt if-setningen med et else -ledd: hvis ingen av de andre if-setningene var sanne, må det være et vanlig tegn som ble trykket. Legg til dette tegnet i teksten.

def key_pressed(app, event):
    if event.key == 'Space':
        app.text += ' '
    elif event.key in ['Enter', 'Return']:
        app.text += '\n'
    elif event.key.lower() == 'backspace':
        if len(app.text) > 0:
            app.text = app.text[:-1]
    else:
        app.text += event.key

Nå skal du kunne skrive inn tekst i programmet ditt (nesten) som vanlig. Eksempel:

Illustrasjon av ferdig program

Legg gjerne til mer funksjonalitet om du vil! For eksempel kan du slette all teksten om man trykker Escape, eller ignorere knapper som ikke er bokstaver eller tall (f. eks. piltaster).

Firkant som flytter seg

I denne oppgaven skal vi endre oppførselen til en bevegende firkant med piltastene. Vi skal også gjøre slik at vi kan skru på en debug-modus hvor vi kan flytte firkanten steg for steg. Ta utgangspunkt i denne koden:

from uib_inf100_graphics.event_app import run_app

def app_started(app):
    app.square_left = app.width//2
    app.square_top = app.height//2
    app.square_size = 25
    app.dx = -4
    app.timer_delay = 25 # millisekunder

def key_pressed(app, event):
    ...

def timer_fired(app):
    # Denne funksjonen kalles periodisk av selve uib_inf100_graphics
    # -rammeverket. Hvor ofte bestemmes av verdien i app.timer_delay.
    do_step(app)

def do_step(app):
    # Flytt horisontalt
    app.square_left += app.dx

    # Sjekk om firkanten har gått utenfor lerretet, og hvis ja, snu
    # retning; men flytt også firkanten til kanten (i stedet for å gå
    # forbi). Merk: det finnes andre, mer sofistikerte måter å håndtere
    # at rektangelet går forbi kanten...
    if app.square_left < 0:
        # snu retningen!
        app.square_left = 0
        app.dx = -app.dx
    elif app.square_left > app.width - app.square_size:
        app.square_left = app.width - app.square_size
        app.dx = -app.dx

def redraw_all(app, canvas):
    # tegn firkanten
    canvas.create_rectangle(
        app.square_left,
        app.square_top,
        app.square_left + app.square_size,
        app.square_top + app.square_size,
        fill="yellow",
    )


run_app(width=400, height=150)

Til å begynne med flytter firkanten seg automatisk fra side til side, og skifter bare retning når den treffer kanten. Legg merke til at firkantens retning bestemmes av fortegnet på app.dx. Når app.dx er positiv går firkanten mot høyre, og når den er negativ går den mot venstre.

Del A: Endre firkantens retning

For å få firkanten til å gå den retningen vi vil, må vi legge til noe i key_pressed som endrer variabelen app.dx. Vi må gjøre slik at firkanten går mot høyre når vi trykker på høyre piltast, og mot venstre når vi trykker på venstre piltast.

Her er tre alternativer til løsning. Hvilken synest du er best?:

def key_pressed(app, event):
    if event.key == "Left":
        app.dx = -4
    elif event.key == "Right":
        app.dx = 4
def key_pressed(app, event):
    if event.key == "Left" or event.key == "Right":
        app.dx = -app.dx
def key_pressed(app, event):
    if event.key in ("Left", "Right"):
        app.dx *= -1

Når du har gjort dette riktig, vil du kunne styre firkantens retning ved å trykke på piltastene:

Illustrasjon av ferdig del A

Del B: Debug-mode

Det som får firkanten til å flytte seg jevnlig, er at timer_fired kalles jevnlig av rammeverket i bakgrunnen, og da kalles do_step av timer_fired. Dette skjer med et intervall lik app.timer_delay. Når vi er i debug-mode, vil vi at firkanten skal flytte seg kun når vi trykker på ‘Space’.

  • I app_started setter vi opp en variabel app.debug_mode og setter den til å begynne med til False.
  • I key_pressed legger vi til en sjekk om ’d’ ble tastet. Da skrur vi debug-mode på eller av.
  • I key_pressed skal vi også sjekke om space ble tastet under debug-mode. Hvis det skjedde, kaller vi do_step.
  • I timer_fired legger vi til en sjekk om app.debug_mode er False. Hvis den er det, kaller vi do_step. Hvis ikke, gjør vi ingenting.

Du er ferdig når du kan skru debug-mode på og av, og du kan flytte firkanten steg for steg når du er i debug-mode.

Legg til dette i app_started:

    app.debug_mode = False

Legg til dette i key_pressed:

    elif event.key == "d":
        app.debug_mode = not app.debug_mode
    elif app.debug_mode and event.key == "Space":
        do_step(app)

Endre timer_fired slik:

def timer_fired(app):
    if not app.debug_mode:
        do_step(app)