コード例 #1
0
class AudioPedantik(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.title = "AudioPedantik"
        self.top = 100
        self.left = 100
        self.width = 700
        self.height = 500

        self.config = configparser.ConfigParser()
        self.config.read("config.ini")

        self.search_dir = ""
        self.dest_dir = ""

        self.audio_id3 = None
        self.results = []
        self.artwork_bytes = None

        self.init_ui()

    def init_ui(self):
        # Main widgets declaration
        search_dir_button = QPushButton("Select search directory")

        dest_dir_button = QPushButton("Select destination directory")

        self.search_dir_edit = QLineEdit()
        self.search_dir_edit.setText(self.config["Last"]["search_dir"])
        self.search_dir = self.config["Last"]["search_dir"]
        self.search_dir_edit.setReadOnly(True)

        self.dest_dir_edit = QLineEdit()
        self.dest_dir_edit.setText(self.config["Last"]["dest_dir"])
        self.dest_dir = self.config["Last"]["dest_dir"]
        self.dest_dir_edit.setReadOnly(True)

        self.listbox = QListWidget()
        self.listbox.setMaximumWidth(250)

        self.combobox = QComboBox()

        # ID3 group widgets declarations
        file_name_label = QLabel("File name: ")

        self.artwork_id3_label = QLabel()
        self.artwork_id3_label.setPixmap(QPixmap("default.jpg"))

        artist_id3_label = QLabel("Artist: ")
        title_id3_label = QLabel("Title: ")
        genre_id3_label = QLabel("Genre: ")
        album_id3_label = QLabel("Album: ")
        release_year_id3_label = QLabel("Release year: ")
        track_no_id3_label = QLabel("Track number: ")

        self.file_name_edit = QLineEdit()

        self.artist_id3_edit = QLineEdit()
        self.artist_id3_edit.setReadOnly(True)

        self.title_id3_edit = QLineEdit()
        self.title_id3_edit.setReadOnly(True)

        self.genre_id3_edit = QLineEdit()
        self.genre_id3_edit.setReadOnly(True)

        self.album_id3_edit = QLineEdit()
        self.album_id3_edit.setReadOnly(True)

        self.release_year_id3_edit = QLineEdit()
        self.release_year_id3_edit.setReadOnly(True)

        self.track_no_id3_edit = QLineEdit()
        self.track_no_id3_edit.setReadOnly(True)

        # iTunes group widgets declarations
        search_phrase_label = QLabel("Search phrase: ")

        self.checkbox = QCheckBox("Don't overwrite artwork")

        self.artwork_itunes_label = QLabel()
        self.artwork_itunes_label.setPixmap(QPixmap("default.jpg"))

        artist_itunes_label = QLabel("Artist: ")
        title_itunes_label = QLabel("Title: ")
        genre_itunes_label = QLabel("Genre: ")
        genre_itunes_label = QLabel("Genre: ")
        album_itunes_label = QLabel("Album: ")
        release_year_itunes_label = QLabel("Release year: ")
        track_no_itunes_label = QLabel("Track number: ")

        self.search_phrase_edit = QLineEdit()
        self.artist_itunes_edit = QLineEdit()
        self.title_itunes_edit = QLineEdit()
        self.genre_itunes_edit = QLineEdit()
        self.album_itunes_edit = QLineEdit()
        self.release_year_itunes_edit = QLineEdit()
        self.track_no_itunes_edit = QLineEdit()

        load_artwork_button = QPushButton("Load custom artwork")

        self.save_button = QPushButton("Save")
        self.save_button.setMaximumWidth(50)
        self.save_button.setDisabled(True)

        # Layout
        main_layout = QGridLayout()

        main_layout.addWidget(search_dir_button, 0, 0)
        main_layout.addWidget(dest_dir_button, 1, 0)
        main_layout.addWidget(self.search_dir_edit, 0, 1)
        main_layout.addWidget(self.dest_dir_edit, 1, 1)
        main_layout.addWidget(self.listbox, 2, 0, 3, 1)
        main_layout.addWidget(self.combobox, 2, 1)

        # Group box for mp3 data
        id3_data_group_box = QGroupBox("MP3 data: ")
        id3_data_group_layout = QGridLayout()

        id3_data_group_layout.addWidget(file_name_label, 0, 0)

        id3_data_group_layout.addWidget(self.artwork_id3_label, 1, 0, 5, 1)

        id3_data_group_layout.addWidget(artist_id3_label, 1, 2)
        id3_data_group_layout.addWidget(title_id3_label, 2, 2)
        id3_data_group_layout.addWidget(genre_id3_label, 3, 2)
        id3_data_group_layout.addWidget(album_id3_label, 4, 2)
        id3_data_group_layout.addWidget(release_year_id3_label, 5, 2)
        id3_data_group_layout.addWidget(track_no_id3_label, 6, 2)

        id3_data_group_layout.addWidget(self.file_name_edit, 0, 2, 1, 2)

        id3_data_group_layout.addWidget(self.artist_id3_edit, 1, 3)
        id3_data_group_layout.addWidget(self.title_id3_edit, 2, 3)
        id3_data_group_layout.addWidget(self.genre_id3_edit, 3, 3)
        id3_data_group_layout.addWidget(self.album_id3_edit, 4, 3)
        id3_data_group_layout.addWidget(self.release_year_id3_edit, 5, 3)
        id3_data_group_layout.addWidget(self.track_no_id3_edit, 6, 3)

        id3_data_group_box.setLayout(id3_data_group_layout)

        main_layout.addWidget(id3_data_group_box, 3, 1)

        # Group box for iTunes data
        itunes_data_group_box = QGroupBox("iTunes data: ")
        itunes_data_group_layout = QGridLayout()

        itunes_data_group_layout.addWidget(search_phrase_label, 0, 0)

        itunes_data_group_layout.addWidget(self.artwork_itunes_label, 1, 0, 5,
                                           1)
        itunes_data_group_layout.addWidget(self.checkbox, 6, 0)

        itunes_data_group_layout.addWidget(artist_itunes_label, 1, 2)
        itunes_data_group_layout.addWidget(title_itunes_label, 2, 2)
        itunes_data_group_layout.addWidget(genre_itunes_label, 3, 2)
        itunes_data_group_layout.addWidget(album_itunes_label, 4, 2)
        itunes_data_group_layout.addWidget(release_year_itunes_label, 5, 2)
        itunes_data_group_layout.addWidget(track_no_itunes_label, 6, 2)
        itunes_data_group_layout.addWidget(track_no_itunes_label, 6, 2)

        itunes_data_group_layout.addWidget(self.search_phrase_edit, 0, 2, 1, 2)

        itunes_data_group_layout.addWidget(self.artist_itunes_edit, 1, 3)
        itunes_data_group_layout.addWidget(self.title_itunes_edit, 2, 3)
        itunes_data_group_layout.addWidget(self.genre_itunes_edit, 3, 3)
        itunes_data_group_layout.addWidget(self.album_itunes_edit, 4, 3)
        itunes_data_group_layout.addWidget(self.release_year_itunes_edit, 5, 3)
        itunes_data_group_layout.addWidget(self.track_no_itunes_edit, 6, 3)

        itunes_data_group_layout.addWidget(load_artwork_button, 7, 0)
        itunes_data_group_layout.addWidget(self.save_button, 7, 3)

        itunes_data_group_box.setLayout(itunes_data_group_layout)

        main_layout.addWidget(itunes_data_group_box, 4, 1)

        self.setLayout(main_layout)

        # Bindings
        dest_dir_button.pressed.connect(self.choose_dest_dir)
        search_dir_button.pressed.connect(self.choose_search_dir)
        load_artwork_button.pressed.connect(self.load_custom_artwork)
        self.save_button.pressed.connect(self.save)
        self.search_phrase_edit.editingFinished.connect(self.search_itunes)
        self.combobox.currentIndexChanged.connect(self.combobox_selected)

        # Window settings
        self.setGeometry(self.top, self.left, self.width, self.height)
        self.setWindowTitle(self.title)
        self.show()

        self.refresh_listbox()
        self.save_button.setDisabled(False)

    def refresh_listbox(self):

        self.listbox.disconnect()
        self.listbox.clear()
        try:
            self.listbox.addItems([
                file for file in os.listdir(self.search_dir)
                if file.endswith(".mp3")
            ])
        except FileNotFoundError:
            # FIXME:
            pass

        self.listbox.currentItemChanged.connect(self.listbox_selected)

    def choose_search_dir(self):

        self.search_dir = QFileDialog.getExistingDirectory(
            self, "Select Search Directory")
        self.search_dir_edit.setText(self.search_dir)
        self.config["Last"]["search_dir"] = self.search_dir

        with open("config.ini", "w") as configfile:
            self.config.write(configfile)

        self.refresh_listbox()

    def choose_dest_dir(self):

        self.dest_dir = QFileDialog.getExistingDirectory(
            self, "Select destination directory")
        self.dest_dir_edit.setText(self.dest_dir)
        self.config["Last"]["dest_dir"] = self.dest_dir

        with open("config.ini", "w") as configfile:
            self.config.write(configfile)

        self.save_button.setDisabled(False)

    def listbox_selected(self):

        file_name = self.listbox.currentItem().text()

        self.search_phrase_edit.setText(file_name[:-4])
        self.file_name_edit.setText(file_name)

        self.get_id3_tags(file_name)
        self.search_itunes()

    def get_id3_tags(self, file_name):
        try:
            self.audio_id3 = ID3(self.search_dir + "/" + file_name,
                                 v2_version=3)
        except _util.ID3NoHeaderError:
            file = File(self.search_dir + "/" + file_name)
            file.add_tags()
            file.save()

            self.audio_id3 = ID3(self.search_dir + "/" + file_name,
                                 v2_version=3)

        for tag in self.audio_id3:
            if tag.startswith("APIC") and (PictureType.COVER_FRONT == 3):
                image = QImage()
                image.loadFromData(self.audio_id3[tag].data)
                self.artwork_id3_label.setPixmap(
                    QPixmap(image).scaled(150, 150))
                break

        else:
            #TODO: default image should be stored as raw data
            self.artwork_id3_label.setPixmap(QPixmap("default.jpg"))

        if "TPE1" in self.audio_id3:
            self.artist_id3_edit.setText(str(self.audio_id3["TPE1"]))
        else:
            self.artist_id3_edit.clear()

        if "TIT2" in self.audio_id3:
            self.title_id3_edit.setText(str(self.audio_id3["TIT2"]))
        else:
            self.title_id3_edit.clear()

        if "TCON" in self.audio_id3:
            self.genre_id3_edit.setText(str(self.audio_id3["TCON"]))
        else:
            self.genre_id3_edit.clear()

        if "TALB" in self.audio_id3:
            self.album_id3_edit.setText(str(self.audio_id3["TALB"]))
        else:
            self.album_id3_edit.clear()

        if "TYER" in self.audio_id3:
            self.release_year_id3_edit.setText(str(self.audio_id3["TYER"]))
        else:
            self.release_year_id3_edit.clear()

        if "TRCK" in self.audio_id3:
            self.track_no_id3_edit.setText(str(self.audio_id3["TRCK"]))
        else:
            self.track_no_id3_edit.clear()

    def search_itunes(self):

        params = {
            "term": self.search_phrase_edit.text(),
            "media": "music",
            "limit": 10
        }

        json_data = json.loads(
            urlopen("https://itunes.apple.com/search?" +
                    urlencode(params)).read().decode('utf8'))

        self.combobox.clear()
        self.combobox.disconnect()

        self.results = []

        for result in json_data["results"]:
            # Prepare icon
            image_data = urlopen(result["artworkUrl30"]).read()
            image = QPixmap()
            image.loadFromData(image_data)

            self.combobox.addItem(
                QIcon(image),
                result["artistName"] + " - " + result["trackName"])
            self.results.append(result)

        self.combobox.currentIndexChanged.connect(self.combobox_selected)

        if len(self.results) > 0:
            self.combobox.setCurrentIndex(0)
            self.combobox_selected()
        else:
            self.artwork_itunes_label.setPixmap(QPixmap("default.jpg"))
            self.artwork_bytes = None

            self.artist_itunes_edit.clear()
            self.title_itunes_edit.clear()
            self.genre_itunes_edit.clear()
            self.album_itunes_edit.clear()
            self.release_year_itunes_edit.clear()

    def combobox_selected(self):

        current_combobox_index = self.combobox.currentIndex()

        artwork_url = self.results[current_combobox_index][
            "artworkUrl30"].split("/")
        artwork_url[-1] = "600x600bb.jpg"
        artwork_url = "/".join(artwork_url)

        self.artwork_bytes = urlopen(artwork_url).read()
        artwork = QPixmap()
        artwork.loadFromData(self.artwork_bytes)

        self.artwork_itunes_label.setPixmap(artwork.scaled(150, 150))

        self.artist_itunes_edit.setText(
            self.results[current_combobox_index]["artistName"])
        self.title_itunes_edit.setText(
            self.results[current_combobox_index]["trackName"])
        self.genre_itunes_edit.setText(
            self.results[current_combobox_index]["primaryGenreName"])

        if "collectionName" in self.results[current_combobox_index]:
            self.album_itunes_edit.setText(
                self.results[current_combobox_index]["collectionName"])

        self.release_year_itunes_edit.setText(
            self.results[current_combobox_index]["releaseDate"][:4])

        if "trackNumber" in self.results[current_combobox_index]:
            self.track_no_itunes_edit.setText(
                str(self.results[current_combobox_index]["trackNumber"]))

        self.file_name_edit.setText(
            self.results[current_combobox_index]["artistName"] + " - " +
            self.results[current_combobox_index]["trackName"] + ".mp3")

    def load_custom_artwork(self):
        image_file_name, _ = QFileDialog.getOpenFileName(
            self, "Select Artwork File", None, "Images (*.png *.jpg)")

        with open(image_file_name, 'rb') as f:
            self.artwork_bytes = f.read()

            artwork = QPixmap()
            artwork.loadFromData(self.artwork_bytes)

        self.artwork_itunes_label.setPixmap(artwork.scaled(150, 150))

    def save(self):
        audio_path = self.search_dir + "/" + self.listbox.currentItem().text()
        audio_save_path = self.dest_dir + "/" + self.file_name_edit.text()

        if not self.artwork_bytes == None:
            if not self.checkbox.isChecked():
                found = 0
                for tag in self.audio_id3:
                    if tag.startswith("APIC") and (PictureType.COVER_FRONT
                                                   == 3):
                        self.audio_id3[tag].data = self.artwork_bytes
                        break

                if not found:
                    self.audio_id3.add(
                        APIC(encoding=3,
                             mime='image/jpeg',
                             type=3,
                             data=self.artwork_bytes))

        self.audio_id3.add(
            TPE1(encoding=3, text=self.artist_itunes_edit.text()))
        self.audio_id3.add(TIT2(encoding=3,
                                text=self.title_itunes_edit.text()))
        self.audio_id3.add(TCON(encoding=3,
                                text=self.genre_itunes_edit.text()))
        self.audio_id3.add(TALB(encoding=3,
                                text=self.album_itunes_edit.text()))
        self.audio_id3.add(
            TYER(encoding=3, text=self.release_year_itunes_edit.text()))
        self.audio_id3.add(
            TRCK(encoding=3, text=self.track_no_itunes_edit.text()))

        self.audio_id3.save(v2_version=3)

        shutil.move(audio_path, audio_save_path)

        self.refresh_listbox()
        self.listbox.setCurrentRow(0)
コード例 #2
0
class AnalysisViewer(AnalysisPlotter, QWidget):
    """This class is a window that provides convenient viewing of a pws acquisition, analysis, and related images.
    It expands upon the functionality of `BigPlot` which handles ROIs but not analysis images."""
    def __init__(self,
                 metadata: pwsdt.Acquisition,
                 analysisLoader: AnalysisPlotter.AnalysisResultsComboType,
                 title: str,
                 roiManager: ROIManager,
                 parent=None,
                 initialField=AnalysisPlotter.PlotFields.Thumbnail,
                 flags=None):
        if flags is not None:
            QWidget.__init__(self, parent=parent, flags=flags)
        else:
            QWidget.__init__(self, parent=parent)
        AnalysisPlotter.__init__(self,
                                 metadata,
                                 analysisLoader,
                                 initialField=initialField)

        self.roiPlot = RoiPlot(metadata,
                               metadata.getThumbnail(),
                               roiManager=roiManager,
                               parent=self)

        self.setWindowTitle(title)
        self.analysisCombo = QComboBox(self)
        self._populateFields()

        self.roiPlot.layout().itemAt(0).insertWidget(
            0, self.analysisCombo)  # This is sketchy, oh well.

        l = QVBoxLayout()
        l.setContentsMargins(0, 0, 0, 0)
        l.addWidget(self.roiPlot)
        self.setLayout(l)

        self.changeData(self.analysisField)

    def _populateFields(self):
        currField = self.analysisCombo.currentText()
        try:
            self.analysisCombo.disconnect()
        except:
            pass  #Sometimes there is nothing to disconnect
        self.analysisCombo.clear()
        _ = self.PlotFields  # Just doing this to make for less typing later.
        items = [_.Thumbnail]
        for i in [
                _.MeanReflectance, _.RMS, _.AutoCorrelationSlope, _.RSquared,
                _.Ld
        ]:
            try:
                if hasattr(
                        self.analysis.pws, i.value[1]
                ):  # This will raise a key error if the analysis object exists but the requested item is not found
                    items.append(i)
            except KeyError:
                pass
        for i in [_.RMS_t_squared, _.Diffusion, _.DynamicsReflectance]:
            try:
                if hasattr(
                        self.analysis.dyn, i.value[1]
                ):  # This will raise a key error if the analysis object exists but the requested item is not found
                    items.append(i)
            except KeyError:
                pass
        if self.analysis.pws is not None:
            if 'reflectance' in self.analysis.pws.file.keys(
            ):  #This is the normalized 3d data cube. needed to generate the opd.
                items.append(_.OpdPeak)
                items.append(_.SingleWavelength)
        for i in range(len(self.acq.fluorescence)):
            items.append(self._fluorescencePlotFields[i])
        self.analysisCombo.addItems([i.name for i in items])
        if currField in [i.name for i in items]:
            self.analysisCombo.setCurrentText(
                currField)  #Maintain the same field if possible.
        self.analysisCombo.currentTextChanged.connect(
            self.changeDataByName
        )  # If this line comes before the analysisCombo.addItems line then it will get triggered when adding items.

    def changeDataByName(self, field: str):
        field = [
            enumField for enumField in self.PlotFields
            if enumField.name == field
        ][0]  #This function recieves the name of the Enum item. we want to get the enum item itself.
        self.changeData(field)

    def changeData(self, field: AnalysisPlotter.PlotFields):
        """Change which image associated with the PWS acquisition we want to view."""
        super().changeData(field)
        if self.analysisCombo.currentText() != field.name:
            self.analysisCombo.setCurrentText(field.name)
        self.roiPlot.setImageData(self.data)

    def setMetadata(self,
                    md: Acquisition,
                    analysis: Optional[ConglomerateAnalysisResults] = None):
        """Change this widget to display data for a different acquisition and optionally an analysis."""
        try:
            super().setMetadata(md, analysis)
        except ValueError:  # Trying to set new metadata may result in an error if the new analysis/metadata can't plot the currently set analysisField
            self.changeData(
                self.PlotFields.Thumbnail
            )  # revert back to thumbnail which should always be possible
            super().setMetadata(md, analysis)
        self.roiPlot.setMetadata(md)
        self._populateFields()
コード例 #3
0
class PlotControlWidget(QWidget):
    """Creates a simple about dialog.

    The about dialog contains general information about the application and
    shows the copyright notice.
    That's why the class has no attributes or return values.

    """

    # signals
    chartViewChanged = pyqtSignal(int)
    lengthEnabled = pyqtSignal(bool)
    countEnabled = pyqtSignal(bool)
    lengthChanged = pyqtSignal(int)
    countChanged = pyqtSignal(int)
    lengthFinished = pyqtSignal()

    def __init__(self):
        super().__init__()

        self._COMBOBOX_ITEM_LIST = (QtCore.QT_TRANSLATE_NOOP("PlotControlWidget", "Area chart"),
                                    QtCore.QT_TRANSLATE_NOOP("PlotControlWidget", "Line chart"))

        # create labels and input fields
        self.viewLabel = QLabel()
        self.viewInput = QComboBox(self)

        self.lengthCheckBox = QCheckBox()
        self.lengthCheckBox.setCheckState(Qt.Checked)
        self.lengthCheckBox.setDisabled(True)
        self.lengthInput = QSpinBox(self, maximum=99999)
        self.lengthInput.setSuffix(" Lfm.")
        self.lengthInput.setDisabled(True)

        self.countCheckBox = QCheckBox()
        self.countCheckBox.setCheckState(Qt.Checked)
        self.countCheckBox.setDisabled(True)
        self.countInput = QSpinBox(self, maximum=99999)
        self.countInput.setSuffix(" St.")
        self.countInput.setDisabled(True)
        self.countCalculator = QPushButton()
        self.countCalculator.setDisabled(True)

        lineFrame = QFrame(frameShadow=QFrame.Sunken, frameShape=QFrame.VLine)

        # create layout
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.viewLabel)
        layout.addWidget(self.viewInput)
        layout.addStretch()
        layout.addWidget(self.lengthCheckBox)
        layout.addWidget(self.lengthInput)
        layout.addWidget(lineFrame)
        layout.addWidget(self.countCheckBox)
        layout.addWidget(self.countInput)
        layout.addWidget(self.countCalculator)

        # connect signals
        self.viewInput.currentIndexChanged.connect(self.chartViewChanged)
        self.lengthCheckBox.stateChanged.connect(self.enableLengthInput)
        self.countCheckBox.stateChanged.connect(self.enableCountInput)
        self.lengthInput.valueChanged.connect(self.lengthChanged)
        self.lengthInput.editingFinished.connect(self.lengthFinished)
        self.countInput.valueChanged.connect(self.countChanged)

        self.countCalculator.clicked.connect(self.countCalculation)

        # translate the graphical user interface
        self.retranslateUi()

    def countCalculation(self):
        dialog = PlantCountDialog()
        if dialog.exec() == QDialog.Accepted:
            self.countInput.setValue(dialog.value)

    def enableLengthInput(self, state):
        if state == Qt.Checked:
            self.lengthInput.setEnabled(True)
            self.lengthEnabled.emit(True)
        else:
            self.lengthInput.setDisabled(True)
            self.lengthEnabled.emit(False)

    def enableCountInput(self, state):
        if state == Qt.Checked:
            self.countInput.setEnabled(True)
            self.countCalculator.setEnabled(True)
            self.countEnabled.emit(True)
        else:
            self.countInput.setDisabled(True)
            self.countCalculator.setDisabled(True)
            self.countEnabled.emit(False)

    def setLength(self, length):
        self.lengthCheckBox.setEnabled(True)
        self.lengthInput.setEnabled(True)
        self.lengthInput.setValue(length)

    def setCount(self, count):
        self.countCheckBox.setEnabled(True)
        self.countInput.setEnabled(True)
        self.countCalculator.setEnabled(True)
        self.countInput.setValue(count)

    def reset(self):
        # reset the view input field
        self.viewInput.disconnect()
        self.viewInput.setCurrentIndex(0)
        #self.viewInput.setDisabled(True)

        # reset length input field
        self.lengthCheckBox.setCheckState(Qt.Checked)
        #self.lengthCheckBox.setDisabled(True)
        self.lengthInput.setValue(0)
        #self.lengthInput.setDisabled(True)

        # reset count check box and count input field
        self.countCheckBox.setCheckState(Qt.Checked)
        #self.countCheckBox.setDisabled(True)
        self.countInput.setValue(0)
        #self.countInput.setDisabled(True)
        #self.countCalculator.setDisabled(True)

    def retranslateUi(self):
        # input fields
        self.viewLabel.setText(QApplication.translate("PlotControlWidget", "Chart view") + ":")                 # Diagrammansicht
        self.viewInput.clear()
        for item in self._COMBOBOX_ITEM_LIST:
            self.viewInput.addItem(QApplication.translate("PlotControlWidget", item))

        self.lengthCheckBox.setText(QApplication.translate("PlotControlWidget", "Fence length") + ":")          # Zaunlänge
        self.countCheckBox.setText(QApplication.translate("PlotControlWidget", "Number of plants") + ":")       # Anzahl der Pflanzen
        self.countCalculator.setText(QApplication.translate("PlotControlWidget", "Calculation help"))           # Umrechnungshilfe
コード例 #4
0
class ModelInfoGroup(QGroupBox):
    """
    This class is a subclass of class QGroupBox.
    """
    send_log = pyqtSignal(str, name='send_log')
    drop_hydro = pyqtSignal()
    drop_merge = pyqtSignal()

    def __init__(self, path_prj, name_prj, send_log):
        super().__init__()
        self.path_prj = path_prj
        self.name_prj = name_prj
        self.send_log = send_log
        self.path_last_file_loaded = self.path_prj
        self.hydraulic_model_information = HydraulicModelInformation()
        self.p = Process(target=None)
        self.pathfile = None
        self.namefile = None
        self.name_hdf5 = None
        self.model_index = None
        self.drop_hydro.connect(lambda: self.name_last_hdf5(self.model_type))
        self.init_ui()

    def init_ui(self):
        self.result_file_title_label = QLabel(self.tr('Result file'))
        self.input_file_combobox = QComboBox()
        self.select_file_button = QPushButton("...")
        self.select_file_button.setToolTip(self.tr("Select file(s)"))
        self.select_file_button.setSizePolicy(QSizePolicy.Maximum,
                                              QSizePolicy.Maximum)
        widget_height = self.input_file_combobox.minimumSizeHint().height()
        self.select_file_button.setFixedHeight(widget_height)
        self.select_file_button.setFixedWidth(widget_height)
        self.select_file_button.clicked.connect(
            self.select_file_and_show_informations_dialog)

        # selection_layout
        self.selection_layout = QHBoxLayout()
        self.selection_layout.addWidget(self.input_file_combobox)
        self.selection_layout.addWidget(self.select_file_button)

        # reach
        reach_name_title_label = QLabel(self.tr('Reach name'))
        self.reach_name_combobox = QComboBox()

        # unit list
        unit_title_label = QLabel(self.tr('Unit name'))
        self.units_QListWidget = QListWidgetClipboard()
        self.units_QListWidget.setSelectionMode(
            QAbstractItemView.ExtendedSelection)

        # unit type
        units_name_title_label = QLabel(self.tr('Type'))
        self.units_name_label = QLabel(self.tr('unknown'))

        # unit number
        units_number_title_label = QLabel(self.tr('Number'))
        self.unit_number_label = QLabel(self.tr('unknown'))

        # unit_layout
        unit_layout = QGridLayout()
        unit_layout.addWidget(self.units_QListWidget, 0, 0, 4, 1)
        unit_layout.addWidget(units_name_title_label, 0, 1, Qt.AlignBottom)
        unit_layout.addWidget(self.units_name_label, 1, 1, Qt.AlignTop)
        unit_layout.addWidget(units_number_title_label, 2, 1, Qt.AlignBottom)
        unit_layout.addWidget(self.unit_number_label, 3, 1, Qt.AlignTop)

        # usefull_mesh_variables
        usefull_mesh_variable_label_title = QLabel(self.tr('Mesh data'))
        # usefull_mesh_variable_label_title.setFixedHeight(widget_height)
        self.usefull_mesh_variable_label = QLabel(self.tr('unknown'))

        # usefull_node_variables
        usefull_node_variable_label_title = QLabel(self.tr('Node data'))
        # usefull_node_variable_label_title.setFixedHeight(widget_height)
        self.usefull_node_variable_label = QLabel(self.tr('unknown'))

        # LAMMI substrate
        sub_radio_group = QButtonGroup(self)
        classification_code_title_label = QLabel(
            self.tr('Sub classification code'))
        classification_code_title_label.setToolTip(
            self.tr("LAMMI data substrate classification code"))
        self.sub_classification_code_edf_radio = QRadioButton("EDF")
        sub_radio_group.addButton(self.sub_classification_code_edf_radio)
        self.sub_classification_code_edf_radio.setToolTip(
            self.tr("8 EDF classes"))
        self.sub_classification_code_cemagref_radio = QRadioButton("Cemagref")
        sub_radio_group.addButton(self.sub_classification_code_cemagref_radio)
        self.sub_classification_code_cemagref_radio.setToolTip(
            self.tr("8 Cemagref classes"))
        if user_preferences.data["lammi_sub_classification_code"] == "EDF":
            self.sub_classification_code_edf_radio.setChecked(True)
        elif user_preferences.data[
                "lammi_sub_classification_code"] == "Cemagref":
            self.sub_classification_code_cemagref_radio.setChecked(True)
        else:
            self.send_log.emit(
                self.
                tr("Warning: lammi_sub_classification_code not recognized in user preferences."
                   ))
            print(
                "Warning: lammi_sub_classification_code not recognized in user preferences."
            )
            self.sub_classification_code_cemagref_radio.setChecked(True)
        self.sub_classification_code_edf_radio.toggled.connect(
            self.lammi_choice_changed)
        sub_radio_layout = QHBoxLayout()
        sub_radio_layout.addWidget(self.sub_classification_code_edf_radio)
        sub_radio_layout.addWidget(self.sub_classification_code_cemagref_radio)
        sub_radio_layout.addStretch()

        # LAMMI equation
        equation_radio_group = QButtonGroup(self)
        equation_title_label = QLabel(self.tr('Calculation method'))
        equation_title_label.setToolTip(
            self.tr("LAMMI hydraulic data calculation method"))
        self.equation_fe_radio = QRadioButton(self.tr("Finite Element Method"))
        equation_radio_group.addButton(self.equation_fe_radio)
        self.equation_fe_radio.setToolTip(
            self.tr("Vertical 1D hydraulic profile data set to node."))
        self.equation_fv_radio = QRadioButton(self.tr("Finite Volume Method"))
        equation_radio_group.addButton(self.equation_fv_radio)
        self.equation_fv_radio.setToolTip(
            self.tr("Vertical 1D hydraulic profile data set to mesh."))
        if user_preferences.data["lammi_calculation_method"] == "FEM":
            self.equation_fe_radio.setChecked(True)
        elif user_preferences.data["lammi_calculation_method"] == "FVM":
            self.equation_fv_radio.setChecked(True)
        else:
            self.send_log.emit(
                self.
                tr("Warning: lammi_calculation_method not recognized in user preferences."
                   ))
            print(
                "Warning: lammi_calculation_method not recognized in user preferences."
            )
            self.equation_fe_radio.setChecked(True)
        self.equation_fe_radio.toggled.connect(self.lammi_choice_changed)
        equation_radio_layout = QHBoxLayout()
        equation_radio_layout.addWidget(self.equation_fe_radio)
        equation_radio_layout.addWidget(self.equation_fv_radio)
        equation_radio_layout.addStretch()

        # epsg
        epsg_title_label = QLabel(self.tr('EPSG code'))
        self.epsg_label = QLineEdit(self.tr('unknown'))
        self.epsg_label.returnPressed.connect(self.load_hydraulic_create_hdf5)

        # hdf5 name
        self.hdf5_name_title_label = QLabel(".hyd " + self.tr('file name'))
        self.hdf5_name_lineedit = QLineEdit()
        self.hdf5_name_lineedit.returnPressed.connect(
            self.load_hydraulic_create_hdf5)

        # last_hydraulic_file_label
        self.last_hydraulic_file_label = QLabel(self.tr('Last file created'))
        self.last_hydraulic_file_name_label = QLabel(self.tr('no file'))

        # progress_layout
        self.progress_layout = ProcessProgLayout(
            self.load_hydraulic_create_hdf5,
            send_log=self.send_log,
            process_type="hyd",
            send_refresh_filenames=self.drop_hydro)

        # layout
        self.hydrau_layout = QGridLayout()
        self.hydrau_layout.addWidget(self.result_file_title_label, 0, 0)
        self.hydrau_layout.addLayout(self.selection_layout, 0, 1)
        self.hydrau_layout.addWidget(reach_name_title_label, 1, 0)
        self.hydrau_layout.addWidget(self.reach_name_combobox, 1, 1)
        self.hydrau_layout.addWidget(unit_title_label, 3, 0)
        self.hydrau_layout.addLayout(unit_layout, 3, 1)
        self.hydrau_layout.addWidget(usefull_mesh_variable_label_title, 5, 0)
        self.hydrau_layout.addWidget(
            self.usefull_mesh_variable_label, 5,
            1)  # from row, from column, nb row, nb column
        self.hydrau_layout.addWidget(usefull_node_variable_label_title, 6, 0)
        self.hydrau_layout.addWidget(
            self.usefull_node_variable_label, 6,
            1)  # from row, from column, nb row, nb column

        self.hydrau_layout.addWidget(classification_code_title_label, 7, 0)
        self.hydrau_layout.addItem(sub_radio_layout, 7, 1)

        self.hydrau_layout.addWidget(equation_title_label, 8, 0)
        self.hydrau_layout.addItem(equation_radio_layout, 8, 1)

        self.hydrau_layout.addWidget(epsg_title_label, 9, 0)
        self.hydrau_layout.addWidget(self.epsg_label, 9, 1)
        self.hydrau_layout.addWidget(self.hdf5_name_title_label, 10, 0)
        self.hydrau_layout.addWidget(self.hdf5_name_lineedit, 10, 1)
        self.hydrau_layout.addLayout(self.progress_layout, 11, 0, 1, 2)
        self.hydrau_layout.addWidget(self.last_hydraulic_file_label, 12, 0)
        self.hydrau_layout.addWidget(self.last_hydraulic_file_name_label, 12,
                                     1)
        self.setLayout(self.hydrau_layout)

    def update_for_lammi(self, on=False):
        # hide/show lammi widgets
        self.hydrau_layout.itemAtPosition(7, 0).widget().setVisible(on)
        for widget_ind in range(
                0,
                self.hydrau_layout.itemAtPosition(7, 1).count()):
            widget_temp = self.hydrau_layout.itemAtPosition(
                7, 1).itemAt(widget_ind).widget()
            if widget_temp:
                widget_temp.setVisible(on)
        self.hydrau_layout.itemAtPosition(8, 0).widget().setVisible(on)
        for widget_ind in range(
                0,
                self.hydrau_layout.itemAtPosition(8, 1).count()):
            widget_temp = self.hydrau_layout.itemAtPosition(
                8, 1).itemAt(widget_ind).widget()
            if widget_temp:
                widget_temp.setVisible(on)
        # change labels
        if on:
            self.hdf5_name_title_label.setText(".hab " + self.tr('file name'))
            self.progress_layout.run_stop_button.setText(
                self.tr("Create .hab file"))
        else:
            self.hdf5_name_title_label.setText(".hyd " + self.tr('file name'))
            self.progress_layout.run_stop_button.setText(
                self.tr("Create .hyd file"))

    def lammi_choice_changed(self):
        # sub
        if self.sub_classification_code_edf_radio.isChecked():
            user_preferences.data["lammi_sub_classification_code"] = "EDF"
        elif self.sub_classification_code_cemagref_radio.isChecked():
            user_preferences.data["lammi_sub_classification_code"] = "Cemagref"
        # equ
        if self.equation_fe_radio.isChecked():
            user_preferences.data["lammi_calculation_method"] = "FEM"
        elif self.equation_fv_radio.isChecked():
            user_preferences.data["lammi_calculation_method"] = "FVM"

        # update variable position
        if self.namefile and (self.sender() == self.equation_fe_radio
                              or self.sender() == self.equation_fv_radio):
            hsr = HydraulicSimulationResults(self.namefile, self.pathfile,
                                             self.model_type, self.path_prj)
            width_char = 120
            mesh_list = ", ".join(
                hsr.hvum.software_detected_list.meshs().names_gui())
            if len(mesh_list) > width_char:
                self.usefull_mesh_variable_label.setText(
                    mesh_list[:width_char] + "...")
                self.usefull_mesh_variable_label.setToolTip(mesh_list)
            else:
                self.usefull_mesh_variable_label.setText(mesh_list)
            node_list = ", ".join(
                hsr.hvum.software_detected_list.nodes().names_gui())
            if len(node_list) > width_char:
                self.usefull_node_variable_label.setText(
                    node_list[:width_char] + "...")
                self.usefull_node_variable_label.setToolTip(node_list)
            else:
                self.usefull_node_variable_label.setText(node_list)

        # save
        user_preferences.save_user_preferences_json()

    def read_attribute_xml(self, att_here):
        """
        A function to read the text of an attribute in the xml project file.

        :param att_here: the attribute name (string).
        """
        data = ''

        filename_path_pro = os.path.join(self.path_prj,
                                         self.name_prj + '.habby')
        if os.path.isfile(filename_path_pro):
            if att_here == "path_last_file_loaded":
                data = load_project_properties(self.path_prj)[att_here]
            else:
                data = load_project_properties(self.path_prj)[att_here]["path"]
        else:
            pass

        return data

    def save_xml(self):
        """
        A function to save the loaded data in the xml file.

        This function adds the name and the path of the newly chosen hydrological data to the xml project file. First,
        it open the xml project file (and send an error if the project is not saved, or if it cannot find the project
        file). Then, it opens the xml file and add the path and name of the file to this xml file. If the model data was
        already loaded, it adds the new name without erasing the old name IF the switch append_name is True. Otherwise,
        it erase the old name and replace it by a new name. The variable “i” has the same role than in select_file_and_show_informations_dialog.

        :param i: a int for the case where there is more than one file to load
        :param append_name: A boolean. If True, the name found will be append to the existing name in the xml file,
                instead of remplacing the old name by the new name.

        """
        filename_path_file = self.pathfile
        filename_path_pro = os.path.join(self.path_prj,
                                         self.name_prj + '.habby')

        # save the name and the path in the xml .prj file
        if not os.path.isfile(filename_path_pro):
            self.end_log.emit(
                'Error: The project is not saved. '
                'Save the project in the General tab before saving hydrological data. \n'
            )
        else:
            # change path_last_file_loaded, model_type (path)
            project_properties = load_project_properties(
                self.path_prj)  # load_project_properties
            project_properties[
                "path_last_file_loaded"] = filename_path_file  # change value
            project_properties[
                self.model_type]["path"] = filename_path_file  # change value
            save_project_properties(
                self.path_prj, project_properties)  # save_project_properties

    def name_last_hdf5(self, type):
        """
        This function opens the xml project file to find the name of the last hdf5 merge file and to add it
        to the GUI on the QLabel self.lm2. It also add a QToolTip with the name of substrate and hydraulic files used
        to create this merge file. If there is no file found, this function do nothing.
        """
        filename_path_pro = os.path.join(self.path_prj,
                                         self.name_prj + '.habby')
        name = QCoreApplication.translate("SubHydroW", 'no file')
        # save the name and the path in the xml .prj file
        if not os.path.isfile(filename_path_pro):
            self.send_log.emit('Error: ' + QCoreApplication.translate(
                "SubHydroW", 'The project is not saved. '
                'Save the project in the General tab before saving hydraulic data. \n'
            ))
        else:
            project_properties = load_project_properties(self.path_prj)
            if project_properties[type]["hdf5"]:
                name = project_properties[type]["hdf5"][-1]

            self.last_hydraulic_file_name_label.setText(name)

    def clean_gui(self):
        self.input_file_combobox.clear()
        self.reach_name_combobox.clear()
        self.units_name_label.setText("unknown")  # kind of unit
        self.unit_number_label.setText("unknown")  # number units
        self.units_QListWidget.clear()
        self.epsg_label.clear()
        self.hdf5_name_lineedit.setText("")  # hdf5 name
        self.progress_layout.run_stop_button.setText(
            self.tr("Create .hyd file"))

    def select_file_and_show_informations_dialog(self):
        """
        A function to obtain the name of the file chosen by the user. This method open a dialog so that the user select
        a file. This file is NOT loaded here. The name and path to this file is saved in an attribute. This attribute
        is then used to loaded the file in other function, which are different for each children class. Based on the
        name of the chosen file, a name is proposed for the hdf5 file.

        :param i: an int for the case where there is more than one file to load
        """
        # get minimum water height as we might neglect very low water height
        self.project_properties = load_project_properties(self.path_prj)

        # prepare the filter to show only useful files
        if len(self.extension.split(", ")) <= 4:
            filter2 = "File ("
            for e in self.extension.split(", "):
                filter2 += '*' + e + ' '
            filter2 = filter2[:-1]
            filter2 += ')' + ";; All File (*.*)"
        else:
            filter2 = ''

        # get last path
        if self.read_attribute_xml(
                self.model_type) != self.path_prj and self.read_attribute_xml(
                    self.model_type) != "":
            model_path = self.read_attribute_xml(self.model_type)  # path spe
        elif self.read_attribute_xml(
                "path_last_file_loaded"
        ) != self.path_prj and self.read_attribute_xml(
                "path_last_file_loaded") != "":
            model_path = self.read_attribute_xml(
                "path_last_file_loaded")  # path last
        else:
            model_path = self.path_prj  # path proj

        # find the filename based on user choice
        if self.extension:
            filename_list = QFileDialog().getOpenFileNames(
                self, self.tr("Select file(s)"), model_path, filter2)
        else:
            filename_list = QFileDialog().getExistingDirectory(
                self, self.tr("Select directory"), model_path)
            filename_list = [filename_list]

        # if file has been selected
        if filename_list[0]:
            # disconnect function for multiple file cases
            try:
                self.input_file_combobox.disconnect()
            except:
                pass

            try:
                self.reach_name_combobox.disconnect()
            except:
                pass

            try:
                self.units_QListWidget.disconnect()
            except:
                pass

            # init
            self.hydrau_case = "unknown"
            self.multi_hdf5 = False
            self.multi_reach = False
            self.index_hydrau_presence = False

            # get_hydrau_description_from_source
            hsra_value = HydraulicSimulationResultsAnalyzer(
                filename_list[0], self.path_prj, self.model_type)

            # warnings
            if hsra_value.warning_list:
                for warn in hsra_value.warning_list:
                    self.send_log.emit(warn)
                    if "Error:" in warn:
                        self.clean_gui()
                        return

            # error
            if type(hsra_value.hydrau_description_list) == str:
                self.clean_gui()
                self.send_log.emit(hsra_value.hydrau_description_list)
                return

            # set to attribute
            self.hydrau_description_list = hsra_value.hydrau_description_list

            # display first hydrau_description_list
            self.hydrau_case = self.hydrau_description_list[0]["hydrau_case"]
            # change suffix
            if not self.project_properties[
                    "cut_mesh_partialy_dry"] and self.hydrau_description_list[
                        0]["model_dimension"] == "2":
                for telemac_description_num in range(
                        len(self.hydrau_description_list)):
                    namehdf5_old = os.path.splitext(
                        self.hydrau_description_list[telemac_description_num]
                        ["hdf5_name"])[0]
                    exthdf5_old = os.path.splitext(
                        self.hydrau_description_list[telemac_description_num]
                        ["hdf5_name"])[1]
                    self.hydrau_description_list[telemac_description_num][
                        "hdf5_name"] = namehdf5_old + "_no_cut" + exthdf5_old
            # save last path
            self.pathfile = self.hydrau_description_list[0][
                "path_filename_source"]  # source file path
            self.namefile = self.hydrau_description_list[0][
                "filename_source"]  # source file name
            self.name_hdf5 = self.hydrau_description_list[0]["hdf5_name"]
            self.save_xml()
            # multi
            if len(self.hydrau_description_list) > 1:
                self.multi_hdf5 = True
            # multi
            if len(self.hydrau_description_list[0]["reach_list"]) > 1:
                self.multi_reach = True

            # get names
            names = [
                description["filename_source"]
                for description in self.hydrau_description_list
            ]

            # clean GUI
            self.clean_gui()

            self.input_file_combobox.addItems(names)

            self.update_reach_from_input_file()
            self.input_file_combobox.currentIndexChanged.connect(
                self.update_reach_from_input_file)
            self.reach_name_combobox.currentIndexChanged.connect(
                self.update_unit_from_reach)
            self.units_QListWidget.itemSelectionChanged.connect(
                self.unit_counter)

            self.hdf5_name_lineedit.setFocus()

    def update_reach_from_input_file(self):
        self.reach_name_combobox.blockSignals(True)
        self.reach_name_combobox.clear()
        self.reach_name_combobox.addItems(self.hydrau_description_list[
            self.input_file_combobox.currentIndex()]["reach_list"])
        width_char = 120
        mesh_list = ", ".join(
            self.hydrau_description_list[self.input_file_combobox.currentIndex(
            )]["variable_name_unit_dict"].meshs().names_gui())
        if len(mesh_list) > width_char:
            self.usefull_mesh_variable_label.setText(mesh_list[:width_char] +
                                                     "...")
            self.usefull_mesh_variable_label.setToolTip(mesh_list)
        else:
            self.usefull_mesh_variable_label.setText(mesh_list)
        node_list = ", ".join(
            self.hydrau_description_list[self.input_file_combobox.currentIndex(
            )]["variable_name_unit_dict"].nodes().names_gui())
        if len(node_list) > width_char:
            self.usefull_node_variable_label.setText(node_list[:width_char] +
                                                     "...")
            self.usefull_node_variable_label.setToolTip(node_list)
        else:
            self.usefull_node_variable_label.setText(node_list)
        self.units_name_label.setText(
            self.hydrau_description_list[self.input_file_combobox.currentIndex(
            )]["unit_type"])  # kind of unit
        self.update_unit_from_reach()
        self.epsg_label.setText(self.hydrau_description_list[
            self.input_file_combobox.currentIndex()]["epsg_code"])
        self.hdf5_name_lineedit.setText(self.hydrau_description_list[
            self.input_file_combobox.currentIndex()]["hdf5_name"])  # hdf5 name
        extension = "hyd"
        if self.hydrau_description_list[
                self.input_file_combobox.currentIndex()]["sub"]:
            extension = "hab"
        text_load_button = self.tr("Create ") + str(
            len(self.hydrau_description_list)) + self.tr(
                " file ") + "." + extension
        if len(self.hydrau_description_list) > 1:
            text_load_button = self.tr("Create ") + str(
                len(self.hydrau_description_list)) + self.tr(
                    " files ") + "." + extension
        self.progress_layout.run_stop_button.setText(text_load_button)
        self.progress_layout.progress_bar.setValue(0.0)
        self.progress_layout.progress_label.setText("{0:.0f}/{1:.0f}".format(
            0.0, len(self.hydrau_description_list)))
        self.reach_name_combobox.blockSignals(False)

    def update_unit_from_reach(self):
        self.units_QListWidget.blockSignals(True)
        self.units_QListWidget.clear()
        self.units_QListWidget.addItems(
            self.hydrau_description_list[self.input_file_combobox.currentIndex(
            )]["unit_list_full"][self.reach_name_combobox.currentIndex()])
        if all(self.hydrau_description_list[
                self.input_file_combobox.currentIndex()]["unit_list_tf"][
                    self.reach_name_combobox.currentIndex()]):
            self.units_QListWidget.selectAll()
        else:
            for i in range(
                    len(self.hydrau_description_list[
                        self.input_file_combobox.currentIndex()]
                        ["unit_list_full"][
                            self.reach_name_combobox.currentIndex()])):
                self.units_QListWidget.item(i).setSelected(
                    self.hydrau_description_list[
                        self.input_file_combobox.currentIndex()]
                    ["unit_list_tf"][
                        self.reach_name_combobox.currentIndex()][i])
                self.units_QListWidget.item(i).setTextAlignment(Qt.AlignLeft)
        self.units_QListWidget.blockSignals(False)
        self.unit_counter()

    def unit_counter(self):
        hyd_desc_index = self.input_file_combobox.currentIndex()
        reach_index = self.reach_name_combobox.currentIndex()
        # count total number items (units)
        total = self.units_QListWidget.count()
        # count total number items selected
        selected = len(self.units_QListWidget.selectedItems())

        # refresh telemac dictonnary
        unit_list = []
        unit_list_full = []
        unit_list_tf = []
        for i in range(total):
            text = self.units_QListWidget.item(i).text()
            if self.units_QListWidget.item(i).isSelected():
                unit_list.append(text)
            unit_list_full.append(text)
            unit_list_tf.append(self.units_QListWidget.item(i).isSelected())
        # save multi
        self.hydrau_description_list[hyd_desc_index]["unit_list"][
            reach_index] = list(unit_list)
        self.hydrau_description_list[hyd_desc_index]["unit_list_full"][
            reach_index] = unit_list_full
        self.hydrau_description_list[hyd_desc_index]["unit_list_tf"][
            reach_index] = unit_list_tf
        self.hydrau_description_list[hyd_desc_index]["unit_number"] = str(
            selected)

        if self.hydrau_case == '2.a' or self.hydrau_case == '2.b':
            # preset name hdf5
            filename_source_list = self.hydrau_description_list[
                hyd_desc_index]["filename_source"].split(", ")
            new_names_list = []
            for file_num, file in enumerate(filename_source_list):
                if self.hydrau_description_list[hyd_desc_index][
                        "unit_list_tf"][reach_index][file_num]:
                    new_names_list.append(os.path.splitext(file)[0])
            self.hydrau_description_list[hyd_desc_index][
                "hdf5_name"] = "_".join(new_names_list) + ".hyd"
            if len(filename_source_list) == len(new_names_list) and len(
                    self.hydrau_description_list[hyd_desc_index]
                ["hdf5_name"]) > 25:
                self.hydrau_description_list[hyd_desc_index]["hdf5_name"] = new_names_list[0].replace(".", "_") \
                                                                                        + "_to_" + \
                                                                                        new_names_list[-1].replace(".", "_") + ".hyd"

        if not load_specific_properties(
                self.path_prj,
            ["cut_mesh_partialy_dry"])[0] and self.hydrau_description_list[
                hyd_desc_index]["model_dimension"] == "2":
            namehdf5_old = \
            os.path.splitext(self.hydrau_description_list[hyd_desc_index]["hdf5_name"])[0]
            exthdf5_old = \
            os.path.splitext(self.hydrau_description_list[hyd_desc_index]["hdf5_name"])[1]
            if not "no_cut" in namehdf5_old:
                self.hydrau_description_list[hyd_desc_index][
                    "hdf5_name"] = namehdf5_old + "_no_cut" + exthdf5_old

        self.hdf5_name_lineedit.setText(
            self.hydrau_description_list[hyd_desc_index]
            ["hdf5_name"])  # hdf5 name

        # set text
        text = str(selected) + "/" + str(total)
        self.unit_number_label.setText(text)  # number units

        self.progress_layout.run_stop_button.setEnabled(True)

    def load_hydraulic_create_hdf5(self):
        """
        The function which call the function which load telemac and
         save the name of files in the project file
        """
        """
        The function which call the function which load hec_ras2d and
         save the name of files in the project file
        """
        if self.progress_layout.run_stop_button.isEnabled():
            # get minimum water height as we might neglect very low water height
            self.project_properties = load_project_properties(self.path_prj)

            # get timestep and epsg selected
            for i in range(len(self.hydrau_description_list)):
                for reach_number in range(
                        int(self.hydrau_description_list[i]["reach_number"])):
                    if not any(self.hydrau_description_list[i]["unit_list_tf"]
                               [reach_number]):
                        self.send_log.emit(
                            "Error: " + self.tr("No units selected for : ") +
                            self.hydrau_description_list[i]["filename_source"]
                            + "\n")
                        return

            # check if extension is set by user (one hdf5 case)
            self.name_hdf5 = self.hdf5_name_lineedit.text()
            self.hydrau_description_list[self.input_file_combobox.currentIndex(
            )]["hdf5_name"] = self.name_hdf5
            if self.name_hdf5 == "":
                self.send_log.emit('Error: ' + self.tr(
                    '.hyd output filename is empty. Please specify it.'))
                return

            # check if extension is set by user (multi hdf5 case)
            hydrau_description_multiple = deepcopy(
                self.hydrau_description_list
            )  # create copy to not erase inital choices
            for hdf5_num in range(len(hydrau_description_multiple)):
                if not os.path.splitext(
                        hydrau_description_multiple[hdf5_num]["hdf5_name"])[1]:
                    hydrau_description_multiple[hdf5_num][
                        "hdf5_name"] = hydrau_description_multiple[hdf5_num][
                            "hdf5_name"] + ".hyd"
                # refresh filename_source
                if self.hydrau_case == '2.a' or self.hydrau_case == '2.b':
                    filename_source_list = hydrau_description_multiple[
                        hdf5_num]["filename_source"].split(", ")
                    new_filename_source_list = []
                    for reach_number in range(
                            len(hydrau_description_multiple[hdf5_num]
                                ["unit_list_tf"])):
                        for file_num, file in enumerate(filename_source_list):
                            if hydrau_description_multiple[hdf5_num][
                                    "unit_list_tf"][reach_number][file_num]:
                                new_filename_source_list.append(
                                    filename_source_list[file_num])
                    hydrau_description_multiple[hdf5_num][
                        "filename_source"] = ", ".join(
                            new_filename_source_list)

            # process_manager
            self.progress_layout.process_manager.set_hyd_mode(
                self.path_prj, hydrau_description_multiple,
                self.project_properties)

            # process_prog_show
            self.progress_layout.start_process()

            # script
            self.create_script(hydrau_description_multiple)

    def create_script(self, hydrau_description_multiple):
        # path_prj
        path_prj_script = self.path_prj + "_restarted"

        # cli
        if sys.argv[0][-3:] == ".py":
            exe_cmd = '"' + sys.executable + '" "' + sys.argv[0] + '"'
        else:
            exe_cmd = '"' + sys.executable + '"'
        script_function_name = "CREATE_HYD"
        cmd_str = exe_cmd + ' ' + script_function_name + \
                  ' model="' + self.model_type + '"' + \
                  ' inputfile="' + os.path.join(self.path_prj, "input", self.name_hdf5.split(".")[0], "indexHYDRAU.txt") + '"' + \
                  ' unit_list=' + str(self.hydrau_description_list[self.input_file_combobox.currentIndex()]['unit_list'][0]).replace("\'", "'").replace(' ', '') + \
                  ' cut=' + str(self.project_properties['cut_mesh_partialy_dry']) + \
                  ' outputfilename="' + self.name_hdf5 + '"' + \
                  ' path_prj="' + path_prj_script + '"'
        self.send_log.emit("script" + cmd_str)

        # py
        cmd_str = F"\t# CREATE_HYD\n" \
                  F"\tfrom src.hydraulic_process_mod import HydraulicSimulationResultsAnalyzer, load_hydraulic_cut_to_hdf5\n\n"
        cmd_str = cmd_str + F'\thsra_value = HydraulicSimulationResultsAnalyzer(filename_path_list=[{repr(os.path.join(self.path_prj, "input", self.name_hdf5.split(".")[0], "indexHYDRAU.txt"))}], ' \
                  F"\tpath_prj={repr(path_prj_script)}, " \
                  F"\tmodel_type={repr(self.model_type)}, " \
                  F"\tnb_dim={repr(str(self.nb_dim))})\n"
        cmd_str = cmd_str + F"\tfor hdf5_file_index in range(0, len(hsra_value.hydrau_description_list)):\n" \
                            F"\t\tprogress_value = Value('d', 0.0)\n" \
                            F"\t\tq = Queue()\n" \
                            F"\t\tload_hydraulic_cut_to_hdf5(hydrau_description=hsra_value.hydrau_description_list[hdf5_file_index], " \
                            F"\tprogress_value=progress_value, " \
                            F"\tq=q, " \
                            F"\tprint_cmd=True, " \
                            F"\tproject_properties=load_project_properties({repr(path_prj_script)}))" + "\n"
        self.send_log.emit("py" + cmd_str)
コード例 #5
0
ファイル: picture_editor.py プロジェクト: hamano0813/PySanDS
class PictureEditor(QFrame):
    picture_data: Picture
    pic_path: str='./'

    def __init__(self, parent, picture_parameter: dict):
        QFrame.__init__(self, parent, flags=Qt.FramelessWindowHint)
        self.buffer = parent.buffer
        self.picture_parameter = picture_parameter

        self.picture_label = QLabel()
        self.resize_multiple = 1

        scroll = QScrollArea(self)
        scroll.setWidget(self.picture_label)
        scroll.setObjectName('picture')

        self.name_combo = QComboBox(self)
        self.index_combo = QComboBox(self)
        self.palette_combo = QComboBox(self)
        self.resize_combo = QComboBox(self)

        self.name_combo.setFixedWidth(120)
        self.index_combo.setFixedWidth(120)
        self.palette_combo.setFixedWidth(120)
        self.resize_combo.setFixedWidth(120)

        self.name_combo.setItemDelegate(QStyledItemDelegate())
        self.index_combo.setItemDelegate(QStyledItemDelegate())
        self.palette_combo.setItemDelegate(QStyledItemDelegate())
        self.resize_combo.setItemDelegate(QStyledItemDelegate())

        self.resize_combo.addItems([f'          × {i + 1}' for i in range(4)])

        self.name_combo.currentTextChanged[str].connect(self.name_change)
        self.index_combo.currentIndexChanged.connect(self.refresh_data)
        self.palette_combo.currentIndexChanged.connect(self.refresh_data)
        self.resize_combo.currentIndexChanged.connect(self.refresh_data)

        self.name_combo.addItems(picture_parameter)

        output_button = QPushButton('導出圖片')
        input_button = QPushButton('導入圖片')
        output_button.clicked.connect(self.output_picture)
        input_button.clicked.connect(self.input_picture)

        control_layout = QVBoxLayout()
        control_layout.addWidget(QLabel('選擇圖片'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.name_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(QLabel('選擇編號'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.index_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(QLabel('選擇色板'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.palette_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(QLabel('缩放比例'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.resize_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(output_button, alignment=Qt.AlignRight)
        control_layout.addWidget(input_button, alignment=Qt.AlignRight)

        control_layout.addStretch()

        layout = QHBoxLayout()
        layout.addLayout(control_layout)
        layout.addWidget(scroll)

        self.setLayout(layout)

    def name_change(self, name: str):
        picture_setting = DATA_PARAMETER.get('圖片_' + name)
        self.index_combo.disconnect()
        self.index_combo.clear()
        self.index_combo.addItems([f'{i+1:>13d}' for i in range(picture_setting['quantity']['normal_quantity'])])
        self.index_combo.currentIndexChanged.connect(self.refresh_data)

        if self.picture_parameter.get(name):
            palette_setting = PALETTE_PARAMETER.get('色板_' + self.picture_parameter.get(name))
        else:
            palette_setting = ()
        self.palette_combo.disconnect()
        self.palette_combo.clear()
        if palette_setting:
            self.palette_combo.addItems([f'0x{address:08X}' for address in palette_setting])
            self.palette_combo.setEnabled(True)
        else:
            self.palette_combo.setEnabled(False)
        self.palette_combo.currentIndexChanged.connect(self.refresh_data)
        self.refresh_data()

    def refresh_data(self):
        picture_setting = DATA_PARAMETER.get('圖片_' + self.name_combo.currentText())
        self.picture_data = Picture(self, **picture_setting)
        if self.palette_combo.isEnabled():
            self.picture_data.palette.set_normal_offset(int(self.palette_combo.currentText(), 16))
        self.resize_multiple = self.resize_combo.currentIndex() + 1
        self.set_picture()

    def set_picture(self):
        picture = self.picture_data.get_data(self.index_combo.currentIndex())
        width = self.picture_data.width * self.resize_multiple
        height = self.picture_data.height * self.resize_multiple
        self.picture_label.setFixedSize(width, height)
        self.picture_label.setPixmap(picture.resize((width, height)).toqpixmap())

    def output_picture(self):
        filename = QFileDialog.getSaveFileName(self, '导出图片', self.pic_path, 'BMP图像(*.bmp)')[0]
        if filename:
            self.pic_path = filename[0: filename.rfind('/') + 1]
            if self.index_combo.isEnabled():
                picture = self.picture_data.get_data(self.index_combo.currentIndex())
            else:
                picture = self.picture_data.get_data(0)
            picture.save(filename)

    def input_picture(self):
        filename = QFileDialog.getOpenFileName(self, '导入图片', self.pic_path, '*.bmp;;*.png;;*.gif;;*.tif')[0]
        if filename:
            self.pic_path = filename[0: filename.rfind('/') + 1]
            width = self.picture_data.width * self.resize_multiple
            height = self.picture_data.height * self.resize_multiple
            picture = Image.open(filename).resize((width, height))
            self.picture_data.set_data(self.index_combo.currentIndex(), picture)
            self.set_picture()
コード例 #6
0
class ImportFrame(QFrame):
    conn: sqlite3.Connection = None
    path: str = './'
    setting_file_path: str = None
    import_wb: Workbook = None
    import_ws: Worksheet = None
    model = ImportModel([])
    type_delegate = TypeDelegate()

    # noinspection PyArgumentList
    def __init__(self, *args):
        super(ImportFrame, self).__init__(*args)
        file_group = QGroupBox('Excel File')
        self.file_line = QLineEdit()
        self.file_line.setReadOnly(True)
        file_button = QPushButton('Load')
        file_button.setFixedWidth(100)
        file_layout = QGridLayout()
        file_layout.addWidget(self.file_line, 0, 0)
        file_layout.addWidget(file_button, 0, 1)
        file_group.setLayout(file_layout)
        file_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        import_group = QGroupBox('Import Settings')
        self.sheet_combo = QComboBox()
        self.header_spin = QSpinBox()
        self.header_spin.setRange(0, 0)
        self.name_line = QLineEdit()
        self.exists_combo = QComboBox()
        self.exists_combo.addItems(['append', 'replace', 'fail'])
        self.exists_combo.setStatusTip(
            '''if table also in database\nappend: insert new records\nreplace: replace data\nfail: abort import''')
        form_layout = QFormLayout()
        form_layout.addRow('Select Sheet', self.sheet_combo)
        form_layout.addRow('Select Title Row', self.header_spin)
        form_layout.addRow('Table Name', self.name_line)
        form_layout.addRow('If Table Exists', self.exists_combo)
        self.column_view = QTableView()
        save_button = QPushButton('&Save')
        save_button.setFixedWidth(100)
        load_button = QPushButton('&Load')
        load_button.setFixedWidth(100)
        fast_button = QPushButton('&FastLoad')
        fast_button.setFixedWidth(100)
        import_button = QPushButton('&Import')
        import_button.setFixedWidth(100)
        button_layout = QHBoxLayout()
        button_layout.addStretch()
        button_layout.addWidget(save_button)
        button_layout.addWidget(load_button)
        button_layout.addWidget(fast_button)
        button_layout.addWidget(import_button)

        setting_layout = QGridLayout()
        setting_layout.addLayout(form_layout, 0, 0)
        setting_layout.addWidget(self.column_view, 1, 0)
        setting_layout.addLayout(button_layout, 2, 0)
        import_group.setLayout(setting_layout)

        main_layout = QGridLayout()
        main_layout.addWidget(file_group, 0, 0)
        main_layout.addWidget(import_group, 1, 0)
        self.setLayout(main_layout)

        file_button.clicked.connect(self.load_file)
        self.header_spin.valueChanged.connect(self.load_column)
        save_button.clicked.connect(self.save_setting)
        load_button.clicked.connect(self.load_setting)
        fast_button.clicked.connect(self.auto_load)
        import_button.clicked.connect(self.import_data)

    def load_file(self):
        file_path: str = QFileDialog.getOpenFileName(self.parent(), 'Load File', self.path, 'Excel File(*.xlsx)',
                                                     options=QFileDialog.DontConfirmOverwrite)[0]
        if file_path:
            self.file_line.setText(file_path)
            self.path = '/'.join(file_path.split('/')[:-1])
            self.import_wb = load_workbook(file_path, data_only=True)
            self.sheet_combo.disconnect()
            self.sheet_combo.clear()
            self.sheet_combo.addItems(self.import_wb.sheetnames)
            self.load_sheet()
            self.sheet_combo.currentIndexChanged.connect(self.load_sheet)

    def load_sheet(self):
        self.import_ws = self.import_wb[self.sheet_combo.currentText()]
        self.name_line.setText(self.sheet_combo.currentText().replace(' ', '_', 10))
        self.header_spin.setRange(1, self.import_ws.max_row)
        self.load_column()

    def load_column(self):
        self.model = ImportModel([cell.value for cell in list(self.import_ws.rows)[self.header_spin.value() - 1]])
        self.column_view.setModel(self.model)
        self.column_view.setItemDelegateForColumn(1, self.type_delegate)
        self.column_view.setColumnWidth(0, 500)
        self.column_view.setColumnWidth(1, 150)

    def import_data(self):
        if not self.file_line.text():
            return
        self.sender().setEnabled(False)
        df = pd.read_excel(self.file_line.text(), self.sheet_combo.currentText(), header=self.header_spin.value() - 1,
                           usecols=self.model.usecols, dtype=self.model.dtype, parse_dates=self.model.parse_dates,
                           date_parser=lambda x: pd.to_datetime(x).strftime('%Y-%m-%d'))
        for k, v in self.model.dtype.items():
            if v == str:
                df[k] = df[k].replace('nan', np.nan)
        df.to_sql(self.name_line.text(), self.conn, if_exists=self.exists_combo.currentText())
        self.sender().setEnabled(True)

    def save_setting(self):
        file_path: str = QFileDialog.getSaveFileName(self.parent(), 'Save Setting', './setting/', 'Setting File(*.stg)',
                                                     options=QFileDialog.DontConfirmOverwrite)[0]
        if file_path:
            save_settings = [self.name_line.text(), self.header_spin.value(), self.model.table_settings]
            file = open(file_path, 'wb')
            pickle.dump(save_settings, file)
            file.close()

    def load_setting(self):
        file_path: str = QFileDialog.getOpenFileName(self.parent(), 'Load Setting', './setting/', 'Setting File(*.stg)',
                                                     options=QFileDialog.DontConfirmOverwrite)[0]
        if file_path:
            self.setting_file_path = file_path
            file = open(file_path, 'rb')
            load_settings = pickle.load(file)
            self.name_line.setText(load_settings[0])
            self.header_spin.setValue(load_settings[1])
            self.model.table_settings = load_settings[2]
            self.column_view.reset()

    def auto_load(self):
        if self.setting_file_path:
            file = open(self.setting_file_path, 'rb')
            load_settings = pickle.load(file)
            self.name_line.setText(load_settings[0])
            self.header_spin.setValue(load_settings[1])
            self.model.table_settings = load_settings[2]
            self.column_view.reset()
コード例 #7
0
ファイル: minimal_DAT_GUI.py プロジェクト: baigouy/DAT
class DAT_GUI(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        screen = QApplication.desktop().screenNumber(
            QApplication.desktop().cursor().pos())
        centerPoint = QApplication.desktop().screenGeometry(screen).center()

        # should fit in 1024x768 (old computer screens)
        window_width = 900
        window_height = 700
        self.setGeometry(
            QtCore.QRect(
                centerPoint.x() - int(window_width / 2),
                centerPoint.y() - int(window_height / 2), window_width,
                window_height))  # should I rather center on the screen

        # zoom parameters
        self.scale = 1.0
        self.min_scaling_factor = 0.1
        self.max_scaling_factor = 20
        self.zoom_increment = 0.05

        self.setWindowTitle(__NAME__ + ' v' + str(__VERSION__))

        self.paint = Createpaintwidget()

        # initiate 2D image for 2D display
        self.img = None

        self.list = QListWidget(
            self)  # a list that contains files to read or play with
        self.list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.list.selectionModel().selectionChanged.connect(
            self.selectionChanged)  # connect it to sel change

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.paint)
        self.paint.scrollArea = self.scrollArea

        self.table_widget = QWidget()
        table_widget_layout = QVBoxLayout()

        # Initialize tab screen
        self.tabs = QTabWidget(self)
        self.tab1 = QWidget()
        self.tab2 = QWidget()
        self.tab3 = QWidget()

        # Add tabs
        self.tabs.addTab(self.tab1, "Mask neuron")
        self.tabs.addTab(self.tab2, "Mask cell body")
        self.tabs.addTab(self.tab3, "Segment dendrites")

        # listen to tab changes
        self.tabs.currentChanged.connect(self._onTabChange)

        # Create first tab
        self.tab1.layout = QGridLayout()
        self.tab1.layout.setAlignment(Qt.AlignTop)
        self.tab1.layout.setHorizontalSpacing(3)
        self.tab1.layout.setVerticalSpacing(3)
        self.tab1.layout.setContentsMargins(0, 0, 0, 0)

        label1_tab1 = QLabel('Step 1:')
        self.tab1.layout.addWidget(label1_tab1, 0, 0)

        self.local_threshold = QPushButton("Local threshold")
        self.local_threshold.clicked.connect(self.run_threshold_neuron)
        self.tab1.layout.addWidget(self.local_threshold, 0, 1)
        self.global_threshold = QPushButton("Global threshold")
        self.global_threshold.clicked.connect(self.run_threshold_neuron)
        self.tab1.layout.addWidget(self.global_threshold, 0, 2)
        self.local_n_global_threshold = QPushButton(
            "Local AND Global threshold")
        self.local_n_global_threshold.clicked.connect(
            self.run_threshold_neuron)
        self.tab1.layout.addWidget(self.local_n_global_threshold, 0, 3)

        self.extra_value_for_threshold = QSpinBox()
        self.extra_value_for_threshold.setSingleStep(1)
        self.extra_value_for_threshold.setRange(0, 1_000_000)
        self.extra_value_for_threshold.setValue(6)
        self.tab1.layout.addWidget(self.extra_value_for_threshold, 0, 4)

        self.threshold_method = QComboBox()
        self.threshold_method.addItem('Mean')
        self.threshold_method.addItem('Median')
        self.tab1.layout.addWidget(self.threshold_method, 0, 5)

        label2_tab1 = QLabel('Step 2 (optional):')
        self.tab1.layout.addWidget(label2_tab1, 1, 0)

        self.remove_pixel_blobs_smaller_or_equal = QPushButton(
            "Remove pixel blobs smaller or equal to")
        self.remove_pixel_blobs_smaller_or_equal.clicked.connect(
            self.remove_blobs)
        self.tab1.layout.addWidget(self.remove_pixel_blobs_smaller_or_equal, 1,
                                   1)

        self.remove_blobs_size = QSpinBox()
        self.remove_blobs_size.setSingleStep(1)
        self.remove_blobs_size.setRange(0, 1_000_000)
        self.remove_blobs_size.setValue(1)
        self.tab1.layout.addWidget(self.remove_blobs_size, 1, 2)

        label3_tab1 = QLabel('Step 3: Save')
        self.tab1.layout.addWidget(label3_tab1, 2, 0)

        self.tab1.setLayout(self.tab1.layout)

        self.tab2.layout = QGridLayout()
        self.tab2.layout.setAlignment(Qt.AlignTop)
        self.tab2.layout.setHorizontalSpacing(3)
        self.tab2.layout.setVerticalSpacing(3)
        self.tab2.layout.setContentsMargins(0, 0, 0, 0)

        label1_tab2 = QLabel('Step 1:')
        self.tab2.layout.addWidget(label1_tab2, 0, 0)

        self.detect_cell_body = QPushButton("Detect cell body")
        self.detect_cell_body.clicked.connect(self.detect_neuronal_cell_body)
        self.tab2.layout.addWidget(self.detect_cell_body, 0, 1)

        self.extraCutOff_cell_body = QSpinBox()
        self.extraCutOff_cell_body.setSingleStep(1)
        self.extraCutOff_cell_body.setRange(0, 1_000_000)
        self.extraCutOff_cell_body.setValue(5)
        self.tab2.layout.addWidget(self.extraCutOff_cell_body, 0, 2)

        erosion_label = QLabel('erosion rounds')
        self.tab2.layout.addWidget(erosion_label, 0, 3)

        self.nb_erosion_cellbody = QSpinBox()
        self.nb_erosion_cellbody.setSingleStep(1)
        self.nb_erosion_cellbody.setRange(0, 1_000_000)
        self.nb_erosion_cellbody.setValue(2)
        self.tab2.layout.addWidget(self.nb_erosion_cellbody, 0, 4)

        min_object_size_label = QLabel('minimum object size')
        self.tab2.layout.addWidget(min_object_size_label, 0, 5)

        self.min_obj_size_px = QSpinBox()
        self.min_obj_size_px.setSingleStep(1)
        self.min_obj_size_px.setRange(0, 1_000_000)
        self.min_obj_size_px.setValue(600)
        self.tab2.layout.addWidget(self.min_obj_size_px, 0, 6)

        fill_label = QLabel('fill up to')
        self.tab2.layout.addWidget(fill_label, 0, 7)

        self.fill_holes_up_to = QSpinBox()
        self.fill_holes_up_to.setSingleStep(1)
        self.fill_holes_up_to.setRange(0, 1_000_000)
        self.fill_holes_up_to.setValue(600)
        self.tab2.layout.addWidget(self.fill_holes_up_to, 0, 8)

        nb_dilation_cell_body_label = QLabel('nb dilation cell body')
        self.tab2.layout.addWidget(nb_dilation_cell_body_label, 0, 9)

        self.nb_dilation_cellbody = QSpinBox()
        self.nb_dilation_cellbody.setSingleStep(1)
        self.nb_dilation_cellbody.setRange(0, 1_000_000)
        self.nb_dilation_cellbody.setValue(2)
        self.tab2.layout.addWidget(self.nb_dilation_cellbody, 0, 10)

        label2_tab2 = QLabel('Step 2: Save')
        self.tab2.layout.addWidget(label2_tab2, 6, 0)

        self.tab2.setLayout(self.tab2.layout)

        self.tab3.layout = QGridLayout()
        self.tab3.layout.setAlignment(Qt.AlignTop)
        self.tab3.layout.setHorizontalSpacing(3)
        self.tab3.layout.setVerticalSpacing(3)
        self.tab3.layout.setContentsMargins(0, 0, 0, 0)

        label1_tab3 = QLabel('Step 1:')
        self.tab3.layout.addWidget(label1_tab3, 0, 0)

        self.wshed = QPushButton("Watershed")
        self.wshed.clicked.connect(self.watershed_segment_the_neuron)
        self.tab3.layout.addWidget(self.wshed, 0, 1)

        self.whsed_big_blur = QDoubleSpinBox()
        self.whsed_big_blur.setSingleStep(0.1)
        self.whsed_big_blur.setRange(0, 100)
        self.whsed_big_blur.setValue(2.1)
        self.tab3.layout.addWidget(self.whsed_big_blur, 0, 2)

        self.whsed_small_blur = QDoubleSpinBox()
        self.whsed_small_blur.setSingleStep(0.1)
        self.whsed_small_blur.setRange(0, 100)
        self.whsed_small_blur.setValue(1.4)
        self.tab3.layout.addWidget(self.whsed_small_blur, 0, 3)

        self.wshed_rm_small_cells = QSpinBox()
        self.wshed_rm_small_cells.setSingleStep(1)
        self.wshed_rm_small_cells.setRange(0, 1_000_000)
        self.wshed_rm_small_cells.setValue(10)
        self.tab3.layout.addWidget(self.wshed_rm_small_cells, 0, 4)

        self.jSpinner11 = QSpinBox()
        self.jSpinner11.setSingleStep(1)
        self.jSpinner11.setRange(0, 1_000_000)
        self.jSpinner11.setValue(10)
        self.tab3.layout.addWidget(self.jSpinner11, 0, 5)

        label1_bis_tab3 = QLabel('Alternative Step 1:')
        self.tab3.layout.addWidget(label1_bis_tab3, 1, 0)

        self.skel = QPushButton("Skeletonize")
        self.skel.clicked.connect(self.skeletonize_mask)
        self.tab3.layout.addWidget(self.skel, 1, 1)

        label2_tab3 = QLabel('Step 2:')
        self.tab3.layout.addWidget(label2_tab3, 2, 0)

        self.apply_cell_body = QPushButton("Apply cell body")
        self.apply_cell_body.clicked.connect(
            self.apply_cell_body_to_skeletonized_mask)
        self.tab3.layout.addWidget(self.apply_cell_body, 2, 1)

        label3_tab3 = QLabel('Step 3 (Optional):')
        self.tab3.layout.addWidget(label3_tab3, 3, 0)

        self.prune = QPushButton("Prune")
        self.prune.clicked.connect(self.prune_dendrites)
        self.tab3.layout.addWidget(self.prune, 3, 1)

        self.prune_length = QSpinBox()
        self.prune_length.setSingleStep(1)
        self.prune_length.setRange(0, 1_000_000)
        self.prune_length.setValue(3)
        self.tab3.layout.addWidget(self.prune_length, 3, 2)

        label4_tab3 = QLabel('Step 4 (Optional):')
        self.tab3.layout.addWidget(label4_tab3, 4, 0)

        self.find_neurons = QPushButton("Find neurons")
        self.find_neurons.clicked.connect(self.find_neurons_in_mask)
        self.tab3.layout.addWidget(self.find_neurons, 4, 1)

        self.find_neurons_min_size = QSpinBox()
        self.find_neurons_min_size.setSingleStep(1)
        self.find_neurons_min_size.setRange(0, 1_000_000)
        self.find_neurons_min_size.setValue(45)
        self.tab3.layout.addWidget(self.find_neurons_min_size, 4, 2)

        self.prune_unconnected_segments = QPushButton(
            "Prune unconnected segments (run 'Find neurons' first)")
        self.prune_unconnected_segments.clicked.connect(
            self.prune_neuron_unconnected_segments)
        self.tab3.layout.addWidget(self.prune_unconnected_segments, 4, 3)

        label6_tab3 = QLabel('Step 5: Save')
        self.tab3.layout.addWidget(label6_tab3, 5, 0)

        label5_tab3 = QLabel('Step 6:')
        self.tab3.layout.addWidget(label5_tab3, 6, 0)

        self.create_n_save_bonds = QPushButton("Segment dendrites")
        self.create_n_save_bonds.clicked.connect(self.save_segmented_bonds)
        self.tab3.layout.addWidget(self.create_n_save_bonds, 6, 1)

        self.tab3.setLayout(self.tab3.layout)

        # Add tabs to widget
        table_widget_layout.addWidget(self.tabs)
        self.table_widget.setLayout(table_widget_layout)

        self.Stack = QStackedWidget(self)
        self.Stack.addWidget(self.scrollArea)

        # create a grid that will contain all the GUI interface
        self.grid = QGridLayout()
        self.grid.addWidget(self.Stack, 0, 0)
        self.grid.addWidget(self.list, 0, 1)
        # The first parameter of the rowStretch method is the row number, the second is the stretch factor. So you need two calls to rowStretch, like this: --> below the first row is occupying 80% and the second 20%
        self.grid.setRowStretch(0, 75)
        self.grid.setRowStretch(2, 25)

        # first col 75% second col 25% of total width
        self.grid.setColumnStretch(0, 75)
        self.grid.setColumnStretch(1, 25)

        # void QGridLayout::addLayout(QLayout * layout, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment = 0)
        self.grid.addWidget(self.table_widget, 2, 0, 1,
                            2)  # spans over one row and 2 columns

        # BEGIN TOOLBAR
        # pen spin box and connect
        self.penSize = QSpinBox()
        self.penSize.setSingleStep(1)
        self.penSize.setRange(1, 256)
        self.penSize.setValue(3)
        self.penSize.valueChanged.connect(self.penSizechange)

        self.channels = QComboBox()
        self.channels.addItem("merge")
        self.channels.currentIndexChanged.connect(self.channelChange)

        tb_drawing_pane = QToolBar()

        save_button = QToolButton()
        save_button.setText("Save")
        save_button.clicked.connect(self.save_current_mask)
        tb_drawing_pane.addWidget(save_button)

        tb_drawing_pane.addWidget(QLabel("Channels"))
        tb_drawing_pane.addWidget(self.channels)

        # tb.addAction("Save")
        #
        tb_drawing_pane.addWidget(QLabel("Pen size"))
        tb_drawing_pane.addWidget(self.penSize)

        self.grid.addWidget(tb_drawing_pane, 1, 0)
        # END toolbar

        # toolbar for the list
        tb_list = QToolBar()

        del_button = QToolButton()
        del_button.setText("Delete selection from list")
        del_button.clicked.connect(self.delete_from_list)
        tb_list.addWidget(del_button)

        self.grid.addWidget(tb_list, 1, 1)

        # self.setCentralWidget(self.scrollArea)
        self.setCentralWidget(QFrame())
        self.centralWidget().setLayout(self.grid)

        # self.statusBar().showMessage('Ready')
        statusBar = self.statusBar(
        )  # sets an empty status bar --> then can add messages in it
        self.paint.statusBar = statusBar

        # add progress bar to status bar
        self.progress = QProgressBar(self)
        self.progress.setGeometry(200, 80, 250, 20)
        statusBar.addWidget(self.progress)

        # Set up menu bar
        self.mainMenu = self.menuBar()

        self.zoomInAct = QAction(
            "Zoom &In (25%)",
            self,  # shortcut="Ctrl++",
            enabled=True,
            triggered=self.zoomIn)
        self.zoomOutAct = QAction(
            "Zoom &Out (25%)",
            self,  # shortcut="Ctrl+-",
            enabled=True,
            triggered=self.zoomOut)
        self.normalSizeAct = QAction(
            "&Normal Size",
            self,  # shortcut="Ctrl+S",
            enabled=True,
            triggered=self.defaultSize)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)

        self.menuBar().addMenu(self.viewMenu)

        self.setMenuBar(self.mainMenu)

        # set drawing window fullscreen
        fullScreenShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_F), self)
        fullScreenShortcut.activated.connect(self.fullScreen)
        fullScreenShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        escapeShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Escape), self)
        escapeShortcut.activated.connect(self.escape)
        escapeShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        # Show/Hide the mask
        escapeShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_H), self)
        escapeShortcut.activated.connect(self.showHideMask)
        escapeShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        zoomPlus = QtWidgets.QShortcut("Ctrl+Shift+=", self)
        zoomPlus.activated.connect(self.zoomIn)
        zoomPlus.setContext(QtCore.Qt.ApplicationShortcut)

        zoomPlus2 = QtWidgets.QShortcut("Ctrl++", self)
        zoomPlus2.activated.connect(self.zoomIn)
        zoomPlus2.setContext(QtCore.Qt.ApplicationShortcut)

        zoomMinus = QtWidgets.QShortcut("Ctrl+Shift+-", self)
        zoomMinus.activated.connect(self.zoomOut)
        zoomMinus.setContext(QtCore.Qt.ApplicationShortcut)

        zoomMinus2 = QtWidgets.QShortcut("Ctrl+-", self)
        zoomMinus2.activated.connect(self.zoomOut)
        zoomMinus2.setContext(QtCore.Qt.ApplicationShortcut)

        spaceShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Space), self)
        spaceShortcut.activated.connect(self.nextFrame)
        spaceShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        backspaceShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Backspace), self)
        backspaceShortcut.activated.connect(self.prevFrame)
        backspaceShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        # connect enter keys to edit dendrites
        enterShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Return), self)
        enterShortcut.activated.connect(self.runSkel)
        enterShortcut.setContext(QtCore.Qt.ApplicationShortcut)
        enter2Shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Enter), self)
        enter2Shortcut.activated.connect(self.runSkel)
        enter2Shortcut.setContext(QtCore.Qt.ApplicationShortcut)

        #Qt.Key_Enter is the Enter located on the keypad:
        #Qt::Key_Return  0x01000004
        #Qt::Key_Enter   0x01000005  Typically located on the keypad.

        self.setAcceptDrops(True)  # KEEP IMPORTANT

    def __get_mask_img_from_overlay(self):
        if self.paint.imageDraw:
            channels_count = 4
            s = self.paint.imageDraw.bits().asstring(
                self.img.shape[0] * self.img.shape[1] * channels_count)
            arr = np.frombuffer(s, dtype=np.uint8).reshape(
                (self.img.shape[0], self.img.shape[1], channels_count))
            return Img(arr[..., 2].copy(), dimensions='hw')
        else:
            return None

    def __get_output_folder(self):
        selected_items = self.list.selectedItems()
        if selected_items:
            filename = selected_items[0].toolTip()
            filename0_without_ext = os.path.splitext(filename)[0]
            return filename0_without_ext
        else:
            return None

    def delete_from_list(self):
        list_items = self.list.selectedItems()
        # empty list --> nothing to do
        if not list_items:
            return
        for item in list_items:
            self.list.takeItem(self.list.row(item))

    def save_current_mask(self):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            logger.error('No image is selected --> nothing to save')
            return
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to save')
            return
        if self.tabs.currentIndex() == 0:
            print('saving', os.path.join(output_folder, 'mask.tif'))
            mask.save(os.path.join(output_folder, 'mask.tif'))
        elif self.tabs.currentIndex() == 1:
            print('saving', os.path.join(output_folder, 'cellBodyMask.tif'))
            mask.save(os.path.join(output_folder, 'cellBodyMask.tif'))
        else:
            print('saving', os.path.join(output_folder, 'handCorrection.tif'))
            mask.save(os.path.join(output_folder, 'handCorrection.tif'))

    def detect_neuronal_cell_body(self):
        try:
            # get image and detect cell body
            mask = detect_cell_body(
                self.img,
                fillHoles=self.fill_holes_up_to.value(),
                denoise=self.min_obj_size_px.value(),
                nbOfErosions=self.nb_erosion_cellbody.value(),
                nbOfDilatations=self.nb_dilation_cellbody.value(),
                extraCutOff=self.extraCutOff_cell_body.value(),
                channel=self.channels.currentText())
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
            else:
                logger.error('Cell body could not be detected')
        except:
            traceback.print_exc()

    def __get_neuronal_mask(self, warn=True):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            if warn:
                logger.error('No image selected --> nothing to do')
            return None
        if os.path.exists(os.path.join(output_folder, 'mask.tif')):
            # NB should I check the nb of channels --> no I don't want to handle externally created files and want people to rely fully on my stuff that has
            return Img(os.path.join(output_folder, 'mask.tif'))
        else:
            if warn:
                logger.error(
                    'Neuronal mask not found --> Please create one in the "Mask neuron" tab first'
                )
        return None

    def __get_corrected_mask(self, warn=True):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            if warn:
                logger.error('No image selected --> nothing to do')
            return None
        if os.path.exists(os.path.join(output_folder, 'handCorrection.tif')):
            return Img(os.path.join(output_folder, 'handCorrection.tif'))
        elif os.path.exists(os.path.join(output_folder,
                                         'mask.tif')) and not warn:
            return Img(os.path.join(output_folder, 'mask.tif'))
        return None

    def __get_cellbody_mask(self, warn=True):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            if warn:
                logger.error('No image selected --> nothing to do')
            return None
        if os.path.exists(os.path.join(output_folder, 'cellBodyMask.tif')):
            # NB should I check the nb of channels --> no I don't want to handle externally created files and want people to rely fully on my stuff that has
            return Img(os.path.join(output_folder, 'cellBodyMask.tif'))
        else:
            if warn:
                logger.error(
                    'Cell body mask not found --> Please create one in the "Mask cell body" tab first'
                )
        return None

    # seems ok for now
    def watershed_segment_the_neuron(self):
        try:
            # get raw image and segment it using the watershed algorithm
            # make it load the neuronal mask
            neuronal_mask = self.__get_neuronal_mask()
            if neuronal_mask is None:
                return
            # TODO should I add autoskel or not
            mask = watershed_segment_neuron(
                self.img,
                neuronal_mask,
                fillSize=self.jSpinner11.value(),
                autoSkel=True,
                first_blur=self.whsed_big_blur.value(),
                second_blur=self.whsed_small_blur.value(),
                min_size=self.wshed_rm_small_cells.value(),
                channel=self.channels.currentText())
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
            else:
                logger.error(
                    'Something went wrong, the neuron could not be segmented, sorry...'
                )
        except:
            traceback.print_exc()

    def save_segmented_bonds(self):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            logger.error('No image is selected --> nothing to save')
            return
        # get mask the find neurons
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        mask = detect_cell_bonds(mask)

        if mask is None:
            logger.error(
                'Could not find dendrites, are you sure a mask is overlayed over the neuron'
            )
            return

        # code for conversion of 24 bits numpy array to an RGB one --> keep and store in Img at some point cause useful
        # convert 24 bits array to RGB
        RGB_mask = np.zeros(shape=(*mask.shape, 3), dtype=np.uint8)
        RGB_mask[..., 2] = mask & 255
        RGB_mask[..., 1] = (mask >> 8) & 255
        RGB_mask[..., 0] = (mask >> 16) & 255

        Img(RGB_mask,
            dimensions='hwc').save(os.path.join(output_folder, 'bonds.tif'))

    def prune_neuron_unconnected_segments(self):
        # get mask the find neurons
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        mask = find_neurons(
            mask,
            neuron_minimum_size_threshold=self.find_neurons_min_size.value(),
            return_unconnected=True)
        if mask is None:
            logger.error(
                'Could not find neurons, are you sure a mask is overlayed over the neuron'
            )
            return

        self.paint.imageDraw = Img(self.createRGBA(mask),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def apply_cell_body_to_skeletonized_mask(self):
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        cell_body_mask = self.__get_cellbody_mask()
        if cell_body_mask is None:
            return
        cell_body_outline = get_cell_body_outline(cell_body_mask, mask)
        if cell_body_outline is None:
            logger.error(
                'Error could not add cell body outline to the neuronal mask...'
            )
            return
        self.paint.imageDraw = Img(self.createRGBA(cell_body_outline),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def find_neurons_in_mask(self):
        # get mask the find neurons
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        mask_copy = mask.copy()
        mask = find_neurons(
            mask,
            neuron_minimum_size_threshold=self.find_neurons_min_size.value())
        if mask is None:
            logger.error(
                'Could not find neurons, are you sure a mask is overlayed over the neuron'
            )
            return

        # we set the red channel, the blue one, the alpha transparency (channel 4) and finally we only allow alpha channel in the two masks regions to keep the rest of the stuff
        final_overlay = np.zeros(shape=(*mask_copy.shape, 4), dtype=np.uint8)
        final_overlay[..., 0] = np.logical_xor(mask, mask_copy).astype(
            np.uint8) * 255  # blue channel
        final_overlay[mask == 0, 0] = 0
        final_overlay[..., 1] = final_overlay[
            ...,
            0]  # green channel # copy the channel to make the stuff appear cyan
        final_overlay[..., 2] = mask_copy  # red channel
        final_overlay[np.logical_or(mask, mask_copy) != 0,
                      3] = 255  # --> need set alpha transparency of the image

        self.paint.imageDraw = Img(final_overlay, dimensions='hwc').getQimage()
        self.paint.update()

    def prune_dendrites(self):

        prune_lgth = self.prune_length.value()
        if prune_lgth <= 0:
            logger.info('prune length is 0 --> nothing to do')
            return

        # get the mask from displayed image
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return

        # see how to get the stuff ????
        mask = prune_dendrites(mask, prune_below=prune_lgth)

        if mask is None:
            logger.error(
                'Could not prune dendrites, are you sure there is a mask ovrlayed on the neuron'
            )
            return

        self.paint.imageDraw = Img(self.createRGBA(mask),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def skeletonize_mask(self):
        # get mask then skeletonize it then return it --> see exactly
        try:
            # get raw image and segment it using the skeletonize algorithm
            # make it load the neuronal mask
            neuronal_mask = self.__get_neuronal_mask()
            if neuronal_mask is None:
                return
            mask = skel_segment_neuronal_mask(neuronal_mask)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
            else:
                logger.error(
                    'Something went wrong, the neuron could not be sekeletonized, sorry...'
                )
        except:
            traceback.print_exc()

    def _onTabChange(self):
        # if tab is changed --> do stuff
        # load files or warn...
        if self.tabs.currentIndex() == 0:
            mask = self.__get_neuronal_mask(warn=False)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
        elif self.tabs.currentIndex() == 1:
            mask = self.__get_cellbody_mask(warn=False)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
        elif self.tabs.currentIndex() == 2:
            mask = self.__get_corrected_mask(warn=False)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()

    def run_threshold_neuron(self):
        try:
            local_or_global = 'global'
            if self.sender() == self.local_threshold:
                local_or_global = 'local'
            elif self.sender() == self.local_n_global_threshold:
                local_or_global = 'local+global'

            mask = threshold_neuron(
                self.img,
                mode=local_or_global,
                blur_method=self.threshold_method.currentText(),
                spin_value=self.extra_value_for_threshold.value(),
                channel=self.channels.currentText())
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
        except:
            traceback.print_exc()

    def channelChange(self, i):
        if self.Stack.currentIndex() == 0:
            if i == 0:
                self.paint.setImage(self.img)
            else:
                channel_img = self.img.imCopy(c=i - 1)
                self.paint.setImage(channel_img)
            self.paint.update()

    def penSizechange(self):
        self.paint.brushSize = self.penSize.value()

    def selectionChanged(self):
        self.paint.maskVisible = True
        selected_items = self.list.selectedItems()
        if selected_items:
            start = timer()
            if self.img is not None:
                # make sure we don't load the image twice
                if selected_items[0].toolTip() != self.img.metadata['path']:
                    self.img = Img(selected_items[0].toolTip())
                    logger.debug("took " + str(timer() - start) +
                                 " secs to load image")
                else:
                    logger.debug("image already loaded --> ignoring")
            else:
                self.img = Img(selected_items[0].toolTip())
                logger.debug("took " + str(timer() - start) +
                             " secs to load image")

        if self.img is not None:
            selection = self.channels.currentIndex()
            self.channels.disconnect()
            self.channels.clear()
            comboData = ['merge']
            if self.img.has_c():
                for i in range(self.img.get_dimension('c')):
                    comboData.append(str(i))
            logger.debug('channels found ' + str(comboData))
            self.channels.addItems(comboData)
            if selection != -1 and selection < self.channels.count():
                self.channels.setCurrentIndex(selection)
            else:
                self.channels.setCurrentIndex(0)
            self.channels.currentIndexChanged.connect(self.channelChange)

        if selected_items:
            self.statusBar().showMessage('Loading ' +
                                         selected_items[0].toolTip())
            selection = self.channels.currentIndex()
            if selection == 0:
                self.paint.setImage(self.img)
            else:
                self.paint.setImage(self.img.imCopy(c=selection - 1))
            self.scaleImage(0)
            self.update()
            self.paint.update()

            if self.list.currentItem() and self.list.currentItem().icon(
            ).isNull():
                logger.debug('Updating icon')
                icon = QIcon(QPixmap.fromImage(self.paint.image))
                pixmap = icon.pixmap(24, 24)
                icon = QIcon(pixmap)
                self.list.currentItem().setIcon(icon)
        else:
            logger.debug("Empty selection")
            self.paint.image = None
            self.scaleImage(0)
            self.update()
            self.paint.update()
            self.img = None
        # try update also the masks if they are available
        try:
            self._onTabChange()
        except:
            pass

    def clearlayout(self, layout):
        for i in reversed(range(layout.count())):
            layout.itemAt(i).widget().setParent(None)

    def showHideMask(self):
        self.paint.maskVisible = not self.paint.maskVisible
        self.paint.update()

    def escape(self):
        if self.Stack.isFullScreen():
            self.fullScreen()

    def fullScreen(self):
        if not self.Stack.isFullScreen():
            self.Stack.setWindowFlags(
                QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint |
                # QtCore.Qt.WindowTitleHint |
                # QtCore.Qt.WindowCloseButtonHint |
                QtCore.Qt.WindowStaysOnTopHint)
            self.Stack.showFullScreen()
        else:
            self.Stack.setWindowFlags(QtCore.Qt.Widget)
            self.grid.addWidget(self.Stack, 0, 0)
            # dirty hack to make it repaint properly --> obviously not all lines below are required but some are --> need test, the last line is key though
            self.grid.update()
            self.Stack.update()
            self.Stack.show()
            self.centralWidget().setLayout(self.grid)
            self.centralWidget().update()
            self.update()
            self.show()
            self.repaint()
            self.Stack.update()
            self.Stack.repaint()
            self.centralWidget().repaint()

    def nextFrame(self):
        idx = self.list.model().index(self.list.currentRow() + 1, 0)
        if idx.isValid():
            self.list.selectionModel().setCurrentIndex(
                idx, QItemSelectionModel.ClearAndSelect)  # SelectCurrent

    def remove_blobs(self):
        blob_size = self.remove_blobs_size.value()
        if blob_size <= 0:
            logger.info('blob size is 0 --> nothing to do')
            return

        # get the mask from displayed image
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to save')
            return

        mask = remove_small_objects(mask.astype(np.bool),
                                    min_size=blob_size,
                                    connectivity=2,
                                    in_place=False)
        # then place back pixels in the mask
        # now set the mask back
        # plt.imshow(mask)
        # plt.show()

        self.paint.imageDraw = Img(self.createRGBA(mask),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def runSkel(self):
        # only allow that for tab 3
        if self.tabs.currentIndex() == 2:
            mask = self.__get_mask_img_from_overlay()
            if mask is None:
                logger.error('No mask/overlay detected --> nothing to do')
                return
            # just skeletonize the image
            mask = skel_segment_neuronal_mask(
                mask, fill_holes=0)  # should I put it to 0 or other things ???
            if mask is None:
                logger.error('Could not skeletonize user edited mask...')
                return

            self.paint.imageDraw = Img(self.createRGBA(mask),
                                       dimensions='hwc').getQimage()
            self.paint.update()

    def createRGBA(self, handCorrection):
        # use pen color to display the mask
        # in fact I need to put the real color
        RGBA = np.zeros((handCorrection.shape[0], handCorrection.shape[1], 4),
                        dtype=np.uint8)
        red = self.paint.drawColor.red()
        green = self.paint.drawColor.green()
        blue = self.paint.drawColor.blue()

        # bug somewhere --> fix it some day --> due to bgra instead of RGBA
        RGBA[handCorrection != 0, 0] = blue  # b
        RGBA[handCorrection != 0, 1] = green  # g
        RGBA[handCorrection != 0, 2] = red  # r
        RGBA[..., 3] = 255  # alpha --> indeed alpha
        RGBA[handCorrection == 0, 3] = 0  # very complex fix some day

        return RGBA

    def prevFrame(self):
        idx = self.list.model().index(self.list.currentRow() - 1, 0)
        if idx.isValid():
            self.list.selectionModel().setCurrentIndex(
                idx, QItemSelectionModel.ClearAndSelect)

    def zoomIn(self):
        self.statusBar().showMessage('Zooming in', msecs=200)
        if self.Stack.currentIndex() == 0:
            self.scaleImage(self.zoom_increment)

    def zoomOut(self):
        self.statusBar().showMessage('Zooming out', msecs=200)
        if self.Stack.currentIndex() == 0:
            self.scaleImage(-self.zoom_increment)

    def defaultSize(self):
        self.paint.adjustSize()
        self.scale = 1.0
        self.scaleImage(0)

    def scaleImage(self, factor):
        self.scale += factor
        if self.paint.image is not None:
            self.paint.resize(self.scale * self.paint.image.size())
        else:
            # no image set size to 0, 0 --> scroll pane will auto adjust
            self.paint.resize(QSize(0, 0))
            self.scale -= factor  # reset zoom

        self.paint.scale = self.scale
        # self.paint.vdp.scale = self.scale

        self.zoomInAct.setEnabled(self.scale < self.max_scaling_factor)
        self.zoomOutAct.setEnabled(self.scale > self.min_scaling_factor)

        # allow DND

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

        # handle DND on drop

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            urls = []
            for url in event.mimeData().urls():
                urls.append(url.toLocalFile())

            for url in urls:
                import os
                item = QListWidgetItem(os.path.basename(url), self.list)
                item.setToolTip(url)
                self.list.addItem(item)
        else:
            event.ignore()