Hlavní navigace

Kombinace PyWebIO, Matplotlibu, Pandasu a Minia aneb webová aplikace za půl hodiny

12. 5. 2022
Doba čtení: 33 minut

Sdílet

 Autor: Fotolia - Rawpixel.com
Dnes si ukážeme, jak poměrně rychle (doslova za půl hodiny) vytvořit jednoduchou webovou aplikaci sloužící pro zobrazení naměřených dat, která jsou uložena v S3/Miniu. Kromě prostého zobrazení dat bude webová aplikace poskytovat i grafy.

Obsah

1. Kombinace PyWebIO, Matplotlibu, Pandasu a Minia aneb webová aplikace za půl hodiny

2. Formát vstupních dat

3. Zobrazení grafu s naměřenými daty i regresní přímkou

4. Načtení dat přímo do datového rámce knihovny Pandas

5. Projekt MinIO

6. Spuštění Minia v režimu serveru

7. Načtení seznamu objektů uložených v Miniu

8. Seznam datových souborů s výsledky měření

9. Načtení objektů z Minia

10. Přečtení dat uložených ve formátu CSV

11. Zobrazení grafu z dat načtených z Minia

12. Od skriptu k dynamické webové stránce s využitím PyWebIO

13. Prázdná dynamická webová stránka

14. Zobrazení seznamu senzorů

15. Popup okna v PyWebIO

16. Zobrazení popup okna s daty senzoru

17. Zobrazení popup okna s grafem

18. Závěr

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Kombinace PyWebIO, Matplotlibu, Pandasu a Minia aneb webová aplikace za půl hodiny

V úvodníku článku o knihovně PyWebIO bylo napsáno: v mnoha situacích potřebuje programátor vytvořit aplikaci s formuláři a dialogy, popř. s grafy, tedy aplikaci s GUI. Pokud používá Python, může využít již popsané knihovny Tkinter, PyObject, PyQt/PySide atd. Alternativně je ovšem možné vytvořit webovou aplikaci, a to čistě v Pythonu: bez HTML, CSS a JavaScriptu. Pro tento účel slouží knihovna PyWebIO.

Dnes si v několika krocích ukážeme vytvoření takové aplikace, přičemž funkční požadavky (odvozené z praxe) jsou ve stručnosti následující:

  1. Má se zobrazit seznam senzorů, které posílají data do S3 nebo Minia (tedy storage)
  2. U každého senzoru se zobrazí jeho jméno, datum odeslání dat a dvojice ovládacích prvků „Data“ a „Graf“
  3. Po výběru ovládacího prvku „Data“ se zobrazí popup okno s tabulkou s daty získanými senzorem (například teploměrem)
  4. Po výběru ovládacího prvku „Graf“ se zobrazí popup okno s průběhem hodnot a taktéž s regresní přímkou (resp. úsečkou)

Začneme skutečně od začátku – od tohoto primitivního skriptu:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
 
def main():
    pass
 
 
if __name__ == "__main__":
    main()

Na konci článku budeme mít prakticky plnohodnotnou webovou aplikaci určenou pro nasazení v intranetu.

2. Formát vstupních dat

Vstupní data získaná jednotlivými senzory jsou uložena ve formátu CSV (Comma Separated Values) a mají jednoduchou strukturu – první sloupec obsahuje časová razítka, druhý sloupec naměřenou hodnotu:

"Time","Value"
2020-12-01 06:21:00,30
2020-12-01 06:21:20,4
2020-12-01 06:21:40,5
2020-12-01 06:22:00,5
2020-12-01 06:22:20,5
2020-12-01 06:22:40,8
2020-12-01 06:23:00,8
2020-12-01 06:23:20,8
2020-12-01 06:23:40,11
2020-12-01 06:24:00,11
2020-12-01 06:24:20,10
2020-12-01 06:24:40,21
2020-12-01 06:25:00,21
2020-12-01 06:25:20,25
2020-12-01 06:25:40,22
2020-12-01 06:26:00,22
2020-12-01 06:26:20,23
2020-12-01 06:26:40,23
2020-12-01 06:27:00,23
2020-12-01 06:27:20,13
2020-12-01 06:27:40,15
2020-12-01 06:28:00,15
2020-12-01 06:28:20,13
2020-12-01 06:28:40,16
2020-12-01 06:29:00,16
2020-12-01 06:29:20,16
2020-12-01 06:29:40,11
2020-12-01 06:30:00,11
2020-12-01 06:30:20,22
2020-12-01 06:30:40,15
2020-12-01 06:31:00,15
2020-12-01 06:31:20,12
2020-12-01 06:31:40,18
2020-12-01 06:32:00,18
2020-12-01 06:32:20,23
2020-12-01 06:32:40,19
2020-12-01 06:33:00,19
2020-12-01 06:33:20,22
2020-12-01 06:33:40,9
2020-12-01 06:34:00,9
2020-12-01 06:34:20,9
2020-12-01 06:34:40,22
2020-12-01 06:35:00,20
2020-12-01 06:35:20,20
2020-12-01 06:35:40,17
2020-12-01 06:36:00,13
2020-12-01 06:36:20,13
2020-12-01 06:36:40,11
2020-12-01 06:37:00,16

Pro každý senzor existuje samostatný datový soubor.

3. Zobrazení grafu s naměřenými daty i regresní přímkou

S problematikou tvorby grafů s využitím knihovny Matplotlib jsme se seznámili například v článku Tvorba grafů v Jupyter Notebooku s využitím knihovny Matplotlib. Ukažme si tedy, jak lze vykreslit graf z hodnot, které jsou přečteny ze souboru CSV a zobrazeny formou základního grafu s průběhem „funkce“. Pro větší zajímavost je do grafu vložena i regresní přímka, která byla z pohledu Matplotlibu popsána zde:

#!/usr/bin/env python3
 
import sys
import csv
import numpy as np
import matplotlib.pyplot as plt
 
# Check if command line argument is specified (it is mandatory).
if len(sys.argv) < 2:
    print("Usage:")
    print("  plot_values.py input_file.csv")
    print("Example:")
    print("  plot_values.py overall.csv")
    sys.exit(1)
 
# First command line argument should contain name of input CSV.
input_csv = sys.argv[1]
 
# Try to open the CSV file specified.
with open(input_csv) as csv_input:
    # And open this file as CSV
    csv_reader = csv.reader(csv_input)
 
    # Skip header
    next(csv_reader, None)
    rows = 0
 
    # Read all rows from the provided CSV file
    data = [(row[0], int(row[1])) for row in csv_reader]
    print(data)
 
# Linear regression
time = [item[0] for item in data]
messages = [item[1] for item in data]
 
# Linear regression
x = np.arange(0, len(messages))
coef = np.polyfit(x, messages, 1)
poly1d_fn = np.poly1d(coef)
 
# Create new graph
plt.plot(messages, "b", poly1d_fn(np.arange(0, len(messages))), "y--")
 
# Title of a graph
plt.title("Sensor values")
 
# Add a label to x-axis
plt.xlabel("Time")
 
# Add a label to y-axis
plt.ylabel("Values")
 
plt.legend(loc="upper right")
 
# Set the plot layout
plt.tight_layout()
 
# And save the plot into raster format and vector format as well
plt.savefig("graph.png")
plt.savefig("graph.svg")
 
# Try to show the plot on screen
plt.show()

Výsledek může vypadat následovně:

Obrázek 1: Graf vykreslený předchozím skriptem.

4. Načtení dat přímo do datového rámce knihovny Pandas

Načtení dat lze ve skutečnosti realizovat ještě jednodušeji, a to s využitím knihovny Pandas (ta nám později nabídne i vykreslení tabulky do HTML). Malou úpravou získáme tento skript; samotný graf by měl být pro stejná vstupní data totožný:

#!/usr/bin/env python3
 
import sys
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
 
# Check if command line argument is specified (it is mandatory).
if len(sys.argv) < 2:
    print("Usage:")
    print("  plot_values_pandas.py input_file.csv")
    print("Example:")
    print("  plot_values_pandas.py overall.csv")
    sys.exit(1)
 
# First command line argument should contain name of input CSV.
input_csv = sys.argv[1]
 
# Try to open the CSV file specified.
df = pd.read_csv(input_csv)
 
# Print info about data frame
print(df.info())
print(df.describe())
 
# Linear regression
time = df["Time"]
messages = df["Value"]
 
# Linear regression
x = np.arange(0, len(messages))
coef = np.polyfit(x, messages, 1)
poly1d_fn = np.poly1d(coef)
 
# Create new graph
plt.plot(messages, "b", poly1d_fn(np.arange(0, len(messages))), "y--")
 
# Title of a graph
plt.title("Sensor values")
 
# Add a label to x-axis
plt.xlabel("Time")
 
# Add a label to y-axis
plt.ylabel("Values")
 
plt.legend(loc="upper right")
 
# Set the plot layout
plt.tight_layout()
 
# And save the plot into raster format and vector format as well
plt.savefig("graph.png")
plt.savefig("graph.svg")
 
# Try to show the plot on screen
plt.show()

Obrázek 2: Graf vykreslený předchozím skriptem.

Aby bylo později možné graf (resp. přesněji řečeno jeho rasterizovanou podobu) uložit do datového bufferu a následně poslat do webové aplikace, budeme muset skript nepatrně upravit – použijeme konstrukci fig, ax = plt.subplots() a skript se tedy změní následovně:

#!/usr/bin/env python3
 
import sys
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
 
# Check if command line argument is specified (it is mandatory).
if len(sys.argv) < 2:
    print("Usage:")
    print("  plot_values_pandas.py input_file.csv")
    print("Example:")
    print("  plot_values_pandas.py overall.csv")
    sys.exit(1)
 
# First command line argument should contain name of input CSV.
input_csv = sys.argv[1]
 
# Try to open the CSV file specified.
df = pd.read_csv(input_csv)
 
# Print info about data frame
print(df.info())
print(df.describe())
 
# Linear regression
time = df["Time"]
messages = df["Value"]
 
# Linear regression
x = np.arange(0, len(messages))
coef = np.polyfit(x, messages, 1)
poly1d_fn = np.poly1d(coef)
 
# Create a figure containing a single axes.
fig, ax = plt.subplots()
 
# Create new graph
ax.plot(messages, "b", poly1d_fn(np.arange(0, len(messages))), "y--")
 
# Title of a graph
ax.set_title("Sensor values")
 
# Add a label to x-axis
ax.set_xlabel("Time")
 
# Add a label to y-axis
ax.set_ylabel("Values")
 
ax.legend(loc="upper right")
 
# And save the plot into raster format and vector format as well
fig.savefig("sensors.png")
fig.savefig("sensors.svg")
 
plt.show()
Poznámka: nyní se tedy operace provádí s objekty nazvanými fig a ax (figure a axis).

Obrázek 3: Graf vykreslený předchozím skriptem.

Poznámka: nyní již víme, jakým způsobem bude možné zobrazit graf s hodnotami získanými ze senzorů i další potřebné údaje (regresní úsečku atd.). Následujícím krokem bude zajištění načtení hodnot uložených ve vhodném datovém úložišti, konkrétně v AWS S3 nebo ve službě Minio, které z pohledu rozhraní nabízí všechny potřebné základní operace prakticky totožné s Miniem.

5. Projekt MinIO

V této kapitole se ve stručnosti seznámíme s projektem nazvaným MinIO. Jedná se o sadu několika služeb a nástrojů, které uživatelům poskytují distribuované datové úložiště určené pro ukládání obecných (strukturovaných i nestrukturovaných) dat. Typicky se jedná o soubory používané v oblasti AI (Artifical Intelligence) a ML (Machine Learning), ovšem kromě těchto populárních (a vlastně do značné míry i módních) oblastí IT je pochopitelně možné službu MinIO použít i pro ukládání logů, souborů, k nimž je zapotřebí rychle přistupovat z mnoha různých, mnohdy vzájemně vzdálených oblastí (zde využijeme možnost distribuovaného systému), jako centrální úložiště dokumentů, obrázků, videí, pochopitelně i obrazů souborových systémů pro Docker apod. MinIO dosahuje velmi slušné rychlosti přístupu k datům (při vhodně nadimenzované síti, která je většinou limitujícím faktorem) a mj. i díky velmi dobré stabilitě ukazuje přednosti programovacího jazyka Go, v němž je celý systém naprogramován.

Jedním z nejdůležitějších a v důsledku i nejpraktičtějších vlastností projektu MinIO je fakt, že se pro přístup k datům používá stejná technologie, jaká je implementována i v populární službě Amazon S3 či možná přesněji AWS S3. To mj. znamená, že dodávaný MinIO Client SDK popsaný v navazujících kapitolách může sloužit jak pro přístup k datům uloženým v Miniu, tak i k datům uloženým ve cloudu na S3. Díky tomu lze například snadněji nastavit konfiguraci pro vývoj, konfiguraci CI, zajistit si možnost využití veřejného cloudu (S3) nebo naopak privátního cloudu (založeného na Miniu) atd. Navíc je MinIO Client SDK určen jen pro přístup k datům a nikoli pro ovládání dalších služeb, takže je jeho zahrnutí do vyvíjené aplikace méně náročné na systémové prostředky. Musíme si totiž uvědomit, že přístup k datům je mnohdy zapotřebí i z relativně málo výkonných zařízení IoT atd. (mj. i z tohoto důvodu se MinIO co do snadnosti integrace porovnává s Redisem, i když oblasti nasazení těchto dvou technologií jsou mnohdy značně odlišné).

Poznámka: zkratka S3 vznikla ze sousloví „Simple Storage Service“ a skutečně se jedná o implementaci služby, která je snadno použitelná, a to prakticky ve všech mainstreamových programovacích jazycích.

Instalace služby (přesněji řečeno serverové části) projektu MinIO je snadná a přímočará. Jelikož se jedná o aplikaci naprogramovanou v jazyce Go, je služba dodávána ve formě jediného (i když relativně objemného) spustitelného souboru. K dispozici je ovšem i obraz pro Docker, překlad lze provést ze zdrojových souborů atd. Dnes se zaměříme na první způsob, tedy na stažení již připravených souborů projektu MinIO. Musíme si pouze vybrat soubor pro právě používaný operační systém a procesorovou architekturu. Pro testování budu používat Linux a architekturu x86–64. Službu MinIO, přesněji řečeno spustitelný binární soubor, který po svém spuštění službu nabídne, získáme jednoduše jediným příkazem:

$ wget https://dl.min.io/server/minio/release/linux-amd64/minio
Poznámka: celková velikost tohoto souboru přesahuje 40 megabajtů a stahování může být relativně pomalé. Může se stát, že budete potřebovat i několik minut.

Následně je nutné nastavit příznak „x“ pro stažený soubor, aby bylo možné službu spustit přímo z příkazového řádku:

$ chmod +x minio

Dále pro jistotu otestujeme, zda je stažený soubor skutečně spustitelný:

$ ./minio version

Podobným způsobem lokálně nainstalujeme i konzoli projektu MinIO. Ta se jmenuje mc. Nejdříve stáhneme příslušný spustitelný soubor pro zvolený operační systém a architekturu mikroprocesoru:

$ wget https://dl.min.io/client/mc/release/linux-amd64/mc
Poznámka: opět se připravte na spíše pomalejší stahování.

Následně, podobně jako v předchozích krocích, nastavíme příznak „x“, aby byla konzole spustitelná:

$ chmod +x mc

A ověříme si, že tomu tak skutečně je:

$ ./mc version
 
Version: 2019-10-09T22:54:57Z
Release-tag: RELEASE.2019-10-09T22-54-57Z
Commit-id: f93fe1330a3647b1afaff0ed8c188d2897bf391e
Poznámka: jméno konzole mc může kolidovat se známým souborovým manažerem Midnight Commander. To nám však prozatím nebude příliš vadit, protože jsme provedli pouze lokální instalaci a budeme tedy rozlišovat mezi těmito příkazy:
$ mc
$ ./mc

A konečně v posledním kroku nainstalujeme balíček pro Python, který zajistí přístup k běžícímu Miniu (nebo i k S3):

$ pip3 install --user minio
 
Requirement already satisfied: minio in ./.local/lib/python3.6/site-packages
Requirement already satisfied: urllib3 in /usr/lib/python3.6/site-packages (from minio)
Requirement already satisfied: pytz in /usr/lib/python3.6/site-packages (from minio)
Requirement already satisfied: certifi in ./.local/lib/python3.6/site-packages (from minio)
Requirement already satisfied: python-dateutil in ./.local/lib/python3.6/site-packages (from minio)
Requirement already satisfied: six>=1.5 in ./.local/lib/python3.6/site-packages (from python-dateutil->minio)

6. Spuštění Minia v režimu serveru

Pokud již máme připravený spustitelný soubor nazvaný minio (viz též předchozí kapitolu), je inicializace a následné spuštění služby MinIO na lokálním počítači otázkou jediného příkazu. Musíme pouze specifikovat, že se má spustit server a na jakém disku a adresáři budou umístěny soubory spravované službou MinIO:

$ ./minio server .

Zprávy zobrazené po spuštění serveru:

API: http://192.168.1.34:9000  http://192.168.122.1:9000  http://192.168.130.1:9000  http://127.0.0.1:9000
RootUser: minioadmin
RootPass: minioadmin
 
Console: http://192.168.1.34:40747 http://192.168.122.1:40747 http://192.168.130.1:40747 http://127.0.0.1:40747
RootUser: minioadmin
RootPass: minioadmin
 
Command-line: https://docs.min.io/docs/minio-client-quickstart-guide
   $ mc alias set myminio http://192.168.1.34:9000 minioadmin minioadmin
 
Documentation: https://docs.min.io

Povšimněte si, že se po spuštění zobrazily všechny informace nutné pro spuštění webového rozhraní, pro použití konzole ovládané z příkazového řádku i pro instalaci SDK pro podporované programovací jazyky. Dále jsme získali i dvojici klíčů, které použijeme v následujících kapitolách, a to jak při přístupu přes webovou konzoli, tak i v demonstračních příkladech založených na SDK Minia.

Poznámka: konkrétní chování Minia se v různých verzích může nepatrně odlišovat od výše uvedených informací. Ovšem základní chování služby zůstává zachováno.

7. Načtení seznamu objektů uložených v Miniu

Nyní navážeme na kostru celé „aplikace“ uvedené v první kapitole a přidáme do ní základní funkcionalitu – získání a zobrazení seznamu objektů uložených v Miniu v určitém bucketu. Jedná se o relativně jednoduchou operaci, protože klient pro Minio nabízí přímo k tomu určenou metodu nazvanou list_objects, které se pouze předá jméno bucketu. Samozřejmě je ovšem nejprve nutné se připojit k Miniu se specifikací adresy a dvojice klíčů (ty lze zajistit přidáním nového uživatele přes konzoli Minia):

client = Minio(
    minio_address,
    minio_access_key,
    minio_secret_key,
    secure=False
)

Celý příklad bude po úpravách vypadat následovně:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
from minio import Minio
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
 
def main():
    client = Minio(
        minio_address,
        minio_access_key,
        minio_secret_key,
        secure=False
    )
 
    found = client.bucket_exists(bucket_name)
    print("Bucket found:", found)
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    for obj in objects:
        print(obj.bucket_name, obj.object_name, obj.last_modified,
              obj.etag, obj.size, obj.content_type)
 
 
if __name__ == "__main__":
    main()

Příklad výsledku pro náhodně přidané soubory (objekty):

$ python3 sensors_v02.py
 
Bucket found: True
sensors pyreverse 2022-05-06 14:45:33.030000+00:00 f992f0e93eaa023caa2e9a735c344fa9 1857 None
sensors storage.go 2022-05-06 14:45:28.235000+00:00 63a36e97f46ac991ebe883c569a0fd5b 40041 None

8. Seznam datových souborů s výsledky měření

Nyní do Minia přidáme několik datových souborů, jejichž jména budou odpovídat jménům senzorů. Bude se jednat o soubory ve formátu CSV obsahující dvojici sloupců. Viz též příklad uvedený ve druhé kapitole, popř. seznam souborů nabídnutých v repositáři s demonstračními příklady.

Datové soubory lze nahrát přes konzoli Minia (mc) nebo přes webovou administrační konzoli (zde ručně):

Obrázek 4: Přidání nového uživatele přes administrační konzoli.

Obrázek 5: Přidání souboru do Minia přes administrační konzoli.

Obrázek 6: Seznam souborů (objektů) uložených v Miniu.

Po opětovném spuštění příkladu z předchozí kapitoly získáme tyto výsledky (časová razítka se samozřejmě budou odlišovat):

$ python3 sensors_v02.py
 
Bucket found: True
sensors sensor_1.csv 2022-05-06 16:04:59.590000+00:00 6761c54a2d4d19342b0583a2350c2007 21181 None
sensors sensor_2.csv 2022-05-06 16:05:03.488000+00:00 cdc3fca684b7e7d3cba1a040caf6b571 1135 None
sensors sensor_3.csv 2022-05-06 16:05:06.402000+00:00 fbbfc6fa348238aecffca3b8fad32716 1507 None
sensors sensor_4.csv 2022-05-06 16:05:09.237000+00:00 e6822d4390400f5045a787c169036a1c 1089 None
sensors sensor_5.csv 2022-05-06 16:05:11.655000+00:00 5a48f56ce4057e360473007af2e82284 1338 None

9. Načtení objektů z Minia

Dalším krokem bude načtení objektů (tedy uložených dat bez jejich další interpretace) z Minia. Jedná se o rychlou operaci, která však vyžaduje dekódování dat, protože údaje poskytnuté přímo Miniem obsahují (kromě dalších věcí) i krátkou hlavičku. Ve chvíli, kdy známe jméno objektu a bucketu, v němž je objekt uložen, je získání původních dat otázka dvou řádků kódu:

response = client.get_object(bucket_name, obj.object_name)
data = response.read().decode()
...
...
...

Popř. i s kontrolou chyb a uzavřením prostředků:

try:
    response = client.get_object(bucket_name, obj.object_name)
    data = response.read().decode()
    ...
    ...
    ...
except ResponseError as err:
    print(err)
finally:
    response.close()
    response.release_conn()

Upravená verze příkladu, který získá všechna data uložená v daném bucketu, může vypadat takto:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
from minio import Minio, ResponseError
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
 
def main():
    client = Minio(
        minio_address,
        minio_access_key,
        minio_secret_key,
        secure=False
    )
 
    found = client.bucket_exists(bucket_name)
    print("Bucket found:", found)
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    for obj in objects:
        print(obj.bucket_name, obj.object_name, obj.last_modified,
              obj.etag, obj.size, obj.content_type)
        try:
            response = client.get_object(bucket_name, obj.object_name)
            data = response.read().decode()
            print(data)
        except ResponseError as err:
            print(err)
        finally:
            response.close()
            response.release_conn()
 
 
if __name__ == "__main__":
    main()

10. Přečtení dat uložených ve formátu CSV

Víme, že data získaná z Minia jsou ve skutečnosti uložena ve formátu CSV, takže je nutné je správně interpretovat. Pro tento účel použijeme knihovnu Pandas, přičemž je při převodu vhodné použít paměťový buffer realizovaný například v balíčku io.StringIO. Načtení objektu z Minia a jeho převod na datový rámec knihovny Pandas může vypadat následovně. Načtení i převod do datového rámce je realizován na pouhých tří řádcích:

response = client.get_object(bucket_name, obj.object_name)
buff = StringIO(response.read().decode())
data = pd.read_csv(buff)

Úplný zdrojový kód takto upraveného demonstračního příkladu bude vypadat následovně:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
from minio import Minio, ResponseError
import pandas as pd
from io import StringIO
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
 
def main():
    client = Minio(
        minio_address,
        minio_access_key,
        minio_secret_key,
        secure=False
    )
 
    found = client.bucket_exists(bucket_name)
    print("Bucket found:", found)
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    for obj in objects:
        print(obj.bucket_name, obj.object_name, obj.last_modified,
              obj.etag, obj.size, obj.content_type)
        try:
            response = client.get_object(bucket_name, obj.object_name)
            buff = StringIO(response.read().decode())
            data = pd.read_csv(buff)
            print(data)
        except ResponseError as err:
            print(err)
        finally:
            response.close()
            response.release_conn()
 
 
if __name__ == "__main__":
    main()

11. Zobrazení grafu z dat načtených z Minia

Nyní zkombinujeme kód, který byl ukázán ve třetí kapitole (zobrazení grafu na základě obsahu datového rámce) a taktéž v kapitole předchozí (načtení datového rámce z Minia). Skutečně se prakticky jedná o spojení obou příkladů s nepatrným refaktoringem, takže se přímo podívejme na to, jak tento příklad bude vypadat:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
from minio import Minio, ResponseError
import pandas as pd
from io import StringIO
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
 
matplotlib.use('agg')  # required, use a non-interactive backend
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
 
def create_graph(filename, df):
    # Linear regression
    time = df["Time"]
    messages = df["Value"]
 
    # Linear regression
    x = np.arange(0, len(messages))
    coef = np.polyfit(x, messages, 1)
    poly1d_fn = np.poly1d(coef)
 
    # Create a figure containing a single axes.
    fig, ax = plt.subplots()
 
    # Create new graph
    ax.plot(messages, "b", poly1d_fn(np.arange(0, len(messages))), "y--")
 
    # Title of a graph
    ax.set_title("Sensor values")
 
    # Add a label to x-axis
    ax.set_xlabel("Time")
 
    # Add a label to y-axis
    ax.set_ylabel("Values")
 
    ax.legend(loc="upper right")
 
    # And save the plot into raster format and vector format as well
    fig.savefig(filename)
 
 
def main():
    client = Minio(
        minio_address,
        minio_access_key,
        minio_secret_key,
        secure=False
    )
 
    found = client.bucket_exists(bucket_name)
    print("Bucket found:", found)
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    for obj in objects:
        print(obj.bucket_name, obj.object_name, obj.last_modified,
              obj.etag, obj.size, obj.content_type)
        try:
            response = client.get_object(bucket_name, obj.object_name)
            buff = StringIO(response.read().decode())
            df = pd.read_csv(buff)
            filename = obj.object_name[0:-4] + ".png"
            create_graph(filename, df)
        except ResponseError as err:
            print(err)
        finally:
            response.close()
            response.release_conn()
 
 
if __name__ == "__main__":
    main()

Tento skript vytvoří tolik grafů, kolik datových souborů našel v Miniu:

Obrázek 7: Druhý vykreslený graf.

Obrázek 8: Třetí vykreslený graf.

12. Od skriptu k dynamické webové stránce s využitím PyWebIO

V rámci dalších kapitol předěláme výše uvedený skript do podoby webové aplikace, která uživateli nabídne seznam senzorů a poté možnost si zobrazit příslušný graf popř. přímo data získaná senzorem. Pro tento účel použijeme knihovnu PyWebIO, s níž jsme se již na stránkách Roota seznámili.

Knihovna PyWebIO je nabízena přes PyPi, takže její instalace by měla být jednoduchá a přímočará. Knihovnu nainstalujeme pro aktuálně přihlášeného uživatele:

$ pip3 install --user pywebio
 
Collecting pywebio
  Downloading https://files.pythonhosted.org/packages/a5/bd/d4e775b6bf43dc83d1c1c596ae91c5ce58baf9199ac28b67b8886a9de8aa/pywebio-1.6.0.tar.gz (468kB)
    100% |████████████████████████████████| 471kB 200kB/s
Collecting tornado>=5.0 (from pywebio)
  Downloading https://files.pythonhosted.org/packages/01/d1/8750ad20cbcefb499bb8b405e243f83c2c89f78d139e6f8c8d800640f554/tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl (427kB)
    100% |████████████████████████████████| 430kB 874kB/s
Collecting user-agents (from pywebio)
  Downloading https://files.pythonhosted.org/packages/8f/1c/20bb3d7b2bad56d881e3704131ddedbb16eb787101306887dff349064662/user_agents-2.2.0-py3-none-any.whl
Collecting ua-parser>=0.10.0 (from user-agents->pywebio)
  Downloading https://files.pythonhosted.org/packages/9d/22/4d16b08db329fd440eed366d35e4dd7195c9babb4ecac5218f28081522a2/ua_parser-0.10.0-py2.py3-none-any.whl
Building wheels for collected packages: pywebio
  Running setup.py bdist_wheel for pywebio ... done
  Stored in directory: /home/ptisnovs/.cache/pip/wheels/fb/29/d3/09c684d68476f021ef044809643cee71560603503907dd42b3
Successfully built pywebio
Installing collected packages: tornado, ua-parser, user-agents, pywebio
Successfully installed pywebio-1.6.0 tornado-6.1 ua-parser-0.10.0 user-agents-2.2.0

13. Prázdná dynamická webová stránka

Začneme tou nejjednodušší možnou podobou webové aplikace – stránkou, v níž se pouze zobrazí informační zpráva. První varianta takové aplikace může vypadat následovně:

from pywebio import *
from pywebio.input import *
from pywebio.output import *
 
 
def main():
    put_info("Výsledky měření senzorů")
 
 
start_server(main, port=8080, debug=True)

Po spuštění tohoto skriptu by se měl spustit webový server na zadané adrese, který uživateli poskytne následující podobu webové aplikace:

Obrázek 9: Webová aplikace v její první (surové) podobě nenabízí příliš funkcí.

14. Zobrazení seznamu senzorů

V dalším kroku zobrazíme ve webové aplikaci seznam senzorů. Bude se jednat o upravený seznam objektů získaných z Minia:

objects = client.list_objects(bucket_name, recursive=False)

Tento seznam zobrazíme formou tabulky, která se vytvoří (poměrně primitivním a neidiomatickým způsobem) například takto:

table = [['Senzor', 'Datum', 'Graf', 'Info']]
 
for obj in objects:
    row = [obj.object_name,
           obj.last_modified,
           "Graf",
           "Info"]
    table.append(row)

Zobrazení tabulky je přímočaré:

put_table(table)

S tímto výsledkem:

Obrázek 10: Seznam senzorů zobrazených ve webové aplikaci.

A pro úplnost si ukažme celý zdrojový kód tohoto demonstračního příkladu:

from pywebio import *
from pywebio.input import *
from pywebio.output import *
 
from minio import Minio, ResponseError
 
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
client = Minio(
    minio_address,
    minio_access_key,
    minio_secret_key,
    secure=False
)
 
 
def main():
    put_info("Výsledky měření senzorů")
    found = client.bucket_exists(bucket_name)
 
    if not found:
        put_error("Data nelze přečíst - chybné připojení k Miniu")
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    table = [['Senzor', 'Datum', 'Graf', 'Info']]
 
    for obj in objects:
        row = [obj.object_name,
               obj.last_modified,
               "Graf",
               "Info"]
        table.append(row)
 
    put_table(table)
 
start_server(main, port=8080, debug=True)

15. Popup okna v PyWebIO

U každého senzoru by se měly objevit ovládací prvky určené pro zobrazení dat získaných senzorem popř. určené pro zobrazení grafu získaného z těchto dat. Tyto údaje se mají zobrazit v popup okně, které se v PyWebIO vytvoří zavoláním funkce popup, které se předá titulek a taktéž seznam ovládacích prvků:

def show_data(sensor):
    popup(f"Data ze senzoru {sensor}", [
        put_text(f"Data ze senzoru {sensor}"),
    ])

Musíme tedy rozšířit tabulku se seznamem vektorů o ovládací prvky, po jejichž výběru se zavolá funkce show_data popř. show_graph. To lze zajistit mnoha způsoby, například následovně:

for obj in objects:
    row = [obj.object_name,
           obj.last_modified,
           put_button("Graf", onclick=partial(show_graph, sensor=obj.object_name)),
           put_button("Data", onclick=partial(show_data, sensor=obj.object_name))]
    table.append(row)

S tímto výsledkem:

Obrázek 11: Upravený seznam senzorů s novými ovládacími prvky.

Obrázek 12: Prozatím prázdné popup okno s tabulkou dat.

Obrázek 13: Prozatím prázdné popup okno s grafem.

A pro úplnost si opět ukažme celý zdrojový kód tohoto demonstračního příkladu:

from pywebio import *
from pywebio.input import *
from pywebio.output import *
from functools import partial
 
from minio import Minio, ResponseError
 
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
client = Minio(
    minio_address,
    minio_access_key,
    minio_secret_key,
    secure=False
)
 
 
def show_data(sensor):
    popup(f"Data ze senzoru {sensor}", [
        put_text(f"Data ze senzoru {sensor}"),
    ])
 
 
def show_graph(sensor):
    popup(f"Graf pro senzor {sensor}", [
        put_text(f"Graf pro senzor {sensor}"),
    ])
 
 
def main():
    put_info("Výsledky měření senzorů")
    found = client.bucket_exists(bucket_name)
 
    if not found:
        put_error("Data nelze přečíst - chybné připojení k Miniu")
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    table = [['Senzor', 'Datum', 'Graf', 'Info']]
 
    for obj in objects:
        row = [obj.object_name,
               obj.last_modified,
               put_button("Graf", onclick=partial(show_graph, sensor=obj.object_name)),
               put_button("Data", onclick=partial(show_data, sensor=obj.object_name))]
        table.append(row)
 
    put_table(table)
 
 
start_server(main, port=8080, debug=True)

16. Zobrazení popup okna s daty senzoru

Předposlední funkcí, kterou ještě musíme implementovat, je zobrazení tabulky s daty senzoru. Samotná data načteme do datového rámce knihovny Pandas a potom datový rámec „vykreslíme“ do HTML fragmentu. Posléze již postačuje tento fragment vložit do popup okna funkcí put_html:

def show_data(bucket_name, sensor):
    response = client.get_object(bucket_name, sensor)
    buff = StringIO(response.read().decode())
    df = pd.read_csv(buff)
 
    popup(f"Data ze senzoru {sensor}", [
        put_text(f"Data ze senzoru {sensor}"),
        put_html(df.to_html(border=0)),
    ])

Obrázek 14: Zobrazení obsahu datového rámce do popup okna, které je pochopitelně plně skrolovatelné.

from pywebio import *
from pywebio.input import *
from pywebio.output import *
from functools import partial
 
from io import StringIO
from minio import Minio, ResponseError
 
import pandas as pd
 
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
client = Minio(
    minio_address,
    minio_access_key,
    minio_secret_key,
    secure=False
)
 
 
def show_data(bucket_name, sensor):
    response = client.get_object(bucket_name, sensor)
    buff = StringIO(response.read().decode())
    df = pd.read_csv(buff)
 
    popup(f"Data ze senzoru {sensor}", [
        put_text(f"Data ze senzoru {sensor}"),
        put_html(df.to_html(border=0)),
    ])
 
 
def show_graph(sensor):
    popup(f"Graf pro senzor {sensor}", [
        put_text(f"Graf pro senzor {sensor}"),
    ])
 
 
def main():
    put_info("Výsledky měření senzorů")
    found = client.bucket_exists(bucket_name)
 
    if not found:
        put_error("Data nelze přečíst - chybné připojení k Miniu")
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    table = [['Senzor', 'Datum', 'Graf', 'Info']]
 
    for obj in objects:
        row = [obj.object_name,
               obj.last_modified,
               put_button("Graf", onclick=partial(show_graph, sensor=obj.object_name)),
               put_button("Data", onclick=partial(show_data, bucket_name=bucket_name, sensor=obj.object_name))]
        table.append(row)
 
    put_table(table)
 
 
start_server(main, port=8080, debug=True)

17. Zobrazení popup okna s grafem

V poslední variantě našeho programu zajistíme vykreslení grafu s údaji získanými z vybraného senzoru do operační paměti s následným zobrazením popup okna s tímto grafem. Graf vykreslujeme do operační paměti (přesněji řečeno do bufferu alokovaného v paměti) z toho důvodu, aby nebylo nutné na straně serveru vytvářet dočasný obrázek, který by se posléze přenesl na klienta (vytváření dočasných souborů není z mnoha hledisek efektivní a ani pro to není v tomto případě žádný praktický důvod).

Prázdný graf je zkonstruován následovně:

fig, ax = plt.subplots()

Do grafu posléze vykreslíme potřebné údaje, tj. jak průběh hodnot, tak i osy, legendu, regresní úsečku atd.:

ax.plot(messages, "b", poly1d_fn(np.arange(0, len(messages))), "y--")
...
...
...

Po provedení těchto operací posléze zajistíme vykreslení grafu do paměťového bufferu, jenž bude obsahovat rastrovou reprezentaci grafu. Ihned po vykreslení grafu uvolníme paměť alokovanou pro graf (což ovšem není vždy nutné provádět):

buf = io.BytesIO()
fig.savefig(buf)
fig.clear()
plt.close(fig)

Graf, resp. přesněji řečeno rastrový obrázek s grafem, se vloží do popup okna na webové stránce příkazem put_image:

    popup(f"Graf pro senzor {name}", [
        put_text(f"Graf pro senzor {name}"),
        put_image(buf.getvalue())
    ])

S tímto výsledkem:

Obrázek 15: Jeden z grafů vybraných uživatelem a zobrazených v popup okně.

Úplný zdrojový kód poslední varianty demonstračního příkladu vypadá následovně:

skolení ELK

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
from pywebio import *
from pywebio.input import *
from pywebio.output import *
from functools import partial
 
from io import StringIO
from minio import Minio, ResponseError
 
import io
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
 
matplotlib.use('agg')  # required, use a non-interactive backend
 
bucket_name = "sensors"
minio_address = "localhost:9000"
minio_access_key = "tester"
minio_secret_key = "tester01"
 
client = Minio(
    minio_address,
    minio_access_key,
    minio_secret_key,
    secure=False
)
 
 
def read_data_frame(bucket_name, sensor):
    response = client.get_object(bucket_name, sensor)
    buff = StringIO(response.read().decode())
    return pd.read_csv(buff)
 
 
def show_data(bucket_name, sensor):
    name = sensor[0:-4]
 
    df = read_data_frame(bucket_name, sensor)
 
    popup(f"Data ze senzoru {name}", [
        put_text(f"Data ze senzoru {name}"),
        put_html(df.to_html(border=0)),
    ])
 
 
def show_graph(bucket_name, sensor):
    name = sensor[0:-4]
 
    df = read_data_frame(bucket_name, sensor)
 
    # Linear regression
    time = df["Time"]
    messages = df["Value"]
 
    # Linear regression
    x = np.arange(0, len(messages))
    coef = np.polyfit(x, messages, 1)
    poly1d_fn = np.poly1d(coef)
 
    # Create a figure containing a single axes.
    fig, ax = plt.subplots()
 
    # Create new graph
    ax.plot(messages, "b", poly1d_fn(np.arange(0, len(messages))), "y--")
 
    # Title of a graph
    ax.set_title("Sensor values")
 
    # Add a label to x-axis
    ax.set_xlabel("Time")
 
    # Add a label to y-axis
    ax.set_ylabel("Values")
 
    ax.legend(loc="upper right")
 
    # Save into buffer, not into file
    buf = io.BytesIO()
    fig.savefig(buf)
    fig.clear()
    plt.close(fig)
 
    popup(f"Graf pro senzor {name}", [
        put_text(f"Graf pro senzor {name}"),
        put_image(buf.getvalue())
    ])
 
 
def main():
    put_info("Výsledky měření senzorů")
    found = client.bucket_exists(bucket_name)
 
    if not found:
        put_error("Data nelze přečíst - chybné připojení k Miniu")
 
    objects = client.list_objects(bucket_name, recursive=False)
 
    table = [['Senzor', 'Datum', 'Graf', 'Info']]
 
    for obj in objects:
        row = [obj.object_name,
               obj.last_modified,
               put_button("Graf", onclick=partial(show_graph, bucket_name=bucket_name, sensor=obj.object_name)),
               put_button("Data", onclick=partial(show_data, bucket_name=bucket_name, sensor=obj.object_name))]
        table.append(row)
 
    put_table(table)
 
 
start_server(main, port=8080, debug=True)

18. Závěr

Z jednotlivých fragmentů, konkrétně ze skriptu pro zobrazení grafu a ze skriptu pro načtení dat z Minia, lze (s alespoň základní znalostí možností knihovny PyWebIO) sestavit celou „webovou aplikaci“ skutečně přibližně za půl hodiny (tedy mnohem rychleji, než trvalo sepsání tohoto článku :-). Nejedná se sice o zabezpečenou aplikaci určenou pro nasazení v prostředí divokého západu Internetu, ale může se jednat o základ poměrně robustní intranetové aplikace, navíc vytvořitelné skutečně pouze se znalostí Pythonu a jeho knihoven. Vyšší cíle, tj. například tvorbu webových aplikací typu Google Doc, si PyWebIO nedává.

19. Repositář s demonstračními příklady

Zdrojové kódy všech předminule, minule i dnes popsaných demonstračních příkladů určených pro programovací jazyk Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Demonstrační příklad Stručný popis příkladu Cesta
1 01_basic_output.py zobrazení běžného textu ve webovém prohlížeči https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/01_ba­sic_output.py
2 02_table.py zobrazení tabulky ve webovém prohlížeči https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/02_table.py
3 03_table.py programová příprava tabulky zobrazené ve webovém prohlížeči https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/03_table.py
4 04_table.py programová příprava tabulky zobrazené ve webovém prohlížeči https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/04_table.py
5 05_color.py podpora pro symbolické názvy barev https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/05_color.py
6 06_message.py výpis ostylovaných zpráv do webové stránky https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/06_message.py
7 07_put_html.py podpora pro výstup HTML kódu z PyWebIO https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/07_put_html­.py
8 08_put_markdown.py výpis textu (dokumentu) napsaného ve značkovacím jazyku Markdown https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/08_put_mar­kdown.py
9 09_put_code.py výpis zdrojového kódu se zvýrazněním syntaxe https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/09_put_co­de.py
10 10_progress_bar.py animované zobrazení průběhu operace https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/10_pro­gress_bar.py
11 11_loading.py informace o výpočtu či načítání stránky https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/11_loading.py
12 12_loading.py informace o výpočtu či načítání stránky https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/12_loading.py
13 13_input.py vstupní textové pole https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/13_input.py
14 14_input.py vylepšená vstupní textová pole https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/14_input.py
15 15_input_numbers.py kontrola korektnosti numerických údajů https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/15_in­put_numbers.py
16 16_input_numbers_required.py povinné vstupní parametry https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/16_in­put_numbers_required.py
17 17_radio.py přepínače https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/17_radio.py
18 18_checkbox.py výběrové boxy https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/18_chec­kbox.py
19 19_actions.py skupina akcí https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/19_actions.py
20 20_slider.py interaktivní posuvník https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/20_slider.py
       
21 21_more_inputs.py postupné zadávání údajů v na sebe navazujících vstupních prvcích https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/21_mo­re_inputs.py
22 22_more_inputs.py seskupení ovládacích prvků do jediného formuláře https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/22_mo­re_inputs.py
23 23_input_validators.py specifikace typů vstupních dat ve vstupních formulářích https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/23_in­put_validators.py
24 24_better_validators.py tvorba a využití vlastních validátorů ve vstupních formulářích https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/24_bet­ter_validators.py
25 25_popup.py zobrazení vyskakovacího okna https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/25_popup.py
26 26_popup.py zobrazení vyskakovacího okna https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/26_popup.py
27 27_rows.py zarovnání prvků do sloupců, řádků a mřížek https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/27_rows.py
28 28_style.py změna stylu zobrazení ovládacích prvků https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/28_style.py
29 29_collapse.py sady prvků, které je možné skrýt https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/29_co­llapse.py
30 30_image.py zobrazení rastrového obrázku přečteného ze specifikovaného zdroje https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/30_image.py
31 31_mandelbrot.py výpočet a zobrazení Mandelbrotovy množiny https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/31_man­delbrot.py
32 32_fm_synth.py interaktivní změna parametrů výpočtu https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/32_fm_syn­th.py
32 server1 Server s několika aplikacemi, první řešení https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/server1
33 server2 Server s několika aplikacemi, druhé řešení https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/server2
       
34 plot_values.py načtení CSV s následným vykreslením grafu https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/plot_va­lues.py
35 plot_values_pandas.py použití knihovny Pandas pro načtení grafu https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/plot_va­lues_pandas.py
36 plot_values_pandas2.py vylepšené vykreslení grafu https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/plot_va­lues_pandas2.py
       
37 sensors_v01.py kostra příkladu https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v01.py
38 sensors_v02.py seznam datových souborů s výsledky měření https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v02.py
39 sensors_v03.py načtení objektů z Minia https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v03.py
40 sensors_v04.py přečtení dat uložených ve formátu CSV https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v04.py
41 sensors_v05.py zobrazení grafu z dat načtených z Minia https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v05.py
42 sensors_v06.py prázdná dynamická webová stránka https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v06.py
43 sensors_v07.py zobrazení seznamu senzorů https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v07.py
44 sensors_v08.py popup okna v PyWebIO https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v08.py
45 sensors_v09.py zobrazení popup okna s daty senzoru https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v09.py
46 sensors_v10.py zobrazení popup okna s grafem https://github.com/tisnik/most-popular-python-libs/blob/master/pywebio/sen­sors_v10.py

20. Odkazy na Internetu

  1. Low code Python web framework
    https://www.pyweb.io/
  2. Repositář projektu
    https://github.com/pywebio/PyWebIO/
  3. Getting Started
    https://www.pyweb.io/tutorial.html
  4. Dokumentace
    https://pywebio.readthedoc­s.io/en/latest/
  5. Why PyWebIO?
    https://github.com/pywebi­o/PyWebIO/wiki/Why-PyWebIO%3F
  6. PyWebIO demos
    https://pywebio-demos.pywebio.online/
  7. PyWebIO Chart Gallery
    https://pywebio-charts.pywebio.online/
  8. Awesome Python
    https://awesome-python.com/
  9. A complete guide to web development in Python
    https://www.educative.io/blog/web-development-in-python
  10. Python Web Development Tutorials
    https://realpython.com/tutorials/web-dev/
  11. What is Flask Python
    https://pythonbasics.org/what-is-flask-python/
  12. CherryPy
    https://cherrypy.dev/
  13. Projekt Zenity
    https://wiki.gnome.org/Pro­jects/Zenity
  14. Nástroj Dialog
    http://invisible-island.net/dialog/
  15. Plotly
    https://plotly.com/
  16. Bokeh
    https://bokeh.org/
  17. pyecharts
    https://github.com/pyechar­ts/pyecharts/blob/master/RE­ADME.en.md
  18. Tvorba grafů v Jupyter Notebooku s využitím knihovny Matplotlib
    https://www.root.cz/clanky/tvorba-grafu-v-jupyter-notebooku-s-vyuzitim-knihovny-matplotlib/
  19. Alternatives to PyWebIO
    https://stackshare.io/pywe­bio/alternatives
  20. The fastest way to build and share data apps – Streamlit
    https://streamlit.io/
  21. Dash Enterprise
    https://plotly.com/dash/
  22. pglet
    https://pglet.io/
  23. Stránky projektu MinIO
    https://min.io/
  24. MinIO Quickstart Guide
    https://docs.min.io/docs/minio-quickstart-guide.html
  25. MinIO Go Client API Reference
    https://docs.min.io/docs/golang-client-api-reference
  26. Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
    https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/
  27. Benchmarking MinIO vs. AWS S3 for Apache Spark
    https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/
  28. MinIO Client Quickstart Guide
    https://docs.min.io/docs/minio-client-quickstart-guide.html
  29. Analýza kvality zdrojových kódů Minia
    https://goreportcard.com/re­port/github.com/minio/minio
  30. This is MinIO
    https://www.youtube.com/wat­ch?v=vF0lQh0XOCs
  31. Running MinIO Standalone
    https://www.youtube.com/wat­ch?v=dIQsPCHvHoM
  32. „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
    https://www.youtube.com/wat­ch?v=wlpn8K0jJ4U