diff --git a/pyproject.toml b/pyproject.toml index 97d1b82..563e7ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ dependencies = [ repository = "https://github.com/sigmf/sigmf-python" [project.scripts] sigmf_validate = "sigmf.validate:main" - sigmf_gui = "sigmf.apps.gui:main [apps]" sigmf_convert_wav = "sigmf.apps.convert_wav:main [apps]" [project.optional-dependencies] test = [ @@ -36,7 +35,6 @@ dependencies = [ "hypothesis", # next-gen testing framework ] apps = [ - "PySimpleGUI", # for gui interface "scipy", # for wav i/o ] @@ -59,7 +57,7 @@ source = ["sigmf", "tests"] command_line = "-m pytest -rA --doctest-modules --junitxml=pytest.xml" [tool.pytest.ini_options] -addopts = "--doctest-modules --ignore=sigmf/apps/gui.py" +addopts = "--doctest-modules" [tool.pylint] [tool.pylint.main] diff --git a/sigmf/apps/gui.py b/sigmf/apps/gui.py deleted file mode 100644 index ce2ec69..0000000 --- a/sigmf/apps/gui.py +++ /dev/null @@ -1,643 +0,0 @@ -# Copyright: Multiple Authors -# -# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python -# -# SPDX-License-Identifier: LGPL-3.0-or-later - -'''GUI for creating & editing SigMF Files''' - -import argparse -import logging -import os - -import PySimpleGUI as sg - -from .. import __version__ as toolversion -from ..archive import SIGMF_ARCHIVE_EXT -from ..sigmffile import SigMFFile, dtype_info, fromarchive - -log = logging.getLogger() - -validate_button = sg.Button('Update', bind_return_key=False, enable_events=True) -submit_button = sg.Button('Save Archive', disabled=True, button_color=('white', '#D3D3D3')) -load_button = sg.Button('Load', key='Load Archive') -combo_button = sg.InputCombo((), size=(20, 1), enable_events=True, key='Capture Combo') - - -class Unit: - MHZ = 'MHz' - US = 'us' - DBI = 'dBi' - - @staticmethod - def convert(unit, value: float): - if unit is None: - return input - elif unit == Unit.MHZ: - return value * 1e6 - elif unit == Unit.US: - return value * 1e-6 - else: - return value - - -class WindowElementGroup(): - def __init__(self, element_list, sigmf_tags, req_tags, el_types, el_units, el_tooltip, el_text, el_help, - el_multiline, el_selector, el_checkbox, el_size): - self.element_list = element_list - self.sigmf_tags = sigmf_tags - self.req_tags = req_tags - self.el_types = el_types - self.el_units = el_units - self.el_tooltips = el_tooltip - self.el_text = el_text - self.el_help = el_help - self.el_multiline = el_multiline - self.el_selector = el_selector - self.el_checkbox = el_checkbox - self.el_size = el_size - - def iter(self): - for el in self.element_list: - yield el - - def iter_x(self, x): - next_tuple = () - for el in self.iter(): - next_tuple += (el,) - if len(next_tuple) == x: - yield next_tuple - next_tuple = () - if next_tuple != (): - yield next_tuple + tuple([None] * (x - len(next_tuple))) - - def get_tag(self, key): - return key if key not in self.sigmf_tags else self.sigmf_tags[key] - - def get_el(self, tag): - if tag not in self.sigmf_tags: - return tag - else: - for el, val in self.sigmf_tags: - if val == tag: - return el - raise show_error('Element {} is not in window group key,tag list'.format(tag)) - - -class WindowInput(WindowElementGroup): - DATA_FILE = 'Data File' - OUTPUT_FOLDER = 'Output Folder' - LOAD_PATH = 'Load saved archive' - DATA_TYPE_COMPLEX = 'Complex?' - DATA_TYPE_UNSIGNED = 'Unsigned?' - DATA_TYPE_FIXEDPOINT = 'Fixedpoint?' - DATA_SAMPLE_SIZE = 'Data Sample Size' - DATA_BYTE_ORDER = 'Data Byte Order' - - def __init__(self): - SAMPLING_RATE = 'Sampling Rate' - DATA_OFFSET = 'Data Offset' - DESCRIPTION = 'Description' - AUTHOR = 'Author' - DATE = 'Date' - HARDWARE = 'Hardware' - RECEIVER_LAT = 'Receiver Lat' - RECEIVER_LON = 'Receiver Lon' - NORM_TECH = 'Normalization Technique' - ANTEN_POL = 'Antenna Polarization' - ANTEN_GAIN = 'Antenna Gain' - - self.core_element_list = [ - WindowInput.DATA_TYPE_COMPLEX, - WindowInput.DATA_TYPE_UNSIGNED, - WindowInput.DATA_TYPE_FIXEDPOINT, - WindowInput.DATA_SAMPLE_SIZE, - WindowInput.DATA_BYTE_ORDER, - DATA_OFFSET, DESCRIPTION, AUTHOR, DATE, SAMPLING_RATE, - ] - self.secondary_element_list = [HARDWARE, NORM_TECH, RECEIVER_LAT, ANTEN_POL, RECEIVER_LON, ANTEN_GAIN] - self.file_element_list = [WindowInput.DATA_FILE, WindowInput.OUTPUT_FOLDER] - self.partial_component_list = [ - WindowInput.DATA_TYPE_COMPLEX, - WindowInput.DATA_TYPE_UNSIGNED, - WindowInput.DATA_TYPE_FIXEDPOINT, - WindowInput.DATA_SAMPLE_SIZE, - WindowInput.DATA_BYTE_ORDER, - ] - sigmf_tags = { - DESCRIPTION: SigMFFile.DESCRIPTION_KEY, - SAMPLING_RATE: SigMFFile.SAMPLE_RATE_KEY, - AUTHOR: SigMFFile.AUTHOR_KEY, - DATE: SigMFFile.DATETIME_KEY, - HARDWARE: SigMFFile.HW_KEY, - RECEIVER_LAT: SigMFFile.LAT_KEY, - RECEIVER_LON: SigMFFile.LON_KEY, - } - req_tags = [WindowInput.DATA_FILE, WindowInput.DATA_SAMPLE_SIZE, WindowInput.DATA_BYTE_ORDER, SAMPLING_RATE] - el_types = { - WindowInput.DATA_TYPE_COMPLEX: bool, - WindowInput.DATA_TYPE_UNSIGNED: bool, - WindowInput.DATA_TYPE_FIXEDPOINT: bool, - DATA_OFFSET: int, RECEIVER_LAT: float, - RECEIVER_LON: float, ANTEN_GAIN: float, SAMPLING_RATE: float, - } - el_units = {ANTEN_GAIN: Unit.DBI} - el_tooltip = { - DATE: 'YYYY-MM-DD', - DATA_OFFSET: 'int: bit offset from start of data file of first element'} - el_text = {} - el_help = {WindowInput.DATA_BYTE_ORDER: 'Data Type Help'} - el_multiline = [DESCRIPTION] - el_selector = {WindowInput.DATA_SAMPLE_SIZE: [8, 16, 32, 64], - WindowInput.DATA_BYTE_ORDER: ['little endian', 'big endian']} - el_checkbox = [WindowInput.DATA_TYPE_COMPLEX, WindowInput.DATA_TYPE_UNSIGNED, WindowInput.DATA_TYPE_FIXEDPOINT] - el_size = { - WindowInput.DATA_TYPE_COMPLEX: (3, 1), - WindowInput.DATA_TYPE_UNSIGNED: (3, 1), - WindowInput.DATA_TYPE_FIXEDPOINT: (3, 1), - WindowInput.DATA_SAMPLE_SIZE: (4, 1), - WindowInput.DATA_BYTE_ORDER: (15, 1), - } - self.first_line_size = 5 - super().__init__(self.core_element_list + self.secondary_element_list, sigmf_tags, req_tags, el_types, el_units, - el_tooltip, el_text, el_help, el_multiline, el_selector, el_checkbox, el_size) - - def iter_core(self): - for el in self.core_element_list: - yield el - - def iter_secondary(self): - for el in self.secondary_element_list: - yield el - - def iter_x_secondary(self, x): - next_tuple = () - for el in self.iter_secondary(): - next_tuple += (el,) - if len(next_tuple) == x: - yield next_tuple - next_tuple = () - if next_tuple != (): - yield next_tuple + tuple([None] * (x - len(next_tuple))) - - -class CaptureData(WindowElementGroup): - START_INDEX = 'Start Index' - - def __init__(self): - EMITTER = 'Emitter' - MODE_OF_OPERATION = 'Mode(s) of Operation' - RECEIVER_RF = 'Receiver RF' - SIGNAL_BANDWIDTH = 'Signal Bandwidth' - MODULATION = 'Modulation' - PRF = 'PRF' - STAGGER = 'Stagger' - FREQUENCY_HOPPING = 'Frequency Hopping' - PW = 'Pulse Width' - BEAM_PATTERN = 'Observed Beam Pattern' - SNR = 'Observed SNR' - CHIP_RATE = 'Chip Rate' - - COMMENT = 'Comment' - - self.annotation_element_list = [COMMENT] - element_list = [CaptureData.START_INDEX, EMITTER, MODE_OF_OPERATION, RECEIVER_RF, SIGNAL_BANDWIDTH, - MODULATION, PRF, STAGGER, FREQUENCY_HOPPING, PW, BEAM_PATTERN, SNR, - CHIP_RATE] + self.annotation_element_list - sigmf_tags = { - CaptureData.START_INDEX: SigMFFile.START_INDEX_KEY, - RECEIVER_RF: SigMFFile.FREQUENCY_KEY, - COMMENT: SigMFFile.COMMENT_KEY - } - req_tags = [CaptureData.START_INDEX] - el_types = { - CaptureData.START_INDEX: int, - RECEIVER_RF: float, SIGNAL_BANDWIDTH: float, - PRF: float, PW: float, SNR: float, CHIP_RATE: float, - } - el_units = {PW: Unit.US, SIGNAL_BANDWIDTH: Unit.MHZ, RECEIVER_RF: Unit.MHZ} - el_tooltip = {CaptureData.START_INDEX: 'int: start index in file of capture'} - el_text = {} - el_help = {} - el_multiline = [] - el_selector = {} - el_checkbox = [] - el_size = {} - super().__init__(element_list, sigmf_tags, req_tags, el_types, el_units, el_tooltip, el_text, el_help, - el_multiline, el_selector, el_checkbox, el_size) - - -def update_dictionary(dictionary, key, val): - dictionary[key] = val - - -def add_sigmf_field(funct, values, field_name, *args, required=False, type=None, unit=None, **kwargs): - input = str(values[field_name]) if field_name in values else '' - log.debug(f'args {args}') - log.debug(f'input {input}') - log.debug(f'kwargs {kwargs}') - if input != '': - if type == int: - if '.' in input: - show_error('Expected an integer for: {}'.format(field_name)) - return False - try: - input = int(input) - except ValueError: - show_error('Expected an integer for: {}'.format(field_name)) - return False - elif type == float: - try: - input = float(input) - except ValueError: - show_error('Expected a double for: {}'.format(field_name)) - return False - elif type == bool: - try: - if input not in ['False', 'True']: - raise ValueError('Unexpected input for boolean') - input = True if input == 'True' else False - except ValueError: - show_error('Expected a bool for: {}'.format(field_name)) - return False - Unit.convert(unit, input) - try: - if kwargs: - funct(*args, input, **kwargs) - else: - funct(*args, input) - except UserWarning as w: - sg.Popup('Warning: {}'.format(repr(w)), title='Warning') - except Exception as e: - show_error(repr(e)) - return False - elif required: - show_error('Missing required field: {}'.format(field_name)) - return False - return True - - -def show_error(message): - sg.PopupError(message, title='Error', line_width=60) - - -def validate_data(file): - isValid = file.validate() - log.info(f'valid: {isValid}') - if not isValid: - show_error('Metadata not valid: ' + isValid.error) - submit_button.Update(disabled=True) - return False - else: - sg.PopupOK('Data is valid\n', file.dumps(pretty=True), title='') - return True - - -def update_capture_screen(capture_data_input, capture_text_blocks, capture_dict): - for el in capture_data_input.iter(): - tag = capture_data_input.get_tag(el) - if capture_dict is not None and tag in capture_dict: - capture_text_blocks[el].Update(value=capture_dict[tag]) - else: - capture_text_blocks[el].Update(value='') - - -def update_global_screen(window_data_input, window_text_blocks, window_dict, archive): - data_type = window_dict[SigMFFile.DATATYPE_KEY] - data_info = dtype_info(data_type) - sample_size = 64 if '64' in data_type else 32 if '32' in data_type else 16 if '16' in data_type else 8 if '8' in data_type else None - assert sample_size is not None - window_text_blocks[WindowInput.DATA_TYPE_FIXEDPOINT].Update(bool(data_info['is_fixedpoint'])) - window_text_blocks[WindowInput.DATA_TYPE_UNSIGNED].Update(bool(data_info['is_unsigned'])) - window_text_blocks[WindowInput.DATA_TYPE_COMPLEX].Update(bool(data_info['is_complex'])) - window_text_blocks[WindowInput.DATA_SAMPLE_SIZE].Update(sample_size) - window_text_blocks[WindowInput.DATA_BYTE_ORDER].Update( - 'little endian' if '<' in str(data_info['sample_dtype']) else 'big endian') - - for el in window_data_input.iter(): - if el in window_data_input.partial_component_list: - continue - tag = window_data_input.get_tag(el) - if window_dict is not None and tag in window_dict: - window_text_blocks[el].Update(value=window_dict[tag]) - else: - window_text_blocks[el].Update(value='') - window_text_blocks[WindowInput.DATA_FILE].Update(value=archive.data_file) - - -def add_capture(capture_data_input, values, capture_selector_dict, file_data, from_archive=False): - capture_dict = {} - added = True - for el in capture_data_input.iter(): - req_field = True if el in capture_data_input.req_tags else False - el_type = capture_data_input.el_types.get(el, None) - el_unit = capture_data_input.el_units.get(el, None) - field = capture_data_input.get_tag(el) if from_archive else el - added = added and add_sigmf_field(update_dictionary, values, field, capture_dict, - capture_data_input.get_tag(el), - required=req_field, type=el_type, unit=el_unit) - if not added: - # requirement not given - return False - annotation_dict = {capture_data_input.get_tag(CaptureData.START_INDEX): capture_dict[ - capture_data_input.get_tag(CaptureData.START_INDEX)]} - tmp_capture_subset = {} - for field in capture_data_input.element_list + [CaptureData.START_INDEX]: - tag = capture_data_input.get_tag(field) - if tag in capture_dict: - if field in capture_data_input.annotation_element_list: - annotation_dict[tag] = capture_dict[tag] - else: - tmp_capture_subset[tag] = capture_dict[tag] - - add_capture = True - for capture in file_data.get_captures(): - if int(capture[capture_data_input.get_tag(CaptureData.START_INDEX)]) == int( - capture_dict[capture_data_input.get_tag(CaptureData.START_INDEX)]): - add_capture = False - break - if add_capture: - add_sigmf_field(SigMFFile.add_capture, tmp_capture_subset, capture_data_input.get_tag(CaptureData.START_INDEX), - file_data, - metadata=tmp_capture_subset, type=capture_data_input.el_types[CaptureData.START_INDEX]) - - capture_length = 0 - try: - capture_length = file_data._count_samples() - except Exception as e: - show_error('{}\n{}'.format(repr(e), 'In call - count_samples()')) - if capture_length < 1: - show_error('No samples in file. Make sure you have selected your input data file') - return False - - add_sigmf_field(SigMFFile.add_annotation, annotation_dict, capture_data_input.get_tag(CaptureData.START_INDEX), - file_data, - length=capture_length, metadata=annotation_dict, - type=capture_data_input.el_types[CaptureData.START_INDEX]) - - new_values = combo_button.Values - new_val = 'Capture {}'.format(capture_dict[capture_data_input.get_tag(CaptureData.START_INDEX)]) - capture_selector_dict.update({new_val: capture_dict}) - if new_val not in list(new_values): - new_values = list(new_values) + [new_val] - combo_button.Update(values=tuple(new_values), value=new_val) - - -def main(): - parser = argparse.ArgumentParser(description='Edit SigMF Archive.') - parser.add_argument('-i', '--input', help='Input SigMF Archive Path.', default=None) - parser.add_argument('-v', '--verbose', action='count', default=0) - parser.add_argument('--version', action='version', version=f'%(prog)s v{toolversion}') - args = parser.parse_args() - - level_lut = { - 0: logging.WARNING, - 1: logging.INFO, - 2: logging.DEBUG, - } - logging.basicConfig(level=level_lut[min(args.verbose, 2)]) - - window_input = WindowInput() - capture_data_input = CaptureData() - capture_text_blocks = {} - window_text_blocks = {} - f = SigMFFile() - capture_selector_dict = {} - - layout = [[sg.Text('This is the SigMF tool to archive RF datasets', size=(80, 1))], - [sg.Text('Enter your data and signal captures below. You must include', auto_size_text=True), - sg.Text('required', text_color='red', font=sg.DEFAULT_FONT + ('italic',), auto_size_text=True), - sg.Text('fields.', size=(50, 1), auto_size_text=True)], - [sg.Text('_' * 150, auto_size_text=True)]] - - layout.append([sg.Text('Global Data', font=('Arial', 12, 'bold'))]) - num_components = 0 - line = [] - for el in window_input.iter_core(): - size = (30, 1) if len(line) == 0 else (None, None) - auto_size = True if len(line) == 0 else (10, 1) - line.extend([sg.Text(el, justification='right', size=size, - text_color='red' if el in window_input.req_tags else None, auto_size_text=auto_size)]) - if el in window_input.el_multiline: - window_text_blocks.update({el: sg.Multiline(window_input.el_text.get(el, ''), key=el, - tooltip=window_input.el_tooltips.get(el, None), size=(30, 2))}) - elif el in window_input.el_selector: - window_text_blocks.update({el: sg.Combo(values=window_input.el_selector[el], key=el, - size=window_input.el_size.get(el, (None, None)))}) - elif el in window_input.el_checkbox: - window_text_blocks.update({el: sg.Checkbox(window_input.el_text.get(el, ''), key=el, - size=window_input.el_size.get(el, (None, None)))}) - else: - window_text_blocks.update({el: sg.InputText(window_input.el_text.get(el, ''), key=el, - tooltip=window_input.el_tooltips.get(el, None))}) - line.append(window_text_blocks[el]) - - if el in window_input.el_units: - line.append(sg.Text(window_input.el_units[el])) - - num_components += 1 - if num_components < window_input.first_line_size: - continue - layout.append(line) - line = [] - - for el1, el2 in window_input.iter_x_secondary(2): - line = [] - for el, size in zip([el1, el2], [30, 22]): - if el is None: - continue - color = 'red' if el in window_input.req_tags else None - window_text_blocks.update({el: sg.InputText(window_input.el_text.get(el, ''), key=el, - tooltip=window_input.el_tooltips.get(el, None))}) - line.extend([sg.Text(el, justification='right', size=(size, 1), text_color=color), window_text_blocks[el]]) - if el in window_input.el_units: - line.append(sg.Text(window_input.el_units[el], size=(5, 1))) - else: - line.append(sg.Text('', size=(5, 1))) - layout.append(line) - - layout.extend( - [[sg.Text('_' * 150, auto_size_text=True)], - [sg.Text('Individual Capture Data', font=('Arial', 12, 'bold'))], - [sg.Text('Capture Selector', auto_size_text=True), combo_button, sg.Text('', size=(10, 1)), - sg.Button('Add Capture', enable_events=True), sg.Button('Remove Capture', enable_events=True, size=(15, 1)), - sg.Button('Clear Capture', enable_events=True, size=(15, 1))]] - ) - - for el1, el2, el3 in capture_data_input.iter_x(3): - line = [] - for el in [el1, el2, el3]: - if el is None: - continue - capture_text_blocks.update({el: sg.InputText(key=el, tooltip=capture_data_input.el_tooltips.get(el, None))}) - color = 'red' if el in capture_data_input.req_tags else None - line.extend([sg.Text(el, justification='right', size=(20, 1), text_color=color), capture_text_blocks[el]]) - if el in capture_data_input.el_units: - line.append(sg.Text(capture_data_input.el_units[el], size=(5, 1))) - else: - line.append(sg.Text('', size=(5, 1))) - layout.append(line) - - window_text_blocks.update( - {WindowInput.DATA_FILE: sg.InputText('', key=WindowInput.DATA_FILE)}) - layout.extend( - [[sg.Text('_' * 150, auto_size_text=True)], - [sg.Text('Data Location', font=('Arial', 12, 'bold'))], - [sg.Text(WindowInput.DATA_FILE, size=(30, 1), justification='right', text_color='red'), - window_text_blocks[WindowInput.DATA_FILE], sg.FileBrowse()], - [sg.Text(WindowInput.OUTPUT_FOLDER, size=(30, 1), justification='right'), - sg.InputText('', key=WindowInput.OUTPUT_FOLDER), sg.FolderBrowse(), submit_button], - [sg.Text(WindowInput.LOAD_PATH, size=(30, 1), justification='right'), sg.InputText('', key=WindowInput.LOAD_PATH), - sg.FileBrowse(file_types=(("Archive Files", "*.sigmf"),)), load_button], - [validate_button, sg.Button('View Data')]] - ) - - window = sg.Window('SigMF Archive Creator', - auto_size_buttons=False, - default_element_size=(20, 1), - auto_size_text=False, - default_button_element_size=(10, 1) - ).Layout(layout) - - if args.input: - window.Refresh() - # optional input file specified -> load - log.info(f'reading from {args.input}') - load_path = args.input - f = fromarchive(load_path) - update_global_screen(window_input, window_text_blocks, f.get_global_info(), f) - capture_selector_dict = {} - for capture in f.get_captures(): - add_capture(capture_data_input, capture, capture_selector_dict, f, from_archive=True) - - while True: - validate_button.Update(text='Update') - load_button.Update(text='Load') - submit_button.Update(text='Save Archive') - - window.Refresh() - event, values = window.Read() - log.debug(f'event: {event}, values: {values}') - if event == 'Load Archive': - load_path = values[WindowInput.LOAD_PATH] - if load_path == '': - show_error('No archive file provided') - continue - - load_button.Update(text='Loading...') - window.Refresh() - log.info(f'reading from {values[WindowInput.LOAD_PATH]}') - f = fromarchive(values[WindowInput.LOAD_PATH]) - update_global_screen(window_input, window_text_blocks, f.get_global_info(), f) - capture_selector_dict = {} - for capture in f.get_captures(): - add_capture(capture_data_input, capture, capture_selector_dict, f, from_archive=True) - - elif event == 'Data Type Help': - sg.PopupOK( - 'Format: _\n\n' - '\tTypeCharacters:\n' - '\t\tUnsigned data: \"u\"\n' - '\t\tComplex data: \"c\"\n' - '\t\tFixedpoint data: \"f\"\n' - '\tElementBitSize:\n' - '\t\t64 bits, 32 bits, 16 bits, or 8 bits\n' - '\tEndianness:\n' - '\t\tl: Little Endian\n' - '\t\tb: Big Endian\n\n\n\n' - 'Example: \"uc32_l\"\n' - 'Unsigned complex data where each element is 32 bits, or 64 bits total, formatted in little endian.', - title='Data Type Help' - ) - elif event == 'Update': - validate_button.Update(text='Validating...') - window.Refresh() - window_data_type_dict = {} - added = True - for el in window_input.iter(): - req_field = True if el in window_input.req_tags else False - el_type = window_input.el_types.get(el, None) - el_unit = window_input.el_units.get(el, None) - if el in window_input.partial_component_list: - added = added and add_sigmf_field(update_dictionary, values, el, window_data_type_dict, - window_input.get_tag(el), required=req_field, - type=el_type, unit=el_unit) - else: - added = added and add_sigmf_field(SigMFFile.set_global_field, values, el, f, - window_input.get_tag(el), - required=req_field, type=el_type, unit=el_unit) - - data_type_str = '' - data_type_str += 'c' if bool(window_data_type_dict[WindowInput.DATA_TYPE_COMPLEX]) else '' - data_type_str += 'f' if not bool(window_data_type_dict[WindowInput.DATA_TYPE_FIXEDPOINT]) else '' - data_type_str += 'u' if bool(window_data_type_dict[WindowInput.DATA_TYPE_UNSIGNED]) else '' - data_type_str += str(window_data_type_dict[WindowInput.DATA_SAMPLE_SIZE]) + '_' - data_type_str += 'le' if window_data_type_dict[WindowInput.DATA_BYTE_ORDER] == 'little endian' else 'be' - data_type_dict = {SigMFFile.DATATYPE_KEY: data_type_str} - added = added and add_sigmf_field(SigMFFile.set_global_field, data_type_dict, SigMFFile.DATATYPE_KEY, f, SigMFFile.DATATYPE_KEY, required=True) - added = added and add_sigmf_field(SigMFFile.set_data_file, values, WindowInput.DATA_FILE, f, required=True) and added - if not added: - # requirement not given - continue - - if validate_data(f): - submit_button.Update(disabled=False, button_color=sg.DEFAULT_BUTTON_COLOR) - elif event == 'Capture Combo': - capture_dict = capture_selector_dict[values['Capture Combo']] - update_capture_screen(capture_data_input, capture_text_blocks, capture_dict) - elif event == 'Add Capture': - add_capture(capture_data_input, values, capture_selector_dict, f) - - elif event == 'Remove Capture': - capture_dict = {} - added = add_sigmf_field(update_dictionary, values, CaptureData.START_INDEX, capture_dict, - SigMFFile.START_INDEX_KEY, required=True, type=int) - if not added: - # requirement not given - continue - - captures = [] - for capture in f._metadata[SigMFFile.CAPTURE_KEY]: - if capture[SigMFFile.START_INDEX_KEY] != capture_dict[SigMFFile.START_INDEX_KEY]: - captures.append(capture) - f._metadata[SigMFFile.CAPTURE_KEY] = captures - - annotations = [] - for annotation in f._metadata[SigMFFile.ANNOTATION_KEY]: - if annotation[SigMFFile.START_INDEX_KEY] != capture_dict[SigMFFile.START_INDEX_KEY]: - annotations.append(annotation) - f._metadata[SigMFFile.ANNOTATION_KEY] = annotations - - new_values = list(combo_button.Values) - rm_val = 'Capture {}'.format(capture_dict[SigMFFile.START_INDEX_KEY]) - if rm_val in capture_selector_dict: - capture_selector_dict.pop(rm_val) - if rm_val in new_values: - new_values.remove(rm_val) - combo_button.Update(values=new_values, set_to_index=0) - capture_dict = capture_selector_dict.get(combo_button.DefaultValue, None) - update_capture_screen(capture_data_input, capture_text_blocks, capture_dict) - elif event == 'Clear Capture': - update_capture_screen(capture_data_input, capture_text_blocks, None) - elif event == 'View Data': - sg.PopupOK('Current data:\n', f.dumps(pretty=True), title='') - elif event == 'Save Archive': - output_folder = values[WindowInput.OUTPUT_FOLDER] - if output_folder == '': - show_error('No output folder provided') - continue - if len(capture_selector_dict.keys()) == 0: - show_error('No capture data specified') - submit_button.Update(text='Saving...') - window.Refresh() - archive_file = output_folder + '/' + os.path.basename(f.data_file).split('.')[0] + SIGMF_ARCHIVE_EXT - f.archive(archive_file) - sg.PopupOK('Saved archive as \n', archive_file, title='') - elif event in ['Cancel', None, 'Exit']: - window.Close() - break - - window.Close() - -if __name__ == "__main__": - main()