From 548d508be4f79ea1dc0988f22164f60379390d12 Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 16:52:27 +0100 Subject: [PATCH 01/14] fix: fail to install in editable mode on Windows A call to input() was raising an EOFError during install The call to input() was replaced with a flag to set manually in setup.py The bug occurs only with pip install, not with the deprecated setup install --- setup.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/setup.py b/setup.py index 2722eba7..1e8300ac 100755 --- a/setup.py +++ b/setup.py @@ -28,6 +28,8 @@ extensions = [] # Now finding the extensions to install +install_camera_link = False +install_py_fgen = False if platform.system() == "Linux": # Find the latest runtime version of SiliconSoftware install try: @@ -48,16 +50,10 @@ "-l", "clsersis", "-l", "fglib5"], include_dirs=[f'/usr/local/lib/python{py_ver}/dist-packages/numpy/' f'core/include']) + p = popen("lsmod | grep menable") - if p.read(): - if input("would you like to install CameraLink module? ([y]/n)") != "n": - print("menable kernel module found, installing CameraLink module.") - extensions.append(cl_module) - else: - print("menable kernel module found, but not installing") - else: - print("Cannot find menable kernel module, CameraLink module won't be " - "available.") + if p.read() and install_camera_link: + extensions.append(cl_module) if platform.system() == "Windows": @@ -71,7 +67,7 @@ library_dirs=["C:\\Program Files\\IVI Foundation\\IVI\\Lib_x64\\msc"], extra_compile_args=["/EHsc", "/WX"]) - if input("would you like to install pyFgen module? ([y]/n)") != "n": + if install_py_fgen: extensions.append(py_fgen_module) cl_path = "C:\\Program Files\\SiliconSoftware\\Runtime5.2.1\\" @@ -86,12 +82,8 @@ extra_compile_args=["/EHsc", "/WX"]) p = popen('driverquery /NH | findstr "me4"') - if p.read(): - if input("would you like to install CameraLink module? ([y]/n)") != "n": - extensions.append(cl_module) - else: - print("Can't find microEnable4 Device driver, clModule will not be " - "compiled") + if p.read() and install_camera_link: + extensions.append(cl_module) setup( # Description of the project From 50710dcfb444bde413765e0d97aad5f271fe891b Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 17:39:56 +0100 Subject: [PATCH 02/14] fix: potential division by zero when checking the achieved FPS --- src/crappy/tool/camera_config/camera_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crappy/tool/camera_config/camera_config.py b/src/crappy/tool/camera_config/camera_config.py index e2d20d62..627c5f9d 100644 --- a/src/crappy/tool/camera_config/camera_config.py +++ b/src/crappy/tool/camera_config/camera_config.py @@ -134,8 +134,8 @@ def main(self) -> None: while self._run: # Remaining below the max allowed frequency - if self._max_freq is None or \ - self._n_loops / (time() - start_time) < self._max_freq: + if self._max_freq is None or (self._n_loops < + self._max_freq * (time() - start_time)): # Update the image, the histogram and the information self._update_img() From c3cab029baafd7187ae6d6992ab146a5e9892047 Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 17:53:12 +0100 Subject: [PATCH 03/14] style: initialize variables before while loop --- .../tool/camera_config/config_tools/histogram_process.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/crappy/tool/camera_config/config_tools/histogram_process.py b/src/crappy/tool/camera_config/config_tools/histogram_process.py index 782788e0..719fecf0 100644 --- a/src/crappy/tool/camera_config/config_tools/histogram_process.py +++ b/src/crappy/tool/camera_config/config_tools/histogram_process.py @@ -71,6 +71,9 @@ def run(self) -> None: try: self._processing_event.clear() + # Initializing the variables + img, auto_range, low_thresh, high_thresh = None, None, None, None + # Looping until told to stop or an exception is raised while not self._stop_event.is_set(): From 30c640abd0030d17c73fc33f37ba2a3ae7fd5d12 Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 17:55:20 +0100 Subject: [PATCH 04/14] refactor: add missing logging to the HistogramProcess --- .../tool/camera_config/config_tools/histogram_process.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/crappy/tool/camera_config/config_tools/histogram_process.py b/src/crappy/tool/camera_config/config_tools/histogram_process.py index 719fecf0..900e1764 100644 --- a/src/crappy/tool/camera_config/config_tools/histogram_process.py +++ b/src/crappy/tool/camera_config/config_tools/histogram_process.py @@ -116,6 +116,11 @@ def run(self) -> None: except KeyboardInterrupt: self.log(logging.INFO, "Caught KeyboardInterrupt, stopping") + except (Exception,) as exc: + self._logger.exception("Caught Exception while running, stopping !", + exc_info=exc) + finally: + self.log(logging.INFO, "HistogramProcess finished") @staticmethod def _hist_func(x: np.ndarray, From df71923b4a1e65f5d3a62419c58a4244f91a3420 Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 18:34:19 +0100 Subject: [PATCH 05/14] fix: histogram not working in camera configuration window on Windows The Pipe sending the histogram from the calculation process to the configuration window seemed to overflow It was replaced by a Queue, that is a bit slower but can never overflow The Pipe sending the image from the window to the process was also changed to a Queue, just in case --- .../tool/camera_config/camera_config.py | 20 ++++++++--------- .../config_tools/histogram_process.py | 22 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/crappy/tool/camera_config/camera_config.py b/src/crappy/tool/camera_config/camera_config.py index 627c5f9d..eba46247 100644 --- a/src/crappy/tool/camera_config/camera_config.py +++ b/src/crappy/tool/camera_config/camera_config.py @@ -10,8 +10,8 @@ from pkg_resources import resource_string from io import BytesIO import logging -from multiprocessing import current_process, Event, Pipe -from multiprocessing.queues import Queue +from multiprocessing import current_process, Event, Queue +from multiprocessing.queues import Queue as MPQueue from .config_tools import Zoom, HistogramProcess from ...camera.meta_camera.camera_setting import CameraBoolSetting, \ @@ -55,7 +55,7 @@ class CameraConfig(tk.Tk): def __init__(self, camera: Camera, - log_queue: Queue, + log_queue: MPQueue, log_level: Optional[int], max_freq: Optional[float]) -> None: """Initializes the interface and displays it. @@ -81,11 +81,11 @@ def __init__(self, # Instantiating objects for the process managing the histogram calculation self._stop_event = Event() self._processing_event = Event() - self._img_in, img_in_proc = Pipe() - img_out_proc, self._img_out = Pipe() + self._img_in = Queue(maxsize=0) + self._img_out = Queue(maxsize=0) self._histogram_process = HistogramProcess( stop_event=self._stop_event, processing_event=self._processing_event, - img_in=img_in_proc, img_out=img_out_proc, log_level=log_level, + img_in=self._img_in, img_out=self._img_out, log_level=log_level, log_queue=log_queue) # Attributes containing the several images and histograms @@ -950,12 +950,12 @@ def _calc_hist(self) -> None: # Sending the image to the histogram process self.log(logging.DEBUG, "Sending image for histogram calculation") - self._img_in.send((hist_img, self._auto_range.get(), - self._low_thresh, self._high_thresh)) + self._img_in.put_nowait((hist_img, self._auto_range.get(), + self._low_thresh, self._high_thresh)) # Checking if a histogram is available for display - while self._img_out.poll(): - self._hist = self._img_out.recv() + while not self._img_out.empty(): + self._hist = self._img_out.get_nowait() self.log(logging.DEBUG, "Received histogram from histogram process") def _resize_hist(self) -> None: diff --git a/src/crappy/tool/camera_config/config_tools/histogram_process.py b/src/crappy/tool/camera_config/config_tools/histogram_process.py index 900e1764..3f3af590 100644 --- a/src/crappy/tool/camera_config/config_tools/histogram_process.py +++ b/src/crappy/tool/camera_config/config_tools/histogram_process.py @@ -3,7 +3,6 @@ import numpy as np from multiprocessing import Process, current_process, get_start_method from multiprocessing.synchronize import Event -from multiprocessing.connection import Connection from multiprocessing.queues import Queue import logging import logging.handlers @@ -26,8 +25,8 @@ class HistogramProcess(Process): def __init__(self, stop_event: Event, processing_event: Event, - img_in: Connection, - img_out: Connection, + img_in: Queue, + img_out: Queue, log_level: Optional[int], log_queue: Queue) -> None: """Sets the arguments and initializes the parent class. @@ -38,9 +37,9 @@ def __init__(self, processing_event: An :obj:`multiprocessing.Event` set by the :obj:`multiprocessing.Process` to indicate that it's currently processing an image. Avoids having images to process piling up. - img_in: The :obj:`~multiprocessing.connection.Connection` through which + img_in: The :obj:`~multiprocessing.queues.Queue` through which the images to process are received. - img_out: The :obj:`~multiprocessing.connection.Connection` through which + img_out: The :obj:`~multiprocessing.queues.Queue` through which the calculated histograms are sent back. log_level: The minimum logging level of the entire Crappy script, as an :obj:`int`. @@ -56,8 +55,8 @@ def __init__(self, self._stop_event: Event = stop_event self._processing_event: Event = processing_event - self._img_in: Connection = img_in - self._img_out: Connection = img_out + self._img_in: Queue = img_in + self._img_out: Queue = img_out def run(self) -> None: """The main method being run by the HistogramProcess. @@ -78,11 +77,12 @@ def run(self) -> None: while not self._stop_event.is_set(): # Setting the processing event when busy processing an image - if self._img_in.poll(): + if not self._img_in.empty(): self._processing_event.set() # Receiving the image to process as well as additional parameters - while self._img_in.poll(): - img, auto_range, low_thresh, high_thresh = self._img_in.recv() + while not self._img_in.empty(): + (img, auto_range, + low_thresh, high_thresh) = self._img_in.get_nowait() self.log(logging.DEBUG, "Received image from CameraConfig") @@ -103,7 +103,7 @@ def run(self) -> None: out_img[:, round(2 * high_thresh)] = 127 # Sending back the histogram - self._img_out.send(out_img) + self._img_out.put_nowait(out_img) self._processing_event.clear() self.log(logging.DEBUG, "Sent the histogram back to the " "CameraConfig") From 3eb84defd136dd7b363bab6aa65e5d5f2b9b860f Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 19:27:07 +0100 Subject: [PATCH 06/14] chore: put right name in error message for missing package --- src/crappy/blocks/client_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crappy/blocks/client_server.py b/src/crappy/blocks/client_server.py index 3be21b0d..af5562ed 100644 --- a/src/crappy/blocks/client_server.py +++ b/src/crappy/blocks/client_server.py @@ -17,7 +17,7 @@ try: import paho.mqtt.client as mqtt except (ModuleNotFoundError, ImportError): - mqtt = OptionalModule("paho.mqtt.client") + mqtt = OptionalModule("paho-mqtt") TopicsType = Iterable[Union[str, Iterable[str]]] From 9bd494c2ee6054e6ea6611901fc3fc618fc51d86 Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 19:27:53 +0100 Subject: [PATCH 07/14] fix: ClientServer not working with spawn start method of multiprocessing --- src/crappy/blocks/client_server.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/crappy/blocks/client_server.py b/src/crappy/blocks/client_server.py index af5562ed..791a47ac 100644 --- a/src/crappy/blocks/client_server.py +++ b/src/crappy/blocks/client_server.py @@ -223,15 +223,8 @@ def __init__(self, self._port = port self._spam = spam self._init_output = init_output if init_output is not None else dict() - self._reader = Thread(target=self._output_reader) self._stop_mosquitto = False - - # Instantiating the client - self._client = mqtt.Client(str(time())) - self._client.on_connect = self._on_connect - self._client.on_message = self._on_message - self._client.reconnect_delay_set(max_delay=10) # These attributes may be set later self._topics: Optional[List[Tuple[str, ...]]] = None @@ -253,9 +246,6 @@ def __init__(self, # The last out vals are given for each label, not each topic self._last_out_val = {label: None for label in chain(*self._topics)} - # The buffer for received data is a dictionary of queues - self._buffer_output = {topic: Queue() for topic in self._topics} - # Preparing for publishing data if cmd_labels is not None: # Replacing strings with tuples @@ -286,16 +276,31 @@ def prepare(self) -> None: if self._cmd_labels is not None and not self.inputs: raise ValueError("cmd_labels are specified but there's no input link !") + # Setting the buffer here because Queue objects cannot be set during + # __init__ in spawn multiprocessing mode + if self._topics is not None: + # The buffer for received data is a dictionary of queues + self._buffer_output = {topic: Queue() for topic in self._topics} + # Starting the broker if self._broker: self.log(logging.INFO, f"Starting the Mosquitto broker on port " f"{self._port}") self._launch_mosquitto() + # Creating and starting a Thread reading the stdout of the broker + self._reader = Thread(target=self._output_reader) self._reader.start() sleep(2) self.log(logging.INFO, "Waiting for Mosquitto to start") sleep(2) + # Instantiating the client here as it cannot be set during __init__ in + # spawn multiprocessing mode + self._client = mqtt.Client(str(time())) + self._client.on_connect = self._on_connect + self._client.on_message = self._on_message + self._client.reconnect_delay_set(max_delay=10) + # Connecting to the broker try_count = 15 while True: From b3f22029e7ec96249d95e5cde238c79a24890457 Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 19:53:50 +0100 Subject: [PATCH 08/14] fix: potential division by zero when calculating the derivative term --- src/crappy/blocks/pid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crappy/blocks/pid.py b/src/crappy/blocks/pid.py index 5445ee64..c69b5b9b 100644 --- a/src/crappy/blocks/pid.py +++ b/src/crappy/blocks/pid.py @@ -178,7 +178,7 @@ def loop(self) -> None: # Calculating the three PID terms p_term = self._kp * error self._i_term += self._ki * error * delta_t - d_term = - self._kd * d_input / delta_t + d_term = - self._kd * d_input / delta_t if delta_t > 0 else 0 self._prev_t = t self._last_input = input_ From 1a20c7d76586434dd25cd3e2615095d017065c0d Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sat, 11 Nov 2023 20:04:06 +0100 Subject: [PATCH 09/14] fix: StopBlock not working with spawn start method of multiprocessing --- src/crappy/blocks/stop_block.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/crappy/blocks/stop_block.py b/src/crappy/blocks/stop_block.py index 50532941..e6081a65 100644 --- a/src/crappy/blocks/stop_block.py +++ b/src/crappy/blocks/stop_block.py @@ -62,8 +62,15 @@ def __init__(self, criteria = (criteria,) criteria = tuple(criteria) - # Ultimately, all the criteria are converted to Callables - self._criteria = tuple(map(self._parse_criterion, criteria)) + self._raw_crit = criteria + self._criteria = None + + def prepare(self) -> None: + """Converts all the given criteria to :ref:`collections.abc.Callable`.""" + + # This operation cannot be performed during __init__ due to limitations of + # the spawn start method of multiprocessing + self._criteria = tuple(map(self._parse_criterion, self._raw_crit)) def loop(self) -> None: """Receives data from upstream Blocks, checks if this data meets the From 9ca14c2c186a8bd3ed82ef242daed07583b2a007 Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sun, 12 Nov 2023 16:37:53 +0100 Subject: [PATCH 10/14] docs: add 3.11 and 3.12 Python version to list of supported ones on PyPI --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 1e8300ac..42ca740f 100755 --- a/setup.py +++ b/setup.py @@ -104,6 +104,8 @@ 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Topic :: Scientific/Engineering', 'Topic :: Software Development :: Build Tools', 'Topic :: Software Development :: Embedded Systems'], From ce23f5b3e3e07c25260d7eeefcc238145611f4be Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Sun, 12 Nov 2023 16:39:05 +0100 Subject: [PATCH 11/14] build: update project version from 2.0.0-rc.0 to 2.0.0 --- docs/source/conf.py | 2 +- src/crappy/__version__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 9185dba5..d3780c7e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,7 +16,7 @@ from time import gmtime, strftime from re import match -__version__ = '2.0.0-rc.0' +__version__ = '2.0.0' # -- Project information ----------------------------------------------------- diff --git a/src/crappy/__version__.py b/src/crappy/__version__.py index de9e01a6..23678b0e 100644 --- a/src/crappy/__version__.py +++ b/src/crappy/__version__.py @@ -1,3 +1,3 @@ # coding: utf-8 -__version__ = '2.0.0-rc.0' +__version__ = '2.0.0' From 5087001528360c816cb79d2f17215725c539bf4b Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Tue, 21 Nov 2023 14:40:53 +0100 Subject: [PATCH 12/14] feat: add deprecation errors for Modifiers renamed in v2.0 from v1.5 --- src/crappy/modifier/__init__.py | 2 + src/crappy/modifier/_deprecated.py | 71 ++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/crappy/modifier/_deprecated.py diff --git a/src/crappy/modifier/__init__.py b/src/crappy/modifier/__init__.py index 83c5448b..ec46e051 100644 --- a/src/crappy/modifier/__init__.py +++ b/src/crappy/modifier/__init__.py @@ -15,4 +15,6 @@ from .trig_on_change import TrigOnChange from .trig_on_value import TrigOnValue +from ._deprecated import Moving_avg, Moving_med, Trig_on_change, Trig_on_value + modifier_dict: Dict[str, Type[Modifier]] = MetaModifier.classes diff --git a/src/crappy/modifier/_deprecated.py b/src/crappy/modifier/_deprecated.py new file mode 100644 index 00000000..b3e41594 --- /dev/null +++ b/src/crappy/modifier/_deprecated.py @@ -0,0 +1,71 @@ +# coding: utf-8 + +from .meta_modifier import Modifier + + +class Moving_avg(Modifier): + """Empty class for signaling an object of version 1.5 whose name changed in + version 2.0 and is now deprecated. + + The new name of the correct object to use is given. + """ + + def __init__(self, *_, **__) -> None: + """Simply raises the exception when instantiating the object.""" + + super().__init__() + + raise NotImplementedError(f"The {type(self).__name__} Modifier was " + f"renamed to MovingAvg in version 2.0.0 ! " + f"Check the documentation for more information.") + + +class Moving_med(Modifier): + """Empty class for signaling an object of version 1.5 whose name changed in + version 2.0 and is now deprecated. + + The new name of the correct object to use is given. + """ + + def __init__(self, *_, **__) -> None: + """Simply raises the exception when instantiating the object.""" + + super().__init__() + + raise NotImplementedError(f"The {type(self).__name__} Modifier was " + f"renamed to MovingMed in version 2.0.0 ! " + f"Check the documentation for more information.") + + +class Trig_on_change(Modifier): + """Empty class for signaling an object of version 1.5 whose name changed in + version 2.0 and is now deprecated. + + The new name of the correct object to use is given. + """ + + def __init__(self, *_, **__) -> None: + """Simply raises the exception when instantiating the object.""" + + super().__init__() + + raise NotImplementedError(f"The {type(self).__name__} Modifier was " + f"renamed to TrigOnChange in version 2.0.0 ! " + f"Check the documentation for more information.") + + +class Trig_on_value(Modifier): + """Empty class for signaling an object of version 1.5 whose name changed in + version 2.0 and is now deprecated. + + The new name of the correct object to use is given. + """ + + def __init__(self, *_, **__) -> None: + """Simply raises the exception when instantiating the object.""" + + super().__init__() + + raise NotImplementedError(f"The {type(self).__name__} Modifier was " + f"renamed to TrigOnValue in version 2.0.0 ! " + f"Check the documentation for more information.") From 9934a825399e204e4e0e1b8fe2a75c553f631b5f Mon Sep 17 00:00:00 2001 From: Antoine Weisrock Date: Tue, 21 Nov 2023 15:19:50 +0100 Subject: [PATCH 13/14] feat: add deprecation errors for deprecated methods of the Block --- src/crappy/blocks/_deprecated.py | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/crappy/blocks/_deprecated.py b/src/crappy/blocks/_deprecated.py index 5e9aaadb..6e56fb41 100644 --- a/src/crappy/blocks/_deprecated.py +++ b/src/crappy/blocks/_deprecated.py @@ -3,6 +3,67 @@ from .meta_block import Block +def recv_all(_): + """Empty function for signaling a deprecated method of the Block object.""" + + raise NotImplementedError("The method recv_all was deprecated in version " + "2.0.0, please use recv_all_data instead !") + + +def poll(_): + """Empty function for signaling a deprecated method of the Block object.""" + + raise NotImplementedError("The method poll was deprecated in version " + "2.0.0, please use data_available instead !") + + +def recv_all_last(_): + """Empty function for signaling a deprecated method of the Block object.""" + + raise NotImplementedError("The method recv_all_last was deprecated in " + "version 2.0.0, please use recv_last_data " + "instead !") + + +def get_last(_, *__, **___): + """Empty function for signaling a deprecated method of the Block object.""" + + raise NotImplementedError("The method get_last was deprecated in version " + "2.0.0, please use recv_last_data instead !") + + +def get_all_last(_, *__, **___): + """Empty function for signaling a deprecated method of the Block object.""" + + raise NotImplementedError("The method get_all_last was deprecated in " + "version 2.0.0, please use recv_all_data " + "instead !") + + +def recv_all_delay(_, *__, **___): + """Empty function for signaling a deprecated method of the Block object.""" + + raise NotImplementedError("The method recv_all_delay was deprecated in " + "version 2.0.0, please use recv_all_data_raw " + "instead !") + + +def drop(_, *__, **___): + """Empty function for signaling a deprecated method of the Block object.""" + + raise NotImplementedError("The method drop was deprecated in version " + "2.0.0 !") + + +setattr(Block, recv_all.__name__, recv_all) +setattr(Block, poll.__name__, poll) +setattr(Block, recv_all_last.__name__, recv_all_last) +setattr(Block, get_last.__name__, get_last) +setattr(Block, get_all_last.__name__, get_all_last) +setattr(Block, recv_all_delay.__name__, recv_all_delay) +setattr(Block, drop.__name__, drop) + + class AutoDrive(Block): """Empty class for signaling an object of version 1.5 whose name changed in version 2.0 and is now deprecated. From e2591cc4ae5cf5785dc02bee9db46eef68664bc9 Mon Sep 17 00:00:00 2001 From: PIERROOOTT Date: Mon, 27 Nov 2023 14:17:34 +0100 Subject: [PATCH 14/14] fix: add settings to the configuration window even if flags=inactive Changing the value of a menu setting can change the flags for some scale settings and make them available. --- src/crappy/camera/gstreamer_camera_v4l2.py | 51 +++++++++++----------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/crappy/camera/gstreamer_camera_v4l2.py b/src/crappy/camera/gstreamer_camera_v4l2.py index 9c52b3d9..8a4f992e 100644 --- a/src/crappy/camera/gstreamer_camera_v4l2.py +++ b/src/crappy/camera/gstreamer_camera_v4l2.py @@ -334,32 +334,31 @@ def open(self, # Create the different settings for param in self.parameters: - if not param.flags: - if param.type == 'int': - self.add_scale_setting(name=param.name, - lowest=int(param.min), - highest=int(param.max), - getter=self._add_scale_getter(param.name), - setter=self._add_setter(param.name), - default=param.default, - step=int(param.step)) - elif param.type == 'bool': - self.add_bool_setting(name=param.name, - getter=self._add_bool_getter(param.name), - setter=self._add_setter(param.name), - default=bool(int(param.default))) - elif param.type == 'menu': - if param.options: - self.add_choice_setting(name=param.name, - choices=param.options, - getter=self._add_menu_getter(param.name), - setter=self._add_setter(param.name), - default=param.default) - else: - self.log(logging.ERROR, f'The type {param.type} is not yet' - f' implemented. Only int, bool and menu ' - f'type are implemented. ') - raise NotImplementedError + if param.type == 'int': + self.add_scale_setting(name=param.name, + lowest=int(param.min), + highest=int(param.max), + getter=self._add_scale_getter(param.name), + setter=self._add_setter(param.name), + default=param.default, + step=int(param.step)) + elif param.type == 'bool': + self.add_bool_setting(name=param.name, + getter=self._add_bool_getter(param.name), + setter=self._add_setter(param.name), + default=bool(int(param.default))) + elif param.type == 'menu': + if param.options: + self.add_choice_setting(name=param.name, + choices=param.options, + getter=self._add_menu_getter(param.name), + setter=self._add_setter(param.name), + default=param.default) + else: + self.log(logging.ERROR, f'The type {param.type} is not yet' + f' implemented. Only int, bool and menu ' + f'type are implemented. ') + raise NotImplementedError self.add_choice_setting(name="channels", choices=('1', '3'), default='1')