From 114ff7096e89708ef8133f8f8d7e42882fd44abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Hern=C3=A1ndez=20Cordero?= Date: Tue, 26 Dec 2023 18:13:59 +0100 Subject: [PATCH 1/2] utils.py to include common code and fixed merge (#20) Signed-off-by: Alejandro Hernandez Cordero --- src/rqt_gauges_2/speedometer_gauge.py | 12 ++-- src/rqt_gauges_2/speedometer_widget.py | 59 +------------------ src/rqt_gauges_2/steering_wheel_widget.py | 59 +------------------ .../throttle_brake_pedals_widget.py | 59 +------------------ src/rqt_gauges_2/utils.py | 57 ++++++++++++++++++ 5 files changed, 66 insertions(+), 180 deletions(-) create mode 100644 src/rqt_gauges_2/utils.py diff --git a/src/rqt_gauges_2/speedometer_gauge.py b/src/rqt_gauges_2/speedometer_gauge.py index 509d88e..c860587 100644 --- a/src/rqt_gauges_2/speedometer_gauge.py +++ b/src/rqt_gauges_2/speedometer_gauge.py @@ -149,9 +149,9 @@ def setMinValue(self, min_value): # Modifies the minimum value of the gauge # Args: # min: Value to update the minimum value of the gauge. - if self.value < min: - self.value = min - if min >= self.maxValue: + if self.value < min_value: + self.value = min_value + if min_value >= self.maxValue: self.minValue = self.maxValue - 1 else: self.minValue = min_value @@ -162,9 +162,9 @@ def setMaxValue(self, max_value): # Modifies the maximum value of the gauge # Args: # max: Value to update the maximum value of the gauge. - if self.value > max: - self.value = max - if max <= self.minValue: + if self.value > max_value: + self.value = max_value + if max_value <= self.minValue: self.maxValue = self.minValue + 1 else: self.maxValue = max_value diff --git a/src/rqt_gauges_2/speedometer_widget.py b/src/rqt_gauges_2/speedometer_widget.py index 54b068b..b6acec4 100644 --- a/src/rqt_gauges_2/speedometer_widget.py +++ b/src/rqt_gauges_2/speedometer_widget.py @@ -7,64 +7,7 @@ from rosidl_runtime_py.utilities import get_message from rqt_py_common.topic_completer import TopicCompleter - -def get_topic_type(node, topic): - """ - Subroutine for getting the topic type. - - (nearly identical to rostopic._get_topic_type, except it returns rest of name instead of fn) - - :returns: topic type, real topic name, and rest of name referenced - if the topic points to a field within a topic, e.g. /rosout/msg, ``str, str, str`` - """ - val = node.get_topic_names_and_types() - matches = [(t, t_types) for t, t_types in val if t == topic or topic.startswith(t + '/')] - for t, t_types in matches: - for t_type in t_types: - if t_type == topic: - return t_type, None, None - for t_type in t_types: - if t_type != '*': - return t_type, t, topic[len(t):] - return None, None, None - - -def _array_eval(field_name, slot_num): - """ - Array evaluation. - - :param field_name: name of field to index into, ``str`` - :param slot_num: index of slot to return, ``str`` - :returns: fn(msg_field)->msg_field[slot_num] - """ - def fn(f): - return getattr(f, field_name).__getitem__(slot_num) - return fn - - -def _field_eval(field_name): - """ - Field evaluation. - - :param field_name: name of field to return, ``str`` - :returns: fn(msg_field)->msg_field.field_name - """ - def fn(f): - return getattr(f, field_name) - return fn - - -def generate_field_evals(fields): - evals = [] - fields = [f for f in fields.split('/') if f] - for f in fields: - if '[' in f: - field_name, rest = f.split('[') - slot_num = int(rest[:rest.find(']')]) - evals.append(_array_eval(field_name, slot_num)) - else: - evals.append(_field_eval(f)) - return evals +from .utils import generate_field_evals, get_topic_type class SpeedometerWidget(QWidget): diff --git a/src/rqt_gauges_2/steering_wheel_widget.py b/src/rqt_gauges_2/steering_wheel_widget.py index 1203ed7..2e751f4 100644 --- a/src/rqt_gauges_2/steering_wheel_widget.py +++ b/src/rqt_gauges_2/steering_wheel_widget.py @@ -7,64 +7,7 @@ from rosidl_runtime_py.utilities import get_message from rqt_py_common.topic_completer import TopicCompleter - -def get_topic_type(node, topic): - """ - Subroutine for getting the topic type. - - (nearly identical to rostopic._get_topic_type, except it returns rest of name instead of fn) - - :returns: topic type, real topic name, and rest of name referenced - if the topic points to a field within a topic, e.g. /rosout/msg, ``str, str, str`` - """ - val = node.get_topic_names_and_types() - matches = [(t, t_types) for t, t_types in val if t == topic or topic.startswith(t + '/')] - for t, t_types in matches: - for t_type in t_types: - if t_type == topic: - return t_type, None, None - for t_type in t_types: - if t_type != '*': - return t_type, t, topic[len(t):] - return None, None, None - - -def _array_eval(field_name, slot_num): - """ - Array Evaluation. - - :param field_name: name of field to index into, ``str`` - :param slot_num: index of slot to return, ``str`` - :returns: fn(msg_field)->msg_field[slot_num] - """ - def fn(f): - return getattr(f, field_name).__getitem__(slot_num) - return fn - - -def _field_eval(field_name): - """ - Field evaluation. - - :param field_name: name of field to return, ``str`` - :returns: fn(msg_field)->msg_field.field_name - """ - def fn(f): - return getattr(f, field_name) - return fn - - -def generate_field_evals(fields): - evals = [] - fields = [f for f in fields.split('/') if f] - for f in fields: - if '[' in f: - field_name, rest = f.split('[') - slot_num = int(rest[:rest.find(']')]) - evals.append(_array_eval(field_name, slot_num)) - else: - evals.append(_field_eval(f)) - return evals +from .utils import generate_field_evals, get_topic_type class SteeringWheelWidget(QWidget): diff --git a/src/rqt_gauges_2/throttle_brake_pedals_widget.py b/src/rqt_gauges_2/throttle_brake_pedals_widget.py index 38d5afb..29fee6d 100644 --- a/src/rqt_gauges_2/throttle_brake_pedals_widget.py +++ b/src/rqt_gauges_2/throttle_brake_pedals_widget.py @@ -7,64 +7,7 @@ from rosidl_runtime_py.utilities import get_message from rqt_py_common.topic_completer import TopicCompleter - -def get_topic_type(node, topic): - """ - Subroutine for getting the topic type. - - (nearly identical to rostopic._get_topic_type, except it returns rest of name instead of fn) - - :returns: topic type, real topic name, and rest of name referenced - if the topic points to a field within a topic, e.g. /rosout/msg, ``str, str, str`` - """ - val = node.get_topic_names_and_types() - matches = [(t, t_types) for t, t_types in val if t == topic or topic.startswith(t + '/')] - for t, t_types in matches: - for t_type in t_types: - if t_type == topic: - return t_type, None, None - for t_type in t_types: - if t_type != '*': - return t_type, t, topic[len(t):] - return None, None, None - - -def _array_eval(field_name, slot_num): - """ - Array Evaluation. - - :param field_name: name of field to index into, ``str`` - :param slot_num: index of slot to return, ``str`` - :returns: fn(msg_field)->msg_field[slot_num] - """ - def fn(f): - return getattr(f, field_name).__getitem__(slot_num) - return fn - - -def _field_eval(field_name): - """ - Field evaluation. - - :param field_name: name of field to return, ``str`` - :returns: fn(msg_field)->msg_field.field_name - """ - def fn(f): - return getattr(f, field_name) - return fn - - -def generate_field_evals(fields): - evals = [] - fields = [f for f in fields.split('/') if f] - for f in fields: - if '[' in f: - field_name, rest = f.split('[') - slot_num = int(rest[:rest.find(']')]) - evals.append(_array_eval(field_name, slot_num)) - else: - evals.append(_field_eval(f)) - return evals +from .utils import generate_field_evals, get_topic_type class ThrottleBrakePedalsWidget(QWidget): diff --git a/src/rqt_gauges_2/utils.py b/src/rqt_gauges_2/utils.py new file mode 100644 index 0000000..c3d0941 --- /dev/null +++ b/src/rqt_gauges_2/utils.py @@ -0,0 +1,57 @@ +def get_topic_type(node, topic): + """ + Subroutine for getting the topic type. + + (nearly identical to rostopic._get_topic_type, except it returns rest of name instead of fn) + + :returns: topic type, real topic name, and rest of name referenced + if the topic points to a field within a topic, e.g. /rosout/msg, ``str, str, str`` + """ + val = node.get_topic_names_and_types() + matches = [(t, t_types) for t, t_types in val if t == topic or topic.startswith(t + '/')] + for t, t_types in matches: + for t_type in t_types: + if t_type == topic: + return t_type, None, None + for t_type in t_types: + if t_type != '*': + return t_type, t, topic[len(t):] + return None, None, None + + +def _array_eval(field_name, slot_num): + """ + Array evaluation. + + :param field_name: name of field to index into, ``str`` + :param slot_num: index of slot to return, ``str`` + :returns: fn(msg_field)->msg_field[slot_num] + """ + def fn(f): + return getattr(f, field_name).__getitem__(slot_num) + return fn + + +def _field_eval(field_name): + """ + Field evaluation. + + :param field_name: name of field to return, ``str`` + :returns: fn(msg_field)->msg_field.field_name + """ + def fn(f): + return getattr(f, field_name) + return fn + + +def generate_field_evals(fields): + evals = [] + fields = [f for f in fields.split('/') if f] + for f in fields: + if '[' in f: + field_name, rest = f.split('[') + slot_num = int(rest[:rest.find(']')]) + evals.append(_array_eval(field_name, slot_num)) + else: + evals.append(_field_eval(f)) + return evals From 3b10255b0cb28c028017f036b1f9691a06e69aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Hern=C3=A1ndez=20Cordero?= Date: Wed, 27 Dec 2023 08:36:06 +0100 Subject: [PATCH 2/2] Fix some crashes (#21) Signed-off-by: Alejandro Hernandez Cordero --- src/rqt_gauges_2/speedometer_widget.py | 10 ++++++-- src/rqt_gauges_2/steering_wheel_gauge.py | 1 + src/rqt_gauges_2/steering_wheel_widget.py | 8 ++++++- .../throttle_brake_pedals_widget.py | 24 ++++++++++++------- src/rqt_gauges_2/utils.py | 24 ++++++++++++------- 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/rqt_gauges_2/speedometer_widget.py b/src/rqt_gauges_2/speedometer_widget.py index b6acec4..2e38db3 100644 --- a/src/rqt_gauges_2/speedometer_widget.py +++ b/src/rqt_gauges_2/speedometer_widget.py @@ -72,7 +72,7 @@ def updateSubscription(self): topic_path = self.topic_to_subscribe.text() topic_type, topic_name, fields = get_topic_type(self.node, topic_path) self.field_evals = generate_field_evals(fields) - if topic_type is not None: + if topic_type is not None and self.field_evals is not None: print('Subscribing to:', topic_name, 'Type:', topic_type, 'Field:', fields) data_class = get_message(topic_type) self.sub = self.node.create_subscription( @@ -85,4 +85,10 @@ def speedometer_callback(self, msg): value = msg for f in self.field_evals: value = f(value) - self.speedometer_gauge.updateValue(float(value)) + if value is not None: + if type(value) == int or type(value) == float or type(value) == str: + self.speedometer_gauge.updateValue(float(value)) + else: + self.speedometer_gauge.updateValue(self.speedometer_gauge.minValue) + else: + self.speedometer_gauge.updateValue(self.speedometer_gauge.minValue) diff --git a/src/rqt_gauges_2/steering_wheel_gauge.py b/src/rqt_gauges_2/steering_wheel_gauge.py index 4735279..2d98356 100644 --- a/src/rqt_gauges_2/steering_wheel_gauge.py +++ b/src/rqt_gauges_2/steering_wheel_gauge.py @@ -98,6 +98,7 @@ def updateValue(self, value: float): # value: Value to update the gauge with. value = max(value, self.minValue) value = min(value, self.maxValue) + self.value = value self.repaint() def draw_background_circle(self): diff --git a/src/rqt_gauges_2/steering_wheel_widget.py b/src/rqt_gauges_2/steering_wheel_widget.py index 2e751f4..66631eb 100644 --- a/src/rqt_gauges_2/steering_wheel_widget.py +++ b/src/rqt_gauges_2/steering_wheel_widget.py @@ -54,4 +54,10 @@ def steering_wheel_callback(self, msg): value = msg for f in self.field_evals: value = f(value) - self.steering_wheel_gauge.updateValue(float(value)) + if value is not None: + if type(value) == int or type(value) == float or type(value) == str: + self.steering_wheel_gauge.updateValue(float(value)) + else: + self.steering_wheel_gauge.updateValue(0) + else: + self.steering_wheel_gauge.updateValue(0) diff --git a/src/rqt_gauges_2/throttle_brake_pedals_widget.py b/src/rqt_gauges_2/throttle_brake_pedals_widget.py index 29fee6d..7dffdb6 100644 --- a/src/rqt_gauges_2/throttle_brake_pedals_widget.py +++ b/src/rqt_gauges_2/throttle_brake_pedals_widget.py @@ -81,18 +81,26 @@ def throttle_callback(self, msg): value = msg for f in self.throttle_field_evals: value = f(value) - if value <= 1 and value >= 0: - self.throttle_pedal.setValue(int(value*100)) - self.throttle_label.setText(str(value)) + if value is not None and (type(value) == int or type(value) == float + or type(value) == str): + if value <= 1 and value >= 0: + self.throttle_pedal.setValue(int(value*100)) + self.throttle_label.setText(str(value)) + else: + print('The throttle pedal value is not between 0 and 1') else: - print('The throttle pedal value is not between 0 and 1') + print('The throttle pedal value is not valid') def brake_callback(self, msg): value = msg for f in self.brake_field_evals: value = f(value) - if value <= 1 and value >= 0: - self.brake_pedal.setValue(int(value*100)) - self.brake_label.setText(str(value)) + if value is not None and (type(value) == int or type(value) == float + or type(value) == str): + if value <= 1 and value >= 0: + self.brake_pedal.setValue(int(value*100)) + self.brake_label.setText(str(value)) + else: + print('The brake pedal value is not between 0 and 1') else: - print('The brake pedal value is not between 0 and 1') + print('The brake pedal value is not valid') diff --git a/src/rqt_gauges_2/utils.py b/src/rqt_gauges_2/utils.py index c3d0941..70a0ce9 100644 --- a/src/rqt_gauges_2/utils.py +++ b/src/rqt_gauges_2/utils.py @@ -40,18 +40,24 @@ def _field_eval(field_name): :returns: fn(msg_field)->msg_field.field_name """ def fn(f): - return getattr(f, field_name) + if (hasattr(f, field_name)): + return getattr(f, field_name) return fn def generate_field_evals(fields): evals = [] - fields = [f for f in fields.split('/') if f] - for f in fields: - if '[' in f: - field_name, rest = f.split('[') - slot_num = int(rest[:rest.find(']')]) - evals.append(_array_eval(field_name, slot_num)) - else: - evals.append(_field_eval(f)) + if fields is not None: + fields = [f for f in fields.split('/') if f] + for f in fields: + if '[' in f: + field_name, rest = f.split('[') + slot_num = int(rest[:rest.find(']')]) + value = _array_eval(field_name, slot_num) + if value is not None: + evals.append(value) + else: + value = _field_eval(f) + if value is not None: + evals.append(value) return evals