def add_log(self): """Add the measurement log to measurement tab widget. Checks also if there's already some logging for this measurement and appends the text field of the user interface with this log. """ self.log = LogWidget() self.add_widget(self.log, minimized=True, has_close_button=False) self.add_UI_logger(self.log) # Checks for log file and appends it to the field. log_default = os.path.join(self.measurement.directory, "default.log") log_error = os.path.join(self.measurement.directory, "errors.log") self.__read_log_file(log_default, 1) self.__read_log_file(log_error, 0)
class MeasurementTabWidget(QtGui.QWidget): """Tab widget where measurement stuff is added. """ def __init__(self, tab_id, measurement, masses, icon_manager): """Init measurement tab class. Args: tab_id: An integer representing ID of the tabwidget. measurement: A measurement class object. masses: A masses class object. icon_manager: An iconmanager class object. """ super().__init__() self.tab_id = tab_id self.ui = uic.loadUi(os.path.join("ui_files", "ui_measurement_tab.ui"), self) self.measurement = measurement self.masses = masses self.icon_manager = icon_manager self.histogram = Null() # self.add_histogram() self.elemental_losses_widget = Null() self.energy_spectrum_widget = Null() self.depth_profile_widget = Null() # self.check_previous_state_files() # For above three. # Hide the measurement specific settings buttons self.ui.settingsFrame.setShown(False) self.ui.saveCutsButton.clicked.connect(self.measurement_save_cuts) self.ui.analyzeElementLossesButton.clicked.connect(lambda: self.open_element_losses(self)) self.ui.energySpectrumButton.clicked.connect(lambda: self.open_energy_spectrum(self)) self.ui.createDepthProfileButton.clicked.connect(lambda: self.open_depth_profile(self)) self.ui.measuringUnitSettingsButton.clicked.connect(self.open_measuring_unit_settings) self.ui.depthProfileSettingsButton.clicked.connect(self.open_depth_profile_settings) self.ui.calibrationSettingsButton.clicked.connect(self.open_calibration_settings) self.ui.command_master.clicked.connect(self.__master_issue_commands) self.data_loaded = False self.panel_shown = True self.ui.hidePanelButton.clicked.connect(lambda: self.hide_panel()) # Enable master button self.toggle_master_button() def add_widget(self, widget, minimized=None, has_close_button=True, icon=None): """Adds a new widget to current (measurement) tab. Args: widget: QWidget to be added into measurement tab widget. minimized: Boolean representing if widget should be minimized. icon: QtGui.QIcon for the subwindow. """ # QtGui.QMdiArea.addSubWindow(QWidget, flags=0) if has_close_button: subwindow = self.ui.mdiArea.addSubWindow(widget) else: subwindow = self.ui.mdiArea.addSubWindow( widget, QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinMaxButtonsHint ) if icon: subwindow.setWindowIcon(icon) subwindow.setAttribute(QtCore.Qt.WA_DeleteOnClose) widget.subwindow = subwindow if minimized: widget.showMinimized() else: widget.show() self.__set_icons() def add_histogram(self): """Adds ToF-E histogram into tab if it doesn't have one already. """ self.histogram = TofeHistogramWidget(self.measurement, self.masses, self.icon_manager) self.measurement.set_axes(self.histogram.matplotlib.axes) self.ui.makeSelectionsButton.clicked.connect( lambda: self.histogram.matplotlib.elementSelectionButton.setChecked(True) ) self.connect( self.histogram.matplotlib, QtCore.SIGNAL("selectionsChanged(PyQt_PyObject)"), self.__set_cut_button_enabled ) # Draw after giving axes -> selections set properly self.histogram.matplotlib.on_draw() if not self.measurement.selector.is_empty(): self.histogram.matplotlib.elementSelectionSelectButton.setEnabled(True) self.add_widget(self.histogram, has_close_button=False) self.histogram.set_cut_button_enabled() # Check if there are selections in the measurement and enable save cut # button. self.__set_cut_button_enabled(self.measurement.selector.selections) def add_log(self): """Add the measurement log to measurement tab widget. Checks also if there's already some logging for this measurement and appends the text field of the user interface with this log. """ self.log = LogWidget() self.add_widget(self.log, minimized=True, has_close_button=False) self.add_UI_logger(self.log) # Checks for log file and appends it to the field. log_default = os.path.join(self.measurement.directory, "default.log") log_error = os.path.join(self.measurement.directory, "errors.log") self.__read_log_file(log_default, 1) self.__read_log_file(log_error, 0) def add_UI_logger(self, log_widget): """Adds handlers to measurement logger so the logger can log the events to the user interface too. log_widget specifies which ui element will handle the logging. That should be the one which is added to this MeasurementTabWidget. """ logger = logging.getLogger(self.measurement.measurement_name) defaultformat = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S") widgetlogger_default = customLogHandler(logging.INFO, defaultformat, log_widget) logger.addHandler(widgetlogger_default) def check_previous_state_files(self, progress_bar=Null(), directory=None): """Check if saved state for Elemental Losses, Energy Spectrum or Depth Profile exists. If yes, load them also. Args: progress_bar: A QtGui.QProgressBar where loading of previous graph can be shown. """ if not directory: directory = self.measurement.directory self.make_elemental_losses(directory, self.measurement.measurement_name) progress_bar.setValue(66) QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents) # Mac requires event processing to show progress bar and its # process. self.make_energy_spectrum(directory, self.measurement.measurement_name) progress_bar.setValue(82) QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents) # Mac requires event processing to show progress bar and its # process. self.make_depth_profile(directory, self.measurement.measurement_name) progress_bar.setValue(98) QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents) # Mac requires event processing to show progress bar and its # process. def del_widget(self, widget): """Delete a widget from current (measurement) tab. Args: widget: QWidget to be removed. """ try: self.ui.mdiArea.removeSubWindow(widget.subwindow) widget.delete() except: # If window was manually closed, do nothing. pass def hide_panel(self, enable_hide=None): """Sets the frame (including all the tool buttons) visible. Args: enable_hide: If True, sets the frame visible and vice versa. If not given, sets the frame visible or hidden depending its previous state. """ if enable_hide != None: self.panel_shown = enable_hide else: self.panel_shown = not self.panel_shown if self.panel_shown: self.ui.hidePanelButton.setText(">") else: self.ui.hidePanelButton.setText("<") self.ui.frame.setShown(self.panel_shown) def make_depth_profile(self, directory, name): """Make depth profile from loaded lines from saved file. Args: directory: A string representing directory. name: A string representing measurement's name. """ file = os.path.join(directory, DepthProfileWidget.save_file) lines = self.__load_file(file) if not lines: return m_name = self.measurement.measurement_name try: output_dir = self.__confirm_filepath(lines[0].strip(), name, m_name) use_cuts = self.__confirm_filepath(lines[2].strip().split("\t"), name, m_name) cut_names = [os.path.basename(cut) for cut in use_cuts] elements_string = lines[1].strip().split("\t") elements = [Element(element) for element in elements_string] x_unit = lines[3].strip() line_zero = False line_scale = False if len(lines) == 7: # "Backwards compatibility" line_zero = lines[4].strip() == "True" line_scale = lines[5].strip() == "True" systerr = float(lines[6].strip()) DepthProfileDialog.x_unit = x_unit DepthProfileDialog.checked_cuts[m_name] = cut_names DepthProfileDialog.line_zero = line_zero DepthProfileDialog.line_scale = line_scale DepthProfileDialog.systerr = systerr self.depth_profile_widget = DepthProfileWidget( self, output_dir, use_cuts, elements, x_unit, line_zero, line_scale, systerr ) icon = self.icon_manager.get_icon("depth_profile_icon_2_16.png") self.add_widget(self.depth_profile_widget, icon=icon) except: # We do not need duplicate error logs, log in widget instead print(sys.exc_info()) # TODO: Remove this. def make_elemental_losses(self, directory, name): """Make elemental losses from loaded lines from saved file. Args: directory: A string representing directory. name: A string representing measurement's name. """ file = os.path.join(directory, ElementLossesWidget.save_file) lines = self.__load_file(file) if not lines: return m_name = self.measurement.measurement_name try: reference_cut = self.__confirm_filepath(lines[0].strip(), name, m_name) checked_cuts = self.__confirm_filepath(lines[1].strip().split("\t"), name, m_name) cut_names = [os.path.basename(cut) for cut in checked_cuts] split_count = int(lines[2]) y_scale = int(lines[3]) ElementLossesDialog.reference_cut[m_name] = os.path.basename(reference_cut) ElementLossesDialog.checked_cuts[m_name] = cut_names ElementLossesDialog.split_count = split_count ElementLossesDialog.y_scale = y_scale self.elemental_losses_widget = ElementLossesWidget(self, reference_cut, checked_cuts, split_count, y_scale) icon = self.icon_manager.get_icon("elemental_losses_icon_16.png") self.add_widget(self.elemental_losses_widget, icon=icon) except: # We do not need duplicate error logs, log in widget instead print(sys.exc_info()) # TODO: Remove this. def make_energy_spectrum(self, directory, name): """Make energy spectrum from loaded lines from saved file. Args: directory: A string representing directory. name: A string representing measurement's name. """ file = os.path.join(directory, EnergySpectrumWidget.save_file) lines = self.__load_file(file) if not lines: return m_name = self.measurement.measurement_name try: use_cuts = self.__confirm_filepath(lines[0].strip().split("\t"), name, m_name) cut_names = [os.path.basename(cut) for cut in use_cuts] width = float(lines[1].strip()) EnergySpectrumParamsDialog.bin_width = width EnergySpectrumParamsDialog.checked_cuts[m_name] = cut_names self.energy_spectrum_widget = EnergySpectrumWidget(self, use_cuts, width) icon = self.icon_manager.get_icon("energy_spectrum_icon_16.png") self.add_widget(self.energy_spectrum_widget, icon=icon) except: # We do not need duplicate error logs, log in widget instead print(sys.exc_info()) # TODO: Remove this. def measurement_save_cuts(self): """Save measurement selections to cut files. """ self.measurement.save_cuts() # Do for all slaves if master. self.measurement.project.save_cuts(self.measurement) def open_measuring_unit_settings(self): """Opens measurement settings dialog. """ MeasurementUnitSettings(self.measurement.measurement_settings, self.measurement.project.masses) def open_depth_profile_settings(self): """Opens depth profile settings dialog. """ DepthProfileSettings(self.measurement.measurement_settings) def open_calibration_settings(self): """Opens calibration settings dialog. """ CalibrationSettings(self.measurement) def open_depth_profile(self, parent): """Opens depth profile dialog. Args: parent: MeasurementTabWidget """ previous = self.depth_profile_widget DepthProfileDialog(parent) if self.depth_profile_widget != previous and type(self.depth_profile_widget) != Null: self.depth_profile_widget.save_to_file() def open_energy_spectrum(self, parent): """Opens energy spectrum dialog. Args: parent: MeasurementTabWidget """ previous = self.energy_spectrum_widget EnergySpectrumParamsDialog(parent) if self.energy_spectrum_widget != previous and type(self.energy_spectrum_widget) != Null: self.energy_spectrum_widget.save_to_file() def open_element_losses(self, parent): """Opens element losses dialog. Args: parent: MeasurementTabWidget """ previous = self.elemental_losses_widget ElementLossesDialog(parent) if self.elemental_losses_widget != previous and type(self.elemental_losses_widget) != Null: self.elemental_losses_widget.save_to_file() def toggle_master_button(self): """Toggle enabled state of the master measurement button in the measurementtabwidget. """ measurement_name = self.measurement.measurement_name master_name = self.measurement.project.has_master() self.ui.command_master.setEnabled(measurement_name == master_name) def __confirm_filepath(self, filepath, name, m_name): """Confirm whether filepath exist and changes it accordingly. Args: filepath: A string representing a filepath. name: A string representing origin measurement's name. m_name: A string representing measurement's name where graph is created. """ if type(filepath) == str: # Replace two for measurement and cut file's name. Not all, in case # the project or directories above it have same name. filepath = self.__rreplace(filepath, name, m_name, 2) try: with open(filepath): pass return filepath except: return os.path.join(self.measurement.directory, filepath) elif type(filepath) == list: newfiles = [] for file in filepath: file = self.__rreplace(file, name, m_name, 2) try: with open(file): pass newfiles.append(file) except: newfiles.append(os.path.join(self.measurement.directory, file)) return newfiles def __load_file(self, file): """Load file Args: file: A string representing full filepath to the file. """ lines = [] try: with open(file, "rt") as fp: for line in fp: lines.append(line) except: pass return lines def __master_issue_commands(self): """Signal that master measurement's command has been issued to all slave measurements in the project. """ meas_name = self.measurement.measurement_name master_name = self.measurement.project.has_master() if meas_name == master_name: self.emit(QtCore.SIGNAL("issueMaster")) def __read_log_file(self, file, state=1): """Read the log file into the log window. Args: file: A string representing log file. state: An integer (0, 1) representing what sort of log we read. 0 = error 1 = text (default) """ if os.path.exists(file): with open(file) as log_file: for line in log_file: if state == 0: self.log.add_error(line.strip()) else: self.log.add_text(line.strip()) def __rreplace(self, s, old, new, occurrence): """Replace from last occurrence. http://stackoverflow.com/questions/2556108/how-to-replace-the-last- occurence-of-an-expression-in-a-string """ li = s.rsplit(old, occurrence) return new.join(li) def __set_cut_button_enabled(self, selections): """Enables save cuts button if the given selections list's lenght is not 0. Otherwise disable. Args: selections: list of Selection objects """ self.ui.saveCutsButton.setEnabled(len(selections)) def __set_icons(self): """Adds icons to UI elements. """ self.icon_manager.set_icon(self.ui.measuringUnitSettingsButton, "measuring_unit_settings.svg") self.icon_manager.set_icon(self.ui.calibrationSettingsButton, "calibration_settings.svg") self.icon_manager.set_icon(self.ui.depthProfileSettingsButton, "gear.svg") self.icon_manager.set_icon(self.ui.makeSelectionsButton, "amarok_edit.svg", size=(30, 30)) self.icon_manager.set_icon(self.ui.saveCutsButton, "save_all.svg", size=(30, 30)) self.icon_manager.set_icon(self.ui.analyzeElementLossesButton, "elemental_losses_icon.svg", size=(30, 30)) self.icon_manager.set_icon(self.ui.energySpectrumButton, "energy_spectrum_icon.svg", size=(30, 30)) self.icon_manager.set_icon(self.ui.createDepthProfileButton, "depth_profile.svg", size=(30, 30)) self.icon_manager.set_icon(self.ui.hideShowSettingsButton, "show_icon.svg", size=(30, 30)) self.icon_manager.set_icon(self.ui.command_master, "editcut.svg", size=(30, 30))