Module fusus.parameters

Settings and configuration

Expand source code Browse git
"""Settings and configuration
"""

import os
from copy import deepcopy
import yaml

HOME = os.path.expanduser('~')
"""Full path to your home directory."""

BASE = f"{HOME}/github"
"""Local directory where all your local clones of GitHub repositories are stored."""

ORG = "among"
"""Organization on GitHub in which this code/data repository resides.

This is also the name of the parent directory of your local clone
of this repo.
"""

REPO = "fusus"
"""Name of this code/data repository."""

REPO_DIR = f"{BASE}/{ORG}/{REPO}"
"""Directory of the local repo.

This is where this repo resides on your computer.
Note that we assume you have followed the convention
that it is in your home directory, and then
`github/among/fusus`.
"""

PROGRAM_DIR = f"{REPO_DIR}/{REPO}"
"""The subdirectory in the repo that contains the `fusus` Python package`.
"""

LOCAL_DIR = f"{REPO_DIR}/_local"
"""Subdirectory containing unpublished input material.

This is material that we cannot make public in this repo.
This directory is not pushed to the online repo,
by virtue of its being in the `.gitignore` of this repo.

See also `UR_DIR`.
"""

SOURCE_DIR = f"{LOCAL_DIR}/source"
"""Subdirectory containing source texts.

Here are the sources that we cannot make public in this repo.

See also `UR_DIR` and `LOCAL_DIR`.
"""

UR_DIR = f"{REPO_DIR}/ur"
"""Subdirectory containing the public source texts.

Here are the sources that we can make public in this repo.

See also `SOURCE_DIR`.
"""

ALL_PAGES = "allpages"

KRAKEN = dict(
    modelPath=f"{REPO_DIR}/model/arabic_generalized.mlmodel"
)

COLORS = dict(
    greyGRS=200,
    blackGRS=0,
    blackRGB=(0, 0, 0),
    whiteGRS=255,
    whiteRGB=(255, 255, 255),
    greenRGB=(0, 255, 0),
    orangeRGB=(255, 127, 0),
    purpleRGB=(255, 0, 127),
    blockRGB=(0, 255, 255),
    letterRGB=(0, 200, 200),
    upperRGB=(0, 200, 0),
    lowerRGB=(200, 0, 0),
    horizontalStrokeRGB=(0, 128, 255),
    verticalStrokeRGB=(255, 128, 0),
    marginGRS=255,
    marginRGB=(200, 200, 200),
    cleanRGB=(255, 255, 255),
    cleanhRGB=(220, 220, 220),
    boxDeleteRGB=(240, 170, 20),
    boxDeleteNRGB=(140, 70, 0),
    boxRemainRGB=(170, 240, 40),
    boxRemainNRGB=(70, 140, 0),
)
"""Named colors. """


BAND_COLORS = dict(
    main=(40, 40, 40),
    inter=(255, 200, 200),
    broad=(0, 0, 255),
    high=(128, 128, 255),
    mid=(128, 255, 128),
    low=(255, 128, 128),
)
"""Band colors.

Each band will be displayed in its own color.
"""

STAGES = dict(
    orig=("image", True, None, None, None),
    gray=("image", False, None, None, None),
    blurred=("image", False, None, None, None),
    normalized=("image", False, None, "proofDir", ""),
    normalizedC=("image", True, None, None, None),
    layout=("image", True, None, None, None),
    histogram=("image", True, None, None, None),
    demargined=("image", False, None, None, None),
    demarginedC=("image", True, None, None, None),
    markData=("data", None, "tsv", None, None),
    boxed=("image", True, None, None, None),
    cleanh=("image", False, None, None, None),
    clean=("image", False, None, "cleanDir", ""),
    binary=("image", False, None, None, None),
    char=("data", None, "tsv", "proofDir", None),
    word=("data", None, "tsv", "outDir", ""),
    line=("data", None, "tsv", "proofDir", "line"),
    proofchar=("link", True, "html", "proofDir", "char"),
    proofword=("link", True, "html", "proofDir", ""),
)
"""Stages in page processing.

When we process a scanned page,
we produce named intermediate stages,
in this order.

The stage data consists of the following bits of information:

* kind: image or data (i.e. tab separated files with unicode data).
* colored: True if colored, False if grayscale, None if not an image
* extension: None if an image file, otherwise the extension of a data file, e.g. `tsv`
"""

SETTINGS = dict(
    debug=0,
    inDir="in",
    outDir="out",
    interDir="inter",
    cleanDir="clean",
    proofDir="proof",
    htmlDir="html",
    marksDir="marks",
    blurX=21,
    blurY=21,
    marginThresholdX=1,
    contourFactor=0.3,
    contourOffset=0.04,
    peakProminenceY=5,
    peakSignificant=0.1,
    peakTargetWidthFraction=0.5,
    valleyProminenceY=5,
    outerValleyShiftFraction=0.3,
    blockMarginX=12,
    accuracy=0.8,
    connectBorder=4,
    connectThreshold=200 * 200,
    connectRatio=0.1,
    boxBorder=3,
    maxHits=5000,
    bandMain=(5, -5),
    bandInter=(5, 5),
    bandBroad=(-15, 10),
    bandMid=(10, -5),
    bandHigh=(10, 30),
    bandLow=(-10, -10),
    defaultLineHeight=200,
)
"""Customizable settings.

These are the settings that can be customized in several ways.

The values here are the default values.

When the pipeline is run in a book directory, it will look
for a file `parameters.yaml` in the toplevel directory of the book
where these settings can be overridden.

In a program or notebook you can also make last-minute changes to these parameters by
calling the `fusus.book.Book.configure` method which calls the
`Config.configure` method.

The default values can be inspected by expanding the source code.

!!! caution "Two-edged sword"
    When you change a parameter to improve a particular effect on a particular page,
    it may wreak havoc with many other pages.

    When you tweak, take care that you do it locally,
    on a single book, or a single page.

debug
:   Whether to show (intermediate) results.
    If `0`: shows nothing, if `1`: shows end result, if `2`: shows intermediate
    results.

inDir
:   name of the subdirectory with page scans

outDir
:   name of the subdirectory with the final results of the workflow

interDir
:   name of the subdirectory with the intermediate results of the workflow

cleanDir
:   name of the subdirectory with the cleaned, blockwise images of the workflow

marksDir
:   name of the subdirectory with the marks

skewBorder
:   the  width of the page  margins that will be whitened in order to
    suppress the sharp black triangles introduces by skewing the page

blurX
:   the amount of blur in the X-direction.
    Blurring is needed to get better histograms
    To much blurring will hamper the binarization, see e.g. pag 102 in the
    examples directory: if you blur with 41, 41 binarization fails.

blurY
:   the amount of blur in the X-direction.
    Blurring is needed to get betterskewing and histograms.

    !!! caution "Amount of Y-blurring"
        Too much vertical blurring will cause the disappearance of horizontal
        bars from the histogram. Footnote  bars will go undetected.

        Too little vertical blurring will result in ragged histograms,
        from which it is difficult to get vertical line boundaries.

marginThresholdX
:   used when interpreting horizontal histograms.

    When histograms for horizontal lines cross marginThresholdY,  it will taken as an
    indication that a line boundary (upper or lower) has been reached.

contourFactor
:   used when computing left and right contour lines of a page.

    Each horizontal line as a left most black pixel and a rightmost one.
    Together they form the left contour and the right contour of the page.
    The length of each line is the distance between the left contour and right contour
    points on that line.

    However, to be useful, the contour lines must be smoothed.
    We look up and down from each contour point and replace it by the median value of
    the contour points above and below that point.

    How far do we have to look?
    We want to neutralize the interline spaces, so we look up and down for a fraction
    line line height.

    That fraction is specified by this parameter.
    A proxy for the line height is the peak distance.

peakSignificant
:   used when interpreting histograms for line detection

    When we look for significant peaks in a histogram, we determine the max peak height.
    Significant peaks are those that have a height greater than a specific fraction
    of the max peak height. This parameter states that fraction.

peakTargetWidthFraction
:   used when interpreting histograms for line detection
    When we have studied the significant peaks and found the regular distance between
    successive peaks, we use that to pass as the `distance` parameter to the SciPy
    [find_peaks](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks.html#scipy.signal.find_peaks)
    algorithm. We get the best results if we do not pass the line height itself, but a
    fraction of it. This parameter is that fraction.

peakProminenceY, valleyProminenceY
:   used when interpreting histograms for line detection

    We detect peaks and valleys in the histogram by means of a SciPy algorithm,
    to which we pass a prominence parameter.
    This will leave out minor peaks and valleys.

outerValleyShiftFraction
:   used when interpreting histograms for line detection

    The valleys at the outer ends of the histogram tend to be very broad and hence
    the valleys will be located too far from the actual ink.
    We correct for that by shifting those valleys a fraction of their plateau sizes
    towards the ink. This parameter is that fraction.

defaultLineHeight
:   used for line detection

    After line detection, a value for the line height is found and stored in this
    parameter. The parameter is read when there is only one line on a page, in
    which case the line detection algorithm has too little information.
    If this occurs at the very first calculation of line heights, a fixed
    default value is used.

accuracy
:   When marks are searched for in the page, we get the result in the form
    of a grayscale page where the value in each point reflects how much
    the page in that area resembles the mark.
    Only hits above the value of *accuracy* will be considered.

connectBorder
:   When marks are found, each hit will be inspected:
    is the ink in the hit connected to the ink outside the hit?
    This will be measured in an inner and outer border of the page,
    whose thickness is given by this parameter.

connectThreshold
:   After computing inner and outer borders,
    they will be inverted, so that black has the maximum
    value. Then the inside and outside borders are multiplied pixel wise,
    so that places where they are both black get very high values.
    All places where this product is higher than the value of *connectThreshold*
    are retained for further calculation.

connectRatio
:   After computing the places where inner and outer borders contain
    joint ink, the ratio of such places with respect to the total border size
    is calculated. If that ratio is greater than *connectRatio*,
    the hit counts as connected to its surroundings.
    We have not found a true instance of the mark, and the mark will not be cleaned.

boxBorder
:   The hits after searching for marks will be indicated on the `boxed` stage
    of the page by means of a small coloured border around each hit.
    The width of this border is *boxBorder*.

maxHits
:   When searching for marks, there are usually multiple hits: the place where
    the mark occurs, and some places very nearby.
    The cleaning algorithm will cluster nearby hits and pick the best
    hit per cluster.
    But if the number of hits is very large, it is a sign that the mark
    is not searched with the right accuracy, and clustering will be
    prevented. It would become very expensive, and useless anyway.
    A warning will be issued in such cases.

bandMain
:   Offsets for the `main` band. Given as `(top, bottom)`, with
    `top` and `bottom` positive or negative integers.

    This band covers most of the ink in a line.

    The `main` band is computed from the histogram after which the
    height of the top and bottom boundaries are adjusted relative to the
    values obtained by the histogram algorithm.

    You can adjust these values: higher values move the boundaries down,
    lower values move them up.

    In practice, the adjustments are zero for the main band, while
    all other bands are derived from the main band by applying adjustments.

bandInter
:   Offsets for the `inter` band.

    This band covers most of the white between two lines.

    The `inter` band is computed from the histogram.

bandBroad
:   Offsets for the `broad` band.

    This band s like `main` but covers even more ink in a line.

bandMid
:   Offsets for the `mid` band.

    This band s like `main` but covers the densest part in a line.

bandHigh
:   Offsets for the `high` band.

    This band s like `inter` but covers the upper part of the letters and the white
    space above it.

bandLow
:   Offsets for the `low` band.

    This band s like `inter` but covers the lower part of the letters and the white
    space below it.
"""


MARK_PARAMS = dict(acc="accuracy", bw="connectBorder", r="connectRatio")

CONFIG_FILE = "parameters.yaml"

LINE_CLUSTER_FACTOR = 0.46
"""Clustering characters into lines in the Lakhnawi PDF.

When the characters on a page are divided into lines based on their height,
this parameter determines which heights can be clustered together.
Heights that differ less than the estimated average line height times this
factor, can be clustered together.

!!! caution "tolerance"
    When you choose a value of `0.44` or lower for this parameter,
    line 8 on the title page will go wrong.

    When you choose higher values, you run the risk of clustering
    the characters of multiple lines into one line.
"""


class Config:
    def __init__(self, tm, **params):
        """Settings manager.

        It will expose all settings as attributes to the rest
        of the application.

        It has methods to collect modified settings from the
        user and apply them.

        The default settings are kept as a separate copy that
        will not be changed in any way.

        User modifications act on the current settings,
        which have been obtained by deep-copying the defaults.

        Parameters
        ----------
        tm: object
            Can display timed info/error messages to the display
        params: dict
            key-value pairs that act as updates for the settings.
            If a value is `None`, the original value will be reinstated.
        """

        self.tm = tm

        # ocr settings
        for (k, v) in KRAKEN.items():
            setattr(self, k, v)

        # colors are fixed
        for (k, v) in COLORS.items():
            setattr(self, k, v)

        # bands
        setattr(self, "colorBand", BAND_COLORS)

        # stages
        setattr(self, "stageOrder", tuple(STAGES))
        setattr(self, "stages", STAGES)

        # marks
        setattr(self, "markParams", MARK_PARAMS)

        # settings
        self.settings = deepcopy(SETTINGS)

        if os.path.exists(CONFIG_FILE):
            with open(CONFIG_FILE) as fh:
                overrides = yaml.load(fh, Loader=yaml.FullLoader)

                # python 3.9 feature
                self.settings |= overrides

        # configure
        self.configure(reset=False, **params)

    def configure(self, reset=False, **params):
        """Updates current settings based on new values.

        User modifications act on the current settings,
        which have been obtained by deep-copying the defaults.

        Parameters
        ----------
        reset: boolean, optional `False`
            If `True`, a fresh deep copy of the defaults will be made
            and that will be the basis for the new current settings.
        params: dict
            key-value pairs that act as updates for the settings.
            If a value is `None`, the original value will be reinstated.
        """

        error = self.tm.error

        if reset:
            self.settings = deepcopy(SETTINGS)

        settings = self.settings

        for (k, v) in params.items():
            if k in SETTINGS:
                settings[k] = SETTINGS[k] if v is None else v
            else:
                error(f"Unknown setting: {k}")

        # band offsets

        offsetBand = {}
        settings["offsetBand"] = offsetBand

        for band in self.colorBand:
            bandOff = f"band{band[0].upper()}{band[1:]}"
            offsetBand[band] = settings[bandOff]

        # deliver as attributes

        for (k, v) in settings.items():
            if not k.startswith("band"):
                setattr(self, k, v)

    def show(self, params=None):
        """Display current settings.

        Parameters
        ----------
        params: str, optional `None`
            If `None`, all settings will be displayed.
            Else it should be a comma-separated string of legal
            parameter names whose values are to be displayed.
        """
        tm = self.tm
        error = tm.error
        info = tm.info

        settings = self.settings

        params = sorted(set(params.split(',')) if params else settings)

        for k in params:
            if k in settings and k not in SETTINGS:
                continue
            if k not in SETTINGS:
                error(f"No such setting: {k}", tm=False)
            info(f"{k:<30} = {settings[k]}", tm=False)

Global variables

var BAND_COLORS

Band colors.

Each band will be displayed in its own color.

var BASE

Local directory where all your local clones of GitHub repositories are stored.

var COLORS

Named colors.

var HOME

Full path to your home directory.

var LINE_CLUSTER_FACTOR

Clustering characters into lines in the Lakhnawi PDF.

When the characters on a page are divided into lines based on their height, this parameter determines which heights can be clustered together. Heights that differ less than the estimated average line height times this factor, can be clustered together.

tolerance

When you choose a value of 0.44 or lower for this parameter, line 8 on the title page will go wrong.

When you choose higher values, you run the risk of clustering the characters of multiple lines into one line.

var LOCAL_DIR

Subdirectory containing unpublished input material.

This is material that we cannot make public in this repo. This directory is not pushed to the online repo, by virtue of its being in the .gitignore of this repo.

See also UR_DIR.

var ORG

Organization on GitHub in which this code/data repository resides.

This is also the name of the parent directory of your local clone of this repo.

var PROGRAM_DIR

The subdirectory in the repo that contains the fusus Python package`.

var REPO

Name of this code/data repository.

var REPO_DIR

Directory of the local repo.

This is where this repo resides on your computer. Note that we assume you have followed the convention that it is in your home directory, and then github/among/fusus.

var SETTINGS

Customizable settings.

These are the settings that can be customized in several ways.

The values here are the default values.

When the pipeline is run in a book directory, it will look for a file parameters.yaml in the toplevel directory of the book where these settings can be overridden.

In a program or notebook you can also make last-minute changes to these parameters by calling the Book.configure() method which calls the Config.configure() method.

The default values can be inspected by expanding the source code.

Two-edged sword

When you change a parameter to improve a particular effect on a particular page, it may wreak havoc with many other pages.

When you tweak, take care that you do it locally, on a single book, or a single page.

debug
Whether to show (intermediate) results. If 0: shows nothing, if 1: shows end result, if 2: shows intermediate results.
inDir
name of the subdirectory with page scans
outDir
name of the subdirectory with the final results of the workflow
interDir
name of the subdirectory with the intermediate results of the workflow
cleanDir
name of the subdirectory with the cleaned, blockwise images of the workflow
marksDir
name of the subdirectory with the marks
skewBorder
the width of the page margins that will be whitened in order to suppress the sharp black triangles introduces by skewing the page
blurX
the amount of blur in the X-direction. Blurring is needed to get better histograms To much blurring will hamper the binarization, see e.g. pag 102 in the examples directory: if you blur with 41, 41 binarization fails.
blurY

the amount of blur in the X-direction. Blurring is needed to get betterskewing and histograms.

Amount of Y-blurring

Too much vertical blurring will cause the disappearance of horizontal bars from the histogram. Footnote bars will go undetected.

Too little vertical blurring will result in ragged histograms, from which it is difficult to get vertical line boundaries.

marginThresholdX

used when interpreting horizontal histograms.

When histograms for horizontal lines cross marginThresholdY, it will taken as an indication that a line boundary (upper or lower) has been reached.

contourFactor

used when computing left and right contour lines of a page.

Each horizontal line as a left most black pixel and a rightmost one. Together they form the left contour and the right contour of the page. The length of each line is the distance between the left contour and right contour points on that line.

However, to be useful, the contour lines must be smoothed. We look up and down from each contour point and replace it by the median value of the contour points above and below that point.

How far do we have to look? We want to neutralize the interline spaces, so we look up and down for a fraction line line height.

That fraction is specified by this parameter. A proxy for the line height is the peak distance.

peakSignificant

used when interpreting histograms for line detection

When we look for significant peaks in a histogram, we determine the max peak height. Significant peaks are those that have a height greater than a specific fraction of the max peak height. This parameter states that fraction.

peakTargetWidthFraction
used when interpreting histograms for line detection When we have studied the significant peaks and found the regular distance between successive peaks, we use that to pass as the distance parameter to the SciPy find_peaks algorithm. We get the best results if we do not pass the line height itself, but a fraction of it. This parameter is that fraction.
peakProminenceY, valleyProminenceY

used when interpreting histograms for line detection

We detect peaks and valleys in the histogram by means of a SciPy algorithm, to which we pass a prominence parameter. This will leave out minor peaks and valleys.

outerValleyShiftFraction

used when interpreting histograms for line detection

The valleys at the outer ends of the histogram tend to be very broad and hence the valleys will be located too far from the actual ink. We correct for that by shifting those valleys a fraction of their plateau sizes towards the ink. This parameter is that fraction.

defaultLineHeight

used for line detection

After line detection, a value for the line height is found and stored in this parameter. The parameter is read when there is only one line on a page, in which case the line detection algorithm has too little information. If this occurs at the very first calculation of line heights, a fixed default value is used.

accuracy
When marks are searched for in the page, we get the result in the form of a grayscale page where the value in each point reflects how much the page in that area resembles the mark. Only hits above the value of accuracy will be considered.
connectBorder
When marks are found, each hit will be inspected: is the ink in the hit connected to the ink outside the hit? This will be measured in an inner and outer border of the page, whose thickness is given by this parameter.
connectThreshold
After computing inner and outer borders, they will be inverted, so that black has the maximum value. Then the inside and outside borders are multiplied pixel wise, so that places where they are both black get very high values. All places where this product is higher than the value of connectThreshold are retained for further calculation.
connectRatio
After computing the places where inner and outer borders contain joint ink, the ratio of such places with respect to the total border size is calculated. If that ratio is greater than connectRatio, the hit counts as connected to its surroundings. We have not found a true instance of the mark, and the mark will not be cleaned.
boxBorder
The hits after searching for marks will be indicated on the boxed stage of the page by means of a small coloured border around each hit. The width of this border is boxBorder.
maxHits
When searching for marks, there are usually multiple hits: the place where the mark occurs, and some places very nearby. The cleaning algorithm will cluster nearby hits and pick the best hit per cluster. But if the number of hits is very large, it is a sign that the mark is not searched with the right accuracy, and clustering will be prevented. It would become very expensive, and useless anyway. A warning will be issued in such cases.
bandMain

Offsets for the main band. Given as (top, bottom), with top and bottom positive or negative integers.

This band covers most of the ink in a line.

The main band is computed from the histogram after which the height of the top and bottom boundaries are adjusted relative to the values obtained by the histogram algorithm.

You can adjust these values: higher values move the boundaries down, lower values move them up.

In practice, the adjustments are zero for the main band, while all other bands are derived from the main band by applying adjustments.

bandInter

Offsets for the inter band.

This band covers most of the white between two lines.

The inter band is computed from the histogram.

bandBroad

Offsets for the broad band.

This band s like main but covers even more ink in a line.

bandMid

Offsets for the mid band.

This band s like main but covers the densest part in a line.

bandHigh

Offsets for the high band.

This band s like inter but covers the upper part of the letters and the white space above it.

bandLow

Offsets for the low band.

This band s like inter but covers the lower part of the letters and the white space below it.

var SOURCE_DIR

Subdirectory containing source texts.

Here are the sources that we cannot make public in this repo.

See also UR_DIR and LOCAL_DIR.

var STAGES

Stages in page processing.

When we process a scanned page, we produce named intermediate stages, in this order.

The stage data consists of the following bits of information:

  • kind: image or data (i.e. tab separated files with unicode data).
  • colored: True if colored, False if grayscale, None if not an image
  • extension: None if an image file, otherwise the extension of a data file, e.g. tsv
var UR_DIR

Subdirectory containing the public source texts.

Here are the sources that we can make public in this repo.

See also SOURCE_DIR.

Classes

class Config (tm, **params)

Settings manager.

It will expose all settings as attributes to the rest of the application.

It has methods to collect modified settings from the user and apply them.

The default settings are kept as a separate copy that will not be changed in any way.

User modifications act on the current settings, which have been obtained by deep-copying the defaults.

Parameters

tm : object
Can display timed info/error messages to the display
params : dict
key-value pairs that act as updates for the settings. If a value is None, the original value will be reinstated.
Expand source code Browse git
class Config:
    def __init__(self, tm, **params):
        """Settings manager.

        It will expose all settings as attributes to the rest
        of the application.

        It has methods to collect modified settings from the
        user and apply them.

        The default settings are kept as a separate copy that
        will not be changed in any way.

        User modifications act on the current settings,
        which have been obtained by deep-copying the defaults.

        Parameters
        ----------
        tm: object
            Can display timed info/error messages to the display
        params: dict
            key-value pairs that act as updates for the settings.
            If a value is `None`, the original value will be reinstated.
        """

        self.tm = tm

        # ocr settings
        for (k, v) in KRAKEN.items():
            setattr(self, k, v)

        # colors are fixed
        for (k, v) in COLORS.items():
            setattr(self, k, v)

        # bands
        setattr(self, "colorBand", BAND_COLORS)

        # stages
        setattr(self, "stageOrder", tuple(STAGES))
        setattr(self, "stages", STAGES)

        # marks
        setattr(self, "markParams", MARK_PARAMS)

        # settings
        self.settings = deepcopy(SETTINGS)

        if os.path.exists(CONFIG_FILE):
            with open(CONFIG_FILE) as fh:
                overrides = yaml.load(fh, Loader=yaml.FullLoader)

                # python 3.9 feature
                self.settings |= overrides

        # configure
        self.configure(reset=False, **params)

    def configure(self, reset=False, **params):
        """Updates current settings based on new values.

        User modifications act on the current settings,
        which have been obtained by deep-copying the defaults.

        Parameters
        ----------
        reset: boolean, optional `False`
            If `True`, a fresh deep copy of the defaults will be made
            and that will be the basis for the new current settings.
        params: dict
            key-value pairs that act as updates for the settings.
            If a value is `None`, the original value will be reinstated.
        """

        error = self.tm.error

        if reset:
            self.settings = deepcopy(SETTINGS)

        settings = self.settings

        for (k, v) in params.items():
            if k in SETTINGS:
                settings[k] = SETTINGS[k] if v is None else v
            else:
                error(f"Unknown setting: {k}")

        # band offsets

        offsetBand = {}
        settings["offsetBand"] = offsetBand

        for band in self.colorBand:
            bandOff = f"band{band[0].upper()}{band[1:]}"
            offsetBand[band] = settings[bandOff]

        # deliver as attributes

        for (k, v) in settings.items():
            if not k.startswith("band"):
                setattr(self, k, v)

    def show(self, params=None):
        """Display current settings.

        Parameters
        ----------
        params: str, optional `None`
            If `None`, all settings will be displayed.
            Else it should be a comma-separated string of legal
            parameter names whose values are to be displayed.
        """
        tm = self.tm
        error = tm.error
        info = tm.info

        settings = self.settings

        params = sorted(set(params.split(',')) if params else settings)

        for k in params:
            if k in settings and k not in SETTINGS:
                continue
            if k not in SETTINGS:
                error(f"No such setting: {k}", tm=False)
            info(f"{k:<30} = {settings[k]}", tm=False)

Methods

def configure(self, reset=False, **params)

Updates current settings based on new values.

User modifications act on the current settings, which have been obtained by deep-copying the defaults.

Parameters

reset : boolean, optional False
If True, a fresh deep copy of the defaults will be made and that will be the basis for the new current settings.
params : dict
key-value pairs that act as updates for the settings. If a value is None, the original value will be reinstated.
Expand source code Browse git
def configure(self, reset=False, **params):
    """Updates current settings based on new values.

    User modifications act on the current settings,
    which have been obtained by deep-copying the defaults.

    Parameters
    ----------
    reset: boolean, optional `False`
        If `True`, a fresh deep copy of the defaults will be made
        and that will be the basis for the new current settings.
    params: dict
        key-value pairs that act as updates for the settings.
        If a value is `None`, the original value will be reinstated.
    """

    error = self.tm.error

    if reset:
        self.settings = deepcopy(SETTINGS)

    settings = self.settings

    for (k, v) in params.items():
        if k in SETTINGS:
            settings[k] = SETTINGS[k] if v is None else v
        else:
            error(f"Unknown setting: {k}")

    # band offsets

    offsetBand = {}
    settings["offsetBand"] = offsetBand

    for band in self.colorBand:
        bandOff = f"band{band[0].upper()}{band[1:]}"
        offsetBand[band] = settings[bandOff]

    # deliver as attributes

    for (k, v) in settings.items():
        if not k.startswith("band"):
            setattr(self, k, v)
def show(self, params=None)

Display current settings.

Parameters

params : str, optional None
If None, all settings will be displayed. Else it should be a comma-separated string of legal parameter names whose values are to be displayed.
Expand source code Browse git
def show(self, params=None):
    """Display current settings.

    Parameters
    ----------
    params: str, optional `None`
        If `None`, all settings will be displayed.
        Else it should be a comma-separated string of legal
        parameter names whose values are to be displayed.
    """
    tm = self.tm
    error = tm.error
    info = tm.info

    settings = self.settings

    params = sorted(set(params.split(',')) if params else settings)

    for k in params:
        if k in settings and k not in SETTINGS:
            continue
        if k not in SETTINGS:
            error(f"No such setting: {k}", tm=False)
        info(f"{k:<30} = {settings[k]}", tm=False)