diff --git a/src/urh/controller/CompareFrameController.py b/src/urh/controller/CompareFrameController.py index e37a0a70..8ecaeb08 100644 --- a/src/urh/controller/CompareFrameController.py +++ b/src/urh/controller/CompareFrameController.py @@ -18,6 +18,7 @@ from urh.models.ProtocolLabelListModel import ProtocolLabelListModel from urh.models.ProtocolTableModel import ProtocolTableModel from urh.models.ProtocolTreeModel import ProtocolTreeModel from urh.plugins.PluginManager import PluginManager +from urh.signalprocessing.FieldType import FieldType from urh.signalprocessing.MessageType import MessageType from urh.signalprocessing.ProtocoLabel import ProtocolLabel from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer @@ -139,6 +140,10 @@ class CompareFrameController(QFrame): self.__set_default_message_type_ui_status() + self.field_types = FieldType.load_from_xml() + self.field_types_by_id = {field_type.id: field_type for field_type in self.field_types} + self.field_types_by_caption = {field_type.caption: field_type for field_type in self.field_types} + @property def active_group_ids(self): """ @@ -192,8 +197,8 @@ class CompareFrameController(QFrame): self.update_field_type_combobox() def update_field_type_combobox(self): - field_types = [t.value for t in ProtocolLabel.Type if t.value] + self.active_message_type.custom_field_types - self.ui.listViewLabelNames.setItemDelegate(ComboBoxDelegate(field_types, is_editable=True)) + field_types = [ft.caption for ft in self.field_types] + self.ui.listViewLabelNames.setItemDelegate(ComboBoxDelegate(field_types, is_editable=True, return_index=False)) def handle_files_dropped(self, files: list): @@ -1448,3 +1453,19 @@ class CompareFrameController(QFrame): self.ui.tblViewProtocol.setRowHidden(i, hide) self.set_shown_protocols() + + def reload_field_types(self): + self.field_types = FieldType.load_from_xml() + self.field_types_by_id = {field_type.id: field_type for field_type in self.field_types} + self.field_types_by_caption = {field_type.caption: field_type for field_type in self.field_types} + + def refresh_field_types_for_labels(self): + self.reload_field_types() + for mt in self.proto_analyzer.message_types: + for lbl in (lbl for lbl in mt if lbl.type is not None): # type: ProtocolLabel + if lbl.type.id not in self.field_types_by_id: + lbl.type = None + else: + lbl.type = self.field_types_by_id[lbl.type.id] + + self.update_field_type_combobox() \ No newline at end of file diff --git a/src/urh/controller/MainController.py b/src/urh/controller/MainController.py index 688ea55d..ef23b7ed 100644 --- a/src/urh/controller/MainController.py +++ b/src/urh/controller/MainController.py @@ -656,6 +656,7 @@ class MainController(QMainWindow): for sf in self.signal_tab_controller.signal_frames: sf.refresh_protocol() + self.compare_frame_controller.refresh_field_types_for_labels() self.compare_frame_controller.set_shown_protocols() if "default_view" in changed_options.keys(): diff --git a/src/urh/models/ProtocolLabelListModel.py b/src/urh/models/ProtocolLabelListModel.py index 15455322..aeb4f5d8 100644 --- a/src/urh/models/ProtocolLabelListModel.py +++ b/src/urh/models/ProtocolLabelListModel.py @@ -1,6 +1,9 @@ from PyQt5.QtCore import QAbstractListModel, pyqtSignal, Qt, QModelIndex, QMimeData +from PyQt5.QtGui import QFont from urh import constants +from urh.signalprocessing.FieldType import FieldType +from urh.signalprocessing.MessageType import MessageType from urh.signalprocessing.ProtocoLabel import ProtocolLabel from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer @@ -13,20 +16,15 @@ class ProtocolLabelListModel(QAbstractListModel): def __init__(self, proto_analyzer: ProtocolAnalyzer, controller, parent=None): super().__init__(parent) self.proto_analyzer = proto_analyzer - self.message_type = controller.active_message_type - """:type urh.signalprocessing.MessageType.MessageType""" + self.message_type = controller.active_message_type # type: MessageType - self.controller = controller - """:type: urh.controller.CompareFrameController.CompareFrameController""" - - self.field_types = [t for t in ProtocolLabel.Type if t.value] # without custom + self.controller = controller # type: CompareFrameController def rowCount(self, QModelIndex_parent=None, *args, **kwargs): return len(self.message_type) def update(self): - self.message_type = self.controller.active_message_type - """:type: urh.signalprocessing.MessageType.MessageType """ + self.message_type = self.controller.active_message_type # type: MessageType self.layoutChanged.emit() @@ -35,12 +33,19 @@ class ProtocolLabelListModel(QAbstractListModel): if row >= len(self.message_type): return + label = self.message_type[row] + + if role == Qt.DisplayRole: - return self.message_type[row].name + return label.name elif role == Qt.CheckStateRole: - return self.message_type[row].show + return label.show elif role == Qt.BackgroundColorRole: - return constants.LABEL_COLORS[self.message_type[row].color_index] + return constants.LABEL_COLORS[label.color_index] + elif role == Qt.FontRole: + font = QFont() + font.setItalic(label.type is None or label.type.function == FieldType.Function.CUSTOM) + return font def setData(self, index: QModelIndex, value, role=Qt.DisplayRole): @@ -50,13 +55,11 @@ class ProtocolLabelListModel(QAbstractListModel): self.protolabel_visibility_changed.emit(proto_label) elif role == Qt.EditRole: proto_label = self.message_type[index.row()] - try: - field_type = self.field_types[value] - proto_label.type = field_type - proto_label.name = field_type.value - except IndexError: - proto_label.type = ProtocolLabel.Type.CUSTOM - proto_label.name = self.message_type.custom_field_types[value - len(self.field_types)] + proto_label.name = value + if value in self.controller.field_types_by_caption: + proto_label.type = self.controller.field_types_by_caption[value] + else: + proto_label.type = None self.protolabel_type_edited.emit() diff --git a/src/urh/signalprocessing/FieldType.py b/src/urh/signalprocessing/FieldType.py index 19d71efe..b5017b78 100644 --- a/src/urh/signalprocessing/FieldType.py +++ b/src/urh/signalprocessing/FieldType.py @@ -1,3 +1,5 @@ +import random +import string from enum import Enum import xml.etree.cElementTree as ET from xml.dom import minidom @@ -7,7 +9,7 @@ from urh import constants class FieldType(object): - __slots__ = ["caption", "function", "display_format_index"] + __slots__ = ["caption", "function", "display_format_index", "__id"] class Function(Enum): PREAMBLE = "preamble" @@ -19,7 +21,7 @@ class FieldType(object): CRC = "crc" CUSTOM = "custom" - def __init__(self, caption: str, function: Function, display_format_index:int = None): + def __init__(self, caption: str, function: Function, display_format_index:int = None, id=None): self.caption = caption self.function = function @@ -35,6 +37,24 @@ class FieldType(object): else: self.display_format_index = display_format_index + if id is None: + self.__id = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(50)) + else: + self.__id = id + + def __eq__(self, other): + return isinstance(other, FieldType) and self.id_match(other.id) + + @property + def id(self): + return self.__id + + def id_match(self, id): + return self.__id == id + + def __hash__(self): + return hash(self.id) + def __repr__(self): return "FieldType: {0} - {1} ({2})".format(self.function.name, self.caption, self.display_format_index) @@ -58,15 +78,32 @@ class FieldType(object): result = [] for tag in e.findall("field_type"): - caption = tag.get("caption", "") - function = FieldType.Function[tag.get("function", "CUSTOM")] - display_format_index = int(tag.get("display_format_index", -1)) - display_format_index = None if display_format_index == -1 else display_format_index - - result.append(FieldType(caption, function, display_format_index)) + result.append(FieldType.from_xml(tag)) return result + def to_xml(self): + return ET.Element("field_type", attrib={ "id": self.id, + "caption": self.caption, + "function": self.function.name, + "display_format_index": str(self.display_format_index)}) + @staticmethod + def from_xml(tag): + """ + + :param tag: ET.Element + :rtype: FieldType + """ + caption = tag.get("caption", "") + function = FieldType.Function[tag.get("function", "CUSTOM")] + display_format_index = int(tag.get("display_format_index", -1)) + display_format_index = None if display_format_index == -1 else display_format_index + + id = tag.get("id", None) + + return FieldType(caption, function, display_format_index, id=id) + + @staticmethod def save_to_xml(field_types): """ @@ -76,10 +113,7 @@ class FieldType(object): """ root = ET.Element("field_types") for field_type in field_types: - root.append(ET.Element("field_type", attrib={ - "caption": field_type.caption, - "function": field_type.function.name, - "display_format_index": str(field_type.display_format_index)})) + root.append(field_type.to_xml()) xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent=" ") with open(constants.FIELD_TYPE_SETTINGS, "w") as f: diff --git a/src/urh/signalprocessing/ProtocoLabel.py b/src/urh/signalprocessing/ProtocoLabel.py index 004f1c15..94b17eb8 100644 --- a/src/urh/signalprocessing/ProtocoLabel.py +++ b/src/urh/signalprocessing/ProtocoLabel.py @@ -3,6 +3,7 @@ from enum import Enum from PyQt5.QtCore import Qt import xml.etree.ElementTree as ET +from urh.signalprocessing.FieldType import FieldType from urh.signalprocessing.Interval import Interval from urh.util.Formatter import Formatter @@ -17,16 +18,6 @@ class ProtocolLabel(object): DISPLAY_FORMATS = ["Bit", "Hex", "ASCII", "Decimal"] SEARCH_TYPES = ["Number", "Bits", "Hex", "ASCII"] - class Type(Enum): - PREAMBLE = "preamble" - SYNC = "synchronization" - SRC_ADDRESS = "source address" - DST_ADDRESS = "destination address" - - SEQUENCE_NUMBER = "sequence number" - CRC = "crc" - CUSTOM = "" - def __init__(self, name: str, start: int, end: int, color_index: int, fuzz_created=False, auto_created=False): self.__name = name self.start = start @@ -41,12 +32,24 @@ class ProtocolLabel(object): self.fuzz_created = fuzz_created - self.__type = ProtocolLabel.Type.CUSTOM + self.__type = None # type: FieldType self.display_format_index = 0 self.auto_created = auto_created + @property + def type(self) -> FieldType: + return self.__type + + @type.setter + def type(self, value: FieldType): + if value != self.type: + self.__type = value + # set viewtype for type + if hasattr(value, "display_format_index"): + self.display_format_index = value.display_format_index + @property def name(self): if not self.__name: @@ -113,7 +116,7 @@ class ProtocolLabel(object): "apply_decoding": str(self.apply_decoding), "index": str(index), "show": str(self.show), "display_format_index": str(self.display_format_index), "fuzz_me": str(self.fuzz_me), "fuzz_values": ",".join(self.fuzz_values), - "auto_created": str(self.auto_created), "type": self.type.name}) + "auto_created": str(self.auto_created), "type_id": self.type.id}) @staticmethod def from_xml(tag: ET.Element): @@ -128,6 +131,7 @@ class ProtocolLabel(object): result.fuzz_values = tag.get("fuzz_values", "").split(",") result.display_format_index = int(tag.get("display_format_index", 0)) result.auto_created = True if tag.get("auto_created", 'False') == "True" else False - result.type = ProtocolLabel.Type[tag.get("type", '')] + type_id = tag.get("type_id", None) + result.type = ProtocolLabel.Type[tag.get("type", None)] # TODO: Need dict with type ids, to assign type return result \ No newline at end of file