def export_plans(self): choose = ExportDialog(self.settings.batch_plans, PlanPreview) if not choose.exec_(): return dial = QFileDialog(self, "Export calculation plans") dial.setFileMode(QFileDialog.AnyFile) dial.setAcceptMode(QFileDialog.AcceptSave) dial.setDirectory( dial.setDirectory( self.settings.get("io.batch_plan_directory", str(Path.home())))) dial.setNameFilter("Calculation plans (*.json)") dial.setDefaultSuffix("json") dial.selectFile("calculation_plans.json") dial.setHistory(dial.history() + self.settings.get_path_history()) if dial.exec_(): file_path = str(dial.selectedFiles()[0]) self.settings.set("io.batch_plan_directory", os.path.dirname(file_path)) self.settings.add_path_history(os.path.dirname(file_path)) data = { x: self.settings.batch_plans[x] for x in choose.get_export_list() } with open(file_path, "w") as ff: json.dump(data, ff, cls=self.settings.json_encoder_class, indent=2)
def import_plans(self): dial = QFileDialog(self, "Import calculation plans") dial.setFileMode(QFileDialog.ExistingFile) dial.setAcceptMode(QFileDialog.AcceptOpen) dial.setDirectory( self.settings.get("io.open_directory", str(Path.home()))) dial.setNameFilter("Calculation plans (*.json)") dial.setDefaultSuffix("json") dial.setHistory(dial.history() + self.settings.get_path_history()) if dial.exec_(): file_path = dial.selectedFiles()[0] plans, err = self.settings.load_part(file_path) self.settings.set("io.batch_plan_directory", os.path.dirname(file_path)) self.settings.add_path_history(os.path.dirname(file_path)) if err: QMessageBox.warning( self, "Import error", "error during importing, part of data were filtered.") choose = ImportDialog(plans, self.settings.batch_plans, PlanPreview) if choose.exec_(): for original_name, final_name in choose.get_import_list(): self.settings.batch_plans[final_name] = plans[ original_name] self.update_plan_list()
def export_measurement_profiles(self): exp = ExportDialog(self.settings.measurement_profiles, StringViewer) if not exp.exec_(): return dial = QFileDialog(self, "Export settings profiles") dial.setDirectory(self.settings.get("io.export_directory", "")) dial.setFileMode(QFileDialog.AnyFile) dial.setAcceptMode(QFileDialog.AcceptSave) dial.setNameFilter("measurement profile (*.json)") dial.setDefaultSuffix("json") dial.selectFile("measurements_profile.json") if dial.exec_(): file_path = str(dial.selectedFiles()[0]) self.settings.set("io.export_directory", file_path) data = { x: self.settings.measurement_profiles[x] for x in exp.get_export_list() } with open(file_path, "w") as ff: json.dump(data, ff, cls=self.settings.json_encoder_class, indent=2) self.settings.set("io.save_directory", os.path.dirname(file_path))
def export_pipeline(self): exp = ExportDialog(self._settings.segmentation_pipelines, ProfileDictViewer) if not exp.exec_(): return dial = QFileDialog(self, "Export pipeline segment") dial.setFileMode(QFileDialog.AnyFile) dial.setAcceptMode(QFileDialog.AcceptSave) dial.setDirectory(self._settings.get("io.save_directory", "")) dial.setNameFilter("Segment pipeline (*.json)") dial.setDefaultSuffix("json") dial.selectFile("segment_pipeline.json") dial.setHistory(dial.history() + self._settings.get_path_history()) if dial.exec_(): file_path = dial.selectedFiles()[0] data = { x: self._settings.segmentation_pipelines[x] for x in exp.get_export_list() } with open(file_path, "w") as ff: json.dump(data, ff, cls=self._settings.json_encoder_class, indent=2) self._settings.set("io.save_directory", os.path.dirname(file_path)) self._settings.add_path_history(os.path.dirname(file_path))
def import_profiles(self): dial = QFileDialog(self, "Import profile segment") dial.setFileMode(QFileDialog.ExistingFile) dial.setAcceptMode(QFileDialog.AcceptOpen) dial.setDirectory( self._settings.get("io.save_directory", str(Path.home()))) dial.setNameFilter("Segment profile (*.json)") dial.setHistory(dial.history() + self._settings.get_path_history()) if dial.exec_(): file_path = dial.selectedFiles()[0] save_dir = os.path.dirname(file_path) self._settings.set("io.save_directory", save_dir) self._settings.add_path_history(save_dir) profs, err = self._settings.load_part(file_path) if err: QMessageBox.warning( self, "Import error", "error during importing, part of data were filtered.") profiles_dict = self._settings.segmentation_profiles imp = ImportDialog(profs, profiles_dict, ProfileDictViewer) if not imp.exec_(): return for original_name, final_name in imp.get_import_list(): profiles_dict[final_name] = profs[original_name] self._settings.dump() self.update_profile_list()
def open_a_file_dialog(parent=None, default_suffix=None, directory=None, file_filter=None, accept_mode=None, file_mode=None): """ Open a dialog asking for a file location and name to and return it :param parent: QWidget; The parent QWidget of the created dialog :param default_suffix: String; The default suffix to be passed :param directory: String; Directory to which the dialog will open :param file_filter: String; The filter name and file type e.g. "Python files (*.py)" :param accept_mode: enum AcceptMode; Defines the AcceptMode of the dialog, check QFileDialog Class for details :param file_mode: enum FileMode; Defines the FileMode of the dialog, check QFileDialog Class for details :return: String; The filename that was selected, it is possible to return a directory so look out for that """ global _LAST_SAVE_DIRECTORY dialog = QFileDialog(parent) # It is the intention to only save the user's last used directory until workbench is restarted similar to other # applications (VSCode, Gedit etc) if _LAST_SAVE_DIRECTORY is not None and directory is None: dialog.setDirectory(_LAST_SAVE_DIRECTORY) elif directory is not None: dialog.setDirectory(directory) else: dialog.setDirectory(os.path.expanduser("~")) if file_filter is not None: dialog.setFilter(QDir.Files) dialog.setNameFilter(file_filter) if default_suffix is not None: dialog.setDefaultSuffix(default_suffix) if file_mode is not None: dialog.setFileMode(file_mode) if accept_mode is not None: dialog.setAcceptMode(accept_mode) # Connect the actual filename setter dialog.fileSelected.connect(_set_last_save) # Wait for dialog to finish before allowing continuation of code if dialog.exec_() == QDialog.Rejected: return None filename = _LAST_SAVE_DIRECTORY # Make sure that the _LAST_SAVE_DIRECTORY is set as a directory if _LAST_SAVE_DIRECTORY is not None and not os.path.isdir( _LAST_SAVE_DIRECTORY): # Remove the file for last directory _LAST_SAVE_DIRECTORY = os.path.dirname( os.path.abspath(_LAST_SAVE_DIRECTORY)) return filename
def __get_directory(self, name): dialog = QFileDialog(parent=self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setNameFilter(f"{name} ({name}.exe {name})") dialog.setWindowTitle(f"Select {name}") if dialog.exec(): selected = dialog.selectedFiles() if len(selected) > 0: return selected[0] return None
def open_a_file_dialog(parent=None, default_suffix=None, directory=None, file_filter=None, accept_mode=None, file_mode=None): """ Open a dialog asking for a file location and name to and return it :param parent: QWidget; The parent QWidget of the created dialog :param default_suffix: String; The default suffix to be passed :param directory: String; Directory to which the dialog will open :param file_filter: String; The filter name and file type e.g. "Python files (*.py)" :param accept_mode: enum AcceptMode; Defines the AcceptMode of the dialog, check QFileDialog Class for details :param file_mode: enum FileMode; Defines the FileMode of the dialog, check QFileDialog Class for details :return: String; The filename that was selected, it is possible to return a directory so look out for that """ global _LAST_SAVE_DIRECTORY dialog = QFileDialog(parent) # It is the intention to only save the user's last used directory until workbench is restarted similar to other # applications (VSCode, Gedit etc) if _LAST_SAVE_DIRECTORY is not None and directory is None: dialog.setDirectory(_LAST_SAVE_DIRECTORY) elif directory is not None: dialog.setDirectory(directory) else: dialog.setDirectory(os.path.expanduser("~")) if file_filter is not None: dialog.setFilter(QDir.Files) dialog.setNameFilter(file_filter) if default_suffix is not None: dialog.setDefaultSuffix(default_suffix) if file_mode is not None: dialog.setFileMode(file_mode) if accept_mode is not None: dialog.setAcceptMode(accept_mode) # Connect the actual filename setter dialog.fileSelected.connect(_set_last_save) # Wait for dialog to finish before allowing continuation of code if dialog.exec_() == QDialog.Rejected: return None filename = _LAST_SAVE_DIRECTORY # Make sure that the _LAST_SAVE_DIRECTORY is set as a directory if _LAST_SAVE_DIRECTORY is not None and not os.path.isdir(_LAST_SAVE_DIRECTORY): # Remove the file for last directory _LAST_SAVE_DIRECTORY = os.path.dirname(os.path.abspath(_LAST_SAVE_DIRECTORY)) return filename
def showExtractCompleteSoundPicker(self): dialog = QFileDialog(parent=self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setNameFilter("Audio (*.wav)") dialog.setWindowTitle(f"Select Notification Sound") if dialog.exec(): selected = dialog.selectedFiles() if len(selected) > 0: self.extractCompleteAudioFile.setText(selected[0]) else: self.extractCompleteAudioFile.setText('') else: self.extractCompleteAudioFile.setText('')
def stl_dialog(self): dialog = QFileDialog(self) dialog.setWindowTitle('Open STL Tool file') dialog.setNameFilter('(*.stl)') filename = None if dialog.exec_() == QDialog.Accepted: filename = dialog.selectedFiles() if filename: self.setFilename(filename[0]) else: self.clearFilename()
def file_dialog(self, *args, **kwargs): dialog = QFileDialog(self) if not self._is_file_dialog_opened: # set the initial directory to HOME dialog.setDirectory(os.path.expanduser("~")) self._is_file_dialog_opened = True dialog.setWindowTitle('Open .edi Files...') dialog.setNameFilter('.edi files (*.edi)') dialog.setFileMode(QFileDialog.ExistingFiles) if dialog.exec_() == QDialog.Accepted: file_list = dialog.selectedFiles() self._progress_bar.setMaximumValue(len(file_list)) self._progress_bar.onStart() self._add_files(file_list, DEFAULT_GROUP_NAME) self._update_tree_view() self._progress_bar.onFinished()
def parse_file(filter, title, parsers): ''' Presents a file dialog to the user so they can choose something to load. :return: a 2 entry tuple with the file name and loaded thing (if anything was loaded) ''' dialog = QFileDialog() dialog.setFileMode(QFileDialog.ExistingFile) dialog.setNameFilter(filter) dialog.setWindowTitle(title) if dialog.exec(): selected = dialog.selectedFiles() if len(selected) > 0: file_name = selected[0] for k, v in parsers.items(): if file_name.endswith(k): return v(file_name) return None, None
def load_filter(parent, status_bar=None): ''' Presents a file dialog to the user so they can choose a filter to load. :return: the loaded filter, if any. ''' dialog = QFileDialog(parent=parent) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setNameFilter(f"*.filter") dialog.setWindowTitle(f"Load Filter") if dialog.exec(): selected = dialog.selectedFiles() if len(selected) > 0: with open(selected[0], 'r') as infile: input = json.load(infile) if status_bar is not None: status_bar.showMessage(f"Loaded filter from {infile.name}") return input return None
def import_measurement_profiles(self): dial = QFileDialog(self, "Import settings profiles") dial.setDirectory(self.settings.get("io.export_directory", "")) dial.setFileMode(QFileDialog.ExistingFile) dial.setNameFilter("measurement profile (*.json)") if dial.exec_(): file_path = str(dial.selectedFiles()[0]) self.settings.set("io.export_directory", file_path) stat, err = self.settings.load_part(file_path) if err: QMessageBox.warning( self, "Import error", "error during importing, part of data were filtered.") measurement_dict = self.settings.measurement_profiles imp = ImportDialog(stat, measurement_dict, StringViewer) if not imp.exec_(): return for original_name, final_name in imp.get_import_list(): measurement_dict[final_name] = stat[original_name] self.profile_list.clear() self.profile_list.addItems(list(sorted(measurement_dict.keys()))) self.settings.dump()
class MainPlotter(InteractivePlotter): """Main application.""" def __init__(self, params, parent=None, testing=False): """Initialize the MainPlotter.""" super().__init__(params, parent=parent, testing=testing) self.unit = self.params["unit"] self.dimensions = self.params["dimensions"] self.default_block_color = self.params["block"]["color"] self.icon_size = self.params["builder"]["toolbar"]["icon_size"] self.button_pressed = False self.button_released = False self.area_selection = False self.floor = None self.ceiling = None self.icons = None self.toolbar = None self.current_block_mode = None self.mode_functions = None self.set_dimensions(self.dimensions) # configuration self.show() self.load_elements() self.add_elements() self.load_block_modes() self.load_icons() self.load_toolbar() self.load_dialogs() self.selector.hide() self.update_camera() self.render_scene() def update_camera(self): """Update the internal camera.""" self.set_focal_point(self.grid.center) super().update_camera() def move_camera(self, update, inverse=False): """Trigger a pick when moving the camera.""" super().move_camera(update, inverse) x, y = self.interactor.GetEventPosition() self.picker.Pick(x, y, 0, self.renderer) self.render_scene() def translate_camera(self, tr): """Translate the camera.""" self.grid.translate(tr) self.set_focal_point(self.grid.center) super().translate_camera(tr) def on_mouse_move(self, vtk_picker, event): """Process mouse move events.""" x, y = vtk_picker.GetEventPosition() self.picker.Pick(x, y, 0, self.renderer) def on_mouse_wheel_forward(self, vtk_picker, event): """Process mouse wheel forward events.""" tr = np.array([0., 0., self.unit]) if self.grid.origin[2] < self.ceiling: self.translate_camera(tr) self.render_scene() def on_mouse_wheel_backward(self, vtk_picker, event): """Process mouse wheel backward events.""" tr = np.array([0., 0., -self.unit]) if self.grid.origin[2] > self.floor: self.translate_camera(tr) self.render_scene() def on_mouse_left_press(self, vtk_picker, event): """Process mouse left button press events.""" x, y = vtk_picker.GetEventPosition() self.button_pressed = True self.picker.Pick(x, y, 0, self.renderer) def on_mouse_left_release(self, vtk_picker, event): """Process mouse left button release events.""" x, y = vtk_picker.GetEventPosition() self.button_released = True self.picker.Pick(x, y, 0, self.renderer) self.button_pressed = False def on_pick(self, vtk_picker, event): """Process pick events.""" func = self.mode_functions.get(self.current_block_mode, None) func(vtk_picker) def load_block_modes(self): """Load the block modes.""" self.set_block_mode(BlockMode.BUILD) self.mode_functions = dict() for mode in BlockMode: func_name = "use_{}_mode".format(mode.name.lower()) self.mode_functions[mode] = getattr(self, func_name) def add_element(self, element): """Add an element to the scene.""" actor = self.add_mesh(**element.plotting) element.actor = actor actor.element_id = element.element_id def add_elements(self): """Add all the default elements to the scene.""" self.add_element(self.block) self.add_element(self.grid) self.add_element(self.plane) self.add_element(self.selector) self.add_element(self.selector.selector_x) self.add_element(self.selector.selector_y) self.add_element(self.selector.selector_xy) def remove_element(self, element): """Remove an elements from the scene.""" self.renderer.RemoveActor(element.actor) element.actor = None def remove_elements(self): """Remove all the default elements of the scene.""" self.remove_element(self.block) self.remove_element(self.grid) self.remove_element(self.plane) self.remove_element(self.selector) self.remove_element(self.selector.selector_x) self.remove_element(self.selector.selector_y) self.remove_element(self.selector.selector_xy) def load_elements(self): """Load the default elements.""" self.block = Block(self.params, self.dimensions) self.grid = Grid(self.params, self.dimensions) self.plane = Plane(self.params, self.dimensions) self.selector = SymmetrySelector(self.params, self.dimensions) def load_icons(self): """Load the icons. The resource configuration file ``blockbuilder/icons/blockbuilder.qrc`` describes the location of the resources in the filesystem and also defines aliases for their use in the code. To automatically generate the resource file in ``blockbuilder/icons``: pyrcc5 -o resources.py blockbuilder.qrc """ self.icons = dict() for category in (BlockMode, Toggle, Symmetry, Action): for element in category: icon_resource = ":/{}.svg".format(element.name.lower()) self.icons[element] = QIcon(icon_resource) def _add_toolbar_group(self, group, func, default_value): button_group = QButtonGroup(parent=self.toolbar) for element in group: icon = self.icons.get(element, None) button = QToolButton() button.setFixedSize(QSize(*self.icon_size)) button.setIcon(icon) button.setCheckable(True) if default_value is not None and element is default_value: button.setChecked(True) button.toggled.connect(DefaultFunction(func, element)) button_group.addButton(button) self.toolbar.addWidget(button) def _add_toolbar_actions(self): for action in Action: icon = self.icons.get(action, None) button = QToolButton() button.setFixedSize(QSize(*self.icon_size)) button.setIcon(icon) func_name = "action_{}".format(action.name.lower()) func = getattr(self, func_name, None) button.clicked.connect(func) self.toolbar.addWidget(button) def _add_toolbar_toggles(self): for toggle in Toggle: icon = self.icons.get(toggle, None) button = QToolButton() button.setFixedSize(QSize(*self.icon_size)) button.setIcon(icon) button.setCheckable(True) toggle_name = toggle.name.lower() default_value = self.params["builder"]["toggles"][toggle_name] func_name = "toggle_{}".format(toggle_name) func = getattr(self, func_name, None) assert callable(func) button.toggled.connect(func) button.setChecked(default_value) func(default_value) self.toolbar.addWidget(button) def _add_toolbar_color_button(self): self.color_button = ColorButton() self.color_button.setFixedSize(QSize(*self.icon_size)) self.color_button.colorChanged.connect(self.set_block_color) self.toolbar.addWidget(self.color_button) self.set_block_color(self.default_block_color, is_int=False) self.color_button.setColor(self.default_block_color, is_int=False) def load_toolbar(self): """Initialize the toolbar.""" self.toolbar = self.addToolBar("toolbar") toolbar_areas = self.params["builder"]["toolbar"]["area"]["range"] toolbar_area = self.params["builder"]["toolbar"]["area"]["value"] self.addToolBar( _get_toolbar_area(toolbar_area, toolbar_areas), self.toolbar, ) self.toolbar.setIconSize(QSize(*self.icon_size)) self._add_toolbar_color_button() self.toolbar.addSeparator() self._add_toolbar_group( group=BlockMode, func=self.set_block_mode, default_value=BlockMode.BUILD, ) self.toolbar.addSeparator() self._add_toolbar_toggles() self.toolbar.addSeparator() self._add_toolbar_group( group=Symmetry, func=self.set_symmetry, default_value=Symmetry.SYMMETRY_NONE, ) self.toolbar.addSeparator() self._add_toolbar_actions() def load_dialogs(self): """Load the dialogs.""" # export dialog self.export_dialog = QFileDialog(self) self.export_dialog.setWindowTitle("Export") self.export_dialog.setNameFilter("Blockset (*.vts *.vtk)") self.export_dialog.setWindowIcon(self.icons[Action.EXPORT]) # XXX: Fails on CI if modal # self.export_dialog.setModal(True) # import dialog self.import_dialog = QFileDialog(self) self.import_dialog.setNameFilter("Blockset (*.vts *.vtk)") self.import_dialog.setWindowTitle("Import") self.import_dialog.setWindowIcon(self.icons[Action.IMPORT]) # XXX: Fails on CI if modal # self.import_dialog.setModal(True) # setting dialog self.setting_dialog = SettingDialog(self.params, self) self.setting_dialog.setWindowIcon(self.icons[Action.SETTING]) # help dialog short_desc = [ "Build mode", "Delete mode", "Area selection", "Edge visibility", "Symmetry Off", "Symmetry X", "Symmetry Y", "Symmetry XY", "Reset", "Import", "Export", "Setting", "Help", ] long_desc = [ "Enable the build mode", "Enable the delete mode", "Toggle the area selection", "Toggle the edge visibility", "Disable the symmetry", "Enable symmetry along the X axis", "Enable symmetry along the Y axis", "Enable symmetry along X and Y axis", "Reset the scene", "Import a blockset", "Export a blockset", "Open the setting dialog", "Open the help dialog", ] self.help_dialog = HelpDialog(self.icons, self.icon_size, short_desc, long_desc, self) self.help_dialog.setWindowIcon(self.icons[Action.HELP]) def set_dimensions(self, dimensions): """Set the current dimensions.""" self.dimensions = np.asarray(dimensions) self.floor = 0. self.ceiling = (self.dimensions[2] - 2) * self.unit self.distance = np.max(self.dimensions) * 2 * self.unit self.distance_rng = [4 * self.unit, 2 * self.distance] def set_symmetry(self, value): """Set the current symmetry.""" self.selector.set_symmetry(value) def set_block_mode(self, value=None): """Set the current block mode.""" if value is None: value = self.current_block_mode else: self.current_block_mode = value self.grid.set_block_mode(value) self.selector.set_block_mode(value) self.render_scene() def set_block_color(self, value=None, is_int=True): """Set the current block color.""" self.block.set_color(value) def use_delete_mode(self, vtk_picker): """Use the delete mode.""" self._build_or_delete(vtk_picker, self.block.remove) def use_build_mode(self, vtk_picker): """Use the build mode.""" self._build_or_delete(vtk_picker, self.block.add) def _build_or_delete(self, vtk_picker, operation): intersection = Intersection(vtk_picker) if not intersection.exist(): self.selector.hide() self.selector.reset_area() self.render_scene() return if not intersection.element(ElementId.GRID): return grid_ipoint = intersection.point(ElementId.GRID) coords = np.floor(grid_ipoint / self.unit) coords[2] = self.grid.origin[2] / self.unit self.coords = coords self.selector.select(coords) self.selector.show() if self.area_selection: if self.button_released: first_set = self.selector.get_first_coords() is not None last_set = self.selector.get_last_coords() is not None if first_set and last_set: for area in self.selector.selection_area(): operation(area) elif first_set and not last_set: for coords in self.selector.selection(): operation(coords) self.selector.reset_area() self.button_released = False elif self.button_pressed: if self.selector.get_first_coords() is None: self.selector.set_first_coords(coords) else: self.selector.set_last_coords(coords) area = ( self.selector.get_first_coords(), self.selector.get_last_coords(), ) self.selector.select_area(area) else: if self.button_pressed: for coords in self.selector.selection(): operation(coords) self.render_scene() def action_reset(self, unused): """Reset the block properties.""" del unused self.block.remove_all() self.set_block_color(self.default_block_color, is_int=False) self.color_button.setColor(self.default_block_color, is_int=False) self.render_scene() def action_import(self, value=None): """Import an external blockset.""" def _import(filename): if len(filename) == 0: raise ValueError("The input filename string is empty") reader = vtk.vtkXMLStructuredGridReader() reader.SetFileName(filename) reader.Update() mesh = reader.GetOutput() dimensions = mesh.GetDimensions() imported_block = Block(self.params, dimensions, mesh) if all(np.equal(dimensions, self.dimensions)): self.block.merge(imported_block) else: final_dimensions = [ self.block.dimensions, imported_block.dimensions ] final_dimensions = np.max(final_dimensions, axis=0) if all(np.equal(self.dimensions, final_dimensions)): self.block.merge(imported_block) else: self.remove_elements() old_block = self.block self.set_dimensions(final_dimensions) self.load_elements() self.add_elements() # restore edge visibility self.block.toggle_edges(old_block.show_edges) # restore block mode self.set_block_mode() self.block.merge(old_block) self.block.merge(imported_block) self.selector.hide() self.update_camera() self.render_scene() if isinstance(value, bool): self.import_dialog.fileSelected.connect(_import) self.import_dialog.show() elif isinstance(value, str): _import(value) else: raise TypeError("Expected type for ``filename``is ``str``" " but {} was given.".format(type(value))) def action_export(self, value=None): """Export the internal blockset.""" def _export(filename): if len(filename) == 0: raise ValueError("The output filename string is empty") writer = vtk.vtkXMLStructuredGridWriter() writer.SetFileName(filename) writer.SetInputData(self.block.mesh) writer.Write() if isinstance(value, bool): self.export_dialog.fileSelected.connect(_export) self.export_dialog.show() elif isinstance(value, str): _export(value) else: raise TypeError("Expected type for ``filename``is ``str``" " but {} was given.".format(type(value))) def action_setting(self, value=None): """Open the settings menu.""" del value self.setting_dialog.show() def action_help(self, value=None): """Display the help menu.""" del value self.help_dialog.show() def toggle_area(self, value): """Toggle area selection.""" self.area_selection = value def toggle_edges(self, value): """Toggle area selection.""" self.block.toggle_edges(value) self.render_scene()
class VMainWindow(VMainWindowBaseClass, Ui_VMainWindowClass): # str, DataFrame, DataType data_selected = Signal(str, object, object) predicted_data_selected = Signal(dict) ground_truth_data_selected = Signal(dict) image_data_selected = Signal([str, list], [str]) file_loaded = Signal(object) def __init__(self): super(VMainWindow, self).__init__() self.setupUi(self) self._widget_setup() self._signals_setup() def __enter__(self): return self def __exit__(self, *args, **kwargs): try: self.controller.cleanup() except AttributeError: pass def _widget_setup(self): self._error_dialog = VErrorMessageBox(self) self._file_dialog = QFileDialog(self, self.tr('Open File'), '') self._file_dialog.setOption(QFileDialog.DontUseNativeDialog) # type(self.marker_options_groupbox) -> widgets.VMarkerOptionsGroupBox self.marker_options_groupbox.add_widget( 'Predicted', DataType.PREDICTED) self.marker_options_groupbox.add_widget( 'Ground Truth', DataType.GROUND_TRUTH) shapes = (Shape.CIRCLE, Shape.DIAMOND) icons = (QIcon(r':/circle'), QIcon(r':/diamond')) for widget in self.marker_options_groupbox.widgets.values(): for icon, shape in zip(icons, shapes): widget.add_marker_shape(shape, icon) scene = VGraphicsScene() self.graphics_view.setScene(scene) self.controller = Controller(scene, self.tables_tab_widget, self.marker_options_groupbox) def _signals_setup(self) -> None: self.action_open_ground_truth.triggered.connect( lambda: self.open(DataType.GROUND_TRUTH)) self.action_open_predicted.triggered.connect( lambda: self.open(DataType.PREDICTED)) self.action_open_image.triggered.connect( lambda: self.open(DataType.IMAGE)) self.graphics_view_scrollbar.value_changed[int].connect( self.controller.set_index) self.file_loaded[object].connect(self.marker_options_groupbox.enable) self.file_loaded[object].connect( lambda d: self.menu_open_data.setEnabled(True) if d & DataType.IMAGE else None) def _parse_filename(self, filelist: Sequence[str], dtype: DataType) -> str: """ Checks for errors in the list of filenames, i.e if list is empty, or if list has more than one filename. If the list passes the checks, we proceed to load the file. For now, we reject TIFF files because loading TIFF images is not implemented. """ def create_error_dialog(message): self._error_dialog.message = message self._error_dialog.exec_() if len(filelist) > 1: return create_error_dialog( '{} files were selected '.format(len(filelist)) + 'but we can only load one file at a time.') elif len(filelist) == 0: return create_error_dialog('No files were selected.') else: filename = filelist[0] # temporary to reject loaded tif images because loading them # hasn't been implemented yet print('***parsing filename***') filename_ext = splitext(filename)[-1] if filename_ext in EXTENSIONS[DataType.TIFF_IMAGE][0]: create_error_dialog('tiff files not yet supported.') return '' else: return filename def open(self, dtype: DataType) -> None: """ Opens a QDialog in which the user selects an HDF5 or TIFF file. """ # we must first create a properly-formated regexp string for the # 'filter' argument of the 'setNameFilter' method in our already- # instantiated but not shown QDialog. extensions = OrderedDict(zip(FILETYPES[dtype], EXTENSIONS[dtype])) ext = [[''] + list(ext) for ext in extensions.values()] extlist = [self.tr(k) + " (" + ' *'.join(e) + ")" for k, e in zip(extensions, ext)] filter_ = ';;'.join(extlist) # example filter_ string: # Tiff Files (*.tif, *.tiff, *.ome.tif);;HDF5 Files (*.h5, *.hdf5, *.hf5, *.hd5) self._file_dialog.setNameFilter(filter_) # closing the QDialog returns an enum, either "Accepted" or "Rejected" # depending on whether the "Ok" or "Cancel" button was pressed, # respectively result = self._file_dialog.exec_() if result == QFileDialog.Accepted: filename = self._parse_filename( self._file_dialog.selectedFiles(), dtype) if filename: self.open_inspection_widget(filename, dtype) def open_inspection_widget(self, filename: str, dtype: DataType): list_widget_columns = ['name', 'directory'] ground_truth_titles = OrderedDict( [('Ground Truth Coordinates', list_widget_columns)]) predicted_titles = OrderedDict( [('Predicted Coordinates', list_widget_columns), ('Probabilities', list_widget_columns)]) image_titles = OrderedDict( [('Images', list_widget_columns)]) list_widget_args = EnumDict([ (DataType.GROUND_TRUTH, ground_truth_titles), (DataType.PREDICTED, predicted_titles), (DataType.HDF_IMAGE, image_titles)]) args = list_widget_args[dtype][0] dialog = make_dialog(filename, args, dtype) result = dialog.exec_() if result == QFileDialog.Accepted: self.load(filename, dtype, dialog.get_data('directory')) def load(self, filename: str, dtype: DataType, handles: DataFrame): if dtype & DataType.HDF_IMAGE: req = HDF5Request1D(filename, handles, axis=0) self.graphics_view_scrollbar.setEnabled(True) elif dtype & DataType.DATA: req = HDF5Request2D(filename, handles) else: raise NotImplementedError('Loading {} not yet implemented.'.format(dtype)) print(filename) print(dtype) print(handles) source = HDF5DataSource(filename, req) self.controller.set_datasource(source, dtype) self.graphics_view_scrollbar.setMaximum(len(source) - 1) self.file_loaded.emit(dtype) @Slot() def save(self): pass @Slot() def save_as(self): pass @Slot() def quit(self): sys.exit(0)