Monitoring state of all spinboxes and checkboxes and later applying changes: Need code review, not exactly sure about correct approach
-
I have reduced code a lot so you will not be able to run it as is, I have more question about philosophy and how should I organize logic around what I want to do and I need advice.
Summary:
I have connected to a device, device has many parameters, such as voltages, gains, offsets (spinboxes) or video_enable, shutter_enable and etc., (checkboxes) and so on. I will need to send different parameters to device. Note that there are 250 of these parameters, I have reduced it in example to three parameters.My Intention:
- There is a dataclass for each parameter called ConfigState. It contains the value itself, getter callback (reads value from device and into widget), setter callback (gets value from ConfigState and sends it to device) and reader callback (gets value from widget and loads it into ConfigState.value)
- There is a DeviceState class that tracks the state. Once is current_state, that has currently inside of the device saved state, and other is new_state, that is selected by widgets, but not yet written into device.
- Chaning value in any of the spinboxes, triggers generic update_value() method, it changes appropriate value in new_state, then take diff between current_state and new_state, and if there is difference between two, enable 'Apply Changes' button.
- Clicking 'Apply Changes' button takes parameters that are in diff of new_state and current_state and calls setter callback of new_state of these parameters and sets new_state as current_state.
My concern is that I am not sure if I am doing it correctly and if my organisation of the code is appropriate, I feel like I am using black magic to make this work what if someone will start scrolling through spinbox very fast and update_value will get called 20 times per second resulting in application crash. On the other hand, I am not making setter button for each individual parameter, so I need to handle everything collectively. I post code down below, as I mentioned, number of parameters reduced to 3 for readability, and I could not bother adding entire code, just the relevant part.
P.S. I have not tested the entire code, it's just a rough idea on how will it be.
I am reluctant to start doing everything until I get confirmation, that it is correct approach, as there are 250 parameters and it will be very cumbersome to do either way.@dataclass class ConfigState: value: bool | int | float # actual value getter: Callable | None # method to get the actual value from device and load it into spinbox/checkbox setter: Callable | None # method to set the value from ConfigState.value into the device reader: Callable | None # method to get value from spinbox/checkbox and load it into ConfigState.value @dataclass class DeviceState: video_enable: ConfigState gain: ConfigState offset: ConfigState def __sub__(self, other) -> List[str]: # compares two device states # returns list of variables (as strings) of variables that are different different_fields = [] for field in fields(self): if getattr(self, field.name).value - getattr(other, field.name).value: different_fields.append(field.name) return different_fields class StateMonitor: def __init__(self, main, device) -> None: self.main = main self.diff = None self.current_state = DeviceState( video_enable=ConfigState( value=False, getter=device.video_enable_get, setter=lambda enable: device.video_enable_set(enable), reader=main.cb_video_enable.isChecked), gain=ConfigState( value=0, getter=device.gain_get, setter=lambda gain: device.gain_set(gain), reader=main.sb_gain.value), temperature_sensor=ConfigState( value=0.0, getter=device.offset_get, setter=lambda offset: device.offset_set(offset), reader=main.sb_offset.value)) self.new_state = copy.copy(self.current_state) main.cb_video_enable.stateChanged.connect(self.new_state.video_enable) main.sb_gain.valueChanged.connect(self.new_state.gain) main.sb_offset.stateChanged.connect(self.new_state.offset) main.button_apply_changes.clicked.connect(self.on_apply_changes) @Slot() def update_value(self, config_state: ConfigState) -> None: config_state.value = config_state.reader() self.diff = self.current_state - self.new_state self.main.button_apply_changes.setEnabled(True if self.diff else False) @Slot() def apply_changes(self): self.main.button_apply_changed.setEnabled(False) for config in self.diff: getattr(self.new_state, config).setter() self.current_state = copy.copy(self.new_state)