Využitím klávesnice ve hrách a dalších multimediálních aplikacích používajících knihovnu Pyglet jsme se zabývali v předchozím článku, takže se dnes logicky přesuneme na popis způsobů využití myši (popř. touchpadu). Knihovna Pyglet programátorům nabízí poměrně široké možnosti, jak lze myš využít, ať již se to týká reakce na rotaci myším kolečkem, tak i možností definice vlastních kurzorů reprezentovaných (barevnými) rastrovými obrázky.
Obsah
1. Multimediální knihovna Pyglet: práce s myší a dalšími polohovacími zařízeními
2. Reakce na stisk tlačítka myš: on_mouse_pressi
3. Stisk versus puštění tlačítka myši: on_mouse_release
5. Reakce na pohyb myši: on_mouse_motion
6. Tažení kurzorem myši: on_mouse_drag
7. Využití informací o relativní změně kurzoru myši
8. Otočení kolečkem myši: on_mouse_scroll
9. Změna velikosti spritu pomocí kolečka myši
10. Standardní množina kurzorů
11. Vlastní kurzor myši načtený z rastrového obrázku
12. Vycentrování vlastního kurzoru
13. Kombinace vlastního kurzoru a standardních kurzorů
15. Repositář s demonstračními příklady
1. Multimediální knihovna Pyglet: práce s myší a dalšími polohovacími zařízeními
Připomeňme si, že v knihovně Pyglet existuje podpora pro několik typů vstupních zařízení. V první řadě se pochopitelně jedná o klávesnici a myš (popř. touchpad), což jsou dnes standardní zařízení dostupná na desktopech i noteboocích. Kromě toho jsou však podporována i další zařízení, zejména joysticky, různé typy pedálů, volanty, dotykové obrazovky atd. Pro přístup k těmto zařízením se používají buď specializované callback funkce nebo je nutné použít modul pyglet.input. Dnes navážeme na předchozí část tohoto seriálu, v níž jsme se zabývali popisem detailů práce s klávesnicí. Ukážeme si totiž, jakými způsoby je možné využít myš, reagovat na stisk tlačítek, pohyb myši, tažení myší, otočení kolečkem atd. Taktéž si ukážeme způsob změny kurzoru myši včetně možnosti použití vlastních kurzorů načtených z externích rastrových obrázků (podobně jako sprity).
Při práci s myší lze použít následující callback funkce:
Callback funkce | Reaguje na událost | Kapitola |
---|---|---|
on_mouse_press | stisk tlačítka myši | 2 |
on_mouse_release | puštění tlačítka myši | 3 |
on_mouse_motion | pohyb kurzoru | 5 |
on_mouse_drag | tažení (pohyb kurzoru se stisknutým tlačítkem) | 6 |
on_mouse_scroll | otočení kolečkem (nebo kolečky!) myši | 8 |
Callback funkce se vždy registrují k určitému oknu, což je ostatně podobný princip, s jakým jsme se již setkali při práci s klávesnicí.
2. Reakce na stisk tlačítka myši: on_mouse_press
Základní událostí při práci s myší je událost, která vznikne ve chvíli, kdy je stlačeno její libovolné tlačítko (nebo kolečko, které je většinou považováno za prostřední tlačítko). V případě, že zpracováváme události pro okno nazvané window, bude callback funkce reagující na stisk tlačítka vypadat následovně:
@window.event
def <strong>on_mouse_press</strong>(x, y, button, modifiers):
pass
Při stisku tlačítka se současně zaznamená i pozice kurzoru myši a případné modifikátory (jako u klávesnice). Pokud současně stisknete více tlačítek, zavolá se callback funkce několikrát, tj. v parametru button bude vždy jen jediná konstanta, nikoli kombinace. Knihovna Pyglet rozlišuje minimálně tři tlačítka – levé, pravé a prostřední (což je většinou kolečko). Pokud vlastníte myš s více tlačítky (herní myši), musíte si otestovat, jak se budou tato tlačítka chovat v praxi. Pokud potřebujeme rozlišit stisk jednoho ze tří základních tlačítek, můžeme postupovat takto:
button_names = {pyglet.window.mouse.LEFT: "left",
pyglet.window.mouse.RIGHT: "right",
pyglet.window.mouse.MIDDLE: "middle"}
@window.event
def <strong>on_mouse_press</strong>(x, y, button, modifiers):
button_name = button_names.get(button, "unknown")
text = format("Mouse press: %s button at [%d, %d]" % (button_name, x, y))
label.text = text
print(text)
on_draw()
Povšimněte si, že stisknuté tlačítko je reprezentováno jednou z konstant:
pyglet.window.mouse.LEFT |
pyglet.window.mouse.RIGHT |
pyglet.window.mouse.MIDDLE |
Obrázek 1: Reakce na stisk levého tlačítka myši.
V následujícím demonstračním příkladu se při stisku tlačítka myši zobrazí jeho jméno na obrazovce aplikace a současně se toto jméno vypíše na konzoli. Navíc se vytiskne i aktuální souřadnice kurzoru myši:
#!/usr/bin/env python
import pyglet
GRAY = (128, 128, 128, 255)
RED = (255, 128, 128, 255)
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def create_gray_label(text, x, y, anchor_x, anchor_y):
return pyglet.text.Label(text,
font_size=18,
x=x,
y=y,
anchor_x=anchor_x,
anchor_y=anchor_y,
color=GRAY)
window = create_window(640, 480)
label = create_gray_label('Mouse press:', 10, 10, 'left', 'bottom')
@window.event
def on_draw():
window.clear()
label.draw()
button_names = {pyglet.window.mouse.LEFT: "left",
pyglet.window.mouse.RIGHT: "right",
pyglet.window.mouse.MIDDLE: "middle"}
@window.event
def on_mouse_press(x, y, button, modifiers):
button_name = button_names.get(button, "unknown")
text = format("Mouse press: %s button at [%d, %d]" % (button_name, x, y))
label.text = text
print(text)
on_draw()
pyglet.app.run()
Obrázek 2: Reakce na stisk pravého tlačítka myši.
3. Stisk versus puštění tlačítka myši: on_mouse_release
Opakem callback funkce zavolané při stisku tlačítka myši je pochopitelně callback funkce reagující na puštění tohoto tlačítka. Obě funkce mají naprosto stejné parametry. Jen pro porovnání:
@window.event
def <strong>on_mouse_press</strong>(x, y, button, modifiers):
pass
@window.event
def <strong>on_mouse_release</strong>(x, y, button, modifiers):
pass
Obrázek 3: Reakce na stisk prostředního tlačítka myši.
Předchozí demonstrační příklad si tedy můžeme nepatrně upravit takovým způsobem, aby vypisoval informace jak o stisku tlačítka myši, tak i o jeho puštění. Vše zajistí jedna univerzální funkce volaná z obou předchozích callback funkcí:
def <strong>on_mouse_action</strong>(x, y, button, action):
button_name = button_names.get(button, "unknown")
text = format("Mouse %s %s button at [%d, %d]" %
(action, button_name, x, y))
label.text = text
print(text)
on_draw()
@window.event
def <strong>on_mouse_press</strong>(x, y, button, modifiers):
on_mouse_action(x, y, button, "pressed")
@window.event
def <strong>on_mouse_release</strong>(x, y, button, modifiers):
on_mouse_action(x, y, button, "released")
Obrázek 4: Reakce na puštění prostředního tlačítka myši.
Úplný zdrojový text tohoto příkladu:
#!/usr/bin/env python
import pyglet
GRAY = (128, 128, 128, 255)
RED = (255, 128, 128, 255)
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def create_gray_label(text, x, y, anchor_x, anchor_y):
return pyglet.text.Label(text,
font_size=18,
x=x,
y=y,
anchor_x=anchor_x,
anchor_y=anchor_y,
color=GRAY)
window = create_window(640, 480)
label = create_gray_label('Mouse:', 10, 10, 'left', 'bottom')
@window.event
def on_draw():
window.clear()
label.draw()
button_names = {pyglet.window.mouse.LEFT: "left",
pyglet.window.mouse.RIGHT: "right",
pyglet.window.mouse.MIDDLE: "middle"}
def on_mouse_action(x, y, button, action):
button_name = button_names.get(button, "unknown")
text = format("Mouse %s %s button at [%d, %d]" %
(action, button_name, x, y))
label.text = text
print(text)
on_draw()
@window.event
def on_mouse_press(x, y, button, modifiers):
on_mouse_action(x, y, button, "pressed")
@window.event
def on_mouse_release(x, y, button, modifiers):
on_mouse_action(x, y, button, "released")
pyglet.app.run()
4. Sprite ovládaný myší
V dalším demonstračním příkladu budeme s využitím myši po obrazovce přesunovat rastrový obrázek neboli sprite. Prozatím využijeme reakci na stisk tlačítka myši (tj. pokud nebude tlačítko zmáčknuto, bude sprite nehybný). V příslušné callback funkci se změní pozice spritu ve scéně:
@window.event
def <strong>on_mouse_press</strong>(x, y, button, modifiers):
sprite.x = x
sprite.y = y
on_mouse_action(x, y, button, "pressed")
Navíc musíme zajistit vycentrování spritu na aktivní bod kurzoru. To se ve skutečnosti provede jednoduše přesunem „kotvy“ svázané se spritem:
# stred spritu bude odpovidat stredu obrazku - sprite se nam bude
# mnohem lepe pozicovat
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
sprite = pyglet.sprite.Sprite(image)
# vycentrovani spritu
sprite.x = window.width / 2 - image.width / 2
sprite.y = window.height / 2 - image.height / 2
Obrázek 5: Přesun spritu pomocí myši.
Úplný zdrojový text tohoto příkladu vypadá následovně:
sprite = make_sprite("gnome-globe.png", window)
@window.event
def on_draw():
window.clear()
label.draw()
sprite.draw()
#!/usr/bin/env python
import pyglet
GRAY = (128, 128, 128, 255)
RED = (255, 128, 128, 255)
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def make_sprite(filename, window):
image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
# stred spritu bude odpovidat stredu obrazku - sprite se nam bude
# mnohem lepe pozicovat
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
sprite = pyglet.sprite.Sprite(image)
# vycentrovani spritu
sprite.x = window.width / 2 - image.width / 2
sprite.y = window.height / 2 - image.height / 2
sprite.step = 5
return sprite
def create_gray_label(text, x, y, anchor_x, anchor_y):
return pyglet.text.Label(text,
font_size=18,
x=x,
y=y,
anchor_x=anchor_x,
anchor_y=anchor_y,
color=GRAY)
window = create_window(640, 480)
label = create_gray_label('Mouse:', 10, 10, 'left', 'bottom')
sprite = make_sprite("gnome-globe.png", window)
@window.event
def on_draw():
window.clear()
label.draw()
sprite.draw()
button_names = {pyglet.window.mouse.LEFT: "left",
pyglet.window.mouse.RIGHT: "right",
pyglet.window.mouse.MIDDLE: "middle"}
def on_mouse_action(x, y, button, action):
button_name = button_names.get(button, "unknown")
text = format("Mouse %s %s button at [%d, %d]" %
(action, button_name, x, y))
label.text = text
print(text)
on_draw()
@window.event
def on_mouse_press(x, y, button, modifiers):
sprite.x = x
sprite.y = y
on_mouse_action(x, y, button, "pressed")
@window.event
def on_mouse_release(x, y, button, modifiers):
on_mouse_action(x, y, button, "released")
pyglet.app.run()
5. Reakce na pohyb myši: on_mouse_motion
Další callback funkcí, kterou můžeme v aplikacích využít, je funkce on_mouse_motion. Jak již název této funkce naznačuje, bude volána ve chvíli, kdy se pohybuje kurzor myši. Funkci se přitom předává jak absolutní pozice kurzoru v okně, tak i pozice relativní, tj. jak daleko se kurzor posunul od posledního volání této funkce. Relativní souřadnice jsou přitom v mnoha aplikacích lépe použitelné. Pokud tedy budeme chtít, aby sprite sledoval kurzor myši (aniž by bylo stlačeno nějaké tlačítko myši), může pomocný kód vypadat velmi jednoduše:
@window.event
def <strong>on_mouse_motion</strong>(x, y, dx, dy):
sprite.x = x
sprite.y = y
Opět si ukažme úplný zdrojový text příkladu, v němž je callback funkce on_mouse_motion použita:
#!/usr/bin/env python
import pyglet
GRAY = (128, 128, 128, 255)
RED = (255, 128, 128, 255)
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def make_sprite(filename, window):
image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
# stred spritu bude odpovidat stredu obrazku - sprite se nam bude
# mnohem lepe pozicovat
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
sprite = pyglet.sprite.Sprite(image)
# vycentrovani spritu
sprite.x = window.width / 2 - image.width / 2
sprite.y = window.height / 2 - image.height / 2
sprite.step = 5
return sprite
window = create_window(640, 480)
sprite = make_sprite("gnome-globe.png", window)
@window.event
def on_draw():
window.clear()
sprite.draw()
@window.event
def on_mouse_motion(x, y, dx, dy):
sprite.x = x
sprite.y = y
pyglet.app.run()
6. Tažení kurzorem myši: on_mouse_drag
Předchozí callback funkci pojmenované on_mouse_motion se podobá další užitečná funkce nazvaná on_mouse_drag. Tato funkce je zavolána ve chvíli, kdy uživatel pohybuje kurzorem myši a současně je stisknuto některé její tlačítko (popř. i další modifikátory). Tomuto chování odpovídají i parametry callback funkce, protože obsahují absolutní i relativní souřadnice kurzoru myši (viz on_mouse_motion) a taktéž informace o stisknutých tlačítkách (viz on_mouse_press a taktéž on_mouse_release):
def <strong>on_mouse_drag</strong>(x, y, dx, dy, buttons, modifiers):
pass
Pokud budeme chtít realizovat pohyb spritu metodou drag and drop, bude to díky výše popsané callback funkci ve skutečnosti velmi snadné až triviální:
@window.event
def <strong>on_mouse_drag</strong>(x, y, dx, dy, buttons, modifiers):
if buttons & pyglet.window.mouse.LEFT:
sprite.x = x
sprite.y = y
Takto implementovanou změnu pozice spritu použijeme v dalším demonstračním příkladu:
#!/usr/bin/env python
import pyglet
GRAY = (128, 128, 128, 255)
RED = (255, 128, 128, 255)
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def make_sprite(filename, window):
image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
# stred spritu bude odpovidat stredu obrazku - sprite se nam bude
# mnohem lepe pozicovat
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
sprite = pyglet.sprite.Sprite(image)
# vycentrovani spritu
sprite.x = window.width / 2 - image.width / 2
sprite.y = window.height / 2 - image.height / 2
sprite.step = 5
return sprite
window = create_window(640, 480)
sprite = make_sprite("gnome-globe.png", window)
@window.event
def on_draw():
window.clear()
sprite.draw()
@window.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
if buttons & pyglet.window.mouse.LEFT:
sprite.x = x
sprite.y = y
pyglet.app.run()
7. Využití informací o relativní změně kurzoru myši
Pokud jste si spustili předchozí příklad, asi jste zjistili, že ve chvíli, kdy je stisknuto tlačítko myši, „poskočí“ sprite přesně na to místo, kde se kurzor nachází. Je tomu tak z toho důvodu, že při stisku tlačítka myši se poprvé zavolá funkce on_mouse_drag, která nastaví pozici spritu nezávisle na jeho předchozí pozici v okně aplikace:
@window.event
def <strong>on_mouse_drag</strong>(x, y, dx, dy, buttons, modifiers):
if buttons & pyglet.window.mouse.LEFT:
sprite.x = x
sprite.y = y
Toto chování je možné relativně snadno zkorigovat a to konkrétně použitím parametrů dx a dy, které obsahují relativní posun kurzoru od posledního volání této funkce. Relativní souřadnice samozřejmě musíme přičíst k aktuálním souřadnicím spritu (pro jednoduchost zde používám operátor +=):
@window.event
def <strong>on_mouse_drag</strong>(x, y, dx, dy, buttons, modifiers):
if buttons & pyglet.window.mouse.LEFT:
sprite.x <strong>+=</strong> dx
sprite.y <strong>+=</strong> dy
Takto vylepšenou změnu pozice spritu použijeme v dalším demonstračním příkladu:
#!/usr/bin/env python
import pyglet
GRAY = (128, 128, 128, 255)
RED = (255, 128, 128, 255)
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def make_sprite(filename, window):
image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
# stred spritu bude odpovidat stredu obrazku - sprite se nam bude
# mnohem lepe pozicovat
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
sprite = pyglet.sprite.Sprite(image)
# vycentrovani spritu
sprite.x = window.width / 2 - image.width / 2
sprite.y = window.height / 2 - image.height / 2
sprite.step = 5
return sprite
window = create_window(640, 480)
sprite = make_sprite("gnome-globe.png", window)
@window.event
def on_draw():
window.clear()
sprite.draw()
@window.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
if buttons & pyglet.window.mouse.LEFT:
sprite.x += dx
sprite.y += dy
pyglet.app.run()
8. Otočení kolečkem myši: on_mouse_scroll
V mnoha aplikacích můžeme využít i kolečko myši. Ve chvíli, kdy uživatel kolečkem otočí, zavolá se callback funkce pojmenovaná on_mouse_scroll, které se předá jak aktuální souřadnice kurzoru myši, tak i relativní hodnota otočení. Ve skutečnosti tato callback funkce podporuje dvě relativní hodnoty – x-ovou a y-ovou. Záleží na konkrétním provedení myši, jaké informace (zda vůbec nějaké) se přenesou v parametru scroll_x; typicky je tato hodnota využitelná u myší, které namísto standardního kolečka obsahují buď malý touchpad nebo tzv. scroll ball (malý trackball). Naproti tomu náklon kolečka se většinou považuje za stisk čtvrtého resp. pátého tlačítka myši:
@window.event
def <strong>on_mouse_scroll</strong>(x, y, scroll_x, scroll_y):
pass
9. Změna velikosti spritu pomocí kolečka myši
Kolečkem myši můžeme měnit například velikost spritu. Callback funkce on_mouse_scroll může vypadat následovně (konstanta 4.0 byla zvolena na základě praktických zkušeností):
@window.event
def <strong>on_mouse_scroll</strong>(x, y, scroll_x, scroll_y):
sprite.scale += float(scroll_y/4.0)
Obrázek 6: Změna velikosti spritu s využitím kolečka myši.
Ve chvíli, kdy uživatel otočí kolečkem, zavolá se callback funkce a v parametru scroll_y bude uloženo kladné či záporné číslo, podle toho, na kterou stranu bylo kolečkem otočeno. Tato hodnota se po mírné úpravě přičte či odečte od aktuálního měřítka (velikosti spritu). Povšimněte si, že pokud měřítka dosáhne záporné hodnoty, bude sprite středově zrcadlen:
Obrázek 7: Zrcadlení spritu s využitím kolečka myši.
Následuje výpis zdrojového kódu tohoto příkladu:
#!/usr/bin/env python
import pyglet
GRAY = (128, 128, 128, 255)
RED = (255, 128, 128, 255)
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def make_sprite(filename, window):
image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
# stred spritu bude odpovidat stredu obrazku - sprite se nam bude
# mnohem lepe pozicovat
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
sprite = pyglet.sprite.Sprite(image)
# vycentrovani spritu
sprite.x = window.width / 2 - image.width / 2
sprite.y = window.height / 2 - image.height / 2
sprite.step = 5
return sprite
window = create_window(640, 480)
sprite = make_sprite("gnome-globe.png", window)
@window.event
def on_draw():
window.clear()
sprite.draw()
@window.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
if buttons & pyglet.window.mouse.LEFT:
sprite.x += dx
sprite.y += dy
@window.event
def on_mouse_scroll(x, y, scroll_x, scroll_y):
sprite.scale += float(scroll_y/4.0)
pyglet.app.run()
Obrázek 8: Zvětšení a současně i posun spritu.
10. Standardní množina kurzorů
V knihovně Pyglet je možné namísto standardního kurzoru ve tvaru šipky směřující doleva nahoru použít i kurzory dalších tvarů. Libovolný standardní (systémový) kurzor se vybere příkazem:
cursor = window.get_system_mouse_cursor(cursor_type)
Následně se tento kurzor nastaví pomocí:
window.set_mouse_cursor(cursor)
Konstanty pro standardní (systémové) kurzory jsou specifikovány ve třídě pyglet.window.Window.. Jak je z následujícího výpisu patrné, je možné namísto konstant použít přímo řetězce obsahující jména kurzorů:
CURSOR_CROSSHAIR = 'crosshair'
CURSOR_DEFAULT = None
CURSOR_HAND = 'hand'
CURSOR_HELP = 'help'
CURSOR_NO = 'no'
CURSOR_SIZE = 'size'
CURSOR_SIZE_DOWN = 'size_down'
CURSOR_SIZE_DOWN_LEFT = 'size_down_left'
CURSOR_SIZE_DOWN_RIGHT = 'size_down_right'
CURSOR_SIZE_LEFT = 'size_left'
CURSOR_SIZE_LEFT_RIGHT = 'size_left_right'
CURSOR_SIZE_RIGHT = 'size_right'
CURSOR_SIZE_UP = 'size_up'
CURSOR_SIZE_UP_DOWN = 'size_up_down'
CURSOR_SIZE_UP_LEFT = 'size_up_left'
CURSOR_SIZE_UP_RIGHT = 'size_up_right'
CURSOR_TEXT = 'text'
CURSOR_WAIT = 'wait'
CURSOR_WAIT_ARROW = 'wait_arrow'
V dalším příkladu si připravíme slovník s mapováním kláves F1 až F10 na konstanty se jmény standardních (systémových) kurzorů:
cursors = {
pyglet.window.key.F1: pyglet.window.Window.CURSOR_DEFAULT,
pyglet.window.key.F2: pyglet.window.Window.CURSOR_HAND,
pyglet.window.key.F3: pyglet.window.Window.CURSOR_HELP,
pyglet.window.key.F4: pyglet.window.Window.CURSOR_SIZE,
pyglet.window.key.F5: pyglet.window.Window.CURSOR_SIZE_UP,
pyglet.window.key.F6: pyglet.window.Window.CURSOR_SIZE_DOWN,
pyglet.window.key.F7: pyglet.window.Window.CURSOR_SIZE_LEFT,
pyglet.window.key.F8: pyglet.window.Window.CURSOR_SIZE_RIGHT,
pyglet.window.key.F9: pyglet.window.Window.CURSOR_WAIT,
pyglet.window.key.F10: pyglet.window.Window.CURSOR_NO
}
Následně po stisku klávesy F1 až F10 provedeme přepnutí kurzoru, což je ve skutečnosti velmi snadné:
@window.event
def on_key_press(symbol, modifiers):
cursor_type = cursors.get(symbol)
cursor = window.get_system_mouse_cursor(cursor_type)
window.set_mouse_cursor(cursor)
Úplný zdrojový kód příkladu pro přepínání kurzorů myši vypadá takto:
#!/usr/bin/env python
import pyglet
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
window = create_window(640, 480)
@window.event
def on_draw():
window.clear()
cursors = {
pyglet.window.key.F1: pyglet.window.Window.CURSOR_DEFAULT,
pyglet.window.key.F2: pyglet.window.Window.CURSOR_HAND,
pyglet.window.key.F3: pyglet.window.Window.CURSOR_HELP,
pyglet.window.key.F4: pyglet.window.Window.CURSOR_SIZE,
pyglet.window.key.F5: pyglet.window.Window.CURSOR_SIZE_UP,
pyglet.window.key.F6: pyglet.window.Window.CURSOR_SIZE_DOWN,
pyglet.window.key.F7: pyglet.window.Window.CURSOR_SIZE_LEFT,
pyglet.window.key.F8: pyglet.window.Window.CURSOR_SIZE_RIGHT,
pyglet.window.key.F9: pyglet.window.Window.CURSOR_WAIT,
pyglet.window.key.F10: pyglet.window.Window.CURSOR_NO
}
@window.event
def on_key_press(symbol, modifiers):
cursor_type = cursors.get(symbol)
cursor = window.get_system_mouse_cursor(cursor_type)
window.set_mouse_cursor(cursor)
pyglet.app.run()
11. Vlastní kurzor myši načtený z rastrového obrázku
Zavoláním metody:
window.<strong>set_mouse_cursor</strong>(cursor)
Je možné namísto předpřipravených kurzorů použít libovolný rastrový obrázek načtený z externího souboru. Pro načtení obrázku a jeho konverzi do kurzoru si můžeme připravit pomocnou funkci:
def load_cursor(filename):
image = pyglet.image.load(filename)
return pyglet.window.ImageMouseCursor(image)
Pokud tedy budeme chtít namísto standardního kurzoru použít náš testovací glóbus, stačí nám na to pouhé dva řádky:
cursor = load_cursor("gnome-globe.png")
window.set_mouse_cursor(cursor)
V dalším příkladu tedy nemusíme načítat sprity ani reagovat na pohyb kurzoru; vše se totiž provede automaticky:
#!/usr/bin/env python
import pyglet
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def load_cursor(filename):
image = pyglet.image.load(filename)
return pyglet.window.ImageMouseCursor(image)
window = create_window(640, 480)
cursor = load_cursor("gnome-globe.png")
window.set_mouse_cursor(cursor)
@window.event
def on_draw():
window.clear()
pyglet.app.run()
12. Vycentrování vlastního kurzoru
V předchozím demonstračním příkladu jsme pro načtení kurzoru použili uživatelskou funkci, která načetla rastrový obrázek a vytvořila z něho kurzor zavoláním pyglet.window.ImageMouseCursor:
def load_cursor(filename):
image = pyglet.image.load(filename)
return pyglet.window.ImageMouseCursor(image)
Tímto způsobem načtený kurzor má jednu nepříjemnou vlastnost – aktivní bod kurzoru je umístěn v levém horním rohu obrázku a nikoli v jeho středu. Tato vlastnost způsobí problémy (i když nijak velké) ve chvíli, kdy je kurzor umístěn na okraji okna aplikace. Úprava chování je ovšem ve skutečnosti velmi jednoduchá, protože funkce pyglet.window.ImageMouseCursor akceptuje další dva nepovinné parametry, přes něž se předává pozice aktivního bodu kurzoru. Pokud do těchto parametrů zadáme souřadnice [image.width/2, image.height/2], bude bod přesunut přesně doprostřed obrázku:
def load_cursor(filename):
image = pyglet.image.load(filename)
return pyglet.window.ImageMouseCursor(image, image.width/2, image.height/2)
Úplný zdrojový kód upraveného demonstračního příkladu je zobrazen pod tímto odstavcem:
#!/usr/bin/env python
import pyglet
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def load_cursor(filename):
image = pyglet.image.load(filename)
return pyglet.window.ImageMouseCursor(image, image.width/2, image.height/2)
window = create_window(640, 480)
cursor = load_cursor("gnome-globe.png")
window.set_mouse_cursor(cursor)
@window.event
def on_draw():
window.clear()
pyglet.app.run()
13. Kombinace vlastního kurzoru a standardních kurzorů
V aplikaci se samozřejmě můžeme přepínat mezi standardními kurzory a kurzorem vlastním (nebo větším množstvím vlastních kurzorů). To je ukázáno v dnešním předposledním demonstračním příkladu, v němž je možné klávesami F1 až F10 vybírat standardní kurzory a mezerníkem pak kurzor vlastní (globus):
Klávesa | Kurzor |
---|---|
F1 | výchozí šipka směřující vlevo nahoru |
F2 | kurzor s ikonou ruky |
F3 | kurzor s ikonou nápovědy |
F4 | kurzor používaný při změně velikosti objektů |
F5 | šipka nahoru |
F6 | šipka dolů |
F7 | šipka doleva |
F8 | šipka doprava |
F9 | čekání na dokončení operace (závisí na systému) |
F10 | většinou zobrazeno jako × |
mezerník | vlastní kurzor (globus) |
Následuje výpis zdrojového kódu tohoto příkladu:
#!/usr/bin/env python
import pyglet
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def load_cursor(filename):
image = pyglet.image.load(filename)
return pyglet.window.ImageMouseCursor(image, image.width/2, image.height/2)
window = create_window(640, 480)
custom_cursor = load_cursor("gnome-globe.png")
@window.event
def on_draw():
window.clear()
cursors = {
pyglet.window.key.F1: pyglet.window.Window.CURSOR_DEFAULT,
pyglet.window.key.F2: pyglet.window.Window.CURSOR_HAND,
pyglet.window.key.F3: pyglet.window.Window.CURSOR_HELP,
pyglet.window.key.F4: pyglet.window.Window.CURSOR_SIZE,
pyglet.window.key.F5: pyglet.window.Window.CURSOR_SIZE_UP,
pyglet.window.key.F6: pyglet.window.Window.CURSOR_SIZE_DOWN,
pyglet.window.key.F7: pyglet.window.Window.CURSOR_SIZE_LEFT,
pyglet.window.key.F8: pyglet.window.Window.CURSOR_SIZE_RIGHT,
pyglet.window.key.F9: pyglet.window.Window.CURSOR_WAIT,
pyglet.window.key.F10: pyglet.window.Window.CURSOR_NO
}
@window.event
def on_key_press(symbol, modifiers):
if symbol == pyglet.window.key.SPACE:
cursor = custom_cursor
else:
cursor_type = cursors.get(symbol)
cursor = window.get_system_mouse_cursor(cursor_type)
window.set_mouse_cursor(cursor)
pyglet.app.run()
14. Exkluzivní režim
Poslední důležitou vlastností knihovny Pyglet při práci s myší je schopnost získat takzvaný exkluzivní přístup ke kurzoru myši. To znamená, že kurzor nebude vyjíždět z okna aktivní aplikace (resp. přesněji řečeno vyjíždět bude, ale nebude viditelný, ostatně implicitně nebude viditelný ani v aktivní aplikaci). Toto chování využijeme zejména ve chvíli, kdy se používají callback funkce on_mouse_motion a on_mouse_drag. Exkluzivní režim se povoluje jednoduše, ideálně ihned po vytvoření okna:
window = create_window(640, 480)
window.<strong>set_exclusive_mouse(True)</strong>
Exkluzivní přístup použijeme v dnešním posledním demonstračním příkladu, v němž se pomocí myši posouvá spritem (malým rastrovým obrázkem) po ploše okna aplikace:
#!/usr/bin/env python
import pyglet
def create_window(width, height):
return pyglet.window.Window(width=width,
height=height,
caption="Pyglet library")
def make_sprite(filename, window):
image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
# stred spritu bude odpovidat stredu obrazku - sprite se nam bude
# mnohem lepe pozicovat
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
sprite = pyglet.sprite.Sprite(image)
# vycentrovani spritu
sprite.x = window.width / 2 - image.width / 2
sprite.y = window.height / 2 - image.height / 2
sprite.step = 5
return sprite
window = create_window(640, 480)
window.set_exclusive_mouse(True)
sprite = make_sprite("gnome-globe.png", window)
@window.event
def on_draw():
window.clear()
sprite.draw()
@window.event
def on_mouse_motion(x, y, dx, dy):
sprite.x += dx
sprite.y += dy
pyglet.app.run()
15. Repositář s demonstračními příklady
Všech dvanáct dnes popsaných demonstračních příkladů bylo uloženo do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář. Pro jejich spuštění je samozřejmě nutné mít nainstalovanou jak knihovnu Pyglet, tak i podpůrné grafické knihovny OpenGL a GLU (což se většinou provede automaticky v rámci instalace balíčku s Pygletem, viz též úvodní díl tohoto seriálu):
16. Odkazy na Internetu
- Working with the mouse
http://pyglet.readthedocs.io/en/latest/programming_guide/mouse.html - Working with the keyboard
http://pyglet.readthedocs.io/en/latest/programming_guide/keyboard.html - pyglet.input
https://pyglet.readthedocs.io/en/latest/modules/input.html - Class pyglet.graphics.vertexdomain.VertexList
https://pythonhosted.org/pyglet/api/pyglet.graphics.vertexdomain.VertexList-class.html - Class pyglet.graphics.vertexdomain.VertexDomain
https://pythonhosted.org/pyglet/api/pyglet.graphics.vertexdomain.VertexDomain-class.html - Pyglet: Module Hierarchy
https://pythonhosted.org/pyglet/api/module-tree.html - Learning Modern OpenGL
https://www.codeproject.com/articles/771225/learning-modern-opengl - OpenGL Utility Library
https://en.wikipedia.org/wiki/OpenGL_Utility_Library - GLU Specification
https://www.opengl.org/registry/doc/glu1.3.pdf - The Perlin noise math FAQ
https://mzucker.github.io/html/perlin-noise-math-faq.html - Perlin noise
https://en.wikipedia.org/wiki/Perlin_noise - Perlin Noise Generator (Python recipe)
http://code.activestate.com/recipes/578470-perlin-noise-generator/ - Simplex noise demystified
http://www.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf - glTexEnv - příkaz OpenGL
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml - glGetTexEnv - příkaz OpenGL
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glGetTexEnv.xml - Pyglet Home Page
https://bitbucket.org/pyglet/pyglet/wiki/Home - Pyglet: dokumentace k verzi 1.2
https://pyglet.readthedocs.io/en/pyglet-1.2-maintenance/ - Dokumentace k verzi 1.2 ve formátu PDF
https://readthedocs.org/projects/pyglet/downloads/pdf/pyglet-1.2-maintenance/ - PyOpenGL
http://pyopengl.sourceforge.net/ - The #! magic, details about the shebang/hash-bang mechanism on various Unix flavours
https://www.in-ulm.de/~mascheck/various/shebang/ - Shebang (Unix)
https://en.wikipedia.org/wiki/Shebang_%28Unix%29 - Domovská stránka systému LÖVE
http://love2d.org/ - Simple DirectMedia Layer (home page)
http://www.libsdl.org/ - Simple DirectMedia Layer (Wikipedia)
https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer - Seriál Grafická knihovna OpenGL
https://www.root.cz/serialy/graficka-knihovna-opengl/ - Pyglet event loop
http://pyglet.readthedocs.io/en/latest/programming_guide/eventloop.html - Decorators I: Introduction to Python Decorators
http://www.artima.com/weblogs/viewpost.jsp?thread=240808 - 3D Programming in Python - Part 1
https://greendalecs.wordpress.com/2012/04/21/3d-programming-in-python-part-1/ - A very basic Pyglet tutorial
http://www.natan.termitnjak.net/tutorials/pyglet_basic.html - Alpha blending
https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending