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()
Beispiel #3
0
    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))
Beispiel #4
0
 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))
Beispiel #5
0
 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()
Beispiel #6
0
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
Beispiel #7
0
 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
Beispiel #8
0
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
Beispiel #9
0
 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('')
Beispiel #10
0
    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()
Beispiel #11
0
 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()
Beispiel #12
0
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
Beispiel #13
0
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
Beispiel #14
0
 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()
Beispiel #15
0
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()
Beispiel #16
0
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)