Coverage for valmix/knob.py: 100%
50 statements
« prev ^ index » next coverage.py v7.9.2, created at 2026-10-25 09:28 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2026-10-25 09:28 +0000
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# SPDX-License-Identifier: Apache-2.0
5# Copyright 2024 Inria
7"""Knob interface."""
9from multiprocessing.sharedctypes import Synchronized as Value
10from typing import Sequence, Union
12T = Union[int, float]
15class Knob:
16 """Name and list values for a target quantity."""
18 def __init__(
19 self,
20 name: str,
21 value: Value,
22 values: Sequence[T],
23 ) -> None:
24 """Create a new knob.
26 Args:
27 name: Display name of the value.
28 value: Actual multiprocessing synchronized shared object.
29 values: Values the internal value may take.
30 """
31 nb_values = len(values)
32 initial_value = value.value
33 self.__index = nb_values // 2
34 self.__mp_value = value
35 self.__name = name
36 self.__nb_values = nb_values
37 self.__values = sorted(values)
38 #
39 self.__snap_to(initial_value)
40 self.__update_mp_value()
42 def __snap_to(self, snap_value: T) -> None:
43 """Snap to available value closest to a given one.
45 Args:
46 snap_value: Quantity to get close to using values from the set.
47 """
48 self.__index = 0
49 best_dist = abs(self.__values[0] - snap_value)
50 for i in range(1, self.__nb_values):
51 cur_dist = abs(self.__values[i] - snap_value)
52 if cur_dist < best_dist:
53 self.__index = i
54 best_dist = cur_dist
56 def __update_mp_value(self) -> None:
57 """Update internal multiprocessing value from current index."""
58 new_value = self.__values[self.__index]
59 with self.__mp_value.get_lock():
60 self.__mp_value.value = new_value
62 def advance(self, step: int) -> None:
63 """Advance to the next value listed when configuring the knob.
65 Args:
66 step: Number of steps to add to the list index.
67 """
68 self.__index += step
69 if self.__index >= self.__nb_values:
70 self.__index = self.__nb_values - 1
71 elif self.__index < 0:
72 self.__index = 0
73 self.__update_mp_value()
75 @property
76 def current_index(self) -> int:
77 """Get current index in the list of available values.
79 Returns:
80 Current index of the knob.
81 """
82 return self.__index
84 @property
85 def value(self) -> T:
86 """Get current value of the knob.
88 Returns:
89 Current value of the knob.
90 """
91 return self.__mp_value.value
93 @property
94 def name(self) -> str:
95 """Knob name to display."""
96 return self.__name
98 @property
99 def nb_values(self) -> int:
100 """Get the number of different values that the knob can take.
102 Returns:
103 Number of different values that the knob can take.
104 """
105 return self.__nb_values
107 def __repr__(self) -> str:
108 """Get a string representation of the knob's value.
110 Returns:
111 String representation of the current value of the knob.
112 """
113 v = self.value
114 if isinstance(v, float):
115 return f"{v:.2f}" if 1e-2 <= abs(v) < 100.0 else f"{v:.1e}"
116 return str(self.value)