def excepthook(excection_type, excection_value, traceback_object): """Callback for any unhandled exceptions in the main thread. To use set sys.excepthook = excepthook""" separator = '-' * 50 body_text = "Oh no! Part of the program has crashed. If you had a session active don't worry it will be saved when the program closes.\n" \ "Please report this error on the github issue tracker.\nPress Ctrl+C to copy text from this dialog." current_time = time.strftime("%Y-%m-%d, %H:%M:%S") version_info = "DySense Version {}".format(app_version) # Get useful information from traceback object traceback_file = cStringIO.StringIO() traceback.print_tb(traceback_object, None, traceback_file) traceback_file.seek(0) traceback_info = traceback_file.read() error_message = '{}: \n{}'.format(excection_type, make_unicode(excection_value)) sections = [body_text, separator, version_info, current_time, separator, error_message, separator, traceback_info] unicode_sections = [make_unicode(s) for s in sections] dialog_message = '\n'.join(unicode_sections) popup = QtGui.QMessageBox() popup.setText(dialog_message) popup.setWindowTitle('Error') popup.setWindowIcon(popup.style().standardIcon(QStyle.SP_MessageBoxCritical)) popup.exec_() # This will cause app.exec_() to return. QtCore.QCoreApplication.exit(1)
def sensor_type_selected(self): self.sensor_name_line_edit.setFocus() try: sensor_id_type = self.possible_sensor_id_types[ self.sensor_type_combo_box.currentIndex()] self.sensor_id_type_label.setText('{} - '.format( make_unicode(sensor_id_type))) self.selected_id_type = make_unicode(sensor_id_type) except IndexError: pass
def handle_new_message(self, id, instance, body): if id == GlobID.AssertMessage: msg = AssertMessage.from_bytes(body) if msg.valid: self.send_text('ASSERT:' + make_unicode(msg.message)) elif id == GlobID.DebugMessage: msg = DebugMessage.from_bytes(body) if msg.valid: self.send_text('DEBUG:' + make_unicode(msg.message)) elif id == GlobID.UserData: msg = UserData.from_bytes(body) self.handle_data(self.utc_time, self.sys_time, (msg.roll, msg.pitch, msg.yaw, msg.battery))
def refresh_sensor_data(self, sensor_id, new_set_of_data): row_index = self.sensor_id_to_row_index[sensor_id] data_info = self.sensor_id_to_data_info[sensor_id] for i, data_value in enumerate(new_set_of_data): # Add one since first column is sensor name. column_index = i + 1 table_item = self.data_table.item(row_index, column_index) if table_item is None: continue # set of data is larger than expected if isinstance(data_value, float): try: desired_decimal_places = data_info[i]['decimal_places'] data_value = format_decimal_places(data_value, desired_decimal_places) except KeyError: # Number of decimals not specified so limit at 5. data_value = limit_decimal_places(data_value, 5) table_item.setText(make_unicode(data_value)) if not self.sensor_id_to_data_status[sensor_id]: # First time showing data for this sensor show make sure columns are big enough to show it. self.data_table.resizeColumnsToContents() else: # Remember that we've shown data for this sensor because constantly resizing looks funny. self.sensor_id_to_data_status[sensor_id] = True
def closeEvent(self, *args, **kwargs): notes = make_unicode(self.session_notes.toPlainText()).strip() self.controller_view.update_session_notes(notes) return QDialog.closeEvent(self, *args, **kwargs)
def update_info(self, controller_info): self.controller_info = controller_info # Treat controller ID as a setting. self.settings_widget.update_setting('id', controller_info['id']) session_state = controller_info['session_state'] if session_state.lower() == 'closed': session_status = 'Not Started' else: session_status = session_state self.session_status_label.setText(make_unicode(pretty(session_status))) settings = controller_info['settings'] for setting_name, setting_value in settings.iteritems(): self.settings_widget.update_setting(setting_name, setting_value) if self.end_session_dialog is not None and self.end_session_dialog.isVisible( ): self.end_session_dialog.update_controller_setting( setting_name, setting_value) extra_settings = controller_info['extra_settings'] for setting_name, setting_value in extra_settings.iteritems(): self.settings_widget.update_setting(setting_name, setting_value) if self.end_session_dialog is not None and self.end_session_dialog.isVisible( ): self.end_session_dialog.update_controller_setting( setting_name, setting_value)
def show_user_message(self, message, level): popup = QtGui.QMessageBox() font = QtGui.QFont() font.setPointSize(13) popup.setFont(font) if level == logging.CRITICAL: level_text = "Critical Error" icon = QStyle.SP_MessageBoxCritical elif level == logging.ERROR: level_text = "Error" icon = QStyle.SP_MessageBoxWarning elif level == logging.WARN: level_text = "Warning" icon = QStyle.SP_MessageBoxWarning elif level == logging.INFO: level_text = "Info" icon = QStyle.SP_MessageBoxInformation else: return # not a level we need to show popup.setText(make_unicode(message)) popup.setWindowTitle('{}'.format(level_text)) popup.setWindowIcon(popup.style().standardIcon(icon)) popup.exec_()
def confirm_button_clicked(self): notes = make_unicode(self.session_notes.toPlainText()).strip() self.presenter.send_controller_command('stop_session', notes, send_to_all_controllers=False) self.close()
def __init__(self, sensor_id, instrument_id, settings, context, connect_endpoint): ''' Constructor. Save properties for opening serial port later. Args: sensor_id - unique ID assigned by the program. settings - dictionary of sensor settings. Must include: port - serial port name (e.g. 'COM20') baud - serial port rate in bits per second. message_rate - how fast GPS is setup to output messages (Hz) context - ZMQ context instance. connect_endpoint - endpoint on local host to receive time/commands from. Raises: ValueError - if not all settings are provided or not in correct format. ''' try: self.port = make_unicode(settings['port']) self.baud = int(settings['baud']) self.message_period = 1.0 / float(settings['message_rate']) self.required_fix = make_unicode(settings['required_fix']) self.required_latlon_error = float(settings['required_error']) self.min_sats = int(settings['min_sats']) except (KeyError, ValueError, ZeroDivisionError) as e: raise ValueError("Bad sensor setting. Exception {}".format(e)) GpsNmea.__init__(self, self.required_fix, self.required_latlon_error, self.min_sats, sensor_id=sensor_id, instrument_id=instrument_id, context=context, connect_endpoint=connect_endpoint, throttle_sensor_read=False) # Set base class fields. self.desired_read_period = self.message_period + 1 self.max_closing_time = 3 # seconds # Serial port connection. self.connection = None
def __init__(self, sensor_id, instrument_id, settings, context, connect_endpoint): ''' Constructor. Save properties for opening serial port later. Args: sensor_id - unique ID assigned by the program. settings - dictionary of sensor settings. Must include: port - serial port name (e.g. 'COM20') baud - serial port rate in bits per second. output_period - how fast microcontroller is setup to output data (seconds) default_reading - distance that sensor returns when it's not getting a valid return. timeout_duration - how long that 'default_reading' has to be received before reporting an error. context - ZMQ context instance. connect_endpoint - endpoint on local host to receive time/commands from. Raises: ValueError - if not all settings are provided or not in correct format. ''' SensorBase.__init__(self, sensor_id, instrument_id, context, connect_endpoint, throttle_sensor_read=False) try: self.port = make_unicode(settings['port']) self.baud = int(settings['baud']) self.output_period = float(settings['output_period']) self.default_reading = float(settings['default_reading']) self.timeout_duration = float(settings['timeout_duration']) except (KeyError, ValueError, ZeroDivisionError) as e: raise ValueError("Bad sensor setting. Exception {}".format(e)) # Set base class fields. Set desired read period to be higher since MCU doesn't report at a constant rate. # This works because the throttle sensor read is disabled. self.desired_read_period = self.output_period + 1 self.max_closing_time = 3 # seconds # Serial port connection. self.connection = None # Buffer of bytes that haven't been converted into a distance yet. self.byte_buffer = [] # State of monitoring timeout based on constant data readings. if self.timeout_duration <= 0: self.monitor_state = 'disabled' else: self.monitor_state = 'ok' # How long that 'default_reading' has been received without getting another value. self.default_reading_elapsed = 0
def local_controller_name(self): stored_controller_name = self.qt_settings.value( "local_controller_name") if stored_controller_name is None: stored_controller_name = "default" # Make it so the first name returned is always the name returned to prevent # the controller name from changing as the program is running. if self.initial_stored_controller_name is None: self.initial_stored_controller_name = stored_controller_name return make_unicode(self.initial_stored_controller_name)
def confirm_question(self, question, title='Question'): popup = QtGui.QMessageBox() popup.setFont(QtGui.QFont('popup_font', pointSize=14)) popup.setText(make_unicode(question)) popup.setWindowTitle(title) popup.setWindowIcon(popup.style().standardIcon( QStyle.SP_MessageBoxQuestion)) popup.setStandardButtons(QMessageBox.No | QMessageBox.Yes) reply = popup.exec_() return reply == QtGui.QMessageBox.Yes
def __init__(self, sensor_id, instrument_id, settings, context, connect_endpoint): try: self.test_file_path = os.path.abspath( make_unicode(settings['test_file_path']).strip('"')) self.output_rate = make_unicode(settings['output_rate']) self.required_fix = make_unicode(settings['required_fix']) self.required_latlon_error = float(settings['required_error']) self.min_sats = int(settings['min_sats']) self.output_rate = float(settings['output_rate']) self.output_period = 1.0 / self.output_rate except (KeyError, ValueError, ZeroDivisionError) as e: raise ValueError("Bad sensor setting. Exception {}".format( repr(e))) GpsNmea.__init__(self, self.required_fix, self.required_latlon_error, self.min_sats, sensor_id=sensor_id, instrument_id=instrument_id, context=context, connect_endpoint=connect_endpoint) self.desired_read_period = self.output_period # Set to true once told user that end of file has been reached. self.warned_about_eof = False self.test_file = None # Set to true to run image latency tests instead of reading from file. self.latency_test = False self.latency_test_period = 0.001 self.latency_test_start_utc = 0 self.latency_mode_run_count = 0
def special_command_clicked(self): obj_ref = make_unicode(self.sender()).split()[3] # TODO get rid of split, here and in setup_special_commands command = self.special_commands_references[obj_ref] # Set command arguments for special commands. command_args = None if command in ['save_primary', 'save_position']: user_notes, ok = QInputDialog.getText(self, 'Text Input Dialog', 'Notes:') if not ok: return # user cancelled so don't send command command_args = user_notes self.presenter.send_sensor_command(command, command_args)
def invalidate_button_clicked(self): question = "Are you sure you want to invalidate the current session? This will add an 'invalidated.txt' file to the output directory. No data will be lost." if not self.presenter.view.confirm_question(question): return notes = make_unicode(self.session_notes.toPlainText()).strip() self.presenter.send_controller_command('invalidate_session', send_to_all_controllers=False) self.presenter.send_controller_command('stop_session', notes, send_to_all_controllers=False) self.close()
def update_sensor_view(self, info_name, value): # Update all screen objects that correspond to the specified sensor info name. matching_obj = self.info_name_to_object.get(info_name, []) if not isinstance(matching_obj, list): matching_obj.setText(make_unicode(value)) else: for i, obj in enumerate(matching_obj): obj.setText(make_unicode(value[i])) if info_name == 'settings': settings = value for setting_name, setting_value in settings.iteritems(): if setting_name in self.setting_name_to_object: obj = self.setting_name_to_object[setting_name] obj.setText(make_unicode(setting_value)) if info_name == 'overall_health': self.overall_sensor_health_update(value) if info_name == 'sensor_paused': if value == True: self.paused_label.setText('Paused') elif value == False: self.paused_label.setText('Active') if info_name == 'sensor_state': self.sensor_state_value_label.setText(pretty(value)) if info_name == 'connection_state': current_connection_state = value if current_connection_state == 'setup' and self.last_connection_state != 'setup': self.sensor_message_center_text_edit.clear() for line_edit in self.data_line_edits: line_edit.clear() self.last_connection_state = current_connection_state
def __init__(self, sensor_id, instrument_id, settings, context, connect_endpoint): ''' Constructor. Args: sensor_id - unique ID assigned by the program. settings - dictionary of sensor settings. Must include: output_rate - how fast to output data (Hz) test_float - any number that is a floating point number test_int - should be between 0 and 100. test_string - any string that's not empty test_bool - either true / false. Just used for testing UI. context - ZMQ context instance. connect_endpoint - endpoint on local host to receive time/commands from. Raises: ValueError - if not all settings are provided or not in correct format. ''' SensorBase.__init__(self, sensor_id, instrument_id, context, connect_endpoint) try: self.output_rate = float(settings['output_rate']) self.test_float = float(settings['test_float']) self.test_int = int(settings['test_int']) self.test_string = make_unicode(settings['test_string']) self.test_bool = bool(settings['test_bool']) self.output_period = 1.0 / self.output_rate except (KeyError, ValueError, ZeroDivisionError) as e: raise ValueError("Bad sensor setting. Exception {}".format( repr(e))) # Set base class fields. self.desired_read_period = self.output_period self.max_closing_time = 2 # Counter for each time data has been generated. self.counter = 0 # Flag that can be overridden with special command for testing. self.data_quality_ok = True # Flag that can be set to true for sending events for doing time testing. self.send_time_test_events = False # Flag to signify that the sensor is 'closed' self.closed = True
def _setup_ui(self): self.widget_font = QFont('mapping_font', pointSize=12) self.clear_map_button = QtGui.QPushButton('Clear Map') self.clear_map_button.setFont(self.widget_font) # Create widgets self.update_rate_line_edit = QtGui.QLineEdit() self.update_rate_line_edit.setText( make_unicode(self.default_min_update_distance)) self.update_rate_line_edit.setMaximumWidth( (QFontMetrics(self.widget_font).width('01.23'))) #self.update_rate_line_edit.setInputMask('00.0') self.update_rate_label = QtGui.QLabel('Update Distance') self.update_rate_units_label = QtGui.QLabel('(meters)') self.spacer1 = QtGui.QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Minimum) self.spacer2 = QtGui.QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Minimum) self.fix_aspect_checkbox = QCheckBox() self.fix_aspect_checkbox.setText('Fix Aspect Ratio') self.fix_aspect_checkbox.setFont(self.widget_font) self.fix_aspect_checkbox.setChecked(True) self._fix_aspect_toggled(self.fix_aspect_checkbox.isChecked()) self.update_rate_label.setFont(self.widget_font) self.update_rate_line_edit.setFont(self.widget_font) self.update_rate_units_label.setFont(self.widget_font) # Add widgets to layout main_layout = QtGui.QVBoxLayout(self) main_layout.addWidget(self.map_view) bottom_row_layout = QtGui.QHBoxLayout() bottom_row_layout.addWidget(self.update_rate_label) bottom_row_layout.addWidget(self.update_rate_line_edit) bottom_row_layout.addWidget(self.update_rate_units_label) bottom_row_layout.addSpacerItem(self.spacer1) bottom_row_layout.addWidget(self.fix_aspect_checkbox) bottom_row_layout.addSpacerItem(self.spacer2) bottom_row_layout.addWidget(self.clear_map_button) main_layout.addLayout(bottom_row_layout) self.map_view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
def update_data_visualization(self, data): for n, line_edit in enumerate(self.data_line_edits): try: data_value = data[n] except IndexError: continue # not all data was provided if isinstance(data_value, float): desired_decimal_places = self.dec_places[n] if desired_decimal_places == 0: # (number not specified) data_value = limit_decimal_places(data_value, 5) else: data_value = format_decimal_places(data_value, desired_decimal_places) line_edit.setText(make_unicode(data_value))
def __init__(self, sensor_id, instrument_id, settings, context, connect_endpoint): ''' Constructor. Save properties for opening serial port later. ''' SensorBase.__init__(self, sensor_id, instrument_id, context, connect_endpoint, throttle_sensor_read=False) try: self.port = make_unicode(settings['port']) self.baud = int(settings['baud']) self.receive_period = 1.0 / float(settings['receive_rate']) except (KeyError, ValueError, ZeroDivisionError) as e: raise ValueError("Bad sensor setting. Exception {}".format(e)) # Set base class fields. Set read period higher since sensor read throttle is disabled. self.desired_read_period = self.receive_period + 1 self.max_closing_time = 3 # seconds # Serial port connection. self.connection = None # Used for converting raw data into glob messages. self.parser = GlobParser()
def run(self): try: self.run_message_loop() except Exception as e: formatted_lines = traceback.format_exc().splitlines() traceback_lines = formatted_lines[:-1] error_lines = [] error_lines.append("------------") error_lines.append("{} - {}".format( type(e).__name__, make_unicode(e))) for traceback_line in traceback_lines: error_lines.append(traceback_line) error_lines.append("------------") # Notify everyone that this manager crashed. # TODO this isn't really a controller event self.handle_controller_event(self.controllers[0], 'manager_crashed', error_lines) finally: self.close_down()
def __init__(self, sensor_id, instrument_id, settings, context, connect_endpoint): ''' Constructor. Save properties for opening serial port later. Args: sensor_id - unique ID assigned by the program. settings - dictionary of sensor settings. Must include: port - serial port name (e.g. 'COM20') baud - serial port rate in bits per second. sample_rate - how quickly to read data in Hz context - ZMQ context instance. connect_endpoint - endpoint on local host to receive time/commands from. Raises: ValueError - if not all settings are provided or not in correct format. ''' SensorBase.__init__(self, sensor_id, instrument_id, context, connect_endpoint) try: self.port = make_unicode(settings['port']) self.baud = int(settings['baud']) self.sample_period = 1.0 / float(settings['sample_rate']) except (KeyError, ValueError, ZeroDivisionError) as e: raise ValueError("Bad sensor setting. Exception {}".format(e)) # Set base class fields. self.desired_read_period = self.sample_period self.max_closing_time = 3 # seconds # Serial port connection. self.connection = None # Time the data was requested self.utc_time_of_request = 0 self.sys_time_of_request = 0 # How many bytes to read each time sensor sends data. self.bytes_to_read = 2
def __init__(self, sensor_id, instrument_id, settings, context, connect_endpoint): ''' Constructor. Save properties for opening serial port later. Args: sensor_id - unique ID assigned by the program. settings - dictionary of sensor settings. Must include: port - serial port name (e.g. 'COM20') baud - serial port rate in bits per second. context - ZMQ context instance. connect_endpoint - endpoint on local host to receive time/commands from. Raises: ValueError - if not all settings are provided or not in correct format. ''' SensorBase.__init__(self, sensor_id, instrument_id, context, connect_endpoint, throttle_sensor_read=False) try: self.port = make_unicode(settings['port']) self.baud = int(settings['baud']) except (KeyError, ValueError, ZeroDivisionError) as e: raise ValueError("Bad sensor setting. Exception {}".format(e)) # Set base class fields. Set read period to how long we want the sensor to complain after not receiving data. self.desired_read_period = 2 self.max_closing_time = 3 # seconds # Serial port connection. self.connection = None # Buffer of bytes that haven't been converted into a distance yet. self.byte_buffer = []
def setup_special_commands(self, special_commands): if special_commands == None: self.special_commands_group_box.deleteLater() return #special commands dict #key = reference #value = special command self.special_commands_references = {} special_commands_layout = QtGui.QHBoxLayout() self.special_commands_group_box.setLayout(special_commands_layout) for command in special_commands: b = QtGui.QPushButton(pretty(command)) special_commands_layout.addWidget(b) # store the button object reference obj_ref = make_unicode(b).split()[3] self.special_commands_references[obj_ref] = command b.clicked.connect(self.special_command_clicked)
def last_saved_config_file_path(self, new_value): self.qt_settings.setValue("last_saved_config_file_path", make_unicode(new_value))
def last_saved_config_file_path(self): file_path = self.qt_settings.value("last_saved_config_file_path") if file_path is None: file_path = os.path.join(os.path.expanduser('~'), 'dysense_config.yaml') return make_unicode(file_path)
def show_stacked_widget(self, widget_ref): widget_title = self.widget_titles[widget_ref] self.stacked_group_box.setTitle(make_unicode(widget_title)) self.stacked_widget.setCurrentWidget(widget_ref)
def last_loaded_config_file_path(self): file_path = self.qt_settings.value("last_loaded_config_file_path") return make_unicode(file_path)
def cancel_button_clicked(self): self.controller_view.update_session_notes( make_unicode(self.session_notes.toPlainText()).strip()) self.close()
def append_sensor_message(self, controller_id, sensor_id, text): sensor_view = self.sensor_to_widget[(controller_id, sensor_id)] sensor_view.display_message(make_unicode(text).strip('[]'), True)