Command-line interface¶
Gebruikersomgevingen¶
Vanaf de jaren '60 van de vorige eeuw werden computers interactief. Het was mogelijk om via een terminal commando's aan de computer te geven en te wachten op een antwoord. In tegenstelling tot moderne gebruikersomgevingen waren deze volledig op tekst gebaseerd. Hoewel moderne besturingssystemen — of het nu computers, tablets of mobiele telefoons betreft — volledig grafisch zijn ingericht, is de tekstuele interface nooit verdwenen. Opdrachten geven door te typen is gewoon best wel handig en snel. Ook is het veel eenvoudiger om applicaties te ontwikkelen zonder grafische interface.
Op ieder besturingssysteem — Linux, MacOS, Windows — is een shell, terminal of command prompt te vinden. Als je die opstart kun je op de zogeheten command line opdrachten intypen. Veelal zijn dit commando's om het bestandssysteem te navigeren en programma's op te starten.
Wanneer je in Visual Studio Code een Python script start dan opent het een terminal onderin het scherm.
Commando's¶
Je hebt tot nu toe al heel wat commando's in de terminal getypt. Laten we een paar voorbeelden bestuderen:
Als eerste vertel je welke applicatie je wilt gaan starten; in dit geval:python
. Daarna geef je met het argument script.py
aan welk Pythonscript je wilt uitvoeren. Vaak kun je ook opties meegeven zoals in:
(ecpc) > python -V
Python 3.10.13
Hiermee vraag je Python om het versienummer weer te geven. Soms kunnen opties zelf weer een argument meekrijgen. Bijvoorbeeld:
Met deze regel geef je Python de optie-m
en die importeert een module (hier antigravity
) en voert die uit. Probeer maar eens zelf wat er gebeurt als je dat commando uitvoert.
Als applicaties veel verschillende functionaliteit hebben dan krijg je regelmatig te maken met een lange regel met een combinatie van argumenten en opties:
Uitgesplitst in argumenten en opties, met vierkante haken [] om aan te geven welke onderdelen bij elkaar horen, is dat:conda create [--name pythondaq] [-channel conda-forge] [python pyvisa-py]
Poetry argumenten
- Naast
conda create
heb je ook met andere argumenten gewerkt zoalsactivate
eninstall
. Welke argumenten ken je al van de applicatiepoetry
? - Vraag de lijst met argumenten (commando's) op van Poetry met
poetry list
, hoeveel kende je nog niet?
Conda opties en argumenten
- Open een
Anaconda Prompt
- Maak gebruik van de optie -h om de helpfunctie van conda op te vragen. (
conda -h
) - Zoek de optie op om de conda versie weer te geven (
conda -V
) - Maak gebruik van de optie -h om de helpfunctie van het commando activate op te vragen (
conda activate -h
) - Welke argumenten moet je meegeven (positional arguments?) en welke opties mag je meegeven (optional arguments?) (argument: env_name_or_prefix, opties: --help, --stack, --no-stack)
Click¶
Als we gebruik willen maken van commando's in onze eigen applicatie moeten we weten wat de gebruiker in de terminal typt. Dit is mogelijk met sys.argv
.1
Waarbij alles wat we in de terminal typen aan input wordt meegegeven:
Met if-statements kunnen we acties verbinden aan bepaalde argumenten:
import sys
args = sys.argv
print(args)
if args[1] == "test":
print(f"This is a test: {args[2]}")
else:
print(f"CommandNotFoundError: No command '{args[1]}'.")
Als je meerdere opties en argumenten meegeeft dan wordt het veel werk om die in je script uit elkaar te plukken en ze goed te interpreteren. Om dat makkelijker te maken zijn er verschillende bibliotheken beschikbaar — waaronder een paar in de standard library van Python. Een hele handige — die níet in de standard library van Pythhon zit maar wél meegeleverd is met de base environment van Anaconda — is Click.5
Info
Click maakt gebruik van decorators (@decorator
). Om decorators te gebruiken, hoef je niet per se te weten hoe ze werken. Als je meer wilt weten over de werking ervan kijk dan de calmcode tutorial of lees de Primer on Python Decorators.
Als kort voorbeeld — geïnspireerd op de documentatie van Click — nemen we het volgende script:
Dit script print de uitdrukking "Hello physicist!". We gaan dit aanpassen en maken het mogelijk om de naam en het aantal begroetingen te kiezen. Hiervoor gebruiken we Click. Allereerst moeten we click
importeren en aangeven dat we de hello()
-functie willen gebruiken als commando:
import click
@click.command()
def hello():
print("Hello physicist!")
if __name__ == "__main__":
hello()
Dit levert ons nog niet zoveel op, maar op de achtergrond is click wel degelijk aan het werk. De @click.command()
houdt in de gaten wat er in de command line wordt ingetypt. Zo kunnen we de helpfunctie aanroepen door --help
achter de naam van het script te zetten.
Help functie
hello.py
over. Je vraagt de helpfunctie van het script op. Je ziet een helptekst verschijnen. Je vraagt je af wat er gebeurt als je @click.command()
weg haalt en dan de helpfunctie opvraagt. Je krijgt gewoon de output van de functie hello()
een geen help tekst.
ECPC
├──
oefenopdrachten
├──
hello.py
└── •••
├──
pythondaq
└── •••
Pseudo-code
import click
# make function Click command
# function
# print hello physicist!
# when run this script:
# run function
(ecpc) > python hello.py --help
Usage: hello.py [OPTIONS]
Options:
--help Show this message and exit.
Checkpunten:
- Je vraagt de helpfunctie op door
--help
achter de bestandsnaam te zetten in de terminal. - Er verschijnt een standaard helptekst.
- Zonder
@click.command()
verschijnt er geen helptekst, maar de output van de functie.
Projecttraject:
- Help functie
- Argumenten toevoegen
- Test hello
- Helptekst toevoegen
- Pauze optie
In de code hieronder geven we met de regel @click.argument("name")
aan dat we van de gebruiker een argument verwachten. Zorg dat het argument ook gebruikt wordt in de functie hello
:
import click
@click.command()
@click.argument("name")
def hello(name):
print(f"Hello {name}!")
if __name__ == "__main__":
hello()
Argument toevoegen
Je runt het bestand hello.py
en geeft achter de bestandsnaam de naam Alice
mee. Er verschijnt Hello Alice!
als output in de terminal.
Pseudo-code
import click
# make function Click command
# make argument name
# function, parameter name
# print hello <name>!
# when run this script:
# run function
(ecpc) > python hello.py
Usage: hello.py [OPTIONS] NAME
Try 'hello.py --help' for help.
Error: Missing argument 'NAME'.
Checkpunten:
- Het draaien van
hello.py
zonder een argument:python hello.py
geeft een foutmelding. - Het draaien van
hello.py
met een argument:python hello.py Alice
werkt zoals verwacht.
Projecttraject:
- Help functie
- Argumenten toevoegen
- Test hello
- Helptekst toevoegen
- Pauze optie
Warning
Let er op dat je bij @click.argument
de naam meegeeft die overeenkomt met de namen van de parameters van je functie. In ons geval hebben we een argument "name"
. Dit moet overeenkomen met de functiedefinitie def hello(name)
.
Argumenten zijn altijd verplicht en moeten in een vaste volgorde staan. Bij opties is dat anders. Je geeft met mintekens aan dat je een optie meegeeft. Veel opties hebben een lange naam en een afkorting (bijvoorbeeld --count
en -c
). Opties kunnen zelf weer een argument hebben (bijvoorbeeld --count 3
). Het is handig om een standaardwaarde te definiëren. In dat geval mag de gebruiker de optie weglaten. We voegen een for-loop2 toe om de begroeting te herhalen.
import click
@click.command()
@click.argument("name")
@click.option(
"-c",
"--count",
default=1,
)
def hello(name, count):
for _ in range(count):
print(f"Hello {name}!")
if __name__ == "__main__":
hello()
5 keer hello
Je runt het bestand hello.py
en geeft achter de bestandsnaam de naam van je assistent mee en geeft aan dat je deze 5 keer wilt printen. Er verschijnt vijf keer Hello <assistent>!
als output in de terminal.
Pseudo-code
import click
# make function Click command
# make argument name
# make option count with default value 1
# function, parameter name and count
# repeat count times
# print hello <name>!
# when run this script:
# run function
(ecpc) > python hello.py David -c 5
Hello David!
Hello David!
Hello David!
Hello David!
Hello David!
Checkpunten:
- Je kan de naam van je assistent 5 keer printen met één commando
- Je kan het aantal keer printen opgeven met
-c
. - Je kan het aantal keer printen ook opgeven met
--count
. - Wanneer de optie
count
wordt weggelaten wordt de naam 1 keer geprint. -
Wanneer er geen argument wordt meegegeven met
count
volgt een foutmelding:(ecpc) > python hello.py David -c Error: Option '-c' requires an argument.
Projecttraject:
- Help functie
- Argumenten toevoegen
- Test hello
- Helptekst toevoegen
- Pauze optie
Warning
Let er op dat je bij @click.option
de afkorting met 1 minteken meegeeft en de lange naam met 2 mintekens. De lange naam moet overeenkomen met de paramater van je functie. In ons geval hebben we een optie "--count"
— de lange naam telt. Dit moet overeenkomen met de functiedefinitie def hello(name, count)
.
Het is handig om een korte helptekst toe te voegen. Dit gaat als volgt:
import click
@click.command()
@click.argument("name")
@click.option(
"-c",
"--count",
default=1,
help="Number of times to print greeting.",
show_default=True, # show default in help
)
def hello(name,count):
for _ in range(count):
print(f"Hello {name}!")
if __name__ == "__main__":
hello()
Helptekst toevoegen
Voeg de helptekst toe en vraag de helptekst op zoals in de opdracht Help functie.
Als je dit script gebruikt ziet dat er zo uit:
(ecpc) > python hello.py --help
Usage: hello.py [OPTIONS] NAME
Options:
-c, --count INTEGER Number of times to print greeting. [default: 1]
--help Show this message and exit.
(ecpc) > python hello.py Alice
Hello Alice!
(ecpc) > python hello.py Alice -c 2
Hello Alice!
Hello Alice!
(ecpc) > python hello.py Alice --count 3
Hello Alice!
Hello Alice!
Hello Alice!
Pauze optie
Je runt het bestand hello.py
en geeft achter de bestandsnaam de naam van je assistent mee en geeft aan dat je deze 5 keer wilt printen met een pauze van 2 seconde ertussen. Het duurt 8 seconden voordat er vijf keer Hello <assistent>!
als output in de terminal staat. Als je geen pauze-optie meegeeft wordt er ook geen pauze gehouden.
Info
Je kan hiervoor gebruik maken van de module time die standaard met Python meekomt3. Met de functie sleep()
kun je de executie van de volgende regel in het script met een aantal seconden uitstellen.
Pseudo-code
import click
# make function Click command
# make argument name
# make option count with default value 1
# make option pause
# function, parameter name and count
# repeat count times
# print hello <name>!
# pause
# when run this script:
# run function
(ecpc) > python hello.py David -c 5
Hello David!
Hello David!
Hello David!
Hello David!
Hello David!
Checkpunten:
- Als de pauze optie niet wordt meegegeven, dan wordt er géén pauze ingelast
- Bij het meegeven van de pauze optie, wacht het programma zo lang als verwacht
Projecttraject:
- Help functie
- Argumenten toevoegen
- Test hello
- Helptekst toevoegen
- Pauze optie
Opties zonder argument werken als vlag — een soort aan/uitknop.4
Vlag
Gebruik een optie als vlag om de gebruiker te laten kiezen tussen het wel (tea
) of niet (no tea
) aanbieden van een kopje thee. Zorg dat er standaard tea
wordt aangeboden.
Argumenten en opties
Je opent met Github Desktop de just_count
in Visual Studio Code. Je hebt ooit een environment voor deze repository aangemaakt maar je hebt geen idee of die in de tussentijd niet per ongeluk stuk is gegaan. Daarom maak je een nieuwe environment just_count
met daarin Python en gebruik je Poetry om het pakket just_count
in de nieuwe omgeving te installeren .
Je activeert het juiste conda environment en past de code aan zodat met het commando square 6
het kwadraat van 6 in de terminal wordt geprint.
Info
Click maakt van alle argumenten een string, tenzij je een default waarde of een type definieert. Gebruik type=int
, type=float
enzovoorts om aan te geven wat voor type object het argument moet worden
Meer functies
- Pas de applicatie aan zodat je kan kiezen tussen het kwadraat of de wortel van het getal.
Pseudo-code
import square
# Add functionality to select a number via click and print its square
def main():
print(f"The square of 5 is {square.square(5)}")
if __name__ == '__main__':
main()
(ecpc) > square 6
The square of 6 is 36
Checkpunten:
- Je hebt
poetry install
in een schone environment (met alleen Python) gedaan. - Je hebt de juiste omgeving geactiveerd.
- Je kan zelf het getal kiezen met het commando en krijgt het verwachte antwoord terug
Projecttraject
- Main functie toevoegen
- commando toevoegen
- Commando testen
- Argumenten en opties
Click subcommando's¶
Tot nu toe konden we maar één functie uitvoeren in onze applicatie. Maar het is ook mogelijk om subcommando's aan te maken zodat je met één programma meerdere taken
kunt uitvoeren. Denk bijvoorbeeld aan conda
. Je installeert packages met conda install
, verwijdert ze met conda remove
, maakt een environment met conda create
en activeert het met conda activate
.
Subcommando's bedenken
Je gaat depythondaq
applicatie straks verder uitbreiden zodat er veel meer mogelijk is dan nu. Wat zou je willen dat de applicatie allemaal kan? Welke subcommando's wil je gaan aanmaken? Overleg met elkaar om goede ideeën uit te wisselen.
Een eenvoudig voorbeeldscript waarin de conda commando's install
en remove
worden nagebootst leggen we hieronder uit. Eerst de code:
fake_conda.py | |
---|---|
cmd_group()
genoemd hebben en die we bovenaan definiëren. In tegenstelling tot het hello.py
-script doet deze functie helemaal niets (pass
). We vertellen aan click dat we een groep van commando's aan gaan maken met de @click.group()
-decorator in regel 3. Vervolgens gaan we commando's binnen deze groep hangen door niet de decorator @click.command()
te gebruiken, maar @cmd_group.command()
— zie regels 7 en 12. De namen van de commando's die worden aangemaakt zijn de namen van de functies. Dus regel 7 en 9 maken samen het commando install
. Verder werkt alles hetzelfde. Dus een argument toevoegen — zoals in regel 8 — is gewoon met @click.argument()
. Hier hoef je geen cmd_group
te gebruiken.
Fake conda
ECPC
een nieuw Poetry project aan voor fake_conda
en zet daarin de code uit het bestand fake_conda.py
. Je past de pyproject.toml
aan zodat je met het commando fake_conda install scipy
zogenaamd scipy
kunt installeren .
ECPC
├──
fake_conda
├──
src/fake_conda
├──
__init__.py
└──
fake_conda.py
└──
pyproject.toml
└── •••
├──
oefenopdrachten
├──
pythondaq
└── •••
commando
Als je een commando met Poetry toevoegt dan heeft dat de opbouw naam_commando = "package.module:naam_functie"
, welke functie moet uitgevoerd worden als je het commando aanroept?
Pseudo-code
Testcode(ecpc) > fake_conda install scipy
Installing scipy...
Checkpunten:
- In de pyproject.toml verwijst
[tool.poetry.scripts]
naar een functie zodatinstall
enremove
subcommando's zijn. - Het commando
fake_conda install scipy
print de tekstInstalling scipy...
als output in de terminal.
Projecttraject
- Fake conda
Smallangle (meer leren)
Met deze opdracht kun je testen hoe goed je het Python-jargon onder de knie hebt. Je zult het woord smallangle
zó vaak tegenkomen dat het je duizelt — maar jij weet precies over welk onderdeel we het hebben.
- Maak een nieuw poetry project (met een
src
indeling) aan met de naamsmallangle
. - Let op de Octocat voor
smallangle
, het moet dus een repository zijn (of worden). - Maak een nieuw environment die
smallangle
heet met daarin alleen Python . - Zet in het package
smallangle
een modulesmallangle.py
. - Plak de onderstaande code in
smallangle.py
:import numpy as np from numpy import pi import pandas as pd def sin(number): x = np.linspace(0, 2 * pi, number) df = pd.DataFrame({"x": x, "sin (x)": np.sin(x)}) print(df) def tan(number): x = np.linspace(0, 2 * pi, number) df = pd.DataFrame({"x": x, "tan (x)": np.tan(x)}) print(df) if __name__ == "__main__": sin(10)
- Ga door naar de opdracht smallangle aanpassen. Je mag de opdracht smallangle installeren overslaan — dat werk heb je nu zelf al gedaan.
smallangle installeren
smallangle
van AnneliesVlaar/smallangle
door de repository in GitHub desktop te openen. Daarna open je het project in Visual Studio Code. Na het installeren van het project in een nieuwe conda environment run je het bestand smallangle.py
en krijg je een lijst van 10 punten tussen 0 en 2 $\pi$ en de sinus van deze punten.
ECPC
├──
pythondaq
└──
smallangle
└── •••
└── •••
Testcode
import numpy as np
from numpy import pi
import pandas as pd
def sin(number):
x = np.linspace(0, 2 * pi, number)
df = pd.DataFrame({"x": x, "sin (x)": np.sin(x)})
print(df)
def tan(number):
x = np.linspace(0, 2 * pi, number)
df = pd.DataFrame({"x": x, "tan (x)": np.tan(x)})
print(df)
if __name__ == "__main__":
sin(10)
(ecpc) > python smallangle.py
x sin (x)
0 0.000000 0.000000e+00
1 0.698132 6.427876e-01
2 1.396263 9.848078e-01
3 2.094395 8.660254e-01
4 2.792527 3.420201e-01
5 3.490659 -3.420201e-01
6 4.188790 -8.660254e-01
7 4.886922 -9.848078e-01
8 5.585054 -6.427876e-01
9 6.283185 -2.449294e-16
Checkpunten:
- Het project is geïnstalleerd in een nieuwe conda environment.
- Na het installeren van het pakket geeft de code de verwachte output.
Projecttraject:
- smallangle installeren
- smallangle aanpassen
- smallangle docstrings
smallangle aanpassen
Je kunt met het commando smallangle
en de subcommando's sin
en tan
een lijst genereren van getallen tussen de 0 en 2 $\pi$ en de bijbehorende sinus dan wel tangens van deze getallen. Met de optie -n
kan je het aantal stappen (het aantal $x$-waardes tussen 0 en $2\pi$) kiezen. Als je de optie -n
weglaat werkt de applicatie met een standaardwaarde.
TypeError: 'int' object is not iterable
Probeer je de code te draaien maar krijg je een foutmelding zoals deze:
Traceback (most recent call last):
File "c:\smallangle\src\smallangle\smallangle.py", line 28, in <module>
sin(10)
File "C:\click\core.py", line 1157, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\click\core.py", line 1067, in main
args = list(args)
^^^^^^^^^^
TypeError: 'int' object is not iterable
Dan komt dat doordat je sin(10)
probeert uit te voeren, terwijl de functie al verClickt is. De functie verwacht een argument vanuit de terminal en geen integer vanuit het pythonscript.
Pas je script aan zodat if __name__ == "__main__":
naar de juiste functie verwijst en Click aanroept; niet sin(10)
.
(ecpc) > smallangle sin -n 9
x sin (x)
0 0.000000 0.000000e+00
1 0.785398 7.071068e-01
2 1.570796 1.000000e+00
3 2.356194 7.071068e-01
4 3.141593 1.224647e-16
5 3.926991 -7.071068e-01
6 4.712389 -1.000000e+00
7 5.497787 -7.071068e-01
8 6.283185 -2.449294e-16
Checkpunten:
- De gebruiker kan met subcommando's kiezen tussen
sin
entan
. - De gebruiker kan het aantal stappen kiezen met een optie.
- De gebruiker kan de optie ook weglaten.
Projecttraject:
- smallangle installeren
- smallangle aanpassen
- smallangle docstrings
Smallangle (uitdaging)
Met het commando approx
en een argument $\epsilon$ moet het script de grootste hoek geven waarvoor nog geldt dat $\lvert x - \sin(x) \rvert \leq \epsilon$, ofwel de grootste hoek waarvoor de kleine-hoekbenadering nog geldt met de opgegeven nauwkeurigheid. Doe dit op drie cijfers nauwkeurig (loop over .000, .001 en .002, etc. totdat de vergelijking niet meer geldt). N.B. besteed geen tijd aan het analytisch oplossen van de vergelijking. Een voorbeeld van de uitvoer:
(ecpc) > smallangle approx .1
For an accuracy of 0.1, the small-angle approximation holds
up to x = 0.854.
Docstrings en Click --help
¶
Docstrings werken ook heel handig samen met Click want ze worden gebruikt als we de helpfunctie aanroepen.
Info
We gebruiken bij click-functies niet de standaard structuur voor docstrings. Click breekt de docstrings standaard af waardoor het algauw een onogelijke brij aan informatie wordt. We kiezen daarom voor een samenvatting in een zin met daarin de PARAMETERS (argumenten en/of opties) in hoofdletters en eventueel een korte toelichting daarop.
Uitgebreide documentatie en Click
In de documentatie van Click vind je meer informatie over het afbreken van zinnen (en het voorkomen daarvan). Ook vind je daar een manier om een uitgebreide docstring te schrijven zonder dat het een bende wordt.
We voegen docstrings toe aan fake-conda:
import click
@click.group()
def cmd_group():
pass
@cmd_group.command()
@click.argument("package")
def install(package):
"""Install a conda PACKAGE.
PACKAGE is the name of the package.
"""
print(f"Installing {package}...")
@cmd_group.command()
@click.argument("package")
def remove(package):
"""Remove a conda PACKAGE.
PACKAGE is the name of the package.
"""
print(f"Removing {package}...")
if __name__ == "__main__":
cmd_group()
(ecpc) > python fake_conda.py --help
Usage: fake_conda.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
install Install a conda PACKAGE.
remove Remove a conda PACKAGE.
Daarna kun je uitleg vragen voor de subcommando's waarbij je de hele docstring te zien krijgt:
(ecpc) > python fake_conda.py install --help
Usage: fake_conda.py install [OPTIONS] PACKAGE
Install a conda PACKAGE.
PACKAGE is the name of the package.
Options:
--help Show this message and exit.
Smallangle docstrings
Je gebruikt het commando smallangle --help
en leest de helptekst van de opdracht smallangle. De helptekst bevat zinvolle informatie die je in staat stelt om te begrijpen wat je met de applicatie kan doen. Je ziet dat er twee subcommando's zijn en bekijkt de helptekst van deze commando's met smallangle sin --help
en daarna smallangle tan --help
. Beide helpteksten stellen je in staat op de applicatie te begrijpen en te bedienen. Tevreden test je de applicatie verder uit.
Pseudo-code
Testcode(ecpc) > smallangle --help
Usage: smallangle [OPTIONS] COMMAND [ARGS] ...
Options:
--help Show this message and exit.
Commands:
Subcommand Summary containing ARGUMENTs.
Checkpunten:
-
smallangle --help
geeft zinvolle informatie -
smallangle sin --help
geeft zinvolle informatie -
smallangle tan --help
geeft zinvolle informatie
Projecttraject
- smallangle installeren
- smallangle aanpassen
- smallangle docstrings
Command-line interface voor ons experiment¶
In hoofdstuk Model-View-Controller heb je pythondaq
uitgesplitst in model, view en controller. Wanneer we een command-line interface gaan bouwen dan is dat de softwarelaag tussen de gebruiker en de rest van de code. De command-line interface is dus een view. Het is helemaal niet gek om meerdere views te hebben, bijvoorbeeld een eenvoudig script zoals view.py
, een command-line interface en een grafische interface. Hier gaan we ons richten op een command-line interface. We gaan een nieuw bestand cli.py
aanmaken en dat langzaam opbouwen.
Pythondaq: commando's
src/pythondaq/cli.py
een opzetje maken waar je stap voor stap functionaliteit aan toevoegt. De oude view.py
maakte eerst een lijst van aangesloten apparaten en daarna werd een scan uitgevoerd. Daarom zet je in cli.py
de subcommando's list
en scan
. Je test de subcommando's en je ziet dat ze werken omdat ze een stukje tekst printen.
ECPC
├──
pythondaq
├──
src/pythondaq
├──
__init__.py
├──
arduino_device.py
├──
diode_experiment.py
├──
view.py
└──
cli.py
└── •••
└── •••
Warning
Omdat de naam van een subcommando gelijk is aan de functienaam kan dat voor problemen zorgen wanneer je gereserveerde namen van python wilt gebruiken zoals: import
, return
, lambda
. Of wanneer je de naam van het subcommando graag hetzelfde wilt hebben als een ander pythonfunctie zoals sin
of list
.
Een oplossing is om de functienaam aan te passen en de subcommando naam expliciet aan click mee te geven bij command
:
@cmd_group.command("import")
@click.argument("package")
def import_package(package):
print(f"import {package}...")
import
aangemaakt — niet een commando import_package
.
Pseudo-code
# subcommando list
# print Work in progress, list devices
# subcommando scan
# print Work in progress, scan LED
(ecpc) > diode list
Work in progress, list devices
(ecpc) > diode scan
Work in progress, scan LED
Checkpunten:
- De applicatie is aan te roepen met
diode
. - Het subcommando
list
print een stukje tekst. - Het subcommando
scan
print een ander stukje tekst.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Het uitvoeren van een meetserie¶
We gaan ons eerst richten op het uitvoeren van een volledige meetserie en het tonen van de resultaten daarvan aan de gebruiker.
Info
Bij het opgeven van argumenten en opties voor de spanning kan het belangrijk zijn om te controleren of de spanning überhaupt wel een getal is tussen 0 en 3.3 V. Je kunt dit doen door de type
-parameter in @click.argument()
en @click.option()
. Je kunt een Pythontype opgeven (bijvoorbeeld: type=int
of type=float
) en Click heeft speciale types zoals type=click.FloatRange(0, 3.3)
voor een kommagetal tussen 0 en 3.3. Bekijk alle speciale types in de Click documentatie. Als je hiervan gebruik maakt hoef je niet zelf te controleren of de parameters kloppen. Click doet dat voor je.
Pythondaq: scan
Je voert het commando diode scan
uit, met argumenten of opties is het mogelijk om het spanningsbereik (in Volt) aan te passen. Er wordt een meting gestart die binnen het spanningsbereik blijft. De stroomsterkte dóór en de spanning óver de LED worden in de terminal geprint. Het is ook mogelijk om de metingen op te slaan als CSV-bestand. Dit kan met de optie --output FILENAME
. Wanneer met die optie een bestandsnaam wordt meegegeven worden de metingen opgeslagen en anders niet.
Pseudo-code
Checkpunten:
- Het programma print een lijst van metingen van de stroomsterkte dóór en de spanning óver de LED.
- De gebruiker moet het spanningsbereik (in volt) zelf kunnen opgeven met argumenten of opties.
- De gebruiker kan de metingen opslaan in een CSV-bestand met een optie
--output FILENAME
. - De meting wordt alleen opgeslagen als de optie wordt meegegeven.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Pythondaq: herhaalmetingen
Je kunt met een optie het aantal herhaalmetingen kiezen. Je test de optie met 1, 3 en 10 metingen en ziet de onzekerheid op de stroomsterkte en de onzekerheid op de spanning over de LED afnemen wanneer het aantal metingen toenneemt.
Pseudo-code
Checkpunten:
- De gebruiker kan het aantal herhaalmetingen met een optie kiezen.
- De herhaalmetingen worden gebruikt om de beste schatting en onzekerheid te berekenen van de stroomsterkte en de spanning.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Het meetinstrument kiezen¶
We kunnen de Arduino benaderen als we de naam weten die de VISA driver er aan heeft toegekend. Helaas kan — ook afhankelijk van het besturingssysteem — die naam veranderen als we de Arduino in een andere poort van onze computer steken of soms zelfs als we een andere Arduino op dezelfde poort koppelen. Met het commando list
laten we alle apparaten zien die gevonden worden door de VISA drivers.
Pythondaq: list
Met het subcommando list
wordt een lijst gegeven van aangesloten instrumenten.
(ecpc) > diode list
('ASRL28::INSTR','ASRL5::INSTR')
Checkpunten:
- De gebruiker kan met
diode list
de lijst met aangesloten devices opvragen.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Pythondaq: info
Nadat je met het subcommando list
een lijst hebt van aangesloten devices wil je weten of de Arduino aan een bepaalde poortnaam is gekoppeld. Je voert het subcommando info DEVICE
uit waarna de identificatiestring van het instrument wordt geprint.
identificatiestring
De identificatiestring van onze Arduino was Arduino VISA firmware v1.0.0
. Je moet natuurlijk niet letterlijk deze string copy/pasten, maar de identificatie opvragen van het instrument. Welk firmwarecommando moest je daarvoor ook alweer gebruiken?
Pseudo-code
Testcode(ecpc) > diode info ASRL28::INSTR
Arduino VISA firmware v1.0.0
Checkpunten:
- De identificatiestring is met
diode info DEVICE
op te vragen. - De string is niet direct gecopypaste, maar wordt daadwerkelijk opgevraagd.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Pythondaq: choose device
Nadat je met het subcommando list
een lijst van aangesloten instrumenten hebt opgevraagd en met het subcommando info
erachter bent gekomen wat de naam is van de poort waar de Arduino aanhangt. Geef je vervolgens deze poortnaam mee bij het subcommando scan
om een meting op de (juiste) Arduino uit te voeren. Daarna test je het subcommando scan
door géén poortnaam mee te geven, je ziet een foutmelding verschijnen. Tot slot leen je de Arduino van je buurmens, kijkt met list
en info
aan welke poortnaam die Arduino hangt en voert een scan uit op die Arduino. Je ziet dat het lampje van de Arduino van je buur gaat branden en niet die van jou.
(ecpc) > diode scan
errorUsage: diode [OPTIONS] DEVICE
Try 'diode --help' for help.
Error: Missing argument 'DEVICE'.
Checkpunten:
- De gebruiker moet een poortnaam meegeven.
- De gekozen device wordt ook daadwerkelijk gebruikt in het model en de controller.
- Als géén poortnaam wordt opgegeven, krijgt de gebruiker een foutmelding.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Pythondaq: --help
Je gaat de applicatie testen vanuit de ogen van een nieuwe gebruiker. Je typt diode --help
en bekijkt de helptekst. Je ziet dat er subcommando's zijn. Met subcommando --help
test je de helpteksten een voor een uit. Ook bekijk je de helpteksten van argumenten en opties. De helpteksten stellen je in staat om de applicatie te begrijpen en te bedienen
Pseudo-code
Testcode(ecpc) > diode --help
Usage: diode [OPTIONS] COMMAND [ARGS] ...
Options:
--help Show this message and exit.
Commands:
Subcommand Summary containing ARGUMENTs.
Checkpunten:
- De informatie in de helpteksten is voldoende om de applicatie te begrijpen en te bedienen.
-
diode --help
vertelt duidelijk welke subcommando's aanwezig zijn en wat ze doen. - Bij alle subcommando's, is het duidelijk welke opties en argumenten er zijn, wat de standaardwaarden zijn en wat ze doen.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Pythondaq: Grafiek
Natuurlijk wil je ook een grafiek maken van de metingen. Je geeft met een boolean flag --graph
aan dat er een grafiek getoond moet worden. De optie --no-graph
zorgt ervoor dat er geen grafiek getoond wordt.
boolean flags
Lees meer over boolean flags in de Click documentatie.
Checkpunten:
- De grafiek wordt alleen getoond wanneer
--graph
wordt meegegeven.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq:
--help
- Pythondaq: Grafiek
Pythondaq: list --search
Breid het commando list
uit met een optie --search
waarmee je niet een lijst van alle instrumenten krijgt, maar alleen de instrumenten die de zoekterm bevatten. Dus bijvoorbeeld:
(ecpc) > diode list
The following devices are connected to your computer:
ASRL/dev/cu.SOC::INSTR
ASRL/dev/cu.MALS::INSTR
ASRL/dev/cu.AirPodsvanDavid-Wireles-1::INSTR
ASRL/dev/cu.Bluetooth-Incoming-Port::INSTR
ASRL/dev/cu.usbmodem143401::INSTR
(ecpc) > diode list -s usbmodem
The following devices match your search string:
ASRL/dev/cu.usbmodem143401::INSTR
De lijst met instrumenten kan er op Windows heel anders uitzien. Sterker nog, op Windows is de lijst meestal vrij saai. Maar leen eens heel even een Arduino van iemand anders en je ziet dat er dan twee poorten in de lijst verschijnen.
Pas — na het uitbreiden van list
— de commando's scan
en info
aan zodat het niet nodig is om de volledige devicenaam mee te geven, maar alleen een zoekterm.
Op dit punt hebben we de functionaliteit van ons snelle script van het vorige hoofdstuk bereikt. Dit was veel meer werk, maar het is veel flexibeler. Als je wilt meten met een andere Arduino, een ander bereik, of een andere stapgrootte dan type je gewoon een iets ander commando in de terminal. Je hoeft geen scripts meer aan te passen. Als je na een tijdje niet meer precies weet hoe het ook alweer werkte allemaal kun je dat snel weer oppakken door --help
aan te roepen.
Alle subcommando's implementeren
Kijk nog eens terug naar het lijstje subcommando's die je in opdracht Subcommando's bedenken hebt opgeschreven. Heb je alles geïmplementeerd? Wat zou je willen dat je nog meer kan instellen? Als er tijd over is, kijk dan of dit lukt.
Rich
Een interface met stijl¶
Ook command-line interfaces gaan met hun tijd mee. Vroeger waren ze per definitie zwart/wit en statisch, maar tegenwoordig worden interfaces vaak opgeleukt met kleur, emoji's en bewegende progressbars. Rich6 is een project dat in recordtijd heel populair is geworden. Het bestaat pas sinds november 2019 en heeft precies twee jaar later meer dan 31000 verzameld. Dat is veel — en de populariteit is sindsdien nog verder toegenomen.
Rich is ontzettend uitgebreid en heeft heel veel mogelijkheden. Voor ons project kan het handig zijn om een progressbar te gebruiken of met Rich een tabel weer te geven. De documentatie7 van Rich is best goed, maar kan lastig zijn om een mooi overzicht te krijgen. Een serie van korte video tutorials kun je vinden bij calmcode. Iedere video duurt maar één tot twee minuten en laat mooi de mogelijkheden zien. Voor de functies die je wilt gebruiken kun je dan meer informatie opzoeken in de documentatie van Rich zelf.
Rich
Verrijk je interface met Rich. Doe dit naar eigen wens en inzicht.
Data-analyse
Data-analyse¶
Door de $I,U$-karakteristiek van de (lichtgevende) diode te analyseren is het mogelijk om de constante van Boltzmann te bepalen. De stoomsterkte door een diode wordt gegeven door de Shockley diodevergelijking. Zie ook hoofdstuk diode.
Lukt het, om binnen de te bepalen onzekerheid, overeenkomst te vinden met de literatuurwaarde? Een LED is helaas geen ideale diode dus dit kan lastig zijn.
Model fitten
Fit het model van Shockley aan je $I,U$-karakteristiek. Welke parameters kun je bepalen? Overleg met je begeleider!
-
argv staat voor: argument vector, een lijst met argumenten ↩
-
Merk op in de code hieronder:
_
is de weggooivariabele in Python. Het gaat ons erom dat de loop een aantal keer doorlopen wordt en we hoeven niets te doen met de loop index. ↩ -
Zie ook: The Python Standard Library ↩
-
Zie voor meer informatie over flags de Click documentatie. ↩
-
Pallets. Click. URL: https://click.palletsprojects.com/. ↩
-
Will McGugan. Rich. URL: https://github.com/willmcgugan/rich. ↩
-
Will McGugan. Rich documentation. URL: https://rich.readthedocs.io/en/latest/. ↩