def delete_clicked(): element = self._get_selected_element() if element is None: config.get_main_window().statusBar().showMessage( "Nothing to delete", config.STATUS_MSG_TIMEOUT) else: self._pipeline_model.delete(element)
def add_element(self, new_elem: SpikeElement) -> None: # Inserts new element into pipeline in proper order new_elem_cls_count = self._elem_cls_count(new_elem.__class__) new_elem_is_singleton = self._element_policy.\ is_cls_singleton(new_elem.__class__) if new_elem_is_singleton and new_elem_cls_count: config.get_main_window().statusBar().showMessage( 'Only one element of this type allowed in pipeline', config.STATUS_MSG_TIMEOUT) return target_positions = self._element_policy.cls_order_dict new_elem_target_pos = target_positions[new_elem.__class__] new_elem_insert_pos = 0 for pipe_elem in self._element_list: pipe_elem_target_pos = target_positions[pipe_elem.__class__] if new_elem_target_pos >= pipe_elem_target_pos: new_elem_insert_pos += 1 else: break self.beginInsertRows(QtCore.QModelIndex(), new_elem_insert_pos, new_elem_insert_pos) self._element_list.insert(new_elem_insert_pos, new_elem) self.endInsertRows()
def move_down(self, elem: SpikeElement) -> None: rank = self._element_policy.cls_order_dict row = self._element_list.index(elem) if row < len(self._element_list) - 1 and \ rank[type(elem)] == rank[type(self._element_list[row + 1])]: self.beginMoveRows(QtCore.QModelIndex(), row + 1, row + 1, QtCore.QModelIndex(), row) # noqa: E128 self._swap(self._element_list, row, row + 1) self.endMoveRows() else: config.get_main_window().statusBar().showMessage( "Cannot move element any lower", config.STATUS_MSG_TIMEOUT)
def _perform_save_action() -> None: """Saves current pipeline of elements to a user specified JSON file Launches a file dialog box that allows the user to specifiy a JSON file, attempts to encode the element pipeline, and if successful writes out the decoded element pipelin in JSON format to file. config.cvt_elem_to_dict() does most of the hard work, by extracting class and parameter data from the element that allows the element to be JSON encoded and reinstantiated later when the filed is read back in by _perform_load_action(). """ global _pipeline_model # TODO: _element_list is supposed to be private - use data() instead? element_list = _pipeline_model._element_list if element_list: options = QtWidgets.QFileDialog.Options() options |= QtWidgets.QFileDialog.DontUseNativeDialog file_name, _filter = QtWidgets.QFileDialog.getSaveFileName( config.get_main_window(), caption='Save File', filter='JSON (*.json)', options=options) if file_name: if not file_name.lower().endswith('.json'): file_name = file_name + '.json' elem_dict_list = [config.cvt_elem_to_dict(element) for element in element_list] with open(file_name, 'w') as json_file: json.dump(elem_dict_list, json_file)
def test_get_main_window(): """Tests function that finds reference to app's main window.""" app = QtWidgets.QApplication(sys.argv) # noqa: F841 win = SpikelyMainWindow() found_win = config.get_main_window() assert win is found_win
def convert_value(self, type_str, value_str): success, cvt_value = True, None try: if value_str == 'None': cvt_value = None elif type_str in ['str', 'file', 'folder', 'file_or_folder']: cvt_value = value_str elif type_str == 'int': if value_str == 'inf': cvt_value = float(value_str) else: cvt_value = int(value_str) elif type_str == 'float': cvt_value = float(value_str) elif type_str == 'int_list': cvt_value = self._str_list_to_int_list(value_str) elif type_str == "int_or_int_list": try: cvt_value = int(value_str) except ValueError: cvt_value = self._str_list_to_int_list(value_str) elif type_str == 'int_list_list': # Strip outer sq brackets: '[[1,2],[3,4]]' -> '[1,2],[3,4]' value_str = re.sub(r'^\[|\]$', '', value_str) # Disambiguate list separator: '[1,2],[3,4]' -> [1,2]:[3,4]' value_str = re.sub(r'\] *, *\[', ':', value_str) # Split into int_list strings and convert into int_lists cvt_value = list(map( self._str_list_to_int_list, value_str.split(':'))) elif type_str == 'bool': if value_str.lower() in ['true', 'yes']: cvt_value = True elif value_str.lower() in ['false', 'no']: cvt_value = False else: raise TypeError(f'{value_str} is not a valid bool type') elif type_str == 'dtype': # Conversion test will trigger exception if not well-formed np.dtype(value_str) # Save dtype as string for consumption by SpikeInterface cvt_value = value_str else: raise TypeError(f'{type_str} is not a Spikely supported type') except (TypeError, ValueError) as err: QtWidgets.QMessageBox.warning( config.get_main_window(), 'Type Conversion Error', repr(err)) success = False return success, cvt_value
def closeEvent(self, event): """Overrides QMainWindow method for confirmation before exiting""" if self.process.state() == QtCore.QProcess.Running: reply = QtWidgets.QMessageBox.question( config.get_main_window(), 'Exiting', 'Exiting will terminate' ' pipeline execution. Are you sure you want to exit?', QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.No: event.ignore()
def _action(name, tip, slot, shortcut=None, checkable=False, checked=None): action = QtWidgets.QAction(name, config.get_main_window(), checkable=checkable) action.setStatusTip(tip) action.triggered.connect(slot) if shortcut is not None: action.setShortcut(shortcut) if checkable and checked is not None: action.setChecked(checked) return action
def _perform_file_action() -> None: options = QtWidgets.QFileDialog.Options() options |= QtWidgets.QFileDialog.DontUseNativeDialog file_name, _filter = QtWidgets.QFileDialog.getOpenFileName( config.get_main_window(), caption='Copy File Name to Clipboard', options=options) if file_name: QtWidgets.QApplication.clipboard().setText(file_name)
def _perform_load_action() -> None: """Loads current pipeline with elements from a previously saved JSON file Launches a file dialog box that allows the user to select a previously saved JSON file, attempts to decode it, and if successful adds the elements to the current pipeline replacing any elements extant in the pipeline. config.cvt_dict_to_elem() does most of the hard work, and throws exceptions if the element is no longer installed, or is no longer compatible with the version saved previously. """ global _pipeline_model options = QtWidgets.QFileDialog.Options() options |= QtWidgets.QFileDialog.DontUseNativeDialog file_name, _filter = QtWidgets.QFileDialog.getOpenFileName( config.get_main_window(), caption='Open File', filter='JSON (*.json)', options=options) if file_name: _pipeline_model.clear() try: with open(file_name, 'r') as json_file: elem_dict_list = json.load(json_file) for elem_dict in elem_dict_list: elem = config.cvt_dict_to_elem(elem_dict) _pipeline_model.add_element(elem) except (json.decoder.JSONDecodeError, ValueError) as e: QtWidgets.QMessageBox.warning( config.get_main_window(), 'JSON File Load Failure', f'Failed to load {file_name}: {str(e)}') _pipeline_model.clear() except Exception as e: QtWidgets.QMessageBox.warning( config.get_main_window(), 'JSON File Load Failure', f'Unspecified exception: {str(e)}') _pipeline_model.clear()
def run(self): # Called in response to user pressing Run button in UI missing_param_count = self._missing_param_count() if missing_param_count: QtWidgets.QMessageBox.warning( config.get_main_window(), 'Run Failure', f'Missing mandatory element parameters. Missing parameter ' f'count: {missing_param_count}') return for cls in self._element_policy.required_cls_list: if not self._elem_cls_count(cls): QtWidgets.QMessageBox.warning( config.get_main_window(), 'Run Failure', f'Missing required element: {cls.__name__}') return config.get_main_window().statusBar().showMessage( 'Running pipeline', config.STATUS_MSG_TIMEOUT) elem_jdict_list = [ config.cvt_elem_to_dict(element) for element in self._element_list ] elem_list_str = json.dumps(elem_jdict_list) # TODO: Add plumbing for shared output support if self.share_output: run_path = pkg_resources.resource_filename('spikely.pipeman', 'piperun.py') else: run_path = pkg_resources.resource_filename('spikely.pipeman', 'pipeman.py') run_process = QtCore.QProcess(self) success = run_process.startDetached('python', [f'{run_path}', elem_list_str]) if not success: QtWidgets.QMessageBox.warning( config.get_main_window(), 'Failed to Start Python Process', f'Command line: python {run_path}, elem_list_str')
def _perform_folder_action() -> None: options = QtWidgets.QFileDialog.Options() options |= QtWidgets.QFileDialog.DontUseNativeDialog options |= QtWidgets.QFileDialog.ShowDirsOnly options |= QtWidgets.QFileDialog.DontResolveSymlinks folder_name = QtWidgets.QFileDialog.getExistingDirectory( config.get_main_window(), caption='Copy Folder Name to Clipboard', options=options) if folder_name: QtWidgets.QApplication.clipboard().setText(folder_name)