예제 #1
0
 def pick_output_dir(self):
     '''
     Sets the output directory.
     '''
     dialog = QFileDialog(parent=self)
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     dialog.setOption(QFileDialog.ShowDirsOnly)
     dialog.setWindowTitle('Select a location to store the generated minidsp config files')
     if dialog.exec():
         selected = dialog.selectedFiles()
         if len(selected) > 0:
             if os.path.abspath(selected[0]) == os.path.abspath(self.__beq_dir):
                 QMessageBox.critical(self, '',
                                      f"Output directory cannot be inside the input directory, choose a different folder",
                                      QMessageBox.Ok)
             else:
                 abspath = os.path.abspath(f"{selected[0]}{os.path.sep}beq_minidsp")
                 if not os.path.exists(abspath):
                     try:
                         os.mkdir(abspath)
                     except:
                         QMessageBox.critical(self, '', f"Unable to create directory - {abspath}", QMessageBox.Ok)
                 if os.path.exists(abspath):
                     self.outputDirectory.setText(abspath)
     self.__enable_process()
예제 #2
0
 def folder_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
     dir_name = None
     dialog.setWindowTitle("Open .edi Directory...")
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     while dir_name is None:
         if dialog.exec_() == QDialog.Accepted:
             dir_name = dialog.selectedFiles()[0]
             dir_name = str(dir_name)
             file_list = [
                 os.path.join(dir_name, edi) for edi in os.listdir(dir_name)
                 if edi.endswith("edi")
             ]
             if not file_list:
                 # empty list
                 QMessageBox.information(
                     self, "NOTE",
                     "Directory does not contain any .edi file, please select again."
                 )
                 dir_name = None  # will read again
             else:
                 self._progress_bar.setMaximumValue(len(file_list))
                 self._progress_bar.onStart()
                 self._add_files(file_list, os.path.basename(dir_name))
                 self._update_tree_view()
                 self._progress_bar.onFinished()
         else:
             break
예제 #3
0
 def showDefaultOutputDirectoryPicker(self):
     dialog = QFileDialog(parent=self)
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     dialog.setWindowTitle(f"Select Extract Audio Output Directory")
     if dialog.exec():
         selected = dialog.selectedFiles()
         if len(selected) > 0:
             self.defaultOutputDirectory.setText(selected[0])
예제 #4
0
 def __select_dir(self):
     dialog = QFileDialog(parent=self)
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     dialog.setWindowTitle('Select Directory')
     if dialog.exec():
         selected = dialog.selectedFiles()
         if len(selected) > 0:
             return selected[0]
     return ''
예제 #5
0
 def selectFile(self):
     self.__reinit_fields()
     dialog = QFileDialog(parent=self)
     dialog.setFileMode(QFileDialog.ExistingFile)
     dialog.setWindowTitle('Select Audio or Video File')
     if dialog.exec():
         selected = dialog.selectedFiles()
         if len(selected) > 0:
             self.inputFile.setText(selected[0])
             self.__probe_file()
예제 #6
0
 def showBeqDirectoryPicker(self):
     ''' selects an output directory for the beq files '''
     dialog = QFileDialog(parent=self)
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     dialog.setWindowTitle(f"Select BEQ Files Download Directory")
     if dialog.exec():
         selected = dialog.selectedFiles()
         if len(selected) > 0:
             self.beqFiltersDir.setText(selected[0])
             self.__count_beq_files()
예제 #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
예제 #8
0
    def open_save_records_dialog(self):
        from functools import partial
        logger, _ = self.current_logger_and_index()
        if not logger:
            return

        d = QFileDialog(self)
        d.selectFile(logger.name + '.log')
        d.setFileMode(QFileDialog.AnyFile)
        d.fileSelected.connect(partial(self.save_records, logger))
        d.setWindowTitle('Save records of "{}" tab to...'.format(logger.name))
        d.open()
예제 #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('')
예제 #10
0
 def setTargetDirectory(self):
     '''
     Sets the target directory based on the user selection.
     '''
     dialog = QFileDialog(parent=self)
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     dialog.setWindowTitle(f"Select Output Directory")
     if dialog.exec():
         selected = dialog.selectedFiles()
         if len(selected) > 0:
             self.targetDir.setText(selected[0])
             if self.__executor is not None:
                 self.__executor.target_dir = selected[0]
                 self.__display_command_info()
예제 #11
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()
예제 #12
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()
예제 #13
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
예제 #14
0
def open_exe_name_dialog(parent, appname):
    options = QFileDialog.Options()
    options |= QDir.AllEntries
    options |= QDir.Hidden

    file_dialog = QFileDialog()
    file_dialog.setFilter(QDir.AllEntries | QDir.Hidden)
    file_dialog.setFileMode(QFileDialog.ExistingFile)
    file_dialog.setWindowTitle(
        f"{appname} could not be found. Please locate in"
        "manually")
    if file_dialog.exec():
        file_name = file_dialog.selectedFiles()
        print(file_name[0])
        return file_name[0]
    else:
        print("No file is selected. guiscrcpy is likely to fail")
예제 #15
0
 def pick_user_source_dir(self):
     '''
     Sets the user source directory.
     '''
     dialog = QFileDialog(parent=self)
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     dialog.setOption(QFileDialog.ShowDirsOnly)
     dialog.setWindowTitle('Choose a directory which holds your own BEQ files')
     if dialog.exec():
         selected = dialog.selectedFiles()
         if len(selected) > 0:
             if os.path.abspath(selected[0]) == os.path.abspath(self.__beq_dir):
                 QMessageBox.critical(self, '',
                                      f"User directory cannot be inside the input directory, choose a different folder",
                                      QMessageBox.Ok)
             else:
                 self.userSourceDir.setText(selected[0])
                 self.update_beq_count()
예제 #16
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
예제 #17
0
    def _export_shape_file(self, *args, **kwargs):
        # show files
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setText("You are about to create shape files.")
        msg.setInformativeText(
            "Please select an output directory after click \"OK\"\n"
            "For the list of .edi files (stations) included in the creation, please click \"Show Details\""
        )
        msg.setWindowTitle("Note")
        msg.setDetailedText("\n".join([
            "{station} ({fn})".format(
                station=station, fn=self._file_handler.station2ref(station))
            for station in self._station_viewer.selected_stations
        ]))
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)

        if msg.exec_() == QMessageBox.Ok:
            dialog = QFileDialog(self)
            dir_name = None
            dialog.setWindowTitle("Selecting Output Directory ...")
            dialog.setFileMode(QFileDialog.DirectoryOnly)
            while dir_name is None:
                if dialog.exec_() == QDialog.Accepted:
                    dir_name = dialog.selectedFiles()[0]
                    dir_name = str(dir_name)
                    if not os.path.isdir(dir_name):
                        QMessageBox.information(
                            self, "NOTE",
                            "Please select a directory to save the created shape files."
                        )
                        dir_name = None  # will read again
                else:
                    break
            if dir_name is not None:
                collect = EdiCollection(mt_objs=[
                    self._file_handler.get_MT_obj(
                        self._file_handler.station2ref(station))
                    for station in self._station_viewer.selected_stations
                ])
                collect.create_mt_station_gdf(dir_name)
                QMessageBox.information(self, "Creation Completed",
                                        "Output written to %s" % dir_name)
                webbrowser.open(dir_name)
예제 #18
0
class LoadMeasurementsDialog(QDialog, Ui_loadMeasurementDialog):
    '''
    Load Measurement dialog
    '''
    def __init__(self, parent=None):
        super(LoadMeasurementsDialog, self).__init__(parent)
        self.setupUi(self)
        self.buttonBox.button(QDialogButtonBox.Open).setText("Select File(s)")
        _translate = QtCore.QCoreApplication.translate
        self.fs.setCurrentText(_translate("loadMeasurementDialog", "48000"))
        self.__dialog = QFileDialog(parent=self)
        self.__errors = 0

    def accept(self):
        '''
        Shows the file select dialog based on the chosen options.
        :return:
        '''
        self.__errors = 0
        self.loadedFiles.clear()
        self.ignoredFiles.clear()
        loadType = self.fileType.currentText()
        fileMode = None
        option = QFileDialog.ShowDirsOnly
        if loadType == 'txt' or loadType == 'dbl':
            fileMode = QFileDialog.DirectoryOnly
        elif loadType == 'wav':
            fileMode = QFileDialog.DirectoryOnly
        elif loadType == 'HolmImpulse':
            fileMode = QFileDialog.ExistingFile
            option = QFileDialog.DontConfirmOverwrite
        elif loadType == 'REW':
            fileMode = QFileDialog.DirectoryOnly
        elif loadType == 'ARTA':
            fileMode = QFileDialog.DirectoryOnly
        else:
            QMessageBox.about(self, "Error", "Unknown format " + loadType)
        if fileMode is not None:
            self.__dialog.setFileMode(fileMode)
            self.__dialog.setOption(option)
            self.__dialog.setWindowTitle("Load Measurements")
            self.__dialog.exec()

    def load(self, measurementModel, dataPathField):
        '''
        Loads the measurements by looking in the selected directory.
        :param measurementModel: the model to load.
        :param dataPathField: the display field.
        :return:
        '''
        selected = self.__dialog.selectedFiles()
        loadType = self.fileType.currentText()
        if len(selected) > 0:
            dataPathField.setText(selected[0])
            if loadType == 'txt':
                measurementModel.load(
                    TxtLoader(self.onFile, selected[0],
                              int(self.fs.currentText())).load())
            elif loadType == 'dbl':
                measurementModel.load(
                    DblLoader(self.onFile, selected[0],
                              int(self.fs.currentText())).load())
            elif loadType == 'wav':
                measurementModel.load(
                    WavLoader(self.onFile, selected[0]).load())
            elif loadType == 'HolmImpulse':
                measurementModel.load(
                    HolmLoader(self.onFile, selected[0]).load())
            elif loadType == 'REW':
                measurementModel.load(
                    REWLoader(self.onFile, selected[0]).load())
            elif loadType == 'ARTA':
                measurementModel.load(
                    ARTALoader(self.onFile, selected[0]).load())
        else:
            measurementModel.clear()
            dataPathField.setText('')
        if self.__errors == 0:
            QDialog.accept(self)

    def onFile(self, filename, loaded):
        if loaded is True:
            self.loadedFiles.appendPlainText(filename)
        else:
            self.ignoredFiles.appendPlainText(filename)
            self.__errors += 1

    def fileTypeChanged(self, text):
        '''
        Hides the fs field if the fs is determined by the source file.
        :param text: the selected text.
        '''
        visible = True
        if text == 'txt' or text == 'dbl':
            pass
        else:
            visible = False
        self.fs.setVisible(visible)
        self.fsLabel.setVisible(visible)
예제 #19
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()
예제 #20
0
 def open_load_records_dialog(self):
     d = QFileDialog(self)
     d.setFileMode(QFileDialog.ExistingFile)
     d.fileSelected.connect(self.load_records)
     d.setWindowTitle('Load records from...')
     d.open()
예제 #21
0
class ExportDialog(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.ui = Ui_Dialog_Export()
        self.ui.setupUi(self)
        self._fig = None

        # setup file types
        for frmt in IMAGE_FORMATS:
            self.ui.comboBox_fileType.addItem("{0[1]} (.{0[0]})".format(frmt))

        self._file_name_changed()  # select the default format

        # setup directory and dir dialog
        self._dir_dialog = QFileDialog(self)
        # self._dir_dialog.setDirectory(os.path.expanduser("~"))
        self._dir_dialog.setFileMode(QFileDialog.DirectoryOnly)
        self._dir_dialog.setWindowTitle("Save to ...")
        self.ui.comboBox_directory.addItem(os.path.expanduser("~"))
        self.ui.pushButton_browse.clicked.connect(self._browse)

        self._dir_validator = DirectoryValidator()
        self.ui.comboBox_directory.lineEdit().setValidator(self._dir_validator)

        # file name
        self.ui.comboBox_fileName.currentIndexChanged.connect(
            self._file_name_changed)

        # file type
        self.ui.comboBox_fileType.currentIndexChanged.connect(
            self._file_type_changed)

        # cancel button
        self.ui.pushButton_cancel.clicked.connect(self._cancel_button_clicked)
        # export button
        self.ui.pushButton_export.clicked.connect(self._export_button_clicked)
        # preview button
        self.ui.pushButton_preview.clicked.connect(
            self._preview_button_clicked)

        # dpi
        self.ui.spinBox_dpi.valueChanged.connect(self._dpi_changed)
        # inches
        self.ui.doubleSpinBox_width_inches.valueChanged.connect(
            self._width_inches_changed)
        self.ui.doubleSpinBox_height_inches.valueChanged.connect(
            self._height_inches_changed)
        # pixels
        self.ui.spinBox_width_pixels.valueChanged.connect(
            self._width_pixels_changed)
        self.ui.spinBox_height_pixels.valueChanged.connect(
            self._height_pixels_changed)

        # message box for when the file already exist
        self._msg_box = QMessageBox(self)
        self._msg_box.setWindowTitle("Export...")
        self._msg_box_button_overwrite = self._msg_box.addButton(
            self.tr("Overwrite"), QMessageBox.AcceptRole)
        self._msg_box_button_save_as = self._msg_box.addButton(
            self.tr("Save As"), QMessageBox.ActionRole)
        self._msg_box_button_cancel = self._msg_box.addButton(
            QMessageBox.Cancel)
        self._msg_box.setDefaultButton(self._msg_box_button_save_as)

    _orientation = ['portrait', 'landscape']

    _no_alpha_channel_formats = ('jpg', 'jpeg')  # ("png", "gif", "psd")

    def _dpi_changed(self, dpi):
        self.ui.doubleSpinBox_height_inches.blockSignals(True)
        self.ui.doubleSpinBox_width_inches.blockSignals(True)
        self.ui.spinBox_height_pixels.setValue(
            self.ui.doubleSpinBox_height_inches.value() * dpi)
        self.ui.spinBox_width_pixels.setValue(
            self.ui.doubleSpinBox_width_inches.value() * dpi)
        self.ui.doubleSpinBox_height_inches.blockSignals(False)
        self.ui.doubleSpinBox_width_inches.blockSignals(False)

    def _width_pixels_changed(self, width):
        self.ui.doubleSpinBox_width_inches.blockSignals(True)
        new_width_inches = width / float(self.ui.spinBox_dpi.value())
        self.ui.doubleSpinBox_width_inches.setValue(new_width_inches)
        self.ui.doubleSpinBox_width_inches.blockSignals(False)

    def _height_pixels_changed(self, height):
        self.ui.doubleSpinBox_height_inches.blockSignals(True)
        new_height_inches = height / float(self.ui.spinBox_dpi.value())
        self.ui.doubleSpinBox_height_inches.setValue(new_height_inches)
        self.ui.doubleSpinBox_height_inches.blockSignals(False)

    def _width_inches_changed(self, width):
        self.ui.spinBox_width_pixels.blockSignals(True)
        self.ui.spinBox_width_pixels.setValue(width *
                                              self.ui.spinBox_dpi.value())
        self.ui.spinBox_width_pixels.blockSignals(False)

    def _height_inches_changed(self, height):
        self.ui.spinBox_height_pixels.blockSignals(True)
        self.ui.spinBox_height_pixels.setValue(height *
                                               self.ui.spinBox_dpi.value())
        self.ui.spinBox_height_pixels.blockSignals(False)

    def _cancel_button_clicked(self, b):
        self.reject()

    def _export_button_clicked(self, b):
        self.accept()

    def _preview_button_clicked(self):
        if self._fig:
            # set figures
            self._fig.set_size_inches(self.get_size_inches_width(),
                                      self.get_size_inches_height())
            params = self.get_savefig_params()
            self._fig.set_dpi(params['dpi'])
            self._fig.set_tight_layout(True if params['bbox_inches'] ==
                                       'tight' else False)

            canvas = FigureCanvas(self._fig)
            canvas.show()

            # dialog
            preview_dialog = PreviewDialog(self, self._fig)
            preview_dialog.exec_()

    def _file_type_changed(self, *args, **kwargs):
        index = self.ui.comboBox_fileType.currentIndex()
        ext, _ = IMAGE_FORMATS[index]
        filename = str(self.ui.comboBox_fileName.currentText())
        filename, _ = os.path.splitext(filename)

        if ext in self._no_alpha_channel_formats:  # enable transparent if the format supports
            self.ui.checkBox_transparent.setEnabled(False)
        else:
            self.ui.checkBox_transparent.setEnabled(True)

        # update file name
        filename = "%s.%s" % (filename, ext)
        index = self.ui.comboBox_fileName.findText(filename)
        if index == -1:
            self.ui.comboBox_fileName.addItem(filename)
        self.ui.comboBox_fileName.setCurrentIndex(
            index if index >= 0 else self.ui.comboBox_fileName.
            findText(filename))

    def _file_name_changed(self, *args, **kwargs):
        filename = str(self.ui.comboBox_fileName.currentText())
        filename, extension = os.path.splitext(filename)
        extension = extension[1:]  # get ride of .
        # check if the extension is supported
        index = [
            i for i, (ext, dsc) in enumerate(IMAGE_FORMATS) if ext == extension
        ]
        if index:
            self.ui.comboBox_fileType.setCurrentIndex(index[0])
        elif extension:
            # no extension
            pass
        else:
            # extension not supported:
            pass

    def _browse(self, *args, **kwargs):
        if self._dir_dialog.exec_() == QDialog.Accepted:
            dirs = self._dir_dialog.selectedFiles(
            )  # behave differently in pyqt4 and pyqt5
            directory = str(dirs[0] if dirs else self._dir_dialog.directory().
                            absolutePath())  # this makes the behave the same
            # update directory
            index = self.ui.comboBox_directory.findText(directory)
            if index == -1:
                self.ui.comboBox_directory.addItem(directory)
            self.ui.comboBox_directory.setCurrentIndex(
                index if index >= 0 else self.ui.comboBox_directory.
                findText(directory))

    def export_to_file(self, fig):
        self._fig = fig
        respawn = True
        while respawn:
            respawn = False
            self.ui.spinBox_dpi.setValue(fig.get_dpi())
            self.ui.doubleSpinBox_width_inches.setValue(fig.get_figwidth())
            self.ui.doubleSpinBox_height_inches.setValue(fig.get_figheight())
            response = self.exec_()
            if response == QDialog.Accepted:
                # saving files
                fname = self.get_save_file_name()

                if os.path.exists(fname):
                    new_name = generate_unique_file_name(fname)
                    self._show_file_exist_message(fname, new_name)
                    if self._msg_box.clickedButton(
                    ) == self._msg_box_button_cancel:
                        respawn = True
                        continue
                    elif self._msg_box.clickedButton(
                    ) == self._msg_box_button_save_as:
                        fname = new_name  # save_as
                    else:
                        pass  # use the original name to overwrite

                params = self.get_savefig_params()
                # change size
                fig.set_size_inches(self.get_size_inches_width(),
                                    self.get_size_inches_height())
                try:
                    fig.savefig(fname, **params)
                except IOError as err:
                    if 'RGBA' in err.message:
                        # if the problem is RGBA as the alpha channel is not supported in the selected format
                        # save to png then save as
                        basename = os.path.basename(fname)
                        tmp_dir = tempfile.gettempdir()
                        filename, ext = os.path.splitext(basename)
                        png_file = filename + ".png"
                        final_format = params['format']
                        params['format'] = 'png'
                        new_fname = os.path.join(tmp_dir, png_file)
                        fig.savefig(new_fname, **params)
                        with Image.open(new_fname) as im:
                            rgb_im = im.convert('RGB')
                            # make sure the fname is ended with the right extension
                            fname, _ = os.path.splitext(fname)
                            fname += "." + final_format
                            rgb_im.save(fname)
                    else:
                        raise err

                if self.ui.checkBox_open_after_export.isChecked():
                    # open with the system default application, this should work on all platforms
                    webbrowser.open(fname)
                return fname
                # elif response == QtGu

    def get_size_inches_width(self):
        return self.ui.doubleSpinBox_width_inches.value()

    def get_size_inches_height(self):
        return self.ui.doubleSpinBox_height_inches.value()

    def _show_file_exist_message(self, fname, new_name):
        self._msg_box.setText(
            "<p>File \"{0}\" already exists. Do you want to overwrite the existing, or save to \"{1}\" instead?<\p>"
            .format(fname, new_name))
        self._msg_box.exec_()

    def get_save_file_name(self):
        name = os.path.join(str(self.ui.comboBox_directory.currentText()),
                            str(self.ui.comboBox_fileName.currentText()))
        return os.path.normpath(name)

    def get_savefig_params(self):
        params = {
            'dpi': self.ui.spinBox_dpi.value(),
            'orientation': self.get_orientation(),
            'format': self.get_file_format()[0],
            'transparent': self.get_transparent(),
            'bbox_inches': self.get_bbox_inches()
        }
        return params

    def get_transparent(self):
        return self.ui.checkBox_transparent.isEnabled(
        ) and self.ui.checkBox_transparent.isChecked()

    def get_file_format(self):
        return IMAGE_FORMATS[self.ui.comboBox_fileType.currentIndex()]

    def get_bbox_inches(self):
        return 'tight' if self.ui.checkBox_tightBbox.isChecked() else None

    def get_orientation(self):
        return self._orientation[self.ui.comboBox_orientation.currentIndex()]

    def keyPressEvent(self, event):
        """Capture and ignore all key press events.

        This is used so that return key event does not trigger any button
        from the dialog. We need to allow the return key to be used in filters
        in the widget."""
        if event.key() == QtCore.Qt.Key_Escape:
            # call reject if Escape is pressed.
            self.reject()
        pass
예제 #22
0
class ExportDialogModEm(QWizard):
    def __init__(self, parent=None):
        QWizard.__init__(self, parent)
        self.ui = Ui_Wizard_esport_modem()
        self.ui.setupUi(self)

        self._logger = MtPyLog.get_mtpy_logger(self.__class__.__name__)
        # setup gui
        # self.setWindowTitle("ModEM input file generator")

        # add math label
        self._math_label_sign_impedances = MathTextLabel(
            self,
            self._math_label_sign_text.format(
                "+" if self.ui.radioButton_impedance_sign_plus.isChecked() else "-"
            )
        )
        self.ui.horizontalLayout_sign_impedance.addWidget(self._math_label_sign_impedances)
        self._math_label_sign_vertical = MathTextLabel(
            self,
            self._math_label_sign_text.format(
                "+" if self.ui.radioButton_vertical_sign_plus.isChecked() else "-"
            )
        )
        self.ui.horizontalLayout_sign_vertical.addWidget(self._math_label_sign_vertical)

        # add math formulae of each error type
        self._math_label_elbert = MathTextLabel(
            self,
            "$E_{egbert}=e_Z\\times |Z_{xy}\\times Z_{yx}|^\\frac{1}{2}$",
        )
        self.ui.verticalLayout_error_types.addWidget(self._math_label_elbert)

        self._math_label_mean = MathTextLabel(self, "$E_{mean} = e_Z\\times mean(|Z_{xy}, Z_{yx}|)$")
        self._math_label_mean.setHidden(True)
        self.ui.verticalLayout_error_types.addWidget(self._math_label_mean)

        self._math_label_eigen = MathTextLabel(self, "$E_{eigen} = e_Z\\times eigenvalues(Z)$")
        self._math_label_eigen.setHidden(True)
        self.ui.verticalLayout_error_types.addWidget(self._math_label_eigen)

        self._math_label_median = MathTextLabel(self, "$E_{median} = e_Z\\times median(|Z_{xx},Z_{xy},Z_{yx},Z_{yy}|)$")
        self._math_label_median.setHidden(True)
        self.ui.verticalLayout_error_types.addWidget(self._math_label_median)

        self._math_label_error_type_note = MathTextLabel(self, "where $E$ is the error and $e$ is the error value (z).")
        self.ui.verticalLayout_error_types.addWidget(self._math_label_error_type_note)

        # add period selection
        self._period_select_ui = FrequencySelection(
            self.ui.wizardPage_period,
            show_period=True,
            show_frequency=False,
            allow_range_select=True,
            select_multiple=True
        )
        self._period_select_ui.ui.checkBox_show_existing.setChecked(True)
        self._period_select_ui.setEnabled(False)
        self._period_select_ui.setHidden(True)
        self.ui.wizardPage_period.layout().addWidget(self._period_select_ui)

        self._period_select_from_file_ui = FrequencySelectionFromFile(self.ui.wizardPage_period)
        self._period_select_from_file_ui.setEnabled(False)
        self._period_select_from_file_ui.setHidden(True)
        self.ui.wizardPage_period.layout().addWidget(self._period_select_from_file_ui)

        # add rotation
        self._rotation_ui = Rotation(self.ui.wizardPage_data)
        self._rotation_ui.setTitle('Data Rotation Angle')
        self.ui.horizontalLayout_data.addWidget(self._rotation_ui)

        self._mesh_rotation_ui = Rotation(self.ui.wizardPage_mesh)
        self._mesh_rotation_ui.setTitle('Mesh Rotation Angle')
        self.ui.gridLayout_mesh.addWidget(self._mesh_rotation_ui)

        # hide error percents
        self._component_error_type_z_changed()

        # hide bottom in vertical mesh as it is not used in mesh gen
        self.ui.doubleSpinBox_bottom.hide()
        self.ui.label_bottom.hide()

        # epsg
        self.ui.comboBox_epsg.addItems(
            [str(epsg) for epsg in sorted(epsg_dict.keys())]
        )

        # set validators
        self._double_validator = QDoubleValidator(-np.inf, np.inf, 1000)
        self._double_validator.setNotation(QDoubleValidator.ScientificNotation)
        self.ui.lineEdit_resistivity_init.setValidator(self._double_validator)
        self.ui.lineEdit_resistivity_air.setValidator(self._double_validator)
        self.ui.lineEdit_resistivity_sea.setValidator(self._double_validator)

        self._file_validator = FileValidator()
        self.ui.comboBox_topography_file.lineEdit().setValidator(self._file_validator)
        self.ui.comboBox_topography_file.lineEdit().setMaxLength(256)

        self._dir_validator = DirectoryValidator()
        self.ui.comboBox_directory.lineEdit().setValidator(self._dir_validator)

        # setup directory and dir dialog
        self._dir_dialog = QFileDialog(self)
        # self._dir_dialog.setDirectory(os.path.expanduser("~"))
        self.ui.comboBox_directory.addItem(os.path.expanduser("~"))
        self._update_full_output()

        # set maximum
        # self.ui.spinBox_cell_num_ew.setMaximum(0xFFFFFFFF)
        # self.ui.spinBox_cell_num_ns.setMaximum(0xFFFFFFFF)

        # tooltip for error types
        for index, tooltip in enumerate(self._error_type_z_tool_tip):
            self.ui.comboBox_error_type_z.setItemData(index, tooltip, QtCore.Qt.ToolTipRole)
            self.ui.comboBox_error_type_zxx.setItemData(index, tooltip, QtCore.Qt.ToolTipRole)
            self.ui.comboBox_error_type_zxy.setItemData(index, tooltip, QtCore.Qt.ToolTipRole)
            self.ui.comboBox_error_type_zyx.setItemData(index, tooltip, QtCore.Qt.ToolTipRole)
            self.ui.comboBox_error_type_zyy.setItemData(index, tooltip, QtCore.Qt.ToolTipRole)

        # hide parts
        self.ui.groupBox_component_error_types.setHidden(True)
        self.ui.label_component_error_types.setHidden(True)

        # connect signals
        self.ui.radioButton_impedance_sign_plus.toggled.connect(
            lambda is_checked: self._math_label_sign_impedances.set_math_text(
                self._math_label_sign_text.format("+" if is_checked else "-"))
        )
        self.ui.radioButton_vertical_sign_plus.toggled.connect(
            lambda is_checked: self._math_label_sign_vertical.set_math_text(
                self._math_label_sign_text.format("+" if is_checked else "-")
            )
        )

        self.ui.radioButton_impedance_full.toggled.connect(self._impedance_full_toggled)
        self.ui.radioButton_impedance_off_diagonal.toggled.connect(self._impedance_off_diagonal_toggled)
        self.ui.radioButton_impedance_none.toggled.connect(self._impedance_none_toggled)
        self.ui.radioButton_vertical_full.toggled.connect(self._vertical_full_toggled)

        self.ui.comboBox_error_type_z.currentIndexChanged.connect(self._error_type_z_changed)
        for component in self._impedance_components:
            combobox = getattr(self.ui, 'comboBox_error_type_{}'.format(component))
            checkbox = getattr(self.ui, 'checkBox_{}'.format(component))
            combobox.currentIndexChanged.connect(self._component_error_type_z_changed)
            checkbox.toggled.connect(self._error_component_checkbox_toggled(combobox))

        self.ui.comboBox_output_name.currentIndexChanged.connect(self._update_full_output)
        self.ui.comboBox_output_name.lineEdit().editingFinished.connect(self._output_name_changed)
        self.ui.comboBox_output_name.editTextChanged.connect(self._update_full_output)
        self.ui.comboBox_directory.currentIndexChanged.connect(self._update_full_output)
        self.ui.comboBox_directory.lineEdit().editingFinished.connect(self._output_dir_changed)
        self.ui.comboBox_directory.editTextChanged.connect(self._update_full_output)
        self.ui.pushButton_browse.clicked.connect(self._browse)

        # self.ui.doubleSpinBox_target_depth.valueChanged.connect(self._target_depth_changed)
        self.ui.doubleSpinBox_target_depth.lineEdit().editingFinished.connect(self._target_depth_changed)
        self.ui.doubleSpinBox_bottom.lineEdit().editingFinished.connect(self._bottom_changed)
        # self.ui.doubleSpinBox_bottom.valueChanged.connect(self._bottom_changed)

        # self.ui.comboBox_topography_file.currentIndexChanged.connect()
        self.ui.comboBox_topography_file.lineEdit().editingFinished.connect(
            self._topography_file_changed
        )
        self.ui.pushButton_browse_topography_file.clicked.connect(self._browse_topography_file)

        self.ui.pushButton_test.clicked.connect(self._test_button_clicked)

        self.ui.checkBox_cell_num_ew.stateChanged.connect(
            lambda p_int: self.ui.spinBox_cell_num_ew.setEnabled(p_int != 0)
        )
        self.ui.checkBox_cell_num_ns.stateChanged.connect(
            lambda p_int: self.ui.spinBox_cell_num_ns.setEnabled(p_int != 0)
        )

        self.ui.checkBox_component_error_types.setEnabled(False)  # disabled due to missing implementations todo implement this option in modem.Data
        self.ui.checkBox_component_error_types.toggled.connect(self._component_error_type_toggled)
        self.ui.radioButton_select_period_percent.toggled.connect(
            lambda checked: self.ui.doubleSpinBox_select_period_percent.setEnabled(checked))
        self.ui.radioButton_select_period.toggled.connect(
            lambda checked: self._period_select_ui.setEnabled(checked))
        self.ui.radioButton_select_period.toggled.connect(
            lambda checked: self._period_select_ui.setHidden(not checked))
        self.ui.radioButton_select_by_file.toggled.connect(
            lambda checked: self._period_select_from_file_ui.setEnabled(checked))
        self.ui.radioButton_select_by_file.toggled.connect(
            lambda checked: self._period_select_from_file_ui.setHidden(not checked))

        # register fields
        self.ui.wizardPage_output.registerField('output_path*', self.ui.lineEdit_full_output)
        self.ui.wizardPage_topography.registerField('topography_file*', self.ui.comboBox_topography_file)
        self.ui.wizardPage_topography.registerField('sea_resistivity',
                                                    self.ui.lineEdit_resistivity_sea)
        self.ui.wizardPage_topography.registerField('air_resistivity',
                                                    self.ui.lineEdit_resistivity_air)

        # attribute
        self._mt_objs = None

        self._progress_bar = ProgressBar()

    _impedance_components = ['zxx', 'zxy', 'zyx', 'zyy']
    _vertical_components = ['tx', 'ty']
    _math_label_sign_text = "$exp({}i\\omega t)$"

    _error_type_z = [
        'egbert',  # 0
        'mean_od',  # 1
        'eigen',  # 2
        'median',  # 3
    ]

    _error_type_z_tool_tip = [
        'sets error to the value of Error Egbert * sqrt(abs(zxy*zyx), see Egbert & Kelbert',
        'sets error to error_value_z * mean([Zxy, Zyx]) (non zeros)',
        'sets error to error_value_z * eigenvalues(Z[ii])',
        'sets error to error_value_z * median([Zxx, Zxy, Zyx, Zyy]) (non zeros)'
    ]

    _error_type_tipper = [
        'abs',
        'floor'
    ]

    def _component_error_type_toggled(self, is_checked):
        self.ui.label_component_error_types.setHidden(not is_checked)
        self.ui.groupBox_component_error_types.setHidden(not is_checked)

    def _target_depth_changed(self, *args):
        value = self.ui.doubleSpinBox_target_depth.value()
        # target depth has too be at least as deep as the bottom
        if self.ui.doubleSpinBox_bottom.value() < value:
            self.ui.doubleSpinBox_bottom.setValue(value)

    def _bottom_changed(self, *args):
        value = self.ui.doubleSpinBox_bottom.value()
        # bottom as to be at least at least as deep as the target depth
        if self.ui.doubleSpinBox_target_depth.value() > value:
            self.ui.doubleSpinBox_target_depth.setValue(value)

    def _impedance_full_toggled(self, checked):
        if checked:
            for comp in self._impedance_components:
                checkbox = getattr(self.ui, 'checkBox_{}'.format(comp))
                checkbox.setEnabled(True)
            self.ui.radioButton_vertical_none.setEnabled(True)
            self.ui.groupBox_sign_impedance.setEnabled(True)

    def _impedance_off_diagonal_toggled(self, checked):
        if checked:
            for comp in self._impedance_components:
                checkbox = getattr(self.ui, 'checkBox_{}'.format(comp))
                checkbox.setEnabled(comp == 'zxy' or comp == 'zyx')
            self.ui.radioButton_vertical_none.setEnabled(True)
            self.ui.groupBox_sign_impedance.setEnabled(True)

    def _impedance_none_toggled(self, checked):
        if checked:
            for comp in self._impedance_components:
                checkbox = getattr(self.ui, 'checkBox_{}'.format(comp))
                checkbox.setEnabled(False)
            self.ui.radioButton_vertical_none.setEnabled(False)
            self.ui.groupBox_sign_impedance.setEnabled(False)

    def _vertical_full_toggled(self, checked):
        self.ui.doubleSpinBox_error_value_tipper.setEnabled(checked)
        self.ui.radioButton_impedance_none.setEnabled(checked)
        self.ui.groupBox_sign_vertical.setEnabled(checked)

    def _error_component_checkbox_toggled(self, combobox):
        def _checkbox_toggled(checked):
            combobox.setEnabled(checked)
            self._component_error_type_z_changed()

        return _checkbox_toggled

    def _component_error_type_z_changed(self, error_type_index=-1):
        # types = {self.ui.comboBox_error_type_z.currentIndex()}
        # for component in self._impedance_components:
        #     combobox = getattr(self.ui, 'comboBox_error_type_{}'.format(component))
        #     if combobox.isEnabled():
        #         types.add(combobox.currentIndex())
        #
        # hidden = bool(0 not in types)
        # self.ui.label_error_floor.setHidden(hidden)
        # self.ui.doubleSpinBox_error_floor.setHidden(hidden)
        # hidden = bool(2 not in types and 3 not in types)
        # self.ui.label_error_egbert.setHidden(hidden)
        # self.ui.doubleSpinBox_error_egbert.setHidden(hidden)
        # self._math_label_elbert.setHidden(hidden)
        # hidden = bool(1 not in types)
        # self.ui.label_error_value.setHidden(hidden)
        # self.ui.doubleSpinBox_error_value.setHidden(hidden)
        pass  # todo re-enable after the sub-type for each component is implemented

    def _error_type_z_changed(self, error_type_index):
        # sync the component error types with default
        # for component in self._impedance_components:
        #     combobox = getattr(self.ui, 'comboBox_error_type_{}'.format(component))
        #     if not combobox.isEnabled():
        #         combobox.blockSignals(True)
        #         combobox.setCurrentIndex(error_type_index)
        #         combobox.blockSignals(False)
        # self._component_error_type_z_changed()
        pass  # todo re-enable after the sub-type for each component is implemented

        self._math_label_elbert.setHidden(error_type_index != 0)
        self._math_label_mean.setHidden(error_type_index != 1)
        self._math_label_eigen.setHidden(error_type_index != 2)
        self._math_label_median.setHidden(error_type_index != 3)

    def _output_name_changed(self, *args, **kwargs):
        output_name = str(self.ui.comboBox_output_name.currentText())
        index = self.ui.comboBox_output_name.findText(output_name)
        if index == -1:
            self.ui.comboBox_output_name.addItem(output_name)
        self.ui.comboBox_output_name.setCurrentIndex(
            index if index >= 0 else self.ui.comboBox_output_name.findText(output_name)
        )

    def _output_dir_changed(self, *args, **kwargs):
        directory = str(self.ui.comboBox_directory.currentText())
        directory = os.path.normpath(directory)
        # update directory
        index = self.ui.comboBox_directory.findText(directory)
        if index == -1:
            self.ui.comboBox_directory.addItem(directory)
        self.ui.comboBox_directory.setCurrentIndex(index
                                                   if index >= 0
                                                   else self.ui.comboBox_directory.findText(directory))

    def _update_full_output(self, *args, **kwargs):
        directory = str(self.ui.comboBox_directory.currentText())
        output_name = str(self.ui.comboBox_output_name.currentText())
        full_output = os.path.normpath(os.path.join(directory, output_name))
        self.ui.lineEdit_full_output.setText(full_output)

    def _browse(self, *args, **kwargs):
        self._dir_dialog.setFileMode(QFileDialog.DirectoryOnly)
        self._dir_dialog.setWindowTitle("Save to ...")
        if self._dir_dialog.exec_() == QDialog.Accepted:
            directory = str(self._dir_dialog.selectedFiles()[0])
            self.ui.comboBox_directory.setEditText(directory)
            self._output_dir_changed()

    def _topography_file_changed(self, *args, **kwargs):
        topo_file = str(self.ui.comboBox_topography_file.currentText())
        topo_file = os.path.normpath(topo_file)
        # update
        index = self.ui.comboBox_topography_file.findText(topo_file)
        if index == -1:
            self.ui.comboBox_topography_file.addItem(topo_file)
        self.ui.comboBox_topography_file.setCurrentIndex(
            index if index >= 0 else self.ui.comboBox_topography_file.findText(topo_file)
        )

    def _browse_topography_file(self, *args, **kwargs):
        self._dir_dialog.setFileMode(QFileDialog.AnyFile)
        self._dir_dialog.setWindowTitle("Find Topography File...")
        if self._dir_dialog.exec_() == QDialog.Accepted:
            file_path = str(self._dir_dialog.selectedFiles()[0])
            self.ui.comboBox_topography_file.setEditText(file_path)
            self._topography_file_changed()

    def _test_button_clicked(self, *args, **kwargs):
        self.export_data(True)

    def set_data(self, mt_objs):
        self._mt_objs = mt_objs
        self._period_select_ui.set_data(self._mt_objs)
        self._period_select_from_file_ui.set_data(self._mt_objs)
        self.ui.listWidget_edi_files.clear()
        for mt_obj in mt_objs:
            self.ui.listWidget_edi_files.addItem("{mt.station} ({mt.fn})".format(mt=mt_obj))

    def get_inversion_mode(self):
        if self.ui.radioButton_impedance_full.isChecked():
            return '1' if self.ui.radioButton_vertical_full.isChecked() else '2'
        elif self.ui.radioButton_impedance_off_diagonal.isChecked():
            return '3' if self.ui.radioButton_vertical_full.isChecked() else '4'
        else:
            return '5'

    def _get_error_type_z(self):
        type = self._error_type_z[self.ui.comboBox_error_type_z.currentIndex()]
        if self.ui.checkBox_error_type_z_floor.isChecked():
            type += "_floor"
        return type

    def get_data_kwargs(self):
        kwargs = {
            'error_type_z': self._get_error_type_z(),
            'error_value_z': self.ui.doubleSpinBox_error_value_z.value(),
            'error_type_tipper': self._error_type_tipper[self.ui.comboBox_error_type_tipper.currentIndex()],
            'error_value_tipper': self.ui.doubleSpinBox_error_value_tipper.value() / 100.,
            'save_path': self.get_save_file_path(),
            'format': '1' if self.ui.radioButton_format_1.isChecked() else '2',
            'rotation_angle': self._rotation_ui.get_rotation_in_degree(),
            'model_epsg': self.get_epsg(),
            'inv_mode': self.get_inversion_mode()
        }

        # comp_error_type
        if any(
                [getattr(self.ui, 'comboBox_error_type_{}'.format(component)).isEnabled()
                 for component in self._impedance_components]
        ):
            kwargs['comp_error_type'] = dict(
                [
                    (
                        component,
                        self._error_type_z[
                            getattr(self.ui, 'comboBox_error_type_{}'.format(component)).currentIndex()
                        ]
                    )
                    for component in self._impedance_components
                    if getattr(self.ui, 'comboBox_error_type_{}'.format(component)).isEnabled()
                ]
            )

        # wave signs
        if self.ui.groupBox_sign_impedance.isEnabled():
            kwargs['wave_sign_impedance'] = '+' if self.ui.radioButton_impedance_sign_plus.isChecked() \
                else '-'
        if self.ui.groupBox_sign_vertical.isEnabled():
            kwargs['wave_sign_tipper'] = '+' if self.ui.radioButton_vertical_sign_plus.isChecked() \
                else '-'

        # units
        kwargs['units'] = '[mV/km]/[nT]' if self.ui.radioButton_unit_mvkmnt.isChecked() \
            else '[V/m]/[T]' if self.ui.radioButton_unit_vmt.isChecked() \
            else 'Ohm'

        return kwargs

    def get_model_kwargs(self):
        kwargs = {
            'save_path': self.get_save_file_path(),
            'cell_size_east': self.ui.doubleSpinBox_cell_size_east.value(),
            'cell_size_north': self.ui.doubleSpinBox_cell_szie_north.value(),
            'pad_east': self.ui.spinBox_pad_east.value(),
            'pad_north': self.ui.spinBox_pad_north.value(),
            'pad_z': self.ui.spinBox_pad_z.value(),
            'pad_stretch_h': self.ui.doubleSpinBox_pad_stretch_h.value(),
            'pad_stretch_v': self.ui.doubleSpinBox_pad_stretch_v.value(),
            'z1_layer': self.ui.doubleSpinBox_z1_thickness.value(),
            'z_target_depth': self.ui.doubleSpinBox_target_depth.value(),
            'z_bottom': self.ui.doubleSpinBox_bottom.value(),
            'n_layers': self.ui.spinBox_num_layers.value(),
            'n_air_layers': self.ui.spinBox_num_air_layers.value(),
            'res_model': self.get_initial_resistivity(),
            'mesh_rotation_angle': self._mesh_rotation_ui.get_rotation_in_degree(),
            'cell_number_ew': self.ui.spinBox_cell_num_ew.value()
            if self.ui.checkBox_cell_num_ew.isChecked() else None,
            'cell_number_ns': self.ui.spinBox_cell_num_ns.value()
            if self.ui.checkBox_cell_num_ns.isChecked() else None
        }
        return kwargs

    def get_save_file_path(self):
        return str(self.ui.lineEdit_full_output.text())

    def get_initial_resistivity(self):
        return float(str(self.ui.lineEdit_resistivity_init.text()))

    def get_air_resistivity(self):
        return float(str(self.ui.lineEdit_resistivity_air.text()))

    def get_sea_resistivity(self):
        return float(str(self.ui.lineEdit_resistivity_sea.text()))

    def get_topography_2_mesh_args(self):
        kwargs = {
            'topography_file': str(self.ui.comboBox_topography_file.currentText()),
            'topography_array': None,
            'interp_method': 'nearest' if self.ui.radioButton_interpo_method_nearest.isChecked()
            else 'linear' if self.ui.radioButton_interpo_method_linear.isChecked()
            else 'cubic',
            'air_resistivity': self.get_air_resistivity(),
            'sea_resistivity': self.get_sea_resistivity()
        }

        return kwargs

    def get_covariance_kwargs(self):
        kwargs = {
            'smoothing_east': self.ui.doubleSpinBox_smoothing_east.value(),
            'smoothing_north': self.ui.doubleSpinBox_smoothing_north.value(),
            'smoothing_z': self.ui.doubleSpinBox_smoothing_z.value(),
            'smoothing_num': self.ui.spinBox_smoothing_number.value(),
            'save_path': self.get_save_file_path()
        }
        return kwargs

    def get_select_period_kwargs(self):
        period_list = None
        if self.ui.radioButton_select_period.isChecked():
            period_list = self._period_select_ui.get_frequencies()
        elif self.ui.radioButton_select_by_file.isChecked():
            period_list = self._period_select_from_file_ui.get_selected_periods()
        elif self.ui.radioButton_select_by_decade.isChecked():
            period_list = []
            unique_periods = set()
            for mt in self._mt_objs:
                unique_periods.update(1. /mt.Z.freq)
            unique_periods = np.array(sorted(list(unique_periods), reverse=True))
            num = self.ui.spinBox_num_per_decade.value()
            decade_min = 10. ** int(np.log(unique_periods[0]))
            decade_max = decade_min * 10.
            while unique_periods[-1] < decade_min:  # todo this block of code can be more efficient
                indexes = np.where((unique_periods <= decade_max) & (unique_periods > decade_min))[0]
                if len(indexes) < num:
                    self._logger.warn("Selecting {} periods per decade, but there is only {} in [{},{}]".format(num, len(indexes), decade_min, decade_max))
                else:
                    # sample n
                    np.random.shuffle(indexes)
                    indexes = indexes[0:num]
                period_list += list(unique_periods[indexes])
                decade_max = decade_min
                decade_min /= 10.

        kwargs = {
            'period_list': period_list,
            'percentage': self.ui.doubleSpinBox_select_period_percent.value()
        }
        return kwargs

    def get_epsg(self):
        return int(str(self.ui.comboBox_epsg.currentText()))

    def export_data(self, test=False):
        if self._mt_objs is None:
            return
        # self._progress_bar.progressbar.setRange(0, 1)
        self._progress_bar.progressbar.setRange(0, 0)
        self._progress_bar.onStart()

        self._update_full_output()

        worker = ModEMWorker(
            self,
            show=test,
            edi_list=[mt_obj.fn for mt_obj in self._mt_objs],
            select_period_kwargs=self.get_select_period_kwargs(),
            data_kwargs=self.get_data_kwargs(),
            mesh_kwargs=self.get_model_kwargs(),
            topo_args=self.get_topography_2_mesh_args(),
            covariance_kwargs=self.get_covariance_kwargs()
        )

        if test:
            worker.figure_updated.connect(self._show_figure)
        self._progress_bar.setWindowTitle(
            'Testing ModEM Data...' if test else 'Generating ModEM Data...'
        )
        # worker.started.connect(self._progress_bar.onStart)
        # worker.finished.connect(self._progress_bar.onFinished)
        worker.status_updated.connect(self._update_progress_bar_text)
        worker.export_error.connect(self._export_error)
        # self._plot_opened.connect(worker.pause)

        # worker.start()
        # worker.wait()
        worker.run()

        # clean up
        worker.deleteLater()
        if self.ui.checkBox_open_output_dir.isChecked():
            webbrowser.open(self.get_save_file_path())

        self._progress_bar.onFinished()
        self._progress_bar.progressbar.setRange(0, 1)

    # _plot_opened = pyqtSignal(bool)

    def _update_progress_bar_text(self, text):
        self._progress_bar.incrementValue()
        self._progress_bar.updateIndicatorText(text)

    def _show_figure(self, string, fig):
        # self._plot_opened.emit(True)
        # print "plot_opened"
        preview_dialog = PreviewDialog(None, fig)
        preview_dialog.setWindowTitle(string)
        preview_dialog.exec_()
        preview_dialog.deleteLater()
        # self._plot_opened.emit(False)

    def _export_error(self, message):
        QMessageBox.critical(self,
                             'Export Error', message,
                             QMessageBox.Close)