Beispiel #1
0
    class Config(SignalNode.Config):
        """Config widget displayed for BandpassFilter."""
        def __init__(self, parent=None):
            super().__init__(parent=parent)

            # Upper bound ----------------------------------------------------------------------------------------------
            self.lower_bound_enable = QCheckBox()
            self.lower_bound_enable.setChecked(True)
            self.lower_bound_enable.stateChanged.connect(self.updateModel)

            self.lower_bound = QDoubleSpinBox()
            self.lower_bound.valueChanged.connect(self.updateModel)
            self.lower_bound.setMinimum(0)
            self.lower_bound.setMaximum(250)
            self.lower_bound.setSuffix(" Hz")

            layout = QHBoxLayout()
            layout.setContentsMargins(0, 0, 0, 0)
            lower_bound_widget = QWidget()
            lower_bound_widget.setContentsMargins(0, 0, 0, 0)
            lower_bound_widget.setLayout(layout)
            layout.addWidget(self.lower_bound_enable)
            layout.addWidget(self.lower_bound)

            # Lower bound ----------------------------------------------------------------------------------------------
            self.upper_bound_enable = QCheckBox()
            self.upper_bound_enable.setChecked(True)
            self.upper_bound_enable.stateChanged.connect(self.updateModel)

            self.upper_bound = QDoubleSpinBox()
            self.upper_bound.valueChanged.connect(self.updateModel)
            self.upper_bound.setMinimum(0)
            self.upper_bound.setMaximum(250)
            self.upper_bound.setSuffix(" Hz")

            layout = QHBoxLayout()
            layout.setContentsMargins(0, 0, 0, 0)
            upper_bound_widget = QWidget()
            upper_bound_widget.setContentsMargins(0, 0, 0, 0)
            upper_bound_widget.setLayout(layout)
            layout.addWidget(self.upper_bound_enable)
            layout.addWidget(self.upper_bound)

            # Filter type and length -----------------------------------------------------------------------------------
            self.filter_type = QComboBox()
            for name in BandpassFilter.filter_name_to_type:
                self.filter_type.addItem(name)
            self.filter_type.currentTextChanged.connect(self.updateModel)

            self.filter_length = QSpinBox()
            self.filter_length.setMinimum(2)
            self.filter_length.setMaximum(1000000)
            self.filter_length.setValue(1000)
            self.filter_length.valueChanged.connect(self.updateModel)

            self.filter_order = QSpinBox()
            self.filter_order.setRange(1, 4)
            self.filter_order.valueChanged.connect(self.updateModel)

            # ----------------------------------------------------------------------------------------------------------
            layout = QFormLayout()
            layout.addRow("Lower bound:", lower_bound_widget)
            layout.addRow("Upper bound:", upper_bound_widget)
            layout.addRow("Filter type:", self.filter_type)
            layout.addRow("Filter order:", self.filter_order)
            layout.addRow("Filter length:", self.filter_length)
            self.setLayout(layout)

        def updateModel(self):
            n = self.node()
            if n is None:
                return

            if self.lower_bound_enable.isChecked():
                lower_bound = self.lower_bound.value()
            else:
                lower_bound = None

            if self.upper_bound_enable.isChecked():
                upper_bound = self.upper_bound.value()
            else:
                upper_bound = None

            filter_type = n.filter_name_to_type[self.filter_type.currentText()]
            filter_length = self.filter_length.value()
            filter_order = self.filter_order.value()

            n.setLowerBound(lower_bound)
            n.setUpperBound(upper_bound)
            n.setFilterType(filter_type)
            n.setFilterLength(filter_length)
            n.setFilterOrder(filter_order)

        def updateView(self):
            n = self.node()
            if n is None:
                return

            # Prevent view fields from emitting signals while they are updated
            self.lower_bound.blockSignals(True)
            self.upper_bound.blockSignals(True)
            self.filter_type.blockSignals(True)
            self.filter_length.blockSignals(True)
            self.filter_order.blockSignals(True)

            if n.upperBound() is None:
                self.upper_bound_enable.setChecked(False)
            else:
                self.upper_bound_enable.setChecked(True)
                self.upper_bound.setValue(n.upperBound())

            if n.lowerBound() is None:
                self.lower_bound_enable.setChecked(False)
            else:
                self.lower_bound_enable.setChecked(True)
                self.lower_bound.setValue(n.lowerBound())

            self.filter_type.setCurrentText(
                n.filter_type_to_name[n.filterType()])
            self.filter_length.setValue(n.filterLength())
            self.filter_order.setValue(n.filterOrder())

            # Release the block and call adjust
            self.lower_bound.blockSignals(False)
            self.upper_bound.blockSignals(False)
            self.filter_type.blockSignals(False)
            self.filter_length.blockSignals(False)
            self.filter_order.blockSignals(False)
            self._adjust()

        def _adjust(self):
            """Adjust displayed values and limits in response to changes."""
            # Enable spinbox widgets based on their checkbox
            self.lower_bound.setEnabled(self.lower_bound_enable.isChecked())
            self.upper_bound.setEnabled(self.upper_bound_enable.isChecked())

            # Adjust min and max so that lower_bound is never higher than upper_bound
            if self.lower_bound_enable.isChecked():
                self.upper_bound.setMinimum(self.lower_bound.value())
            else:
                self.upper_bound.setMinimum(0)

            if self.upper_bound_enable.isChecked():
                self.lower_bound.setMaximum(self.upper_bound.value())
            else:
                self.lower_bound.setMaximum(250)

            if self.filter_type.currentText() == "Butterworth":
                self.filter_order.setEnabled(True)
            else:
                self.filter_order.setEnabled(False)
Beispiel #2
0
 def __init_advanced_features_widget__(self, parent=None):
     self.__advanced_widget = QGroupBox("Advanced Features Options", parent)
     fixed_layer_check = QCheckBox("Fixed Layer", self.__advanced_widget)
     fixed_layer_check.setChecked(self.dlp_controller.fixed_layer)
     fixed_layer_check.toggled.connect(self.dlp_controller.set_fixed_layer)
     incremental_amplitude_check = QCheckBox("Incremental Amplitude",
                                             self.__advanced_widget)
     incremental_amplitude_check.setChecked(
         self.dlp_controller.incremental_amplitude)
     incremental_amplitude_check.toggled.connect(
         self.dlp_controller.set_incremental_amplitude)
     starting_amplitude_label = QLabel("Starting Amplitude",
                                       self.__advanced_widget)
     starting_amplitude_edit = QSpinBox(self.__advanced_widget)
     starting_amplitude_edit.setMaximum(1000)
     starting_amplitude_edit.setMinimum(0)
     starting_amplitude_edit.setSingleStep(1)
     starting_amplitude_edit.setValue(
         self.dlp_controller.starting_incremental_amplitude)
     starting_amplitude_edit.valueChanged.connect(
         self.dlp_controller.set_starting_incremental_amplitude)
     amplitude_step_label = QLabel("Step Size", self.__advanced_widget)
     amplitude_step_edit = QSpinBox(self.__advanced_widget)
     amplitude_step_edit.setMaximum(1000)
     amplitude_step_edit.setMinimum(0)
     amplitude_step_edit.setSingleStep(1)
     amplitude_step_edit.setValue(
         self.dlp_controller.incremental_step_amplitude)
     amplitude_step_edit.valueChanged.connect(
         self.dlp_controller.set_incremental_step_amplitude)
     incremental_exposure_check = QCheckBox("Incremental Exposure",
                                            self.__advanced_widget)
     incremental_exposure_check.setChecked(
         self.dlp_controller.incremental_exposure)
     incremental_exposure_check.toggled.connect(
         self.dlp_controller.set_incremental_exposure)
     starting_exposure_label = QLabel("Starting Exposure",
                                      self.__advanced_widget)
     starting_exposure_edit = QDoubleSpinBox(self.__advanced_widget)
     starting_exposure_edit.setSuffix(str('ms'))
     starting_exposure_edit.setMaximum(100000)
     starting_exposure_edit.setMinimum(0)
     starting_exposure_edit.setDecimals(1)
     starting_exposure_edit.setSingleStep(0.1)
     starting_exposure_edit.valueChanged.connect(
         self.dlp_controller.set_starting_incremental_exposure)
     starting_exposure_edit.setValue(
         self.dlp_controller.starting_incremental_exposure)
     exposure_step_label = QLabel("Step Size", self.__advanced_widget)
     exposure_step_edit = QDoubleSpinBox(self.__advanced_widget)
     exposure_step_edit.setSuffix(str('ms'))
     exposure_step_edit.setMaximum(100000)
     exposure_step_edit.setMinimum(0)
     exposure_step_edit.setDecimals(1)
     exposure_step_edit.setSingleStep(0.1)
     exposure_step_edit.valueChanged.connect(
         self.dlp_controller.set_incremental_step_exposure)
     exposure_step_edit.setValue(
         self.dlp_controller.incremental_step_exposure)
     incremental_thickness_check = QCheckBox("Incremental Thickness",
                                             self.__advanced_widget)
     incremental_thickness_check.setChecked(
         self.dlp_controller.incremental_thickness)
     incremental_thickness_check.toggled.connect(
         self.dlp_controller.set_incremental_thickness)
     thickness_label = QLabel("Starting Thickness", self.__features_widget)
     self.starting_thickness_edit = MyDiscreteStepsSpinBox(
         self.dlp_controller.get_step_length_microns(),
         self.__features_widget)
     self.starting_thickness_edit.setSuffix(str('\u03BCm'))
     self.starting_thickness_edit.setMaximum(1000000)
     self.starting_thickness_edit.setMinimum(0)
     self.starting_thickness_edit.setDecimals(3)
     self.starting_thickness_edit.my_value_changed_signal.connect(
         self.dlp_controller.set_starting_incremental_thickness)
     self.starting_thickness_edit.setValue(
         self.dlp_controller.starting_incremental_thickness * 1000)
     thickness_step_label = QLabel("Step Size", self.__features_widget)
     self.thickness_step_edit = MyDiscreteStepsSpinBox(
         self.dlp_controller.get_step_length_microns(),
         self.__features_widget)
     self.thickness_step_edit.setSuffix(str('\u03BCm'))
     self.thickness_step_edit.setMaximum(1000000)
     self.thickness_step_edit.setMinimum(0)
     self.thickness_step_edit.setDecimals(3)
     self.thickness_step_edit.my_value_changed_signal.connect(
         self.dlp_controller.set_incremental_step_thickness)
     self.thickness_step_edit.setValue(
         self.dlp_controller.incremental_step_thickness * 1000)
     apply_grayscale_correction_check = QCheckBox("Grayscale Correction",
                                                  self.__advanced_widget)
     apply_grayscale_correction_check.setChecked(
         self.dlp_controller.grayscale_correction)
     apply_grayscale_correction_check.toggled.connect(
         self.dlp_controller.set_grayscale_correction)
     grayscale_parameters_widget = QWidget(self.__features_widget)
     a_parameter_label = QLabel("\u03B1", grayscale_parameters_widget)
     alpha_parameter_edit = QDoubleSpinBox(grayscale_parameters_widget)
     alpha_parameter_edit.setMaximum(1000)
     alpha_parameter_edit.setMinimum(0)
     alpha_parameter_edit.setDecimals(3)
     alpha_parameter_edit.setSingleStep(0.001)
     alpha_parameter_edit.valueChanged.connect(
         self.dlp_controller.set_grayscale_alpha)
     alpha_parameter_edit.setValue(self.dlp_controller.grayscale_alpha)
     beta_parameter_label = QLabel("\u03B2", grayscale_parameters_widget)
     beta_parameter_edit = QDoubleSpinBox(grayscale_parameters_widget)
     beta_parameter_edit.setMaximum(1000)
     beta_parameter_edit.setMinimum(0)
     beta_parameter_edit.setDecimals(3)
     beta_parameter_edit.setSingleStep(0.001)
     beta_parameter_edit.valueChanged.connect(
         self.dlp_controller.set_grayscale_beta)
     beta_parameter_edit.setValue(self.dlp_controller.grayscale_beta)
     gamma_parameter_label = QLabel("\u03B3", grayscale_parameters_widget)
     gamma_parameter_edit = QDoubleSpinBox(grayscale_parameters_widget)
     gamma_parameter_edit.setMaximum(1000)
     gamma_parameter_edit.setMinimum(0)
     gamma_parameter_edit.setDecimals(3)
     gamma_parameter_edit.setSingleStep(0.001)
     gamma_parameter_edit.valueChanged.connect(
         self.dlp_controller.set_grayscale_gamma)
     gamma_parameter_edit.setValue(self.dlp_controller.grayscale_gamma)
     grayscale_parameters_layout = QHBoxLayout(grayscale_parameters_widget)
     grayscale_parameters_layout.addWidget(a_parameter_label)
     grayscale_parameters_layout.addWidget(alpha_parameter_edit)
     grayscale_parameters_layout.addWidget(beta_parameter_label)
     grayscale_parameters_layout.addWidget(beta_parameter_edit)
     grayscale_parameters_layout.addWidget(gamma_parameter_label)
     grayscale_parameters_layout.addWidget(gamma_parameter_edit)
     grayscale_parameters_widget.setLayout(grayscale_parameters_layout)
     advanced_features_layout = QGridLayout(self.__advanced_widget)
     advanced_features_layout.addWidget(incremental_amplitude_check, 1, 0,
                                        1, 2)
     advanced_features_layout.addWidget(fixed_layer_check, 1, 3)
     advanced_features_layout.addWidget(starting_amplitude_label, 2, 0)
     advanced_features_layout.addWidget(starting_amplitude_edit, 2, 1)
     advanced_features_layout.addWidget(amplitude_step_label, 2, 2)
     advanced_features_layout.addWidget(amplitude_step_edit, 2, 3)
     advanced_features_layout.addWidget(incremental_exposure_check, 3, 0, 1,
                                        2)
     advanced_features_layout.addWidget(starting_exposure_label, 4, 0)
     advanced_features_layout.addWidget(starting_exposure_edit, 4, 1)
     advanced_features_layout.addWidget(exposure_step_label, 4, 2)
     advanced_features_layout.addWidget(exposure_step_edit, 4, 3)
     advanced_features_layout.addWidget(incremental_thickness_check, 5, 0,
                                        1, 2)
     advanced_features_layout.addWidget(thickness_label, 6, 0)
     advanced_features_layout.addWidget(self.starting_thickness_edit, 6, 1)
     advanced_features_layout.addWidget(thickness_step_label, 6, 2)
     advanced_features_layout.addWidget(self.thickness_step_edit, 6, 3)
     advanced_features_layout.addWidget(apply_grayscale_correction_check, 7,
                                        0, 1, 2)
     advanced_features_layout.addWidget(grayscale_parameters_widget, 8, 0,
                                        1, 4)
     self.__advanced_widget.setLayout(advanced_features_layout)
Beispiel #3
0
class Canvas (QWidget):
    def __init__(self):
        QWidget.__init__(self)

        self.file = "mug.webp"

        self.__img = cv2.imread(self.file)
        self.__mask = np.zeros(1)
        self.original = cv2.imread(self.file)
        self.__thirdChannelMask = np.dstack((self.__mask, self.__mask, self.__mask))

        self.__nseg = 1
        self.__sig = 1
        self.__comp = 1

        self.nSlider = QSlider(orientation=Qt.Horizontal)
        self.sigSlider = QSlider(orientation=Qt.Horizontal)
        self.thicSlider = QSlider(orientation=Qt.Horizontal)

        self.resize_spinbox = QSpinBox(self)
        self.resize_spinbox.setRange(1, 100)
        self.resize_spinbox.setValue(100)
        self.resize_spinbox.setSuffix(" %")

        self.double_spin_width = QDoubleSpinBox(self)
        self.double_spin_width.setSuffix(" px")
        self.double_spin_width.setValue(0)
        self.double_spin_width.setRange(1, 2000)

        self.double_spin_height = QDoubleSpinBox(self)
        self.double_spin_height.setSuffix(" px")
        self.double_spin_height.setValue(0)
        self.double_spin_height.setRange(1, 2000)

        self.zeroModeCheck = QCheckBox("Usar SLIC0")

        self.__highlightcolor = QColor(255, 255, 255)

        self.__transparency = 0.5

        self.__AllColors = [self.__highlightcolor.toTuple()[:3]]

        nLabel = QLabel("Numero de segmentos:")
        sigLabel = QLabel("Sigma:")
        thicLabel = QLabel("Compactação:")
        resizeLabel = QLabel("Fator de resize da image:")
        makssizeLabel = QLabel("Dimensão da mascara de saída:")

        self.__label = QLabel()

        nLabel.setToolTip("O número aproximado de labels da imagem segmentada")
        sigLabel.setToolTip("A largura da Gaussiana")
        thicLabel.setToolTip("Equilibra a proximidade das cores e a proximidade do espaço, maiores valores tornam os Superpixels mais quadrados")

        self.nSlider.setMinimum(1)
        self.nSlider.setMaximum(100)

        self.sigSlider.setMinimum(1)
        self.sigSlider.setMaximum(100)

        self.thicSlider.setMinimum(1)
        self.thicSlider.setMaximum(100)

        glayout1 = QGridLayout()
        glayout1.addWidget(nLabel, 0, 0)
        glayout1.addWidget(self.nSlider, 0, 1)
        glayout1.addWidget(sigLabel, 1, 0)
        glayout1.addWidget(self.sigSlider, 1, 1)
        glayout1.addWidget(thicLabel, 2, 0)
        glayout1.addWidget(self.thicSlider, 2, 1)

        glayout2 = QGridLayout()
        glayout2.addWidget(resizeLabel, 0, 0)
        glayout2.addWidget(self.resize_spinbox, 0, 1)
        glayout2.addWidget(self.zeroModeCheck, 0, 2)
        glayout2.addWidget(makssizeLabel, 1, 0)
        glayout2.addWidget(self.double_spin_width, 1, 1)
        glayout2.addWidget(self.double_spin_height, 1, 2)

        glayout2.setColumnStretch(3, 1)

        self.__label.setAlignment(Qt.AlignLeft | Qt.AlignTop)

        mainlayout = QVBoxLayout()
        mainlayout.addLayout(glayout1)
        mainlayout.addLayout(glayout2)
        mainlayout.addStretch(1)
        mainlayout.addWidget(self.__label)
        mainlayout.addStretch(1)
        mainlayout.setAlignment(Qt.AlignCenter)
        self.setLayout(mainlayout)

        self.nSlider.sliderReleased.connect(self.onNsegChange)
        self.sigSlider.sliderReleased.connect(self.onSigChange)
        self.thicSlider.sliderReleased.connect(self.onCompChange)

        self.__label.mousePressEvent = self.Highlight

        self.resize_spinbox.valueChanged.connect(self.Resize)

        self.open_image(self.__img)

    def getBackground(self):
        mask = self.__thirdChannelMask.copy()
        mask_r = mask[:, :, 2]
        mask_g = mask[:, :, 1]
        mask_b = mask[:, :, 0]

        offImage = list()
        for color in self.__AllColors:
            b_off = mask_b != color[2]
            g_off = mask_g != color[1]
            r_off = mask_r != color[0]
            aux = np.logical_and(b_off, g_off)
            offImage.append(np.logical_and(aux, r_off))

        final = offImage[0]
        for cut in offImage:
            final = np.logical_or(final, cut)

        return final

    def changeImage(self):
        self.__mask = slic(self.__img, n_segments=self.__nseg, compactness=self.__comp, sigma=self.__sig, convert2lab=True, slic_zero=self.zeroModeCheck.isChecked())

        mask = self.__mask.copy()
        mask = np.dstack((mask, mask, mask))
        mask = img_as_ubyte(mask)

        self.__thirdChannelMask = mask
        img = cv2.addWeighted(self.__img, 1, mask, 0.5, 0)
        marc_img = mark_boundaries(img, self.__mask)
        self.open_image(marc_img)

    def load_image(self):
        self.__img = cv2.imread(self.file)
        self.original = self.__img
        self.double_spin_width.setValue(self.__img.shape[1])
        self.double_spin_height.setValue(self.__img.shape[0])

        val = self.resize_spinbox.value()
        newDim = int(self.__img.shape[1]*val/100), int(self.__img.shape[0]*val/100)

        self.__img = cv2.resize(self.__img, newDim)

        self.open_image(self.__img)

    def open_image(self, img):
        if img.shape[2] == 4:
            qformat = QImage.Format_RGBA8888
        else:
            qformat = QImage.Format_RGB888

        copy = img_as_ubyte(img)
        qimg = QImage(copy.data, copy.shape[1], copy.shape[0], copy.strides[0], qformat).rgbSwapped()
        pixmap = QPixmap.fromImage(qimg)

        self.__label.setPixmap(pixmap)
        self.__label.adjustSize()

    @Slot()
    def onNsegChange(self):
        self.__nseg = self.nSlider.value()
        self.changeImage()

    @Slot()
    def onSigChange(self):
        self.__sig = self.sigSlider.value()
        self.changeImage()

    @Slot()
    def onCompChange(self):
        self.__comp = self.thicSlider.value()
        self.changeImage()

    @Slot()
    def onFileOpen(self):
        self.thicSlider.setValue(1)
        self.nSlider.setValue(1)
        self.sigSlider.setValue(1)
        diag = QFileDialog()
        file = diag.getOpenFileName()[0]
        if file != "":
            self.file = file
            self.load_image()

    @Slot()
    def onSaveFile(self):
        diag = QFileDialog()
        file = diag.getSaveFileName()[0]
        if self.file != "":
            self.__label.pixmap().save(file)

    @Slot()
    def onSaveMask(self):
        diag = QFileDialog()
        file = diag.getSaveFileName()[0]
        final_img = cv2.resize(self.__mask, (self.double_spin_width.value(), self.double_spin_height.value()))

        if file != "":
            cv2.imwrite(file, final_img)

    @Slot()
    def Highlight(self, e):
        if e.x() < 0 or e.x() > self.__img.shape[1] or e.y() < 0 or e.y() > self.__img.shape[0]:
            return

        self.__mask = flood_fill(self.__mask, (e.y(), e.x()), 255)

        self.__thirdChannelMask[:, :, 2] = flood_fill(self.__thirdChannelMask[:, :, 2], (e.y(), e.x()), self.__highlightcolor.red())
        self.__thirdChannelMask[:, :, 1] = flood_fill(self.__thirdChannelMask[:, :, 1], (e.y(), e.x()), self.__highlightcolor.green())
        self.__thirdChannelMask[:, :, 0] = flood_fill(self.__thirdChannelMask[:, :, 0], (e.y(), e.x()), self.__highlightcolor.blue())

        img = cv2.addWeighted(self.__img, 1, self.__thirdChannelMask, self.__transparency, 0)
        marc_img = mark_boundaries(img, self.__mask)
        self.open_image(marc_img)


    @Slot()
    def exportBinary(self):
        diag = QFileDialog()
        file = diag.getSaveFileName()[0]
        mask = self.__thirdChannelMask.copy()
        final = self.getBackground()
        b = mask[:, :, 0]
        g = mask[:, :, 1]
        r = mask[:, :, 2]
        b[final] = 0
        g[final] = 0
        r[final] = 0

        final_img = cv2.resize(mask, (int(self.double_spin_width.value()), int(self.double_spin_height.value())))
        if file != "":
            cv2.imwrite(file, final_img)

    @Slot()
    def onRemoveBackgroud(self):
        box = QMessageBox()
        box.setText("Selecione a cor do background")
        box.setIcon(QMessageBox.Information)
        box.exec()
        diag = QColorDialog()
        backColor = diag.getColor()

        final = self.getBackground()
        b = self.__img[:, :, 0]
        g = self.__img[:, :, 1]
        r = self.__img[:, :, 2]
        b[final] = backColor.blue()
        g[final] = backColor.green()
        r[final] = backColor.red()

        self.open_image(self.__img)

    @Slot()
    def Resize(self):
        val = self.resize_spinbox.value()
        newDim = int(self.original.shape[1] * val / 100), int(self.original.shape[0] * val / 100)
        self.__img = cv2.resize(self.original, newDim)
        self.open_image(self.__img)

    @Slot()
    def setHighlightColor(self, color):
        self.__highlightcolor = color

    @Slot()
    def getAllColors(self, colors):
        self.__AllColors = colors

    @Slot()
    def setTran(self, value):
        self.__transparency = 1- value/100

    @Slot()
    def onUndo(self):
        self.thicSlider.setValue(1)
        self.nSlider.setValue(1)
        self.sigSlider.setValue(1)
        self.onNsegChange()
        self.onSigChange()
        self.onCompChange()
        self.__img = self.original
        self.open_image(self.__img)
Beispiel #4
0
 def __init_projector_widget__(self, parent=None):
     self.__projector_widget = QGroupBox("Projector Options", parent)
     # self.__projector_widget.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
     mirror_x = QCheckBox("Mirror X")
     mirror_x.setChecked(self.dlp_controller.is_horizontal_mirrored())
     mirror_x.toggled.connect(self.dlp_controller.set_horizontal_mirroring)
     mirror_y = QCheckBox("Mirror Y")
     mirror_y.setChecked(self.dlp_controller.is_vertical_mirrored())
     mirror_y.toggled.connect(self.dlp_controller.set_vertical_mirroring)
     start_projector_button = QPushButton("Start Projector",
                                          self.__projector_widget)
     start_projector_button.clicked.connect(
         self.dlp_controller.start_projector)
     project_pattern_button = QPushButton("Project Pattern",
                                          self.__projector_widget)
     project_pattern_button.clicked.connect(
         self.dlp_controller.project_calibration_pattern)
     print_position_button = QPushButton("Print Position",
                                         self.__projector_widget)
     print_position_button.clicked.connect(
         self.dlp_controller.print_motor_position)
     home_projector_button = QPushButton("Home Projector",
                                         self.__projector_widget)
     home_projector_button.clicked.connect(
         self.dlp_controller.home_projector)
     move_projector_button = QPushButton("Move Projector",
                                         self.__projector_widget)
     move_projector_button.clicked.connect(
         self.dlp_controller.move_projector)
     move_projector_edit = QDoubleSpinBox(self.__projector_widget)
     move_projector_edit.setSuffix("mm")
     move_projector_edit.setMaximum(10000)
     move_projector_edit.setMinimum(-10000)
     move_projector_edit.setDecimals(3)
     move_projector_edit.setSingleStep(0.001)
     move_projector_edit.valueChanged.connect(
         self.dlp_controller.update_projector_distance)
     set_amplitude_button = QPushButton("Set Projector Amplitude",
                                        self.__projector_widget)
     set_amplitude_button.clicked.connect(
         self.dlp_controller.set_projector_amplitude)
     set_amplitude_edit = QSpinBox(self.__projector_widget)
     set_amplitude_edit.setMaximum(1000)
     set_amplitude_edit.setMinimum(0)
     set_amplitude_edit.setValue(self.dlp_controller.projector_amplitude)
     set_amplitude_edit.valueChanged.connect(
         self.dlp_controller.update_projector_amplitude)
     lock_unlock_projector_button = QPushButton("Lock/Unlock Projector",
                                                self.__projector_widget)
     lock_unlock_projector_button.clicked.connect(
         self.dlp_controller.lock_unlock_projector)
     projector_layout = QGridLayout(self.__projector_widget)
     # projector_layout.addWidget(projector_label, 0, 0)
     projector_layout.addWidget(mirror_x, 0, 1)
     projector_layout.addWidget(mirror_y, 0, 2)
     projector_layout.addWidget(start_projector_button, 1, 0, 1, 1)
     projector_layout.addWidget(project_pattern_button, 1, 1, 1, 1)
     projector_layout.addWidget(print_position_button, 1, 2, 1, 1)
     projector_layout.addWidget(home_projector_button, 2, 0, 1, 1)
     projector_layout.addWidget(move_projector_button, 2, 1, 1, 1)
     projector_layout.addWidget(move_projector_edit, 2, 2)
     projector_layout.addWidget(set_amplitude_button, 3, 0, 1, 1)
     projector_layout.addWidget(set_amplitude_edit, 3, 1)
     projector_layout.addWidget(lock_unlock_projector_button, 3, 2, 1, 1)
     self.__projector_widget.setLayout(projector_layout)
Beispiel #5
0
 def __init_features_widget__(self, parent=None):
     self.__features_widget = QGroupBox("Features Layers Parameters",
                                        parent)
     # self.__features_widget.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
     thickness_label = QLabel("Layer Thickness", self.__features_widget)
     self.features_thickness_edit = MyDiscreteStepsSpinBox(
         self.dlp_controller.get_step_length_microns(),
         self.__features_widget)
     self.features_thickness_edit.setSuffix(str('\u03BCm'))
     self.features_thickness_edit.setMaximum(1000000)
     self.features_thickness_edit.setMinimum(0)
     self.features_thickness_edit.setDecimals(3)
     self.features_thickness_edit.my_value_changed_signal.connect(
         self.dlp_controller.set_features_thickness)
     self.features_thickness_edit.setValue(
         self.dlp_controller.features_thickness * 1000)
     exposure_label = QLabel("Exposure Time", self.__features_widget)
     exposure_edit = QDoubleSpinBox(self.__features_widget)
     exposure_edit.setSuffix(str('ms'))
     exposure_edit.setMaximum(10000000)
     exposure_edit.setMinimum(0)
     exposure_edit.setDecimals(1)
     exposure_edit.setSingleStep(0.1)
     exposure_edit.setValue(self.dlp_controller.features_exposure)
     exposure_edit.valueChanged.connect(
         self.dlp_controller.set_features_exposure_time)
     amplitude_label = QLabel("Light Amplitude", self.__features_widget)
     amplitude_edit = QSpinBox(self.__features_widget)
     amplitude_edit.setMaximum(1600)
     amplitude_edit.setMinimum(0)
     amplitude_edit.setSingleStep(1)
     amplitude_edit.setValue(self.dlp_controller.features_amplitude)
     amplitude_edit.valueChanged.connect(
         self.dlp_controller.set_features_amplitude)
     burn_layers_label = QLabel("Burn Layers", self.__features_widget)
     burn_layers_edit = QSpinBox(self.__features_widget)
     burn_layers_edit.setMaximum(1000)
     burn_layers_edit.setMinimum(0)
     burn_layers_edit.setSingleStep(1)
     burn_layers_edit.setValue(self.dlp_controller.features_burn_layers)
     burn_layers_edit.valueChanged.connect(
         self.dlp_controller.set_features_burning_layers)
     burn_exposure_label = QLabel("Burn Exposure", self.__features_widget)
     burn_exposure_edit = QDoubleSpinBox(self.__features_widget)
     burn_exposure_edit.setSuffix(str('ms'))
     burn_exposure_edit.setMaximum(100000)
     burn_exposure_edit.setMinimum(0)
     burn_exposure_edit.setDecimals(1)
     burn_exposure_edit.setSingleStep(0.1)
     burn_exposure_edit.setValue(self.dlp_controller.features_burn_exposure)
     burn_exposure_edit.valueChanged.connect(
         self.dlp_controller.set_features_burning_exposure_time)
     burn_amplitude_label = QLabel("Burn Amplitude", self.__features_widget)
     burn_amplitude_edit = QSpinBox(self.__features_widget)
     burn_amplitude_edit.setMaximum(1600)
     burn_amplitude_edit.setMinimum(0)
     burn_amplitude_edit.setSingleStep(1)
     burn_amplitude_edit.setValue(
         self.dlp_controller.features_burn_amplitude)
     burn_amplitude_edit.valueChanged.connect(
         self.dlp_controller.set_features_burning_amplitude)
     select_layers_button = QPushButton("Select Features Images")
     select_layers_button.clicked.connect(self.load_features_images)
     features_layout = QGridLayout(self.__features_widget)
     features_layout.addWidget(thickness_label, 1, 0)
     features_layout.addWidget(self.features_thickness_edit, 1, 1)
     features_layout.addWidget(exposure_label, 2, 0)
     features_layout.addWidget(exposure_edit, 2, 1)
     features_layout.addWidget(amplitude_label, 3, 0)
     features_layout.addWidget(amplitude_edit, 3, 1)
     features_layout.addWidget(burn_layers_label, 4, 0)
     features_layout.addWidget(burn_layers_edit, 4, 1)
     features_layout.addWidget(burn_exposure_label, 5, 0)
     features_layout.addWidget(burn_exposure_edit, 5, 1)
     features_layout.addWidget(burn_amplitude_label, 6, 0)
     features_layout.addWidget(burn_amplitude_edit, 6, 1)
     features_layout.addWidget(select_layers_button, 7, 0, 1, 2)
     self.__features_widget.setLayout(features_layout)
Beispiel #6
0
    def __init_slicer_options_widget__(self):
        self.__slicer_options_widget = QGroupBox("Slicer Options", self)

        thickness_label = QLabel("Layer Thickness",
                                 self.__slicer_options_widget)
        thickness_edit = QDoubleSpinBox(self.__slicer_options_widget)
        thickness_edit.setSuffix(str('\u03BCm'))
        thickness_edit.setMaximum(1000000)
        thickness_edit.setMinimum(0)
        thickness_edit.setDecimals(3)
        thickness_edit.setSingleStep(0.001)
        thickness_edit.setValue(self.dlp_controller.support_thickness * 1000)
        # self.__opengl_widget.set_slice_thickness(self.dlp_controller.support_thickness)
        thickness_edit.valueChanged.connect(
            self.__slicer_widget.set_slice_thickness)

        pixel_size_label = QLabel("Projector Pixel Size",
                                  self.__slicer_options_widget)
        pixel_size_edit = QDoubleSpinBox(self.__slicer_options_widget)
        pixel_size_edit.setSuffix(str('\u03BCm'))
        pixel_size_edit.setMaximum(1000000)
        pixel_size_edit.setMinimum(0)
        pixel_size_edit.setDecimals(2)
        pixel_size_edit.setSingleStep(0.01)
        pixel_size_edit.setValue(self.dlp_controller.projector_pixel_size *
                                 1000)
        pixel_size_edit.valueChanged.connect(
            self.__slicer_widget.set_pixel_size)

        projector_resolution_label = QLabel("Projector Resolution",
                                            self.__slicer_options_widget)
        projector_resolution_edit_x = QSpinBox(self.__slicer_options_widget)
        projector_resolution_edit_x.setSuffix(str('W'))
        projector_resolution_edit_x.setMaximum(1000000)
        projector_resolution_edit_x.setMinimum(0)
        projector_resolution_edit_x.setValue(
            self.dlp_controller.projector_width)
        projector_resolution_edit_x.valueChanged.connect(
            self.__slicer_widget.set_projector_width)
        projector_resolution_edit_y = QSpinBox(self.__slicer_options_widget)
        projector_resolution_edit_y.setSuffix(str('H'))
        projector_resolution_edit_y.setMaximum(1000000)
        projector_resolution_edit_y.setMinimum(0)
        projector_resolution_edit_y.setValue(
            self.dlp_controller.projector_height)
        projector_resolution_edit_y.valueChanged.connect(
            self.__slicer_widget.set_projector_height)

        samples_per_pixel_label = QLabel("Samples per Pixel",
                                         self.__slicer_options_widget)
        samples_per_pixel_edit = QSpinBox(self.__slicer_options_widget)
        samples_per_pixel_edit.setMaximum(1000000)
        samples_per_pixel_edit.setMinimum(1)
        samples_per_pixel_edit.setValue(self.dlp_controller.samples_per_pixel)
        samples_per_pixel_edit.valueChanged.connect(
            self.__slicer_widget.set_samples_per_pixel)

        slice_geometry_button = QPushButton("Slice Geometry")
        slice_geometry_button.clicked.connect(self.start_slicing_process)

        slice_interrupt_button = QPushButton("Stop Slicing")
        slice_interrupt_button.clicked.connect(
            self.__slicer_widget.interrupt_slicing)

        self.slices_label = QLabel(f'Slicing progress: {0:.0f}/{0:.0f}',
                                   self.__info_widget)

        thickness_label_row = 0
        pixel_size_row = 1
        projector_resolution_row = 2
        samples_per_pixel_row = 3
        slice_button_row = 4
        slices_label_row = 5
        # slice_interrupt_row = slice_button_row

        slice_layout = QGridLayout(self.__slicer_options_widget)
        slice_layout.addWidget(thickness_label, thickness_label_row, 0)
        slice_layout.addWidget(thickness_edit, thickness_label_row, 1)
        slice_layout.addWidget(pixel_size_label, pixel_size_row, 0)
        slice_layout.addWidget(pixel_size_edit, pixel_size_row, 1)
        slice_layout.addWidget(projector_resolution_label,
                               projector_resolution_row, 0)
        slice_layout.addWidget(projector_resolution_edit_x,
                               projector_resolution_row, 1)
        slice_layout.addWidget(projector_resolution_edit_y,
                               projector_resolution_row, 2)
        slice_layout.addWidget(self.slices_label, slice_button_row, 0)
        slice_layout.addWidget(slice_geometry_button, slice_button_row, 1)
        slice_layout.addWidget(slice_interrupt_button, slice_button_row, 2)
        slice_layout.addWidget(samples_per_pixel_label, samples_per_pixel_row,
                               0)
        slice_layout.addWidget(samples_per_pixel_edit, samples_per_pixel_row,
                               1)
        self.__slicer_options_widget.setLayout(slice_layout)
class BlockView(QWidget):
    """Config widget for a single experiment block.  
    In a model-view paradigm, this is a view, and block is a model. A new block can be set using setModel.
    """
    statistics_type_to_name = {
        "max": "Min/Max",
        "meanstd": "Standardise"
    }
    statistics_name_to_type = {v: k for k, v in statistics_type_to_name.items()}

    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QFormLayout()
        self.setLayout(layout)

        self._model = None

        # Block properties ---------------------------------------------------------------------------------------------
        self.duration = QWidget()
        self.duration.setContentsMargins(0, 0, 0, 0)
        ly = QHBoxLayout()
        ly.setContentsMargins(0, 0, 0, 0)
        self.duration.setLayout(ly)

        self.duration_base = QDoubleSpinBox()
        self.duration_base.setRange(0, 1000)
        self.duration_base.setValue(10)

        self.duration_deviation = QDoubleSpinBox()
        self.duration_deviation.setRange(0, 10)
        self.duration_deviation.setValue(0)
        self.duration_deviation.setSingleStep(0.1)
        self.duration_deviation.setSuffix(" s")
        self.duration_base.valueChanged.connect(self.duration_deviation.setMaximum)

        ly.addWidget(self.duration_base)
        ly.addWidget(QLabel("±"))
        ly.addWidget(self.duration_deviation)

        self.feedback_source = QLineEdit("All")

        self.feedback_type = QComboBox()
        self.feedback_type.addItem("Baseline")
        self.feedback_type.addItem("Feedback")

        self.mock_signal_path = QLineEdit()
        self.mock_signal_dataset = QLineEdit()
        self.mock_previous = QSpinBox()
        self.mock_previous_reverse = QCheckBox()
        self.mock_previous_random = QCheckBox()
        self.pause = QCheckBox()
        self.beep = QCheckBox()
        self.start_data_driven_filter_designer = QCheckBox()
        self.update_statistics = QCheckBox()
        self.update_statistics.stateChanged.connect(lambda state: self.statistics_type.setEnabled(bool(state)))
        self.statistics_type = QComboBox()
        self.statistics_type.setEnabled(False)
        for name in self.statistics_name_to_type:
            self.statistics_type.addItem(name)
        self.statistics_type.setCurrentText("Standardise")

        self.random_bound = QComboBox()
        self.random_bound.addItem("SimCircle")
        self.random_bound.addItem("RandomCircle")
        self.random_bound.addItem("Bar")

        self.video_path = QLineEdit()
        
        self.message = QLineEdit()
        self.feedback_type.currentTextChanged.connect(lambda ftype: self.message.setEnabled(ftype == "Baseline"))

        self.voiceover = QCheckBox()

        # Grouped properties -------------------------------------------------------------------------------------------
        # Mock signal
        mock_signal_groupbox = QGroupBox("Mock signal")
        mock_signal_gblayout = QFormLayout()
        mock_signal_groupbox.setLayout(mock_signal_gblayout)
        mock_signal_gblayout.addRow("Mock signal file path", self.mock_signal_path)
        mock_signal_gblayout.addRow("Mock signal file dataset", self.mock_signal_dataset)
        mock_signal_gblayout.addRow("Mock previous", self.mock_previous)
        mock_signal_gblayout.addRow("Reverse mock previous", self.mock_previous_reverse)
        mock_signal_gblayout.addRow("Random mock previous", self.mock_previous_random)

        # After block actions
        after_block_groupbox = QGroupBox("After block actions")
        after_block_gblayout = QFormLayout()
        after_block_groupbox.setLayout(after_block_gblayout)
        after_block_gblayout.addRow("Start data driven filter designer", self.start_data_driven_filter_designer)
        after_block_gblayout.addRow("Pause", self.pause)
        after_block_gblayout.addRow("Beep", self.beep)
        after_block_gblayout.addRow("Update statistics", self.update_statistics)
        after_block_gblayout.addRow("Statistics type", self.statistics_type)

        # Adding properties to the widget ------------------------------------------------------------------------------
        layout.addRow("Duration", self.duration)
        layout.addRow("Source", self.feedback_source)
        layout.addRow("FB Type", self.feedback_type)
        layout.addRow("Random bound", self.random_bound)
        layout.addRow("Video path", self.video_path)
        layout.addRow("Message for test subject", self.message)
        layout.addRow("Voiceover for message", self.voiceover)
        layout.addRow(mock_signal_groupbox)
        layout.addRow(after_block_groupbox)

    def model(self):
        return self._model

    def setModel(self, block, /):
        """Set the model block for this view.
        Data in the view will be updated to reflect the new block.
        """
        self._model = block
        self.updateView()
    
    def updateModel(self):
        """Copy data from this view to the block model.
        A similarly named function in the block copies data the opposite way. Use one or the other depending on where
        data was changed.
        """
        model = self.model()
        if model is None:
            return
        
        model.duration = self.duration_base.value()
        model.duration_deviation = self.duration_deviation.value()
        model.feedback_source = self.feedback_source.text()
        model.feedback_type = self.feedback_type.currentText()
        model.random_bound = self.random_bound.currentText()
        model.video_path = self.video_path.text()
        model.message = self.message.text()
        model.voiceover = self.voiceover.isChecked()
        model.mock_signal_path = self.mock_signal_path.text()
        model.mock_signal_dataset = self.mock_signal_dataset.text()
        model.mock_previous = self.mock_previous.value()
        model.mock_previous_reverse = self.mock_previous_reverse.isChecked()
        model.mock_previous_random = self.mock_previous_random.isChecked()
        model.start_data_driven_filter_designer = self.start_data_driven_filter_designer.isChecked()
        model.pause = self.pause.isChecked()
        model.beep = self.beep.isChecked()
        model.update_statistics = self.update_statistics.isChecked()
        model.statistics_type = self.statistics_name_to_type[self.statistics_type.currentText()]

    def updateView(self):
        model = self.model()
        if model is None:
            return
        
        self.duration_base.setValue(model.duration)
        self.duration_deviation.setValue(model.duration_deviation)
        self.feedback_source.setText(model.feedback_source)
        self.feedback_type.setCurrentText(model.feedback_type)
        self.mock_signal_path.setText(model.mock_signal_path)
        self.mock_signal_dataset.setText(model.mock_signal_dataset)
        self.mock_previous.setValue(model.mock_previous)
        self.mock_previous_reverse.setChecked(model.mock_previous_reverse)
        self.mock_previous_random.setChecked(model.mock_previous_random)
        self.pause.setChecked(model.pause)
        self.beep.setChecked(model.beep)
        self.start_data_driven_filter_designer.setChecked(model.start_data_driven_filter_designer)
        self.update_statistics.setChecked(model.update_statistics)
        self.statistics_type.setCurrentText(self.statistics_type_to_name[model.statistics_type])
        self.random_bound.setCurrentText(model.random_bound)
        self.video_path.setText(model.video_path)
        self.message.setText(model.message)
        self.voiceover.setChecked(model.voiceover)
Beispiel #8
0
class ConfigDialog(QDialog):
    config_changed = Signal()

    def __init__(self, requester, config, parent=None):
        super().__init__(parent)
        self.requester = requester
        self.requester.pin_needed.connect(self.input_pin)
        self.requester.umi_made.connect(self.write_umi)
        self.requester.msg_passed.connect(self.error_msg)
        self.config = config
        self.lbl_id = QLabel('계정명')
        self.lbl_id.setAlignment(Qt.AlignCenter)
        self.lbl_pw = QLabel('비밀번호')
        self.lbl_pw.setAlignment(Qt.AlignCenter)
        self.lbl_umi = QLabel('umi 쿠키')
        self.lbl_umi.setAlignment(Qt.AlignCenter)
        self.lbl_ua = QLabel('유저 에이전트')
        self.lbl_ua.setAlignment(Qt.AlignCenter)
        self.lbl_delay = QLabel('저속 간격')
        self.lbl_delay.setAlignment(Qt.AlignCenter)
        self.lbl_msg = QLabel('')
        self.line_id = QLineEdit()
        self.line_pw = QLineEdit()
        self.line_pw.setEchoMode(QLineEdit.PasswordEchoOnEdit)
        self.line_umi = QLineEdit()
        self.line_umi.setPlaceholderText('로그인 시 자동 입력')
        self.line_ua = QLineEdit()
        self.line_delay = QDoubleSpinBox()
        self.line_delay.setMinimum(3)
        self.line_delay.setDecimals(1)
        self.line_delay.setSuffix('초')
        self.line_delay.setSingleStep(0.1)
        self.btn_save = NPButton('저장', 10, self)
        self.btn_save.clicked.connect(self.save)
        self.btn_cancel = NPButton('취소', 10, self)
        self.btn_cancel.clicked.connect(self.reject)
        self.btn_get_umi = NPButton('로그인', 10, self)
        self.btn_get_umi.setSizePolicy(QSizePolicy.Preferred,
                                       QSizePolicy.MinimumExpanding)
        self.btn_get_umi.clicked.connect(self.get_umi)
        grid = QGridLayout()
        grid.addWidget(self.lbl_id, 0, 0, 1, 1)
        grid.addWidget(self.line_id, 0, 1, 1, 6)
        grid.addWidget(self.lbl_pw, 1, 0, 1, 1)
        grid.addWidget(self.line_pw, 1, 1, 1, 6)
        grid.addWidget(self.btn_get_umi, 0, 7, 2, 2)
        grid.addWidget(self.lbl_umi, 2, 0, 1, 1)
        grid.addWidget(self.line_umi, 2, 1, 1, 8)
        grid.addWidget(self.lbl_ua, 3, 0, 1, 1)
        grid.addWidget(self.line_ua, 3, 1, 1, 8)
        grid.addWidget(self.lbl_delay, 4, 0, 1, 1)
        grid.addWidget(self.line_delay, 4, 1, 1, 8)
        grid.addWidget(self.lbl_msg, 5, 0, 1, 4)
        grid.addWidget(self.btn_save, 5, 5, 1, 2)
        grid.addWidget(self.btn_cancel, 5, 7, 1, 2)
        self.setLayout(grid)
        self.input_dialog = InputDialog(self)
        self.input_dialog.input.setInputMask('999999')
        self.setWindowTitle('개인정보')
        self.setWindowIcon(QIcon('icon.png'))
        self.setStyleSheet('font: 10pt \'맑은 고딕\'')
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        self.c_login, self.c_work = {}, {}

    @Slot(str)
    def error_msg(self, t):
        self.lbl_msg.setText(t)

    def load(self):
        self.line_id.setText(self.config.c['login']['ID'])
        self.line_pw.setText(self.config.c['login']['PW'])
        self.line_umi.setText(self.config.c['login']['UMI'])
        self.line_ua.setText(self.config.c['login']['UA'])
        self.line_ua.setCursorPosition(0)
        self.line_delay.setValue(float(self.config.c['work']['DELAY']))
        self.lbl_msg.clear()
        ok = self.exec_()
        if ok == QDialog.Accepted:
            self.config_changed.emit()

    def save(self):
        self.config.save(login={
            'ID': self.line_id.text().strip(),
            'PW': self.line_pw.text().strip(),
            'UMI': self.line_umi.text().strip(),
            'UA': self.line_ua.text().strip()
        },
                         delay=self.line_delay.value())
        self.accept()

    def get_umi(self):
        self.lbl_msg.setText('로그인 시도...')
        self.line_umi.clear()
        self.input_dialog.input.clear()
        self.requester.init_login(self.line_id.text().strip(),
                                  self.line_pw.text().strip())

    @Slot(str)
    def write_umi(self, umi):
        self.line_umi.setText(umi)

    @Slot(str)
    def input_pin(self, mail):
        pin, ok = self.input_dialog.get_text('로그인 PIN 입력',
                                             f'이메일({mail})로 전송된 PIN을 입력해주세요.')
        if ok:
            if pin:
                self.requester.typed_pin = pin
            else:
                self.requester.typed_pin = 'nothing'
        else:
            self.requester.typed_pin = 'deny'
class GeneralView(QWidget):
    """Config widget for general properties of an experiment.
    This "view" does not have a model. Instead, it is a part of a bigger view called ExperimentView, and gets updated
    with it.
    """

    inlet_type_export_values = {
        "LSL stream": "lsl",
        "LSL file stream": "lsl_from_file",
        "LSL generator": "lsl_generator",
        "Field trip buffer": "ftbuffer"
    }
    inlet_type_import_values = {
        v: k
        for k, v in inlet_type_export_values.items()
    }

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        layout = QFormLayout()
        self.setLayout(layout)

        self.name = QLineEdit()

        # prefilter_lower_bound ---------------------------------------------------------------------------------------------
        self.prefilter_lower_bound_enable = QCheckBox()
        self.prefilter_lower_bound_enable.stateChanged.connect(self._adjust)

        self.prefilter_lower_bound = QDoubleSpinBox()
        self.prefilter_lower_bound.setEnabled(False)
        self.prefilter_lower_bound.valueChanged.connect(self._adjust)
        self.prefilter_lower_bound.setMinimum(0)
        self.prefilter_lower_bound.setMaximum(0)  # TODO: add proper value
        self.prefilter_lower_bound.setValue(0)  # TODO: add proper value

        prefilter_lower_bound_widget = QWidget()
        prefilter_lower_bound_widget.setContentsMargins(0, 0, 0, 0)
        prefilter_lower_bound_widget.setLayout(QHBoxLayout())
        prefilter_lower_bound_widget.layout().setContentsMargins(0, 0, 0, 0)
        prefilter_lower_bound_widget.layout().addWidget(
            self.prefilter_lower_bound_enable)
        prefilter_lower_bound_widget.layout().addWidget(
            self.prefilter_lower_bound)

        # prefilter_upper_bound --------------------------------------------------------------------------------------------
        self.prefilter_upper_bound_enable = QCheckBox()
        self.prefilter_upper_bound_enable.stateChanged.connect(self._adjust)

        self.prefilter_upper_bound = QDoubleSpinBox()
        self.prefilter_upper_bound.setEnabled(False)
        self.prefilter_upper_bound.valueChanged.connect(self._adjust)
        self.prefilter_upper_bound.setMinimum(
            self.prefilter_lower_bound.value())
        self.prefilter_upper_bound.setMaximum(10000)  # TODO: add proper value
        self.prefilter_upper_bound.setValue(0)  # TODO: add proper value

        prefilter_upper_bound_widget = QWidget()
        prefilter_upper_bound_widget.setContentsMargins(0, 0, 0, 0)
        prefilter_upper_bound_widget.setLayout(QHBoxLayout())
        prefilter_upper_bound_widget.layout().setContentsMargins(0, 0, 0, 0)
        prefilter_upper_bound_widget.layout().addWidget(
            self.prefilter_upper_bound_enable)
        prefilter_upper_bound_widget.layout().addWidget(
            self.prefilter_upper_bound)

        # Inlet selection ----------------------------------------------------------------------------------------------
        self.inlet_type = QComboBox()
        self.inlet_type.addItem("LSL stream")
        self.inlet_type.addItem("LSL file stream")
        self.inlet_type.addItem("LSL generator")
        self.inlet_type.addItem("Field trip buffer")

        self.lsl_stream_name = QComboBox()
        self.lsl_stream_name.addItem("NVX136_Data")
        self.lsl_stream_name.addItem("Mitsar")

        self.lsl_filename = PathEdit()
        dialog = QFileDialog(self, "Open")
        dialog.setFileMode(dialog.AnyFile)
        self.lsl_filename.setDialog(dialog)
        self.hostname_port = QLineEdit("localhost:1972")

        self.inlet_params = StackedDictWidget()
        self.inlet_params.setMaximumHeight(25)
        self.inlet_params.addWidget("LSL stream", self.lsl_stream_name)
        self.inlet_params.addWidget("LSL file stream", self.lsl_filename)
        self.inlet_params.addWidget("LSL generator", QWidget())
        self.inlet_params.addWidget("Field trip buffer", self.hostname_port)
        # TODO: LSL generator is not reflected in the exported file, even when selected.

        self.inlet_type.currentTextChanged.connect(
            self.inlet_params.setCurrentKey)

        self.inlet_config = QWidget()
        self.inlet_config.setContentsMargins(0, 0, 0, 0)
        inlet_layout = QHBoxLayout()
        inlet_layout.setContentsMargins(0, 0, 0, 0)
        inlet_layout.addWidget(self.inlet_type)
        inlet_layout.addWidget(self.inlet_params)
        self.inlet_config.setLayout(inlet_layout)

        # --------------------------------------------------------------------------------------------------------------
        self.name = QLineEdit("Experiment")
        self.dc = QCheckBox()

        self.plot_raw = QCheckBox()
        self.plot_raw.setChecked(True)
        self.plot_signals = QCheckBox()
        self.plot_signals.setChecked(True)

        self.discard_channels = QLineEdit()
        self.reference_sub = QLineEdit()
        self.show_photo_rectangle = QCheckBox()
        self.show_notch_filters = QCheckBox()

        self.reward_refractory_period = QDoubleSpinBox()
        self.reward_refractory_period.setRange(0.1, 10)
        self.reward_refractory_period.setValue(0.25)
        self.reward_refractory_period.setSuffix(" s")

        # Adding properties to the widget ------------------------------------------------------------------------------
        layout.addRow("Name", self.name)
        layout.addRow("Inlet", self.inlet_config)
        layout.addRow("Enable DC blocker", self.dc)
        layout.addRow("Prefilter band (lower bound)",
                      prefilter_lower_bound_widget)
        layout.addRow("Prefilter band (upper bound)",
                      prefilter_upper_bound_widget)
        layout.addRow("Plot raw", self.plot_raw)
        layout.addRow("Plot signals", self.plot_signals)
        layout.addRow("Discard channels", self.discard_channels)
        layout.addRow("Reference sub", self.reference_sub)
        layout.addRow("Show photo-sensor rectangle", self.show_photo_rectangle)
        layout.addRow("Show notch filters", self.show_notch_filters)
        layout.addRow("Reward refractory period",
                      self.reward_refractory_period)

    def updateModel(self, ex, /):
        ex.name = self.name.text()
        ex.inlet = self.inlet_type_export_values[self.inlet_type.currentText()]
        ex.lsl_stream_name = self.lsl_stream_name.currentText()
        ex.raw_data_path = self.lsl_filename.text()
        ex.hostname_port = self.hostname_port.text()
        ex.dc = self.dc.isChecked()

        if self.prefilter_lower_bound_enable.isChecked():
            prefilter_lower_bound = self.prefilter_lower_bound.value()
        else:
            prefilter_lower_bound = None

        if self.prefilter_upper_bound_enable.isChecked():
            prefilter_upper_bound = self.prefilter_upper_bound.value()
        else:
            prefilter_upper_bound = None

        ex.prefilter_band = (prefilter_lower_bound, prefilter_upper_bound)
        ex.plot_raw = self.plot_raw.isChecked()
        ex.plot_signals = self.plot_signals.isChecked()
        ex.discard_channels = self.discard_channels.text()
        ex.reference_sub = self.reference_sub.text()
        ex.show_photo_rectangle = self.show_photo_rectangle.isChecked()
        ex.show_notch_filters = self.show_notch_filters.isChecked()
        ex.reward_refractory_period = self.reward_refractory_period.value()

    def _adjust(self):
        if self.prefilter_lower_bound_enable.isChecked():
            self.prefilter_lower_bound.setEnabled(True)
            self.prefilter_upper_bound.setMinimum(
                self.prefilter_lower_bound.value())
        else:
            self.prefilter_lower_bound.setEnabled(False)
            self.prefilter_upper_bound.setMinimum(0)

        if self.prefilter_upper_bound_enable.isChecked():
            self.prefilter_upper_bound.setEnabled(True)
            self.prefilter_lower_bound.setMaximum(
                self.prefilter_upper_bound.value())
        else:
            self.prefilter_upper_bound.setEnabled(False)
            self.prefilter_lower_bound.setMaximum(
                10000)  # TODO: add proper value
class FilterGroup(AbstractProcessGroup):
    def __init__(self, title, fs):
        AbstractProcessGroup.__init__(self, title, fs)

        self.setupFilterLayout()

    def setupFilterLayout(self):
        filterLayout = QVBoxLayout(self)
        filterSettLayout = QHBoxLayout()

        self.filterBandChooser = QComboBox()
        self.filterTypeChooser = QComboBox()
        filterTypeLayout = QFormLayout()

        filterTypeLayout.addWidget(QLabel('Type'))
        filterTypeLayout.addWidget(self.filterBandChooser)
        bandTypes = {
            'Low Pass': '******',
            'Band Pass': '******',
            'High Pass': '******',
            'Band Stop': 'bandstop'
        }
        [self.filterBandChooser.addItem(i, bandTypes[i]) for i in bandTypes]
        self.filterBandChooser.setCurrentText('Band Pass')
        filterTypeLayout.addWidget(self.filterTypeChooser)
        filterTypes = {'Butter': 'butter', 'Bessel': 'bessel'}
        [
            self.filterTypeChooser.addItem(i, filterTypes[i])
            for i in filterTypes
        ]

        self.lowFreqEdit = QDoubleSpinBox()
        self.lowFreqEdit.setSuffix(' Hz')
        self.lowFreqEdit.setDecimals(1)
        self.lowFreqEdit.setRange(0.1, self.fs / 2 - 0.1)
        self.highFreqEdit = QDoubleSpinBox()
        self.highFreqEdit.setSuffix(' Hz')
        self.highFreqEdit.setDecimals(1)
        self.highFreqEdit.setLocale(
            QLocale(QLocale.Polish, QLocale.EuropeanUnion))
        self.highFreqEdit.setRange(0.1, self.fs / 2 - 0.1)
        self.filterBandChooser.currentTextChanged.connect(self.setFilterBand)
        filterFreqLayout = QFormLayout()
        filterFreqLayout.addRow(QLabel('Cutoff Frequencies'))
        filterFreqLayout.addRow('Low', self.lowFreqEdit)
        filterFreqLayout.addRow('High', self.highFreqEdit)

        filterOrdLayout = QFormLayout()
        self.filterOrdEdit = QSpinBox()
        self.filterOrdEdit.setMinimum(1)
        self.filterOrdEdit.setValue(5)
        filterOrdLayout.addRow(QLabel('Order'))
        filterOrdLayout.addRow(self.filterOrdEdit)

        filterSettLayout.addLayout(filterTypeLayout)
        filterSettLayout.addSpacing(10)
        filterSettLayout.addLayout(filterFreqLayout)
        filterSettLayout.addSpacing(10)
        filterSettLayout.addLayout(filterOrdLayout)

        btn = QPushButton('Show filter response')
        btn.clicked.connect(self.showFilterResponse)

        filterLayout.addLayout(filterSettLayout)
        filterLayout.addWidget(btn, 0, Qt.AlignRight)

    def setFilterBand(self):
        if self.filterBandChooser.currentText() == 'Low Pass':
            self.lowFreqEdit.setDisabled(True)
        else:
            self.lowFreqEdit.setEnabled(True)
        if self.filterBandChooser.currentText() == 'High Pass':
            self.highFreqEdit.setDisabled(True)
        else:
            self.highFreqEdit.setEnabled(True)

    def calcFilter(self):
        bandArr = [
            x.value() for x in (self.lowFreqEdit, self.highFreqEdit)
            if x.isEnabled() == True
        ]
        return filterCalc(order=self.filterOrdEdit.value(),
                          bandarr=bandArr,
                          fs=self.fs,
                          btype=self.filterBandChooser.currentData(),
                          ftype=self.filterTypeChooser.currentData())

    def showFilterResponse(self):
        bandArr = [
            x.value() for x in (self.lowFreqEdit, self.highFreqEdit)
            if x.isEnabled() == True
        ]
        b, a = self.calcFilter()
        w, h = signal.freqz(b, a)
        fig = plt.figure()
        ax1 = fig.add_subplot(111)
        ax1.set_title(
            label='Filter frequency response\n{}, {}, {}Hz, ord={}'.format(
                self.filterBandChooser.currentText(),
                self.filterTypeChooser.currentText(), bandArr,
                self.filterOrdEdit.value()))
        ax1.plot(w * (self.fs / (2 * np.pi)), 20 * np.log10(abs(h)), 'b')
        ax1.set_ylabel('Amplitude [dB]', color='b')
        ax1.set_xlabel('Frequency [Hz]')
        ax1.tick_params(axis='y', colors='b')
        ax2 = ax1.twinx()
        angles = np.unwrap(np.angle(h))
        ax2.plot(w * (self.fs / (2 * np.pi)), angles, 'g')
        ax2.set_ylabel('Angle (radians)', color='g')
        ax2.tick_params(axis='y', colors='g')
        plt.grid()
        plt.axis('tight')
        plt.show()

    def process(self, inData):
        b, a = self.calcFilter()
        outData = []
        progStep = 100.0 / len(inData)
        prog = 0
        for data in inData:
            newData = signal.lfilter(b, a, data)
            outData.append(newData)
            prog = prog + progStep
            self.progress.emit(int(prog))
        return outData