def __init__(self, parent=None, image_files=None): super(MainWindow, self).__init__(parent) loader = UiLoader() self.ui = loader.load_file('main_window.ui', parent) self.thread_pool = QThreadPool(self) self.cal_progress_dialog = CalProgressDialog(self.ui) # Let the left dock widget take up the whole left side self.ui.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.ui.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.color_map_editor = ColorMapEditor(self.ui.image_tab_widget, self.ui.central_widget) self.ui.color_map_dock_widgets.layout().addWidget( self.color_map_editor.ui) self.image_mode = 'raw' self.image_mode_widget = ImageModeWidget(self.ui.central_widget) self.ui.image_mode_dock_widgets.layout().addWidget( self.image_mode_widget.ui) self.add_materials_panel() self.load_widget = LoadPanel(self.ui) self.ui.load_page.setLayout(QVBoxLayout()) self.ui.load_page.layout().addWidget(self.load_widget.ui) # self.ui.load_page = self.load_widget self.cal_tree_view = CalTreeView(self.ui) self.calibration_config_widget = CalibrationConfigWidget(self.ui) self.calibration_slider_widget = CalibrationSliderWidget(self.ui) tab_texts = ['Tree View', 'Form View', 'Slider View'] self.ui.calibration_tab_widget.clear() self.ui.calibration_tab_widget.addTab(self.cal_tree_view, tab_texts[0]) self.ui.calibration_tab_widget.addTab( self.calibration_config_widget.ui, tab_texts[1]) self.ui.calibration_tab_widget.addTab( self.calibration_slider_widget.ui, tab_texts[2]) self.setup_connections() self.calibration_config_widget.update_gui_from_config() self.ui.action_show_live_updates.setChecked(HexrdConfig().live_update) self.live_update(HexrdConfig().live_update)
def __init__(self, parent=None, image_files=None): super(MainWindow, self).__init__(parent) loader = UiLoader() self.ui = loader.load_file('main_window.ui', parent) self.workflow_widgets = {'HEDM': [], 'LLNL': []} self.thread_pool = QThreadPool(self) self.progress_dialog = ProgressDialog(self.ui) self.progress_dialog.setWindowTitle('Calibration Running') # Let the left dock widget take up the whole left side self.ui.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.ui.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.color_map_editor = ColorMapEditor(self.ui.image_tab_widget, self.ui.central_widget) self.ui.color_map_dock_widgets.layout().addWidget( self.color_map_editor.ui) self.image_mode = ViewType.raw self.image_mode_widget = ImageModeWidget(self.ui.central_widget) self.ui.image_mode_dock_widgets.layout().addWidget( self.image_mode_widget.ui) self.add_materials_panel() self.load_widget = LoadPanel(self.ui) self.ui.load_page.setLayout(QVBoxLayout()) self.ui.load_page.layout().addWidget(self.load_widget.ui) self.load_widget.ui.setVisible(False) self.workflow_widgets[WORKFLOW_HEDM].append(self.load_widget.ui) self.import_data_widget = ImportDataPanel(self.ui) self.ui.load_page.setLayout(QVBoxLayout()) self.ui.load_page.layout().addWidget(self.import_data_widget.ui) self.import_data_widget.ui.setVisible(False) self.workflow_widgets[WORKFLOW_LLNL].append(self.import_data_widget.ui) self.cal_tree_view = CalTreeView(self.ui) self.calibration_config_widget = CalibrationConfigWidget(self.ui) self.calibration_slider_widget = CalibrationSliderWidget(self.ui) tab_texts = ['Tree View', 'Form View', 'Slider View'] self.ui.calibration_tab_widget.clear() self.ui.calibration_tab_widget.addTab(self.cal_tree_view, tab_texts[0]) self.ui.calibration_tab_widget.addTab( self.calibration_config_widget.ui, tab_texts[1]) self.ui.calibration_tab_widget.addTab( self.calibration_slider_widget.ui, tab_texts[2]) self.mask_manager_dialog = MaskManagerDialog(self.ui) self.setup_connections() self.update_config_gui() self.add_workflow_widgets() self.ui.action_show_live_updates.setChecked(HexrdConfig().live_update) self.live_update(HexrdConfig().live_update) ImageFileManager().load_dummy_images(True) # In order to avoid both a not very nice looking black window, # and a bug with the tabbed view # (see https://github.com/HEXRD/hexrdgui/issues/261), # do not draw the images before the first paint event has # occurred. The images will be drawn automatically after # the first paint event has occurred (see MainWindow.eventFilter). self.workflow_selection_dialog = WorkflowSelectionDialog(self.ui)
class MainWindow(QObject): # Emitted when new images are loaded new_images_loaded = Signal() # Emitted when a new mask is added new_mask_added = Signal() def __init__(self, parent=None, image_files=None): super(MainWindow, self).__init__(parent) loader = UiLoader() self.ui = loader.load_file('main_window.ui', parent) self.workflow_widgets = {'HEDM': [], 'LLNL': []} self.thread_pool = QThreadPool(self) self.progress_dialog = ProgressDialog(self.ui) self.progress_dialog.setWindowTitle('Calibration Running') # Let the left dock widget take up the whole left side self.ui.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.ui.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.color_map_editor = ColorMapEditor(self.ui.image_tab_widget, self.ui.central_widget) self.ui.color_map_dock_widgets.layout().addWidget( self.color_map_editor.ui) self.image_mode = ViewType.raw self.image_mode_widget = ImageModeWidget(self.ui.central_widget) self.ui.image_mode_dock_widgets.layout().addWidget( self.image_mode_widget.ui) self.add_materials_panel() self.load_widget = LoadPanel(self.ui) self.ui.load_page.setLayout(QVBoxLayout()) self.ui.load_page.layout().addWidget(self.load_widget.ui) self.load_widget.ui.setVisible(False) self.workflow_widgets[WORKFLOW_HEDM].append(self.load_widget.ui) self.import_data_widget = ImportDataPanel(self.ui) self.ui.load_page.setLayout(QVBoxLayout()) self.ui.load_page.layout().addWidget(self.import_data_widget.ui) self.import_data_widget.ui.setVisible(False) self.workflow_widgets[WORKFLOW_LLNL].append(self.import_data_widget.ui) self.cal_tree_view = CalTreeView(self.ui) self.calibration_config_widget = CalibrationConfigWidget(self.ui) self.calibration_slider_widget = CalibrationSliderWidget(self.ui) tab_texts = ['Tree View', 'Form View', 'Slider View'] self.ui.calibration_tab_widget.clear() self.ui.calibration_tab_widget.addTab(self.cal_tree_view, tab_texts[0]) self.ui.calibration_tab_widget.addTab( self.calibration_config_widget.ui, tab_texts[1]) self.ui.calibration_tab_widget.addTab( self.calibration_slider_widget.ui, tab_texts[2]) self.mask_manager_dialog = MaskManagerDialog(self.ui) self.setup_connections() self.update_config_gui() self.add_workflow_widgets() self.ui.action_show_live_updates.setChecked(HexrdConfig().live_update) self.live_update(HexrdConfig().live_update) ImageFileManager().load_dummy_images(True) # In order to avoid both a not very nice looking black window, # and a bug with the tabbed view # (see https://github.com/HEXRD/hexrdgui/issues/261), # do not draw the images before the first paint event has # occurred. The images will be drawn automatically after # the first paint event has occurred (see MainWindow.eventFilter). self.workflow_selection_dialog = WorkflowSelectionDialog(self.ui) def setup_connections(self): """This is to setup connections for non-gui objects""" self.ui.installEventFilter(self) self.ui.action_open_config_yaml.triggered.connect( self.on_action_open_config_yaml_triggered) self.ui.action_open_config_dir.triggered.connect( self.on_action_open_config_dir_triggered) self.ui.action_save_config_yaml.triggered.connect( self.on_action_save_config_yaml_triggered) self.ui.action_save_config_dir.triggered.connect( self.on_action_save_config_dir_triggered) self.ui.action_open_materials.triggered.connect( self.on_action_open_materials_triggered) self.ui.action_save_imageseries.triggered.connect( self.on_action_save_imageseries_triggered) self.ui.action_save_materials.triggered.connect( self.on_action_save_materials_triggered) self.ui.action_export_polar_plot.triggered.connect( self.on_action_export_polar_plot_triggered) self.ui.action_edit_euler_angle_convention.triggered.connect( self.on_action_edit_euler_angle_convention) self.ui.action_edit_apply_polar_mask.triggered.connect( self.on_action_edit_apply_polar_mask_triggered) self.ui.action_edit_apply_laue_mask_to_polar.triggered.connect( self.on_action_edit_apply_laue_mask_to_polar_triggered) self.ui.action_edit_reset_instrument_config.triggered.connect( self.on_action_edit_reset_instrument_config) self.ui.action_transform_detectors.triggered.connect( self.on_action_transform_detectors_triggered) self.ui.action_open_mask_manager.triggered.connect( self.on_action_open_mask_manager_triggered) self.ui.action_show_live_updates.toggled.connect(self.live_update) self.ui.action_show_detector_borders.toggled.connect( HexrdConfig().set_show_detector_borders) self.ui.calibration_tab_widget.currentChanged.connect( self.update_config_gui) self.image_mode_widget.tab_changed.connect(self.change_image_mode) self.image_mode_widget.mask_applied.connect(self.update_all) self.ui.action_run_powder_calibration.triggered.connect( self.start_powder_calibration) self.ui.action_calibration_line_picker.triggered.connect( self.on_action_calibration_line_picker_triggered) self.ui.action_run_indexing.triggered.connect( self.on_action_run_indexing_triggered) self.new_images_loaded.connect(self.update_color_map_bounds) self.new_images_loaded.connect(self.update_indexing_menu) self.new_images_loaded.connect(self.color_map_editor.reset_range) self.new_images_loaded.connect(self.image_mode_widget.reset_masking) self.ui.image_tab_widget.update_needed.connect(self.update_all) self.ui.image_tab_widget.new_mouse_position.connect( self.new_mouse_position) self.ui.image_tab_widget.clear_mouse_position.connect( self.ui.status_bar.clearMessage) self.calibration_slider_widget.update_if_mode_matches.connect( self.update_if_mode_matches) self.load_widget.images_loaded.connect(self.images_loaded) self.import_data_widget.new_config_loaded.connect( self.update_config_gui) self.image_mode_widget.polar_show_snip1d.connect( self.ui.image_tab_widget.polar_show_snip1d) self.ui.action_open_images.triggered.connect(self.open_image_files) self.ui.action_open_aps_imageseries.triggered.connect( self.open_aps_imageseries) HexrdConfig().update_status_bar.connect(self.ui.status_bar.showMessage) HexrdConfig().detectors_changed.connect(self.on_detectors_changed) HexrdConfig().deep_rerender_needed.connect(self.deep_rerender) HexrdConfig().workflow_changed.connect(self.add_workflow_widgets) ImageLoadManager().update_needed.connect(self.update_all) ImageLoadManager().new_images_loaded.connect(self.new_images_loaded) self.ui.action_switch_workflow.triggered.connect( self.on_action_switch_workflow_triggered) self.mask_manager_dialog.update_masks.connect(self.update_all) self.new_mask_added.connect(self.mask_manager_dialog.update_masks_list) def set_icon(self, icon): self.ui.setWindowIcon(icon) def show(self): self.ui.show() def add_workflow_widgets(self): current_workflow = HexrdConfig().workflow for key in self.workflow_widgets.keys(): visible = True if key == current_workflow else False for widget in self.workflow_widgets[key]: widget.setVisible(visible) def add_materials_panel(self): # Remove the placeholder materials panel from the UI, and # add the real one. materials_panel_index = -1 for i in range(self.ui.config_tool_box.count()): if self.ui.config_tool_box.itemText(i) == 'Materials': materials_panel_index = i if materials_panel_index < 0: raise Exception('"Materials" panel not found!') self.ui.config_tool_box.removeItem(materials_panel_index) self.materials_panel = MaterialsPanel(self.ui) self.ui.config_tool_box.insertItem(materials_panel_index, self.materials_panel.ui, 'Materials') def on_action_open_config_yaml_triggered(self): selected_file, selected_filter = QFileDialog.getOpenFileName( self.ui, 'Load Configuration', HexrdConfig().working_dir, 'YAML files (*.yml)') if selected_file: path = Path(selected_file) HexrdConfig().working_dir = str(path.parent) HexrdConfig().load_instrument_config(str(path)) self.update_config_gui() def on_action_open_config_dir_triggered(self): dialog = QFileDialog(self.ui, 'Load Configuration', HexrdConfig().working_dir, 'HEXRD directories (*)') dialog.setFileMode(QFileDialog.Directory) selected_files = [] if dialog.exec_(): selected_files = dialog.selectedFiles() if len(selected_files) == 0: return else: selected_file = selected_files[0] path = Path(selected_file) def _load(): path = Path(selected_file) HexrdConfig().working_dir = str(path.parent) # Opening a .hexrd directory, so point to config.yml path = path / 'config.yml' HexrdConfig().load_instrument_config(str(path)) # Check we have the config.yml if not (path / 'config.yml').exists(): msg = 'Invalid HEXRD directory, config.yml missing.' QMessageBox.critical(self.ui, 'HEXRD', msg) return # We do this in a worker thread so the UI can refresh. worker = AsyncWorker(_load) worker.signals.finished.connect(self.update_config_gui) self.thread_pool.start(worker) def on_action_save_config_yaml_triggered(self): selected_file, selected_filter = QFileDialog.getSaveFileName( self.ui, 'Save Configuration', HexrdConfig().working_dir, 'YAML files (*.yml)') if selected_file: HexrdConfig().working_dir = str(Path(selected_file).parent) return HexrdConfig().save_instrument_config(selected_file) def on_action_save_config_dir_triggered(self): dialog = QFileDialog(self.ui, 'Save Configuration', HexrdConfig().working_dir, 'HEXRD directories (*)') dialog.setFileMode(QFileDialog.Directory) dialog.setAcceptMode(QFileDialog.AcceptSave) # We connect up the curentChanged signal, so we can save a listing # before the QFileDialog creates a directory, so we can determine if # the directory already existed, see below. listing = [] def _current_changed(p): nonlocal listing listing = [str(p) for p in Path(p).parent.iterdir()] dialog.currentChanged.connect(_current_changed) selected_files = [] if dialog.exec(): selected_files = dialog.selectedFiles() if len(selected_files) == 0: return else: selected_file = selected_files[0] # The QFileDialog will create the directory for us. However, if # the user didn't us a .hexrd suffix we need to add it, but only if is # didn't exist before. This is hack to get around the fact that we # can't easily stop QFileDialog from creating the directory. if Path(selected_file).suffix != '.hexrd': # if it wasn't in the listing before the QFileDialog, we can # be pretty confident that is was create by the QFileDialog. if selected_file not in listing: selected_file = Path(selected_file).rename( f'{selected_file}.hexrd') # else just added the suffix else: selected_file = Path(selected_file).with_suffix('.hexrd') if selected_file: path = Path(selected_file) HexrdConfig().working_dir = str(path.parent) path.mkdir(parents=True, exist_ok=True) path = path / 'config.yml' return HexrdConfig().save_instrument_config(str(path)) def on_detectors_changed(self): HexrdConfig().clear_overlay_data() HexrdConfig().current_imageseries_idx = 0 self.load_dummy_images() self.ui.image_tab_widget.switch_toolbar(0) # Update the load widget self.load_widget.config_changed() def load_dummy_images(self): ImageFileManager().load_dummy_images() self.update_all(clear_canvases=True) self.ui.action_transform_detectors.setEnabled(False) self.new_images_loaded.emit() def open_image_file(self): images_dir = HexrdConfig().images_dir selected_file, selected_filter = QFileDialog.getOpenFileNames( self.ui, dir=images_dir) if len(selected_file) > 1: msg = ('Please select only one file.') QMessageBox.warning(self.ui, 'HEXRD', msg) return return selected_file def open_image_files(self): # Get the most recent images dir images_dir = HexrdConfig().images_dir selected_files, selected_filter = QFileDialog.getOpenFileNames( self.ui, dir=images_dir) if selected_files: # Save the chosen dir HexrdConfig().set_images_dir(selected_files[0]) files, manual = ImageLoadManager().load_images(selected_files) if not files: return if (any(len(f) != 1 for f in files) or len(files) < len(HexrdConfig().detector_names)): msg = ('Number of files must match number of detectors: ' + str(len(HexrdConfig().detector_names))) QMessageBox.warning(self.ui, 'HEXRD', msg) return # If it is a hdf5 file allow the user to select the path ext = os.path.splitext(selected_files[0])[1] if (ImageFileManager().is_hdf(ext) and not ImageFileManager().path_exists(selected_files[0])): selection = ImageFileManager().path_prompt(selected_files[0]) if not selection: return dialog = LoadImagesDialog(files, manual, self.ui) if dialog.exec_(): detector_names, image_files = dialog.results() image_files = [img for f in files for img in f] files = [[] for det in HexrdConfig().detector_names] for d, f in zip(detector_names, image_files): pos = HexrdConfig().detector_names.index(d) files[pos].append(f) ImageLoadManager().read_data(files, parent=self.ui) self.images_loaded() def images_loaded(self): self.ui.action_transform_detectors.setEnabled(True) def open_aps_imageseries(self): # Get the most recent images dir images_dir = HexrdConfig().images_dir detector_names = HexrdConfig().detector_names selected_dirs = [] for name in detector_names: caption = 'Select directory for detector: ' + name d = QFileDialog.getExistingDirectory(self.ui, caption, dir=images_dir) if not d: return selected_dirs.append(d) images_dir = os.path.dirname(d) ImageFileManager().load_aps_imageseries(detector_names, selected_dirs) self.update_all() self.new_images_loaded.emit() def on_action_open_materials_triggered(self): selected_file, selected_filter = QFileDialog.getOpenFileName( self.ui, 'Load Materials File', HexrdConfig().working_dir, 'HEXRD files (*.hexrd)') if selected_file: HexrdConfig().working_dir = os.path.dirname(selected_file) HexrdConfig().load_materials(selected_file) self.materials_panel.update_gui_from_config() def on_action_save_imageseries_triggered(self): if not HexrdConfig().has_images(): msg = ('No ImageSeries available for saving.') QMessageBox.warning(self.ui, 'HEXRD', msg) return if ImageLoadManager().unaggregated_images: ims_dict = ImageLoadManager().unaggregated_images else: ims_dict = HexrdConfig().imageseries_dict if len(ims_dict) > 1: # Have the user choose an imageseries to save names = list(ims_dict.keys()) name, ok = QInputDialog.getItem(self.ui, 'HEXRD', 'Select ImageSeries', names, 0, False) if not ok: # User canceled... return else: name = list(ims_dict.keys())[0] selected_file, selected_filter = QFileDialog.getSaveFileName( self.ui, 'Save ImageSeries', HexrdConfig().working_dir, 'HDF5 files (*.h5 *.hdf5);; NPZ files (*.npz)') if selected_file: HexrdConfig().working_dir = os.path.dirname(selected_file) if selected_filter.startswith('HDF5'): selected_format = 'hdf5' elif selected_filter.startswith('NPZ'): selected_format = 'frame-cache' kwargs = {} if selected_format == 'hdf5': # A path must be specified. Set it ourselves for now. kwargs['path'] = 'imageseries' elif selected_format == 'frame-cache': # Get the user to pick a threshold result, ok = QInputDialog.getDouble(self.ui, 'HEXRD', 'Choose Threshold', 10, 0, 1e12, 3) if not ok: # User canceled... return kwargs['threshold'] = result # This needs to be specified, but I think it just needs # to be the same as the file name... kwargs['cache_file'] = selected_file HexrdConfig().save_imageseries(ims_dict.get(name), name, selected_file, selected_format, **kwargs) def on_action_save_materials_triggered(self): selected_file, selected_filter = QFileDialog.getSaveFileName( self.ui, 'Save Materials', HexrdConfig().working_dir, 'HEXRD files (*.hexrd)') if selected_file: HexrdConfig().working_dir = os.path.dirname(selected_file) return HexrdConfig().save_materials(selected_file) def on_action_export_polar_plot_triggered(self): selected_file, selected_filter = QFileDialog.getSaveFileName( self.ui, 'Save Polar Image', HexrdConfig().working_dir, 'HDF5 files (*.h5 *.hdf5);; NPZ files (*.npz)') if selected_file: HexrdConfig().working_dir = os.path.dirname(selected_file) return self.ui.image_tab_widget.export_polar_plot(selected_file) def on_action_calibration_line_picker_triggered(self): # Do a quick check for refinable paramters, which are required flags = HexrdConfig().get_statuses_instrument_format() if np.count_nonzero(flags) == 0: msg = 'There are no refinable parameters' QMessageBox.warning(self.ui, 'HEXRD', msg) return # Make the dialog canvas = self.ui.image_tab_widget.image_canvases[0] self._calibration_line_picker = LinePickerDialog(canvas, self.ui) self._calibration_line_picker.start() self._calibration_line_picker.finished.connect( self.start_line_picked_calibration) def start_line_picked_calibration(self, line_data): HexrdConfig().emit_update_status_bar('Running powder calibration...') # Run the calibration in a background thread worker = AsyncWorker(run_line_picked_calibration, line_data) self.thread_pool.start(worker) # We currently don't have any progress updates, so make the # progress bar indeterminate. self.progress_dialog.setRange(0, 0) self.progress_dialog.setWindowTitle('Calibration Running') # Get the results and close the progress dialog when finished worker.signals.result.connect(self.finish_line_picked_calibration) worker.signals.finished.connect(self.progress_dialog.accept) msg = 'Powder calibration finished!' f = lambda: HexrdConfig().emit_update_status_bar(msg) worker.signals.finished.connect(f) self.progress_dialog.exec_() def finish_line_picked_calibration(self, res): print('Received result from line picked calibration') if res is not True: print('Optimization failed!') return print('Updating the GUI') self.update_config_gui() self.update_all() def on_action_run_indexing_triggered(self): self._indexing_runner = IndexingRunner(self.ui) self._indexing_runner.run() def update_color_map_bounds(self): self.color_map_editor.update_bounds( HexrdConfig().current_images_dict()) def on_action_edit_euler_angle_convention(self): allowed_conventions = ['None', 'Extrinsic XYZ', 'Intrinsic ZXZ'] corresponding_values = [ None, { 'axes_order': 'xyz', 'extrinsic': True }, { 'axes_order': 'zxz', 'extrinsic': False } ] current = HexrdConfig().euler_angle_convention ind = corresponding_values.index(current) name, ok = QInputDialog.getItem(self.ui, 'HEXRD', 'Select Euler Angle Convention', allowed_conventions, ind, False) if not ok: # User canceled... return chosen = corresponding_values[allowed_conventions.index(name)] HexrdConfig().set_euler_angle_convention(chosen) self.update_all() self.update_config_gui() def on_action_edit_apply_polar_mask_triggered(self): # Make the dialog canvas = self.ui.image_tab_widget.image_canvases[0] self._apply_polar_mask_line_picker = LinePickerDialog(canvas, self.ui) self._apply_polar_mask_line_picker.start() self._apply_polar_mask_line_picker.finished.connect( self.run_apply_polar_mask) def run_apply_polar_mask(self, line_data): HexrdConfig().polar_masks_line_data.append(line_data.copy()) self.new_mask_added.emit() self.update_all() def on_action_edit_apply_laue_mask_to_polar_triggered(self): if not HexrdConfig().show_overlays: msg = 'Overlays are not displayed' QMessageBox.critical(self.ui, 'HEXRD', msg) return overlays = HexrdConfig().overlays laue_overlays = [x for x in overlays if x['type'] == OverlayType.laue] laue_overlays = [x for x in laue_overlays if x['visible']] if not laue_overlays: msg = 'No Laue overlays found' QMessageBox.critical(self.ui, 'HEXRD', msg) return data = [] for overlay in laue_overlays: for det, val in overlay['data'].items(): for ranges in val['ranges']: data.append(ranges) if not data: msg = 'No Laue overlay ranges found' QMessageBox.critical(self.ui, 'HEXRD', msg) return HexrdConfig().polar_masks_line_data.append(data) self.new_mask_added.emit() self.update_all() def on_action_edit_reset_instrument_config(self): HexrdConfig().restore_instrument_config_backup() self.update_config_gui() def change_image_mode(self, mode): self.image_mode = mode self.update_image_mode_enable_states() # Clear the overlays HexrdConfig().clear_overlay_data() self.update_all() def update_image_mode_enable_states(self): # This is for enable states that depend on the image mode is_raw = self.image_mode == ViewType.raw is_cartesian = self.image_mode == ViewType.cartesian is_polar = self.image_mode == ViewType.polar has_images = HexrdConfig().has_images() self.ui.action_export_polar_plot.setEnabled(is_polar and has_images) self.ui.action_calibration_line_picker.setEnabled(is_polar and has_images) self.ui.action_edit_apply_polar_mask.setEnabled(is_polar and has_images) self.ui.action_edit_apply_laue_mask_to_polar.setEnabled(is_polar) def start_powder_calibration(self): if not HexrdConfig().has_images(): msg = ('No images available for calibration.') QMessageBox.warning(self.ui, 'HEXRD', msg) return d = PowderCalibrationDialog(self.ui) if not d.exec_(): return HexrdConfig().emit_update_status_bar('Running powder calibration...') # Run the calibration in a background thread worker = AsyncWorker(run_powder_calibration) self.thread_pool.start(worker) # We currently don't have any progress updates, so make the # progress bar indeterminate. self.progress_dialog.setRange(0, 0) self.progress_dialog.setWindowTitle('Calibration Running') # Get the results and close the progress dialog when finished worker.signals.result.connect(self.finish_powder_calibration) worker.signals.finished.connect(self.progress_dialog.accept) msg = 'Powder calibration finished!' f = lambda: HexrdConfig().emit_update_status_bar(msg) worker.signals.finished.connect(f) self.progress_dialog.exec_() def finish_powder_calibration(self): self.update_config_gui() self.update_all() def update_config_gui(self): current_widget = self.ui.calibration_tab_widget.currentWidget() if current_widget is self.cal_tree_view: self.cal_tree_view.rebuild_tree() elif current_widget is self.calibration_config_widget.ui: self.calibration_config_widget.update_gui_from_config() elif current_widget is self.calibration_slider_widget.ui: self.calibration_slider_widget.update_gui_from_config() def eventFilter(self, target, event): if type(target) == QMainWindow and event.type() == QEvent.Close: # If the main window is closing, save the config settings HexrdConfig().save_settings() if not hasattr(self, '_first_paint_occurred'): if type(target) == QMainWindow and event.type() == QEvent.Paint: # Draw the images for the first time after the first paint # has occurred in order to avoid a black window. QTimer.singleShot(0, self.update_all) self._first_paint_occurred = True return False def update_if_mode_matches(self, mode): if self.image_mode == mode: self.update_all() def deep_rerender(self): # Clear all overlays HexrdConfig().clear_overlay_data() # Update all and clear the canvases self.update_all(clear_canvases=True) def update_all(self, clear_canvases=False): # If there are no images loaded, skip the request if not HexrdConfig().has_images(): return prev_blocked = self.calibration_config_widget.block_all_signals() # Need to clear focus from current widget if enter is pressed or # else all clicks are emit an editingFinished signal and view is # constantly re-rendered if QApplication.focusWidget() is not None: QApplication.focusWidget().clearFocus() if clear_canvases: for canvas in self.ui.image_tab_widget.image_canvases: canvas.clear() if self.image_mode == ViewType.cartesian: self.ui.image_tab_widget.show_cartesian() elif self.image_mode == ViewType.polar: # Rebuild polar masks del HexrdConfig().polar_masks[:] for line_data in HexrdConfig().polar_masks_line_data: create_polar_mask(line_data) self.ui.image_tab_widget.show_polar() else: self.ui.image_tab_widget.load_images() # Only ask if have haven't asked before if HexrdConfig().workflow is None: self.workflow_selection_dialog.show() self.calibration_config_widget.unblock_all_signals(prev_blocked) def live_update(self, enabled): previous = HexrdConfig().live_update HexrdConfig().set_live_update(enabled) if enabled: HexrdConfig().rerender_needed.connect(self.update_all) # Go ahead and trigger an update as well self.update_all() # Only disconnect if we were previously enabled. i.e. the signal was connected elif previous: HexrdConfig().rerender_needed.disconnect(self.update_all) def new_mouse_position(self, info): labels = [] labels.append('x = {:8.3f}'.format(info['x_data'])) labels.append('y = {:8.3f}'.format(info['y_data'])) delimiter = ', ' intensity = info['intensity'] if intensity is not None: labels.append('value = {:8.3f}'.format(info['intensity'])) if info['mode'] in [ViewType.cartesian, ViewType.polar]: labels.append('tth = {:8.3f}'.format(info['tth'])) labels.append('eta = {:8.3f}'.format(info['eta'])) labels.append('dsp = {:8.3f}'.format(info['dsp'])) labels.append('hkl = ' + info['hkl']) msg = delimiter.join(labels) self.ui.status_bar.showMessage(msg) def on_action_transform_detectors_triggered(self): mask_state = HexrdConfig().threshold_mask_status self.image_mode_widget.reset_masking() td = TransformDialog(self.ui).exec_() self.image_mode_widget.reset_masking(mask_state) def on_action_switch_workflow_triggered(self): self.workflow_selection_dialog.show() def update_indexing_menu(self): enabled = False image_series_dict = ImageLoadManager().unaggregated_images image_series_dict = HexrdConfig( ).imageseries_dict if image_series_dict is None else image_series_dict if image_series_dict: # Check length of first series series = next(iter(image_series_dict.values())) enabled = len(series) > 1 self.ui.action_run_indexing.setEnabled(enabled) def on_action_open_mask_manager_triggered(self): self.mask_manager_dialog.show()
class MainWindow(QObject): # Emitted when new images are loaded new_images_loaded = Signal() # Emitted when a new mask is added new_mask_added = Signal(str) # Emitted when a new configuration is loaded config_loaded = Signal() def __init__(self, parent=None, image_files=None): super().__init__(parent) loader = UiLoader() self.ui = loader.load_file('main_window.ui', parent) self.workflow_widgets = {'HEDM': [], 'LLNL': []} self.thread_pool = QThreadPool(self) self.progress_dialog = ProgressDialog(self.ui) self.progress_dialog.setWindowTitle('Calibration Running') self.messages_widget = MessagesWidget(self.ui) dock_widget_contents = self.ui.messages_dock_widget_contents dock_widget_contents.layout().addWidget(self.messages_widget.ui) self.ui.resizeDocks([self.ui.messages_dock_widget], [80], Qt.Vertical) # Let the left dock widget take up the whole left side self.ui.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.ui.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.color_map_editor = ColorMapEditor(self.ui.image_tab_widget, self.ui.central_widget) self.ui.color_map_dock_widgets.layout().addWidget( self.color_map_editor.ui) self.image_mode = ViewType.raw self.image_mode_widget = ImageModeWidget(self.ui.central_widget) self.ui.image_mode_dock_widgets.layout().addWidget( self.image_mode_widget.ui) self.add_materials_panel() self.load_widget = LoadPanel(self.ui) self.ui.load_page.setLayout(QVBoxLayout()) self.ui.load_page.layout().addWidget(self.load_widget.ui) self.load_widget.ui.setVisible(False) self.workflow_widgets[WORKFLOW_HEDM].append(self.load_widget.ui) self.import_data_widget = ImportDataPanel(self.color_map_editor, self.ui) self.ui.load_page.setLayout(QVBoxLayout()) self.ui.load_page.layout().addWidget(self.import_data_widget.ui) self.import_data_widget.ui.setVisible(False) self.workflow_widgets[WORKFLOW_LLNL].append(self.import_data_widget.ui) self.cal_tree_view = CalTreeView(self.ui) self.calibration_config_widget = CalibrationConfigWidget(self.ui) self.calibration_slider_widget = CalibrationSliderWidget(self.ui) tab_texts = ['Tree View', 'Form View', 'Slider View'] self.ui.calibration_tab_widget.clear() self.ui.calibration_tab_widget.addTab(self.cal_tree_view, tab_texts[0]) self.ui.calibration_tab_widget.addTab( self.calibration_config_widget.ui, tab_texts[1]) self.ui.calibration_tab_widget.addTab( self.calibration_slider_widget.ui, tab_texts[2]) self.mask_manager_dialog = MaskManagerDialog(self.ui) self.setup_connections() self.update_config_gui() self.add_workflow_widgets() self.ui.action_apply_pixel_solid_angle_correction.setChecked( HexrdConfig().apply_pixel_solid_angle_correction) self.ui.action_apply_lorentz_polarization_correction.setChecked( HexrdConfig().apply_lorentz_polarization_correction) self.ui.action_show_live_updates.setChecked(HexrdConfig().live_update) self.live_update(HexrdConfig().live_update) ImageFileManager().load_dummy_images(True) # In order to avoid both a not very nice looking black window, # and a bug with the tabbed view # (see https://github.com/HEXRD/hexrdgui/issues/261), # do not draw the images before the first paint event has # occurred. The images will be drawn automatically after # the first paint event has occurred (see MainWindow.eventFilter). self.workflow_selection_dialog = WorkflowSelectionDialog(self.ui) self.add_view_dock_widget_actions() def setup_connections(self): """This is to setup connections for non-gui objects""" self.ui.installEventFilter(self) self.ui.action_open_config_file.triggered.connect( self.on_action_open_config_file_triggered) self.ui.action_open_grain_fitting_results.triggered.connect( self.open_grain_fitting_results) self.ui.action_save_config_yaml.triggered.connect( self.on_action_save_config_yaml_triggered) self.ui.action_save_config_hexrd.triggered.connect( self.on_action_save_config_hexrd_triggered) self.ui.action_open_materials.triggered.connect( self.on_action_open_materials_triggered) self.ui.action_save_imageseries.triggered.connect( self.on_action_save_imageseries_triggered) self.ui.action_save_materials.triggered.connect( self.on_action_save_materials_triggered) self.ui.action_export_current_plot.triggered.connect( self.on_action_export_current_plot_triggered) self.ui.action_edit_euler_angle_convention.triggered.connect( self.on_action_edit_euler_angle_convention) self.ui.action_edit_apply_polar_mask.triggered.connect( self.on_action_edit_apply_polar_mask_triggered) self.ui.action_edit_apply_polar_mask.triggered.connect( self.ui.image_tab_widget.toggle_off_toolbar) self.ui.action_edit_apply_laue_mask_to_polar.triggered.connect( self.on_action_edit_apply_laue_mask_to_polar_triggered) self.ui.action_edit_apply_polygon_mask.triggered.connect( self.on_action_edit_apply_polygon_mask_triggered) self.ui.action_edit_apply_polygon_mask.triggered.connect( self.ui.image_tab_widget.toggle_off_toolbar) self.ui.action_edit_reset_instrument_config.triggered.connect( self.on_action_edit_reset_instrument_config) self.ui.action_edit_refinements.triggered.connect( self.edit_refinements) self.ui.action_transform_detectors.triggered.connect( self.on_action_transform_detectors_triggered) self.ui.action_open_mask_manager.triggered.connect( self.on_action_open_mask_manager_triggered) self.ui.action_show_live_updates.toggled.connect(self.live_update) self.ui.action_show_detector_borders.toggled.connect( HexrdConfig().set_show_detector_borders) self.ui.action_view_indexing_config.triggered.connect( self.view_indexing_config) self.ui.action_view_fit_grains_config.triggered.connect( self.view_fit_grains_config) self.ui.calibration_tab_widget.currentChanged.connect( self.update_config_gui) self.image_mode_widget.tab_changed.connect(self.change_image_mode) self.image_mode_widget.mask_applied.connect(self.update_all) self.ui.action_run_powder_calibration.triggered.connect( self.start_powder_calibration) self.ui.action_run_calibration.triggered.connect( self.on_action_run_calibration_triggered) self.ui.action_run_calibration.triggered.connect( self.ui.image_tab_widget.toggle_off_toolbar) self.ui.action_run_indexing.triggered.connect( self.on_action_run_indexing_triggered) self.ui.action_run_fit_grains.triggered.connect( self.on_action_run_fit_grains_triggered) self.ui.action_run_wppf.triggered.connect(self.run_wppf) self.new_images_loaded.connect(self.images_loaded) self.ui.image_tab_widget.update_needed.connect(self.update_all) self.ui.image_tab_widget.new_mouse_position.connect( self.new_mouse_position) self.ui.image_tab_widget.clear_mouse_position.connect( self.ui.status_bar.clearMessage) self.import_data_widget.new_config_loaded.connect( self.update_config_gui) self.import_data_widget.cancel_workflow.connect(self.load_dummy_images) self.config_loaded.connect( self.import_data_widget.config_loaded_from_menu) self.ui.action_show_toolbar.toggled.connect( self.ui.image_tab_widget.toggle_off_toolbar) self.image_mode_widget.polar_show_snip1d.connect( self.ui.image_tab_widget.polar_show_snip1d) self.ui.action_open_images.triggered.connect(self.open_image_files) self.ui.action_open_aps_imageseries.triggered.connect( self.open_aps_imageseries) HexrdConfig().update_status_bar.connect(self.ui.status_bar.showMessage) HexrdConfig().detectors_changed.connect(self.on_detectors_changed) HexrdConfig().deep_rerender_needed.connect(self.deep_rerender) HexrdConfig().workflow_changed.connect(self.add_workflow_widgets) HexrdConfig().raw_masks_changed.connect( self.ui.image_tab_widget.load_images) ImageLoadManager().update_needed.connect(self.update_all) ImageLoadManager().new_images_loaded.connect(self.new_images_loaded) ImageLoadManager().images_transformed.connect(self.update_config_gui) ImageLoadManager().live_update_status.connect(self.live_update) ImageLoadManager().state_updated.connect(self.load_widget.setup_gui) self.ui.action_switch_workflow.triggered.connect( self.on_action_switch_workflow_triggered) self.new_mask_added.connect(self.mask_manager_dialog.update_masks_list) self.image_mode_widget.tab_changed.connect( self.mask_manager_dialog.image_mode_changed) HexrdConfig().calibration_complete.connect(self.calibration_finished) self.ui.action_apply_pixel_solid_angle_correction.toggled.connect( HexrdConfig().set_apply_pixel_solid_angle_correction) self.ui.action_apply_lorentz_polarization_correction.toggled.connect( self.apply_lorentz_polarization_correction_toggled) self.import_data_widget.enforce_raw_mode.connect( self.enforce_view_mode) def set_icon(self, icon): self.ui.setWindowIcon(icon) def show(self): self.ui.show() def add_workflow_widgets(self): current_workflow = HexrdConfig().workflow for key in self.workflow_widgets.keys(): visible = True if key == current_workflow else False for widget in self.workflow_widgets[key]: widget.setVisible(visible) def add_materials_panel(self): # Remove the placeholder materials panel from the UI, and # add the real one. materials_panel_index = -1 for i in range(self.ui.config_tool_box.count()): if self.ui.config_tool_box.itemText(i) == 'Materials': materials_panel_index = i if materials_panel_index < 0: raise Exception('"Materials" panel not found!') self.ui.config_tool_box.removeItem(materials_panel_index) self.materials_panel = MaterialsPanel(self.ui) self.ui.config_tool_box.insertItem(materials_panel_index, self.materials_panel.ui, 'Materials') def on_action_open_config_file_triggered(self): selected_file, selected_filter = QFileDialog.getOpenFileName( self.ui, 'Load Configuration', HexrdConfig().working_dir, 'HEXRD files (*.hexrd *.yml)') if selected_file: path = Path(selected_file) HexrdConfig().working_dir = str(path.parent) HexrdConfig().load_instrument_config(str(path)) self.update_config_gui() def _save_config(self, extension, filter): selected_file, selected_filter = QFileDialog.getSaveFileName( self.ui, 'Save Configuration', HexrdConfig().working_dir, filter) if selected_file: if Path(selected_file).suffix != extension: selected_file += extension HexrdConfig().working_dir = str(Path(selected_file).parent) return HexrdConfig().save_instrument_config(selected_file) def on_action_save_config_hexrd_triggered(self): self._save_config('.hexrd', 'HEXRD files (*.hexrd)') def on_action_save_config_yaml_triggered(self): self._save_config('.yml', 'YAML files (*.yml)') def open_grain_fitting_results(self): selected_file, _ = QFileDialog.getOpenFileName( self.ui, 'Open Grain Fitting File', HexrdConfig().working_dir, 'Grain fitting output files (*.out)') if selected_file: path = Path(selected_file) HexrdConfig().working_dir = str(path.parent) data = np.loadtxt(selected_file) dialog = FitGrainsResultsDialog(data) dialog.show() self._fit_grains_results_dialog = dialog def on_detectors_changed(self): HexrdConfig().clear_overlay_data() HexrdConfig().current_imageseries_idx = 0 self.load_dummy_images() self.ui.image_tab_widget.switch_toolbar(0) if self.workflow_selection_dialog == WORKFLOW_LLNL: # Update the load widget self.load_widget.config_changed() def load_dummy_images(self): ImageFileManager().load_dummy_images() self.update_all(clear_canvases=True) self.ui.action_transform_detectors.setEnabled(False) self.new_images_loaded.emit() def open_image_file(self): images_dir = HexrdConfig().images_dir selected_file, selected_filter = QFileDialog.getOpenFileNames( self.ui, dir=images_dir) if len(selected_file) > 1: msg = ('Please select only one file.') QMessageBox.warning(self.ui, 'HEXRD', msg) return return selected_file def open_image_files(self): # Get the most recent images dir images_dir = HexrdConfig().images_dir selected_files, selected_filter = QFileDialog.getOpenFileNames( self.ui, dir=images_dir) if selected_files: # Save the chosen dir HexrdConfig().set_images_dir(selected_files[0]) files, manual = ImageLoadManager().load_images(selected_files) if not files: return if (any(len(f) != 1 for f in files) or len(files) < len(HexrdConfig().detector_names)): msg = ('Number of files must match number of detectors: ' + str(len(HexrdConfig().detector_names))) QMessageBox.warning(self.ui, 'HEXRD', msg) return # If it is a hdf5 file allow the user to select the path ext = os.path.splitext(selected_files[0])[1] if (ImageFileManager().is_hdf(ext) and not ImageFileManager().path_exists(selected_files[0])): selection = ImageFileManager().path_prompt(selected_files[0]) if not selection: return dialog = LoadImagesDialog(files, manual, self.ui) if dialog.exec_(): detector_names, image_files = dialog.results() image_files = [img for f in files for img in f] files = [[] for det in HexrdConfig().detector_names] for d, f in zip(detector_names, image_files): pos = HexrdConfig().detector_names.index(d) files[pos].append(f) ImageLoadManager().read_data(files, parent=self.ui) def images_loaded(self, enabled=True): self.ui.action_transform_detectors.setEnabled(enabled) self.update_color_map_bounds() self.update_hedm_enable_states() self.color_map_editor.reset_range() self.image_mode_widget.reset_masking() def open_aps_imageseries(self): # Get the most recent images dir images_dir = HexrdConfig().images_dir detector_names = HexrdConfig().detector_names selected_dirs = [] for name in detector_names: caption = 'Select directory for detector: ' + name d = QFileDialog.getExistingDirectory(self.ui, caption, dir=images_dir) if not d: return selected_dirs.append(d) images_dir = os.path.dirname(d) ImageFileManager().load_aps_imageseries(detector_names, selected_dirs) self.update_all() self.new_images_loaded.emit() def on_action_open_materials_triggered(self): selected_file, selected_filter = QFileDialog.getOpenFileName( self.ui, 'Load Materials File', HexrdConfig().working_dir, 'HDF5 files (*.h5 *.hdf5)') if selected_file: HexrdConfig().working_dir = os.path.dirname(selected_file) HexrdConfig().load_materials(selected_file) def on_action_save_imageseries_triggered(self): if not HexrdConfig().has_images(): msg = ('No ImageSeries available for saving.') QMessageBox.warning(self.ui, 'HEXRD', msg) return SaveImagesDialog(self.ui).exec_() def on_action_save_materials_triggered(self): selected_file, selected_filter = QFileDialog.getSaveFileName( self.ui, 'Save Materials', HexrdConfig().working_dir, 'HDF5 files (*.h5 *.hdf5)') if selected_file: HexrdConfig().working_dir = os.path.dirname(selected_file) # Ensure the file name has an hdf5 ending acceptable_exts = ['.h5', '.hdf5'] if not any(selected_file.endswith(x) for x in acceptable_exts): selected_file += '.h5' return HexrdConfig().save_materials(selected_file) def on_action_export_current_plot_triggered(self): selected_file, selected_filter = QFileDialog.getSaveFileName( self.ui, 'Save Current View', HexrdConfig().working_dir, 'HDF5 files (*.h5 *.hdf5);; NPZ files (*.npz)') if selected_file: HexrdConfig().working_dir = os.path.dirname(selected_file) return self.ui.image_tab_widget.export_current_plot(selected_file) def on_action_run_calibration_triggered(self): canvas = self.ui.image_tab_widget.image_canvases[0] runner = CalibrationRunner(canvas) self._calibration_runner = runner try: runner.run() except Exception as e: QMessageBox.warning(self.ui, 'HEXRD', str(e)) return def calibration_finished(self): print('Calibration finished') print('Updating the GUI') self.update_config_gui() self.update_all() def on_action_run_indexing_triggered(self): self._indexing_runner = IndexingRunner(self.ui) self._indexing_runner.run() def on_action_run_fit_grains_triggered(self): kwargs = { 'grains_table': None, 'indexing_runner': getattr(self, '_indexing_runner', None), 'parent': self.ui, } runner = self._grain_fitting_runner = FitGrainsRunner(**kwargs) runner.run() def run_wppf(self): self._wppf_runner = WppfRunner(self.ui) try: self._wppf_runner.run() except Exception as e: QMessageBox.critical(self.ui, 'HEXRD', str(e)) raise def update_color_map_bounds(self): self.color_map_editor.update_bounds(HexrdConfig().images_dict) def on_action_edit_euler_angle_convention(self): allowed_conventions = ['None', 'Extrinsic XYZ', 'Intrinsic ZXZ'] corresponding_values = [ None, { 'axes_order': 'xyz', 'extrinsic': True }, { 'axes_order': 'zxz', 'extrinsic': False } ] current = HexrdConfig().euler_angle_convention ind = corresponding_values.index(current) name, ok = QInputDialog.getItem(self.ui, 'HEXRD', 'Select Euler Angle Convention', allowed_conventions, ind, False) if not ok: # User canceled... return chosen = corresponding_values[allowed_conventions.index(name)] HexrdConfig().set_euler_angle_convention(chosen) self.update_all() self.update_config_gui() def on_action_edit_apply_polar_mask_triggered(self): # Make the dialog canvas = self.ui.image_tab_widget.image_canvases[0] self._apply_polar_mask_line_picker = (HandDrawnMaskDialog( canvas, self.ui)) self._apply_polar_mask_line_picker.start() self._apply_polar_mask_line_picker.finished.connect( self.run_apply_polar_mask) def run_apply_polar_mask(self, line_data): for line in line_data: name = create_unique_name(HexrdConfig().polar_masks_line_data, 'polar_mask_0') HexrdConfig().polar_masks_line_data[name] = line.copy() HexrdConfig().visible_masks.append(name) create_polar_mask([line.copy()], name) HexrdConfig().polar_masks_changed.emit() self.new_mask_added.emit(self.image_mode) def on_action_edit_apply_laue_mask_to_polar_triggered(self): if not HexrdConfig().show_overlays: msg = 'Overlays are not displayed' QMessageBox.critical(self.ui, 'HEXRD', msg) return overlays = HexrdConfig().overlays laue_overlays = [x for x in overlays if x['type'] == OverlayType.laue] laue_overlays = [x for x in laue_overlays if x['visible']] if not laue_overlays: msg = 'No Laue overlays found' QMessageBox.critical(self.ui, 'HEXRD', msg) return data = [] for overlay in laue_overlays: for det, val in overlay['data'].items(): for ranges in val['ranges']: data.append(ranges) if not data: msg = 'No Laue overlay ranges found' QMessageBox.critical(self.ui, 'HEXRD', msg) return name = create_unique_name(HexrdConfig().polar_masks_line_data, 'laue_mask') create_polar_mask(data, name) HexrdConfig().polar_masks_line_data[name] = data HexrdConfig().visible_masks.append(name) self.new_mask_added.emit(self.image_mode) HexrdConfig().polar_masks_changed.emit() def on_action_edit_apply_polygon_mask_triggered(self): mrd = MaskRegionsDialog(self.ui) mrd.new_mask_added.connect(self.new_mask_added.emit) mrd.show() def on_action_edit_reset_instrument_config(self): HexrdConfig().restore_instrument_config_backup() self.update_config_gui() def edit_refinements(self): w = self._refinements_editor = RefinementsEditor(self.ui) if not w.ui.exec_(): return # Update the UI in case settings have changed self.update_config_gui() self.materials_panel.update_overlay_editor() if w.material_values_modified: HexrdConfig().active_material_modified.emit() update_canvas = w.iconfig_values_modified or w.material_values_modified if update_canvas: self.update_all(clear_canvases=True) def change_image_mode(self, mode): self.image_mode = mode self.update_image_mode_enable_states() # Clear the overlays HexrdConfig().clear_overlay_data() self.update_all() def update_image_mode_enable_states(self): # This is for enable states that depend on the image mode is_cartesian = self.image_mode == ViewType.cartesian is_polar = self.image_mode == ViewType.polar has_images = HexrdConfig().has_images() self.ui.action_export_current_plot.setEnabled( (is_polar or is_cartesian) and has_images) self.ui.action_run_calibration.setEnabled(is_polar and has_images) self.ui.action_edit_apply_polar_mask.setEnabled(is_polar and has_images) self.ui.action_run_wppf.setEnabled(is_polar and has_images) self.ui.action_edit_apply_laue_mask_to_polar.setEnabled(is_polar) def start_powder_calibration(self): if not HexrdConfig().has_images(): msg = ('No images available for calibration.') QMessageBox.warning(self.ui, 'HEXRD', msg) return if hasattr(self, '_powder_runner'): self._powder_runner.clear() self._powder_runner = PowderRunner(self.ui) self._powder_runner.finished.connect(self.finish_powder_calibration) self._powder_runner.run() def finish_powder_calibration(self): self.update_config_gui() self.update_all() def update_config_gui(self): current_widget = self.ui.calibration_tab_widget.currentWidget() if current_widget is self.cal_tree_view: self.cal_tree_view.rebuild_tree() elif current_widget is self.calibration_config_widget.ui: self.calibration_config_widget.update_gui_from_config() elif current_widget is self.calibration_slider_widget.ui: self.calibration_slider_widget.update_gui_from_config() self.config_loaded.emit() def eventFilter(self, target, event): if type(target) == QMainWindow and event.type() == QEvent.Close: # If the main window is closing, save the config settings HexrdConfig().save_settings() if not hasattr(self, '_first_paint_occurred'): if type(target) == QMainWindow and event.type() == QEvent.Paint: # Draw the images for the first time after the first paint # has occurred in order to avoid a black window. QTimer.singleShot(0, self.update_all) self._first_paint_occurred = True return False def update_if_mode_matches(self, mode): if self.image_mode == mode: self.update_all() def deep_rerender(self): # Clear all overlays HexrdConfig().clear_overlay_data() # Update all and clear the canvases self.update_all(clear_canvases=True) def update_all(self, clear_canvases=False): # If there are no images loaded, skip the request if not HexrdConfig().has_images(): return prev_blocked = self.calibration_config_widget.block_all_signals() # Need to clear focus from current widget if enter is pressed or # else all clicks are emit an editingFinished signal and view is # constantly re-rendered if QApplication.focusWidget() is not None: QApplication.focusWidget().clearFocus() if clear_canvases: for canvas in self.ui.image_tab_widget.image_canvases: canvas.clear() if self.image_mode == ViewType.cartesian: self.ui.image_tab_widget.show_cartesian() elif self.image_mode == ViewType.polar: # Rebuild polar masks HexrdConfig().polar_masks.clear() for name, line_data in HexrdConfig().polar_masks_line_data.items(): if not isinstance(line_data, list): line_data = [line_data] create_polar_mask(line_data, name) for name, value in HexrdConfig().raw_masks_line_data.items(): det, data = value[0] line_data = convert_raw_to_polar(det, data) create_polar_mask(line_data, name) self.ui.image_tab_widget.show_polar() else: # Rebuild raw masks HexrdConfig().raw_masks.clear() for name, line_data in HexrdConfig().raw_masks_line_data.items(): create_raw_mask(name, line_data) for name, data in HexrdConfig().polar_masks_line_data.items(): if isinstance(data, list): # These are Laue spots continue else: line_data = convert_polar_to_raw(data) create_raw_mask(name, line_data) self.ui.image_tab_widget.load_images() # Only ask if have haven't asked before if HexrdConfig().workflow is None: self.workflow_selection_dialog.show() self.calibration_config_widget.unblock_all_signals(prev_blocked) def live_update(self, enabled): previous = HexrdConfig().live_update HexrdConfig().set_live_update(enabled) if enabled: HexrdConfig().rerender_needed.connect(self.update_all) # Go ahead and trigger an update as well self.update_all() # Only disconnect if we were previously enabled. i.e. the signal was # connected elif previous: HexrdConfig().rerender_needed.disconnect(self.update_all) def view_indexing_config(self): if hasattr(self, '_indexing_config_view'): self._indexing_config_view.reject() view = self._indexing_config_view = IndexingTreeViewDialog(self.ui) view.show() def view_fit_grains_config(self): if hasattr(self, '_fit_grains_config_view'): self._fit_grains_config_view.reject() view = self._fit_grains_config_view = FitGrainsTreeViewDialog(self.ui) view.show() def new_mouse_position(self, info): labels = [] labels.append(f'x = {info["x_data"]:8.3f}') labels.append(f'y = {info["y_data"]:8.3f}') delimiter = ', ' intensity = info['intensity'] if intensity is not None: labels.append(f'value = {info["intensity"]:8.3f}') labels.append(f'tth = {info["tth"]:8.3f}') labels.append(f'eta = {info["eta"]:8.3f}') labels.append(f'dsp = {info["dsp"]:8.3f}') labels.append(f'hkl = {info["hkl"]}') msg = delimiter.join(labels) self.ui.status_bar.showMessage(msg) def on_action_transform_detectors_triggered(self): mask_state = HexrdConfig().threshold_mask_status self.image_mode_widget.reset_masking() _ = TransformDialog(self.ui).exec_() self.image_mode_widget.reset_masking(mask_state) def on_action_switch_workflow_triggered(self): self.workflow_selection_dialog.show() def update_hedm_enable_states(self): actions = (self.ui.action_run_indexing, self.ui.action_run_fit_grains) for action in actions: action.setEnabled(False) image_series_dict = HexrdConfig().unagg_images if image_series_dict is None: image_series_dict = HexrdConfig().imageseries_dict if not image_series_dict: return # Check length of first series series = next(iter(image_series_dict.values())) if not len(series) > 1: return # If we made it here, they should be enabled. for action in actions: action.setEnabled(True) def on_action_open_mask_manager_triggered(self): self.mask_manager_dialog.show() def add_view_dock_widget_actions(self): # Add actions to show/hide all of the dock widgets dock_widgets = self.ui.findChildren(QDockWidget) titles = [w.windowTitle() for w in dock_widgets] for title, w in sorted(zip(titles, dock_widgets)): self.ui.view_dock_widgets.addAction(w.toggleViewAction()) def enforce_view_mode(self, raw_only): if raw_only: self.image_mode_widget.ui.tab_widget.setCurrentIndex(0) def apply_lorentz_polarization_correction_toggled(self, b): if not b: # Just turn it off and return HexrdConfig().apply_lorentz_polarization_correction = b return # Get the user to first select the Lorentz polarization options d = LorentzPolarizationOptionsDialog(self.ui) if not d.exec_(): # Canceled... uncheck the action. action = self.ui.action_apply_lorentz_polarization_correction action.setChecked(False) return # The dialog should have modified HexrdConfig's Lorentz options # already. Just apply it now. HexrdConfig().apply_lorentz_polarization_correction = b