Processing math: 100%

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

Час 14 - Догађаји миша и тастатуре

Као што је то обично случај у програмима са графичким корисничким интерфејсом и PyGame програми врше интеракцију са корисником коришћењем догађаја. Наиме, сваки пут када корисник помери миша, притисне неко дугме миша или тастер на тастатури систем то бележи у облику догађаја који се региструје у нашем програму и на шта можемо одреаговати извршавањем неког програмског кода.

Обрада догађаја у свим програмима које ћемо писати ће се заснивати на постојању функције obradi_dogadjaj чији ће задатак бити да анализира сваки догађај који се десио и да на основу тога промени стање програма (представљено најчешће преко глобалних променљивих). Информације о сваком догађају упаковане су у објекат dogadjaj који ће се аутоматски прослеђивати као аргумент ове функције.

Поред функције obradi_dogadjaj дефинисаћемо и функцију crtaj чији ће задатак бити да исцрта прозор на основу текућег стања програма. Пошто ћемо многе догађаје игнорисати (на пример, у неком програму ћемо реаговати само на померање миша, а не и на тастере тастатуре), неће бити потребе да се сцена поново исцртава након сваког догађаја. Зато ћемо усвојити конвенцију да функција obradi_dogadjaj враћа логичку вредност којом се назначава да ли је потребно поново исцртати сцену.

Постоји неколико начина да се у програму реализује реаговање на догађаје и о њима више можеш прочитати овде. Ми ћемо користити библиотеку PyGameBg у којој ће се догађаји обрађивати у главној петљи која се покреће функцијом pygamebg.event_loop чији су параметри функција за цртање crtaj и функција обраде догађаја obradi_dogadjaj.

Типови догађаја

Функција obradi_dogadjaj као аргумент прима догађај који се десио и потребно је прво да утврди његов тип (да ли је то био притисак неког тастера на тастатури, клик неког дугмета миша, померање миша и слично). Сваки догађај чува податак о типу догађаја коме можемо приступити помоћу поља type и стога ће се у функцији obradi_dogadjaj најчешће вршити гранање на основу вредности овог поља. У програмима заснованим на коришћењу библиотеке PyGameBg, обрада догађаја ће најчешће функционисати на следећи начин.

 
1
import pygame as pg, pygamebg
2
3
# otvaramo prozor
4
pygamebg.open_window(sirina, visina, "...")
5
6
# globalno stanje programa
7
...
8
9
# crtanje scene
10
def crtaj():
11
    ...   # na ovom mestu se vrši crtanje
12
13
# obrada dogadjaja
14
def obradi_dogadjaj(dogadjaj):
15
    global ...   # promenljive koje se mogu promeniti na osnovu nekog dogadjaja
16
    if dogadjaj.type = pg.???:   # ako je u pitanju događaj tipa ???
17
        ...
18
        return True     # treba ponovo iscrtati scenu
19
    elif dogadjaj.type = pg.???: # ako je u pitanju događaj tipa ???
20
        ...
21
        return True     # treba ponovo iscrtati scenu
22
    return False        # ne treba ponovo iscrtati scenu
23
24
# započinjemo petlju obrade događaja
25
pygamebg.event_loop(crtaj, obradi_dogadjaj)
26

(tipovi_dogadjaja_pygamebg)

Догађаји тастатуре

Сваки пут када корисник притисне и када отпусти неки тастер на тастатури региструје се догађај. За сваки клик тастером догоде се два посебна догађаја: pg.KEYDOWN и pg.KEYUP.

У склопу обраде догађаја притиска тастера, често нас занима да сазнамо који је тастер притиснут. Догађај типа pg.KEYDOWN податак о томе чува у пољу key, па ако је догађај сачуван у променљивој dogadjaj, анализом вредности dogadjaj.key можемо одредити који је тастер притиснут и реаговати на одговарајући начин. Ако је притиснута нека од стрелица, dogadjaj.key ће имати вредности pg.K_LEFT (стрелица на лево), pg.K_RIGHT (стрелица на десно), pg.K_UP (стрелица на горе) или pg.K_DOWN (стрелица на доле). Сличне константе постоје и за друге тастере (на пример, за слова можемо користити pg.K_a, pg.K_b, …, pg.K_z).

Бојење круга тастатуром

Напиши програм који ће цртати круг на центру екрана који ће бити обојен док је неки тастер притиснут.

  • Контролу да ли круг треба или не треба да буде обојен вршићемо помоћу глобалне логичке променљиве obojen.

  • У функцији crtaj ћемо цртати круг, чија ће дебљина зависити од вредности глобалне променљиве obojen. Подсетимо се, дебљина 0 означава да круг треба да буде испуњен.

  • Приликом сваког догађаја типа pg.KEYDOWN променљивој obojen ћемо додељивати вредност True, а приликом сваког догађаја типа pg.KEYUP вредност False.

27
 
1
import pygame as pg
2
import pygamebg
3
4
(sirina, visina) = (400, 400)    # otvaramo prozor
5
prozor = pygamebg.open_window(sirina, visina, "Бојење круга тастатуром")
6
7
obojen = False   # promenljiva koja određuje da li je krug obojen ili nije
8
9
# crtanje scene
10
def crtaj():
11
    debljina = 0 if obojen else 1
12
    prozor.fill(pg.Color("white"))
13
    pg.draw.circle(prozor, pg.Color("red"), (200, 200), 100, ???)
14
15
# obrada događaja
16
def obradi_dogadjaj(dogadjaj):
17
    global obojen
18
    if dogadjaj.type == pg.KEYDOWN:    # prisnut je taster
19
        ???                            # krug postaje obojen
20
        return True                    # treba ponovo nacrtati scenu
21
    elif dogadjaj.type == pg.KEYUP:    # otpušten je taster
22
        ???                            # krug postaje neobojen
23
        return True                    # treba ponovo nacrtati scenu
24
    return False                       # ne treba punovo crtati scenu
25
26
pygamebg.event_loop(crtaj, obradi_dogadjaj)
27

(bojenje_kruga_tastaturom)

Покушај да модификујеш претходни програм тако што ћеш док је тастер притиснут уместо црвеног круга цртати плави квадрат. Покушај да га модификујеш тако да реагује само на притисак и отпуштање тастера за размак.

Шетање лоптице тастатуром

Напиши програм у којем корисник шета лоптицу по екрану тастатуром.

  • Стање програма је одређено положајем лоптице. Помоћу две глобалне променљиве x и y памтићемо тренутни положај центра лоптице на екрану (иницијализоваћемо их тако да се лоптица налази у центру екрана). Променљива r садржаће полупречник лоптице, али се она неће мењати током рада програма.

  • У функцији crtaj бојићемо позадину прозора у бело (да би се обрисао његов претходни садржај) и исцртаваћемо плаву лоптицу на положају одређеном текућим стањем програма (вредностима променљивих x и y).

  • Догађаје ћемо обрађивати у посебној функцији obradi_dogadjaj. Приликом сваког притиска на тастер стрелице, координата x или координата y центра лоптице треба за мало да се промени (увећа или умањи, у зависности од тога која је стрелица притиснута). То колико ће се мало лоптица померити одређено је променљивама dx и dy (поставићемо их обе на 10 пиксела). Када притиснемо стрелицу на десно, тада увећавамо променљиву x за вредност dx. Слично, када притиснемо стрелицу на лево, тада умањујемо променљиву x за dx, када притиснемо стрелицу на горе тада умањујемо променљиву y за dy, а када притиснемо стрелицу на доле, тада увећавамо променљиву y за dy.

36
 
1
import pygame as pg
2
import pygamebg
3
4
(sirina, visina) = (400, 400)    # otvaramo prozor
5
prozor = pygamebg.open_window(sirina, visina, "Шетање лоптице тастатуром")
6
7
# podešavamo događaje tastaturom - prvi događaj se generise nakon
8
# 50ms, a svaki naredni nakon 25ms
9
pg.key.set_repeat(50, 25)
10
11
(x, y) = (sirina // 2, visina // 2)  # koordinate centra loptice (inicijalno je ona u centru prozora)
12
r = 40                               # poluprečnik loptice
13
(dx, dy) = (10, 10)                  # pomeraji po x i y koordinati
14
15
def crtanje():
16
    prozor.fill(pg.Color("white"))                       # bojimo prozor u belo
17
    pg.draw.circle(prozor, pg.Color("blue"), (x, y), r)  # crtamo lopticu
18
19
def obradi_dogadjaj(dogadjaj):
20
    global x, y
21
    if dogadjaj.type == pg.KEYDOWN:      # pritisak tastera na tastaturi
22
        # strelica na levo
23
        if dogadjaj.key == pg.K_LEFT:    # strelica na levo
24
            x -= dx                      # pomeramo lopticu na levo
25
            return True                  # treba ponovo nacrtati ekran
26
        # strelica na desno
27
        ???
28
        # strelica na gore
29
        ???
30
        # strelica na dole
31
        ???
32
        ???
33
    return False                         # ne treba ponovo nacrtati ekran
34
35
pygamebg.event_loop(crtaj, obradi_dogadjaj)
36

(setanje_loptice_tastaturom)

Решење са речником

Уместо гранања којим испитујемо која стрелица је притиснута, можемо направити речник који сваком тастеру придружује уређен пар који представља померај обе координате који се додаје на текуће координате центра лоптице када се тај тастер притисне.

5
 
1
pomeraj = {pg.K_LEFT: (-dx, 0),
2
           pg.K_RIGHT: (dx, 0),
3
           pg.K_DOWN: (0, dy),
4
           pg.K_UP: (0, -dy)}
5

(recnik_pomeraja)

Tада се реакција на притисак тастера може реализовати веома једноставно.

8
 
1
# pritisak tastera na tastaturi
2
if dogadjaj.type == pg.KEYDOWN:
3
    if dogadjaj.key in pomeraj:
4
        # pomeramo centar loptice za odgovarajući pomeraj
5
        (DX, DY) = pomeraj[dogadjaj.key]
6
        x += DX
7
        y += DY
8

(recnik_pomeraja_reakcija)

Шетање свемирског брода

Додатно, уместо цртања лоптице можемо да шетамо неку сличицу по екрану и тиме добијемо основу неке једноставне игрице. То, на пример, може бити следећи свемирски брод (слика се зове spaceship.png и не заборави да је ископираш ако покрећеш овај пример у локалу).

../_images/spaceship.png
40
 
1
import pygame as pg
2
import pygamebg
3
4
(sirina, visina) = (400, 400)    # otvaramo prozor
5
prozor = pygamebg.open_window(sirina, visina, "Шетање лика тастатуром")
6
7
# podešavamo događaje tastaturom - prvi događaj se generiše nakon
8
# 50ms, a svaki naredni nakon 25ms
9
pg.key.set_repeat(50, 25)
10
11
brod = pg.image.load('spaceship.png')  # učitavamo sliku svemirskog broda
12
brod_sirina = brod.get_width()         # očitavamo dimenzije slike
13
brod_visina = ???
14
15
(x, y) = (sirina / 2, visina / 2)   # koordinate centra broda (inicijalno u centru prozora)
16
(dx, dy) = (10, 10)                 # pomeraji po x i y koordinati
17
18
def crtanje():
19
    prozor.fill(pg.Color("black"))        # bojimo prozor u belo
20
    prozor.blit(brod, (x - ???, y - ???)) # crtamo brod tako da mu je centar u (x, y)
21
22
def obradi_dogadjaj(dogadjaj):
23
    global x, y
24
    # pomeraji koji odgovaraju strelicama
25
    pomeraj = {pg.K_LEFT: (-dx, 0),
26
               ???,
27
               ???,
28
               ???}
29
    if dogadjaj.type == pg.KEYDOWN:      # pritisak tastera na tastaturi
30
        if dogadjaj.key in pomeraj:
31
            # pomeramo centar broda za odgovarajući pomeraj
32
            (DX, DY) = pomeraj[dogadjaj.key]
33
            ???   # ažuriramo x koordinatu
34
            ???   # ažuriramo y koordinatu
35
            # pošto je brod pomeren, ponovo ćemo crtati scenu
36
            return True
37
    return False # ne treba ponovo crtati scenu
38
39
pygamebg.event_loop(crtaj, obradi_dogadjaj)
40

(setanje_lika_tastaturom)

Сударање са ивицама

Модификуј програм у којем се шетала лоптица тако да сваки пут када лоптица удари у ивицу екрана, она мења боју на насумичан начин.

  • Стање програма проширујемо глобалном променљивом boja која ће садржати боју лоптице (она ће бити постављена насумично, помоћу функције nasumicna_boja која је већ имплементирана у „сивом коду”).

  • Функцију crtaj ћемо модификовати тако да у обзир узме и вредност променљиве boja.

  • Након сваке промене положаја центра лоптице (што се дешава у склопу обраде догађаја), треба проверити да ли је она испала ван граница екрана и ако јесте, вратити је и променити јој боју. Притиском стрелица на десно лоптица која је била на екрану је могла испасти једино преко десне ивице екрана. Зато је приликом реакције на догађај притиска тог тастера довољно само проверити да ли је десни крај лоптице десно од десне ивице екрана тј. да ли је вредност x + r већа од ширине екрана тј. вредности променљиве sirina. Ако јесте, тада x можемо поставити на sirina - r (што је најдешњи положај лоптице у коме се она још налази на екрану) и променити јој насумично боју. Веома слично, приликом реаговања на догађај притиска стрелице на лево умањиваћемо x за dx, проверавати да ли је x - r постало негативно и ако јесте постављати x на r и лоптици мењати боју. Аналогно ћемо поступати и у случају друге две стрелице (једино што ћемо тада мењати y за dy).

45
 
1
import random
2
import pygame as pg
3
import pygamebg
4
5
(sirina, visina) = (400, 400)    # otvaramo prozor
6
prozor = pygamebg.open_window(sirina, visina, "Шетање лоптице тастатуром")
7
8
# podešavamo događaje tastaturom - prvi događaj se generiše nakon
9
# 50ms, a svaki naredni nakon 25ms
10
pg.key.set_repeat(50, 25)
11
12
# funkcija koja vraća nasumično odredjenu boju
13
def nasumicna_boja():
14
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
15
16
(x, y) = (sirina / 2, visina / 2)   # koordinate centra prozora
17
boja = nasumicna_boja()             # boja loptice se odredjuje nasumično
18
r = 40                              # poluprečnik loptice
19
(dx, dy) = (10, 10)                 # pomeraji po x i y koordinati
20
21
def crtaj():
22
    prozor.fill(pg.Color("white"))                        # bojimo prozor u belo
23
    pg.draw.circle(prozor, boja, (round(x), round(y)), r) # crtamo lopticu
24
25
def obradi_dogadjaj(dogadjaj):
26
    global x, y, boja
27
28
    if dogadjaj.type == pg.KEYDOWN:
29
        # strelica na levo
30
        if dogadjaj.key == pg.K_LEFT:
31
            x -= dx                      # pomeramo lopticu na levo
32
            if x - r < 0:                # ako je ispala van prozora
33
                x = r                    #    vraćamo je
34
                boja = nasumicna_boja()  #    menjamo joj boju
35
            return True                  # treba ponovo nacrtati scenu
36
        # strelica na desno
37
        ???
38
        # strelica na gore
39
        ???
40
        # strelica na dole
41
        ???
42
    return True                  # ne treba ponovo nacrtati scenu
43
44
pygamebg.event_loop(crtaj, obradi_dogadjaj)
45

(setanje_loptice_tastaturom_sudari)

Догађаји миша

Као и тастатура и миш има дугмад и за њих постоје догађаји притиска и догађаји отпуштања дугмета: pg.MOUSEBUTTONDOWN и pg.MOUSEBUTTONUP. Реаговање на њих је веома слично као што је реаговање на догађаје тастатуре pg.KEYDOWN и pg.KEYUP. Миш често има три дугмета (лево, средње и десно) и клик на било које од њих генерише поменута два догађаја. Ови догађаји садрже следећа поља.

  • Поље dogadjaj.button може да садржи број од 1 до 5 и означава које дугме миша је притиснуто (1 - лево, 2 - средње, 3 - десно, 4 - скрол на горе, 5 - скрол на доле).

  • Поље dogadjaj.pos садржи уређени пар координата тачке на којој је дугме притиснуто.

Поред догађаја притиска на дугме, приликом померања миша генерише се догађај pg.MOUSEMOTION. Заправо, током померања миша генерише се више оваквих догађаја (сваки од њих описује неко мало померање миша у неком веома кратком временском интервалу, тако да сваки такав догађај обично описује померање тек за неколико пиксела). Ови догађаји садрже следећа поља.

  • Позицију миша након померања можемо одредити помоћу dogadjaj.pos, које садржи уређени пар координата на којима се миш нашао након померања.

  • Поље dogadjaj.rel садржи уређени пар који описује колико се током тог једног померања миша позиција променила (тај пар представља разлику између крајње и почетне координате x и крајње и почетне y координате).

  • Поље dogadjaj.buttons садржи трочлану листу логичких вредности које за свако од три дугмета миша одређују да ли је било притиснуто током померања миша.

Дан и ноћ

Напиши програм који приказује небо и то ако је дан, плаво са жутим сунцем у горњем левом углу, а ако је ноћ, онда црно са 100 белих звездица насумично распоређених по њему. Када се клинке мишем било где на прозор, дан се мења у ноћ, а ноћ у дан.

  • Стање програма биће одређено логичком променљивом dan која ће имати вредност True ако и само ако је тренутно дан.

  • У функцији за цртање вршићемо гранање на основу вредности те променљиве и цртамо плаво небо са сунцем тј. црно небо са звездицама (њих цртамо у петљи и положај им одређујемо насумично).

  • У функцији обраде догађаја проверавамо да ли је у питању догађај притиска дугмета миша pg.MOUSEBUTTONDOWN и ако јесте, мењамо вредност логичке променљиве dan (гранањем, или још лакше помоћу оператора негације not).

30
 
1
import random
2
import pygame as pg
3
import pygamebg
4
5
(sirina, visina) = (200, 200) # otvaramo prozor
6
prozor = pygamebg.open_window(sirina, visina, "Дан и ноћ")
7
8
dan = True   # da li je dan ili noć
9
10
def crtaj():
11
    if dan:  # ako je dan
12
        prozor.fill(pg.Color("skyblue"))                          # plavo nebo
13
        pg.draw.circle(prozor, pg.Color("yellow"), (50, 50), 40)  # sunce
14
    else:    # ako je noć
15
        prozor.fill(pg.Color("black"))                            # crno nebo
16
        broj_zvezda = 100                                         # zvezde
17
        for i in range(???):
18
            x = random.randint(0, sirina)
19
            y = random.randint(???)
20
            pg.draw.circle(prozor, pg.Color("white"), ???, 2)
21
22
def obradi_dogadjaj(dogadjaj):
23
    global dan
24
    if dogadjaj.type == ???: # pritisak dugmeta miša
25
        dan = ???            #   ako je bio dan, sada više nije i obratno
26
        return True          #   treba ponovo crtati
27
    return ???               # ne treba ponovo crtati
28
29
pygamebg.event_loop(crtaj, obradi_dogadjaj)
30

(dan_noc)

Боја позадине мишем

Напиши програм који мења боју позадине екрана у зависности од положаја миша. Што се миш налази ближе десној ивици прозора, то је више црвене боје, а што је ближе доњој ивици прозора, то је више плаве боје. Зелена компонента је стално на нули.

  • Глобално стање програма биће одређено променљивама crvena и plava које имају вредности између 0 и 255 и одређују количину црвене тј. плаве светлости у тренутној боји позадине.

  • Функција crtaj ће бити веома једноставна - бојиће позадину прозора на основу вредности променљивих crvena и plava.

  • У овом задатку не реагујемо на клик миша, него на свако померање миша (догађај pg.MOUSEMOTION). Из позиције на којој се миш налази тј. поља dogadjaj.pos издвајамо координате x и y и на основу њих одређујемо боју тј. ажурирамо вредности глобалних променљивих crvena и plava. Нијансу цврене боје одређујемо коришћењем линеарне функције која ће бити таква да x координату нула пресликава у интензитет боје нула, а x координату једнаку ширини екрана пресликава у 255. Ту функцију је лако конструисати - координату x делимо са ширином екрана и множимо са 255 (наравно, заокружимо резултат на цео број). Потпуно аналогно, на основу y координате одређујемо нијансу плаве боје.

На основу претходне дискусије допуни наредни програм.

26
 
1
import random
2
import pygame as pg
3
import pygamebg
4
5
(visina, sirina) = (300, 300) # otvaramo prozor
6
prozor = pygamebg.open_window(visina, sirina, "Боја мишем")
7
8
crvena = 0  # količina crvene boje
9
plava = 0   # količina plave boje
10
11
def crtanje():
12
    prozor.fill((???, ???, ???))      # bojimo prozor
13
14
def obradi_dogadjaj(dogadjaj):
15
    global crvena, plava, treba_crtati
16
17
    if dogadjaj.type == ???:              # dogadjaj pomeranja miša
18
        (x, y) = dogadjaj.pos             #   koordinate na kojima se miš trenutno nalazi
19
        crvena = round(x / sirina * 255)  #   količina crvene boje zavisi od x
20
        plava  = ???                      #   količina plave boje zavisi od y
21
        return True                       #   treba crtati ponovo
22
23
    return False                          # ne treba crtati ponovo
24
25
pygamebg.event_loop(crtaj, obradi_dogadjaj)
26

(boja_misem)

Чекић

Направи програм у коме мишем помераш чекић по екрану. Чекић је у подигнутом положају, а када притисне дугме миша чекић се спусти. Можеш употребити слике CekicGore.png и CekicDole.png.

../_images/CekicGore.png ../_images/CekicDole.png
  • Стање програма биће одређено положајем центра чекића који ће бити одређен вредностима променљивих mis_x и mis_y. Слике ћемо учитати у уређени пар mis_slika (прво чекић горе, затим чекић доле), а слику коју тренутно треба приказати ћемо одређивати на основу вредности променљиве i_slika (њена вредност 0 ће указивати на то да треба нацртати чекић горе, а 1 да треба нацртати чекић доле).

  • У функцији crtaj бојићемо позадину екрана у светло-плаво (да би се обрисала претходна слика) и приказиваћемо одговарајућу слику (елемент пара mis_slika на позицији i_mis) тако да јој се центар налази на позицији (mis_x, mis_y) (подсетимо се, треба одредити положај горњег-левог угла слике и то се ради тако што се од центра слике одузме пола њене ширине тј. висине).

  • У функцији за обраду догађаја ћемо реаговати на притисак тастера миша (догађај pg.MOUSEBUTTONDOWN) и тада ћемо променљивој i_mis додељивати вредност 1, како би се приказивао спуштен чекић, на отпуштање тастера миша (догађај pg.MOUSEBUTTONUP) и тада ћемо променљивој i_mis додељивати вредност 0, како би се приказивао подигнут чекић и на померање миша (догађај pg.MOUSEMOTION) и тада ћемо променљиве mis_x и mis_y ажурирати на основу очитаног положаја миша (вредности dogadjaj.pos).

Покушај да на основу претходне дискусије самостално напишеш програм, а ако видиш да ти је помоћ потребна, затражи је.

9
 
1
import pygame as pg
2
import pygamebg
3
(sirina, visina) = (400, 400)
4
prozor = pygamebg.open_window(sirina, visina, "Слика као курсор миша")
5
6
7
8
pygamebg.event_loop(crtaj, obradi_dogadjaj)
9

(cekic)