Skip to content

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:

Terminal
PS> python script.py
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 

Hiermee vraag je Python om het versienummer weer te geven. Soms kunnen opties zelf weer een argument meekrijgen. Bijvoorbeeld:

Terminal
PS> python -m antigravity
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:

Terminal
PS> conda create --name pythondaq --channel conda-forge python pyvisa-py
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

  1. Naast conda create heb je ook met andere argumenten gewerkt zoals activate en install. Welke argumenten ken je al van de applicatie poetry?
  2. Vraag de lijst met argumenten (commando's) op van Poetry met poetry list, hoeveel kende je nog niet?

Conda opties en argumenten

  1. Open een Anaconda Prompt
  2. Maak gebruik van de optie -h om de helpfunctie van conda op te vragen. (conda -h)
  3. Zoek de optie op om de conda versie weer te geven (conda -V)
  4. Maak gebruik van de optie -h om de helpfunctie van het commando activate op te vragen (conda activate -h)
  5. 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:

cli.py
import sys

print(sys.argv)
(ecpc) > python cli.py test 123

Met if-statements kunnen we acties verbinden aan bepaalde argumenten:

cli.py
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:

hello.py
def hello():
    print("Hello physicist!")

if __name__ == "__main__":
    hello()

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:

hello.py
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.

Terminal
python hello.py --help

Help functie

Je neemt het script 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

hello.py
import click

# make function Click command
# function
    # print hello physicist!

# when run this script:
    # run function
Testcode
(ecpc) > python hello.py --help 

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:

hello.py
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

hello.py
import click

# make function Click command
# make argument name
# function, parameter name
    # print hello <name>!

# when run this script:
    # run function
Testcode
(ecpc) > python hello.py 

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.

hello.py
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

hello.py
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
Testcode
(ecpc) > python hello.py David -c 5 

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 
    
    

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:

hello.py
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 


(ecpc) > python hello.py Alice 


(ecpc) > python hello.py Alice -c 2 


(ecpc) > python hello.py Alice --count 3 

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.

import time
# wait 28 second
time.sleep(28)

Pseudo-code

hello.py
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
Testcode
(ecpc) > python hello.py David -c 5 

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
  1. Pas de applicatie aan zodat je kan kiezen tussen het kwadraat of de wortel van het getal.

Pseudo-code

count_count.py
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()
Testcode
(ecpc) > square 6 

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 de pythondaq 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
import click

@click.group()
def cmd_group():
    pass

@cmd_group.command()
@click.argument("package")
def install(package):
    print(f"Installing {package}...")

@cmd_group.command()
@click.argument("package")
def remove(package):
    print(f"Removing {package}...")

if __name__ == "__main__":
    cmd_group()
In (de laatste) regel 18 roepen we de hoofdfunctie aan die we enigszins willekeurig 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

Nu je hebt geleerd om met Click subcommando's te maken wil je deze uittesten in combinatie met het commando wat je met Poetry kan aanmaken om een functie uit een script uit te voeren. Je maakt in de map 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

pyproject.toml
[tool.poetry.scripts]
naam_commando = "package.module:naam_functie"
Testcode
(ecpc) > fake_conda install scipy 

Checkpunten:

  • In de pyproject.toml verwijst [tool.poetry.scripts] naar een functie zodat install en remove subcommando's zijn.
  • Het commando fake_conda install scipy print de tekst Installing 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.

  1. Maak een nieuw poetry project (met een src indeling) aan met de naam smallangle .
  2. Let op de Octocat voor smallangle, het moet dus een repository zijn (of worden).
  3. Maak een nieuw environment die smallangle heet met daarin alleen Python .
  4. Zet in het package smallangle een module smallangle.py.
  5. 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)
    
  6. Ga door naar de opdracht smallangle aanpassen. Je mag de opdracht smallangle installeren overslaan — dat werk heb je nu zelf al gedaan.

smallangle installeren

Je cloned het Poetry project 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

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)
(ecpc) > python smallangle.py

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:

Terminal
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 

Checkpunten:

  • De gebruiker kan met subcommando's kiezen tussen sin en tan.
  • 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 

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:

fakeconda.py
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()
Als we vervolgens de help functie aanroepen zien we de eerste regel van de docstrings verschijnen voor alle subcommando's:

(ecpc) > python fake_conda.py --help 

Daarna kun je uitleg vragen voor de subcommando's waarbij je de hele docstring te zien krijgt:

(ecpc) > python fake_conda.py install --help 

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

"""Summary containing ARGUMENTs.

ARGUMENT description of the argument.
"""
Testcode
(ecpc) > smallangle --help 

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

Om de command-line interface voor pythondaq te maken ga je in een nieuw bestand 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}...")
We hebben nu een commando 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
Testcode
(ecpc) > diode list 

(ecpc) > diode scan 

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

# subcommando scan with range in Volt and output CSV
    # start scan with range
    # print current and voltage
    # if output:
        # create csv

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

# subcommando scan with range in Volt, output CSV and repeat measurements
    # start scan with range and repeat measurements
    # print current, voltage and errors
    # if output:
        # create csv

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 

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

# subcommando info with device
    # print identificationstring of device
Testcode
(ecpc) > diode info ASRL28::INSTR 

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 

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

"""Summary containing ARGUMENTs.

ARGUMENT description of the argument.
"""
Testcode
(ecpc) > diode --help 

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 

(ecpc) > diode list -s usbmodem 

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!

  1. argv staat voor: argument vector, een lijst met argumenten 

  2. 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. 

  3. Zie ook: The Python Standard Library 

  4. Zie voor meer informatie over flags de Click documentatie

  5. Pallets. Click. URL: https://click.palletsprojects.com/

  6. Will McGugan. Rich. URL: https://github.com/willmcgugan/rich

  7. Will McGugan. Rich documentation. URL: https://rich.readthedocs.io/en/latest/