Vzhledem k tomu, že je k Raspberry Pi možné připojit monitor popř. i (dotykový) displej, lze tyto oblíbené jednodeskové mikropočítače použít k zobrazování naměřených či vypočtených hodnot formou různých grafů. K dispozici je celá řada nástrojů, které je možné pro zobrazení grafů použít. My se dnes seznámíme s kombinací knihovny Matplotlib s Pygame.
Obsah
1. Vykreslování grafů do framebufferu Raspberry Pi s použitím knihoven Pygame a Matplotlib
2. Vytvoření grafu s jeho uložením do souboru
3. Vykreslení grafu knihovnou Pygame
5. Zdrojový kód prvního demonstračního příkladu
7. Zdrojový kód druhého demonstračního příkladu
8. Specifikace rozlišení obrázku s grafem
9. Zobrazení grafu ve středu obrazovky
10. Zdrojový kód třetího demonstračního příkladu
11. Vytvoření obrázku v „nativním“ rozlišení
12. Zjištění aktuálně nastaveného rozlišení framebufferu
13. Zdrojový kód čtvrtého demonstračního příkladu
14. Odlišný typ grafu – kontury
15. Zdrojový kód pátého demonstračního příkladu
16. Články o knihovnách NumPy, Matplotlib a Pygame
17. Repositář s demonstračními příklady
1. Vykreslování grafů do framebufferu Raspberry Pi s použitím knihoven Pygame a Matplotlib
S knihovnami Matplotlib a Pygame jsme se již na tomto serveru několikrát setkali (viz též šestnáctou kapitolu s odkazy na dříve vydané články), takže již víme, že Matplotlib je knihovna určená pro programovací jazyk Python, která slouží k tvorbě a částečně i k interaktivním úpravám různých typů grafů, například klasických grafů funkcí jedné proměnné, ovšem i mnoha grafů více či méně složitějších (grafy více funkcí, trojrozměrné grafy, polární grafy, zobrazení kontur atd.). Možnosti knihovny Matplotlib jsou skutečně značně široké a přitom je její použití poměrně jednoduché a snadno pochopitelné, pokud samozřejmě vynecháme některé pokročilejší operace. Jednou ze zajímavých možností představuje použití této knihovny v interaktivním prostředí IPython popř. IPython Notebook, zejména v kombinaci s další populární Pythonovskou knihovnou Numpy.
Obrázek 1: Knihovnu matplotlib je možné použít i interaktivně v IPythonu či v IPython Notebooku.
V mnoha projektech by bylo užitečné využít možnosti nabízené knihovnou Matplotlib i pro aplikace typické pro jednodeskové mikropočítače Raspberry Pi – měření různých veličin, ovládání dalších zařízení atd. K Raspberry Pi je samozřejmě možné připojit monitor přes rozhraní HDMI popř. lze připojit i dotykový displej, takže potřebný video výstup je zajištěn. Zbývá doprogramovat pouze „maličkost“ – vykreslení grafů do framebufferu (video paměti), abychom se obešli bez nutnosti mít spuštěný X Window System. K dosažení tohoto účelu zkombinujeme možnosti knihovny Matplotlib s další nám již známou knihovnou Pygame. Tyto knihovny nepropojíme přímo, ale využijeme meziuložení grafu na disk (či ještě lépe do ramdisku) do rastrového obrázku. Hned na úvod je nutné říci, že se sice v žádném případě nejedná o nejrychlejší řešení, na druhou stranu je však Matplotlib velmi flexibilní a Pygame může sloužit pro interaktivní ovládání Raspberry Pi.
Obrázek 2: Vykreslení průběhu funkce s využitím pouhých pěti příkazů (z toho dva příkazy slouží k importu knihoven).
2. Vytvoření grafu s jeho uložením do souboru
Pro jednoduchost si zvolme graf funkce sinus. Pro výpočet průběhu této funkce použijeme knihovnu Numpy, pro vykreslení pak knihovnu Matplotlib. Povšimněte si velmi důležitého druhého řádku, kterým se určuje, jaký „backend“ má Matplotlib použít. Zde vyžadujeme použití Agg (nativní knihovna pro C++), což nám umožní spustit aplikaci z konzole (bez X Window Systemu):
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
Následně je pomocí funkce numpy.linspace() vytvořeno pole sta prvků s hodnotami od 0 do 2π. Na toto pole je aplikována funkce numpy.sin(), jejímž výsledkem je nové stoprvkové pole (hodnoty prvků leží v rozsahu od -1 do 1). Funkcí matplotlib.pyplot.plot() je vykreslen průběh funkce, ovšem graf ještě není zobrazen, takže do něj můžeme přidat popis obou os. Z funkce vrátíme objekt plně popisující vykreslovaný graf, jeho rozměry, osy apod:
# Vytvoreni grafu
def create_graph():
fig = plt.figure()
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("sin(x)")
return fig
Další funkce uloží graf do souboru pomocí metody savefig(), které se v nejjednodušším případě předá pouze jméno výstupního souboru. Tato funkce pak na základě analýzy přípony souboru rozhodne o tom, který formát použije. K dispozici jsou mj. tyto formáty:
# | Koncovka | Formát |
---|---|---|
1 | png | rastrový formát PNG |
2 | dokument ve formátu PDF s vektorovým grafem | |
3 | eps | vektorový formát Encapsulated PostScript (varianta PostScriptu určená pro vložení do dalších dokumentů, včetně (La)TeXu) |
4 | ps | vektorový formát PostScript |
5 | svg | vektorový formát SVG |
Realizace uložení do souboru bude (prozatím) triviální:
# Ulozeni grafu do souboru
def save_graph(fig, imageFile):
plt.savefig(imageFile)
Obrázek 3: Průběh funkce sin(x) vykreslený nepatrně upraveným demonstračním příkladem.
3. Vykreslení grafu knihovnou Pygame
Ve chvíli, kdy již máme graf vykreslený a současně i uložený do rastrového obrázku typu PNG, můžeme se pokusit o jeho zobrazení na monitoru. Využijeme přitom možnosti nabízené knihovnou Pygame, především její schopnost vykreslování jakýchkoli obrazců do framebufferu, načítání rastrových obrázků ze souboru i interaktivní čtení stisknutých kláves popř. čtení stavu myši (nebo i vstupu dotykového displeje). Nejdříve musíme vytvořit funkci, která inicializuje obrazovku pro vykreslování. Této funkci předáme požadované rozlišení a taktéž barvu pozadí. Funkce nejdříve inicializuje grafický režim (může provést i jeho přepnutí), dále skryje kurzor myši, který nepotřebujeme a následně obrazovku vyplní konstantní barvou. Návratovou hodnotou je objekt reprezentující obrazovou paměť:
# Inicializace knihovny Pygame, inicializace video systemu a otevreni framebufferu
def initialize_pygame(width, height, background_color):
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.mouse.set_visible(0)
screen.fill(background_color)
return screen
O načtení a zobrazení obrázku s grafem se postará další funkce nazvaná show_image(), které mj. předáme jméno souboru s obrázkem. Tato funkce nejdříve obrázek načte a posléze ho operací blit (Bit Block Transfer) zobrazí na monitoru. Přitom se však (prozatím) neprovádí žádné testy, zda se obrázek na displej skutečně vejde či nikoli:
# Zobrazeni rastroveho obrazku do framebufferu
def show_image(screen, imageFile):
image = pygame.image.load(imageFile)
screen.blit(image, (0,0))
pygame.display.flip()
Poslední užitečná funkce postupně načítá ze smyčky událostí (event loop) jednotlivé události vzniklé činností uživatele. Ve chvíli, kdy je detekován stisk klávesy, je funkce ukončena:
# Cekani na ukonceni aplikace libovolnou klavesou
def wait_for_key():
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == KEYDOWN:
return
clock.tick(20)
Obrázek 4: Různé styly vykreslování. Povšimněte si, že v grafu je v levém dolním rohu zobrazena i legenda.
4. Výběr framebufferu
Linuxové jádro může obsluhovat větší množství framebufferů. U počítačů Raspberry Pi je vždy k dispozici framebuffer realizovaný přes čip BCM270x, který má v operační paměti vyhrazený prostor, jenž je framebufferem využíván (velikost videopaměti je konfigurovatelná, projeví se po restartu systému). Kromě toho je však možné k Raspberry Pi připojit například dotykový displej, který se mnohdy též ovládá přes framebuffer v závislosti na konkrétním ovladači. Proto je nejdříve nutné zjistit, který framebuffer bude naše aplikace používat. Ke zjištění všech dostupných framebufferů slouží nástroj nazvaný fbset, který spustíme s parametry -v a -i:
fbset -v -i
Pokud je použita standardní konfigurace Raspbiannu, měly by se vypsat zhruba následující informace. Konkrétní rozlišení samozřejmě může být odlišné, protože závisí na typu připojeného monitoru:
Linux Frame Buffer Device Configuration Version 2.1 (23/06/1999)
(C) Copyright 1995-1999 by Geert Uytterhoeven
Opening frame buffer device `/dev/fb0'
Using current video mode from `/dev/fb0'
mode "1280x1024"
geometry 1280 1024 1280 1024 16
timings 0 0 0 0 0 0 0
rgba 5/11,6/5,5/0,0/16
endmode
Getting further frame buffer information
Frame buffer device information:
Name : BCM2708 FB
Address : 0x1c006000
Size : 2621440
Type : PACKED PIXELS
Visual : TRUECOLOR
XPanStep : 1
YPanStep : 1
YWrapStep : 0
LineLength : 2560
Accelerator : No
Vypsané jméno zařízení realizující přístup do framebufferu (/dev/fb0) použijeme v našem příkladu, protože před inicializací knihovny Pygame nastavíme proměnnou prostředí SDL_FBDEV a přiřadíme jí jméno zařízení s framebufferem. Funkce initialize_pygame() se tedy nepatrně změní:
# Inicializace knihovny Pygame, inicializace video systemu a otevreni framebufferu
def initialize_pygame(width, height, background_color, framebuffer_device):
os.environ["SDL_FBDEV"] = framebuffer_device
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.mouse.set_visible(0)
screen.fill(background_color)
return screen
Volání této funkce bude samozřejmě taktéž nepatrně modifikováno, protože jí kromě požadovaných rozměrů okna a barvy pozadí předáme i jméno zařízení s framebufferem:
# jmeno zarizeni implementujiciho rozhrani k framebufferu
FRAMEBUFFER_DEVICE = "/dev/fb0"
screen = initialize_pygame(WIDTH, HEIGHT, (0, 0, 200), FRAMEBUFFER_DEVICE)
5. Zdrojový kód prvního demonstračního příkladu
Všechny funkce popsané v předchozích třech kapitolách nyní použijeme v dnešním prvním demonstračním příkladu, který vytvoří graf, uloží ho do souboru, inicializuje knihovnu Pygame, vykreslí obrázek s grafem a následně čeká na stisk klávesy (poté se program ukončí):
# Hlavni funkce aplikace
def main():
fig = create_graph()
save_graph(fig, IMAGE_FILE)
screen = initialize_pygame(WIDTH, HEIGHT, (0, 0, 200), FRAMEBUFFER_DEVICE)
show_image(screen, IMAGE_FILE)
wait_for_key()
exit()
Úplný zdrojový kód prvního demonstračního příkladu vypadá následovně:
#!/usr/bin/python
# vim: set fileencodings=utf-8
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import pygame
import os
import sys
# nutno importovat kvuli konstantam QUIT atd.
from pygame.locals import *
# velikost okna na obrazovce (framebufferu)
WIDTH = 640
HEIGHT = 480
# jmeno souboru s grafem
IMAGE_FILE = "plot1.png"
# jmeno zarizeni implementujiciho rozhrani k framebufferu
FRAMEBUFFER_DEVICE = "/dev/fb0"
# Vytvoreni grafu
def create_graph():
fig = plt.figure()
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("sin(x)")
return fig
# Ulozeni grafu do souboru
def save_graph(fig, imageFile):
plt.savefig(imageFile)
# Inicializace knihovny Pygame, inicializace video systemu a otevreni framebufferu
def initialize_pygame(width, height, background_color, framebuffer_device):
os.environ["SDL_FBDEV"] = framebuffer_device
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.mouse.set_visible(0)
screen.fill(background_color)
return screen
# Zobrazeni rastroveho obrazku do framebufferu
def show_image(screen, imageFile):
image = pygame.image.load(imageFile)
screen.blit(image, (0,0))
pygame.display.flip()
# Cekani na ukonceni aplikace libovolnou klavesou
def wait_for_key():
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == KEYDOWN:
return
clock.tick(20)
# Ukonceni aplikace
def exit():
pygame.quit()
sys.exit()
# Hlavni funkce aplikace
def main():
fig = create_graph()
save_graph(fig, IMAGE_FILE)
screen = initialize_pygame(WIDTH, HEIGHT, (0, 0, 200), FRAMEBUFFER_DEVICE)
show_image(screen, IMAGE_FILE)
wait_for_key()
exit()
# Vstupni bod
if __name__ == "__main__":
main()
Obrázek 5: Obrázek vygenerovaný prvním demonstračním příkladem.
6. Úprava parametrů grafu
Parametry grafu lze samozřejmě měnit, do grafu je například možné přidat popisky os, legendu, průběhy dalších funkcí, měnit parametry jejich zobrazení, přidat do grafu mřížku atd. Podívejme se nyní na parametry, které lze měnit ve chvíli, kdy se graf vykresluje. Změnit je možné pozadí okolí grafu, okraje okolo grafu atd. Vše se provádí předáním pojmenovaných parametrů do funkce savefig():
# Ulozeni grafu do souboru
def save_graph(fig, imageFile):
plt.savefig(imageFile, facecolor=fig.get_facecolor(), bbox_inches='tight', dpi=80, pad_inches=0.03)
7. Zdrojový kód druhého demonstračního příkladu
Druhý příklad se od příkladu prvního liší pouze úpravou funkce save_graph() a změnou názvu pracovního souboru s uloženým grafem:
#!/usr/bin/python
# vim: set fileencodings=utf-8
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import pygame
import os
import sys
# nutno importovat kvuli konstantam QUIT atd.
from pygame.locals import *
# velikost okna na obrazovce (framebufferu)
WIDTH = 640
HEIGHT = 480
# jmeno souboru s grafem
IMAGE_FILE = "plot2.png"
# jmeno zarizeni implementujiciho rozhrani k framebufferu
FRAMEBUFFER_DEVICE = "/dev/fb0"
# Vytvoreni grafu
def create_graph():
fig = plt.figure()
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("sin(x)")
return fig
# Ulozeni grafu do souboru
def save_graph(fig, imageFile):
plt.savefig(imageFile, facecolor=fig.get_facecolor(), bbox_inches='tight', dpi=80, pad_inches=0.03)
# Inicializace knihovny Pygame, inicializace video systemu a otevreni framebufferu
def initialize_pygame(width, height, background_color, framebuffer_device):
os.environ["SDL_FBDEV"] = framebuffer_device
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.mouse.set_visible(0)
screen.fill(background_color)
return screen
# Zobrazeni rastroveho obrazku do framebufferu
def show_image(screen, imageFile):
image = pygame.image.load(imageFile)
screen.blit(image, (0,0))
pygame.display.flip()
# Cekani na ukonceni aplikace libovolnou klavesou
def wait_for_key():
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == KEYDOWN:
return
clock.tick(20)
# Ukonceni aplikace
def exit():
pygame.quit()
sys.exit()
# Hlavni funkce aplikace
def main():
fig = create_graph()
save_graph(fig, IMAGE_FILE)
screen = initialize_pygame(WIDTH, HEIGHT, (0, 0, 200), FRAMEBUFFER_DEVICE)
show_image(screen, IMAGE_FILE)
wait_for_key()
exit()
# Vstupni bod
if __name__ == "__main__":
main()
Obrázek 6: Obrázek vygenerovaný druhým demonstračním příkladem.
8. Specifikace rozlišení obrázku s grafem
Ve chvíli, kdy přesně známe aktuálně nastavené rozlišení framebufferu, se můžeme zabývat dalším problémem – jak přesně nastavit rozměry grafu tak, aby se zobrazil přes celou obrazovku. Knihovna Matplotlib je zvláštní tím, že se v ní všechny rozměry specifikují v palcích, což znamená, že pro přepočet mezi pixely a palci musíme použít konstantu DPI (Dot Per Inch). V praxi to může vypadat tak, že si hodnotu DPI stanovíme sami, například na konstantu 100:
# DPI (umele zvolena hodnota)
DPI = 100
To znamená, že knihovna Matplotlib bude předpokládat, že jeden palec bude odpovídat sto pixelům. Pokud tedy zadáme velikost obrázku s rozměry:
šířka_pro_matplotlib=šířka_v_pixelech/100
výška_pro_matplotlib=výška_v_pixelech/100
dostaneme při vykreslení do rastrového obrázku správné rozměry. Jen u větších rozměrů (desítky tisíc pixelů) by mohlo dojít k nepatrnému zaokrouhlení velikosti obrázku o několik pixelů, což nám však nebude vadit. Pouze nesmíme zapomenout na to, že rozměry v palcích mohou obsahovat i desetinnou část (nemá smysl dělit například 640 pixelů hodnotou 100, protože by výsledek celočíselného dělení byt 6), takže funkce pro vytvoření grafu bude po úpravě vypadat následovně:
# Vytvoreni grafu
def create_graph(width, height, dpi):
fig = plt.figure(figsize=(1.0*width/dpi, 1.0*height/dpi), dpi=dpi)
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("sin(x)")
return fig
Při ukládání obrázku je nutné hodnotu DPI specifikovat explicitně volitelným (keyword) parametrem pojmenovaným taktéž dpi:
# Ulozeni grafu do souboru
def save_graph(fig, imageFile, dpi):
plt.savefig(imageFile, facecolor=fig.get_facecolor(), dpi=dpi)
9. Zobrazení grafu ve středu obrazovky
Pokud je rastrový obrázek obsahující graf menší než rozlišení obrazovky, bude výhodnější takový graf zobrazit doprostřed plochy monitoru. To je samozřejmě možné, protože metodě screen.blit() se kromě objektu s vykreslovaným obrázkem/spritem předává i levý horní roh na obrazovce. Jediný krok, který musíme udělat, je vypočítat nové souřadnice tohoto levého horního rohu, což lze realizovat snadno, když si uvědomíme, že rozměry obrazovky i obrázku jsou reprezentovány objektem typu rect s atributy width, height a x, y (poslední dva zmíněné atributy však nepoužijeme). Funkci pro vykreslení obrázku nepatrně upravíme takto:
# Zobrazeni rastroveho obrazku do framebufferu
def show_image(screen, imageFile):
image = pygame.image.load(imageFile)
image_rect = image.get_rect()
screen_rect = screen.get_rect()
x = (screen_rect.width - image_rect.width) / 2
y = (screen_rect.height - image_rect.height) / 2
screen.blit(image, (x,y))
pygame.display.flip()
10. Zdrojový kód třetího demonstračního příkladu
Všechny výše popsané změny opět zapracujeme do demonstračního příkladu, jehož třetí verze bude vypadat následovně:
#!/usr/bin/python
# vim: set fileencodings=utf-8
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import pygame
import os
import sys
# nutno importovat kvuli konstantam QUIT atd.
from pygame.locals import *
# velikost okna na obrazovce (framebufferu)
WIDTH = 640
HEIGHT = 480
# DPI (umele zvolena hodnota)
DPI = 100
# jmeno souboru s grafem
IMAGE_FILE = "plot3.png"
# jmeno zarizeni implementujiciho rozhrani k framebufferu
FRAMEBUFFER_DEVICE = "/dev/fb0"
# Vytvoreni grafu
def create_graph(width, height, dpi):
fig = plt.figure(figsize=(1.0*width/dpi, 1.0*height/dpi), dpi=dpi)
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("sin(x)")
return fig
# Ulozeni grafu do souboru
def save_graph(fig, imageFile, dpi):
plt.savefig(imageFile, facecolor=fig.get_facecolor(), dpi=dpi)
# Inicializace knihovny Pygame, inicializace video systemu a otevreni framebufferu
def initialize_pygame(width, height, background_color, framebuffer_device):
os.environ["SDL_FBDEV"] = framebuffer_device
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.mouse.set_visible(0)
screen.fill(background_color)
return screen
# Zobrazeni rastroveho obrazku do framebufferu
def show_image(screen, imageFile):
image = pygame.image.load(imageFile)
image_rect = image.get_rect()
screen_rect = screen.get_rect()
x = (screen_rect.width - image_rect.width) / 2
y = (screen_rect.height - image_rect.height) / 2
screen.blit(image, (x,y))
pygame.display.flip()
# Cekani na ukonceni aplikace libovolnou klavesou
def wait_for_key():
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == KEYDOWN:
return
clock.tick(20)
# Ukonceni aplikace
def exit():
pygame.quit()
sys.exit()
# Hlavni funkce aplikace
def main():
fig = create_graph(WIDTH, HEIGHT, DPI)
save_graph(fig, IMAGE_FILE, DPI)
screen = initialize_pygame(WIDTH, HEIGHT, (10, 10, 40), FRAMEBUFFER_DEVICE)
show_image(screen, IMAGE_FILE)
wait_for_key()
exit()
# Vstupni bod
if __name__ == "__main__":
main()
Obrázek 7: Obrázek vygenerovaný třetím demonstračním příkladem.
11. Vytvoření obrázku v „nativním“ rozlišení
Prozatím jsme v předchozích třech demonstračních příkladech nastavovali rozlišení monitoru na hodnotu 640×480 pixelů. Tato hodnota sice zaručí, že příklad bude použitelný na většině monitorů, ovšem výsledek vizuálně nebude v žádném případě dokonalý, protože je mnohem lepší využít nativního rozlišení monitoru nastaveného při startu operačního systému (rozlišení je ovšem možné změnit i v běžícím systému, i když tato možnost je omezena). Pro získání informace o tom, jaké konkrétní rozlišení je právě nastaveno, máme několik možností. Můžeme se například pokusit o přečtení globálního konfiguračního souboru /boot/config.txt, což ovšem nemusí být přesné. Přesnější je spuštění již na začátku tohoto článku zmíněné utility fbset a přečtení hodnot, které tato utilita vypíše na standardní výstup.
12. Zjištění aktuálně nastaveného rozlišení framebufferu
Externí utilitu fbset spustíme přímo z našeho skriptu naprogramovaného v Pythonu jako samostatný proces, z něhož získáme text poslaný na standardní výstup. V tomto textu se následně budeme snažit najít řádek začínající slovem „geometry“ (s případnými mezerami před tímto slovem). Ve chvíli, kdy slovo „geometry“ skutečně nalezneme, se pokusíme získat dvě celá čísla zapsaná za tímto slovem, která reprezentují aktuálně nastavené rozlišení framebufferu. Celý postup si můžeme vyzkoušet spuštěním následujícího příkladu:
import subprocess
import re
def get_framebuffer_resolution(framebuffer_device):
fbset_output = subprocess.check_output(["fbset", "-s", "-fb", framebuffer_device])
for line in fbset_output.split("\n"):
line = line.strip()
if line.startswith("geometry"):
print(line)
parsed = re.match(r"geometry (\d+) (\d+)", line)
return (parsed.group(1), parsed.group(2))
print(get_framebuffer_resolution("/dev/fb0"))
13. Zdrojový kód čtvrtého demonstračního příkladu
Do zdrojového kódu čtvrtého příkladu byla přidána tato funkce, která vrátí rozměry aktuálně používaného framebufferu. Rozměry se vrátí ve formě dvojice (n-tice) obsahující dvě celá čísla:
# precteni aktualne nastaveneho rozliseni framebufferu
def get_framebuffer_resolution(framebuffer_device):
fbset_output = subprocess.check_output(["fbset", "-s", "-fb", framebuffer_device])
for line in fbset_output.split("\n"):
line = line.strip()
if line.startswith("geometry"):
print(line)
parsed = re.match(r"geometry (\d+) (\d+)", line)
return (int(parsed.group(1)), int(parsed.group(2)))
Následně se tyto rozměry použijí při vytvoření grafu a současně i inicializaci knihovny Pygame:
width, height = get_framebuffer_resolution(FRAMEBUFFER_DEVICE)
fig = create_graph(width, height, DPI)
save_graph(fig, IMAGE_FILE, DPI)
screen = initialize_pygame(width, height, (10, 10, 40), FRAMEBUFFER_DEVICE)
Úplný zdrojový kód tohoto příkladu vypadá následovně:
#!/usr/bin/python
# vim: set fileencodings=utf-8
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import pygame
import os
import sys
import subprocess
import re
# nutno importovat kvuli konstantam QUIT atd.
from pygame.locals import *
# DPI (umele zvolena hodnota)
DPI = 100
# jmeno souboru s grafem
IMAGE_FILE = "plot4.png"
# jmeno zarizeni implementujiciho rozhrani k framebufferu
FRAMEBUFFER_DEVICE = "/dev/fb0"
# Vytvoreni grafu
def create_graph(width, height, dpi):
fig = plt.figure(figsize=(1.0*width/dpi, 1.0*height/dpi), dpi=dpi)
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("sin(x)")
return fig
# Ulozeni grafu do souboru
def save_graph(fig, imageFile, dpi):
plt.savefig(imageFile, facecolor=fig.get_facecolor(), dpi=dpi)
# Inicializace knihovny Pygame, inicializace video systemu a otevreni framebufferu
def initialize_pygame(width, height, background_color, framebuffer_device):
os.environ["SDL_FBDEV"] = framebuffer_device
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.mouse.set_visible(0)
screen.fill(background_color)
return screen
# Zobrazeni rastroveho obrazku do framebufferu
def show_image(screen, imageFile):
image = pygame.image.load(imageFile)
image_rect = image.get_rect()
screen_rect = screen.get_rect()
x = (screen_rect.width - image_rect.width) / 2
y = (screen_rect.height - image_rect.height) / 2
screen.blit(image, (x,y))
pygame.display.flip()
# Cekani na ukonceni aplikace libovolnou klavesou
def wait_for_key():
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == KEYDOWN:
return
clock.tick(20)
# precteni aktualne nastaveneho rozliseni framebufferu
def get_framebuffer_resolution(framebuffer_device):
fbset_output = subprocess.check_output(["fbset", "-s", "-fb", framebuffer_device])
for line in fbset_output.split("\n"):
line = line.strip()
if line.startswith("geometry"):
print(line)
parsed = re.match(r"geometry (\d+) (\d+)", line)
return (int(parsed.group(1)), int(parsed.group(2)))
# Ukonceni aplikace
def exit():
pygame.quit()
sys.exit()
# Hlavni funkce aplikace
def main():
width, height = get_framebuffer_resolution(FRAMEBUFFER_DEVICE)
fig = create_graph(width, height, DPI)
save_graph(fig, IMAGE_FILE, DPI)
screen = initialize_pygame(width, height, (10, 10, 40), FRAMEBUFFER_DEVICE)
show_image(screen, IMAGE_FILE)
wait_for_key()
exit()
# Vstupni bod
if __name__ == "__main__":
main()
Obrázek 8: Obrázek vygenerovaný čtvrtým demonstračním příkladem.
14. Odlišný typ grafu – kontury
Knihovna Matplotlib samozřejmě nabízí i několik možností vykreslení grafů funkcí typu z=f(x,y), tj. funkcí se dvěma nezávislými proměnnými. Tyto funkce je možné zobrazit různým způsobem, například ve formě kontur („vrstevnic“), drátového modelu (wireframe) či dokonce vyplněné plochy (mapy výšek, heightfield). Pro zajímavost si vyzkoušíme implementaci vykreslení takzvaných kontur, které si pro zjednodušení můžeme představit jako vrstevnice na mapě – body spojené konturou/vrstevnicí mají stejnou hodnotu funkce (tj. stejnou hodnotu z-ové souřadnice). Při vyhodnocování a následném vykreslení funkce budeme postupovat následovně:
- Vytvoříme vektor x s hodnotami nezávislé proměnné x.
- Vytvoříme vektor y s hodnotami nezávislé proměnné y.
- S využitím numpy.meshgrid necháme vygenerovat dvojici matic souřadnic.
- Necháme vypočítat body ležící na ploše funkce (z-ové souřadnice se uloží do matice Z).
- Vlastní vykreslení kontur zajistí funkce matplotlib.pyplot.contour(X, Y, Z).
Podívejme se úpravu funkce pro vykreslení grafu, která výše uvedené kroky realizuje:
# Vytvoreni grafu
def create_graph(width, height, dpi):
fig = plt.figure(figsize=(1.0*width/dpi, 1.0*height/dpi), dpi=dpi)
delta = 0.1
# prubeh nezavisle promenne x
x = np.arange(-10.0, 10.0, delta)
# prubeh nezavisle promenne y
y = np.arange(-10.0, 10.0, delta)
# vytvoreni dvou poli se souradnicemi [x,y]
X, Y = np.meshgrid(x, y)
# vzdalenost od bodu [0,0]
R1 = np.sqrt(X*X+Y*Y)
# vzdalenost od bodu [3,3]
R2 = np.sqrt((X-3)*(X-3)+(Y-3)*(Y-3))
# vypocet funkce, kterou pouzijeme pri vykreslovani grafu
Z = np.sin(R1)-np.cos(R2)
# povoleni zobrazeni mrizky
plt.grid(True)
# vytvoreni grafu s konturami funkce z=f(x,y)
plt.contour(X, Y, Z)
# zobrazeni grafu
plt.plot()
return fig
Obrázek 9: Nepatrnou úpravou kódu lze dosáhnout toho, že kromě vrstevnic a jejich hodnot se napravo od grafu zobrazí i „mapa výšek“. Relativní velikost mapy vůči celému grafu se řídí hodnotou shrink. Zde konkrétně má celá legenda výšku jen 70% výšky celého grafu.
15. Zdrojový kód pátého demonstračního příkladu
Úplný zdrojový kód pátého a současně i dnešního posledního příkladu vypadá následovně:
#!/usr/bin/python
# vim: set fileencodings=utf-8
import matplotlib
matplotlib.use("Agg")
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import numpy as np
import pygame
import os
import sys
import subprocess
import re
# nutno importovat kvuli konstantam QUIT atd.
from pygame.locals import *
# DPI (umele zvolena hodnota)
DPI = 100
# jmeno souboru s grafem
IMAGE_FILE = "plot5.png"
# jmeno zarizeni implementujiciho rozhrani k framebufferu
FRAMEBUFFER_DEVICE = "/dev/fb0"
# Vytvoreni grafu
def create_graph(width, height, dpi):
fig = plt.figure(figsize=(1.0*width/dpi, 1.0*height/dpi), dpi=dpi)
delta = 0.1
# prubeh nezavisle promenne x
x = np.arange(-10.0, 10.0, delta)
# prubeh nezavisle promenne y
y = np.arange(-10.0, 10.0, delta)
# vytvoreni dvou poli se souradnicemi [x,y]
X, Y = np.meshgrid(x, y)
# vzdalenost od bodu [0,0]
R1 = np.sqrt(X*X+Y*Y)
# vzdalenost od bodu [3,3]
R2 = np.sqrt((X-3)*(X-3)+(Y-3)*(Y-3))
# vypocet funkce, kterou pouzijeme pri vykreslovani grafu
Z = np.sin(R1)-np.cos(R2)
# povoleni zobrazeni mrizky
plt.grid(True)
# vytvoreni grafu s konturami funkce z=f(x,y)
plt.contour(X, Y, Z)
# zobrazeni grafu
plt.plot()
return fig
# Ulozeni grafu do souboru
def save_graph(fig, imageFile, dpi):
plt.savefig(imageFile, facecolor=fig.get_facecolor(), dpi=dpi)
# Inicializace knihovny Pygame, inicializace video systemu a otevreni framebufferu
def initialize_pygame(width, height, background_color, framebuffer_device):
os.environ["SDL_FBDEV"] = framebuffer_device
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.mouse.set_visible(0)
screen.fill(background_color)
return screen
# Zobrazeni rastroveho obrazku do framebufferu
def show_image(screen, imageFile):
image = pygame.image.load(imageFile)
image_rect = image.get_rect()
screen_rect = screen.get_rect()
x = (screen_rect.width - image_rect.width) / 2
y = (screen_rect.height - image_rect.height) / 2
screen.blit(image, (x,y))
pygame.display.flip()
# Cekani na ukonceni aplikace libovolnou klavesou
def wait_for_key():
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == KEYDOWN:
return
clock.tick(20)
# precteni aktualne nastaveneho rozliseni framebufferu
def get_framebuffer_resolution(framebuffer_device):
fbset_output = subprocess.check_output(["fbset", "-s", "-fb", framebuffer_device])
for line in fbset_output.split("\n"):
line = line.strip()
if line.startswith("geometry"):
print(line)
parsed = re.match(r"geometry (\d+) (\d+)", line)
return (int(parsed.group(1)), int(parsed.group(2)))
# Ukonceni aplikace
def exit():
pygame.quit()
sys.exit()
# Hlavni funkce aplikace
def main():
width, height = get_framebuffer_resolution(FRAMEBUFFER_DEVICE)
fig = create_graph(width, height, DPI)
save_graph(fig, IMAGE_FILE, DPI)
screen = initialize_pygame(width, height, (10, 10, 40), FRAMEBUFFER_DEVICE)
show_image(screen, IMAGE_FILE)
wait_for_key()
exit()
# Vstupni bod
if __name__ == "__main__":
main()
Obrázek 10: Obrázek vygenerovaný pátým demonstračním příkladem.
16. Články o knihovnách NumPy, Matplotlib a Pygame
V následujícím seznamu jsou vypsány články o knihovnách NumPy, Matplotlib a Pygame, které již byly na serveru mojefedora.cz v minulosti vydány:
- Integrovaná vývojová prostředí ve Fedoře: IPython a IPython Notebook
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy (2.část)
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy-2-cast/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib (2.část)
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib-2-cast/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib (dokončení)
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib-dokonceni/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib (dokončení)
https://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: práce s bitmapami a TrueType fonty
https://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-prace-s-bitmapami-a-truetype-fonty/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: knihovna Pygame prakticky
https://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame-prakticky/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: sprity v knihovně Pygame
https://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-sprity-v-knihovne-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: detekce kolize spritů
https://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-detekce-kolize-spritu/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: transformace rastrových obrázků
https://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-transformace-rastrovych-obrazku/
17. Repositář s demonstračními příklady
Všechny demonstrační příklady, s nimiž jsme se v dnešním článku seznámili, byly uloženy do Git repositáře umístěného na GitHubu (https://github.com/tisnik/presentations). Poslední verze zdrojových kódů naleznete pod odkazy uvedenými v tabulce pod tímto odstavcem:
Příklad | Odkaz do repositáře |
---|---|
pygame_matplotlib_1.py | https://github.com/tisnik/presentations/blob/master/matplotlib/pygame_matplotlib_1.py |
pygame_matplotlib_2.py | https://github.com/tisnik/presentations/blob/master/matplotlib/pygame_matplotlib_2.py |
pygame_matplotlib_3.py | https://github.com/tisnik/presentations/blob/master/matplotlib/pygame_matplotlib_3.py |
pygame_matplotlib_4.py | https://github.com/tisnik/presentations/blob/master/matplotlib/pygame_matplotlib_4.py |
pygame_matplotlib_5.py | https://github.com/tisnik/presentations/blob/master/matplotlib/pygame_matplotlib_5.py |
get_framebuffer_resolution.py | https://github.com/tisnik/presentations/blob/master/matplotlib/get_framebuffer_resolution.py |
Pro úspěšné spuštění těchto příkladů je nutné dodržet tato pravidla:
- Musí být nainstalována knihovna Matplotlib a Numpy.
- Dále je nutné nainstalovat i knihovnu Pygame
- Příklady by měly být spouštěny z terminálu Raspberry Pi bez X systému
- Příkazem fbset -v si ověřte, že se skutečně používá zařízení /dev/fb0
- Uživatel spouštějící příklad musí mít přístup do framebufferu. Většinou to znamená laborování se skupinami či použití sudo
18. Odkazy na Internetu
- Pygame.org
http://pygame.org/hifi.html - Pygame - instalační soubory pro různé operační systémy
http://pygame.org/download.shtml - Pygame: documentation
http://www.pygame.org/docs/ - Pygame Wiki: Getting Started
http://www.pygame.org/wiki/GettingStarted - Pygame Tutorials: Tutorials Basic
http://pygametutorials.wikidot.com/tutorials-basic - Python.org (dokumentace k jazyku, odkazy na instalační soubory atd.)
https://www.python.org/ - How to Draw with Pygame on Your Raspberry Pi
http://www.dummies.com/how-to/content/how-to-draw-with-pygame-on-your-raspberry-pi.html - NumPy Home Page
http://www.numpy.org/ - NumPy v1.10 Manual
http://docs.scipy.org/doc/numpy/index.html - NumPy (Wikipedia)
https://en.wikipedia.org/wiki/NumPy - Matplotlib Home Page
http://matplotlib.org/ - matplotlib (Wikipedia)
https://en.wikipedia.org/wiki/Matplotlib - Lorenzův atraktor
http://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-iii/#k03 - Popis barvových map modulu matplotlib.cm
https://gist.github.com/endolith/2719900#id7 - Ukázky (palety) barvových map modulu matplotlib.cm
http://matplotlib.org/examples/color/colormaps_reference.html - Lorenz system
https://en.wikipedia.org/wiki/Lorenz_system - Customising contour plots in matplotlib
https://philbull.wordpress.com/2012/12/27/customising-contour-plots-in-matplotlib/ - Graphics with Matplotlib
http://kestrel.nmt.edu/~raymond/software/python_notes/paper004.html - Systémy lineárních rovnic
http://www.matematika.cz/systemy-linearnich-rovnic - IPython homepage
http://ipython.org/ - Dokumentace k IPythonu
http://ipython.org/documentation.html# - IPython Tutorial
http://ipython.readthedocs.org/en/stable/interactive/tutorial.html - The cell magics in IPython
http://nbviewer.jupyter.org/github/ipython/ipython/blob/1.x/examples/notebooks/Cell%20Magics.ipynb - 0MQ Home Page
http://zeromq.org/ - Is IPython Notebook ever used as an IDE, or merely for presentations?
https://www.reddit.com/r/IPython/comments/1uk7hp/is_ipython_notebook_ever_used_as_an_ide_or_merely/ - The IDE as a Bad Programming Language Enabler
https://dzone.com/articles/ide-bad-programming-language - Enhanced Interactive Python with IPython
http://www.onlamp.com/pub/a/python/2005/01/27/ipython.html - Direct mode
https://en.wikipedia.org/wiki/Direct_mode - Seznámení s Python IDE Spyder (článek vyšel zde na mojefedora.cz)
http://mojefedora.cz/seznameni-s-python-ide-spyder/ - Stránka s popisem různých IDE pro Python
http://quintagroup.com/cms/python/ide - Eclipse (stránka o frameworku na Fedoraproject.org)
https://fedoraproject.org/wiki/Eclipse