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

4. Výběr framebufferu

5. Zdrojový kód prvního demonstračního příkladu

6. Úprava parametrů grafu

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

18. Odkazy na Internetu

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.

08

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.

19

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 pdf 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)

figure_02

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)

figure_04

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ě:

  1. Vytvoříme vektor x s hodnotami nezávislé proměnné x.
  2. Vytvoříme vektor y s hodnotami nezávislé proměnné y.
  3. S využitím numpy.meshgrid necháme vygenerovat dvojici matic souřadnic.
  4. Necháme vypočítat body ležící na ploše funkce (z-ové souřadnice se uloží do matice Z).
  5. 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

04

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:

  1. Integrovaná vývojová prostředí ve Fedoře: IPython a IPython Notebook
    http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/
  2. 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/
  3. 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/
  4. 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/
  5. 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/
  6. 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/
  7. 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/
  8. 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/
  9. 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/
  10. 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/
  11. 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/
  12. 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:

  1. Musí být nainstalována knihovna Matplotlib a Numpy.
  2. Dále je nutné nainstalovat i knihovnu Pygame
  3. Příklady by měly být spouštěny z terminálu Raspberry Pi bez X systému
  4. Příkazem fbset -v si ověřte, že se skutečně používá zařízení /dev/fb0
  5. 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

  1. Pygame.org
    http://pygame.org/hifi.html
  2. Pygame - instalační soubory pro různé operační systémy
    http://pygame.org/download.shtml
  3. Pygame: documentation
    http://www.pygame.org/docs/
  4. Pygame Wiki: Getting Started
    http://www.pygame.org/wiki/GettingStarted
  5. Pygame Tutorials: Tutorials Basic
    http://pygametutorials.wikidot.com/tutorials-basic
  6. Python.org (dokumentace k jazyku, odkazy na instalační soubory atd.)
    https://www.python.org/
  7. 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
  8. NumPy Home Page
    http://www.numpy.org/
  9. NumPy v1.10 Manual
    http://docs.scipy.org/doc/numpy/index.html
  10. NumPy (Wikipedia)
    https://en.wikipedia.org/wiki/NumPy
  11. Matplotlib Home Page
    http://matplotlib.org/
  12. matplotlib (Wikipedia)
    https://en.wikipedia.org/wiki/Matplotlib
  13. Lorenzův atraktor
    http://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-iii/#k03
  14. Popis barvových map modulu matplotlib.cm
    https://gist.github.com/endolith/2719900#id7
  15. Ukázky (palety) barvových map modulu matplotlib.cm
    http://matplotlib.org/examples/color/colormaps_reference.html
  16. Lorenz system
    https://en.wikipedia.org/wiki/Lorenz_system
  17. Customising contour plots in matplotlib
    https://philbull.wordpress.com/2012/12/27/customising-contour-plots-in-matplotlib/
  18. Graphics with Matplotlib
    http://kestrel.nmt.edu/~raymond/software/python_notes/paper004.html
  19. Systémy lineárních rovnic
    http://www.matematika.cz/systemy-linearnich-rovnic
  20. IPython homepage
    http://ipython.org/
  21. Dokumentace k IPythonu
    http://ipython.org/documentation.html#
  22. IPython Tutorial
    http://ipython.readthedocs.org/en/stable/interactive/tutorial.html
  23. The cell magics in IPython
    http://nbviewer.jupyter.org/github/ipython/ipython/blob/1.x/examples/notebooks/Cell%20Magics.ipynb
  24. 0MQ Home Page
    http://zeromq.org/
  25. 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/
  26. The IDE as a Bad Programming Language Enabler
    https://dzone.com/articles/ide-bad-programming-language
  27. Enhanced Interactive Python with IPython
    http://www.onlamp.com/pub/a/python/2005/01/27/ipython.html
  28. Direct mode
    https://en.wikipedia.org/wiki/Direct_mode
  29. Seznámení s Python IDE Spyder (článek vyšel zde na mojefedora.cz)
    http://mojefedora.cz/seznameni-s-python-ide-spyder/
  30. Stránka s popisem různých IDE pro Python
    http://quintagroup.com/cms/python/ide
  31. Eclipse (stránka o frameworku na Fedoraproject.org)
    https://fedoraproject.org/wiki/Eclipse