Esempio n. 1
0
class FpsDlg(QFilterConfig):
    def _createControls(self):
        self.setWindowTitle("Configure Crop")

        layout = QVBoxLayout(self)
        self.setLayout(layout)

        self.sourceWidget = QWidget(self)
        self.sourceSelection = self.createSourceControl(self.sourceWidget)
        self.sourceSelection.currentDataChanged.connect(self.setFilterSource)

        srclayout = QHBoxLayout()
        srclayout.addWidget(QLabel("Source: ", self.sourceWidget))
        srclayout.addWidget(self.sourceSelection)

        self.sourceWidget.setLayout(srclayout)
        layout.addWidget(self.sourceWidget)

        regex = QRegExp(r"^(\d+(?:\.\d+)?|\.\d+|\d+/\d+)$")
        validator = QRegExpValidator(regex)

        fpsLabel = QLabel("Frame rate:", self)
        self.fpsEdit = QLineEdit(self)
        self.fpsEdit.setText("24000/1001")
        self.fpsEdit.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.fpsEdit.setValidator(validator)
        self.fpsEdit.textChanged.connect(self.handleRateChanged)

        hlayout = QHBoxLayout()
        hlayout.addWidget(fpsLabel)
        hlayout.addWidget(self.fpsEdit)

        layout.addLayout(hlayout)

        self._prepareDlgButtons()

    def createNewFilterInstance(self):
        return Fps()

    @pyqtSlot(str)
    def handleRateChanged(self, value):
        if not regex.match(r"^(\d+(?:\.\d+)?|\.\d+|\d+/\d+)$", value):
            return

        if regex.match(r"^\d+/\d+$", value):
            value = QQ(value)

        elif regex.match(r"^\d+$", value):
            value = int(value)

        else:
            value = float(value)

        self.filtercopy.rate = value
        self.isModified()

    def _resetControls(self):
        self.fpsEdit.blockSignals(True)
        self.fpsEdit.setText(f"{self.filtercopy.rate}")
        self.fpsEdit.blockSignals(False)
Esempio n. 2
0
 def valueChangedCallbackPreTreat(self, val):
     if len(val) > self.nelem:
         val = str(val)[:self.nelem]
         self.updateView(val)
     QLineEdit.blockSignals(self, True)
     self.WidgetValueChangedCallback(val)
     QLineEdit.blockSignals(self, False)
Esempio n. 3
0
class CustLabel(QWidget):
    def __init__(self, parent=None):
        super(CustLabel, self).__init__(parent)

        # Create ui
        self.myEdit = QLineEdit()
        self.myEdit.hide()  # Hide line edit
        self.myEdit.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.myEdit.editingFinished.connect(self.textEdited)
        self.myLabel = BuddyLabel(
            self.myEdit
        )  # Create our custom label, and assign myEdit as its buddy
        # self.myLabel.setText("Nothing has been entered")
        self.myEdit.setText(self.myLabel.text())
        self.myLabel.setSizePolicy(
            QSizePolicy.Fixed, QSizePolicy.Fixed
        )  # Change vertical size policy so they both match and you don't get popping when switching

        # Put them under a layout together
        hLayout = QHBoxLayout()
        hLayout.addWidget(self.myLabel)
        hLayout.addWidget(self.myEdit)

        self.setFocus(
        )  # By default this line edit may have focus and the place holder won't show up on load, so focus on the widget

        self.setLayout(hLayout)

        self.callback = self.empty_callback

    def empty_callback(self, *args):
        print("empty callback")
        pass

    def textEdited(self):
        print("edit text")
        self.myLabel.setText(self.myEdit.text())
        # If the input is left empty, revert back to the label showing
        if not self.myEdit.text():
            self.myLabel.setText("Nothing has been entered")

        # hide() triggers editingFinished causing double callback
        # blocking signals stops this
        self.myEdit.blockSignals(True)
        self.myEdit.hide()
        self.myEdit.blockSignals(False)
        self.myLabel.show()
        self.callback(self)

    def mousePressEvent(self, event):
        self.myEdit.hide()
        self.myLabel.setText(self.myEdit.text())
        if not self.myEdit.text() or not self.myLabel.text():
            self.myLabel.setText("Nothing has been entered")
        self.myLabel.show()

    def putText(self, label_text):
        self.myLabel.setText(label_text)
        self.myEdit.setText(label_text)
Esempio n. 4
0
class Header(QHeaderView):

    def __init__(self, orientation=Qt.Horizontal, parent=None):
        super(Header, self).__init__(orientation, parent)
        self.editable = True
        self.setSectionsClickable(True)
        self.setSectionResizeMode(QHeaderView.ResizeToContents)
        self.line = QLineEdit(parent=self.viewport())
        self.line.setAlignment(Qt.AlignTop)
        self.line.setHidden(True)
        self.line.blockSignals(True)
        self.col = 0

        # Connections
        self.sectionDoubleClicked[int].connect(self.__edit)
        self.line.editingFinished.connect(self.__done_editing)

    def __edit(self, index):
        if not self.editable:
            return
        geo = self.line.geometry()
        geo.setWidth(self.sectionSize(index))
        geo.moveLeft(self.sectionViewportPosition(index))
        current_text = self.model().headerData(index, Qt.Horizontal,
                                               Qt.DisplayRole)
        self.line.setGeometry(geo)
        self.line.setHidden(False)
        self.line.blockSignals(False)
        self.line.setText(str(current_text))
        self.line.setFocus()
        self.line.selectAll()
        self.col = index

    def __done_editing(self):
        text = self.line.text()
        if not text.strip():
            # No debe ser vacío
            QMessageBox.critical(self, "Error",
                                 self.tr("El campo no debe ser vacío"))
            self.line.hide()
            return
        self.line.blockSignals(True)
        self.line.setHidden(False)
        self.model().setHeaderData(self.col, Qt.Horizontal, text,
                                   Qt.DisplayRole)
        self.line.setText("")
        self.line.hide()
        self.setCurrentIndex(QModelIndex())
Esempio n. 5
0
class Header(QHeaderView):
    def __init__(self, orientation=Qt.Horizontal, parent=None):
        super(Header, self).__init__(orientation, parent)
        self.editable = True
        self.setSectionsClickable(True)
        self.setSectionResizeMode(QHeaderView.ResizeToContents)
        self.line = QLineEdit(parent=self.viewport())
        self.line.setAlignment(Qt.AlignTop)
        self.line.setHidden(True)
        self.line.blockSignals(True)
        self.col = 0

        # Connections
        self.sectionDoubleClicked[int].connect(self.__edit)
        self.line.editingFinished.connect(self.__done_editing)

    def __edit(self, index):
        if not self.editable:
            return
        geo = self.line.geometry()
        geo.setWidth(self.sectionSize(index))
        geo.moveLeft(self.sectionViewportPosition(index))
        current_text = self.model().headerData(index, Qt.Horizontal,
                                               Qt.DisplayRole)
        self.line.setGeometry(geo)
        self.line.setHidden(False)
        self.line.blockSignals(False)
        self.line.setText(str(current_text))
        self.line.setFocus()
        self.line.selectAll()
        self.col = index

    def __done_editing(self):
        text = self.line.text()
        if not text.strip():
            # No debe ser vacío
            QMessageBox.critical(self, "Error",
                                 self.tr("El campo no debe ser vacío"))
            self.line.hide()
            return
        self.line.blockSignals(True)
        self.line.setHidden(False)
        self.model().setHeaderData(self.col, Qt.Horizontal, text,
                                   Qt.DisplayRole)
        self.line.setText("")
        self.line.hide()
        self.setCurrentIndex(QModelIndex())
class MetaHeaderView(QHeaderView):
    # source code, and do some miner changes
    # https://www.qtcentre.org/threads/12835-How-to-edit-Horizontal-Header-Item-in-QTableWidget?p=224376#post224376
    def __init__(self, orientation, parent=None):
        super(MetaHeaderView, self).__init__(orientation, parent)
        # self.setMovable(True)
        # self.setClickable(True)
        self.setSectionsClickable(True)

        # This block sets up the edit line by making setting the parent
        # to the Headers Viewport.
        self.line = QLineEdit(parent=self.viewport())  # Create
        self.line.setAlignment(Qt.AlignTop)  # Set the Alignmnet
        self.line.setHidden(True)  # Hide it till its needed
        # This is needed because I am having a werid issue that I believe has
        # to do with it losing focus after editing is done.
        self.line.blockSignals(True)
        self.sectionedit = 0
        # Connects to double click
        self.sectionDoubleClicked.connect(self.editHeader)
        self.line.editingFinished.connect(self.doneEditing)

    def doneEditing(self):
        # This block signals needs to happen first otherwise I have lose focus
        # problems again when there are no rows
        self.line.blockSignals(True)
        self.line.setHidden(True)
        newname = self.line.text()
        self.model().setHeaderData(self.sectionedit, Qt.Horizontal, newname)
        self.setCurrentIndex(QModelIndex())

    def editHeader(self, section):
        # This block sets up the geometry for the line edit
        edit_geometry = self.line.geometry()
        edit_geometry.setWidth(self.sectionSize(section))
        edit_geometry.moveLeft(self.sectionViewportPosition(section))
        self.line.setGeometry(edit_geometry)
        self.line.setText(str(self.model().headerData(section, Qt.Horizontal)))
        self.line.setHidden(False)  # Make it visiable
        self.line.blockSignals(False)  # Let it send signals
        self.line.setFocus()
        self.line.selectAll()
        self.sectionedit = section
Esempio n. 7
0
class Header(QHeaderView):

    def __init__(self, orientation=Qt.Horizontal, parent=None):
        super(Header, self).__init__(orientation, parent)
        self.setSectionsClickable(True)
        self.setSectionResizeMode(QHeaderView.ResizeToContents)
        self.line = QLineEdit(parent=self.viewport())
        self.line.setAlignment(Qt.AlignTop)
        self.line.setHidden(True)
        self.line.blockSignals(True)
        self.col = 0

        # Connections
        self.sectionDoubleClicked[int].connect(self.__edit)
        self.line.editingFinished.connect(self.__done_editing)

    def __edit(self, index):
        geo = self.line.geometry()
        geo.setWidth(self.sectionSize(index))
        geo.moveLeft(self.sectionViewportPosition(index))
        current_text = self.model().headerData(index, Qt.Horizontal)
        self.line.setGeometry(geo)
        self.line.setHidden(False)
        self.line.blockSignals(False)
        self.line.setText(str(current_text))
        self.line.setFocus()
        self.line.selectAll()
        self.col = index

    def __done_editing(self):
        self.line.blockSignals(True)
        self.line.setHidden(False)
        text = self.line.text()
        self.model().setHeaderData(self.col, Qt.Horizontal, text)
        self.line.setText("")
        self.line.hide()
        self.setCurrentIndex(QModelIndex())
Esempio n. 8
0
class OptionsDialog(QDialog):
    def __init__(self, setting: Settings, have_dutils, parent=None):
        super(OptionsDialog, self).__init__(parent)

        self.settings = setting
        self.enabled_video = True  # temporary toggle to disable video features as they do not exist
        self.enabled_logging = True
        self.enabled_keybindings = True
        self.enabled_dutils = have_dutils
        self.setWindowTitle("Tcam-Capture Options")
        self.layout = QVBoxLayout(self)
        self.setLayout(self.layout)

        self.tabs = QTabWidget()

        self.general_widget = QWidget()
        self.keybindings_widget = QWidget()
        self.logging_widget = QWidget()
        self.saving_widget = QWidget()

        self._setup_general_ui()
        self.tabs.addTab(self.general_widget, "General")

        self._setup_saving_ui()
        self.tabs.addTab(self.saving_widget, "Image/Video")

        self.layout.addWidget(self.tabs)
        # OK and Cancel buttons
        self.buttons = QDialogButtonBox(
            QDialogButtonBox.Reset | QDialogButtonBox.Ok
            | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        self.layout.addWidget(self.buttons)

        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)
        self.buttons.clicked.connect(self.clicked)

    def _setup_general_ui(self):
        """
        Create everything related to the general tab
        """

        layout = QFormLayout()
        layout.setSpacing(20)
        layout.setVerticalSpacing(20)

        self.device_dialog_checkbox = QCheckBox(self)
        device_dialog_label = QLabel("Open device dialog on start:")
        layout.addRow(device_dialog_label, self.device_dialog_checkbox)

        self.reopen_device_checkbox = QCheckBox(self)
        reopen_device_label = QLabel(
            "Reopen device on start(ignores device dialog):", self)
        layout.addRow(reopen_device_label, self.reopen_device_checkbox)

        self.use_dutils_checkbox = QCheckBox(self)
        self.use_dutils_label = QLabel("Use tiscamera dutils, if present:",
                                       self)
        layout.addRow(self.use_dutils_label, self.use_dutils_checkbox)

        if not self.enabled_dutils:
            self.use_dutils_label.setToolTip(
                "Enabled when tiscamera-dutils are installed")
            self.use_dutils_label.setEnabled(False)
            self.use_dutils_checkbox.setToolTip(
                "Enabled when tiscamera-dutils are installed")
            self.use_dutils_checkbox.setEnabled(False)

        self.general_widget.setLayout(layout)

    def _setup_saving_ui(self):
        """
        Create everything related to the image/video saving tab
        """
        encoder_dict = Encoder.get_encoder_dict()
        form_layout = QFormLayout()

        layout = QVBoxLayout()
        layout.addLayout(form_layout)

        location_layout = QHBoxLayout()
        location_label = QLabel("Where to save images/videos:", self)
        self.location_edit = QLineEdit(self)
        location_dialog_button = QPushButton("...", self)
        location_dialog_button.clicked.connect(self.open_file_dialog)
        location_layout.addWidget(self.location_edit)
        location_layout.addWidget(location_dialog_button)

        # maintain descriptions as own labels
        # pyqt seems to loose the descriptions somewhere
        # when simple strings are used or the qlabel does not have self as owner
        form_layout.addRow(location_label, location_layout)

        self.image_type_combobox = QComboBox(self)
        for key, value in encoder_dict.items():
            if value.encoder_type == Encoder.MediaType.image:
                self.image_type_combobox.addItem(key)
        image_type_label = QLabel("Save images as:")
        self.image_type_combobox.currentIndexChanged['QString'].connect(
            self.image_name_suffix_changed)

        form_layout.addRow(image_type_label, self.image_type_combobox)
        if self.enabled_video:
            self.video_type_combobox = QComboBox(self)
            for key, value in encoder_dict.items():
                if value.encoder_type == Encoder.MediaType.video:
                    self.video_type_combobox.addItem(key)
            self.video_type_combobox.currentIndexChanged['QString'].connect(
                self.video_name_suffix_changed)

            video_type_label = QLabel("Save videos as:", self)
            form_layout.addRow(video_type_label, self.video_type_combobox)

        image_name_groupbox = QGroupBox("Image File Names")
        groupbox_layout = QFormLayout()
        image_name_groupbox.setLayout(groupbox_layout)

        self.image_name_preview = QLabel(
            "<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png")
        self.image_name_preview_description = QLabel(
            "Images will be named like:")
        groupbox_layout.addRow(self.image_name_preview_description,
                               self.image_name_preview)

        self.image_name_prefix = QLineEdit()
        self.image_name_prefix.textChanged.connect(
            self.image_name_prefix_changed)
        self.image_name_prefix.setMaxLength(100)

        self.image_name_prefix_description = QLabel("User Prefix:", self)
        groupbox_layout.addRow(self.image_name_prefix_description,
                               self.image_name_prefix)

        self.image_name_serial = QCheckBox(self)
        self.image_name_serial.toggled.connect(
            self.image_name_properties_toggled)
        self.image_name_serial_description = QLabel("Include Serial:")
        groupbox_layout.addRow(self.image_name_serial_description,
                               self.image_name_serial)

        self.image_name_format = QCheckBox(self)
        self.image_name_format.toggled.connect(
            self.image_name_properties_toggled)

        self.image_name_format_description = QLabel("Include Format:")
        groupbox_layout.addRow(self.image_name_format_description,
                               self.image_name_format)

        self.image_name_counter = QCheckBox(self)
        self.image_name_counter.toggled.connect(
            self.image_name_properties_toggled)
        self.image_name_counter_description = QLabel("Include Counter:")
        groupbox_layout.addRow(self.image_name_counter_description,
                               self.image_name_counter)

        self.image_name_counter_box = QSpinBox(self)
        self.image_name_counter_box.setRange(1, 10)
        self.image_name_counter_box.valueChanged.connect(
            self.image_name_counter_changed)
        self.image_name_counter_box_description = QLabel("Counter Size:")
        groupbox_layout.addRow(self.image_name_counter_box_description,
                               self.image_name_counter_box)

        self.image_name_counter.toggled.connect(
            self.toggle_image_counter_box_availability)
        self.image_name_counter.toggled.connect(
            self.image_name_properties_toggled)

        self.image_name_timestamp = QCheckBox(self)
        self.image_name_timestamp.toggled.connect(
            self.image_name_properties_toggled)
        self.image_name_timestamp_description = QLabel("Include Timestamp:")
        groupbox_layout.addRow(self.image_name_timestamp_description,
                               self.image_name_timestamp)

        layout.addWidget(image_name_groupbox)

        video_groupbox = QGroupBox("Video File Names")

        video_layout = QFormLayout()
        video_groupbox.setLayout(video_layout)

        self.video_name_preview = QLabel(
            "<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png")
        self.video_name_preview_description = QLabel(
            "Videos will be named like:")
        video_layout.addRow(self.video_name_preview_description,
                            self.video_name_preview)

        self.video_name_prefix = QLineEdit()
        self.video_name_prefix.textChanged.connect(
            self.video_name_prefix_changed)
        self.video_name_prefix.setMaxLength(100)

        self.video_name_prefix_description = QLabel("User Prefix:", self)
        video_layout.addRow(self.video_name_prefix_description,
                            self.video_name_prefix)

        self.video_name_serial = QCheckBox(self)
        self.video_name_serial.toggled.connect(
            self.video_name_properties_toggled)
        self.video_name_serial_description = QLabel("Include Serial:")
        video_layout.addRow(self.video_name_serial_description,
                            self.video_name_serial)

        self.video_name_format = QCheckBox(self)
        self.video_name_format.toggled.connect(
            self.video_name_properties_toggled)

        self.video_name_format_description = QLabel("Include Format:")
        video_layout.addRow(self.video_name_format_description,
                            self.video_name_format)

        self.video_name_counter = QCheckBox(self)
        self.video_name_counter.toggled.connect(
            self.video_name_properties_toggled)
        self.video_name_counter_description = QLabel("Include Counter:")
        video_layout.addRow(self.video_name_counter_description,
                            self.video_name_counter)

        self.video_name_counter_box = QSpinBox(self)
        self.video_name_counter_box.setRange(1, 10)
        self.video_name_counter_box.valueChanged.connect(
            self.video_name_counter_changed)
        self.video_name_counter_box_description = QLabel("Counter Size:")
        video_layout.addRow(self.video_name_counter_box_description,
                            self.video_name_counter_box)

        self.video_name_counter.toggled.connect(
            self.toggle_video_counter_box_availability)
        self.video_name_counter.toggled.connect(
            self.video_name_properties_toggled)

        self.video_name_timestamp = QCheckBox(self)
        self.video_name_timestamp.toggled.connect(
            self.video_name_properties_toggled)
        self.video_name_timestamp_description = QLabel("Include Timestamp:")
        video_layout.addRow(self.video_name_timestamp_description,
                            self.video_name_timestamp)

        layout.addWidget(video_groupbox)

        self.saving_widget.setLayout(layout)

    def image_name_prefix_changed(self, name: str):
        """"""

        self.settings.image_name.user_prefix = self.image_name_prefix.text()
        self.update_image_name_preview()

    def image_name_suffix_changed(self, suffix: str):
        """"""

        self.update_image_name_preview()

    def image_name_counter_changed(self, name: str):
        """"""
        self.settings.image_name.counter_size = self.image_name_counter_box.value(
        )
        self.update_image_name_preview()

    def image_name_properties_toggled(self):
        """"""

        self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked(
        )
        self.settings.image_name.include_counter = self.image_name_counter.isChecked(
        )
        self.settings.image_name.include_format = self.image_name_format.isChecked(
        )
        self.settings.image_name.include_serial = self.image_name_serial.isChecked(
        )

        self.update_image_name_preview()

    def update_image_name_preview(self):

        preview_string = ""

        if self.settings.image_name.user_prefix != "":

            max_prefix_length = 15
            prefix = (
                self.settings.image_name.user_prefix[:max_prefix_length] +
                '..') if len(
                    self.settings.image_name.user_prefix
                ) > max_prefix_length else self.settings.image_name.user_prefix

            preview_string += prefix

        if self.settings.image_name.include_serial:
            if preview_string != "":
                preview_string += "-"
            preview_string += "00001234"

        if self.settings.image_name.include_format:
            if preview_string != "":
                preview_string += "-"
            preview_string += "gbrg_1920x1080_15_1"

        if self.settings.image_name.include_timestamp:
            if preview_string != "":
                preview_string += "-"
            preview_string += "19701230T125503"

        if self.settings.image_name.include_counter:
            if preview_string != "":
                preview_string += "-"
            preview_string += '{message:0>{fill}}'.format(
                message=1, fill=self.settings.image_name.counter_size)

        if preview_string == "":
            preview_string = "image"

        preview_string += "." + self.image_type_combobox.currentText()

        self.image_name_preview.setText(preview_string)

    def video_name_prefix_changed(self, name: str):
        """"""

        self.settings.video_name.user_prefix = self.video_name_prefix.text()
        self.update_video_name_preview()

    def video_name_suffix_changed(self, suffix: str):
        """"""

        self.update_video_name_preview()

    def video_name_counter_changed(self, name: str):
        """"""
        self.settings.video_name.counter_size = self.video_name_counter_box.value(
        )
        self.update_video_name_preview()

    def video_name_properties_toggled(self):
        """"""

        self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked(
        )
        self.settings.video_name.include_counter = self.video_name_counter.isChecked(
        )
        self.settings.video_name.include_format = self.video_name_format.isChecked(
        )
        self.settings.video_name.include_serial = self.video_name_serial.isChecked(
        )

        self.update_video_name_preview()

    def update_video_name_preview(self):

        preview_string = ""

        if self.settings.video_name.user_prefix != "":

            # This is a convenience change to the displayed string.
            # We only display an amount of max_prefix_length
            # chars to save screen space
            max_prefix_length = 15
            prefix = (
                self.settings.video_name.user_prefix[:max_prefix_length] +
                '..') if len(
                    self.settings.video_name.user_prefix
                ) > max_prefix_length else self.settings.video_name.user_prefix

            preview_string += prefix

        if self.settings.video_name.include_serial:
            if preview_string != "":
                preview_string += "-"
            preview_string += "00001234"

        if self.settings.video_name.include_format:
            if preview_string != "":
                preview_string += "-"
            preview_string += "gbrg_1920x1080_15_1"

        if self.settings.video_name.include_timestamp:
            if preview_string != "":
                preview_string += "-"
            preview_string += "19701230T125503"

        if self.settings.video_name.include_counter:
            if preview_string != "":
                preview_string += "-"
            preview_string += '{message:0>{fill}}'.format(
                message=1, fill=self.settings.video_name.counter_size)

        if preview_string == "":
            preview_string = "video"

        preview_string += "." + self.video_type_combobox.currentText()

        self.video_name_preview.setText(preview_string)

    def toggle_image_counter_box_availability(self):
        """"""
        if self.image_name_counter.isChecked():
            self.image_name_counter_box.setEnabled(True)
        else:
            self.image_name_counter_box.setEnabled(False)

    def toggle_video_counter_box_availability(self):
        """"""
        if self.video_name_counter.isChecked():
            self.video_name_counter_box.setEnabled(True)
        else:
            self.video_name_counter_box.setEnabled(False)

    def set_settings(self, settings: Settings):
        self.location_edit.setText(settings.get_save_location())
        self.image_type_combobox.setCurrentText(settings.get_image_type())
        if self.enabled_video:
            self.video_type_combobox.setCurrentText(settings.get_video_type())
        self.device_dialog_checkbox.setChecked(
            settings.show_device_dialog_on_startup)
        self.reopen_device_checkbox.setChecked(
            settings.reopen_device_on_startup)
        self.use_dutils_checkbox.setChecked(settings.use_dutils)

        if settings.image_name.include_timestamp:
            self.image_name_timestamp.blockSignals(True)
            self.image_name_timestamp.toggle()
            self.image_name_timestamp.blockSignals(False)
        if settings.image_name.include_counter:
            self.image_name_counter.blockSignals(True)
            self.image_name_counter.toggle()
            self.image_name_counter.blockSignals(False)

        self.image_name_counter_box.blockSignals(True)
        self.image_name_counter_box.setValue(settings.image_name.counter_size)
        self.image_name_counter_box.blockSignals(False)
        self.toggle_image_counter_box_availability()

        if settings.image_name.include_format:
            self.image_name_format.blockSignals(True)
            self.image_name_format.toggle()
            self.image_name_format.blockSignals(False)
        if settings.image_name.include_serial:
            self.image_name_serial.blockSignals(True)
            self.image_name_serial.toggle()
            self.image_name_serial.blockSignals(False)
        self.image_name_prefix.blockSignals(True)
        self.image_name_prefix.setText(settings.image_name.user_prefix)
        self.image_name_prefix.blockSignals(False)

        self.update_image_name_preview()

        if settings.video_name.include_timestamp:
            self.video_name_timestamp.blockSignals(True)
            self.video_name_timestamp.toggle()
            self.video_name_timestamp.blockSignals(False)
        if settings.video_name.include_counter:
            self.video_name_counter.blockSignals(True)
            self.video_name_counter.toggle()
            self.video_name_counter.blockSignals(False)

        self.video_name_counter_box.blockSignals(True)
        self.video_name_counter_box.setValue(settings.video_name.counter_size)
        self.video_name_counter_box.blockSignals(False)
        self.toggle_video_counter_box_availability()

        if settings.video_name.include_format:
            self.video_name_format.blockSignals(True)
            self.video_name_format.toggle()
            self.video_name_format.blockSignals(False)
        if settings.video_name.include_serial:
            self.video_name_serial.blockSignals(True)
            self.video_name_serial.toggle()
            self.video_name_serial.blockSignals(False)
        self.video_name_prefix.blockSignals(True)
        self.video_name_prefix.setText(settings.video_name.user_prefix)
        self.video_name_prefix.blockSignals(False)

        self.update_video_name_preview()

    def save_settings(self):
        self.settings.save_location = self.location_edit.text()
        self.settings.image_type = self.image_type_combobox.currentText()
        if self.enabled_video:
            self.settings.video_type = self.video_type_combobox.currentText()
        self.settings.show_device_dialog_on_startup = self.device_dialog_checkbox.isChecked(
        )
        self.settings.reopen_device_on_startup = self.reopen_device_checkbox.isChecked(
        )
        self.settings.use_dutils = self.use_dutils_checkbox.isChecked()

        self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked(
        )
        self.settings.image_name.include_counter = self.image_name_counter.isChecked(
        )
        if self.image_name_counter.isChecked():
            self.settings.image_name.counter_size = self.image_name_counter_box.value(
            )

        self.settings.image_name.include_format = self.image_name_format.isChecked(
        )
        self.settings.image_name.include_serial = self.image_name_serial.isChecked(
        )
        self.settings.image_name.user_prefix = self.image_name_prefix.text()

        self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked(
        )
        self.settings.video_name.include_counter = self.video_name_counter.isChecked(
        )
        if self.video_name_counter.isChecked():
            self.settings.video_name.counter_size = self.video_name_counter_box.value(
            )

        self.settings.video_name.include_format = self.video_name_format.isChecked(
        )
        self.settings.video_name.include_serial = self.video_name_serial.isChecked(
        )
        self.settings.video_name.user_prefix = self.video_name_prefix.text()

    def open_file_dialog(self):
        fdia = QFileDialog()
        fdia.setFileMode(QFileDialog.Directory)
        fdia.setWindowTitle("Select Directory for saving images and videos")
        if fdia.exec_():
            self.location_edit.setText(fdia.selectedFiles()[0])

    def get_location(self):
        return self.location_edit.text()

    def get_image_format(self):
        return self.image_type_combobox.currentText()

    def get_video_format(self):
        return self.video_type_combobox.currentText()

    def clicked(self, button):

        if self.buttons.buttonRole(button) == QDialogButtonBox.ResetRole:
            self.reset()

    def reset(self):
        """"""
        log.info("reset called")
        self.settings.reset()
        self.set_settings(self.settings)

    @staticmethod
    def get_options(settings, parent=None):
        dialog = OptionsDialog(settings, parent)

        if settings is not None:
            dialog.set_settings(settings)
        result = dialog.exec_()

        if result == QDialog.Accepted:
            dialog.save_settings()
            settings.save()

        return result == QDialog.Accepted
Esempio n. 9
0
class energy_to_charge(QWidget):

    changed = pyqtSignal()

    def __init__(self):
        QWidget.__init__(self)
        self.position = "top"
        self.charge_type = "electron"
        self.hbox = QHBoxLayout()
        self.edit_m3 = QLineEdit()
        self.edit_m3.setMaximumWidth(60)
        self.label_m3 = QLabel("m<sup>-3</sup> /")
        self.label_m3.setStyleSheet("QLabel { border: 0px; padding: 0px; }")
        self.label_m3.setMaximumWidth(30)

        self.edit_eV = QLineEdit()
        self.edit_eV.setMaximumWidth(50)
        self.label_eV = QLabel("eV")
        self.label_eV.setStyleSheet("QLabel { border: 0px; padding: 0px; }")
        self.label_eV.setMaximumWidth(20)

        #self.button=QPushButton()
        #self.button.setFixedSize(25, 25)
        #self.button.setText("...")
        self.hbox.addWidget(self.edit_m3, Qt.AlignLeft)
        self.hbox.addWidget(self.label_m3, Qt.AlignLeft)
        self.hbox.addWidget(self.edit_eV, Qt.AlignLeft)
        self.hbox.addWidget(self.label_eV, Qt.AlignLeft)
        self.hbox.setSpacing(0)
        #self.hbox.addWidget(self.button)

        self.edit_m3.textChanged.connect(self.callback_m3_changed)
        self.edit_eV.textChanged.connect(self.callback_eV_changed)

        self.hbox.setContentsMargins(0, 0, 0, 0)
        self.edit_m3.setStyleSheet("QLineEdit { border: none }")

        #self.button.clicked.connect(self.callback_button_click)

        self.setLayout(self.hbox)

    def cal_ev(self):
        for l in get_epi().layers:
            file_name = l.dos_file + ".inp"
            if file_name.startswith("dos"):
                f = inp()
                f.load(file_name)
                if self.charge_type == "electron":
                    eV = gen_fermi_from_np(float(self.edit_m3.text()),
                                           float(f.get_token("#Nc")), 300.0)
                else:
                    eV = gen_fermi_from_np(float(self.edit_m3.text()),
                                           float(f.get_token("#Nv")), 300.0)
        return eV

    def cal_m3(self):
        for l in get_epi().layers:
            file_name = l.dos_file + ".inp"
            if file_name.startswith("dos"):
                f = inp()
                f.load(file_name)
                if self.charge_type == "electron":
                    n = gen_np_from_fermi(float(self.edit_eV.text()),
                                          float(f.get_token("#Nc")), 300.0)
                else:
                    n = gen_np_from_fermi(float(self.edit_eV.text()),
                                          float(f.get_token("#Nv")), 300.0)
        return n

    def callback_eV_changed(self):
        n = self.cal_m3()
        self.edit_m3.blockSignals(True)
        self.edit_m3.setText('%.0e' % n)
        self.edit_m3.blockSignals(False)

        self.changed.emit()

    def callback_m3_changed(self):
        try:
            ev = self.cal_ev()
            self.edit_eV.blockSignals(True)
            self.edit_eV.setText('%.2f' % ev)
            self.edit_eV.blockSignals(False)
        except:
            pass
        self.changed.emit()

    def setText(self, text):
        val = float(text)
        text = '%.2e' % val
        text = str(decimal.Decimal(text).normalize()).lower().replace('+', '')
        self.edit_m3.setText(text)
        #self.edit_eV.setText(text)

    def text(self):
        return self.edit_m3.text()
Esempio n. 10
0
class AnalyizeDataWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(100, 100, 500, 200)
        self.setWindowTitle("Analyze Datafile")
        self.layout = QGridLayout()

        # self.rangeDict = {"Default" : [[0,1],[0,100],[0,100],
        #                                [0,100],[0,100],[0,1]]}
        self.dataAnalDict = {}
        # self.dataAnalDict['force settings'] = {}
        self.dataAnalDict['Vertical force'] = {}
        self.dataAnalDict['Lateral force'] = {}
        self.initialize_dict("Default", range_clear=True)
        self.dataAnalDict['misc settings'] = {}

        self.home()

    #call this when your are adding a new roi label. #initialize force settings in dict
    def initialize_dict(self, roi_label, range_clear=True):
        # self.dataAnalDict['force settings'][roi_label] = {}
        # self.init_force_dict(roi_label, "Vertical force")
        # self.init_force_dict(roi_label, "Lateral force")
        if range_clear == True:
            self.dataAnalDict["Vertical force"]["ranges"] = {}
            self.dataAnalDict["Lateral force"]["ranges"] = {}
        self.init_force_dict("Vertical force", roi_label)
        self.init_force_dict("Lateral force", roi_label)

    #initialize sub properties of force
    def init_force_dict(self, force, roi_label):
        self.dataAnalDict[force]["ranges"][roi_label] = {}
        self.dataAnalDict[force]["ranges"][roi_label]["Zero"] = "100,200"
        self.dataAnalDict[force]["ranges"][roi_label]["Force"] = "100,200"
        self.dataAnalDict[force]["ranges"][roi_label]["Preload"] = "100,200"
        self.dataAnalDict[force]["transform"] = {}
        self.dataAnalDict[force]["transform"]["Filter"] = False
        self.dataAnalDict[force]["transform"]["Filter window"] = 43
        self.dataAnalDict[force]["transform"]["Filter order"] = 2
        self.dataAnalDict[force]["transform"]["Cross Talk"] = 0
        self.dataAnalDict[force]["transform"]["Zero subtract"] = False
        # self.dataAnalDict['force settings'][roi_label][force] = {}
        # self.dataAnalDict['force settings'][roi_label][force]["Zero"] = "0,10"
        # self.dataAnalDict['force settings'][roi_label][force]["Force"] = "0,10"
        # self.dataAnalDict['force settings'][roi_label][force]["Preload"] = "0,10"
        # self.dataAnalDict['force settings'][roi_label][force]["Filter"] = False
        # self.dataAnalDict['force settings'][roi_label][force]["Filter window"] = 43
        # self.dataAnalDict['force settings'][roi_label][force]["Filter order"] = 2
        # self.dataAnalDict['force settings'][roi_label][force]["Cross Talk"] = 0

    def home(self):

        # self.showContactArea = QCheckBox('contact area', self) #contact area
        # self.showContactArea.setChecked(True)

        # self.showROIArea = QCheckBox('ROI area', self) #roi area
        # self.showContactLength = QCheckBox('contact length', self) #contact length
        # self.showROILength = QCheckBox('ROI length', self) #roi length
        # self.showContactNumber = QCheckBox('contact number', self) #contact number
        # self.showEcc = QCheckBox('eccentricity', self) #median eccentricity

        # self.showLateralForce = QCheckBox('lateral force', self) #lateral force
        # self.showZPiezo = QCheckBox('vertical piezo', self) #z piezo
        # self.showXPiezo = QCheckBox('lateral piezo', self) #x piezo
        # self.showAdhesion = QCheckBox('adhesion calculation', self) #adhesion/preload calc line
        # self.showFriction = QCheckBox('friction calculation', self) #friction calc lines
        # self.showStress = QCheckBox('stress', self) #stress
        # self.showDeformation = QCheckBox('deformation', self) #deformation
        # self.showTitle = QCheckBox('title', self) #plt title
        # self.showTitle.setChecked(True)
        # self.showLegend2 = QCheckBox('legend2', self) #plt title
        # self.showLegend2.setChecked(True)

        # self.showWidgets = [self.showContactArea, self.showROIArea, self.showZPiezo,
        #                     self.showXPiezo, self.showAdhesion, self.showFriction,
        #                     self.showLateralForce, self.showContactLength, self.showROILength,
        #                     self.showContactNumber, self.showEcc, self.showStress,
        #                     self.showDeformation, self.showTitle, self.showLegend2]

        # self.xAxisLabel = QLabel("<b>X Axis:</b>", self)
        # self.xAxisParam = QComboBox(self) #x axis parameter
        # self.xAxisParam.addItem("Time (s)")
        # self.xAxisParam.addItem("Vertical Position (μm)")
        # self.xAxisParam.addItem("Lateral Position (μm)")
        # self.xAxisParam.addItem("Deformation (μm)")

        # self.fontLabel = QLabel("Font Size:", self)
        # self.fontSize = QDoubleSpinBox(self) #vertical force zero range start
        # self.fontSize.setValue(12)
        # self.fontSize.setSingleStep(1)
        # self.fontSize.setRange(1, 100)

        roiChoiceLabel = QLabel("ROI Label:", self)
        self.roiChoice = QComboBox(self)  #choose ROI
        self.roiChoice.addItem("Default")
        self.roiChoice.setCurrentIndex(0)
        self.roiChoice.currentIndexChanged.connect(self.update_widgets)

        dataChoiceLabel = QLabel("Data:", self)
        self.dataChoice = QComboBox(self)  #force data
        self.dataChoiceDict = {
            "Vertical force": "Adhesion",
            "Lateral force": "Friction"
        }
        self.dataChoice.addItems(list(self.dataChoiceDict.keys()))
        self.dataChoice.setCurrentIndex(0)
        self.dataChoice.currentIndexChanged.connect(self.update_widgets)

        self.zeroBtn = QPushButton("Zero Range", self)  #zero
        self.zeroLabel = QLineEdit(self)
        self.zeroLabel.setReadOnly(True)
        self.zeroLabel.setText("100,200")

        self.forceBtn = QPushButton(
            self.dataChoiceDict[self.dataChoice.currentText()] + " Range",
            self)  #adhesion/friction
        self.forceLabel = QLineEdit(self)
        self.forceLabel.setReadOnly(True)
        self.forceLabel.setText("100,200")

        self.preloadBtn = QPushButton("Preload Range", self)  #preload
        self.preloadLabel = QLineEdit(self)
        self.preloadLabel.setReadOnly(True)
        self.preloadLabel.setText("100,200")

        # self.startLabel = QLabel("Start (%):", self)
        # self.endLabel = QLabel("End (%):", self)

        # self.zeroLabel = QLabel("Zero Range", self)
        # self.adhLabel = QLabel("Adhesion Range", self)
        # self.prl1Label = QLabel("Preload Range", self)

        # self.zeroRange1 = QDoubleSpinBox(self) #vertical force zero range start
        # self.zeroRange1.setValue(0)
        # self.zeroRange1.setSingleStep(1)
        # self.zeroRange1.setRange(0, 100)
        # self.zeroRange1.valueChanged.connect(self.update_dict)

        # self.zeroRange2 = QDoubleSpinBox(self) #vertical force zero range end
        # self.zeroRange2.setValue(1)
        # self.zeroRange2.setSingleStep(1)
        # self.zeroRange2.setRange(0, 100)
        # self.zeroRange2.valueChanged.connect(self.update_dict)

        # self.adhRange1 = QDoubleSpinBox(self) #adhesion peak range start
        # self.adhRange1.setValue(0)
        # self.adhRange1.setSingleStep(1)
        # self.adhRange1.setRange(0, 100)
        # self.adhRange1.valueChanged.connect(self.update_dict)

        # self.adhRange2 = QDoubleSpinBox(self) #adhesion peak range start
        # self.adhRange2.setValue(100)
        # self.adhRange2.setSingleStep(1)
        # self.adhRange2.setRange(0, 100)
        # self.adhRange2.valueChanged.connect(self.update_dict)

        # self.prl1Range1 = QDoubleSpinBox(self) #preload peak range start
        # self.prl1Range1.setValue(0)
        # self.prl1Range1.setSingleStep(1)
        # self.prl1Range1.setRange(0, 100)
        # self.prl1Range1.valueChanged.connect(self.update_dict)

        # self.prl1Range2 = QDoubleSpinBox(self) #preload peak range start
        # self.prl1Range2.setValue(100)
        # self.prl1Range2.setSingleStep(1)
        # self.prl1Range2.setRange(0, 100)
        # self.prl1Range2.valueChanged.connect(self.update_dict)

        # self.zero2Range1 = QDoubleSpinBox(self) #lateral force zero range start
        # self.zero2Range1.setValue(0)
        # self.zero2Range1.setSingleStep(1)
        # self.zero2Range1.setRange(0, 100)
        # self.zero2Range1.valueChanged.connect(self.update_dict)

        # self.zero2Range2 = QDoubleSpinBox(self) #lateral force zero range end
        # self.zero2Range2.setValue(1)
        # self.zero2Range2.setSingleStep(1)
        # self.zero2Range2.setRange(0, 100)
        # self.zero2Range2.valueChanged.connect(self.update_dict)

        # self.filterLatF = QCheckBox('Filter stress curve', self) #filter

        self.filter = QCheckBox('Filter', self)  #filter
        # self.filter.stateChanged.connect(self.update_dict)

        windLabel = QLabel("Window Length:", self)
        self.filter_wind = QSpinBox(self)  #filter window
        self.filter_wind.setValue(43)
        self.filter_wind.setSingleStep(20)
        self.filter_wind.setRange(3, 10001)
        # self.filter_wind.valueChanged.connect(self.filter_change)

        polyLabel = QLabel("Polynomial Order:", self)
        self.filter_poly = QSpinBox(self)  #filter polynom
        self.filter_poly.setValue(2)
        self.filter_poly.setSingleStep(1)
        self.filter_poly.setRange(1, 20000)
        # self.filter_poly.valueChanged.connect(self.update_dict)

        self.zero_subtract = QCheckBox('Zero subtract', self)  #filter

        # self.startLabel2 = QLabel("Start (%):", self)
        # self.endLabel2 = QLabel("End (%):", self)

        # self.frLabel = QLabel("Friction Range", self)
        # self.prl2Label = QLabel("Preload Range", self)
        # self.zero2Label = QLabel("Zero Range", self)

        eqLabel = QLabel("Lateral Calib. Equation (μN):", self)
        self.latCalibEq = QLineEdit(self)  #lateral force calib equation
        self.latCalibEq.setText("29181.73*x")

        noiseStepsLabel = QLabel("Noisy Steps:", self)
        noiseSteps = QLineEdit(self)  #remove first data point from steps
        noiseSteps.setText("")

        # self.legendPosLabel = QLabel("Legend:", self) #legend position
        # self.legendPos = QLineEdit(self)
        # self.legendPos.setText("upper right")

        # self.startFullLabel = QLabel("Start (%):", self)
        # self.endFullLabel = QLabel("End (%):", self)

        # self.startFull = QDoubleSpinBox(self) #plot range start
        # self.startFull.setValue(0)
        # self.startFull.setSingleStep(1)
        # self.startFull.setRange(0, 100)

        # self.endFull = QDoubleSpinBox(self) #plot range end
        # self.endFull.setValue(100)
        # self.endFull.setSingleStep(1)
        # self.endFull.setRange(0, 100)

        # self.invertLatForce = QCheckBox('Invert Lateral Force', self) #invert

        applyCrossTalk = QCheckBox('Apply Cross Talk', self)  #cross talk flag
        # self.zeroShift = QCheckBox('Shift to Zero', self) #force curve shift to zero

        # self.vertCrossTalk = QDoubleSpinBox(self) #vertical cross talk slope
        # self.vertCrossTalk.setValue(0)
        # self.vertCrossTalk.setSingleStep(0.1)
        # self.vertCrossTalk.setDecimals(4)
        # self.vertCrossTalk.setRange(-1000, 1000)
        # self.vertCTlabel = QLabel("Cross Talk (μN/μN):", self)

        # self.latCrossTalk = QDoubleSpinBox(self) #lateral cross talk slope
        # self.latCrossTalk.setValue(0)
        # self.latCrossTalk.setSingleStep(0.1)
        # self.latCrossTalk.setDecimals(4)
        # self.latCrossTalk.setRange(-1000, 1000)
        # self.latCTlabel = QLabel("Cross Talk (μN/μN):", self)

        CTlabel = QLabel("Cross Talk (μN/μN):", self)  # cross talk slope
        self.crossTalk = QDoubleSpinBox(self)
        self.crossTalk.setValue(0)
        self.crossTalk.setSingleStep(0.1)
        self.crossTalk.setDecimals(4)
        self.crossTalk.setRange(-1000, 1000)
        # self.crossTalk.valueChanged.connect(self.update_dict)

        # self.frictionRange1 = QDoubleSpinBox(self) #friction range start
        # self.frictionRange1.setValue(0)
        # self.frictionRange1.setSingleStep(1)
        # self.frictionRange1.setRange(0, 100)
        # self.frictionRange1.valueChanged.connect(self.update_dict)

        # self.frictionRange2 = QDoubleSpinBox(self) #friction range end
        # self.frictionRange2.setValue(100)
        # self.frictionRange2.setSingleStep(1)
        # self.frictionRange2.setRange(0, 100)
        # self.frictionRange2.valueChanged.connect(self.update_dict)

        # self.prl2Range1 = QDoubleSpinBox(self) #friction preload peak range start
        # self.prl2Range1.setValue(0)
        # self.prl2Range1.setSingleStep(1)
        # self.prl2Range1.setRange(0, 100)
        # self.prl2Range1.valueChanged.connect(self.update_dict)

        # self.prl2Range2 = QDoubleSpinBox(self) #friction preload peak range start
        # self.prl2Range2.setValue(100)
        # self.prl2Range2.setSingleStep(1)
        # self.prl2Range2.setRange(0, 100)
        # self.prl2Range2.valueChanged.connect(self.update_dict)

        # self.fitPosLabel = QLabel("Fit Position\n(x,y):", self) #fit eq. position
        # self.fitPos = QLineEdit(self)
        # self.fitPos.setText('0.5,0.5')

        # self.showFitEq = QCheckBox('Show Slope', self) #display equation on plot

        kBeamLabel = QLabel("Beam Spring Constant (μN/μm):",
                            self)  #beam dpring constant
        kBeam = QLineEdit(self)
        kBeam.setText('30,1')

        # deformStartLabel = QLabel("Deformation Start:", self) #contact start tolerance auto detect
        self.deformBtn = QPushButton("Deformation Range", self)  #deformation
        self.deformLabel = QLineEdit(self)
        self.deformLabel.setReadOnly(True)
        # self.deformLabel.textChanged.connect(self.updateRange)
        self.deformLabel.setText("100,200")
        # self.deformStart = QSpinBox(self)
        # self.deformStart.setValue(100)
        # self.deformStart.setSingleStep(1)
        # self.deformStart.setRange(0, 10000)

        # self.dataAnalDict['misc'] = {}
        self.dataAnalDict['misc settings']['apply cross talk'] = applyCrossTalk
        self.dataAnalDict['misc settings']['noise steps'] = noiseSteps
        self.dataAnalDict['misc settings'][
            'deformation range'] = self.deformLabel
        self.dataAnalDict['misc settings']['beam spring constant'] = kBeam

        self.okBtn = QPushButton("OK", self)  #Close window

        self.updateBtn = QPushButton("Update", self)  #Update

        #update dictionary on widget value change
        self.zeroLabel.textChanged.connect(self.update_dict)
        self.forceLabel.textChanged.connect(self.update_dict)
        self.preloadLabel.textChanged.connect(self.update_dict)
        self.filter.stateChanged.connect(self.update_dict)
        self.filter_wind.valueChanged.connect(self.filter_change)
        self.filter_poly.valueChanged.connect(self.update_dict)
        self.zero_subtract.stateChanged.connect(self.update_dict)
        self.crossTalk.valueChanged.connect(self.update_dict)
        self.zeroLabel.textChanged.connect(self.update_dict)
        self.forceLabel.textChanged.connect(self.update_dict)
        self.preloadLabel.textChanged.connect(self.update_dict)

        # self.zeroGroupBox = QGroupBox("Configure Vertical Force")
        # filterGroupBox = QGroupBox("Configure Plot")
        # flagGroupBox = QGroupBox("Show")
        # self.latCalibGroupBox = QGroupBox("Configure Lateral Force")
        # self.fittingGroupBox = QGroupBox("Fit Data")

        forceGroupBox = QGroupBox("Force")
        miscGroupBox = QGroupBox("Misc")
        buttonGroupBox = QGroupBox()

        forceGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        miscGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.zeroGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # filterGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # flagGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.latCalibGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.fittingGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.fittingGroupBox.setCheckable(True)
        # self.fittingGroupBox.setChecked(False)

        # self.layout.addWidget(self.roiChoice, 0, 0, 1, 2)
        # self.layout.addWidget(self.zeroGroupBox, 1, 0)
        # self.layout.addWidget(filterGroupBox, 2, 1)
        # self.layout.addWidget(flagGroupBox, 2, 0)
        # self.layout.addWidget(self.latCalibGroupBox, 1, 1)
        # self.layout.addWidget(self.fittingGroupBox, 3, 0)
        self.layout.addWidget(forceGroupBox, 0, 0)
        self.layout.addWidget(miscGroupBox, 1, 0)
        self.layout.addWidget(buttonGroupBox, 2, 0)

        self.setLayout(self.layout)

        buttonVbox = QGridLayout()
        buttonGroupBox.setLayout(buttonVbox)
        buttonVbox.addWidget(self.updateBtn, 0, 0)
        buttonVbox.addWidget(self.okBtn, 0, 1)

        forceLayout = QGridLayout()
        forceGroupBox.setLayout(forceLayout)
        forceLayout.addWidget(roiChoiceLabel, 0, 0, 1, 1)
        forceLayout.addWidget(self.roiChoice, 0, 1, 1, 1)
        forceLayout.addWidget(dataChoiceLabel, 0, 2, 1, 1)
        forceLayout.addWidget(self.dataChoice, 0, 3, 1, 1)
        forceLayout.addWidget(self.zeroBtn, 1, 0, 1, 1)
        forceLayout.addWidget(self.zeroLabel, 1, 1, 1, 1)
        forceLayout.addWidget(self.forceBtn, 2, 0, 1, 1)
        forceLayout.addWidget(self.forceLabel, 2, 1, 1, 1)
        forceLayout.addWidget(self.preloadBtn, 3, 0, 1, 1)
        forceLayout.addWidget(self.preloadLabel, 3, 1, 1, 1)
        forceLayout.addWidget(self.filter, 1, 2, 1, 2)
        forceLayout.addWidget(windLabel, 2, 2, 1, 1)
        forceLayout.addWidget(self.filter_wind, 2, 3, 1, 1)
        forceLayout.addWidget(polyLabel, 3, 2, 1, 1)
        forceLayout.addWidget(self.filter_poly, 3, 3, 1, 1)
        forceLayout.addWidget(self.zero_subtract, 4, 2, 1, 2)
        forceLayout.addWidget(CTlabel, 4, 0, 1, 1)
        forceLayout.addWidget(self.crossTalk, 4, 1, 1, 1)

        miscLayout = QGridLayout()
        miscGroupBox.setLayout(miscLayout)
        miscLayout.addWidget(applyCrossTalk, 0, 0, 1, 2)
        miscLayout.addWidget(self.deformBtn, 1, 0, 1, 1)
        miscLayout.addWidget(self.deformLabel, 1, 1, 1, 1)
        miscLayout.addWidget(noiseStepsLabel, 0, 2, 1, 1)
        miscLayout.addWidget(noiseSteps, 0, 3, 1, 1)
        miscLayout.addWidget(kBeamLabel, 1, 2, 1, 1)
        miscLayout.addWidget(kBeam, 1, 3, 1, 1)
        miscLayout.addWidget(eqLabel, 2, 0, 1, 1)  #remove
        miscLayout.addWidget(self.latCalibEq, 2, 1, 1, 1)  #remove
        # miscLayout.addWidget(self.zeroShift, 2, 2, 1, 2) #remove

        # zeroVbox = QGridLayout()
        # self.zeroGroupBox.setLayout(zeroVbox)
        # zeroVbox.addWidget(self.zeroLabel, 0, 1, 1, 1)
        # zeroVbox.addWidget(self.adhLabel, 0, 2, 1, 1)
        # zeroVbox.addWidget(self.prl1Label, 0, 3, 1, 1)
        # zeroVbox.addWidget(self.startLabel, 1, 0, 1, 1)
        # zeroVbox.addWidget(self.endLabel, 2, 0, 1, 1)
        # zeroVbox.addWidget(self.zeroRange1, 1, 1, 1, 1)
        # zeroVbox.addWidget(self.zeroRange2, 2, 1, 1, 1)
        # zeroVbox.addWidget(self.adhRange1, 1, 2, 1, 1)
        # zeroVbox.addWidget(self.adhRange2, 2, 2, 1, 1)
        # zeroVbox.addWidget(self.prl1Range1, 1, 3, 1, 1)
        # zeroVbox.addWidget(self.prl1Range2, 2, 3, 1, 1)
        # zeroVbox.addWidget(self.vertCTlabel, 3, 0, 1, 1)
        # zeroVbox.addWidget(self.vertCrossTalk, 3, 1, 1, 1)

        # filterVbox = QGridLayout()
        # filterGroupBox.setLayout(filterVbox)
        # filterVbox.addWidget(self.filterLatF, 1, 0, 1, 2)
        # filterVbox.addWidget(self.windLabel, 2, 0, 1, 1)
        # filterVbox.addWidget(self.filter_wind, 2, 1, 1, 1)
        # filterVbox.addWidget(self.polyLabel, 3, 0, 1, 1)
        # filterVbox.addWidget(self.filter_poly, 3, 1, 1, 1)
        # # filterVbox.addWidget(self.fontLabel, 2, 2, 1, 1)
        # # filterVbox.addWidget(self.fontSize, 2, 3, 1, 1)
        # filterVbox.addWidget(self.eqLabel, 3, 2, 1, 1)
        # filterVbox.addWidget(self.latCalibEq, 3, 3, 1, 1)
        # # filterVbox.addWidget(self.invertLatForce, 0, 2, 1, 2)
        # filterVbox.addWidget(self.zeroShift, 0, 0, 1, 1)
        # filterVbox.addWidget(self.applyCrossTalk, 1, 2, 1, 2)
        # # filterVbox.addWidget(self.xAxisLabel, 0, 3, 1, 1)
        # # filterVbox.addWidget(self.xAxisParam, 1, 3, 1, 1)
        # filterVbox.addWidget(self.noiseStepsLabel, 4, 2, 1, 1)
        # filterVbox.addWidget(self.noiseSteps, 5, 2, 1, 1)
        # # filterVbox.addWidget(self.legendPosLabel, 4, 3, 1, 1)
        # # filterVbox.addWidget(self.legendPos, 5, 3, 1, 1)
        # # filterVbox.addWidget(self.startFullLabel, 4, 0, 1, 1)
        # # filterVbox.addWidget(self.endFullLabel, 5, 0, 1, 1)
        # # filterVbox.addWidget(self.startFull, 4, 1, 1, 1)
        # # filterVbox.addWidget(self.endFull, 5, 1, 1, 1)
        # filterVbox.addWidget(self.kBeamLabel, 6, 2, 1, 1)
        # filterVbox.addWidget(self.kBeam, 6, 3, 1, 1)
        # filterVbox.addWidget(self.deformStartLabel, 6, 0, 1, 1)
        # filterVbox.addWidget(self.deformStart, 6, 1, 1, 1)

        # flagVbox = QGridLayout()
        # flagGroupBox.setLayout(flagVbox)
        # flagVbox.addWidget(self.showContactArea, 0, 0)
        # flagVbox.addWidget(self.showROIArea, 0, 1)
        # flagVbox.addWidget(self.showZPiezo, 0, 2)
        # flagVbox.addWidget(self.showXPiezo, 1, 0)
        # flagVbox.addWidget(self.showAdhesion, 1, 1)
        # flagVbox.addWidget(self.showFriction, 1, 2)
        # flagVbox.addWidget(self.showLateralForce, 2, 0)
        # flagVbox.addWidget(self.showContactLength, 2, 1)
        # flagVbox.addWidget(self.showROILength, 2, 2)
        # flagVbox.addWidget(self.showContactNumber, 3, 0)
        # flagVbox.addWidget(self.showEcc, 3, 1)
        # flagVbox.addWidget(self.showStress, 3, 2)
        # flagVbox.addWidget(self.showDeformation, 4, 0)
        # flagVbox.addWidget(self.showTitle, 4, 1)
        # flagVbox.addWidget(self.showLegend2, 4, 2)

        # lastCalibVbox = QGridLayout()
        # self.latCalibGroupBox.setLayout(lastCalibVbox)
        # lastCalibVbox.addWidget(self.frLabel, 0, 1, 1, 1)
        # lastCalibVbox.addWidget(self.prl2Label, 0, 2, 1, 1)
        # lastCalibVbox.addWidget(self.zero2Label, 0, 3, 1, 1)
        # lastCalibVbox.addWidget(self.startLabel2, 1, 0, 1, 1)
        # lastCalibVbox.addWidget(self.frictionRange1, 1, 1, 1, 1)
        # lastCalibVbox.addWidget(self.endLabel2, 2, 0, 1, 1)
        # lastCalibVbox.addWidget(self.frictionRange2, 2, 1, 1, 1)
        # lastCalibVbox.addWidget(self.prl2Range1, 1, 2, 1, 1)
        # lastCalibVbox.addWidget(self.prl2Range2, 2, 2, 1, 1)
        # lastCalibVbox.addWidget(self.zero2Range1, 1, 3, 1, 1)
        # lastCalibVbox.addWidget(self.zero2Range2, 2, 3, 1, 1)
        # lastCalibVbox.addWidget(self.latCTlabel, 3, 0, 1, 1)
        # lastCalibVbox.addWidget(self.latCrossTalk, 3, 1, 1, 1)

        # fittingVbox = QGridLayout()
        # self.fittingGroupBox.setLayout(fittingVbox)
        # fittingVbox.addWidget(self.startFitLabel, 0, 0, 1, 1)
        # fittingVbox.addWidget(self.endFitLabel, 1, 0, 1, 1)
        # fittingVbox.addWidget(self.fitStart, 0, 1, 1, 1)
        # fittingVbox.addWidget(self.fitStop, 1, 1, 1, 1)
        # fittingVbox.addWidget(self.xFitLabel, 0, 2, 1, 1)
        # fittingVbox.addWidget(self.yFitLabel, 1, 2, 1, 1)
        # fittingVbox.addWidget(self.xFit, 0, 3, 1, 1)
        # fittingVbox.addWidget(self.yFit, 1, 3, 1, 1)
        # fittingVbox.addWidget(self.fitPosLabel, 0, 4, 1, 1)
        # fittingVbox.addWidget(self.fitPos, 0, 5, 1, 1)
        # fittingVbox.addWidget(self.showFitEq, 1, 4, 1, 2)

    def filter_change(self):
        if self.filter_wind.value() % 2 == 0:  #make sure its odd
            self.filter_wind.blockSignals(True)
            self.filter_wind.setValue(self.filter_wind.value() + 1)
            self.filter_wind.blockSignals(False)
        self.update_dict()

    # def update_range(self):
    #     key = self.roiChoice.currentText()
    #     if key not in self.rangeDict.keys():
    #         key = "Default"

    #     self.zeroRange1.blockSignals(True)
    #     self.zeroRange1.setValue(self.rangeDict[key][0][0])
    #     self.zeroRange1.blockSignals(False)
    #     self.zeroRange2.blockSignals(True)
    #     self.zeroRange2.setValue(self.rangeDict[key][0][1])
    #     self.zeroRange2.blockSignals(False)
    #     self.adhRange1.blockSignals(True)
    #     self.adhRange1.setValue(self.rangeDict[key][1][0])
    #     self.adhRange1.blockSignals(False)
    #     self.adhRange2.blockSignals(True)
    #     self.adhRange2.setValue(self.rangeDict[key][1][1])
    #     self.adhRange2.blockSignals(False)
    #     self.prl1Range1.blockSignals(True)
    #     self.prl1Range1.setValue(self.rangeDict[key][2][0])
    #     self.prl1Range1.blockSignals(False)
    #     self.prl1Range2.blockSignals(True)
    #     self.prl1Range2.setValue(self.rangeDict[key][2][1])
    #     self.prl1Range2.blockSignals(False)
    #     self.frictionRange1.blockSignals(True)
    #     self.frictionRange1.setValue(self.rangeDict[key][3][0])
    #     self.frictionRange1.blockSignals(False)
    #     self.frictionRange2.blockSignals(True)
    #     self.frictionRange2.setValue(self.rangeDict[key][3][1])
    #     self.frictionRange2.blockSignals(False)
    #     self.prl2Range1.blockSignals(True)
    #     self.prl2Range1.setValue(self.rangeDict[key][4][0])
    #     self.prl2Range1.blockSignals(False)
    #     self.prl2Range2.blockSignals(True)
    #     self.prl2Range2.setValue(self.rangeDict[key][4][1])
    #     self.prl2Range2.blockSignals(False)
    #     self.zero2Range1.blockSignals(True)
    #     self.zero2Range1.setValue(self.rangeDict[key][5][0])
    #     self.zero2Range1.blockSignals(False)
    #     self.zero2Range2.blockSignals(True)
    #     self.zero2Range2.setValue(self.rangeDict[key][5][1])
    #     self.zero2Range2.blockSignals(False)

    def update_widgets(self):
        self.forceBtn.setText(
            self.dataChoiceDict[self.dataChoice.currentText()] + " Range")
        # range_dict = self.dataAnalDict['force settings'][self.roiChoice.currentText()][self.dataChoice.currentText()]
        range_dict = self.dataAnalDict[self.dataChoice.currentText(
        )]["ranges"][self.roiChoice.currentText()]
        transform_dict = self.dataAnalDict[
            self.dataChoice.currentText()]["transform"]
        self.zeroLabel.blockSignals(True)
        self.zeroLabel.setText(range_dict["Zero"])
        self.zeroLabel.blockSignals(False)
        self.forceLabel.blockSignals(True)
        self.forceLabel.setText(range_dict["Force"])
        self.forceLabel.blockSignals(False)
        self.preloadLabel.blockSignals(True)
        self.preloadLabel.setText(range_dict["Preload"])
        self.preloadLabel.blockSignals(False)
        self.filter.blockSignals(True)
        self.filter.setChecked(transform_dict["Filter"])
        self.filter.blockSignals(False)
        self.filter_wind.blockSignals(True)
        self.filter_wind.setValue(transform_dict["Filter window"])
        self.filter_wind.blockSignals(False)
        self.filter_poly.blockSignals(True)
        self.filter_poly.setValue(transform_dict["Filter order"])
        self.filter_poly.blockSignals(False)
        self.zero_subtract.blockSignals(True)
        self.zero_subtract.setChecked(transform_dict["Zero subtract"])
        self.zero_subtract.blockSignals(False)
        self.crossTalk.blockSignals(True)
        self.crossTalk.setValue(transform_dict["Cross Talk"])
        self.crossTalk.blockSignals(False)

    def update_dict(self):
        # range_dict = self.dataAnalDict['force settings'][self.roiChoice.currentText()][self.dataChoice.currentText()]
        range_dict = self.dataAnalDict[self.dataChoice.currentText(
        )]["ranges"][self.roiChoice.currentText()]
        transform_dict = self.dataAnalDict[
            self.dataChoice.currentText()]["transform"]
        range_dict["Zero"] = self.zeroLabel.text()
        range_dict["Force"] = self.forceLabel.text()
        range_dict["Preload"] = self.preloadLabel.text()
        transform_dict["Filter"] = self.filter.isChecked()
        transform_dict["Filter window"] = self.filter_wind.value()
        transform_dict["Filter order"] = self.filter_poly.value()
        transform_dict["Zero subtract"] = self.zero_subtract.isChecked()
        transform_dict["Cross Talk"] = self.crossTalk.value()
        # self.rangeDict[self.roiChoice.currentText()] = [[self.zeroRange1.value(),
        #                                                    self.zeroRange2.value()],
        #                                                 [self.adhRange1.value(),
        #                                                    self.adhRange2.value()],
        #                                                 [self.prl1Range1.value(),
        #                                                    self.prl1Range2.value()],
        #                                                 [self.frictionRange1.value(),
        #                                                    self.frictionRange2.value()],
        #                                                 [self.prl2Range1.value(),
        #                                                  self.prl2Range2.value()],
        #                                                 [self.zero2Range1.value(),
        #                                                    self.zero2Range2.value()]]
        logging.debug('%s', self.dataAnalDict)

    def show_window(self):  #show window
        # self.update_range()
        self.update_dict()
        self.show()
Esempio n. 11
0
class myQLineEditPlus(QWidget):
    changed = pyqtSignal()

    def __init__(self, parent=None):
        QWidget.__init__(self)
        self.parent = parent
        self.lbl = QLabel(self)
        self.chk = QCheckBox(self)
        #self.chk.setFocusPolicy(Qt.NoFocus)
        self.txt = QLineEdit(self)
        self.lblSuffix = QLabel(self)
        self.lay = QHBoxLayout(self)
        self.lay.addWidget(self.lbl)
        self.lay.addWidget(self.chk)
        self.lay.addWidget(self.txt)
        self.lay.addWidget(self.lblSuffix)
        self.setLayout(self.lay)

        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(1)
        self.lbl.setSizePolicy(sizePolicy)

        self.setSuffix("")
        self.setValue(None)
        self.setLabel(self.tr("Add a value"))

        self.chk.stateChanged.connect(self.on_chk_stateChanged)
        self.txt.textChanged.connect(self.on_textChanged)
        self.txt.setMaxLength(30)
        self.txt.setAlignment(Qt.AlignRight)

    ## Functión to get the suffix in a label next to qlineedit
    def getSuffix(self):
        return self.lblSuffix.text()

    ## Functión to set the suffix in a label next to qlineedit
    def setSuffix(self, value):
        if value == None or value == "":
            self.lblSuffix.hide()
        else:
            self.lblSuffix.show()
            self.lblSuffix.setText(value)

    ## Functión to get the label next to qlineedit
    def getLabel(self):
        return self.lbl.text()

    ## Functión to set the label next to qlineedit
    def setLabel(self, value):
        value = "" if value == None else value
        if value == "":
            self.lbl.hide()
        else:
            self.lbl.show()
            self.lbl.setText(value)

    def value(self):
        if self.chk.isChecked() == False:
            return None
        if self.isNumber():
            return Decimal(self.txt.text())
        return None

    def setValue(self, v):
        self.txt.blockSignals(True)
        if v == None:
            self.chk.setCheckState(Qt.Unchecked)
            self.txt.setEnabled(False)
            self.txt.setText("")
        else:
            self.chk.setCheckState(Qt.Checked)
            self.txt.setEnabled(True)
            self.txt.setText(str(v))
        self.txt.blockSignals(False)
        self.changed.emit()

    def on_chk_stateChanged(self, state):
        if state == Qt.Unchecked:
            self.setValue(None)
        else:
            self.setValue("0")
            self.chk.setFocusPolicy(Qt.NoFocus)
            self.txt.setFocus()
            self.txt.selectAll()

    def isChecked(self):
        return self.chk.isChecked()

    def isValid(self):
        if self.isNumber() or self.chk.isChecked() == False:
            return True
        else:
            return False

    def isNumber(self):
        try:
            Decimal(self.txt.text())
            return True
        except:
            return False

    @pyqtSlot(str)
    def on_textChanged(self, text):
        pos = self.txt.cursorPosition()
        text = text.replace(",", ".")
        text = text.replace("e", "0")  #Avoids scientific numbers
        self.setValue(text)
        if self.isChecked() == False:
            self.txt.setStyleSheet(
                "QLineEdit { background-color: rgb(239, 239, 239); }")
        elif self.isNumber():
            self.txt.setStyleSheet(
                "QLineEdit { background-color: rgb(255, 255, 255); }")
        else:
            self.txt.setStyleSheet(
                "QLineEdit { background-color: rgb(255, 182, 182); }")
        self.txt.setCursorPosition(pos)

    def setMandatory(self, b):
        if b == True:
            self.chk.hide()
            self.chk.setCheckState(Qt.Checked)
            #self.txt.setText("0")
            #self.txt.setEnabled(True)
        else:
            self.chk.show()
Esempio n. 12
0
class CaesarCipherDialog(QDialog):
    def __init__(self, input, callback):
        super(CaesarCipherDialog, self).__init__()
        main_layout = QVBoxLayout()
        main_layout.addWidget(self._init_editor_frame())
        main_layout.addWidget(self._init_button_box())
        self.setLayout(main_layout)
        self.setWindowIcon(qtawesome.icon("fa.edit"))
        self.setWindowTitle("Caesar Cipher")
        self._setup_shortcuts()
        self._input = input
        self._text_area.setPlainText(self._input)
        self._callback = callback

    def _setup_shortcuts(self):
        ctrl_return_shortcut = QShortcut(
            QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Return), self)
        ctrl_return_shortcut.activated.connect(self._accept)
        alt_return_shortcut = QShortcut(
            QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_Return), self)
        alt_return_shortcut.activated.connect(self._accept)
        alt_o_shortcut = QShortcut(
            QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_O), self)
        alt_o_shortcut.activated.connect(self._accept)

    def _init_button_box(self):
        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self._accept)
        button_box.rejected.connect(self.reject)
        return button_box

    def _init_editor_frame(self):
        main_frame = QFrame()
        main_frame_layout = QVBoxLayout()

        slider_frame = self._init_slider_frame()
        main_frame_layout.addWidget(slider_frame)

        self._text_area = QPlainTextEdit()
        self._text_area.setReadOnly(True)
        self._text_area.setFixedHeight(126)
        main_frame_layout.addWidget(self._text_area)

        main_frame.setLayout(main_frame_layout)
        return main_frame

    def _init_slider_frame(self):
        slider_frame = QFrame()
        slider_frame_layout = QHBoxLayout()
        self._shift_slider = QSlider(QtCore.Qt.Horizontal)
        self._shift_slider.setMinimum(0)
        self._shift_slider.setMaximum(26)
        self._shift_slider.setValue(0)
        self._shift_slider.valueChanged.connect(self._shift_slider_changed)
        slider_frame_layout.addWidget(self._shift_slider)
        self._shift_text = QLineEdit()
        self._shift_text.setText("0")
        self._shift_text.setFixedWidth(30)
        self._shift_text.setValidator(QIntValidator(0, 26))
        self._shift_text.textChanged.connect(self._shift_text_changed)
        slider_frame_layout.addWidget(self._shift_text)
        slider_frame.setLayout(slider_frame_layout)
        return slider_frame

    def _shift_slider_changed(self, shift):
        if not shift:
            shift = 0
        self._shift_changed(shift)

    def _shift_text_changed(self, shift):
        if not shift:
            shift = 0
        self._shift_changed(int(shift))

    def _shift_changed(self, shift):
        self._shift_text.blockSignals(True)
        self._shift_slider.blockSignals(True)
        self._shift_slider.setValue(shift)
        self._shift_text.setText(str(shift))
        self._text_area.setPlainText(self._callback(self._input, shift))
        self._shift_slider.blockSignals(False)
        self._shift_text.blockSignals(False)

    def _accept(self):
        self.accept()

    def getShift(self):
        return self._shift_slider.value()
Esempio n. 13
0
File: iv.py Progetto: wi1k1n/pytb
class iv(QMainWindow):
    zoom_factor = 1.1
    x_zoom = True
    y_zoom = True
    x_stop_at_orig = True
    y_stop_at_orig = True

    def __init__(self, *args, **kwargs):
        app = QtCore.QCoreApplication.instance()
        if app is None:
            app = QApplication([''])
        QMainWindow.__init__(self, parent=None)

        timestamp = datetime.now().strftime("%y%m%d_%H%M%S")
        self.setWindowTitle('iv ' + timestamp)

        shell = get_ipython()
        shell.magic('%matplotlib qt')

        # store list of input images
        if len(args) == 1 and isinstance(args[0], torch.Tensor):
            # handle torch.Tensor input
            if args[0].ndim <= 3:
                self.images = [args[0].detach().cpu().numpy()]
            elif args[0].ndim == 4:
                # probably a torch tensor with dimensions [batch, channels, y, x]
                self.images = [[]] * args[0].shape[0]
                tmp = args[0].detach().cpu().numpy().transpose((2, 3, 1, 0))
                for imind in range(tmp.shape[3]):
                    self.images[imind] = tmp[:, :, :, imind]
                del tmp
            else:
                raise Exception('torch tensors can at most have 4 dimensions')

        elif len(args) == 1 and isinstance(args[0], np.ndarray) and len(
                args[0].shape) == 4:
            # handle 4D numpy.ndarray input by slicing in 4th dimension
            self.images = [[]] * args[0].shape[3]
            for imind in range(args[0].shape[3]):
                self.images[imind] = args[0][:, :, :, imind]

        elif len(args) == 1 and (isinstance(args[0], list)
                                 or isinstance(args[0], tuple)):
            self.images = list(args[0])

        else:
            self.images = list(args)

        for imind in range(len(self.images)):
            if isinstance(self.images[imind], torch.Tensor):
                self.images[imind] = self.images[imind].detach().cpu().numpy()
                if self.images[imind].ndim == 4:
                    # probably a torch tensor with dimensions [batch, channels, y, x]
                    self.images[imind] = self.images[imind].transpose(
                        (2, 3, 1, 0))
                elif self.images[imind].ndim > 4:
                    raise Exception(
                        'torch tensors can at most have 4 dimensions')

            self.images[imind] = np.atleast_3d(self.images[imind])
            if self.images[imind].shape[2] != 1 and self.images[imind].shape[
                    2] != 3:
                if self.images[imind].ndim == 4:

                    self.images[imind] = self.images[imind].transpose(
                        (2, 3, 1, 0))

        self.imind = 0  # currently selected image
        self.nims = len(self.images)
        self.scale = 1.
        self.gamma = 1.
        self.offset = 0.
        self.autoscalePrctile = 0.1
        self.autoscaleUsePrctiles = True
        self.autoscaleOnChange = False
        self.autoscalePerImg = False
        self.collageActive = False
        self.collageTranspose = False
        self.collageTransposeIms = False
        self.collage_nc = int(np.ceil(np.sqrt(self.nims)))
        self.collage_nr = int(np.ceil(self.nims / self.collage_nc))
        self.collage_border_width = 0
        self.collage_border_value = 0.
        self.crop = kwargs.get('crop', False)
        self.crop_global = kwargs.get('crop_global', True)
        self.zoom_factor = 1.1
        self.x_zoom = True
        self.y_zoom = True
        self.x_stop_at_orig = True
        self.y_stop_at_orig = True
        self.annotate = False
        self.font_size = 12

        self.crop_bounds()
        self.initUI()

        self.ax.set_xticks([])
        self.ax.set_yticks([])
        #plt.tight_layout()
        self.updateImage()
        if self.autoscaleOnChange:
            self.autoscale()
        self.cur_xlims = self.ih.axes.axis()[0:2]
        self.cur_ylims = self.ih.axes.axis()[2:]

        self.mouse_down = 0
        self.x_start = 0
        self.y_start = 0
        self.cid = self.fig.canvas.mpl_connect('button_press_event',
                                               self.onclick)
        self.cid = self.fig.canvas.mpl_connect('button_release_event',
                                               self.onrelease)
        self.cid = self.fig.canvas.mpl_connect('motion_notify_event',
                                               self.onmotion)
        self.cid = self.fig.canvas.mpl_connect(
            'key_press_event', self.keyPressEvent)  #onkeypress)
        self.cid = self.fig.canvas.mpl_connect(
            'key_release_event', self.keyReleaseEvent)  #onkeyrelease)
        self.cid = self.fig.canvas.mpl_connect('scroll_event', self.onscroll)
        self.alt = False
        self.control = False
        self.shift = False
        self.prev_delta_x = 0
        self.prev_delta_y = 0
        #plt.show(block=True)
        #plt.pause(10)
        #plt.show(block=False)

        self.setWindowModality(QtCore.Qt.WindowModal)
        self.show()

    def crop_bounds(self):
        # pre-compute cropping bounds (tight bounding box around non-zero pixels)
        nzs = [np.where(np.sum(im, axis=2) > 0) for im in self.images]
        self.xmins = [np.min(nz[1]) if len(nz[1]) else 0 for nz in nzs]
        self.xmaxs = [
            np.max(nz[1]) + 1 if len(nz[1]) else im.shape[1]
            for nz, im in zip(nzs, self.images)
        ]  # +1 to allow easier indexing
        self.ymins = [np.min(nz[0]) if len(nz[0]) else 0 for nz in nzs]
        self.ymaxs = [
            np.max(nz[0]) + 1 if len(nz[0]) else im.shape[0]
            for nz, im in zip(nzs, self.images)
        ]  # +1 to allow easier indexing
        if self.crop_global:
            self.xmins = [np.min(self.xmins) for _ in self.xmins]
            self.xmaxs = [np.max(self.xmaxs) for _ in self.xmaxs]
            self.ymins = [np.min(self.ymins) for _ in self.ymins]
            self.ymaxs = [np.max(self.ymaxs) for _ in self.ymaxs]

    def initUI(self):
        #self.fig = plt.figure(figsize = (10, 10))
        #self.ax = plt.axes([0,0,1,1])#, self.gs[0])

        self.widget = QWidget()

        self.fig = Figure(dpi=100)
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.widget)

        #self.ax = Axes(fig=self.fig, rect=[0,0,1,1])
        self.ax = self.fig.add_subplot(111)
        self.ax.set_position(Bbox([[0, 0], [1, 1]]))
        self.ax.set_anchor('NW')
        try:
            self.ax.get_yaxis().set_inverted(True)
        except Exception:
            self.ax.invert_yaxis()

        self.uiLabelModifiers = QLabel('')
        self.uiLEScale = QLineEdit(str(self.scale))
        self.uiLEScale.setMinimumWidth(200)
        self.uiLEScale.editingFinished.connect(
            lambda: self.callbackLineEdit(self.uiLEScale))
        self.uiLEGamma = QLineEdit(str(self.gamma))
        self.uiLEGamma.setMinimumWidth(200)
        self.uiLEGamma.editingFinished.connect(
            lambda: self.callbackLineEdit(self.uiLEGamma))
        self.uiLEOffset = QLineEdit(str(self.offset))
        self.uiLEOffset.setMinimumWidth(200)
        self.uiLEOffset.editingFinished.connect(
            lambda: self.callbackLineEdit(self.uiLEOffset))
        self.uiLEAutoscalePrctile = QLineEdit(str(self.autoscalePrctile))
        self.uiLEAutoscalePrctile.setMinimumWidth(200)
        self.uiLEAutoscalePrctile.editingFinished.connect(
            lambda: self.callbackLineEdit(self.uiLEAutoscalePrctile))
        self.uiCBAutoscaleUsePrctiles = QCheckBox('use percentiles')
        self.uiCBAutoscaleUsePrctiles.setCheckState(self.autoscaleUsePrctiles)
        self.uiCBAutoscaleUsePrctiles.setTristate(False)
        self.uiCBAutoscaleUsePrctiles.stateChanged.connect(
            lambda state: self.callbackCheckBox(self.uiCBAutoscaleUsePrctiles,
                                                state))
        self.uiCBAutoscaleOnChange = QCheckBox('on change')
        self.uiCBAutoscaleOnChange.setCheckState(self.autoscaleOnChange)
        self.uiCBAutoscaleOnChange.setTristate(False)
        self.uiCBAutoscaleOnChange.stateChanged.connect(
            lambda state: self.callbackCheckBox(self.uiCBAutoscaleOnChange,
                                                state))
        if self.nims > 1:
            self.uiCBAutoscalePerImg = QCheckBox('per image')
            self.uiCBAutoscalePerImg.setCheckState(self.autoscalePerImg)
            self.uiCBAutoscalePerImg.setTristate(False)
            self.uiCBAutoscalePerImg.stateChanged.connect(
                lambda state: self.callbackCheckBox(self.uiCBAutoscalePerImg,
                                                    state))
            self.uiCBCollageActive = QCheckBox('enable')
            self.uiCBCollageActive.setCheckState(self.collageActive)
            self.uiCBCollageActive.setTristate(False)
            self.uiCBCollageActive.stateChanged.connect(
                lambda state: self.callbackCheckBox(self.uiCBCollageActive,
                                                    state))
            self.uiCBCollageTranspose = QCheckBox('transpose')
            self.uiCBCollageTranspose.setCheckState(self.collageTranspose)
            self.uiCBCollageTranspose.setTristate(False)
            self.uiCBCollageTranspose.stateChanged.connect(
                lambda state: self.callbackCheckBox(self.uiCBCollageTranspose,
                                                    state))
            self.uiCBCollageTransposeIms = QCheckBox('transpose images')
            self.uiCBCollageTransposeIms.setCheckState(
                self.collageTransposeIms)
            self.uiCBCollageTransposeIms.setTristate(False)
            self.uiCBCollageTransposeIms.stateChanged.connect(
                lambda state: self.callbackCheckBox(
                    self.uiCBCollageTransposeIms, state))
            self.uiLECollageNr = QLineEdit(str(self.collage_nr))
            self.uiLECollageNr.setMinimumWidth(200)
            self.uiLECollageNr.editingFinished.connect(
                lambda: self.callbackLineEdit(self.uiLECollageNr))
            self.uiLECollageNc = QLineEdit(str(self.collage_nc))
            self.uiLECollageNc.setMinimumWidth(200)
            self.uiLECollageNc.editingFinished.connect(
                lambda: self.callbackLineEdit(self.uiLECollageNc))
            self.uiLECollageBW = QLineEdit(str(self.collage_border_width))
            self.uiLECollageBW.setMinimumWidth(200)
            self.uiLECollageBW.editingFinished.connect(
                lambda: self.callbackLineEdit(self.uiLECollageBW))
            self.uiLECollageBV = QLineEdit(str(self.collage_border_value))
            self.uiLECollageBV.setMinimumWidth(200)
            self.uiLECollageBV.editingFinished.connect(
                lambda: self.callbackLineEdit(self.uiLECollageBV))
        self.uiCBCrop = QCheckBox('enable')
        self.uiCBCrop.setCheckState(self.crop)
        self.uiCBCrop.setTristate(False)
        self.uiCBCrop.stateChanged.connect(
            lambda state: self.callbackCheckBox(self.uiCBCrop, state))
        self.uiCBCropGlobal = QCheckBox('enable')
        self.uiCBCropGlobal.setCheckState(self.crop_global)
        self.uiCBCropGlobal.setTristate(False)
        self.uiCBCropGlobal.stateChanged.connect(
            lambda state: self.callbackCheckBox(self.uiCBCropGlobal, state))
        self.uiCBAnnotate = QCheckBox('enable')
        self.uiCBAnnotate.setCheckState(self.annotate)
        self.uiCBAnnotate.setTristate(False)
        self.uiCBAnnotate.stateChanged.connect(
            lambda state: self.callbackCheckBox(self.uiCBAnnotate, state))
        self.uiLEFontSize = QLineEdit(str(self.font_size))
        self.uiLEFontSize.setMinimumWidth(200)
        self.uiLEFontSize.editingFinished.connect(
            lambda: self.callbackLineEdit(self.uiLEFontSize))
        self.uiPBCopyClipboard = QPushButton('&copy')
        self.uiPBCopyClipboard.clicked.connect(
            lambda: self.callbackPushButton(self.uiPBCopyClipboard))

        form = QFormLayout()
        form.addRow(QLabel('modifiers:'), self.uiLabelModifiers)
        form.addRow(QLabel('scale:'), self.uiLEScale)
        form.addRow(QLabel('gamma:'), self.uiLEGamma)
        form.addRow(QLabel('offset:'), self.uiLEOffset)
        form.addRow(QLabel('autoScale:'), self.uiCBAutoscaleUsePrctiles)
        form.addRow(QLabel('percentile:'), self.uiLEAutoscalePrctile)
        form.addRow(QLabel('autoScale:'), self.uiCBAutoscaleOnChange)
        if self.nims > 1:
            form.addRow(QLabel('autoScale:'), self.uiCBAutoscalePerImg)
            form.addRow(QLabel('collage:'), self.uiCBCollageActive)
            form.addRow(QLabel('collage:'), self.uiCBCollageTranspose)
            form.addRow(QLabel('collage:'), self.uiCBCollageTransposeIms)
            form.addRow(QLabel('collage #rows:'), self.uiLECollageNr)
            form.addRow(QLabel('collage #cols:'), self.uiLECollageNc)
            form.addRow(QLabel('collage #BW:'), self.uiLECollageBW)
            form.addRow(QLabel('collage #BV:'), self.uiLECollageBV)
        form.addRow(QLabel('crop:'), self.uiCBCrop)
        form.addRow(QLabel('crop global:'), self.uiCBCropGlobal)
        form.addRow(QLabel('annotate:'), self.uiCBAnnotate)
        form.addRow(QLabel('font size:'), self.uiLEFontSize)
        form_bottom = QFormLayout()
        form_bottom.addRow(self.uiPBCopyClipboard)
        vbox = QVBoxLayout()
        vbox.addLayout(form)
        vbox.addItem(QSpacerItem(1, 1, vPolicy=QSizePolicy.Expanding))
        vbox.addLayout(form_bottom)

        hbox = QHBoxLayout()
        hbox.addWidget(self.canvas)
        hbox.addLayout(vbox)

        self.widget.setLayout(hbox)
        self.setCentralWidget(self.widget)

        # make image canvas expand with window
        sp = self.canvas.sizePolicy()
        sp.setHorizontalStretch(1)
        sp.setVerticalStretch(1)
        self.canvas.setSizePolicy(sp)

        self.ih = self.ax.imshow(np.zeros(self.get_img().shape[:2] + (3, )),
                                 origin='upper')
        self.ax.set_position(Bbox([[0, 0], [1, 1]]))
        try:
            self.ax.get_yaxis().set_inverted(True)
        except Exception:
            self.ax.invert_yaxis()

        # keyboard shortcuts
        #scaleShortcut = QShortcut(QKeySequence('Ctrl+Shift+a'), self.widget)
        #scaleShortcut.activated.connect(self.autoscale)
        closeShortcut = QShortcut(QKeySequence('Escape'), self.widget)
        closeShortcut.activated.connect(self.close)
        QShortcut(QKeySequence('a'),
                  self.widget).activated.connect(self.autoscale)
        QShortcut(QKeySequence('Shift+a'), self.widget).activated.connect(
            self.toggleautoscaleUsePrctiles)

    #@MyPyQtSlot("bool")
    def callbackLineEdit(self, ui):
        try:
            tmp = float(ui.text())
        except:
            return

        if ui == self.uiLEScale:
            self.setScale(tmp)
        elif ui == self.uiLEGamma:
            self.setGamma(tmp)
        elif ui == self.uiLEOffset:
            self.setOffset(tmp)
        elif ui == self.uiLEAutoscalePrctile:
            self.autoscalePrctile = tmp
            self.autoscale()
        elif ui == self.uiLECollageNr:
            self.collage_nr = int(tmp)
            self.collage()
        elif ui == self.uiLECollageNc:
            self.collage_nc = int(tmp)
            self.collage()
        elif ui == self.uiLECollageBW:
            self.collage_border_width = int(tmp)
            self.collage()
        elif ui == self.uiLECollageBV:
            self.collage_border_value = float(tmp)
            self.collage()
        elif ui == self.uiLEFontSize:
            self.font_size = int(tmp)
            self.updateImage()

    #@MyPyQtSlot("bool")
    def callbackCheckBox(self, ui, state):
        if ui == self.uiCBAutoscaleUsePrctiles:
            self.autoscaleUsePrctiles = bool(state)
            if self.autoscaleOnChange:
                self.autoscale()
        elif ui == self.uiCBAutoscaleOnChange:
            self.autoscaleOnChange = bool(state)
            if self.autoscaleOnChange:
                self.autoscale()
        elif ui == self.uiCBAutoscalePerImg:
            self.autoscalePerImg = bool(state)
            self.autoscale()
        elif ui == self.uiCBCollageActive:
            self.collageActive = bool(state)
            self.updateImage()
        elif ui == self.uiCBCollageTranspose:
            self.collageTranspose = bool(state)
            self.updateImage()
        elif ui == self.uiCBCollageTransposeIms:
            self.collageTransposeIms = bool(state)
            self.updateImage()
        elif ui == self.uiCBCrop:
            self.crop = bool(state)
            self.updateImage()
        elif ui == self.uiCBCropGlobal:
            self.crop_global = bool(state)
            self.crop_bounds()
            self.updateImage()
        elif ui == self.uiCBAnnotate:
            self.annotate = bool(state)
            self.updateImage()

    def callbackPushButton(self, ui):
        if ui == self.uiPBCopyClipboard:
            self.copy_to_clipboard()

    '''
    @MyPyQtSlot()
    def slot_text(self):#, ui=None):
        ui = self.uiLEScale
        if ui == self.uiLEScale:
            print('scale: ' + str(self.scale))
            tmp = self.scale
            try:
                tmp = float(self.uiLEScale.text())
            except ValueError:
                print('error')
                self.uiLEScale.setText(str(self.scale))
            self.scale = tmp
            self.updateImage()
        elif ui == self.uiLEGamma:
            print('gamma')
        elif ui == self.uiLEOffset:
            print('offset')
    
    def on_draw(self):
        """ Redraws the figure
        """
        #self.axes.grid(self.grid_cb.isChecked())
        self.canvas.draw()
    '''

    def print_usage(self):
        print(' ')
        print('hotkeys: ')
        print('a: trigger autoscale')
        print('A: toggle autoscale of [min, max] or ')
        print('   [prctile_low, prctile_high] -> [0, 1], ')
        print('   prctiles can be changed via ctrl+shift+wheel')
        print('c: toggle autoscale on image change')
        print('G: reset gamma to 1')
        print('L: create collage by arranging all images in a ')
        print('   rectangular manner')
        print('O: reset offset to 0')
        print('p: toggle per image auto scale limit computations ')
        print('   (vs. globally over all images)')
        print('S: reset scale to 1')
        print('Z: reset zoom to 100%')
        print('left / right:         switch to next / previous image')
        print('page down / up:       go through images in ~10% steps')
        print('')
        print('wheel:                zoom in / out (inside image axes)')
        print('wheel:                switch to next / previous image')
        print('                      (outside image axes)')
        print('ctrl + wheel:         scale up / down')
        print('shift + wheel:        gamma up / down')
        print('ctrl + shift + wheel: increase / decrease autoscale')
        print('                      percentiles')
        print('left mouse dragged:   pan image')
        print('')

    def get_img(self, i=None):
        if i is None:
            i = self.imind
        im = self.images[i]
        if self.crop:
            im = im[self.ymins[i]:self.ymaxs[i],
                    self.xmins[i]:self.xmaxs[i], :]
        if self.annotate:
            from pytb.utils import annotate_image
            im = annotate_image(im, str(i), font_size=self.font_size)
        return im

    def get_imgs(self):
        return [self.get_img(ind) for ind in range(len(self.images))]

    def copy_to_clipboard(self):
        from PyQt5.Qt import QImage
        im = (255 * self.ih.get_array()).astype(np.uint8)
        h, w, nc = im.shape[:3]
        im = QImage(im.tobytes(), w, h, nc * w, QImage.Format_RGB888)
        c = QApplication.clipboard()
        c.setImage(im)

    def autoscale(self):
        # autoscale between user-selected percentiles
        if self.autoscaleUsePrctiles:
            if self.autoscalePerImg:
                lower, upper = np.percentile(
                    self.get_img(),
                    (self.autoscalePrctile, 100 - self.autoscalePrctile))
            else:
                limits = [
                    np.percentile(
                        image,
                        (self.autoscalePrctile, 100 - self.autoscalePrctile))
                    for image in self.get_imgs()
                ]
                lower = np.min([lims[0] for lims in limits])
                upper = np.max([lims[1] for lims in limits])
        else:
            if self.autoscalePerImg:
                lower = np.min(self.get_img())
                upper = np.max(self.get_img())
            else:
                lower = np.min([np.min(image) for image in self.get_imgs()])
                upper = np.max([np.max(image) for image in self.get_imgs()])
        self.setOffset(lower, False)
        self.setScale(1. / (upper - lower), True)

    def toggleautoscaleUsePrctiles(self):
        self.autoscaleUsePrctiles = not self.autoscaleUsePrctiles
        self.autoscale()

    def collage(self):
        if self.collage_nr * self.collage_nc < self.nims:
            nc = int(np.ceil(np.sqrt(self.nims)))
            nr = int(np.ceil(self.nims / nc))
            self.collage_nr = nr
            self.collage_nc = nc
            self.uiLECollageNr.blockSignals(True)
            self.uiLECollageNc.blockSignals(True)
            self.uiLECollageNr.setText(str(nr))
            self.uiLECollageNc.setText(str(nc))
            self.uiLECollageNr.blockSignals(False)
            self.uiLECollageNc.blockSignals(False)
        else:
            nc = self.collage_nc
            nr = self.collage_nr

        # pad array so it matches the product nc * nr
        padding = nc * nr - self.nims
        ims = self.get_imgs()
        h = np.max([im.shape[0] for im in ims])
        w = np.max([im.shape[1] for im in ims])
        numChans = np.max([im.shape[2] for im in ims])
        ims = [
            pad(im, new_width=w, new_height=h, new_num_channels=numChans)
            for im in ims
        ]
        ims += [np.zeros((h, w, numChans))] * padding
        coll = np.stack(ims, axis=3)
        coll = np.reshape(coll, (h, w, numChans, nc, nr))
        # 0  1  2   3   4
        # y, x, ch, co, ro
        if self.collage_border_width:
            # pad each patch by border if requested
            coll = np.append(
                coll,
                self.collage_border_value *
                np.ones((self.collage_border_width, ) + coll.shape[1:5]),
                axis=0)
            coll = np.append(
                coll,
                self.collage_border_value *
                np.ones((coll.shape[0], self.collage_border_width) +
                        coll.shape[2:5]),
                axis=1)
        if self.collageTranspose:
            nim0 = nr
            nim1 = nc
            if self.collageTransposeIms:
                dim0 = w
                dim1 = h
                #                          nr w  nc h  ch
                coll = np.transpose(coll, (4, 1, 3, 0, 2))
            else:
                dim0 = h
                dim1 = w
                #                          nr h  nc w  ch
                coll = np.transpose(coll, (4, 0, 3, 1, 2))
        else:
            nim0 = nc
            nim1 = nr
            if self.collageTransposeIms:
                dim0 = w
                dim1 = h
                #                          nc w  nr h  ch
                coll = np.transpose(coll, (3, 1, 4, 0, 2))
            else:
                dim0 = h
                dim1 = w
                #                          nc h  nr w  ch
                coll = np.transpose(coll, (3, 0, 4, 1, 2))
        coll = np.reshape(
            coll, ((dim0 + self.collage_border_width) * nim0,
                   (dim1 + self.collage_border_width) * nim1, numChans))

        #self.ih.set_data(self.tonemap(coll))
        self.ax.clear()
        self.ih = self.ax.imshow(self.tonemap(coll), origin='upper')

        height, width = self.ih.get_size()
        lims = (-0.5, width - 0.5, -0.5, height - 0.5)
        self.ax.set(xlim=lims[0:2], ylim=lims[2:4])
        try:
            self.ax.get_yaxis().set_inverted(True)
        except Exception:
            self.ax.invert_yaxis()
        self.fig.canvas.draw()

    def switch_to_single_image(self):
        if self.collageActive:
            self.ax.clear()
            self.ih = self.ax.imshow(np.zeros(self.get_img().shape[:3]),
                                     origin='upper')
        self.collageActive = False

    def reset_zoom(self):
        height, width = self.ih.get_size()
        lims = (-0.5, width - 0.5, -0.5, height - 0.5)
        self.ih.axes.axis(lims)
        self.ax.set_position(Bbox([[0, 0], [1, 1]]))
        try:
            self.ax.get_yaxis().set_inverted(True)
        except Exception:
            self.ax.invert_yaxis()
        self.fig.canvas.draw()

    def zoom(self, pos, factor):
        lims = self.ih.axes.axis()
        xlim = lims[0:2]
        ylim = lims[2:]

        # compute interval lengths left, right, below and above cursor
        left = pos[0] - xlim[0]
        right = xlim[1] - pos[0]
        below = pos[1] - ylim[0]
        above = ylim[1] - pos[1]

        # zoom in or out
        if self.x_zoom:
            xlim = [pos[0] - factor * left, pos[0] + factor * right]
        if self.y_zoom:
            ylim = [pos[1] - factor * below, pos[1] + factor * above]

        # no zooming out beyond original zoom level
        height, width = self.ih.get_size()

        if self.x_stop_at_orig:
            xlim = [
                np.maximum(-0.5, xlim[0]),
                np.minimum(width - 0.5, xlim[1])
            ]

        if self.y_stop_at_orig:
            ylim = [
                np.maximum(-0.5, ylim[0]),
                np.minimum(height - 0.5, ylim[1])
            ]

        # update axes
        if xlim[0] != xlim[1] and ylim[0] != ylim[1]:
            lims = (xlim[0], xlim[1], ylim[0], ylim[1])
            self.ih.axes.axis(lims)
            try:
                self.ax.get_yaxis().set_inverted(True)
            except Exception:
                self.ax.invert_yaxis()
            self.ax.set_position(Bbox([[0, 0], [1, 1]]))
            self.fig.canvas.draw()
        return

    def tonemap(self, im):
        if isinstance(im, np.matrix):
            im = np.array(im)
        if im.shape[2] == 1:
            im = np.repeat(im, 3, axis=2)
        elif im.shape[2] == 2:
            im = np.concatenate(
                (im, np.zeros((im.shape[0], im.shape[1], 2), dtype=im.dtype)),
                axis=2)
        elif im.shape[2] != 3:
            # project to RGB
            raise Exception('spectral to RGB conversion not implemented')
        return np.power(
            np.maximum(0., np.minimum(1., (im - self.offset) * self.scale)),
            1. / self.gamma)

    def updateImage(self):
        if self.collageActive:
            self.collage()
        else:
            if self.nims > 1:
                self.uiCBCollageActive.blockSignals(True)
                self.uiCBCollageActive.setChecked(False)
                self.uiCBCollageActive.blockSignals(False)
            height, width = self.ih.get_size()
            im = self.get_img()
            if height != im.shape[0] or width != im.shape[1]:
                # image size changed, create new axes
                self.ax.clear()
                self.ih = self.ax.imshow(self.tonemap(im))
            else:
                self.ih.set_data(self.tonemap(im))
            height, width = self.ih.get_size()
            lims = (-0.5, width - 0.5, -0.5, height - 0.5)
            self.ax.set(xlim=lims[0:2], ylim=lims[2:4])
            try:
                self.ax.get_yaxis().set_inverted(True)
            except Exception:
                self.ax.invert_yaxis()
            self.fig.canvas.draw()

    def setScale(self, scale, update=True):
        self.scale = scale
        self.uiLEScale.setText(str(self.scale))
        if update:
            self.updateImage()

    def setGamma(self, gamma, update=True):
        self.gamma = gamma
        self.uiLEGamma.setText(str(self.gamma))
        if update:
            self.updateImage()

    def setOffset(self, offset, update=True):
        self.offset = offset
        self.uiLEOffset.setText(str(self.offset))
        if update:
            self.updateImage()

    def onclick(self, event):
        if event.dblclick:
            self.reset_zoom()
            self.mouse_down ^= event.button
        elif event.inaxes:
            self.x_start = event.xdata
            self.y_start = event.ydata
            self.prev_delta_x = 0
            self.prev_delta_y = 0
            self.cur_xlims = self.ih.axes.axis()[0:2]
            self.cur_ylims = self.ih.axes.axis()[2:]
            self.mouse_down |= event.button

    def onrelease(self, event):
        self.mouse_down ^= event.button

    def onmotion(self, event):
        if self.mouse_down == 1 and event.inaxes:
            delta_x = self.x_start - event.xdata
            delta_y = self.y_start - event.ydata
            self.ih.axes.axis(
                (self.cur_xlims[0] + delta_x, self.cur_xlims[1] + delta_x,
                 self.cur_ylims[0] + delta_y, self.cur_ylims[1] + delta_y))
            self.fig.canvas.draw()
            self.x_start += (delta_x - self.prev_delta_x)
            self.y_start += (delta_y - self.prev_delta_y)
            self.prev_delta_x = delta_x
            self.prev_delta_y = delta_y

    def keyPressEvent(self, event):
        #def onkeypress(self, event):
        key = event.key()
        mod = event.modifiers()
        if key == Qt.Key_Question:  # ?
            self.print_usage()
        elif key == Qt.Key_A:  # a
            # trigger autoscale
            self.autoscale()
            return
        elif key == Qt.Key_A and mod == Qt.Key_Shift:  # A
            # toggle autoscale between user-selected percentiles or min-max
            self.autoscaleUsePrctiles = not self.autoscaleUsePrctiles
            self.autoscale()
            return
        elif key == Qt.Key_C:
            # toggle on-change autoscale
            self.autoscaleOnChange = not self.autoscaleOnChange
            print('on-change autoscaling is %s' %
                  ('on' if self.autoscaleOnChange else 'off'))
        elif key == Qt.Key_G:
            self.gamma = 1.
        elif key == Qt.Key_L:
            # update axes for single image dimensions
            if self.collageActive:
                self.switch_to_single_image()
            else:
                # toggle showing collage
                self.collageActive = not self.collageActive
            # also disable per-image scaling limit computation
            self.autoscalePerImg = not self.autoscalePerImg
        elif key == Qt.Key_O:
            self.offset = 0.
        elif key == Qt.Key_P:
            self.autoscalePerImg = not self.autoscalePerImg
            print('per-image scaling is %s' %
                  ('on' if self.autoscalePerImg else 'off'))
            self.autoscale()
        elif key == Qt.Key_S:
            self.scale = 1.
        elif key == Qt.Key_Z:
            # reset zoom
            self.ih.axes.autoscale(True)
        elif key == Qt.Key_Alt:
            self.alt = True
            self.uiLabelModifiers.setText('alt: %d, ctrl: %d, shift: %d' %
                                          (self.alt, self.control, self.shift))
            return
        elif key == Qt.Key_Control:
            self.control = True
            self.uiLabelModifiers.setText('alt: %d, ctrl: %d, shift: %d' %
                                          (self.alt, self.control, self.shift))
            return
        elif key == Qt.Key_Shift:
            self.shift = True
            self.uiLabelModifiers.setText('alt: %d, ctrl: %d, shift: %d' %
                                          (self.alt, self.control, self.shift))
            return
        elif key == Qt.Key_Left:
            self.switch_to_single_image()
            self.imind = np.mod(self.imind - 1, self.nims)
            print('image %d / %d' % (self.imind + 1, self.nims))
            if self.autoscaleOnChange:
                self.autoscale()
                return
        elif key == Qt.Key_Right:
            self.switch_to_single_image()
            self.imind = np.mod(self.imind + 1, self.nims)
            print('image %d / %d' % (self.imind + 1, self.nims))
            if self.autoscaleOnChange:
                self.autoscale()
                return
        else:
            return
        self.updateImage()

    def keyReleaseEvent(self, event):
        #def onkeyrelease(self, event):
        key = event.key()
        if key == Qt.Key_Alt:
            self.alt = False
        elif key == Qt.Key_Control:
            self.control = False
        elif key == Qt.Key_Shift:
            self.shift = False
        self.uiLabelModifiers.setText('alt: %d, ctrl: %d, shift: %d' %
                                      (self.alt, self.control, self.shift))

    def onscroll(self, event):
        if self.control and self.shift:
            # autoscale percentiles
            self.autoscalePrctile *= np.power(1.1, event.step)
            self.autoscalePrctile = np.minimum(100, self.autoscalePrctile)
            print('auto percentiles: [%3.5f, %3.5f]' %
                  (self.autoscalePrctile, 100 - self.autoscalePrctile))
            self.autoscaleUsePrctiles = True
            self.autoscale()
        elif self.control:
            # scale
            #self.setScale(self.scale * np.power(1.1, event.step))
            self.setScale(self.scale * np.power(1.1, event.step))
        elif self.shift:
            # gamma
            self.setGamma(self.gamma * np.power(1.1, event.step))
        elif event.inaxes:
            # zoom when inside image axes
            factor = np.power(self.zoom_factor, -event.step)
            self.zoom([event.xdata, event.ydata], factor)
            return
        else:
            # scroll through images when outside of axes
            self.switch_to_single_image()
            self.imind = int(np.mod(self.imind - event.step, self.nims))
            print('image %d / %d' % (self.imind + 1, self.nims))
            if self.autoscaleOnChange:
                self.autoscale()
                return
        self.updateImage()

    def save(self, ofname):
        imageio.imwrite(ofname, np.array(self.ih.get_array()))
Esempio n. 14
0
class QWatson(QWidget, QWatsonImportMixin, QWatsonProjectMixin,
              QWatsonActivityMixin):
    def __init__(self, config_dir=None, parent=None):
        super(QWatson, self).__init__(parent)
        self.setWindowIcon(icons.get_icon('master'))
        self.setWindowTitle(__namever__)
        self.setMinimumWidth(300)
        self.setWindowFlags(Qt.Window | Qt.WindowMinimizeButtonHint
                            | Qt.WindowCloseButtonHint)

        if platform.system() == 'Windows':
            import ctypes
            myappid = __namever__
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                myappid)

        config_dir = (config_dir or os.environ.get('QWATSON_DIR')
                      or click.get_app_dir('QWatson'))

        self.client = Watson(config_dir=config_dir)
        self.model = WatsonTableModel(self.client)

        self.setup_activity_overview()
        self.setup()

        if self.client.is_started:
            self.add_new_project(self.client.current['project'])
            self.stop_watson(tags=['error'],
                             message="last session not closed correctly.")
        self.set_settings_from_index(-1)

    # ---- Setup layout

    def setup(self):
        """Setup the main widget."""

        # Setup the stack widget.

        self.stackwidget = QStackedWidget()

        self.setup_activity_tracker()
        self.setup_datetime_input_dialog()
        self.setup_close_dialog()
        self.setup_del_project_dialog()
        self.setup_merge_project_dialog()
        self.setup_import_dialog()

        # Setup the main layout of the widget

        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.stackwidget)

    def setup_close_dialog(self):
        """
        Setup a dialog that is shown when closing QWatson while and activity
        is being tracked.
        """
        self.close_dial = CloseDialog(parent=self)
        self.close_dial.register_dialog_to(self)

    def setup_datetime_input_dialog(self):
        """
        Setup the dialog to ask the user to enter a datetime value for
        the starting time of the activity.
        """
        self.datetime_input_dial = DateTimeInputDialog(parent=self)
        self.datetime_input_dial.register_dialog_to(self)

    # ---- Main interface

    def setup_activity_tracker(self):
        """Setup the widget used to start, track, and stop new activity."""
        stopwatch = self.setup_stopwatch()
        managers = self.setup_watson_managers()
        statusbar = self.setup_statusbar()

        tracker = QWidget()
        layout = QVBoxLayout(tracker)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(stopwatch)
        layout.addWidget(managers)
        layout.addWidget(statusbar)
        layout.setStretch(1, 100)

        self.stackwidget.addWidget(tracker)

    # ---- Project, Tags and Comment

    def setup_watson_managers(self):
        """
        Setup the embedded dialog to setup the current activity parameters.
        """
        project_manager = self.setup_project_manager()

        self.tag_manager = TagLineEdit()
        self.tag_manager.setPlaceholderText("Tags (comma separated)")

        self.comment_manager = QLineEdit()
        self.comment_manager.setPlaceholderText("Comment")

        # ---- Setup the layout

        managers = ColoredFrame('light')

        layout = QGridLayout(managers)
        layout.setContentsMargins(5, 5, 5, 5)

        layout.addWidget(project_manager, 0, 1)
        layout.addWidget(self.tag_manager, 1, 1)
        layout.addWidget(self.comment_manager, 2, 1)

        layout.addWidget(QLabel('project :'), 0, 0)
        layout.addWidget(QLabel('tags :'), 1, 0)
        layout.addWidget(QLabel('comment :'), 2, 0)

        return managers

    def set_settings_from_index(self, index):
        """
        Load the settings in the manager from the data of the frame saved
        at index.
        """
        if index is not None:
            try:
                frame = self.client.frames[index]
                self.project_manager.blockSignals(True)
                self.project_manager.setCurrentProject(frame.project)
                self.project_manager.blockSignals(False)

                self.tag_manager.blockSignals(True)
                self.tag_manager.set_tags(frame.tags)
                self.tag_manager.blockSignals(False)

                self.comment_manager.blockSignals(True)
                self.comment_manager.setText(frame.message)
                self.comment_manager.blockSignals(False)
            except IndexError:
                print("IndexError: list index out of range")

    # ---- Bottom Toolbar

    def setup_statusbar(self):
        """Setup the toolbar located at the bottom of the main widget."""
        self.btn_report = QToolButtonSmall('note')
        self.btn_report.clicked.connect(self.overview_widg.show)
        self.btn_report.setToolTip("<b>Activity Overview</b><br><br>"
                                   "Open the activity overview window.")

        self.round_time_btn = DropDownToolButton(style='text_only')
        self.round_time_btn.addItems(list(ROUNDMIN.keys()))
        self.round_time_btn.setCurrentIndex(1)
        self.round_time_btn.setToolTip(
            "<b>Round Start and Stop</b><br><br>"
            "Round start and stop times to the nearest"
            " multiple of the selected factor.")

        self.btn_startfrom = DropDownToolButton(style='text_only')
        self.btn_startfrom.addItems(
            ['start from now', 'start from last', 'start from other'])
        self.btn_startfrom.setCurrentIndex(0)
        self.btn_startfrom.setToolTip(
            "<b>Start From</b><br><br>"
            "Set whether the current activity starts"
            " from the current time (now),"
            " from the stop time of the last logged activity (last),"
            " or from a user defined time (other).")

        # Setup the layout of the statusbar

        statusbar = ToolBarWidget('window')
        statusbar.setSpacing(0)
        statusbar.addWidget(self.round_time_btn)
        statusbar.addWidget(self.btn_startfrom)
        statusbar.addStretch(100)
        statusbar.addWidget(self.btn_report)
        statusbar.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred))

        return statusbar

    def roundTo(self):
        """
        Return the start and stop rounding time factor, in minutes, that
        corresponds to the option selected in the round_time_btn.
        """
        return ROUNDMIN[self.round_time_btn.text()]

    def startFrom(self):
        """
        Return the mode to use to determine at what reference time the activity
        must refer to calculate its elapsed time.
        """
        return STARTFROM[self.btn_startfrom.text()]

    # ---- Stackwidget handlers

    def addWidget(self, widget):
        """
        Add a widget to the stackwidget and return the index where the
        widget was added.
        """
        self.stackwidget.addWidget(widget)
        return self.stackwidget.count() - 1

    def removeWidget(self, widget):
        """Remove a widget from the stackwidget."""
        self.stackwidget.removeWidget(widget)

    def currentIndex(self):
        """Return the current index of the stackwidget."""
        return self.stackwidget.currentIndex()

    def setCurrentIndex(self, index):
        """Set the current index of the stackwidget."""
        self.stackwidget.setCurrentIndex(index)

    # ---- Stop, Start, and Cancel

    def setup_stopwatch(self):
        """
        Setup the widget that contains a button to start/stop Watson and a
        digital clock that shows the elapsed amount of time since Watson
        was started.
        """
        self.stopwatch = StopWatchWidget()
        self.stopwatch.sig_btn_start_clicked.connect(self.start_watson)
        self.stopwatch.sig_btn_stop_clicked.connect(self.stop_watson)
        self.stopwatch.sig_btn_cancel_clicked.connect(self.cancel_watson)

        return self.stopwatch

    def start_watson(self, start_time=None):
        """Start monitoring a new activity with the Watson client."""
        if isinstance(start_time, arrow.Arrow):
            self.btn_startfrom.setEnabled(False)
            self.stopwatch.start(start_time)
            self.client.start(self.currentProject())
            self.client._current['start'] = start_time
        else:
            frames = self.client.frames
            if self.startFrom() == 'now':
                self.start_watson(arrow.now())
            elif self.startFrom() == 'last' and len(frames) > 0:
                self.start_watson(min(frames[-1].stop, arrow.now()))
            else:
                self.datetime_input_dial.show()

    def cancel_watson(self):
        """Cancel the Watson client if it is running and reset the UI."""
        self.btn_startfrom.setEnabled(True)
        self.stopwatch.cancel()
        if self.client.is_started:
            self.client.cancel()

    def stop_watson(self,
                    message=None,
                    project=None,
                    tags=None,
                    round_to=None):
        """Stop Watson and update the table model."""
        self.btn_startfrom.setEnabled(True)
        self.stopwatch.stop()

        self.client._current['message'] = \
            self.comment_manager.text() if message is None else message
        self.client._current['project'] = \
            self.currentProject() if project is None else project
        self.client._current['tags'] = \
            self.tag_manager.tags if tags is None else tags

        self.model.beginInsertRows(QModelIndex(), len(self.client.frames),
                                   len(self.client.frames))
        self.client.stop()

        # Round the start and stop times of the last added frame.
        round_frame_at(self.client, -1,
                       self.roundTo() if round_to is None else round_to)

        self.client.save()
        self.model.endInsertRows()

    def closeEvent(self, event):
        """Qt method override."""
        if self.client.is_started:
            self.close_dial.show()
            event.ignore()
        else:
            self.overview_widg.close()
            self.client.save()
            event.accept()
            print("QWatson is closed.\n")
Esempio n. 15
0
class tabdemo(QTabWidget):
    def __init__(self, parent = None):
        super(tabdemo, self).__init__(parent)

        CurrentSoftwareVersion = '1.0.0'                        #Update as needed. Don't forget to update the Revision History as well
        
        self.AppName = "DF-DAQ - " + CurrentSoftwareVersion    #Sets the name in the upper left hand corner of the GUI

        self.setWindowIcon(QIcon('DFIcon-01.ico'))   #Sets the GUI Icon

        self.pressureOptions = ['PSI','HPA','KPA','MBAR','BAR','CMH2O','INH2O','MMHG']
        self.oldRate = 0
        self.dataOutputMultiplier = 1
        self.outputType = ''

        self.tab1 = QWidget()                       #Define Tab1
        self.tab2 = QWidget()                       #Define Tab2
        self.tab3 = QWidget()                       #Define Tab3
        self.tab4 = QWidget()                       #Define Tab4
	
        self.addTab(self.tab1,"Acquisition")       #Add Tab 1 to the QTabWidget
        #self.addTab(self.tab2,"Plot")              #Add Tab 2 to the QTabWidget
        self.addTab(self.tab3,"Live Data")         #Add Live Data Tab to QTabWidget
        #self.addTab(self.tab4,"Settings")          #Add Settings Tab to QTabWidget

        self.tab1UI()                               #Run the Tab 1 setup
        self.tab2UI()
        self.tab3UI()
        self.tab4UI()

        self.DAQ = DF_DAQ()                      #Create a DF_DAQ object
 
        self.logMsg('Date: ' + datetime.datetime.now().strftime('%m-%d-%y'), False, 'black')    #Write the Start Date to the Log
        self.logMsg('Software: ' + self.AppName, False, 'black')                                #Write the Software Version to the Log
 
        self.SearchCOMs()                       #Search the COM Ports for a Teensy
      
        self.setWindowTitle(self.AppName)
        self.setGeometry(650, 400, 610, 300)    #Sets the X and Y location to start up in and the Width and Height of the GUI (px)
        self.setMinimumHeight(350)                #Locks the height to 300 (px)
        self.setMinimumWidth(610)               #Locks the minimum width to 600 (px)
        
#==============================================================================
# Input Parameters: none
# Output Returns: none 
#
# Description: This function searches the COM Ports and populates the COM Port
# ComboBox with all avaliable Teensy Ports. 
#==============================================================================
    def SearchCOMs(self):
        COM = self.DAQ.findPort()   #Use the DF_DAQ 'findPort' method to autodetect the Teensy
        self.COMDis.clear()     #Reset the COM Port ComboBox

        if(len(COM) < 1):           #Teensy not found, list of COM Ports returned
            print('No DF Hardware Connected')
            self.COMDis.addItem('NA')           #Set the COM Port ComboBox to NA
            self.COMDis.setDisabled(True)       #Disable the COM Port ComboBox
            self.FirmDis.setText('NA')          #Set the Firmware LineEdit to NA
            self.DataOutput.setCurrentIndex(0)  #Set the Output ComboBox to the first index
            self.HardChannels.setText('NA')     #Set the Channels LineEdit to NA
            self.DataRate.setText('10')          #Set the Rate LineEdit to 0
            self.rateMax = 10    
            self.disableGUI(True)
            self.updateMult()                   #Update the Multiplier LineEdit
            self.logMsg('Please plug in the DroidForge hardware and refresh the COM', True, 'blue')
        else:                       #Teensy Found
            print('DF Hardware Connected')
            self.disableGUI(False)
            if (len(COM) == 1):     #Only ONE DF Hardware Connected
                
                self.COMDis.addItem(str(COM[0]))                        #Add the prot to the COM Port ComboBox
                self.COMDis.setDisabled(True)                           #Disable the COM Port ComboBOx
            else:                   #Multiple DF Hardware Connected
                for i in range (0, len(COM)):                           #Loop Through all found COM Ports
                    self.COMDis.addItem(str(COM[i]))                    #Add all items to the COM Port ComboBox
                self.COMDis.setDisabled(False)                          #Enable the COM Port ComboBox

            if (self.FirmDis.text() == 'NA'):
                print('No Firmware Detected')
                self.disableGUI(True)
                self.DataRate.setText('10')
                self.DataPrefix.setText("")
                self.removeTab(1)
                self.rateMax = 10
                self.DataMultiplier.setText('NA')
                self.DataTime.setText('-')
                
                
                self.FileOutput.setText(str(os.getcwd()) + '\Temp.txt')
            
#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: This function sets all the Hardware information with for the 
# selected COM Port.
#==============================================================================                
               
    def disableGUI(self, disabled):
        self.DataPrefix.setDisabled(disabled)
        self.DataRate.setDisabled(disabled)
        self.DataTime.setDisabled(disabled)
        self.DataOutput.setDisabled(disabled)
        self.plotStop.setDisabled(disabled)
        self.ButtonStart.setDisabled(disabled)
        self.plotWidth.setDisabled(disabled)
    
    def updateHardware(self):
        print('Updating Hardware')
        if(self.COMDis.currentText() != 'NA'): #Do nothing if there is no DF Hardware Detected
            self.logMsg('Searching for Attached Hardware...<br>', False, 'black')
            self.FirmDis.setText(self.DAQ.getFirmVer(str(self.COMDis.currentText())))      #Use the DF_DAQ 'getFirmVer' method to get the current firmware form the selected Teensy
            FirmStartup = self.DAQ.getSetup(str(self.COMDis.currentText())).split('-')
            
            self.logMsg('DF Board: ' + str(self.COMDis.currentText()), False, 'black')   #Write the Teensy COM Port to the Log
            self.logMsg('Firmware: ' + self.FirmDis.text(), False, 'black') #Write the Firmware Version to the Log
            
            if(FirmStartup[0] == 'P'):
                self.DataOutput.clear()
                self.outputType = 'Pressure'
                for key in self.pressureOptions:
                    self.DataOutput.addItem(str(key))
                index = self.DataOutput.findText(FirmStartup[1], QtCore.Qt.MatchFixedString)
                if index >= 0:
                    self.DataOutput.setCurrentIndex(index)
                self.logMsg('Hardware Type: Pressure', False, 'black')
                self.DataTime.setText('-') #Disable rate
                
            else: #Other Firmware Detected
                self.HardChannels.setText('NA') #Set the Channesl LineEdit to NA
                self.DataRate.setText('10')      #Set the Rate LineEdit to 0
                self.rateMax = 10
                
            self.HardChannels.setText(FirmStartup[2])
            self.rateMax = float(FirmStartup[3])
            self.logMsg('Max Sample Rate: ' + str(self.rateMax), False, 'black')
            self.DataRate.setText(str(int(FirmStartup[4])))
            
#==============================================================================
# Input Parameters: msg (Str), bold (Bool), color (Str)
# Output Returns: none
#
# Description: This function takes in a mesage and writes it to the Log TextEdit.
# If bold is True, the line is bolded. Color sets the color of the line, set to
# 'black' for default. 
#==============================================================================
    def logMsg(self, msg, bold, color):             
        boldStart = '<b>'                           #Bold Start Character
        boldEnd = '</b>'                            #Bold End Character
        colorStart = '<font color = ' + color + '>' #Color Start Character + color 
        colorEnd = '</font>'                        #Color End Character
        
        if bold:
            msg = boldStart + str(msg) + boldEnd    #Add Bold HTML Characters
        msg = colorStart + str(msg) + colorEnd      #Add Color HTML Characters
        self.Log.moveCursor(QTextCursor.Start)      #Move the cursor to the beginning of the TextEdit
        self.Log.insertHtml(str(datetime.datetime.now().strftime('%H:%M:%S  -  ')) + msg + '<br>') #Add the TimeStamp, Message Text and finally a return
        QApplication.processEvents()    #Update the Log in the GUI

#==============================================================================
# Input Parameters: none
# Output Returns: none (Tab 1 Layout)
#
# Description: This funcition defines the layout for the first Tab and associated
# widgets. 
#==============================================================================
    def tab1UI(self):
        #Define all the different Layouts
        hlayout = QHBoxLayout()     #Main Layer
        h2layout = QHBoxLayout()    
        h4layout = QHBoxLayout()
        h6layout = QHBoxLayout()
        h7layout = QHBoxLayout()
        h8layout = QHBoxLayout()
        h9layout = QHBoxLayout()
        h10layout = QHBoxLayout()
        glayout = QGridLayout()
        glayout2 = QGridLayout()
        vlayout = QVBoxLayout()
        v2layout = QVBoxLayout()
        v3layout = QVBoxLayout()
        
        #Creat a font style to bold the headers
        headerFont = QFont()
        headerFont.setBold(True)
        
        #Run Time Log
        self.Log = QTextEdit()
        self.Log.setReadOnly(True)
        self.Log.setToolTip('Run Time Log')
        self.Log.setMinimumWidth(270)

        #COM Port
        self.COMDis = QComboBox()
        self.COMDis.setMinimumWidth(58)
        self.COMDis.setToolTip('COM Port')
        self.COMDis.currentIndexChanged.connect(self.updateHardware)
        
        #COM Refresh Button
        self.RefreshCOM = QPushButton()
        self.RefreshCOM.setMaximumWidth(22)
        self.RefreshCOM.setMaximumHeight(22)
        self.RefreshCOM.setToolTip('Refresh COM Port List')
        self.RefreshCOM.setIcon(QIcon('refresh.png'))
        self.RefreshCOM.clicked.connect(self.RefreshCOMs)
        
        #Firmware
        self.FirmDis = QLineEdit()
        self.FirmDis.setReadOnly(True)
        self.FirmDis.setDisabled(True)
        self.FirmDis.setMaximumWidth(80)
        self.FirmDis.setToolTip('Firmware on Device')
        
        #Channels
        self.HardChannels = QLineEdit()
        self.HardChannels.setMaximumWidth(80)
        self.HardChannels.setReadOnly(True)
        self.HardChannels.setDisabled(True)
        self.HardChannels.setToolTip('Number of channels on the hardware')        
        
        #Start Button
        self.ButtonStart = QPushButton()
        self.ButtonStart.setText('Start')
        self.ButtonStart.setMaximumWidth(315)
        self.ButtonStart.clicked.connect(self.ToggleStartStop)
        self.ButtonStart.setToolTip('Start Recording Data')
        self.Start = False  #Start boolean. (toggles when the Start Button is pressed)
        
        #Add a refresh button to the COM Port List
        h9layout.addWidget(self.COMDis)
        h9layout.addWidget(self.RefreshCOM)
        
        #Hardware Column - Grid Layout
        space = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Expanding)        
        
        glayout.addWidget(QLabel('COM Port'), 1, 3)
        glayout.addLayout(h9layout, 1, 5)
        glayout.addWidget(QLabel('Firmware'), 3, 3)
        glayout.addWidget(self.FirmDis, 3, 5)
        glayout.addWidget(QLabel('Channels'), 5, 3)
        glayout.addWidget(self.HardChannels, 5, 5)
        glayout.addItem(space, 7, 5)
        
        #Prefix
        self.DataPrefix = QLineEdit()
        self.DataPrefix.setText('POS')
        self.DataPrefix.setToolTip('Sets the prefix for the recorded data')
        self.DataPrefix.setMaximumWidth(80)
        
        self.OutputOptions = {'N/A':'1.0'}        
        
        #Output
        self.DataOutput = QComboBox()
        self.DataOutput.setMaximumWidth(80)
        for key in self.OutputOptions:
            self.DataOutput.addItem(str(key))
        self.DataOutput.model().sort(0)
        self.DataOutput.setToolTip('Sets the Multiplier for the incomming data')
        self.DataOutput.currentIndexChanged.connect(self.convertOutput)        
        
        #Multiplier
        self.DataMultiplier = QLineEdit()
        self.DataMultiplier.setMaximumWidth(80)
        self.DataMultiplier.setToolTip('Sets a multiplier to conver the AD reading into Volts or Ohms')
        self.DataMultiplier.textChanged.connect(self.OnlyAllowInt)
        self.DataMultiplier.setDisabled(True)
        
        #Test Time
        self.DataTime = QLineEdit()
        self.DataTime.setMaximumWidth(55)
        self.DataTime.setText('-')
        self.DataTime.setToolTip('Length of recording (seconds). Set to 0 to disable')
        self.DataTime.textChanged.connect(self.OnlyAllowInt2)
        
        #Rate
        self.DataRate = QLineEdit()
        self.DataRate.setMaximumWidth(55)
        self.DataRate.setToolTip('Sets the rate of the firmware in Samples/Second')
        self.DataRate.setText('10')
        self.DataRate.textChanged.connect(self.SetRate)
        
        #Add a label to the Test Time Grid Point
        h7layout.addWidget(self.DataTime)
        h7layout.addWidget(QLabel('Sec'))    
        
        #Add a label to the Rate Grid Point
        h8layout.addWidget(self.DataRate)
        h8layout.addWidget(QLabel('Hz'))
        
        h10layout.addStretch(1)
#        h10layout.addWidget(self.SetRateButton)
        
        #Customize Data - Grid Layout
        # glayout2.addWidget(QLabel('Prefix'), 1, 3)
        # glayout2.addWidget(self.DataPrefix , 1, 5)
        glayout2.addWidget(QLabel('Output'), 3, 3)
        glayout2.addWidget(self.DataOutput, 3, 5)
        glayout2.addWidget(QLabel('Multiplier'), 5, 3)
        glayout2.addWidget(self.DataMultiplier , 5, 5)
        glayout2.addWidget(QLabel('Test Time'), 7, 3)
        glayout2.addLayout(h7layout, 7, 5)
        glayout2.addWidget(QLabel('Rate'), 9, 3)
        glayout2.addLayout(h8layout, 9, 5)
        glayout2.addLayout(h10layout, 11, 3, 1, 3)        
        
        CustomizeDataFrame = QGroupBox()
        CustomizeDataFrame.setTitle('Customize Data')
        CustomizeDataFrame.setLayout(glayout2)
        
        #Left Column
        v2layout.addLayout(h4layout)
        v2layout.addWidget(CustomizeDataFrame)

        HardwareFrame = QGroupBox()
        HardwareFrame.setTitle('Hardware')
        HardwareFrame.setLayout(glayout)

        #Right Column
        v3layout.addWidget(HardwareFrame)
        
        maxFileOptionsWidth = 600
        self.FileOutput = QLineEdit()
        self.FileOutput.setMaximumWidth(maxFileOptionsWidth)
        self.FileOutput.setMaximumHeight(22)
        self.FileOutput.setToolTip('File path to output file')
        self.FileOutput.setReadOnly(True)
        fileUnique = 1
        while(os.path.exists("Test-" + str(fileUnique) + '.xlsx')):
            fileUnique += 1;
        self.fileUniqueStr = str(os.getcwd()) + '\Test-' + str(fileUnique) + '.xlsx'
        self.FileOutput.setText(self.fileUniqueStr)
        
        self.SaveAs = QPushButton()
        self.SaveAs.setText('Set Save Filename')
        self.SaveAs.setMaximumWidth(100)
        self.SaveAs.setToolTip('Chose where to automatically save recorded data')
        self.SaveAs.clicked.connect(partial(self.SaveExcelAs, self.fileUniqueStr))
        
        #Left Side of the screen
        h2layout.addLayout(v2layout)
        h2layout.addLayout(v3layout)
        h2layout.addStretch(1)        
        
        #File Options Widgets
        h6layout.addWidget(QLabel('Output:'))
        h6layout.addWidget(self.FileOutput)    
        h6layout.addWidget(self.SaveAs)
        
        FileOptionsFrame = QGroupBox()
        FileOptionsFrame.setTitle('File Options')
        FileOptionsFrame.setLayout(h6layout)
        FileOptionsFrame.setMaximumWidth(maxFileOptionsWidth)
        #Left Size Vertical Layout
        vlayout.addWidget(QHLine())
        vlayout.addLayout(h2layout)
        vlayout.addWidget(FileOptionsFrame)
        vlayout.addStretch(1)
        vlayout.addWidget(self.ButtonStart)        
        
        #Main Layout
        hlayout.addLayout(vlayout)
        hlayout.addWidget(self.Log)     
        
        #Set the Tab Layout
        self.tab1.setLayout(hlayout)

    def update_plot_data(self):
        if(len(self.x) == 0):
            self.x = [0]
            self.xAll = []
            self.y = []
            self.yAll = []
        elif(len(self.x) < self.plotFixedWidth.value()):
            self.x.append(self.x[-1] + 1)
        else:
            self.x = self.x[1:]
            self.x.append(self.x[-1] + 1)
            self.y = self.y[1:]
        
        self.xAll.append(self.x[-1])
        
        self.y.append(self.DAQ.Read(str(self.COMDis.currentText())) * self.dataOutputMultiplier)
        self.yAll.append(self.y[-1])
    
        if(self.plotWidth.currentIndex() != 0):
            self.xLive = self.xAll[-(self.plotFixedWidth.value()):]
            self.yLive = self.yAll[-(self.plotFixedWidth.value()):]
        else:
            self.xLive, self.yLive = self.downsample(0, len(self.xAll))
    
        self.data_line.setData(self.xLive, self.yLive)

    def tab3UI(self): #Live Data Plot
        vlayout = QVBoxLayout()
        hlayout = QHBoxLayout()
        
        self.plot = pg.PlotWidget()
        self.plot.setToolTip('Right click plot for options')
        self.plot.setBackground('w')
        self.plot.setTitle("Live Data Plot")
        self.plot.showGrid(x=True, y=True)
        self.plot.setLabel('bottom', 'Sample (N)', color = 'gray', size = 40)
        
        self.pen = pg.mkPen(color=(59,187,228), width=2)
        self.penGray = pg.mkPen(color=(180,180,180), width=2)
        
        self.x = []#list(range(100))
        self.y = []#[randint(0,100) for _ in range(100)]
        self.xAll = self.x
        self.yAll = self.y
        
        self.testTimer = QtCore.QTimer()
        self.testTimer.timeout.connect(self.ToggleStartStop)
        
        self.timer = QtCore.QTimer()
        self.timer.setInterval(50)
        self.timer.timeout.connect(self.update_plot_data)
        #self.timer.start()
        
        self.plotWidth = QComboBox()
        self.plotWidth.setMaximumWidth(80)
        self.plotWidth.addItem('All')
        self.plotWidth.addItem('Fixed Width')
        self.plotWidth.setToolTip('Sets the x window for streaming data')
        self.plotWidth.currentIndexChanged.connect(self.plotUpdateData)   
        self.plotWidth.setDisabled(True)
        
        self.plotFixedWidth = QSpinBox()
        self.plotFixedWidth.hide()
        self.plotFixedWidth.setMinimum(10)
        self.plotFixedWidth.setMaximum(100000)
        self.plotFixedWidth.setValue(100)
        self.plotFixedWidth.valueChanged.connect(self.plotUpdateData)
        self.plotFixedWidth.setDisabled(True)
        
        self.plotZero = QPushButton()
        self.plotZero.setText('Zero')
        self.plotZero.setToolTip('Zeroes the sensor data')
        self.plotZero.clicked.connect(self.zeroSensor)
        self.plotZero.setDisabled(True)
        
        self.plotStop = QPushButton()
        self.plotStop.setText('Start')
        self.plotStop.setToolTip('Stops the data Stream')
        self.plotStop.clicked.connect(self.ToggleStartStop)
        
        vlayout.addWidget(self.plotWidth)
        vlayout.addWidget(self.plotFixedWidth)
        vlayout.addWidget(self.plotZero)
        vlayout.addWidget(QLabel('(Right click plot for\nadditional options)'))
        vlayout.addStretch(1)
        vlayout.addWidget(self.plotStop)
        
        hlayout.addLayout(vlayout)
        hlayout.addWidget(self.plot)
        
        self.tab3.setLayout(hlayout)

    def tab4UI(self):
        hlayout = QHBoxLayout()
        self.tab4.setLayout(hlayout)

#==============================================================================
# Input Parameters: none
# Output Returns: none (Tab 2 Layout)
#
# Description: This funcition defines the layout for the second Tab and associated
# widgets. 
#==============================================================================
    def tab2UI(self):
        #Define all the different Layouts
        vlayout = QVBoxLayout() #Main Layout
        v2layout = QVBoxLayout()
        hlayout = QHBoxLayout()
        h2layout = QHBoxLayout()
        glayout = QGridLayout()
        h3layout = QHBoxLayout()
        
        #Creat a font style to bold the headers
        headerFont = QFont()
        headerFont.setBold(True)        
        
        #Loaded File Location
        self.pFileName = QLineEdit()
        self.pFileName.setMaximumHeight(22)
        self.pFileName.setReadOnly(True)
        self.pFileName.setToolTip('Location of data to plot')
        
        #Import Button
        self.pImport = QPushButton()
        self.pImport.setMaximumWidth(80)
        self.pImport.setText('Import')
        self.pImport.setToolTip('Import Data to plot')
        self.pImport.clicked.connect(self.OpenExcel)
        
        #Plot Button
        self.pPlot = QPushButton()
        self.pPlot.setMaximumWidth(80)
        self.pPlot.setText('Plot')
        self.pPlot.setToolTip('Plot the data')
        self.pPlot.setDisabled(True)
        self.pPlot.clicked.connect(self.PlotData)
        
        #Individual Radio Button
        self.pAllLines = QRadioButton('Individual')
        self.pAllLines.setChecked(True)
        self.pAllLines.setToolTip('Gives each line a unique color')
        
        #By Cup Radio Button
        self.pCupLines = QRadioButton('By Cup')
        self.pCupLines.setToolTip('Highlights the data by cup')
        
        #Hightlighting Header
        highlight = QLabel('Highlighting')
        highlight.setFont(headerFont)        
        
        #Highlighting Data
        glayout.addWidget(self.pAllLines, 1, 3)
        glayout.addWidget(self.pCupLines, 1, 5)        
        
        HighlightingFrame = QGroupBox()
        HighlightingFrame.setTitle('Highlighting')
        HighlightingFrame.setLayout(glayout)        
        
        #Left Column
        v2layout.addWidget(HighlightingFrame)
        v2layout.addStretch(1)        
        
        #Main Horizontal Layout for Columns
        h3layout.addLayout(v2layout)
        h3layout.addStretch(1)
        
        #Import Layout
        hlayout.addWidget(QLabel('Import:'))
        hlayout.addWidget(self.pFileName)
        hlayout.addWidget(self.pImport)        
        
        #Plot button
        h2layout.addStretch(1)
        h2layout.addWidget(self.pPlot)        
        
        #Main Layout
        vlayout.addWidget(QHLine())
        vlayout.addLayout(hlayout)
        vlayout.addLayout(h3layout)
        vlayout.addStretch(1)
        vlayout.addLayout(h2layout)
        
        #Set the Layout for Tab2
        self.tab2.setLayout(vlayout)

#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: This function updates the DataMultiplier LineEdit based on the 
# selected DataOutput ComboBox
#==============================================================================
    def updateMult(self):
        self.DataMultiplier.setText(self.OutputOptions[str(self.DataOutput.currentText())])
        key = self.DataOutput.currentText()
        self.DataMultiplier.setDisabled(True)
        if(key == 'Temp 700'):
            self.DataMultiplier.setToolTip('A custom equation is required to convert to temperature<br><b>700 Ohm Thermistors</b>')
        elif(key == 'Temp 800'):
            self.DataMultiplier.setToolTip('A custom equation is required to convert to temperature<br><b>800 Ohm Thermistors</b>')            
        elif(key == 'Custom'):
            self.DataMultiplier.setToolTip('Enter in a Custom Multiplier to convert the A to D signal')
            self.DataMultiplier.setDisabled(False)
        else:
            self.DataMultiplier.setToolTip('Multiplier to convert the A to D signal')

#==============================================================================
# Input Parameters: none
# Output Returns: none (HTML Plot of Selected Data)
#
# Description: This function imports XLSX information, takes a couple user inputs
# and then plots the file to an HTML file using Bokeh.
#==============================================================================
    def PlotData(self):
        print ('Plotting Data')
        
        numlines = len(self.dfData.columns) #Number of Columns
            
        if(self.pAllLines.isChecked()):     #Individual Radio Button Selected
            #Palet containing 64 unique colors
            mypalette = ['black','dimgray','lightgrey','rosybrown','brown','maroon','red','salmon','sienna','chocolate','saddlebrown','sandybrown','orange','darkgoldenrod','gold','olivedrab','yellowgreen','darkolivegreen','chartreuse','darkseagreen','limegreen','darkgreen','green','lime','springgreen','mediumspringgreen','mediumaquamarine','aquamarine','turquoise','mediumturquoise','lightseagreen','darkcyan','aqua','cadetblue','deepskyblue','skyblue','lightskyblue','steelblue','royalblue','midnightblue','blue','navy','slateblue','mediumslateblue','darkorchid','mediumpurple','darkviolet','indigo','darkviolet','darkorchid','purple','darkmagenta','orchid','magenta','deeppink','crimson','pink','mediumvioletred','palevioletred','darkorange','peru','tan','coral','dodgerblue']        
            
            #Legend separating the data by the 'Line' on the MUX
            #Note: Using a unique legend for each column makes the legend too big 
            legends = ['Line 1','Line 2','Line 3','Line 4','Line 5','Line 6','Line 7','Line 8'] * 8
            
        elif(self.pCupLines.isChecked()):   #By Cup Radio Button Selected
            Palette1 = ['firebrick'] * 16   #Set Cup 1 to a single color
            Palette2 = ['seagreen'] * 16    #Set Cup 2 to a single color
            Palette3 = ['navy'] * 16        #Set Cup 3 to a single color
            Palette4 = ['darkmagenta'] * 16 #Set Cup 4 to a single color
            
            mypalette = Palette1 + Palette2 + Palette3 + Palette4 #Creat a Master List of Colors 
            
            #Give each Cup a legend
            legends = ['Cup1'] * 16 + ['Cup2'] * 16 + ['Cup3'] * 16 + ['Cup4'] * 16
            
        #Bokeh Plot Object
        p = figure(toolbar_location = 'above', plot_width = 1650, plot_height = 800, tools = ['box_zoom', 'pan', 'wheel_zoom', 'reset', 'save'])
        
        xs=[self.dfData.index.values]*numlines                  #List of a List of X Index values
        ys = [self.dfData[name].values for name in self.dfData] #List of a List of Y Values
        
        #Loop through all the data and add a line plot for each Channel
        for (colr, leg, x, y) in zip(mypalette, legends, xs, ys):
            p.line(x, y, color = colr, legend = leg)    #Bokeh Line with given color and legend
        
        p.legend.click_policy = 'hide'  #Allows the legend to be clicked to hide/show data
        
        show(p)#Show the resulting Plot

        print ('Data Plotted!')

#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: This function is connected to a 'textChanged' event from the rate
# and will auto update the firmware rate
#==============================================================================
    def SetRate(self):
        rate = self.DataRate.text()        
        
        if float(rate) > self.rateMax: 
            rate = str(self.rateMax)
            self.DataRate.setText(rate)
            self.logMsg('Warning! - Maximum rate is ' + rate + 'Hz', True, 'orange')

        newRate = int(1000 / float(self.DataRate.text()))

        if(newRate != self.oldRate):
            self.timer.setInterval(newRate)
            self.logMsg('<b>Sample Time: ' + str(newRate) + 'ms</b>', False, 'blue')
            self.oldRate = newRate

#==============================================================================
# Input Parameters: Argument (string) *Automatically passed in I guess
# Output Returns: self.DataMultiplier.setText (string)
#
# Description: This function is connected to a 'textChanged' event from either
# a QTextEdit or QLineEdit object. Whenever a text value is inputed in the text
# field by the user, this function will look at the typed character and remove
# it from the resulting string if that character is not an integer, (0-9) a 
# period (.) or a minus (-) symbol.
#==============================================================================
    def OnlyAllowInt(self, arg):
        #Setup a list of valid characters
        valid = ['-','.','0','1','2','3','4','5','6','7','8','9']

        #Loop through the inputed argument
        for c in range(0, len(arg)):
            #Look at the specific character of the string and check to see if it is valid
            if arg[c] not in valid:
                #If there is an invalid character, replace the character with an empty string ('')
                if len(arg)>0: arg=arg.replace(arg[c],'')
        
        #Set the text in DataMultiplier to the 'cleaned' argument
        self.DataMultiplier.blockSignals(True)
        self.DataMultiplier.setText(arg)
        self.DataMultiplier.blockSignals(False)

#==============================================================================
# Input Parameters: Argument (string) *Automatically passed in I guess
# Output Returns: self.DataTime.setText (string)
#
# Description: This function is connected to a 'textChanged' event from either
# a QTextEdit or QLineEdit object. Whenever a text value is inputed in the text
# field by the user, this function will look at the typed character and remove
# it from the resulting string if that character is not an integer, (0-9).
#==============================================================================
    def OnlyAllowInt2(self, arg):
        #Setup a list of valid characters
        valid = ['0','1','2','3','4','5','6','7','8','9']

        #Loop through the inputed argument
        for c in range(0, len(arg)):
            #Look at the specific character of the string and check to see if it is valid
            if arg[c] not in valid:
                #If there is an invalid character, replace the character with an empty string ('')
                if len(arg)>0: arg=arg.replace(arg[c],'')
        
        if(arg == '0'):
            arg = '-'
        
        #Set the text in DataTime to the 'cleaned' argument
        self.DataTime.blockSignals(True)
        self.DataTime.setText(arg)
        self.DataTime.blockSignals(False)

#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: This function opens the window file dialog to save a file
#==============================================================================
    def SaveExcelAs(self, fnameIn):
        if (os.path.exists(fnameIn)): #file already exists
            x = fnameIn.split('-')
            y = len(x)
            m = 1
            
            fnameIn = x[0]
            
            if (y > 2):
                i = 2
                while (y > i):
                    fnameIn = fnameIn + '-' + x[i-1]
                    i += 1
                    
            if (len(x[y-1]) > 0):
                z = x[y-1].split('.xlsx')
                if (len(z[0]) > 0):
                    m = int(z[0]) + 1
                          
            fnameIn = fnameIn + '-' + str(m) + '.xlsx'
            
        fname = QFileDialog.getSaveFileName(self, 'Save file', fnameIn, "Excel files (*.xlsx)")
        if fname != '':
            #print('Save File As: ' + str(fname))
            self.FileOutput.setText(fname[0])
            self.fileUniqueStr = fname[0]


#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: This function opens the window file dialog to open a file and then
# Reads the Sheet1 of the file into a pandas Data Frame
#==============================================================================
    def OpenExcel(self):
        fname = QFileDialog.getOpenFileName(self, 'Save file', 'c:\\',"Excel files (*.xlsx)")
        if fname != '':
            self.pFileName.setText(fname)  
        print ('Opening File')
        try:
            self.dfData = pd.read_excel(open(fname, 'rb'), sheet_name = 'Sheet1')
            print ('Data Extracted from File!')
            numlines = len(self.dfData.columns) #Number of Columns
            if(numlines != 64):
                self.pPlot.setDisabled(True)
                print (')ERROR - Plotter can only plot data with 64 Columns!')
            else:
                self.pPlot.setDisabled(False)
        except:
            print ('ERROR - Could not Extract data from file!')
            print ('Ensure desired data is on sheet 1 of the file')
            self.dfData = None 
            self.pPlot.setDisabled(True)

#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: This function Refreshes the COM Port List
#==============================================================================
    def RefreshCOMs(self):
        self.logMsg('Refresh COM Ports<br>', False, 'black')
        self.SearchCOMs()

#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: updates the plot fixed width if in fixed width mode
#==============================================================================
    def plotUpdateData(self):
        if(self.plotWidth.currentIndex() != 0):
            self.plotFixedWidth.show()
            self.x = self.xAll[(len(self.xAll) - self.plotFixedWidth.value()):]
            self.y = self.yAll[(len(self.yAll) - self.plotFixedWidth.value()):]
        else:
            self.plotFixedWidth.hide()

#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: downsamples the data for the plot
#==============================================================================
    def downsample(self, xstart, xend):
        max_points = self.plotFixedWidth.value()
        
        if(len(self.xAll) > max_points):
            origXData = np.asarray(self.xAll)
            origYData = np.asarray(self.yAll)
            mask = (origXData > xstart) & (origXData < xend)
            mask = np.convolve([1,1], mask, mode='same').astype(bool)
            ratio = max(np.sum(mask) // max_points, 1)
    
            xdata = origXData[mask]
            ydata = origYData[mask]
            
            xdata = xdata[::ratio]
            ydata = ydata[::ratio]
        else:
            xdata = self.xAll
            ydata = self.yAll
        
        return xdata, ydata
        
    def convertOutput(self):
        if(self.outputType == 'Pressure'):
            if(self.DataOutput.currentText() == 'PSI'):
                self.dataOutputMultiplier = 1.0
            elif(self.DataOutput.currentText() == 'HPA'):
                self.dataOutputMultiplier = 6894.7572932
            elif(self.DataOutput.currentText() == 'BAR'):
                self.dataOutputMultiplier = 0.068947572932
            elif(self.DataOutput.currentText() == 'MBAR'):
                self.dataOutputMultiplier = 68.947572932
            elif(self.DataOutput.currentText() == 'KPA'):
                self.dataOutputMultiplier = 6.8947572932
            elif(self.DataOutput.currentText() == 'CMH2O'):
                self.dataOutputMultiplier = 70.3069578296
            elif(self.DataOutput.currentText() == 'INH2O'):
                self.dataOutputMultiplier = 27.6799048425
            elif(self.DataOutput.currentText() == 'MMHG'):
                self.dataOutputMultiplier = 51.71492
                
            self.plot.setLabel('left', 'Pressure (' + str(self.DataOutput.currentText()) + ')', color = 'gray', size = 40)

        self.DataMultiplier.setText("{:.2f}".format(self.dataOutputMultiplier))
        
#==============================================================================
# Input Parameters: none
# Output Returns: none
#
# Description: This function Starts and Stops the Data Acqusition. This function
# will either stop after all the Samples have been taken or when the user presses
# the 'Stop' button.
#==============================================================================
    def ToggleStartStop(self):
        self.Start = not(self.Start)
        if(not(self.Start)):
            print ('Stop Test')
            self.timer.stop()
            self.testTimer.stop()
            self.DAQ.Abort = True
            self.DAQ.CloseCOM(str(self.COMDis.currentText()))
            
            self.plot.clear()
            self.data_line = self.plot.plot(self.xLive, self.yLive, pen=self.penGray)
            self.setCurrentIndex(0)
            self.tab1.setDisabled(False)
            
            self.plotStop.setText('Start')
            self.plotZero.setDisabled(True)
            self.plotWidth.setDisabled(True)
            self.plotFixedWidth.setDisabled(True)
            self.RefreshCOM.setDisabled(False)
            self.ButtonStart.setToolTip('Start Recording Data')
            
            self.logMsg('Saving Data...', True, 'black')
            
            if(os.path.exists(self.fileUniqueStr)):
                reply = QMessageBox.question(self, 'Overwrite File?', 'File already exists,\ndo you want to overwrite?', buttons=QMessageBox.No|QMessageBox.Yes, defaultButton=QMessageBox.No)
                if reply == QMessageBox.No:
                    self.SaveExcelAs(self.fileUniqueStr)
            
            if(self.SaveData(self.fileUniqueStr)):
                self.logMsg(self.FileOutput.text(), False, 'black')
                self.logMsg('...Data Saved!', True, '#00aa00')
            else:
                self.logMsg('...Data Could Not be Saved', True, 'red')
                self.logMsg('Check Recovery Files', True, 'red')
            
        else:
            print ('Starting Test')
            self.plotStop.setText('Stop')
            self.plotZero.setDisabled(False)
            self.plotWidth.setDisabled(False)
            self.plotFixedWidth.setDisabled(False)
            self.RefreshCOM.setDisabled(True)
            self.tab1.setDisabled(True)
            self.ButtonStart.setToolTip('Stop Recording Data')
            
            self.setCurrentIndex(1)
            
            self.plot.clear()
            self.x=[]
            self.y=[]
            self.data_line = self.plot.plot(self.x, self.y, pen=self.pen)
            
            if(self.DataTime.displayText() != '-'):
                testTime = int(float(self.DataTime.displayText())*1000)
                self.testTimer.start(testTime)
                print('Starting test for: '+str(testTime)+' mSec')
            
            self.timer.start()
            COMPort = str(self.COMDis.currentText())
            print (COMPort)
            # if(COMPort != 'NA'):            
            #     if(self.FirmDis.text() != 'NA'):
            #         self.logMsg('--DAQ Settings--<br><br>', True, '#900090')
            #         self.logMsg('Hardware Detected: ' + COMPort, False, 'black')
                    
            #         #Set the ADC Multiplier
            #         self.DAQ.SetTemp('NA') #Set the Temp variable in the MaxtecDAQ Class to NA
            #         if(self.DataMultiplier.text() == ''):   #Make sure there is a multiplier value
            #             self.DataMultiplier.setText('1')    #Default to 1
                        
            #         if(self.DataOutput.currentText() == 'Temp 700'):  #Check if the output is a Temperature
            #             self.DAQ.SetTemp('Temp700') #Set the Temp variable in the MaxtecDAQ Class to convert the final results 
                        
            #         elif(self.DataOutput.currentText() == 'Temp 800'):  #Check if the output is a Temperature
            #             self.DAQ.SetTemp('Temp800') #Set the Temp variable in the MaxtecDAQ Class to convert the final results                     
    
            #         self.DAQ.ADCMult = float(self.DataMultiplier.text())
            #         print ('ADCMult set to: ' + str(self.DataMultiplier.text()))
            #         self.logMsg('Multiplier: ' + str(self.DAQ.ADCMult), False, 'black')
                    
            #         #Set the Recording Time
            #         Time = self.DataTime.text()
            #         self.logMsg('Record Time: ' + Time + ' Sec', False, 'black')
                    
            #         #Set the Sample Rate
            #         SampRate = float(self.DataRate.text())
            #         self.logMsg('Sample Rate: ' + str(SampRate) + ' Samp/Sec', False, 'black')         
                    
            #         #Set the number of Samples
            #         SampNum = int(int(Time) * SampRate)
            #         self.logMsg('Number of Samples: ' + str(SampNum) + ' Samp', False, 'black')
                    
            #         Prefix = self.DataPrefix.text()
            #         self.logMsg('Header Names: ' + Prefix + 'xx', False, 'black')
            #         self.logMsg('Channels: ' + str(self.HardChannels.text()), False, 'black')
                    
            #         #Get Firmware Version
            #         FirmVer = self.DAQ.getFirmVer(COMPort)
            #         if(FirmVer != 'NA'):
            #             self.logMsg('Firmware: ' + str(FirmVer), False, 'black')
            #             self.FirmDis.setText(FirmVer)
            #         else:
            #             self.logMsg('ERROR! - Could not get Firmware from Teensy', True, 'red')
            #             self.FirmDis.setText('NA')
            #         List = []
            #         for i in range(0, int(self.HardChannels.text())):
            #             if(i < 9):
            #                 List.append(str(Prefix + '0' + str(i + 1)))
            #             else:
            #                 List.append(str(Prefix + str(i + 1)))
            #         print (List)          

            #         #Start Recording Data
            #         self.logMsg('Data Recording Started...', True, '#0000ff')                
            #         self.DAQ.ReadStart(List, SampNum, COMPort, SampRate)
            #         self.logMsg('...Data Recording Stopped', True, '#0000ff')
            #         self.logMsg('Saving Data...', True, '#00aa00')                
            #         if(self.DAQ.SaveData(self.fileUniqueStr)):
            #             self.logMsg(self.FileOutput.text(), False, 'black')
            #             self.logMsg('...Data Saved!', True, '#00aa00')
            #         else:
            #             self.logMsg('...Data Could Not be Saved', True, 'red')
            #             self.logMsg('Check Recovery Files', True, 'red')

            #     else:
            #         self.DAQ.ReadStreamStart(COMPort)                      
            #         with open(self.FileOutput.text(), "w") as text_files:
            #             text_files.write(self.Log.toPlainText())
            #         self.logMsg('Data Saved', True, '#00aa00')
            #     #Reset the start latch
            #     self.Start = not(self.Start)
                
            #     #Reset the start button
            #     self.ButtonStart.setText('Start')
            #     self.ButtonStart.setToolTip('Start Recording Data')

            # else: # COM Port not found
            #     self.logMsg('ERROR! - Teensy COM Port NOT Found', True, 'red')
    
    def zeroSensor(self):
        self.logMsg('Zeroing Sensor Output', False, 'blue')
        if(self.DAQ.zero()):
            self.logMsg('Zero Set', False, 'green')
        else:
            self.logMsg('Zero NOT Set', False, 'red')
    
    def SaveData(self, fname):
        print ("Saving to Excel")
        timeIndex = [x/float(self.DataRate.displayText()) for x in self.xAll]
        pressureHeading = 'Pressure ('+self.DataOutput.currentText()+')'
        data = {'Time (sec)':timeIndex, pressureHeading:self.yAll}
        self.df = pd.DataFrame(data=data)
        try:
            self.df.to_excel(str(fname))
            print ("File Saved!")
            return True
        except:
            print ("ERROR - Could not save Excel File!")
            RecoveryPath = str(os.getcwd()) + '\Recovery-' + str(datetime.datetime.now().strftime('%H%M%S') + '.xlsx')
            self.df.to_excel(RecoveryPath)
            print ("File Recovery avaliable at: " + RecoveryPath)
            return False
    
    def closeEvent(self, event):
        if self.Start:
            quit_msg = 'Are you sure you want to exit the program?'
            reply = QMessageBox.question(self, 'Message', quit_msg, QMessageBox.Yes, QMessageBox.No)
            
            if reply == QMessageBox.Yes:
                self.ToggleStartStop()
                event.accept()
            else:
                event.ignore()
        else:
            event.accept()
Esempio n. 16
0
class OptionsDialog(QDialog):
    def __init__(self, setting: Settings, have_dutils, parent=None):
        super(OptionsDialog, self).__init__(parent)

        self.settings = setting
        self.enabled_video = True  # temporary toggle to disable video features as they do not exist
        self.enabled_logging = True
        self.enabled_keybindings = True
        self.enabled_dutils = have_dutils
        self.setWindowTitle("Tcam-Capture Options")
        self.layout = QVBoxLayout(self)
        self.setLayout(self.layout)

        self.tabs = QTabWidget()

        self.general_widget = QWidget()
        self.keybindings_widget = QWidget()
        self.logging_widget = QWidget()
        self.saving_widget = QWidget()

        self._setup_general_ui()
        self.tabs.addTab(self.general_widget, "General")

        if self.enabled_keybindings:
            self._setup_keybindings_ui()
            self.tabs.addTab(self.keybindings_widget, "Keybindings")
        self._setup_saving_ui()
        self.tabs.addTab(self.saving_widget, "Image/Video")

        self.layout.addWidget(self.tabs)
        # OK and Cancel buttons
        self.buttons = QDialogButtonBox(
            QDialogButtonBox.Reset | QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
            Qt.Horizontal, self)
        self.layout.addWidget(self.buttons)

        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)
        self.buttons.clicked.connect(self.clicked)

    def _setup_general_ui(self):
        """
        Create everything related to the general tab
        """

        layout = QFormLayout()
        layout.setSpacing(20)
        layout.setVerticalSpacing(20)

        self.device_dialog_checkbox = QCheckBox(self)
        device_dialog_label = QLabel("Open device dialog on start:")
        layout.addRow(device_dialog_label,
                      self.device_dialog_checkbox)

        self.reopen_device_checkbox = QCheckBox(self)
        reopen_device_label = QLabel("Reopen device on start(ignores device dialog):", self)
        layout.addRow(reopen_device_label,
                      self.reopen_device_checkbox)

        self.use_dutils_checkbox = QCheckBox(self)
        self.use_dutils_label = QLabel("Use tiscamera dutils, if present:", self)
        layout.addRow(self.use_dutils_label,
                      self.use_dutils_checkbox)

        if not self.enabled_dutils:
            self.use_dutils_label.setToolTip("Enabled when tiscamera-dutils are installed")
            self.use_dutils_label.setEnabled(False)
            self.use_dutils_checkbox.setToolTip("Enabled when tiscamera-dutils are installed")
            self.use_dutils_checkbox.setEnabled(False)

        self.general_widget.setLayout(layout)

    def _setup_saving_ui(self):
        """
        Create everything related to the image/video saving tab
        """
        encoder_dict = Encoder.get_encoder_dict()
        form_layout = QFormLayout()

        layout = QVBoxLayout()
        layout.addLayout(form_layout)

        location_layout = QHBoxLayout()
        location_label = QLabel("Where to save images/videos:", self)
        self.location_edit = QLineEdit(self)
        location_dialog_button = QPushButton("...", self)
        location_dialog_button.clicked.connect(self.open_file_dialog)
        location_layout.addWidget(self.location_edit)
        location_layout.addWidget(location_dialog_button)

        # maintain descriptions as own labels
        # pyqt seems to loose the descriptions somewhere
        # when simple strings are used or the qlabel does not have self as owner
        form_layout.addRow(location_label,
                           location_layout)

        self.image_type_combobox = QComboBox(self)
        for key, value in encoder_dict.items():
            if value.encoder_type == Encoder.MediaType.image:
                self.image_type_combobox.addItem(key)
        image_type_label = QLabel("Save images as:")
        self.image_type_combobox.currentIndexChanged['QString'].connect(self.image_name_suffix_changed)

        form_layout.addRow(image_type_label,
                           self.image_type_combobox)
        if self.enabled_video:
            self.video_type_combobox = QComboBox(self)
            for key, value in encoder_dict.items():
                if value.encoder_type == Encoder.MediaType.video:
                    self.video_type_combobox.addItem(key)
            self.video_type_combobox.currentIndexChanged['QString'].connect(self.video_name_suffix_changed)

            video_type_label = QLabel("Save videos as:", self)
            form_layout.addRow(video_type_label,
                               self.video_type_combobox)

        image_name_groupbox = QGroupBox("Image File Names")
        groupbox_layout = QFormLayout()
        image_name_groupbox.setLayout(groupbox_layout)

        self.image_name_preview = QLabel("<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png")
        self.image_name_preview_description = QLabel("Images will be named like:")
        groupbox_layout.addRow(self.image_name_preview_description,
                               self.image_name_preview)

        self.image_name_prefix = QLineEdit()
        self.image_name_prefix.textChanged.connect(self.image_name_prefix_changed)
        self.image_name_prefix.setMaxLength(100)

        self.image_name_prefix_description = QLabel("User Prefix:", self)
        groupbox_layout.addRow(self.image_name_prefix_description,
                               self.image_name_prefix)

        self.image_name_serial = QCheckBox(self)
        self.image_name_serial.toggled.connect(self.image_name_properties_toggled)
        self.image_name_serial_description = QLabel("Include Serial:")
        groupbox_layout.addRow(self.image_name_serial_description,
                               self.image_name_serial)

        self.image_name_format = QCheckBox(self)
        self.image_name_format.toggled.connect(self.image_name_properties_toggled)

        self.image_name_format_description = QLabel("Include Format:")
        groupbox_layout.addRow(self.image_name_format_description,
                               self.image_name_format)

        self.image_name_counter = QCheckBox(self)
        self.image_name_counter.toggled.connect(self.image_name_properties_toggled)
        self.image_name_counter_description = QLabel("Include Counter:")
        groupbox_layout.addRow(self.image_name_counter_description,
                               self.image_name_counter)

        self.image_name_counter_box = QSpinBox(self)
        self.image_name_counter_box.setRange(1, 10)
        self.image_name_counter_box.valueChanged.connect(self.image_name_counter_changed)
        self.image_name_counter_box_description = QLabel("Counter Size:")
        groupbox_layout.addRow(self.image_name_counter_box_description,
                               self.image_name_counter_box)

        self.image_name_counter.toggled.connect(self.toggle_image_counter_box_availability)
        self.image_name_counter.toggled.connect(self.image_name_properties_toggled)

        self.image_name_timestamp = QCheckBox(self)
        self.image_name_timestamp.toggled.connect(self.image_name_properties_toggled)
        self.image_name_timestamp_description = QLabel("Include Timestamp:")
        groupbox_layout.addRow(self.image_name_timestamp_description,
                               self.image_name_timestamp)

        layout.addWidget(image_name_groupbox)

        video_groupbox = QGroupBox("Video File Names")

        video_layout = QFormLayout()
        video_groupbox.setLayout(video_layout)

        self.video_name_preview = QLabel("<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png")
        self.video_name_preview_description = QLabel("Videos will be named like:")
        video_layout.addRow(self.video_name_preview_description,
                            self.video_name_preview)

        self.video_name_prefix = QLineEdit()
        self.video_name_prefix.textChanged.connect(self.video_name_prefix_changed)
        self.video_name_prefix.setMaxLength(100)

        self.video_name_prefix_description = QLabel("User Prefix:", self)
        video_layout.addRow(self.video_name_prefix_description,
                            self.video_name_prefix)

        self.video_name_serial = QCheckBox(self)
        self.video_name_serial.toggled.connect(self.video_name_properties_toggled)
        self.video_name_serial_description = QLabel("Include Serial:")
        video_layout.addRow(self.video_name_serial_description,
                            self.video_name_serial)

        self.video_name_format = QCheckBox(self)
        self.video_name_format.toggled.connect(self.video_name_properties_toggled)

        self.video_name_format_description = QLabel("Include Format:")
        video_layout.addRow(self.video_name_format_description,
                            self.video_name_format)

        self.video_name_counter = QCheckBox(self)
        self.video_name_counter.toggled.connect(self.video_name_properties_toggled)
        self.video_name_counter_description = QLabel("Include Counter:")
        video_layout.addRow(self.video_name_counter_description,
                            self.video_name_counter)

        self.video_name_counter_box = QSpinBox(self)
        self.video_name_counter_box.setRange(1, 10)
        self.video_name_counter_box.valueChanged.connect(self.video_name_counter_changed)
        self.video_name_counter_box_description = QLabel("Counter Size:")
        video_layout.addRow(self.video_name_counter_box_description,
                            self.video_name_counter_box)

        self.video_name_counter.toggled.connect(self.toggle_video_counter_box_availability)
        self.video_name_counter.toggled.connect(self.video_name_properties_toggled)

        self.video_name_timestamp = QCheckBox(self)
        self.video_name_timestamp.toggled.connect(self.video_name_properties_toggled)
        self.video_name_timestamp_description = QLabel("Include Timestamp:")
        video_layout.addRow(self.video_name_timestamp_description,
                            self.video_name_timestamp)

        layout.addWidget(video_groupbox)

        self.saving_widget.setLayout(layout)

    def image_name_prefix_changed(self, name: str):
        """"""

        self.settings.image_name.user_prefix = self.image_name_prefix.text()
        self.update_image_name_preview()

    def image_name_suffix_changed(self, suffix: str):
        """"""

        self.update_image_name_preview()

    def image_name_counter_changed(self, name: str):
        """"""
        self.settings.image_name.counter_size = self.image_name_counter_box.value()
        self.update_image_name_preview()

    def image_name_properties_toggled(self):
        """"""

        self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked()
        self.settings.image_name.include_counter = self.image_name_counter.isChecked()
        self.settings.image_name.include_format = self.image_name_format.isChecked()
        self.settings.image_name.include_serial = self.image_name_serial.isChecked()

        self.update_image_name_preview()

    def update_image_name_preview(self):

        preview_string = ""

        if self.settings.image_name.user_prefix != "":

            max_prefix_length = 15
            prefix = (self.settings.image_name.user_prefix[:max_prefix_length] + '..') if len(self.settings.image_name.user_prefix) > max_prefix_length else self.settings.image_name.user_prefix

            preview_string += prefix

        if self.settings.image_name.include_serial:
            if preview_string != "":
                preview_string += "-"
            preview_string += "00001234"

        if self.settings.image_name.include_format:
            if preview_string != "":
                preview_string += "-"
            preview_string += "gbrg_1920x1080_15_1"

        if self.settings.image_name.include_timestamp:
            if preview_string != "":
                preview_string += "-"
            preview_string += "19701230T125503"

        if self.settings.image_name.include_counter:
            if preview_string != "":
                preview_string += "-"
            preview_string += '{message:0>{fill}}'.format(message=1,
                                                          fill=self.settings.image_name.counter_size)

        if preview_string == "":
            preview_string = "image"

        preview_string += "." + self.image_type_combobox.currentText()

        self.image_name_preview.setText(preview_string)


    def video_name_prefix_changed(self, name: str):
        """"""

        self.settings.video_name.user_prefix = self.video_name_prefix.text()
        self.update_video_name_preview()

    def video_name_suffix_changed(self, suffix: str):
        """"""

        self.update_video_name_preview()

    def video_name_counter_changed(self, name: str):
        """"""
        self.settings.video_name.counter_size = self.video_name_counter_box.value()
        self.update_video_name_preview()

    def video_name_properties_toggled(self):
        """"""

        self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked()
        self.settings.video_name.include_counter = self.video_name_counter.isChecked()
        self.settings.video_name.include_format = self.video_name_format.isChecked()
        self.settings.video_name.include_serial = self.video_name_serial.isChecked()

        self.update_video_name_preview()

    def update_video_name_preview(self):

        preview_string = ""

        if self.settings.video_name.user_prefix != "":

            # This is a convenience change to the displayed string.
            # We only display an amount of max_prefix_length
            # chars to save screen space
            max_prefix_length = 15
            prefix = (self.settings.video_name.user_prefix[:max_prefix_length] + '..') if len(self.settings.video_name.user_prefix) > max_prefix_length else self.settings.video_name.user_prefix

            preview_string += prefix

        if self.settings.video_name.include_serial:
            if preview_string != "":
                preview_string += "-"
            preview_string += "00001234"

        if self.settings.video_name.include_format:
            if preview_string != "":
                preview_string += "-"
            preview_string += "gbrg_1920x1080_15_1"

        if self.settings.video_name.include_timestamp:
            if preview_string != "":
                preview_string += "-"
            preview_string += "19701230T125503"

        if self.settings.video_name.include_counter:
            if preview_string != "":
                preview_string += "-"
            preview_string += '{message:0>{fill}}'.format(message=1,
                                                          fill=self.settings.video_name.counter_size)

        if preview_string == "":
            preview_string = "video"

        preview_string += "." + self.video_type_combobox.currentText()

        self.video_name_preview.setText(preview_string)

    def toggle_image_counter_box_availability(self):
        """"""
        if self.image_name_counter.isChecked():
            self.image_name_counter_box.setEnabled(True)
        else:
            self.image_name_counter_box.setEnabled(False)

    def toggle_video_counter_box_availability(self):
        """"""
        if self.video_name_counter.isChecked():
            self.video_name_counter_box.setEnabled(True)
        else:
            self.video_name_counter_box.setEnabled(False)

    def _setup_keybindings_ui(self):
        """
        Create everything related to the keybindings tab
        """

        layout = QFormLayout()
        self.keybinding_fullscreen_label = QLabel("Toggle Fullscreen:")
        self.keybinding_fullscreen = QKeySequenceEdit()
        layout.addRow(self.keybinding_fullscreen_label,
                      self.keybinding_fullscreen)

        self.keybinding_save_image_label = QLabel("Save image:")
        self.keybinding_save_image = QKeySequenceEdit(QKeySequence(self.settings.keybinding_save_image))
        layout.addRow(self.keybinding_save_image_label,
                      self.keybinding_save_image)

        self.keybinding_trigger_image_label = QLabel("Trigger images via softwaretrigger:")
        self.keybinding_trigger_image = QKeySequenceEdit(QKeySequence(self.settings.keybinding_trigger_image))
        layout.addRow(self.keybinding_trigger_image_label,
                      self.keybinding_trigger_image)

        self.keybinding_open_dialog_label = QLabel("Open device dialog:")
        self.keybinding_open_dialog = QKeySequenceEdit(QKeySequence(self.settings.keybinding_open_dialog))
        layout.addRow(self.keybinding_open_dialog_label,
                      self.keybinding_open_dialog)

        self.keybindings_widget.setLayout(layout)

    def set_settings(self, settings: Settings):
        self.location_edit.setText(settings.get_save_location())
        self.image_type_combobox.setCurrentText(settings.get_image_type())
        if self.enabled_video:
            self.video_type_combobox.setCurrentText(settings.get_video_type())
        self.device_dialog_checkbox.setChecked(settings.show_device_dialog_on_startup)
        self.reopen_device_checkbox.setChecked(settings.reopen_device_on_startup)
        self.use_dutils_checkbox.setChecked(settings.use_dutils)

        #
        # keybindings
        #
        if self.enabled_keybindings:
            self.keybinding_fullscreen.setKeySequence(QKeySequence(self.settings.keybinding_fullscreen))
            self.keybinding_save_image.setKeySequence(QKeySequence(self.settings.keybinding_save_image))
            self.keybinding_trigger_image.setKeySequence(QKeySequence(self.settings.keybinding_trigger_image))
            self.keybinding_open_dialog.setKeySequence(QKeySequence(self.settings.keybinding_open_dialog))

        #
        # image saving
        #
        if settings.image_name.include_timestamp:
            self.image_name_timestamp.blockSignals(True)
            self.image_name_timestamp.toggle()
            self.image_name_timestamp.blockSignals(False)
        if settings.image_name.include_counter:
            self.image_name_counter.blockSignals(True)
            self.image_name_counter.toggle()
            self.image_name_counter.blockSignals(False)

        self.image_name_counter_box.blockSignals(True)
        self.image_name_counter_box.setValue(settings.image_name.counter_size)
        self.image_name_counter_box.blockSignals(False)
        self.toggle_image_counter_box_availability()

        if settings.image_name.include_format:
            self.image_name_format.blockSignals(True)
            self.image_name_format.toggle()
            self.image_name_format.blockSignals(False)
        if settings.image_name.include_serial:
            self.image_name_serial.blockSignals(True)
            self.image_name_serial.toggle()
            self.image_name_serial.blockSignals(False)
        self.image_name_prefix.blockSignals(True)
        self.image_name_prefix.setText(settings.image_name.user_prefix)
        self.image_name_prefix.blockSignals(False)

        self.update_image_name_preview()

        #
        # video saving
        #
        if settings.video_name.include_timestamp:
            self.video_name_timestamp.blockSignals(True)
            self.video_name_timestamp.toggle()
            self.video_name_timestamp.blockSignals(False)
        if settings.video_name.include_counter:
            self.video_name_counter.blockSignals(True)
            self.video_name_counter.toggle()
            self.video_name_counter.blockSignals(False)

        self.video_name_counter_box.blockSignals(True)
        self.video_name_counter_box.setValue(settings.video_name.counter_size)
        self.video_name_counter_box.blockSignals(False)
        self.toggle_video_counter_box_availability()

        if settings.video_name.include_format:
            self.video_name_format.blockSignals(True)
            self.video_name_format.toggle()
            self.video_name_format.blockSignals(False)
        if settings.video_name.include_serial:
            self.video_name_serial.blockSignals(True)
            self.video_name_serial.toggle()
            self.video_name_serial.blockSignals(False)
        self.video_name_prefix.blockSignals(True)
        self.video_name_prefix.setText(settings.video_name.user_prefix)
        self.video_name_prefix.blockSignals(False)

        self.update_video_name_preview()

    def save_settings(self):
        self.settings.save_location = self.location_edit.text()
        self.settings.image_type = self.image_type_combobox.currentText()
        if self.enabled_video:
            self.settings.video_type = self.video_type_combobox.currentText()
        self.settings.show_device_dialog_on_startup = self.device_dialog_checkbox.isChecked()
        self.settings.reopen_device_on_startup = self.reopen_device_checkbox.isChecked()
        self.settings.use_dutils = self.use_dutils_checkbox.isChecked()

        #
        # keybindings
        #
        if self.enabled_keybindings:
            self.settings.keybinding_fullscreen = self.keybinding_fullscreen.keySequence().toString()
            self.settings.keybinding_save_image = self.keybinding_save_image.keySequence().toString()
            self.settings.keybinding_trigger_image = self.keybinding_trigger_image.keySequence().toString()
            self.settings.keybinding_open_dialog = self.keybinding_open_dialog.keySequence().toString()

        #
        # image saving
        #
        self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked()
        self.settings.image_name.include_counter = self.image_name_counter.isChecked()
        if self.image_name_counter.isChecked():
            self.settings.image_name.counter_size = self.image_name_counter_box.value()

        self.settings.image_name.include_format = self.image_name_format.isChecked()
        self.settings.image_name.include_serial = self.image_name_serial.isChecked()
        self.settings.image_name.user_prefix = self.image_name_prefix.text()

        #
        # video saving
        #
        self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked()
        self.settings.video_name.include_counter = self.video_name_counter.isChecked()
        if self.video_name_counter.isChecked():
            self.settings.video_name.counter_size = self.video_name_counter_box.value()

        self.settings.video_name.include_format = self.video_name_format.isChecked()
        self.settings.video_name.include_serial = self.video_name_serial.isChecked()
        self.settings.video_name.user_prefix = self.video_name_prefix.text()

    def open_file_dialog(self):
        fdia = QFileDialog()
        fdia.setFileMode(QFileDialog.Directory)
        fdia.setWindowTitle("Select Directory for saving images and videos")
        if fdia.exec_():
            self.location_edit.setText(fdia.selectedFiles()[0])

    def get_location(self):
        return self.location_edit.text()

    def get_image_format(self):
        return self.image_type_combobox.currentText()

    def get_video_format(self):
        return self.video_type_combobox.currentText()

    def clicked(self, button):

        if self.buttons.buttonRole(button) == QDialogButtonBox.ResetRole:
            self.reset()

    def reset(self):
        """"""
        log.info("reset called")
        self.settings.reset()
        self.set_settings(self.settings)

    @staticmethod
    def get_options(settings, parent=None):
        dialog = OptionsDialog(settings, parent)

        if settings is not None:
            dialog.set_settings(settings)
        result = dialog.exec_()

        if result == QDialog.Accepted:
            dialog.save_settings()
            settings.save()

        return result == QDialog.Accepted
class SendFundsDestination(QtWidgets.QWidget):
    resized_signal = QtCore.pyqtSignal()

    def __init__(self, parent, parent_dialog, app_config,
                 hw_session: HwSessionInfo):
        QtWidgets.QWidget.__init__(self, parent)
        self.app_config = app_config
        self.parent_dialog = parent_dialog
        self.hw_session = hw_session
        self.recipients: List[SendFundsDestinationItem] = []
        self.change_addresses: List[Tuple[str, str]] = [
        ]  # List[Tuple[address, bip32 path]]
        self.change_controls_visible = True
        self.address_widget_width = None
        self.inputs_total_amount = 0.0
        self.fee_amount = 0.0
        self.inputs_count = 0
        self.values_unit = OUTPUT_VALUE_UNIT_AMOUNT
        self.tm_calculate_change_value = QTimer(self)
        self.tm_calculate_change_value.timeout.connect(
            self.on_tm_calculate_change_value)
        self.current_file_name = ''
        self.current_file_encrypted = False
        self.recent_data_files = []  # recent used data files
        self.setupUi(self)

    def setupUi(self, Form):
        self.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
                                  QtWidgets.QSizePolicy.MinimumExpanding))

        self.lay_main = QtWidgets.QVBoxLayout(Form)
        self.lay_main.setContentsMargins(6, 6, 6, 6)
        self.lay_main.setSpacing(3)

        # 'totals' area:
        self.lbl_totals = QLabel(Form)
        self.lbl_totals.setTextInteractionFlags(
            QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
        self.lay_main.addWidget(self.lbl_totals)

        # output definition data file labels:
        self.lay_data_file = QHBoxLayout()
        self.lay_data_file.setContentsMargins(0, 0, 0, 6)
        self.lay_main.addItem(self.lay_data_file)
        self.lbl_data_file_name = QLabel(Form)
        self.lay_data_file.addWidget(self.lbl_data_file_name)
        self.lbl_data_file_badge = QLabel(Form)
        self.lay_data_file.addWidget(self.lbl_data_file_badge)
        self.lbl_data_file_name.setTextInteractionFlags(
            QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
        self.lbl_data_file_badge.setTextInteractionFlags(
            QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
        self.lay_data_file.addStretch()

        # actions/options area:
        self.lay_actions = QHBoxLayout()
        self.lay_actions.setSpacing(6)
        self.lay_actions.setContentsMargins(0, 0, 0, 0)
        self.lay_main.addItem(self.lay_actions)
        self.btn_add_recipient = QPushButton(Form)
        self.btn_add_recipient.clicked.connect(
            partial(self.add_dest_address, 1))
        self.btn_add_recipient.setAutoDefault(False)
        self.btn_add_recipient.setText("Add recipient")
        self.lay_actions.addWidget(self.btn_add_recipient)
        #
        self.btn_actions = QPushButton(Form)
        self.btn_actions.clicked.connect(partial(self.add_dest_address, 1))
        self.btn_actions.setAutoDefault(False)
        self.btn_actions.setText("Actions")
        self.lay_actions.addWidget(self.btn_actions)

        # context menu for the 'Actions' button
        self.mnu_actions = QMenu()
        self.btn_actions.setMenu(self.mnu_actions)
        a = self.mnu_actions.addAction("Load from file...")
        a.triggered.connect(self.on_read_from_file_clicked)
        self.mnu_recent_files = self.mnu_actions.addMenu('Recent files')
        self.mnu_recent_files.setVisible(False)
        a = self.mnu_actions.addAction("Save to encrypted file...")
        a.triggered.connect(partial(self.save_to_file, True))
        a = self.mnu_actions.addAction("Save to plain CSV file...")
        a.triggered.connect(partial(self.save_to_file, False))
        a = self.mnu_actions.addAction("Clear recipients")
        a.triggered.connect(self.clear_outputs)

        self.lbl_output_unit = QLabel(Form)
        self.lbl_output_unit.setText('Values as')
        self.lay_actions.addWidget(self.lbl_output_unit)
        self.cbo_output_unit = QComboBox(Form)
        self.cbo_output_unit.addItems(['amount', 'percentage'])
        self.cbo_output_unit.setCurrentIndex(0)
        self.cbo_output_unit.currentIndexChanged.connect(
            self.on_cbo_output_unit_change)
        self.lay_actions.addWidget(self.cbo_output_unit)
        self.lay_actions.addStretch(0)

        # scroll area for send to (destination) addresses
        self.scroll_area = QtWidgets.QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area.setMinimumHeight(30)
        self.scroll_area.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
                                  QtWidgets.QSizePolicy.MinimumExpanding))
        self.scroll_area.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.lay_main.addWidget(self.scroll_area)

        self.scroll_area_widget = QtWidgets.QWidget()
        self.scroll_area_widget.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
                                  QtWidgets.QSizePolicy.MinimumExpanding))
        self.lay_scroll_area = QtWidgets.QVBoxLayout()
        self.lay_scroll_area.setContentsMargins(0, 0, 0, 0)
        self.lay_scroll_area.setSpacing(0)
        self.scroll_area_widget.setLayout(self.lay_scroll_area)
        self.scroll_area.setWidget(self.scroll_area_widget)

        # grid layout for destination addresses and their corresponding controls:
        self.lay_addresses = QtWidgets.QGridLayout()
        self.lay_addresses.setSpacing(3)
        self.lay_addresses.setContentsMargins(0, 0, 0, 0)
        self.lay_scroll_area.addLayout(self.lay_addresses)
        self.lay_scroll_area.addStretch(0)

        # controls for the 'change' address/amount (it's placed in the last row of the addresses grid layout):
        self.lbl_change_address = QLabel(self.scroll_area_widget)
        self.lbl_change_address.setText('Change address')
        self.lbl_change_address.setAlignment(QtCore.Qt.AlignRight
                                             | QtCore.Qt.AlignTrailing
                                             | QtCore.Qt.AlignVCenter)
        self.lay_addresses.addWidget(self.lbl_change_address, 0, 0)
        # the 'change' address combobox:
        self.cbo_change_address = QtWidgets.QComboBox(self.scroll_area_widget)
        width = self.cbo_change_address.fontMetrics().width(
            'XvqNXF23dRBksxjW3VQGrBtJw7vkhWhenQ')
        self.address_widget_width = width + 40
        # combobox width on macos needs to be tweaked:
        self.cbo_change_address.setFixedWidth(
            self.address_widget_width + {'darwin': 5}.get(sys.platform, 0))
        self.lay_addresses.addWidget(self.cbo_change_address, 0, 1)
        self.lbl_change_amount = QLabel(self.scroll_area_widget)
        self.set_change_value_label()
        self.lay_addresses.addWidget(self.lbl_change_amount, 0, 2)
        # read only editbox for the amount of the change:
        self.edt_change_amount = QLineEdit(self.scroll_area_widget)
        self.edt_change_amount.setFixedWidth(100)
        self.edt_change_amount.setReadOnly(True)
        self.edt_change_amount.setStyleSheet('background-color:lightgray')
        self.lay_addresses.addWidget(self.edt_change_amount, 0, 3)
        # label dedicated to the second-unit value (e.g percentage if the main unit is set to (Dash) amount value)
        self.lbl_second_unit = QLabel(self.scroll_area_widget)
        self.lay_addresses.addWidget(self.lbl_second_unit, 0, 4)
        # spacer
        spacer = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding,
                                       QtWidgets.QSizePolicy.Minimum)
        self.lay_addresses.addItem(spacer, 0, 5)

        # the last row of the grid layout is dedicated to 'fee' controls
        self.lbl_fee = QLabel(self.scroll_area_widget)
        self.lbl_fee.setText('Fee [Dash]')
        self.lbl_fee.setAlignment(QtCore.Qt.AlignRight
                                  | QtCore.Qt.AlignTrailing
                                  | QtCore.Qt.AlignVCenter)
        self.lay_addresses.addWidget(self.lbl_fee, 1, 0)

        # the fee value editbox with the 'use default' button:
        self.lay_fee_value = QHBoxLayout()
        self.lay_fee_value.setContentsMargins(0, 0, 0, 0)
        self.lay_fee_value.setSpacing(0)
        self.lay_addresses.addItem(self.lay_fee_value, 1, 1)
        self.edt_fee_value = QLineEdit(self.scroll_area_widget)
        self.edt_fee_value.setFixedWidth(100)
        self.edt_fee_value.textChanged.connect(self.on_edt_fee_value_changed)
        self.lay_fee_value.addWidget(self.edt_fee_value)
        self.btn_get_default_fee = QToolButton(self.scroll_area_widget)
        self.btn_get_default_fee.setText('\u2b06')
        self.btn_get_default_fee.setFixedSize(
            14,
            self.edt_fee_value.sizeHint().height())
        self.btn_get_default_fee.setToolTip('Use default fee')
        self.btn_get_default_fee.clicked.connect(
            self.on_btn_get_default_fee_clicked)
        self.lay_fee_value.addWidget(self.btn_get_default_fee)
        self.lay_fee_value.addStretch(0)

        # below the addresses grid place a label dedicated do display messages
        self.lbl_message = QLabel(Form)
        self.lbl_message.setTextInteractionFlags(
            QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
        self.lbl_message.setVisible(False)
        self.lay_main.addWidget(self.lbl_message)

        # add one 'send to' address row (in most cases it will bu sufficient)
        self.add_dest_address(1)

        # load last used file names from cache
        mru = app_cache.get_value(CACHE_ITEM_DATA_FILE_MRU_LIST,
                                  default_value=[],
                                  type=list)
        if isinstance(mru, list):
            for file_name in mru:
                if os.path.exists(file_name):
                    self.recent_data_files.append(file_name)
        self.update_mru_menu_items()

        self.retranslateUi(Form)

    def retranslateUi(self, Form):
        pass

    def sizeHint(self):
        sh = self.lay_scroll_area.sizeHint()
        marg_sl = self.lay_scroll_area.getContentsMargins()
        marg_ml = self.lay_main.getContentsMargins()
        if self.lbl_message.isVisible():
            msg_height = self.lbl_message.height()
        else:
            msg_height = 0
        sh.setHeight(sh.height() + marg_sl[1] + marg_sl[3] +
                     self.lay_actions.sizeHint().height() +
                     self.lbl_totals.sizeHint().height() +
                     self.lay_data_file.sizeHint().height() +
                     ((self.lay_main.count() - 1) * self.lay_main.spacing()) +
                     marg_ml[1] + marg_ml[3] + msg_height)
        return sh

    def display_message(self, message, color: Optional[str] = None):
        if message:
            self.lbl_message.setText(message)
            if color:
                self.lbl_message.setStyleSheet(f'QLabel{{color:{color}}}')
            changed_visibility = self.lbl_message.isVisible() != True
            self.lbl_message.setVisible(True)
        else:
            changed_visibility = self.lbl_message.isVisible() != False
            self.lbl_message.setVisible(False)

        if changed_visibility:
            QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
            self.resized_signal.emit()

    def move_grid_layout_row(self, from_row, to_row):
        for col_idx in range(self.lay_addresses.columnCount()):
            item = self.lay_addresses.itemAtPosition(from_row, col_idx)
            if item:
                if isinstance(item, QWidgetItem):
                    w = item.widget()
                    self.lay_addresses.removeWidget(w)
                    self.lay_addresses.addWidget(w, to_row, col_idx)
                elif isinstance(item, QLayout):
                    self.lay_addresses.removeItem(item)
                    self.lay_addresses.addItem(item, to_row, col_idx)
                elif isinstance(item, QSpacerItem):
                    self.lay_addresses.removeItem(item)
                    self.lay_addresses.addItem(item, to_row, col_idx)
                else:
                    raise Exception('Invalid item type')

    def add_dest_address(self, address_count: int = 1):
        # make a free space in the grid-layout for new addresses, just behind the last item related to the dest address
        for row_idx in reversed(
                range(len(self.recipients), self.lay_addresses.rowCount())):
            self.move_grid_layout_row(row_idx, row_idx + address_count)

        for nr in range(address_count):
            rcp_item = SendFundsDestinationItem(self.scroll_area_widget,
                                                self.app_config,
                                                self.lay_addresses,
                                                len(self.recipients),
                                                self.address_widget_width)
            rcp_item.sig_remove_address.connect(self.remove_dest_address)
            rcp_item.sig_use_all_funds.connect(self.use_all_funds_for_address)
            rcp_item.sig_amount_changed.connect(self.amount_changed)
            rcp_item.set_output_value_unit(self.values_unit)
            rcp_item.set_inputs_total_amount(self.inputs_total_amount -
                                             self.fee_amount)
            self.recipients.append(rcp_item)

        QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
        self.resized_signal.emit()
        self.show_hide_remove_buttons()
        self.display_totals()
        self.set_default_fee()

    def remove_item_from_layout(self, item):
        if item:
            if isinstance(item, QWidgetItem):
                w = item.widget()
                self.lay_addresses.removeWidget(w)
                w.setParent(None)
                del w
            elif isinstance(item, QLayout):
                for subitem_idx in reversed(range(item.count())):
                    subitem = item.itemAt(subitem_idx)
                    self.remove_item_from_layout(subitem)
                self.lay_addresses.removeItem(item)
                item.setParent(None)
                del item
            elif isinstance(item, QSpacerItem):
                del item
            else:
                raise Exception('Invalid item type')

    def remove_dest_address(self, address_item):
        row_idx = self.recipients.index(address_item)
        # remove all widgets related to the 'send to' address that is being removed
        for col_idx in range(self.lay_addresses.columnCount()):
            item = self.lay_addresses.itemAtPosition(row_idx, col_idx)
            self.remove_item_from_layout(item)

        # move up all rows greater than the row being removed
        for row in range(row_idx + 1, len(self.recipients)):
            self.move_grid_layout_row(row, row - 1)

        del self.recipients[row_idx]

        QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
        self.resized_signal.emit()
        self.show_hide_remove_buttons()
        self.set_default_fee()
        # self.calculate_change_amount()
        self.display_totals()

    def use_all_funds_for_address(self, address_item):
        row_idx = self.recipients.index(address_item)
        sum = 0.0
        left = 0.0

        # sum all the funds in all rows other than the current one
        for idx, addr in enumerate(self.recipients):
            if idx != row_idx:
                sum += addr.get_value(default_value=0.0)

        if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
            left = self.inputs_total_amount - sum - self.fee_amount
        elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
            left = 100.0 - sum

        left = round(left, 8) + 0.0
        if left < 0:
            left = 0.0
        address_item.set_value(left)
        self.update_change_amount()

    def amount_changed(self, addres_item):
        """ Activated after changing value in the 'amount' edit box of a recipient address. """
        self.init_calculate_change_value()

    def on_edt_fee_value_changed(self, text):
        if not text:
            text = '0.0'
        try:
            self.fee_amount = float(text)
            self.init_calculate_change_value()
        except Exception:
            self.display_message('Invalid \'transaction fee\' value.',
                                 'red')  # display error message

    def show_hide_change_address(self, visible):
        if visible != self.change_controls_visible:
            row_nr = self.lay_addresses.rowCount() - 1
            if row_nr >= 0:
                for col_idx in range(self.lay_addresses.columnCount()):
                    item = self.lay_addresses.itemAtPosition(row_nr, col_idx)
                    if item:
                        if isinstance(item, QWidgetItem):
                            item.widget().setVisible(visible)
                        elif isinstance(
                                item, (QSpacerItem, QHBoxLayout, QVBoxLayout)):
                            pass
                        else:
                            raise Exception('Invalid item type')
            self.change_controls_visible = visible
            QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
            self.resized_signal.emit()

    def show_hide_remove_buttons(self):
        visible = len(self.recipients) > 1
        for item in self.recipients:
            item.set_btn_remove_address_visible(visible)

    def set_change_addresses(self, addresses: List[Tuple[str, str]]):
        """
        :param addresses: addresses[0]: dest change address
                          addresses[1]: dest change bip32
        :return:
        """
        self.cbo_change_address.clear()
        self.change_addresses.clear()
        for addr in addresses:
            self.cbo_change_address.addItem(addr[0])
            self.change_addresses.append((addr[0], addr[1]))

    def set_input_amount(self, amount, inputs_count):
        self.inputs_count = inputs_count
        if amount != self.inputs_total_amount or inputs_count != self.inputs_count:
            # if there is only one recipient address and his current amount equals to the
            # previuus input_amount, assign new value to him

            last_total_amount = self.inputs_total_amount
            last_fee_amount = self.fee_amount
            self.inputs_total_amount = amount
            self.fee_amount = self.calculate_fee()

            if (len(self.recipients) == 1
                    or self.recipients[0].get_value(default_value=0.0) == 0.0
                    or self.recipients[0].get_value(default_value=0.0)
                    == round(last_total_amount - last_fee_amount, 8)):

                if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
                    amount_minus_fee = round(amount - self.fee_amount, 8)
                    if amount_minus_fee < 0:
                        amount_minus_fee = 0.0
                    self.recipients[0].set_value(amount_minus_fee)
                elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
                    self.recipients[0].set_value(100.0)

            old_state = self.edt_fee_value.blockSignals(True)
            self.edt_fee_value.setText(app_utils.to_string(self.fee_amount))
            self.edt_fee_value.blockSignals(old_state)

            for addr in self.recipients:
                addr.set_inputs_total_amount(amount - self.fee_amount)
                addr.clear_validation_results()

            self.edt_fee_value.update()
            self.update_change_amount()
            self.display_totals()

    def calculate_change_value(self) -> float:
        """Returns the change value in Dash."""
        sum = 0.0
        for addr in self.recipients:
            sum += addr.get_value(default_value=0.0)

        if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
            change_amount = round(
                self.inputs_total_amount - sum - self.fee_amount,
                8) + 0  # eliminate -0.0
        elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
            change_amount = round(
                (100.0 - sum) * (self.inputs_total_amount - self.fee_amount) /
                100, 8)
        else:
            raise Exception('Invalid unit')
        return change_amount

    def update_change_amount(self) -> None:
        change_amount = self.calculate_change_value()
        if self.inputs_total_amount - self.fee_amount != 0:
            change_pct = round(
                change_amount * 100 /
                (self.inputs_total_amount - self.fee_amount), 8) + 0
        else:
            change_pct = 0.0

        if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
            left_second_unit_str = app_utils.to_string(round(change_pct,
                                                             3)) + '%'
            self.edt_change_amount.setText(
                app_utils.to_string(round(change_amount, 8)))
        elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
            left_second_unit_str = app_utils.to_string(round(change_amount,
                                                             8)) + ' Dash'
            sum = 0.0
            for addr in self.recipients:
                sum += addr.get_value(default_value=0.0)
            self.edt_change_amount.setText(
                app_utils.to_string(round(100 - sum, 8)))
        else:
            raise Exception('Invalid unit')

        msg = ''
        if change_amount < 0:
            used_amount = round(self.inputs_total_amount - change_amount,
                                8) + 0
            msg = f'Not enough funds - used amount: ' \
                  f'{used_amount}, available: {self.inputs_total_amount}. Adjust ' \
                  f'the output values.'
        self.lbl_second_unit.setText(left_second_unit_str)
        self.display_message(msg, 'red')

    def validate_output_data(self) -> bool:
        ret = True
        for addr in self.recipients:
            if not addr.validate():
                ret = False
        if not ret:
            self.display_message(
                'Data of at least one recipient is invalid or empty. '
                'Please correct the data to continue.', 'red')
        else:
            self.display_message('')
        return ret

    def on_tm_calculate_change_value(self):
        self.tm_calculate_change_value.stop()
        for addr_item in self.recipients:
            addr_item.set_inputs_total_amount(self.inputs_total_amount -
                                              self.fee_amount)
        self.update_change_amount()

    def calculate_fee(self):
        if self.inputs_total_amount > 0.0:
            bytes = (self.inputs_count * 148) + (len(self.recipients) *
                                                 34) + 10
            fee = round(bytes * FEE_SAT_PER_BYTE, 8)
            if not fee:
                fee = MIN_TX_FEE
            fee = round(fee / 1e8, 8)
        else:
            fee = 0.0
        return fee

    def get_tx_fee(self):
        if self.fee_amount < 0.0:
            raise Exception('Invalid the fee value.')
        return round(self.fee_amount * 1e8)

    def init_calculate_change_value(self):
        self.tm_calculate_change_value.start(100)

    def set_default_fee(self):
        self.fee_amount = self.calculate_fee()

        old_status = self.edt_fee_value.blockSignals(True)
        try:
            self.edt_fee_value.setText(app_utils.to_string(self.fee_amount))
        finally:
            self.edt_fee_value.blockSignals(old_status)
        self.edt_fee_value.update()
        self.init_calculate_change_value()

    def on_btn_get_default_fee_clicked(self):
        self.set_default_fee()

    def set_dest_addresses(self, addresses: List):
        if len(addresses) > 0:
            count_diff = len(addresses) - len(self.recipients)
            if count_diff > 0:
                self.add_dest_address(count_diff)
            elif count_diff < 0:
                # remove unecessary rows, beginning from the largest one
                for nr in reversed(range(len(addresses),
                                         len(self.recipients))):
                    self.remove_dest_address(self.recipients[nr])
            for idx, addr_item in enumerate(self.recipients):
                if isinstance(addresses[idx], (list, tuple)):
                    # passed address-value tuple
                    if len(addresses[idx]) >= 1:
                        addr_item.set_address(addresses[idx][0])
                    if len(addresses[idx]) >= 2:
                        addr_item.set_value(addresses[idx][1])
                else:
                    addr_item.set_address(addresses[idx])
            self.display_totals()

    def set_change_value_label(self):
        if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
            self.lbl_change_amount.setText('value')
            self.lbl_change_amount.setToolTip(
                'Unused amount - will be sent back to the change address')
        else:
            self.lbl_change_amount.setText('pct. value')
            self.lbl_change_amount.setToolTip(
                'Unused amount (as percent of the total value of all inputs) - will '
                'be sent back to the change address')

    def on_cbo_output_unit_change(self, index):
        if index == 0:
            self.values_unit = OUTPUT_VALUE_UNIT_AMOUNT
        else:
            self.values_unit = OUTPUT_VALUE_UNIT_PERCENT

        self.set_change_value_label()
        for addr_item in self.recipients:
            addr_item.set_output_value_unit(self.values_unit)
        self.update_change_amount()

    def update_ui_value_unit(self):
        if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
            self.cbo_output_unit.setCurrentIndex(0)
        else:
            self.cbo_output_unit.setCurrentIndex(1)

    def simplyfy_file_home_dir(self, file_name):
        home_dir = os.path.expanduser('~')
        if self.current_file_name.find(home_dir) == 0:
            file_name = '~' + self.current_file_name[len(home_dir):]
        else:
            file_name = self.current_file_name
        return file_name

    def display_totals(self):
        bytes = (self.inputs_count * 148) + (len(self.recipients) * 34) + 10
        text = f'<span class="label"><b>Total value of selected inputs:</b>&nbsp;</span><span class="value">&nbsp;{self.inputs_total_amount} Dash&nbsp;</span>'
        if self.inputs_total_amount > 0:
            text += f'<span class="label">&nbsp;<b>Inputs:</b>&nbsp;</span><span class="value">&nbsp;{self.inputs_count}&nbsp;</span>' \
                    f'<span class="label">&nbsp;<b>Outputs:</b>&nbsp;</span><span class="value">&nbsp;{len(self.recipients)}&nbsp;</span>' \
                    f'<span class="label">&nbsp;<b>Transaction size:</b>&nbsp;</span><span class="value">&nbsp;{bytes} B&nbsp;</span>'
        self.lbl_totals.setText(text)

        if self.current_file_name:
            file_name = self.simplyfy_file_home_dir(self.current_file_name)
            text = f'<span class="label"><b>File:</b>&nbsp;</span><span class="value">{file_name}&nbsp;</span>'
            self.lbl_data_file_name.setText(text)
            self.lbl_data_file_name.setVisible(True)
            self.lbl_data_file_badge.setVisible(True)

            if self.current_file_encrypted:
                self.lbl_data_file_badge.setText('Encrypted')
                self.lbl_data_file_badge.setStyleSheet(
                    "QLabel{background-color:#2eb82e;color:white; padding: 1px 3px 1px 3px; border-radius: 3px;}"
                )
            else:
                self.lbl_data_file_badge.setText('Not encrypted')
                self.lbl_data_file_badge.setStyleSheet(
                    "QLabel{background-color:orange;color:white; padding: 1px 3px 1px 3px; border-radius: 3px;}"
                )
        else:
            self.lbl_data_file_name.setVisible(False)
            self.lbl_data_file_badge.setVisible(False)

    def clear_outputs(self):
        if WndUtils.queryDlg("Do you really want to clear all outputs?",
                             default_button=QMessageBox.Cancel,
                             icon=QMessageBox.Warning) == QMessageBox.Ok:
            self.set_dest_addresses([('', '')])
            self.use_all_funds_for_address(self.recipients[0])

    def save_to_file(self, save_encrypted):
        if self.current_file_name and os.path.exists(
                os.path.dirname(self.current_file_name)):
            dir = os.path.dirname(self.current_file_name)
        else:
            dir = self.app_config.data_dir

        if save_encrypted:
            initial_filter = "DAT files (*.dat)"
        else:
            initial_filter = "CSV files (*.csv)"

        file_filter = f"{initial_filter};;All Files (*)"

        file_name = WndUtils.save_file_query(
            self.parent_dialog,
            message='Enter the file name to save the data.',
            directory=dir,
            filter=file_filter,
            initial_filter=initial_filter)

        if file_name:
            data = bytes()
            data += b'RECIPIENT_ADDRESS\tVALUE\n'
            if self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
                suffix = '%'
            else:
                suffix = ''

            for addr in self.recipients:
                line = f'{addr.get_address()}{CSV_SEPARATOR}{str(addr.get_value(default_value=""))}{suffix}\n'
                data += line.encode('utf-8')

            if save_encrypted:
                write_file_encrypted(file_name, self.hw_session, data)
            else:
                with open(file_name, 'wb') as f_ptr:
                    f_ptr.write(data)

            self.current_file_name = file_name
            self.current_file_encrypted = save_encrypted
            self.add_menu_item_to_mru(self.current_file_name)
            self.update_mru_menu_items()
            self.display_totals()

    def on_read_from_file_clicked(self):
        try:
            if self.current_file_name and os.path.exists(
                    os.path.dirname(self.current_file_name)):
                dir = os.path.dirname(self.current_file_name)
            else:
                dir = self.app_config.data_dir

            initial_filter1 = "DAT files (*.dat)"
            initial_filter2 = "CSV files (*.csv)"

            file_filter = f"{initial_filter1};;{initial_filter2};;All Files (*.*)"

            file_name = WndUtils.open_file_query(
                self.parent_dialog,
                message='Enter the file name to read the data.',
                directory=dir,
                filter=file_filter,
                initial_filter='All Files (*.*)')

            if file_name:
                self.read_from_file(file_name)
        except Exception as e:
            self.parent_dialog.errorMsg(str(e))

    def read_from_file(self, file_name):
        try:
            file_info = {}
            data_decrypted = bytearray()
            for block in read_file_encrypted(file_name, file_info,
                                             self.hw_session):
                data_decrypted.extend(block)

            file_encrypted = file_info.get('encrypted', False)
            data = data_decrypted.decode('utf-8')

            addresses = []
            value_unit = None
            for line_idx, line in enumerate(data.split('\n')):
                if line:
                    elems = line.split('\t')
                    if len(elems) < 2:
                        elems = line.split(';')

                    if len(elems) < 2:
                        raise ValueError(
                            f'Invalid data file entry for line: {line_idx+1}.')

                    address = elems[0].strip()
                    value = elems[1].strip()

                    address_valid = dash_utils.validate_address(
                        address, dash_network=None)
                    if not address_valid:
                        if line_idx == 0 and re.match(r'^[A-Za-z_]+$',
                                                      address):
                            continue  # header line
                        else:
                            raise ValueError(
                                f'Invalid recipient address ({address}) (line {line_idx+1}).'
                            )

                    if value.endswith('%'):
                        vu = OUTPUT_VALUE_UNIT_PERCENT
                        value = value[:-1]
                    else:
                        vu = OUTPUT_VALUE_UNIT_AMOUNT
                    if value_unit is None:
                        value_unit = vu
                    elif value_unit != vu:
                        raise ValueError(
                            f'The value unit in line {line_idx+1} differs from the previous '
                            f'line.')

                    try:
                        if value:
                            value = float(value.replace(',', '.'))
                        else:
                            value = None
                        addresses.append((address, value))
                    except Exception as e:
                        raise ValueError(
                            f'Invalid data in the \'value\' field (line {line_idx+1}).'
                        )

            if len(addresses) == 0:
                raise Exception('File doesn\'t contain any recipient\'s data.')
            else:
                if self.values_unit != value_unit:
                    self.values_unit = value_unit
                    self.update_ui_value_unit()
                self.set_dest_addresses(addresses)
                self.current_file_name = file_name
                self.current_file_encrypted = file_encrypted
                self.add_menu_item_to_mru(self.current_file_name)
                self.update_mru_menu_items()
                self.update_change_amount()
                self.display_totals()
        except Exception as e:
            self.update_mru_menu_items()
            logging.exception(
                'Exception while reading file with recipients data.')
            self.parent_dialog.errorMsg(str(e))

    def add_menu_item_to_mru(self, file_name: str) -> None:
        if file_name:
            try:
                if file_name in self.recent_data_files:
                    idx = self.recent_data_files.index(file_name)
                    del self.recent_data_files[idx]
                    self.recent_data_files.insert(0, file_name)
                else:
                    self.recent_data_files.insert(0, file_name)
                app_cache.set_value(CACHE_ITEM_DATA_FILE_MRU_LIST,
                                    self.recent_data_files)
            except Exception as e:
                logging.warning(str(e))

    def update_mru_menu_items(self):
        app_utils.update_mru_menu_items(self.recent_data_files,
                                        self.mnu_recent_files,
                                        self.on_data_file_mru_action_triggered,
                                        self.current_file_name,
                                        self.on_act_clear_mru_items)

    def on_act_clear_mru_items(self):
        self.recent_data_files.clear()
        app_cache.set_value(CACHE_ITEM_DATA_FILE_MRU_LIST,
                            self.recent_data_files)
        self.update_mru_menu_items()

    def on_data_file_mru_action_triggered(self, file_name: str) -> None:
        """ Triggered by clicking one of the subitems of the 'Open Recent' menu item. Each subitem is
        related to one of recently openend data files.
        :param file_name: A data file name accociated with the menu action clicked.
        """
        self.read_from_file(file_name)

    def get_tx_destination_data(self) -> List[Tuple[str, int, str]]:
        """
        :return: Tuple structure:
            [0]: dest address
            [1]: value in satoshis/duffs
            [2]: bip32 path of the address if the item is a change address, otherwise None
        """
        if self.validate_output_data():
            change_amount = self.calculate_change_value()
            if change_amount < 0.0:
                self.update_change_amount(
                )  # here an appropriate message will be displayed
                raise Exception('Not enough funds!!!')

            dest_data = []
            for addr in self.recipients:
                dest_addr = addr.get_address()
                value = round(addr.get_value_amount() * 1e8)
                dest_data.append((dest_addr, value, None))

            if change_amount > 0.0:
                change_address_idx = self.cbo_change_address.currentIndex()
                if change_address_idx >= 0 and change_address_idx < len(
                        self.change_addresses):
                    dest_data.append(
                        (self.change_addresses[change_address_idx][0],
                         round(change_amount * 1e8),
                         self.change_addresses[change_address_idx][1]))
                else:
                    raise Exception('Invalid address for the change.')
            return dest_data
        else:
            return []

    def get_recipients_list(self) -> List[Tuple[str, ]]:
        """
        :return: List of recipient addresses
                 List[Tuple[str <address>, float <value>]
        """
        dest_data = []
        for addr in self.recipients:
            dest_addr = addr.get_address()
            if dest_addr:
                dest_data.append((dest_addr, ))
        return dest_data
Esempio n. 18
0
class QOutputConfig(QWidget):
    contentsModified = pyqtSignal()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.trackTable = OutputTrackList(self)
        self.trackTable.contentsModified.connect(self.isModified)
        layout.addWidget(self.trackTable)

        self.titleLabel = QLabel("Title:", self)
        self.titleEdit = QLineEdit(self)
        self.titleEdit.textChanged.connect(self.setOutputTitle)

        sublayout = QHBoxLayout()
        sublayout.addWidget(self.titleLabel)
        sublayout.addWidget(self.titleEdit)
        layout.addLayout(sublayout)

        self.fileLabel = QLabel("File name:", self)
        self.fileEdit = QLineEdit(self)
        self.fileEdit.textChanged.connect(self.setOutputPath)

        self.browseBtn = QPushButton(self)
        self.browseBtn.clicked.connect(self.execBrowseDlg)
        self.browseBtn.setIcon(QIcon.fromTheme("document-open"))

        sublayout = QHBoxLayout()
        sublayout.addWidget(self.fileLabel)
        sublayout.addWidget(self.fileEdit)
        sublayout.addWidget(self.browseBtn)
        layout.addLayout(sublayout)

        sublayout = QHBoxLayout()
        self.targetSizeCheckBox = QCheckBoxMatchHeight(
            "&Target File Size (Overrides settings in video encoder)", self)

        self.targetSizeSpinBox = QDoubleSpinBox(self)
        self.targetSizeSpinBox.setMinimum(1)
        self.targetSizeSpinBox.setDecimals(3)
        self.targetSizeSpinBox.setMaximum(65536)
        self.targetSizeSpinBox.setSuffix(" MB")

        self.targetSizeCheckBox.setHeightMatch(self.targetSizeSpinBox)

        self.targetSizeCheckBox.stateChanged.connect(self.setTargetSizeMode)
        self.targetSizeSpinBox.valueChanged.connect(self.setTargetSize)

        sublayout.addWidget(self.targetSizeCheckBox)
        sublayout.addWidget(self.targetSizeSpinBox)
        sublayout.addStretch()
        layout.addLayout(sublayout)

        self.settingsBtn = QPushButton("Con&figure Container...", self)
        self.settingsBtn.setIcon(QIcon.fromTheme("preferences-other"))
        self.settingsBtn.clicked.connect(self.configureContainer)

        self.btnlayout = QHBoxLayout()
        self.btnlayout.addStretch()
        self.btnlayout.addWidget(self.settingsBtn)
        layout.addLayout(self.btnlayout)

        self.setOutputFile(None)

    def setOutputTitle(self, title):
        self.output_file.title = title
        self.isModified()

    def setOutputPath(self, path):
        self.output_file.outputpathrel = path
        self.isModified()

    def setTargetSize(self, value):
        if self.targetSizeCheckBox.checkState():
            self.output_file.targetsize = value * 1024**2
            self.isModified()

    def setTargetSizeMode(self, flag):
        self.targetSizeSpinBox.setVisible(flag)

        if flag:
            self.output_file.targetsize = (self.targetSizeSpinBox.value() *
                                           1024**2)

        else:
            self.output_file.targetsize = None

        self.isModified()

    def execBrowseDlg(self):
        exts = ' '.join(f'*{ext}' for ext in self.output_file.extensions)
        filters = (f"{self.output_file.fmtname} Files ({exts})")

        if self.output_file.config and self.output_file.config.workingdir:
            fileName = os.path.join(self.output_file.config.workingdir,
                                    self.fileEdit.text())

        else:
            fileName = self.fileEdit.text()

        fileName, _ = QFileDialog.getSaveFileName(self, "Save File", fileName,
                                                  filters)

        if fileName:
            if (self.output_file.config
                    and self.output_file.config.workingdir):
                fileName = os.path.join(self.output_file.config.workingdir,
                                        fileName)

                if not os.path.relpath(
                        fileName,
                        self.output_file.config.workingdir).startswith("../"):
                    fileName = os.path.relpath(
                        fileName, self.output_file.config.workingdir)

            self.fileEdit.setText(fileName)
            self.isModified()
            return True

        return False

    def isModified(self):
        self._modified = True
        self.contentsModified.emit()

    def _resetMinimumSize(self):
        self.targetSizeSpinBox.setMinimum(self.output_file.minimumSize() /
                                          1024**2)

    def notModified(self):
        self._modified = False

    def modified(self):
        return self._modified

    def updateOutputPath(self):
        self.fileEdit.blockSignals(True)

        if (isinstance(self.output_file, BaseWriter)
                and self.output_file.outputpathrel):
            self.fileEdit.setText(self.output_file.outputpathrel or "")

        else:
            self.fileEdit.setText("")

        self.fileEdit.blockSignals(False)

    def setOutputFile(self, output_file=None):
        self.notModified()
        self.output_file = output_file

        if output_file is not None:
            self.trackTable.setOutputFile(output_file)
            self.trackTable.contentsModified.connect(self._resetMinimumSize)

            self.titleEdit.blockSignals(True)
            self.titleEdit.setText(output_file.title or "")
            self.titleEdit.blockSignals(False)

            self.updateOutputPath()

            self.targetSizeCheckBox.blockSignals(True)
            self.targetSizeCheckBox.setTristate(False)
            self.targetSizeCheckBox.setCheckState(
                2 if output_file.targetsize is not None else 0)
            self.targetSizeCheckBox.blockSignals(False)

            self.targetSizeSpinBox.setHidden(output_file.targetsize is None)

            if output_file.targetsize:
                output_file.loadOverhead()
                self.targetSizeSpinBox.blockSignals(True)
                self._resetMinimumSize()
                self.targetSizeSpinBox.setValue(output_file.targetsize /
                                                1024**2)
                self.targetSizeSpinBox.blockSignals(False)

            self.settingsBtn.setEnabled(output_file.QtDlgClass() is not None)
            self.settingsBtn.setText(f"{output_file.fmtname} Options...")

        else:
            self.titleEdit.blockSignals(True)
            self.titleEdit.setText("")
            self.titleEdit.blockSignals(False)

            self.fileEdit.blockSignals(True)
            self.fileEdit.setText("")
            self.fileEdit.blockSignals(False)

            self.trackTable.setOutputFile(None)

            self.settingsBtn.setEnabled(False)
            self.settingsBtn.setText("Options...")

            self.targetSizeCheckBox.blockSignals(True)
            self.targetSizeCheckBox.setTristate(True)
            self.targetSizeCheckBox.setCheckState(1)
            self.targetSizeCheckBox.blockSignals(False)
            self.targetSizeSpinBox.setHidden(True)

        self.setEnabled(output_file is not None)

    def configureContainer(self):
        dlg = self.output_file.QtDlg(self)

        if dlg is not None:
            dlg.settingsApplied.connect(self.contentsModified)
            dlg.exec_()
            self._resetMinimumSize()
class RequestNavigation(PWidget):
    alert = pyqtSignal(str, str, object, object)
    currentChange = pyqtSignal()

    def __init__(self):
        super().__init__()
        # self.setMaximumWidth(600)
        self.setObjectName(Parapluie.Object_Raised_Off)

        self.backButton = QToolButton()
        self.backButton.setIcon(
            PResource.defaultIcon(Parapluie.Icon_Left_Arrow_Svg))
        self.backButton.setFixedSize(36, 36)
        self.backButton.pressed.connect(self.onBackPressed)

        self.searchBar = QLineEdit()
        self.searchBar.setPlaceholderText("Search..")
        self.searchBar.textChanged.connect(self.searchFile)
        self.searchBar.setFixedHeight(36)

        self.addButton = QToolButton()
        self.addButton.setIcon(PResource.defaultIcon(Parapluie.Icon_Plus_Svg))
        self.addButton.setFixedSize(36, 36)
        self.addButton.pressed.connect(self.newFile)

        self.openButton = QToolButton()
        self.openButton.setIcon(
            PResource.defaultIcon(Parapluie.Icon_Folder_Svg))
        self.openButton.setFixedSize(36, 36)
        self.openButton.pressed.connect(self.openFile)

        topBar = QHBoxLayout()
        topBar.addWidget(self.backButton)
        topBar.addWidget(self.searchBar)
        topBar.addWidget(self.openButton)
        topBar.addWidget(self.addButton)

        self.listDataWidget = QListWidget()
        self.listDataWidget.setContentsMargins(0, 0, 0, 0)
        # self.listDataWidget.setObjectName(Parapluie.Object_Raised)
        self.listDataWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.listDataWidget.setSizePolicy(QSizePolicy.Expanding,
                                          QSizePolicy.Expanding)
        self.listDataWidget.horizontalScrollBar().setEnabled(False)
        self.listDataWidget.setSpacing(3)

        self.gridData = PGridWidget(2)
        # self.gridData.setObjectName(Parapluie.Object_Raised)
        self.gridData.setContentsMargins(0, 0, 0, 0)
        self.gridData.setHorizontalSpacing(5)
        self.gridData.setVerticalSpacing(5)
        self.gridData.setFixColumn(True)
        self.gridData.setRowHeight(100)
        self.gridData.setAlignment(Qt.AlignTop | Qt.AlignLeft)

        self.stacked = QStackedWidget()
        self.stacked.addWidget(self.gridData)
        self.stacked.addWidget(self.listDataWidget)
        self.stacked.currentChanged.connect(self.tabChange)

        layout = QVBoxLayout()
        layout.addLayout(topBar)
        layout.addWidget(self.stacked)
        layout.setContentsMargins(8, 8, 0, 8)

        self.setLayout(layout)

        self.categoriesChange = None

        self.dataList = []  # combine
        self.categories = []  # string
        self.dataSource = XashList()
        self.dataTemp = []  # xfile
        self.listFile = []  # xfile

        self.currentFile = None
        self.newCount = -1
        self.lastSearch = {1: "", 0: ""}

        self.listAdapter = ItemAdapter(self, self.listDataWidget,
                                       self.dataList, self.dataSource,
                                       self.onItemSelected, self.onItemClosed)

        gridAdapter = GridAdapter(data=self.categories)
        gridAdapter.setOnItemClick(self.onCategoriesClicked)
        self.gridData.setAdapter(gridAdapter)

        self.stacked.setCurrentIndex(1)
        self.tabChange(1)
        self.loadFile()

    def loadFile(self):
        self.dataSource.clear()
        self.dataSource.load(Config.getRequestFolder()[0])
        self.updateCategories(self.dataSource.list)
        self.updateDataList(self.dataTemp, self.dataSource.list)
        self.listAdapter.refresh()
        self.gridData.refresh()

    def updateCategories(self, lstSrc):
        self.categories.clear()
        self.categories.append("all")
        self.categories.append('recent')
        for data in lstSrc:
            c = data.categories.category if data.categories is not None else ""
            if c != "" and c not in self.categories:
                self.categories.append(c)
        if self.categoriesChange is not None:
            self.categoriesChange(self.categories)

    def searchFile(self, a0: str):
        if self.stacked.currentIndex() == 1:
            self.lastSearch[1] = a0
            if a0 == "" or a0 == "categories:all":
                self.updateDataList(self.dataTemp, self.dataSource.list)
            elif a0 == 'categories:recent':
                self.updateDataList(self.dataTemp, [])
            else:
                if a0.startswith("categories:"):
                    self.updateDataList([],
                                        self.dataSource.findWithCategory(
                                            a0.replace("categories:", "")))
                elif a0.startswith("name:"):
                    self.updateDataList([],
                                        self.dataSource.findWithName(
                                            a0.replace("name:", "")))
                else:
                    self.updateDataList([],
                                        self.dataSource.findEveryWhere(
                                            "", a0, "description#" + a0))
            self.listAdapter.refresh()
        else:
            self.lastSearch[0] = a0
            self.categories.clear()
            if a0 == "":
                self.categories.append("all")
                self.categories.append('recent')
                for data in self.dataSource.list:
                    d: Xash = data
                    c = d.categories.category if d.categories is not None else ""
                    if c != "" and c not in self.categories:
                        self.categories.append(c)
            else:
                for data in self.dataSource.list:
                    d: Xash = data
                    c = d.categories.category if d.categories is not None else ""
                    if c != "" and c not in self.categories and a0 in c:
                        self.categories.append(c)
            self.gridData.refresh()

    def tabChange(self, index):
        if index == 1:
            self.backButton.setVisible(True)
            self.searchBar.setText(self.lastSearch[1])
            self.searchBar.blockSignals(False)
        else:
            self.backButton.setVisible(False)
            self.searchBar.setText(self.lastSearch[0])

    def onCategoriesClicked(self, txt):
        self.stacked.setCurrentIndex(1)
        self.searchBar.setText("categories:" + txt)

    def onBackPressed(self):
        self.stacked.setCurrentIndex(0)

    def resizeEvent(self, a0: QtGui.QResizeEvent):
        super(RequestNavigation, self).resizeEvent(a0)
        col = int(self.width() / 150) + 1
        self.gridData.setNumberColumn(col)

    def refresh(self):
        self.listAdapter.refresh()
        self.gridData.refresh()

    def newFile(self):
        self.newCount += 1
        file = XFile("Unsaved/Untitled-" + str(self.newCount))
        self.dataTemp.insert(0, file)
        self.onItemSelected(file)
        self.updateDataList(self.dataTemp, self.dataSource.list)
        self.refresh()

    def pushAlert(self, text, tpe=Parapluie.Alert_Error):
        self.alert.emit(text, tpe, None, None)

    def openFile(self):
        init_dir = Config.getRequestConfig_LastOpen()

        name = QFileDialog.getOpenFileName(
            self, 'Open file', init_dir,
            "XuCompa Request (*.xreq);; JSON files (*.json);; All files (*)")
        if name[0] != "":
            config = Config.getConfig()
            config["viewer"]["last_open"] = os.path.dirname(name[0])
            Config.updateConfig(config)

            file = XFile(name[0])
            self.dataTemp.insert(0, file)
            self.onItemSelected(file)
            self.updateDataList(self.dataTemp, self.dataSource.list)
            self.refresh()

    def updateDataList(self, lstNew, lstSrc):
        self.dataList.clear()
        if len(lstNew):
            # header item
            item = ItemModel()
            item.file = "Recent File"
            item.selected = -1
            self.dataList.append(item)
            for data in lstNew:  # XFile
                item = ItemModel()
                item.file = data
                item.selected = 1 if self.currentFile == data else 0
                self.dataList.append(item)

        if len(lstSrc) > 0:
            # header item
            item = ItemModel()
            item.file = "Request Folder"
            item.selected = -1
            self.dataList.append(item)
            for data in lstSrc:  # Xash
                item = ItemModel()
                if data.getPath() in self.listFile:
                    inx = self.listFile.index(data.getPath())
                    file = self.listFile[inx]
                else:
                    file = XFile(data.getPath())
                    self.listFile.append(file)
                item.file = file
                item.selected = 1 if self.currentFile == file else 0
                self.dataList.append(item)

    def onItemSelected(self, data):
        if isinstance(data, XFile):

            self.currentFile = data
            self.currentChange.emit()

            for item in self.dataList:
                if item.file == data:
                    item.selected = 1
                else:
                    item.selected = 0
                self.refresh()

    def onItemClosed(self, data):
        if self.currentFile == data:
            self.currentFile = None
            self.currentChange.emit()

        if isinstance(data, XFile):
            if inRequestFolder(data.getPath()):
                message = PMessage(
                    self,
                    QRect(QPoint(0, 0),
                          QApplication.focusWindow().screen().size()))
                Utilities.Style.applyWindowIcon(message)
                message.initWarning("Are you want delete this item!",
                                    negative="Delete")
                code = message.exec_()
                if code == 1:
                    # remove description
                    xash = XashHelp.path2Xash(data.getPath())
                    xashId = xash.getId()
                    self.saveDescription("", xashId, True)

                    # remove file
                    os.remove(data.getPath())

        if data in self.dataList:
            self.dataList.remove(data)
        if data in self.dataTemp:
            self.dataTemp.remove(data)
        self.loadFile()

    def saveData(self, file: XFile, newData):
        if file.unsavedData is not None:  # has data unsaved
            if inRequestFolder(file.getPath()):
                self.saveExistedInRequest(file, newData)
            else:
                self.saveNewFile(file, newData)

    def getSavePath(self, name, category, description) -> str:
        # create new xashID
        xashId = XashHelp.newXashId(category, self.dataSource)
        # write description
        descId = self.saveDescription(description, xashId)
        # gotta new xash string
        x = XashHelp.createXash(name, xashId, {'description': descId}, '.xreq')
        # join with folder
        path = os.path.join(Config.getRequestFolder()[0], x)
        return path

    def saveDescription(self, desc, descId=None, isRemove=False):
        # read descriptions
        xDef = Config.getRequestFolder()[1]
        if os.path.isfile(xDef):
            file = open(xDef, "r", encoding='utf-8')
            data = file.read()
            file.close()
        else:
            data = ''

        # edit
        listDesc = data.split("\n")
        if descId is None and not isRemove:
            newId = '[desc#%s]' % str(len(listDesc))
            lines = newId + 'xreq_description--description#' + desc
            listDesc.append(lines)
        else:
            newId = descId
            exist = False
            for d in listDesc:
                if d.startswith(newId):
                    inx = listDesc.index(d)
                    if not isRemove:
                        listDesc[
                            inx] = newId + 'xreq_description--description#' + desc
                        exist = True
                    else:
                        listDesc.remove(d)
                    break
            if not exist:
                lines = newId + 'xreq_description--description#' + desc
                listDesc.append(lines)

        # save
        file = open(xDef, "w", encoding='utf-8')
        data = "\n".join(listDesc)
        file.write(data)
        file.close()
        return newId

    def saveNewFile(self, file: XFile, newData):
        temp = self.dataSource.findMatchAll(newData[1],
                                            newData[0],
                                            "",
                                            sensitive=False)
        if len(temp) > 0:
            self.pushAlert("File was existed!!!")
        else:
            # data
            text = Formatter.dumps(file.unsavedData.data, EditorType.JSON,
                                   self.pushAlert)
            path = self.getSavePath(newData[0], newData[1], newData[2])
            if self.save(file, path, text):
                if file in self.dataTemp:
                    self.dataTemp.remove(file)
                self.loadFile()

    def saveExistedInRequest(self, file: XFile, newData: tuple):
        # data
        text = Formatter.dumps(file.unsavedData.data, EditorType.JSON,
                               self.pushAlert)
        xash = XashHelp.path2Xash(file.getPath())

        if xash.getCategory() == newData[1] and xash.getName() == newData[0]:
            # replace existed description
            xashId = xash.getId()
            self.saveDescription(newData[2], xashId)

            if self.save(file, file.getPath(), text):
                self.loadFile()
        else:
            temp = self.dataSource.findMatchAll(newData[1],
                                                newData[0],
                                                "",
                                                sensitive=False)
            if len(temp) > 0:
                self.pushAlert("File was existed!!!")
            else:
                message = PMessage(
                    self,
                    QRect(QPoint(0, 0),
                          QApplication.focusWindow().screen().size()))
                message.initQuestion(
                    "File is saved, do you want to rename or create new file?",
                    [{
                        "text": "Rename",
                        'type': Parapluie.Button_Negative
                    }, {
                        "text": "New File",
                        'type': Parapluie.Button_Positive
                    }, {
                        "text": "Close",
                        'type': Parapluie.Button_Neutral
                    }], 'Save request')
                code = message.exec_()
                if code == 0:
                    # remove old description
                    xashId = xash.getId()
                    self.saveDescription(newData[2], xashId, True)

                    path = self.getSavePath(newData[0], newData[1], newData[2])

                    os.rename(file.getPath(), path)

                    if self.save(file, path, text):
                        self.loadFile()
                elif code == 1:
                    path = self.getSavePath(newData[0], newData[1], newData[2])
                    if self.save(file, path, text):
                        self.loadFile()

    def save(self, file: XFile, path, text) -> bool:
        try:
            f = open(path, 'w', encoding='utf-8')
            f.write(text)
            f.close()

            file.unsavedData = None
            file.setPath(path)
            file.data = XashHelp.path2Xash(path)

            self.pushAlert("Saved!!!", Parapluie.Alert_Success)
            return True
        except Exception as ex:
            logging.exception(ex)
            self.pushAlert(str(ex))
            return False
Esempio n. 20
0
class double_thresold(QWidget):
    from_slider_min = pyqtSignal(str)
    from_slider_max = pyqtSignal(str)
    from_line_min = pyqtSignal(int)
    from_line_max = pyqtSignal(int)
    from_label_min = pyqtSignal(int)
    from_label_max = pyqtSignal(int)

    def __init__(self, img_path='1023.png'):
        super().__init__()
        self.minValue = 100
        self.maxValue = 200
        self.press = 0
        self.now = 0
        self.img_path = img_path

        self.init_ui()
        # self.histpath(self.img_path)

    # 初始化
    def init_ui(self):
        # 设置窗口大小,标题
        self.setFixedSize(750, 300)
        self.setWindowModality(Qt.ApplicationModal)
        self.move(300, 300)
        self.setWindowTitle("Double Threshold")
        # 定义框架,主框架下有左右两个盒式框架,左侧框架中是堆叠框架,实现控件的堆叠,右侧框架(纵向排列)中有两个横向排列盒式框架,存放标签、文本框和滑块
        self.main_box = QHBoxLayout()
        self.left1 = QVBoxLayout()
        self.stack = QStackedLayout()
        self.right1 = QVBoxLayout()
        self.right1_1 = QHBoxLayout()
        self.right1_2 = QHBoxLayout()

        # 定义控件,F为嵌入的matplotlib图像
        try:
            self.F = MyFigure()
            # self.F.hist(self.img_path)
            self.F.setMinimumWidth(500)
            self.F.setMaximumWidth(500)
            self.right1.setSpacing(10)
            self.right1_1.setSpacing(10)
            self.right1_2.setSpacing(10)
            self.label1_1 = QLabel("Min Val:")
            self.lineEdit1_1 = QLineEdit("100")
            self.lineEdit1_1.setMaximumWidth(70)
            self.slider1_1 = QSlider(Qt.Horizontal)
            self.slider1_1.setFocusPolicy(Qt.NoFocus)
            self.slider1_1.setRange(0, 255)
            self.slider1_1.setValue(100)
            self.label1_2 = QLabel("Max Val:")
            self.lineEdit1_2 = QLineEdit("200")
            self.lineEdit1_2.setMaximumWidth(70)
            self.slider1_2 = QSlider(Qt.Horizontal)
            self.slider1_2.setFocusPolicy(Qt.NoFocus)
            self.slider1_2.setRange(0, 255)
            self.slider1_2.setValue(200)
            self.button1_3 = QPushButton("Apply to Image")
            # 用label和F的堆叠实现用蓝色色块标注当前选中区域的功能
            self.mask = QLabel()
            # 堆叠框架只能间接添加框架
            self.mask_layout_widget = QWidget()
            self.mask_layout = QHBoxLayout()
            self.mask_layout.addWidget(self.mask)
            self.mask_layout_widget.setLayout(self.mask_layout)
            # 绑定控件的事件

            self.slider1_1.valueChanged.connect(self.change_sld_min_value)
            self.slider1_2.valueChanged.connect(self.change_sld_max_value)
            self.lineEdit1_1.editingFinished.connect(
                self.change_line_min_value_finished)
            self.lineEdit1_2.editingFinished.connect(
                self.change_line_max_value_finished)
            self.mask.mousePressEvent = self.label_mouse_press_event
            self.mask.mouseMoveEvent = self.label_mouse_move_event
        except Exception as err:
            print("set widgets:{0}".format(err))
        # 把控件添加到框架中
        try:
            self.left1.addLayout(self.stack)
            self.stack.addWidget(self.F)
            self.stack.addWidget(self.mask_layout_widget)
            self.right1_1.addWidget(self.label1_1)
            self.right1_1.addWidget(self.lineEdit1_1)
            self.right1_1.addWidget(self.slider1_1)
            self.right1_2.addWidget(self.label1_2)
            self.right1_2.addWidget(self.lineEdit1_2)
            self.right1_2.addWidget(self.slider1_2)
            self.right1.addLayout(self.right1_1)
            self.right1.addLayout(self.right1_2)
            self.right1.addWidget(self.button1_3)
        except Exception as err:
            print(err)
        # 对框架进行设置,并把框架添加到主框架中
        try:
            self.stack.setStackingMode(1)
            self.main_box.addLayout(self.left1)
            self.main_box.addLayout(self.right1)
            self.setLayout(self.main_box)
        except Exception as err:
            print(err)
        # 设置色块label覆盖区域的初始数据,如边距和颜色
        try:
            self.mask_left_margin = 78
            self.mask_up_margin = 33
            self.mask_right_margin = 65
            self.mask_bottom_margin = 30
            self.mask_range = self.F.width(
            ) - self.mask_left_margin - self.mask_right_margin
            self.mask.setStyleSheet(
                "QLabel{background-color: rgb(0,0,255,50);}")
            self.mask_layout.setContentsMargins(self.eva_start(),
                                                self.mask_up_margin,
                                                self.eva_end(),
                                                self.mask_bottom_margin)
        except Exception as err:
            print("mask Error:{0}".format(err))
        # 为设置的信号绑定函数
        try:
            self.from_slider_min.connect(self.change_line_min_value)
            self.from_slider_min.connect(self.change_label_min)
            self.from_slider_max.connect(self.change_line_max_value)
            self.from_slider_max.connect(self.change_label_max)
            self.from_line_min.connect(self.change_sld_min_value)
            self.from_line_min.connect(self.change_label_min)
            self.from_line_max.connect(self.change_sld_max_value)
            self.from_line_max.connect(self.change_label_max)
            self.from_label_max.connect(self.change_line_max_value)
            self.from_label_max.connect(self.change_sld_max_value)
            self.from_label_min.connect(self.change_line_min_value)
            self.from_label_min.connect(self.change_sld_min_value)

        except Exception as err:
            print("Signal Error:{0}".format(err))

    def histpath(self, path):
        self.img_path = path
        self.F.hist(self.img_path)

    # 分解拖动 = 按下 + 移动
    # 按下事件,记录按下时的位置
    def label_mouse_press_event(self, event):
        if event.buttons() == Qt.LeftButton:
            self.press = event.pos().x()

    # 移动事件,仅在左键被按下时进行处理,获取当前的位置,计算和初始位置的差值,把差值传递到移动label的函数
    def label_mouse_move_event(self, event):
        if event.buttons() == Qt.LeftButton:
            self.now = event.pos().x()
            self.differ = self.now - self.press
            self.move_mask(self.differ)

    # 判定文本是否符合要求
    def is_available_int(self, text):
        try:
            if int(text) >= 0 and int(text) <= 255:
                return True
            return False
        except:
            print("except")
            return False

    # 计算label色块的左边界
    def eva_start(self):
        return int(round(
            self.minValue * self.mask_range / 255.0)) + self.mask_left_margin

    # 计算label色块的右边界
    def eva_end(self):
        return int(round((255 - self.maxValue) * self.mask_range /
                         255.0)) + self.mask_right_margin

    # 拖动滑块触发的事件,区分是操作控件时触发还是其他控件的信号触发
    def change_sld_min_value(self, value):
        # 其他信号触发
        if type(self.sender()) != QSlider:
            # 屏蔽自身信号,避免死循环
            self.slider1_1.blockSignals(True)
            try:
                self.slider1_1.setValue(value)
            except Exception as err:
                print("err in set slider1_1 value: {0}".format(err))
            self.slider1_1.blockSignals(False)
        # 自身信号触发
        else:
            if value > self.maxValue:
                QMessageBox.information(
                    self, "Warning",
                    "Minimum value cannot be greater than maximum value")
                value = 0
                self.slider1_1.setValue(value)
            else:
                self.from_slider_min.emit(str(value))

    # 拖动滑块触发的事件,区分是操作控件时触发还是其他控件的信号触发
    def change_sld_max_value(self, value):
        # 其他信号触发
        if type(self.sender()) != QSlider:
            # 屏蔽自身信号,避免死循环
            self.slider1_2.blockSignals(True)
            try:
                self.slider1_2.setValue(value)
            except Exception as err:
                print("err in set slider1_2 value: {0}".format(err))
            self.slider1_2.blockSignals(False)
        # 自身信号触发
        else:
            if value < self.minValue:
                QMessageBox.information(
                    self, "Warning",
                    "Maximum value cannot be less than minimum value")
                value = 255
                self.slider1_2.setValue(value)
            else:
                self.from_slider_max.emit(str(value))

    # 更改文本(Min Val)触发的事件,区分是操作控件时触发还是其他控件的信号触发
    def change_line_min_value(self, text):
        # 来自其他信号
        if type(self.sender()) != QLineEdit:
            self.lineEdit1_1.blockSignals(True)
            self.lineEdit1_1.setText(str(text))
            self.lineEdit1_1.blockSignals(False)
        else:
            # 来自自身事件信号
            # 判定文本是否符合要求
            if self.is_available_int(text):
                if int(text) > self.maxValue:
                    QMessageBox.information(
                        self, "Warning",
                        "Minimum value cannot be greater than maximum value")
                    text = "0"
                    self.lineEdit1_1.setText(str(text))
                else:
                    self.from_line_min.emit(int(text))
            else:
                QMessageBox.information(
                    self, "Attention",
                    "Please enter a positive integer between 0 and 255")
                self.lineEdit1_1.blockSignals(True)
                self.lineEdit1_1.setText("0")
                self.lineEdit1_1.blockSignals(False)

    def change_line_min_value_finished(self):
        text = self.lineEdit1_1.text()
        # 来自其他信号
        if type(self.sender()) != QLineEdit:
            self.lineEdit1_1.blockSignals(True)
            self.lineEdit1_1.setText(str(text))
            self.lineEdit1_1.blockSignals(False)
        else:
            # 来自自身事件信号
            # 判定文本是否符合要求
            if self.is_available_int(text):
                if int(text) > self.maxValue:
                    QMessageBox.information(
                        self, "Warning",
                        "Minimum value cannot be greater than maximum value")
                    text = "0"
                    self.lineEdit1_1.setText(str(text))
                else:
                    self.from_line_min.emit(int(text))
            else:
                QMessageBox.information(
                    self, "Attention",
                    "Please enter a positive integer between 0 and 255")
                self.lineEdit1_1.blockSignals(True)
                self.lineEdit1_1.setText("0")
                self.lineEdit1_1.blockSignals(False)

    def change_line_max_value(self, text):
        if type(self.sender()) != QLineEdit:
            # 其他信号触发
            self.lineEdit1_2.blockSignals(True)
            self.lineEdit1_2.setText(str(text))
            self.lineEdit1_2.blockSignals(False)
        else:
            # 来自自身事件信号
            # 判定文本是否符合要求
            if self.is_available_int(text):
                if int(text) < self.minValue:
                    QMessageBox.information(
                        self, "Warning",
                        "Maximum value cannot be less than minimum value")
                    text = "255"
                    self.lineEdit1_2.setText(str(text))
                else:
                    self.from_line_max.emit(int(text))
            else:
                QMessageBox.information(
                    self, "Attention",
                    "Please enter a positive integer between 0 and 255")
                self.lineEdit1_2.blockSignals(True)
                self.lineEdit1_2.setText("255")
                self.lineEdit1_2.blockSignals(False)

    def change_line_max_value_finished(self):
        text = self.lineEdit1_2.text()
        if type(self.sender()) != QLineEdit:
            # 其他信号触发
            self.lineEdit1_2.blockSignals(True)
            self.lineEdit1_2.setText(str(text))
            self.lineEdit1_2.blockSignals(False)
        else:
            # 来自自身事件信号
            # 判定文本是否符合要求
            if self.is_available_int(text):
                if int(text) < self.minValue:
                    QMessageBox.information(
                        self, "Warning",
                        "Maximum value cannot be less than minimum value")
                    text = "255"
                    self.lineEdit1_2.setText(str(text))
                else:
                    self.from_line_max.emit(int(text))
            else:
                QMessageBox.information(
                    self, "Attention",
                    "Please enter a positive integer between 0 and 255")
                self.lineEdit1_2.blockSignals(True)
                self.lineEdit1_2.setText("255")
                self.lineEdit1_2.blockSignals(False)

    # 只会从其他控件处收到信号,更改色块显示的范围
    def change_label_min(self, left):
        if self.is_available_int(left):
            if int(left) <= self.maxValue:
                self.minValue = int(left)
                self.mask_layout.setContentsMargins(self.eva_start(),
                                                    self.mask_up_margin,
                                                    self.eva_end(),
                                                    self.mask_bottom_margin)

    # 只会从其他控件处收到信号,更改色块显示的范围
    def change_label_max(self, right):
        if self.is_available_int(right):
            if int(right) >= self.minValue:
                self.maxValue = int(right)
                self.mask_layout.setContentsMargins(self.eva_start(),
                                                    self.mask_up_margin,
                                                    self.eva_end(),
                                                    self.mask_bottom_margin)

    # 拖动色块时触发,重新计算左右边界,并把信号传递到其他控件
    def move_mask(self, diff):
        if self.is_available_int(
                self.minValue + diff) and self.is_available_int(self.maxValue +
                                                                diff):
            self.minValue += diff
            self.maxValue += diff
            self.avail_range = self.maxValue - self.minValue
            self.mask_layout.setContentsMargins(self.eva_start(),
                                                self.mask_up_margin,
                                                self.eva_end(),
                                                self.mask_bottom_margin)
            self.from_label_min.emit(self.minValue)
            self.from_label_max.emit(self.maxValue)
        elif self.is_available_int(self.minValue + diff):
            self.maxValue = 255
            self.minValue = 255 - self.avail_range
            self.mask_layout.setContentsMargins(self.eva_start(),
                                                self.mask_up_margin,
                                                self.eva_end(),
                                                self.mask_bottom_margin)
            self.from_label_min.emit(self.minValue)
            self.from_label_max.emit(self.maxValue)
        elif self.minValue + diff < 0:
            self.minValue = 0
            self.maxValue = self.avail_range
            self.mask_layout.setContentsMargins(self.eva_start(),
                                                self.mask_up_margin,
                                                self.eva_end(),
                                                self.mask_bottom_margin)
            self.from_label_min.emit(self.minValue)
            self.from_label_max.emit(self.maxValue)

    # 按下按钮时触发,对图片进行处理,先处理大于Min的部分,然后处理小于Max的部分,然后取交集
    def apply(self):
        try:
            if is_gray(self.img_path):
                self.img = skimage.io.imread(self.img_path)
                # ret, temp_image1 = cv2.threshold(self.img, self.minValue, 255, cv2.THRESH_BINARY)
                # ret, temp_image2 = cv2.threshold(self.img, self.maxValue, 255, cv2.THRESH_BINARY_INV)
                # image = cv2.bitwise_and(temp_image1, temp_image2)
                dt = np.zeros_like(self.img)
                dt[(self.img >= self.minValue)
                   & (self.img <= self.maxValue)] = 1.0
                # cv2.imshow("double threshold", dt * 255)
                # cv2.waitKey()
                # cv2.destroyAllWindows()
                dt *= 255
                return dt
        except Exception as err:
            print("apply error:{}".format(err))
Esempio n. 21
0
class gpvdm_contact_resistance(QWidget):

    changed = pyqtSignal()

    def __init__(self, file_box=True):
        QWidget.__init__(self)
        self.raw_value = "ground"
        self.hbox = QHBoxLayout()
        self.edit = QLineEdit()
        self.combobox = QComboBoxLang()
        self.combobox.addItemLang("ground", _("Ground"))
        self.combobox.addItemLang("constant", _("Constant bias"))
        self.combobox.addItemLang("change", _("Change"))

        self.hbox.addWidget(self.combobox)
        self.hbox.addWidget(self.edit)

        self.hbox.setContentsMargins(0, 0, 0, 0)
        self.edit.setStyleSheet("QLineEdit { border: none }")

        #self.button.clicked.connect(self.callback_button_click)
        self.combobox.currentIndexChanged.connect(self.callback_combobox)
        self.edit.textChanged.connect(self.callback_edit)
        self.setLayout(self.hbox)

    def update(self):
        self.edit.blockSignals(True)

        cb_value = self.applied_voltage_type
        if cb_value != "ground" and cb_value != "change":
            self.edit.setEnabled(True)
            self.edit.setText(self.applied_voltage)
        else:
            self.edit.setEnabled(False)
            if cb_value == "ground":
                self.edit.setText("0")
            elif cb_value == "change":
                self.edit.setText("Vsig")

        self.edit.blockSignals(False)

    def callback_edit(self):
        try:
            self.applied_voltage = str(float(self.edit.text()))
            self.changed.emit()
        except:
            pass

    def callback_combobox(self):
        self.applied_voltage_type = self.combobox.currentText_english()
        self.update()
        self.changed.emit()

    def setText(self, text):
        self.applied_voltage_type = text.split(":")[0]
        self.applied_voltage = text.split(":")[1]
        self.combobox.setValue_using_english(self.applied_voltage_type)
        self.update()

    def text(self):
        return self.applied_voltage_type + ":" + self.applied_voltage
Esempio n. 22
0
class CColorInfos(QWidget):

    colorChanged = pyqtSignal(QColor, int)
    colorAdded = pyqtSignal(QColor)

    def __init__(self, *args, **kwargs):
        super(CColorInfos, self).__init__(*args, **kwargs)
        layout = QGridLayout(self)
        layout.setContentsMargins(11, 2, 11, 2)
        layout.setSpacing(8)

        self.editHex = QLineEdit('#FF0000',
                                 self,
                                 alignment=Qt.AlignCenter,
                                 objectName='editHex',
                                 textChanged=self.onHexChanged)
        self.editHex.setValidator(
            QRegExpValidator(QRegExp('#[0-9a-fA-F]{6}$'), self.editHex))
        self.labelHex = QLabel('HEX', self, alignment=Qt.AlignCenter)
        layout.addWidget(self.editHex, 0, 0)
        layout.addWidget(self.labelHex, 1, 0)

        layout.addItem(
            QSpacerItem(10, 20, QSizePolicy.Fixed, QSizePolicy.Minimum), 0, 1)

        self.editRed = QSpinBox(self,
                                buttonSymbols=QSpinBox.NoButtons,
                                alignment=Qt.AlignCenter,
                                valueChanged=self.onRgbaChanged)
        self.editRed.setRange(0, 255)
        self.labelRed = QLabel('R', self, alignment=Qt.AlignCenter)
        layout.addWidget(self.editRed, 0, 2)
        layout.addWidget(self.labelRed, 1, 2)

        self.editGreen = QSpinBox(self,
                                  buttonSymbols=QSpinBox.NoButtons,
                                  alignment=Qt.AlignCenter,
                                  valueChanged=self.onRgbaChanged)
        self.editGreen.setRange(0, 255)
        self.labelGreen = QLabel('G', self, alignment=Qt.AlignCenter)
        layout.addWidget(self.editGreen, 0, 3)
        layout.addWidget(self.labelGreen, 1, 3)

        self.editBlue = QSpinBox(self,
                                 buttonSymbols=QSpinBox.NoButtons,
                                 alignment=Qt.AlignCenter,
                                 valueChanged=self.onRgbaChanged)
        self.editBlue.setRange(0, 255)
        self.labelBlue = QLabel('B', self, alignment=Qt.AlignCenter)
        layout.addWidget(self.editBlue, 0, 4)
        layout.addWidget(self.labelBlue, 1, 4)

        self.editAlpha = QSpinBox(self,
                                  buttonSymbols=QSpinBox.NoButtons,
                                  alignment=Qt.AlignCenter,
                                  valueChanged=self.onRgbaChanged)
        self.editAlpha.setRange(0, 255)
        self.labelAlpha = QLabel('A', self, alignment=Qt.AlignCenter)
        layout.addWidget(self.editAlpha, 0, 5)
        layout.addWidget(self.labelAlpha, 1, 5)

        layout.addItem(
            QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0,
            6)
        layout.addWidget(
            QPushButton('+',
                        self,
                        cursor=Qt.PointingHandCursor,
                        toolTip='添加自定义颜色',
                        clicked=self.onColorAdd), 0, 7)

        layout.setColumnStretch(0, 3)
        layout.setColumnStretch(1, 1)
        layout.setColumnStretch(2, 1)
        layout.setColumnStretch(3, 1)
        layout.setColumnStretch(4, 1)
        layout.setColumnStretch(5, 1)
        layout.setColumnStretch(6, 2)
        layout.setColumnStretch(7, 1)
        self.setFocus()
        self.editRed.setValue(255)
        self.editAlpha.setValue(255)

    def reset(self):
        pass

    def onColorAdd(self):
        self.colorAdded.emit(
            QColor(self.editRed.value(), self.editGreen.value(),
                   self.editBlue.value()))

    def setHex(self, code):
        self.editHex.setText(str(code))

    def updateColor(self, color):
        self.editRed.setValue(color.red())
        self.editGreen.setValue(color.green())
        self.editBlue.setValue(color.blue())

    def updateAlpha(self, _, alpha):
        self.editAlpha.setValue(alpha)

    def onHexChanged(self, code):
        if len(code) != 7:
            return
        color = QColor(code)
        if color.isValid():
            self.blockRgbaSignals(True)
            self.editHex.blockSignals(True)
            self.editRed.setValue(color.red())
            self.editGreen.setValue(color.green())
            self.editBlue.setValue(color.blue())
            self.editAlpha.setValue(color.alpha())
            self.colorChanged.emit(color, color.alpha())
            self.editHex.blockSignals(False)
            self.blockRgbaSignals(False)

    def onRgbaChanged(self, _):
        self.editHex.blockSignals(True)
        self.blockRgbaSignals(True)
        color = QColor(self.editRed.value(), self.editGreen.value(),
                       self.editBlue.value(), self.editAlpha.value())
        self.editHex.setText(color.name())
        self.colorChanged.emit(color, self.editAlpha.value())
        self.blockRgbaSignals(False)
        self.editHex.blockSignals(False)

    def blockRgbaSignals(self, block=True):
        self.editRed.blockSignals(block)
        self.editGreen.blockSignals(block)
        self.editBlue.blockSignals(block)
        self.editAlpha.blockSignals(block)

    def sizeHint(self):
        return QSize(280, 48)
Esempio n. 23
0
class ConfigROIWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(720, 140, 100, 100)
        self.setWindowTitle("Configure ROI")
        self.layout = QGridLayout()
        self.roiDict = {}
        self.home()

    def home(self):
        self.roiNum = QSpinBox(self)  #no. of ROI
        self.roiNum.setValue(1)
        self.roiNum.setSingleStep(1)
        self.roiNum.setRange(1, 100)
        self.roiNum.valueChanged.connect(self.num_change)
        self.label1 = QLabel("ROI Number", self)

        self.roiLabel = QLineEdit(self)  #Name of ROI
        ##        self.roiLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.roiLabel.setText("")
        self.roiLabel.textChanged.connect(self.label_change)
        self.label2 = QLabel("ROI Name", self)

        self.roiDrawBtn = QPushButton("Draw ROI", self)  #Draw ROI
        self.roiDrawBtn.setEnabled(False)
        ##        self.roiDrawBtn.clicked.connect(self.roi_draw)

        self.roiDef = QLabel("ROI Definition:", self)  #display definitions

        self.delBtn = QPushButton("Delete", self)  #Delete current definition
        self.delBtn.clicked.connect(self.del_roi)

        self.okBtn = QPushButton("OK", self)  #Close window

        self.layout.addWidget(self.roiNum, 1, 0, 1, 1)
        self.layout.addWidget(self.label1, 0, 0, 1, 1)
        self.layout.addWidget(self.roiLabel, 1, 1, 1, 1)
        self.layout.addWidget(self.label2, 0, 1, 1, 1)
        self.layout.addWidget(self.roiDrawBtn, 2, 0, 1, 1)
        self.layout.addWidget(self.delBtn, 2, 1, 1, 1)
        self.layout.addWidget(self.roiDef, 0, 2, 3, 1)
        self.layout.addWidget(self.okBtn, 3, 0, 1, 2)

        self.setLayout(self.layout)

    def num_change(self):
        key = self.roiNum.value()
        self.roiLabel.blockSignals(True)
        if key in self.roiDict.keys():
            self.roiLabel.setText(self.roiDict[key])
            self.roiDrawBtn.setEnabled(True)
        else:
            self.roiLabel.setText("")
            self.roiDrawBtn.setEnabled(False)
        self.roiLabel.blockSignals(False)

    def label_change(self):
        if self.roiLabel.text() in ["Dict", "dict", "All",
                                    "all"]:  #banned keywords
            self.roiLabel.blockSignals(True)
            self.roiLabel.setText("")
            self.roiLabel.blockSignals(False)
            self.roiDict[self.roiNum.value()] = self.roiLabel.text()
            self.update_def()
            self.roiDrawBtn.setEnabled(False)
        else:
            self.roiDict[self.roiNum.value()] = self.roiLabel.text()
            self.update_def()
            self.roiDrawBtn.setEnabled(True)

    def del_roi(self):  #delete current definition
        del self.roiDict[self.roiNum.value()]
        self.roiNum.setValue(self.roiNum.value() - 1)
        self.update_def()

    def update_def(self):  #update definition label
        defString = "ROI Definition:"
        for k in self.roiDict.keys():
            defString += "\nROI " + str(k) + ":\t" + self.roiDict[k]

        self.roiDef.setText(defString)

    def roi_draw(self):
        pass

    def showWindow(self):
        self.show()
Esempio n. 24
0
 def updateView(self, value):
     widget = self
     QLineEdit.blockSignals(widget, True)
     QLineEdit.setText(widget, str(value))
     QLineEdit.blockSignals(widget, False)
Esempio n. 25
0
class ColourPallet2(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        self.col = QColor(0, 84, 154)

        red = QLabel('Red')
        green = QLabel('Green')
        blue = QLabel('Blue')
        hue = QLabel('Hue')
        saturation = QLabel('Saturation')
        luminace = QLabel('Luminace')

        self.redSlider = QSlider()
        self.redSlider.setSingleStep(1)
        self.redSlider.setMaximum(255)
        self.redSlider.valueChanged.connect(self.rgb1SlidersChanged)
        self.greenSlider = QSlider()
        self.greenSlider.setSingleStep(1)
        self.greenSlider.setMaximum(255)
        self.greenSlider.valueChanged.connect(self.rgb1SlidersChanged)
        self.blueSlider = QSlider()
        self.blueSlider.setSingleStep(1)
        self.blueSlider.setMaximum(255)
        self.blueSlider.valueChanged.connect(self.rgb1SlidersChanged)

        self.hueSlider = QSlider()
        self.hueSlider.setSingleStep(1)
        self.hueSlider.setMaximum(359)
        self.hueSlider.valueChanged.connect(self.hslSliderChanged)
        self.saturationSlider = QSlider()
        self.saturationSlider.setSingleStep(1)
        self.saturationSlider.setMaximum(255)
        self.saturationSlider.valueChanged.connect(self.hslSliderChanged)
        self.luminaceSlider = QSlider()
        self.luminaceSlider.setSingleStep(1)
        self.luminaceSlider.setMaximum(255)
        self.luminaceSlider.valueChanged.connect(self.hslSliderChanged)

        grid1 = QGridLayout()
        grid1.setHorizontalSpacing(10)
        grid1.setContentsMargins(0, 0, 0, 0)
        for i in range(6):
            grid1.setColumnStretch(i, 1)
            grid1.setColumnMinimumWidth(i, 50)
            grid1.setRowMinimumHeight(i, 10)

        grid1.addWidget(red, 0, 0, alignment=Qt.AlignCenter)
        grid1.addWidget(self.redSlider, 1, 0, alignment=Qt.AlignCenter)
        grid1.addWidget(green, 0, 1, alignment=Qt.AlignCenter)
        grid1.addWidget(self.greenSlider, 1, 1, alignment=Qt.AlignCenter)
        grid1.addWidget(blue, 0, 2, alignment=Qt.AlignCenter)
        grid1.addWidget(self.blueSlider, 1, 2, alignment=Qt.AlignCenter)

        grid1.addWidget(hue, 0, 3, alignment=Qt.AlignCenter)
        grid1.addWidget(self.hueSlider, 1, 3, alignment=Qt.AlignCenter)
        grid1.addWidget(saturation, 0, 4, alignment=Qt.AlignCenter)
        grid1.addWidget(self.saturationSlider, 1, 4, alignment=Qt.AlignCenter)
        grid1.addWidget(luminace, 0, 5, alignment=Qt.AlignCenter)
        grid1.addWidget(self.luminaceSlider, 1, 5, alignment=Qt.AlignCenter)

        rgb1_ = QLabel('rgb')
        rgb255_ = QLabel('RGB')
        hex_ = QLabel('Hex')
        hsl_ = QLabel('hsl')

        self.rgb1Edit = QLineEdit()
        self.rgb1Edit.editingFinished.connect(self.rg1EditChanged)
        self.rgb255Edit = QLineEdit()
        self.rgb255Edit.editingFinished.connect(self.rgb255EditChanged)
        self.hexEdit = QLineEdit()
        self.hexEdit.editingFinished.connect(self.hexEditChanged)
        self.hslEdit = QLineEdit()
        self.hslEdit.editingFinished.connect(self.hslEditChanged)

        grid2 = QGridLayout()
        grid2.setVerticalSpacing(2)
        grid2.setHorizontalSpacing(20)
        grid2.setContentsMargins(0, 0, 0, 0)

        grid2.addWidget(rgb1_, 0, 0)
        grid2.addWidget(rgb255_, 0, 1)
        grid2.addWidget(hex_, 0, 2)
        grid2.addWidget(hsl_, 0, 3)

        grid2.addWidget(self.rgb1Edit, 1, 0)
        grid2.addWidget(self.rgb255Edit, 1, 1)
        grid2.addWidget(self.hexEdit, 1, 2)
        grid2.addWidget(self.hslEdit, 1, 3)

        self.square = QFrame(self)
        self.square.setStyleSheet("QWidget { background-color: %s }" %
                                  self.col.name())

        grid = QGridLayout()
        grid.addLayout(grid1, 0, 0)
        grid.addLayout(grid2, 1, 0)
        grid.addWidget(self.square, 2, 0)
        for i in (0, 2):
            grid.setRowStretch(i, 1)
        grid.setVerticalSpacing(10)

        self.setLayout(grid)

        self.setEverything()
        self.setGeometry(400, 400, 500, 400)
        self.setWindowTitle('Colour Pallet 2')
        self.show()

    def rg1EditChanged(self):
        rgb1 = ast.literal_eval(self.rgb1Edit.text())
        rgb255 = [round(i * 255) for i in rgb1]
        self.col.setRgb(*rgb255)
        self.setEverything()

    def rgb255EditChanged(self):
        rgb255 = ast.literal_eval(self.rgb255Edit.text())
        self.col.setRgb(*rgb255)
        self.setEverything()

    def hexEditChanged(self):
        self.col.setNamedColor(self.hexEdit.text())
        self.setEverything()

    def hslEditChanged(self):
        hsl = ast.literal_eval(self.hslEdit.text())
        self.col.setHsl(*hsl)
        self.setEverything()

    def rgb1SlidersChanged(self):
        self.col.setRgb(self.redSlider.value(), self.greenSlider.value(),
                        self.blueSlider.value())
        self.setEverything()

    def hslSliderChanged(self):
        self.col.setHsl(self.hueSlider.value(), self.saturationSlider.value(),
                        self.luminaceSlider.value())
        self.setEverything()

    def setEverything(self):
        hsl = self.col.getHsl()[0:3]
        Hex = self.col.name()
        rgb255 = self.col.getRgb()[0:3]
        rgb1 = [i / 255 for i in rgb255][0:3]

        self.BlockSignals(True)
        self.hslEdit.setText(str(hsl))
        self.hexEdit.setText(Hex)
        self.rgb255Edit.setText(str(rgb255))
        self.rgb1Edit.setText(str(rgb1))

        self.redSlider.setValue(rgb255[0])
        self.greenSlider.setValue(rgb255[1])
        self.blueSlider.setValue(rgb255[2])

        self.hueSlider.setValue(hsl[0])
        self.saturationSlider.setValue(hsl[1])
        self.luminaceSlider.setValue(hsl[2])

        self.square.setStyleSheet("QWidget { background-color: %s }" %
                                  self.col.name())
        self.BlockSignals(False)

    def BlockSignals(self, state):
        self.hslEdit.blockSignals(state)
        self.hexEdit.blockSignals(state)
        self.rgb255Edit.blockSignals(state)
        self.rgb1Edit.blockSignals(state)
        self.redSlider.blockSignals(state)
        self.greenSlider.blockSignals(state)
        self.blueSlider.blockSignals(state)
        self.hueSlider.blockSignals(state)
        self.saturationSlider.blockSignals(state)
        self.luminaceSlider.blockSignals(state)
Esempio n. 26
0
class CaesarCipherDialog(QDialog):
    def __init__(self, input: str, config: PluginConfig, codec):
        super(CaesarCipherDialog, self).__init__()
        self.config = config
        main_layout = QVBoxLayout()
        main_layout.addWidget(self._init_editor_frame())
        main_layout.addWidget(self._init_button_box())
        self.setLayout(main_layout)
        self.setWindowIcon(qtawesome.icon("fa.edit"))
        self.setWindowTitle("Caesar Cipher")
        self._setup_shortcuts()
        self._input = input
        self._text_area.setPlainText(self._input)
        self._codec = codec

    #############################################
    #   Initialize
    #############################################

    def _setup_shortcuts(self):
        ctrl_return_shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Return),
                                         self)
        ctrl_return_shortcut.activated.connect(self._accept)
        alt_return_shortcut = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Return),
                                        self)
        alt_return_shortcut.activated.connect(self._accept)
        alt_o_shortcut = QShortcut(QKeySequence(Qt.ALT + Qt.Key_O), self)
        alt_o_shortcut.activated.connect(self._accept)

    def _init_button_box(self):
        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self._accept)
        button_box.rejected.connect(self.reject)
        return button_box

    def _init_editor_frame(self):
        main_frame = QFrame()
        main_frame_layout = QVBoxLayout()

        slider_frame = self._init_slider_frame()
        main_frame_layout.addWidget(slider_frame)

        self._text_area = QPlainTextEdit()
        self._text_area.setReadOnly(True)
        self._text_area.setFixedHeight(126)
        main_frame_layout.addWidget(self._text_area)

        main_frame.setLayout(main_frame_layout)
        return main_frame

    def _init_slider_frame(self):
        slider_frame = QFrame()
        slider_frame_layout = QHBoxLayout()

        self._shift_slider = QSlider(Qt.Horizontal)
        self._shift_slider.setMinimum(0)
        self._shift_slider.setMaximum(26)
        self._shift_slider.setValue(self.config.get(Plugin.Option.Shift).value)
        self._shift_slider.valueChanged.connect(self._shift_slider_changed)
        slider_frame_layout.addWidget(self._shift_slider)

        self._shift_text = QLineEdit()
        self._shift_text.setText(
            str(self.config.get(Plugin.Option.Shift).value))
        self._shift_text.setFixedWidth(30)
        self._shift_text.setValidator(QIntValidator(0, 26))
        self._shift_text.textChanged.connect(self._shift_text_changed)
        slider_frame_layout.addWidget(self._shift_text)

        self._shift_calculate_button = QPushButton("Calculate")
        self._shift_calculate_button.clicked.connect(
            self._shift_calculate_button_clicked)
        slider_frame_layout.addWidget(self._shift_calculate_button)

        slider_frame.setLayout(slider_frame_layout)
        return slider_frame

    #############################################
    #   Events
    #############################################

    def _shift_calculate_button_clicked(self):
        offset = self._codec.calculate_offset(self._input)
        self._shift_slider.setSliderPosition(offset)

    def _shift_slider_changed(self, shift):
        if not shift:
            shift = 0
        self._shift_changed(shift)

    def _shift_text_changed(self, shift):
        if not shift:
            shift = 0
        self._shift_changed(int(shift))

    def _shift_changed(self, shift):
        self._shift_text.blockSignals(True)
        self._shift_slider.blockSignals(True)
        self._shift_slider.setValue(shift)
        self._shift_text.setText(str(shift))
        self._text_area.setPlainText(self._codec.run(self._input, shift))
        self._shift_slider.blockSignals(False)
        self._shift_text.blockSignals(False)

    #############################################
    #   Private interface
    #############################################

    def _get_shift(self):
        return self._shift_slider.value()

    def _accept(self):
        self.config.update({Plugin.Option.Shift.key: self._get_shift()})
        self.accept()

    #############################################
    #   Public interface
    #############################################

    def setInput(self, input) -> str:
        self._input = input
        self._shift_changed(self._get_shift())
class SendFundsDestinationItem(QObject):
    sig_remove_address = QtCore.pyqtSignal(object)
    sig_use_all_funds = QtCore.pyqtSignal(object)
    sig_amount_changed = QtCore.pyqtSignal(object)

    def __init__(self, parent, app_config, grid_layout, row_index,
                 address_widget_width):
        QObject.__init__(self)
        self.app_config = app_config
        self.main_layout = grid_layout
        self.row_number = row_index
        self.values_unit = OUTPUT_VALUE_UNIT_AMOUNT
        self.value_amount = None
        self.value_percent = None
        self.inputs_total_amount = None  # sum of all inputs (for counting percent type value)
        self.address_widget_width = address_widget_width
        self.setupUi(parent)

    def setupUi(self, Form):

        self.lbl_dest_address = QLabel(Form)
        self.lbl_dest_address.setText("Address")
        self.lbl_dest_address.setAlignment(QtCore.Qt.AlignRight
                                           | QtCore.Qt.AlignTrailing
                                           | QtCore.Qt.AlignVCenter)
        self.main_layout.addWidget(self.lbl_dest_address, self.row_number, 0)

        self.edt_dest_address = QLineEdit(Form)
        self.edt_dest_address.setFixedWidth(self.address_widget_width)
        self.main_layout.addWidget(self.edt_dest_address, self.row_number, 1)

        self.lbl_amount = QLabel(Form)
        self.lbl_amount.setText("Amount")
        self.main_layout.addWidget(self.lbl_amount, self.row_number, 2)

        self.lay_amount = QHBoxLayout()
        self.lay_amount.setContentsMargins(0, 0, 0, 0)
        self.lay_amount.setSpacing(0)
        self.main_layout.addLayout(self.lay_amount, self.row_number, 3)
        self.edt_amount = QLineEdit(Form)
        self.edt_amount.setFixedWidth(100)
        self.edt_amount.textChanged.connect(self.on_edt_amount_changed)
        self.lay_amount.addWidget(self.edt_amount)
        self.btn_use_all = QToolButton(Form)
        self.btn_use_all.setText('\u2b06')
        self.btn_use_all.setFixedSize(14, self.edt_amount.sizeHint().height())
        self.btn_use_all.setToolTip('Use remaining funds')
        self.btn_use_all.clicked.connect(self.on_btn_use_all_funds_clicked)
        self.lay_amount.addWidget(self.btn_use_all)

        # label for the second unit (e.g. percent if self.values_unit equals OUTPUT_VALUE_UNIT_AMOUNT)
        self.lbl_second_unit_value = QLabel(Form)
        self.main_layout.addWidget(self.lbl_second_unit_value, self.row_number,
                                   4)

        self.btn_remove_address = QToolButton(Form)
        self.btn_remove_address.setFixedSize(
            self.edt_amount.sizeHint().height(),
            self.edt_amount.sizeHint().height())
        self.main_layout.addWidget(self.btn_remove_address, self.row_number, 5)

        self.btn_remove_address.setStyleSheet("QToolButton{color: red}")
        self.btn_remove_address.setVisible(False)
        self.btn_remove_address.clicked.connect(
            self.on_btn_remove_address_clicked)
        self.btn_remove_address.setText('\u2716')  # 2501, 2716
        self.btn_remove_address.setToolTip("Remove address")

    def set_btn_remove_address_visible(self, visible):
        self.btn_remove_address.setVisible(visible)

    def on_btn_remove_address_clicked(self):
        self.sig_remove_address.emit(self)

    def on_btn_use_all_funds_clicked(self):
        self.sig_use_all_funds.emit(self)

    def on_edt_amount_changed(self, text):
        try:
            value = round(float(self.edt_amount.text()), 8)

            if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
                self.value_amount = value
            elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
                self.value_percent = value

            self.re_calculate_second_unit_value()
            self.sig_amount_changed.emit(self)

        except Exception:
            pass

    def get_value(self, default_value=None):
        """
        :param default_value: value that will be returned if the value entered by a user is invalid or empty
        """
        amount = self.edt_amount.text()
        if amount:
            try:
                return float(amount)
            except Exception:
                pass
        return default_value

    def get_value_amount(self):
        return self.value_amount

    def set_value(self, value):
        old_state = self.edt_amount.blockSignals(True)
        try:
            if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
                self.value_amount = value
            elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
                self.value_percent = value
            self.re_calculate_second_unit_value()

            self.edt_amount.setText(app_utils.to_string(value))
        finally:
            self.edt_amount.blockSignals(old_state)
        self.edt_amount.update()

    def display_second_unit_value(self):
        if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
            if self.value_percent is not None:
                self.lbl_second_unit_value.setText(
                    app_utils.to_string(round(self.value_percent, 3)) + '%')
            else:
                self.lbl_second_unit_value.setText('')
        elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
            if self.value_amount is not None:
                self.lbl_second_unit_value.setText(
                    app_utils.to_string(round(self.value_amount, 8)) + ' Dash')
            else:
                self.lbl_second_unit_value.setText('')

    def re_calculate_second_unit_value(self):
        if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
            # calculate the percent-value based on the inputs total amount and our item's amount
            if self.inputs_total_amount and self.value_amount is not None:
                self.value_percent = round(
                    self.value_amount * 100 / self.inputs_total_amount, 8)
                self.display_second_unit_value()
        elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
            # calculate the amount value based on inputs total amount and our item's percent-value
            if self.inputs_total_amount is not None and self.value_percent is not None:
                self.value_amount = round(
                    self.inputs_total_amount * self.value_percent / 100, 8)
                self.display_second_unit_value()

    def set_inputs_total_amount(self, amount):
        self.inputs_total_amount = amount
        self.re_calculate_second_unit_value()

    def set_address(self, address):
        self.edt_dest_address.setText(address)

    def get_address(self):
        return self.edt_dest_address.text()

    def set_output_value_unit(self, unit):
        old_state = self.edt_amount.blockSignals(True)
        try:
            if unit == OUTPUT_VALUE_UNIT_AMOUNT:
                self.edt_amount.setText(app_utils.to_string(self.value_amount))
                self.lbl_amount.setText('value')
            elif unit == OUTPUT_VALUE_UNIT_PERCENT:
                self.edt_amount.setText(app_utils.to_string(
                    self.value_percent))
                self.lbl_amount.setText('pct. value')
            else:
                raise Exception('Invalid unit')
            self.values_unit = unit
            self.display_second_unit_value()
        finally:
            self.edt_amount.blockSignals(old_state)
        self.edt_amount.update()

    def set_style_sheet(self):
        style = 'QLineEdit[invalid="true"]{border: 1px solid red}'
        self.edt_dest_address.setStyleSheet(style)
        self.edt_amount.setStyleSheet(style)

    def validate(self):
        valid = True
        address = self.edt_dest_address.text()
        if not address:
            valid = False
        elif not dash_utils.validate_address(address,
                                             self.app_config.dash_network):
            valid = False
        else:
            self.message = None
        if valid:
            self.edt_dest_address.setProperty('invalid', False)
        else:
            self.edt_dest_address.setProperty('invalid', True)

        amount = self.edt_amount.text()
        try:
            amount = float(amount)
            if amount > 0.0:
                self.edt_amount.setProperty('invalid', False)
            else:
                self.edt_amount.setProperty('invalid', True)
                valid = False
        except:
            self.edt_amount.setProperty('invalid', True)
            valid = False

        self.set_style_sheet()
        return valid

    def clear_validation_results(self):
        self.edt_amount.setProperty('invalid', False)
        self.edt_dest_address.setProperty('invalid', False)
        self.set_style_sheet()
Esempio n. 28
0
class SpellCheckDialog(QDialog):
    """Dialog to perform and control the spell check operation.
    """
    misspellFound = pyqtSignal()
    changeRequest = pyqtSignal(str)

    def __init__(self, spellCheckInterface, parent=None):
        """Create the dialog.

        Arguments:
            spellCheckInterface -- a reference to the spell engine interface
            parent -- the parent dialog
        """
        super().__init__(parent)
        self.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint
                            | Qt.WindowCloseButtonHint)
        self.setWindowTitle(_('Spell Check'))
        self.spellCheckInterface = spellCheckInterface
        self.textLineIter = None
        self.textLine = ''
        self.replaceAllDict = {}
        self.tmpIgnoreWords = set()
        self.word = ''
        self.postion = 0

        topLayout = QHBoxLayout(self)
        leftLayout = QVBoxLayout()
        topLayout.addLayout(leftLayout)
        wordBox = QGroupBox(_('Not in Dictionary'))
        leftLayout.addWidget(wordBox)
        wordLayout = QVBoxLayout(wordBox)
        label = QLabel(_('Word:'))
        wordLayout.addWidget(label)
        self.wordEdit = QLineEdit()
        wordLayout.addWidget(self.wordEdit)
        self.wordEdit.textChanged.connect(self.updateFromWord)
        wordLayout.addSpacing(5)
        label = QLabel(_('Context:'))
        wordLayout.addWidget(label)
        self.contextEdit = SpellContextEdit()
        wordLayout.addWidget(self.contextEdit)
        self.contextEdit.textChanged.connect(self.updateFromContext)

        suggestBox = QGroupBox(_('Suggestions'))
        leftLayout.addWidget(suggestBox)
        suggestLayout = QVBoxLayout(suggestBox)
        self.suggestList = QListWidget()
        suggestLayout.addWidget(self.suggestList)
        self.suggestList.itemDoubleClicked.connect(self.replace)

        rightLayout = QVBoxLayout()
        topLayout.addLayout(rightLayout)
        ignoreButton = QPushButton(_('Ignor&e'))
        rightLayout.addWidget(ignoreButton)
        ignoreButton.clicked.connect(self.ignore)
        ignoreAllButton = QPushButton(_('&Ignore All'))
        rightLayout.addWidget(ignoreAllButton)
        ignoreAllButton.clicked.connect(self.ignoreAll)
        rightLayout.addStretch()
        addButton = QPushButton(_('&Add'))
        rightLayout.addWidget(addButton)
        addButton.clicked.connect(self.add)
        addLowerButton = QPushButton(_('Add &Lowercase'))
        rightLayout.addWidget(addLowerButton)
        addLowerButton.clicked.connect(self.addLower)
        rightLayout.addStretch()
        replaceButton = QPushButton(_('&Replace'))
        rightLayout.addWidget(replaceButton)
        replaceButton.clicked.connect(self.replace)
        self.replaceAllButton = QPushButton(_('Re&place All'))
        rightLayout.addWidget(self.replaceAllButton)
        self.replaceAllButton.clicked.connect(self.replaceAll)
        rightLayout.addStretch()
        cancelButton = QPushButton(_('&Cancel'))
        rightLayout.addWidget(cancelButton)
        cancelButton.clicked.connect(self.reject)
        self.widgetDisableList = [
            ignoreButton, ignoreAllButton, addButton, addLowerButton,
            self.suggestList
        ]
        self.fullDisableList = (self.widgetDisableList +
                                [self.replaceAllButton, self.wordEdit])

    def startSpellCheck(self, textLineIter):
        """Spell check text lines given in the iterator.

        Block execution except for the dialog if mispellings are found.
        Return True if spell check completes, False if cancelled.
        Arguments:
            textLineIter -- an iterator of text lines to check
        """
        self.textLineIter = textLineIter
        try:
            self.textLine = next(self.textLineIter)
        except StopIteration:
            return True
        if self.spellCheck():
            if self.exec_() == QDialog.Rejected:
                return False
        return True

    def continueSpellCheck(self):
        """Check lines, starting with current line.

        Exit the dialog if there are no more lines to check.
        """
        if not self.spellCheck():
            self.accept()

    def spellCheck(self):
        """Step through the iterator and spell check the lines.

        If results found, update the dialog with the results and return True.
        Return false if the end of the iterator is reached.
        """
        while True:
            results = self.spellCheckInterface.checkLine(
                self.textLine, self.tmpIgnoreWords)
            if results:
                self.word, self.position, suggestions = results[0]
                newWord = self.replaceAllDict.get(self.word, '')
                if newWord:
                    self.textLine = self.replaceWord(newWord)
                    self.changeRequest.emit(self.textLine)
                else:
                    self.misspellFound.emit()
                    self.setWord(suggestions)
                    return True
            try:
                self.textLine = next(self.textLineIter)
                self.tmpIgnoreWords.clear()
            except StopIteration:
                return False

    def setWord(self, suggestions):
        """Set dialog contents from the checked line and spell check results.
        
        Arguments:
            suggestions -- a list of suggested replacement words
        """
        self.wordEdit.blockSignals(True)
        self.wordEdit.setText(self.word)
        self.wordEdit.blockSignals(False)
        self.contextEdit.blockSignals(True)
        self.contextEdit.setPlainText(self.textLine)
        self.contextEdit.setSelection(self.position,
                                      self.position + len(self.word))
        self.contextEdit.blockSignals(False)
        self.suggestList.clear()
        self.suggestList.addItems(suggestions)
        self.suggestList.setCurrentItem(self.suggestList.item(0))
        for widget in self.fullDisableList:
            widget.setEnabled(True)

    def replaceWord(self, newWord):
        """Return textLine with word replaced with newWord.
        
        Arguments:
            newWord -- the replacement word
        """
        return (self.textLine[:self.position] + newWord +
                self.textLine[self.position + len(self.word):])

    def ignore(self):
        """Set word to ignored (this check only) and continue spell check.
        """
        self.tmpIgnoreWords.add(self.word)
        self.continueSpellCheck()

    def ignoreAll(self):
        """Add to dictionary's ignore list and continue spell check.
        """
        self.spellCheckInterface.acceptWord(self.word)
        self.continueSpellCheck()

    def add(self):
        """Add misspelling to dictionary and continue spell check"""
        self.spellCheckInterface.addToDict(self.word, False)
        self.continueSpellCheck()

    def addLower(self):
        """Add misspelling to dictionary as lowercase and continue spell check.
        """
        self.spellCheckInterface.addToDict(self.word, True)
        self.continueSpellCheck()

    def replace(self):
        """Replace misspelled word with suggestion or context edit box
        
        Then continue spell check.
        """
        if self.suggestList.isEnabled():
            newWord = self.suggestList.currentItem().text()
            self.textLine = self.replaceWord(newWord)
        else:
            self.textLine = self.contextEdit.toPlainText()
        self.changeRequest.emit(self.textLine)
        self.continueSpellCheck()

    def replaceAll(self):
        """Replace misspelled word with suggestion or word edit (in future too).
        
        Stores changed word in replaceAllDict and continues spell check.
        """
        if self.suggestList.isEnabled():
            newWord = self.suggestList.currentItem().text()
        else:
            newWord = self.wordEdit.text()
        self.textLine = self.replaceWord(newWord)
        self.replaceAllDict[self.word] = newWord
        self.changeRequest.emit(self.textLine)
        self.continueSpellCheck()

    def updateFromWord(self):
        """Update dialog after word line editor change.
        
        Disables suggests and ignore/add controls. Updates the context editor.
        """
        for widget in self.widgetDisableList:
            widget.setEnabled(False)
        newWord = self.wordEdit.text()
        self.suggestList.clearSelection()
        self.contextEdit.blockSignals(True)
        self.contextEdit.setPlainText(self.replaceWord(newWord))
        self.contextEdit.setSelection(self.position,
                                      self.position + len(newWord))
        self.contextEdit.blockSignals(False)

    def updateFromContext(self):
        """Update dialog after context editor change.
        
        Disables controls except for replace.
        """
        for widget in self.fullDisableList:
            widget.setEnabled(False)
        self.suggestList.clearSelection()
Esempio n. 29
0
class FitDataWindow(QWidget):
    def __init__(self, *args, **kwargs):
##        super(QWidget, self).__init__(*args, **kwargs)
        super().__init__()
        self.setGeometry(100, 100, 1000, 500)
        self.setWindowTitle("Data Fitting")
        self.home()
        
    def home(self):
        
        # startFitLabel = QLabel("Start (%):")
        # endFitLabel = QLabel("End (%):")

        # self.fitStart = QDoubleSpinBox(self) #fitting range start
        # self.fitStart.setValue(0)
        # self.fitStart.setSingleStep(1)
        # self.fitStart.setRange(0, 100)

        # self.fitStop = QDoubleSpinBox(self) #fitting range end
        # self.fitStop.setValue(100)
        # self.fitStop.setSingleStep(1)
        # self.fitStop.setRange(0, 100)
        
        params_list = ["Index", "Time", "Vertical piezo", "Lateral piezo",
                       "Deformation", "Vertical force", "Lateral force"]
        
        xPlotLabel = QLabel("X Axis:", self)
        self.xPlot = QComboBox(self) #x param
        self.xPlot.addItems(params_list)
        self.xPlot.setCurrentIndex(0)
        self.xPlot.currentIndexChanged.connect(self.plotSequence)
        
        self.enableFitting = QCheckBox("Enable", self)
        self.enableFitting.stateChanged.connect(lambda: self.fitData(True))
        
        xFitLabel = QLabel("X Parameter:", self)
        yFitLabel = QLabel("Y Parameter:", self)

        self.xFit = QComboBox(self) #x param
        self.xFit.addItems(params_list)
        self.xFit.setCurrentIndex(4)
        self.xFit.currentIndexChanged.connect(self.plotSequence)

        self.yFit = QComboBox(self) #x param
        self.yFit.addItems(params_list)
        self.yFit.setCurrentIndex(5)
        self.yFit.currentIndexChanged.connect(self.plotSequence)
        
        # self.xDict = {'Vertical piezo':None,
        #          'Lateral piezo':None,
        #          'Deformation':None,
        #          'Time':None,
        #          'Index':None}
        # self.yDict = {'Vertical force':None,
        #              'Lateral force':None}
        self.fileDataDict = {}
        
        fitparamLabel = QLabel("Fit Parameters:", self)
        self.fittingParams = QLineEdit(self)
        self.fittingParams.setText('m,c')
        self.fittingParams.textChanged.connect(self.updateTEX)
        self.params_old = self.fittingParams.text().split(',')
        
        guessValLabel = QLabel("Initial Guess:", self)
        self.guessValues = QLineEdit(self)
        self.guessValues.setText('0,0')
        
        lowBoundLabel = QLabel("Lower Bouond:", self)
        self.lowBound = QLineEdit(self)
        self.lowBound.setText(',')
        
        upBoundLabel = QLabel("Upper Bouond:", self)
        self.upBound = QLineEdit(self)
        self.upBound.setText(',')
        
        constantsLabel = QLabel("Constants:", self)
        self.constantParams = QLineEdit(self)
        self.constantParams.setText('p=1,q=2,r=3')
        self.constantParams.textChanged.connect(self.updateTEX)
        self.constants_old = [_x.split('=')[0] for _x in self.constantParams.text().split(',')]
        
        fitfunctionLabel = QLabel("Fitting Function:", self)
        self.fittingFunctionType = QComboBox(self)
        self.fittingFunctionType.addItem("Linear")
        self.fittingFunctionType.addItem("Quadratic")
        self.fittingFunctionType.addItem("Custom")
        self.fittingFunctionType.currentIndexChanged.connect(self.updateFitFunction)

        #standard functions: equation, params, guess, l_bound, u_bound
        self.functionDict = {'Linear': ['m*x+c', 'm,c', '0,0', ',', ','],
                             'Quadratic': ['a*x**2+b*x+c', 'a,b,c', '0,0,0', ',,', ',,'],
                             'Custom': ['a*x', 'a', '0', '', '']}
        
        self.fittingFunctionText = QTextEdit(self)
        self.fittingFunctionText.setText('m*x+c')
        self.fittingFunctionText.textChanged.connect(self.updateTEX)
        
        self.generateFunction()
        
        self.fittingFunctionTEX = MathTextLabel(self.mathText, self)
        
        self.applyFitBtn = QPushButton("Fit!", self)
        # self.applyFitBtn.clicked.connect(lambda: self.fitData(True))

        self.fitResult = QTextEdit(self)
        self.fitResult.setText("Result:\n")
        self.fitResult.setReadOnly(True)
        # self.fitResult.setStyleSheet("QLabel { font-weight: bold; font-size: 16px;} ")

        self.plotInitialize()
        
        plot = PlotWidget(self.fig,
                          cursor1_init = self.axes.get_xbound()[0],
                          cursor2_init = self.axes.get_xbound()[1])
        self.plotWidget = plot.wid
        # plotToolbar = NavigationToolbar(self.plotWidget, self)

        # self.fitPosLabel = QLabel("Fit Position\n(x,y):", self) #fit eq. position        
        # self.fitPos = QLineEdit(self)
        # self.fitPos.setText('0.5,0.5')

        # self.showFitEq = QCheckBox('Show Slope', self) #display equation on plot
        
        paramGroupBox = QGroupBox()
        paramlayout=QGridLayout()
        paramGroupBox.setLayout(paramlayout)
        paramlayout.addWidget(xPlotLabel, 0, 0, 1, 1)
        paramlayout.addWidget(self.xPlot, 0, 1, 1, 1)
        paramlayout.addWidget(self.enableFitting, 0, 3, 1, 1)
        paramlayout.addWidget(xFitLabel, 1, 0, 1, 1)
        paramlayout.addWidget(self.xFit, 1, 1, 1, 1)
        paramlayout.addWidget(yFitLabel, 1, 2, 1, 1)
        paramlayout.addWidget(self.yFit, 1, 3, 1, 1)
        paramlayout.addWidget(fitparamLabel, 2, 0, 1, 1)
        paramlayout.addWidget(self.fittingParams, 2, 1, 1, 1)
        paramlayout.addWidget(guessValLabel, 2, 2, 1, 1)
        paramlayout.addWidget(self.guessValues, 2, 3, 1, 1)
        paramlayout.addWidget(lowBoundLabel, 3, 0, 1, 1)
        paramlayout.addWidget(self.lowBound, 3, 1, 1, 1)
        paramlayout.addWidget(upBoundLabel, 3, 2, 1, 1)
        paramlayout.addWidget(self.upBound, 3, 3, 1, 1)
        paramlayout.addWidget(constantsLabel, 4, 0, 1, 1)
        paramlayout.addWidget(self.constantParams, 4, 1, 1, 1)
        paramlayout.addWidget(fitfunctionLabel, 4, 2, 1, 1)
        paramlayout.addWidget(self.fittingFunctionType, 4, 3, 1, 1)
        paramlayout.addWidget(self.fittingFunctionText, 5, 0, 1, 4)
        paramlayout.addWidget(self.fittingFunctionTEX, 6, 0, 3, 4)
        paramlayout.addWidget(self.fitResult, 9,0, 1, 4)
        paramlayout.addWidget(self.applyFitBtn, 10, 1, 1, 2)
        # layout.addWidget(plotToolbar, 0, 4, 1, 6)
        # layout.addWidget(self.plotWidget, 1, 4, 9, 6)
        
        
        # plotGroupBox = QGroupBox()
        # plotlayout=QGridLayout()
        # plotGroupBox.setLayout(plotlayout)
        # plotlayout.addWidget(plot, 0, 0, 1, 1)
        # plotlayout.addWidget(plotToolbar, 0, 0, 1, 1)
        # plotlayout.addWidget(self.plotWidget, 1, 0, 1, 1)
        
        layout=QGridLayout()
        layout.addWidget(paramGroupBox, 0, 0, 1, 1)
        layout.addWidget(plot, 0, 1, 1, 1)
        
        self.setLayout(layout)
        # self.show()
      
            
    def updateFitFunction(self):
        logging.debug('test0')
        _key = self.fittingFunctionType.currentText()
        self.fittingFunctionText.blockSignals(True)
        self.fittingFunctionText.setText(self.functionDict[_key][0])
        self.fittingFunctionText.blockSignals(False)
        logging.debug('test')
        self.fittingParams.blockSignals(True)
        self.fittingParams.setText(self.functionDict[_key][1])
        self.fittingParams.blockSignals(False)
        logging.debug('test2')
        self.guessValues.setText(self.functionDict[_key][2])
        self.lowBound.setText(self.functionDict[_key][3])
        self.upBound.setText(self.functionDict[_key][4])

        self.updateTEX()
    
    def updateTEX(self):
        #delete old variables
        for _x in self.params_old:
            if _x != '':
                exec('del ' + _x, globals())

        for _x in self.constants_old:
            if _x != '':
                exec('del ' + _x, globals())

        #update function        
        self.generateFunction()

        self.params_old = self.fittingParams.text().split(',')
        self.constants_old = [_x.split('=')[0] for _x in self.constantParams.text().split(',')]

        #draw equation
        if self.mathText != None:
            self.fittingFunctionTEX.drawFigure(self.mathText)
            #below optional, remove later: CHECK!
            # self.plotRawData()
            # self.plotWidget.cursor1.set_ydata(self.axes.get_ybound()) #CHECK
            # self.plotWidget.cursor2.set_ydata(self.axes.get_ybound()) #CHECK
    ##        self.plotWidget.add_cursors()
            # self.fitData(False)
            # self.updatePlot()
        

    #create fitting function and TEX format for display
    def generateFunction(self):
        try:
            math_functions = ['re','im','sign','Abs','arg','conjugate',
                              'polar_lift','periodic_argument',
                              'principal_branch','sin','cos','tan',
                              'cot','sec','csc','sinc','asin','acos',
                              'atan','acot','asec','acsc','atan2',
                              'sinh','cosh','tanh','coth','sech',
                              'csch','asinh','acosh','atanh','acoth',
                              'asech','acsch','ceiling','floor','frac',
                              'exp','LambertW','log','exp_polar','Min',
                              'Max','root','sqrt','cbrt','real_root','pi']
            
            x_param = 'x'
            y_param = 'y'
            fit_params = self.fittingParams.text()
            constants = self.constantParams.text()
            # constant_vals = '1,2,3'
            equation_fit = self.fittingFunctionText.toPlainText()

            variables = x_param + ',' + fit_params
            
            global var
            var = sp.symbols(variables.replace(',',' '))
            exec(variables +  ' = var', globals())
            for _x in constants.split(','):
                exec(_x, globals())
    ##        print(p+q)
            for item in math_functions:
                equation_fit = equation_fit.replace(item,'sp.'+item)

         
            self.mathText = r'$' + y_param + '=' + sp.latex(eval(equation_fit),
                                                            ln_notation = True) + '$'

            self.func = sp.lambdify(list(var),eval(equation_fit))
            logging.debug(self.mathText)
        except Exception as e:
            logging.error(str(e))
            self.mathText = None

    def plotInitialize(self):

        self.fig = Figure(figsize=(5, 4), dpi=100)
        self.axes = self.fig.add_subplot(111)
        self.ax_raw = None
        # self.ax_norm = None
        self.ax_constr = None

        # #generate random data (for testing)
        xdata = np.linspace(0, 4, 50)
        self.fileDataDict[self.xFit.currentText()] = xdata
        self.fileDataDict[self.xPlot.currentText()] = xdata
        y = self.func(xdata, 2.5, 1.3)
        np.random.seed(1729)
        y_noise = 0.2 * np.random.normal(size=xdata.size)
        self.fileDataDict[self.yFit.currentText()] = y + y_noise
        
        self.plotRawData()
        self.updatePlot()
        self.fit_range = [None,None]

    def plotRawData(self):
                
        self.plotxdata= self.fileDataDict[self.xPlot.currentText()]

        self.xdata = self.fileDataDict[self.xFit.currentText()]
        self.ydata = self.fileDataDict[self.yFit.currentText()]

        if self.ax_raw != None: #check
            self.axes.lines.remove(self.ax_raw)
        
        # if self.xdata != None and self.ydata != None:
        self.ax_raw, = self.axes.plot(self.plotxdata, self.ydata, 'ro',
                                      linewidth=1, markersize=1)  
        self.axes.relim()
        self.axes.autoscale()
        self.axes.set_xlabel(self.xPlot.currentText())
        self.axes.set_ylabel(self.yFit.currentText())
        
        # self.updatePlot()
        # self.plotWidget.cursor1.set_xdata(self.axes.get_xbound()[0]) #CHECK
        # self.plotWidget.cursor2.set_xdata(self.axes.get_xbound()[1]) #CHECK
        
    def updatePlot(self):
        self.axes.relim()
        self.axes.autoscale()
        self.fig.tight_layout()
        self.fig.canvas.draw()
       
    def plotSequence(self):
        self.plotRawData()         
        self.update_cursor()
        self.fitData(False)
        # self.updatePlot()

    def update_cursor(self):
        if self.fit_range ==  [None,None]:
            self.fit_range[:] = [0,len(self.plotxdata)-1]
        if self.plotWidget.cursor1 != None:
            # x = self.plotxdata.min()
            x = self.plotxdata[self.fit_range[0]]
            y = [self.ydata.min(), self.ydata.max()]
            self.plotWidget.cursor1.set_xdata([x,x])
            self.plotWidget.cursor1.set_ydata(y) #CHECK
        if self.plotWidget.cursor2 != None:
            # x = self.plotxdata.max()
            x = self.plotxdata[self.fit_range[1]-1]
            y = [self.ydata.min(), self.ydata.max()]
            self.plotWidget.cursor2.set_xdata([x,x])
            self.plotWidget.cursor2.set_ydata(y) #CHECK
        # self.axes.relim()
        # self.axes.autoscale()
        self.updatePlot()
        self.plotWidget.draw_idle()
        
            
    # data fitting
    def fitData(self, update_slice = True):
        logging.debug("fit data")
        if self.enableFitting.isChecked() == True:
            self.generateFunction()
            #draw equation
            # if self.mathText != None:
            #     self.fittingFunctionTEX.drawFigure(self.mathText)
            # self.updateTEX()
            
            if update_slice == True:
                xlim1 = min([self.plotWidget.cursor1.get_xdata()[0],
                             self.plotWidget.cursor2.get_xdata()[0]])
                xlim2 = max([self.plotWidget.cursor1.get_xdata()[0],
                             self.plotWidget.cursor2.get_xdata()[0]])
        
                self.fit_range[:] = [np.searchsorted(self.plotxdata, [xlim1])[0],
                                  np.searchsorted(self.plotxdata, [xlim2])[0]+1]
                logging.debug("inside")
            
            fit_slice = slice(*self.fit_range)
            logging.debug('%s', fit_slice)
            
            guess_vals = self.guessValues.text()
            l_bounds = self.lowBound.text()
            u_bounds = self.upBound.text()
            
            l_bounds_val = [float(_x) if _x != '' else -np.inf \
                            for _x in l_bounds.split(',')] \
                            if l_bounds != '' else -np.inf
            u_bounds_val = [float(_x) if _x != '' else np.inf \
                            for _x in u_bounds.split(',')] \
                            if u_bounds != '' else np.inf
            
            labeltext = self.fittingParams.text().replace(',', '=%5.3f, ') + \
                        '=%5.3f'
                    
            
            # if self.ax_norm != None:
            #     self.axes.lines.remove(self.ax_norm)
    
            if self.ax_constr != None:
                self.axes.lines.remove(self.ax_constr)
    
            try:
                logging.debug("test")
                #normal fit
            
                # popt, pcov = curve_fit(self.func, self.xdata[fit_slice],
                #                        self.ydata[fit_slice],
                #                        [float(x) for x in guess_vals.split(',')])
    
                # print("normal", popt)
                # self.ax_norm, = self.axes.plot(self.xdata[fit_slice],
                #                                self.func(self.xdata[fit_slice], *popt), 'b-',
                #                          label= labeltext % tuple(popt))
                
                #contrained fit
                popt, pcov = curve_fit(self.func, self.xdata[fit_slice],
                                       self.ydata[fit_slice],
                                       [float(_x) for _x in guess_vals.split(',')],
                                       bounds=(l_bounds_val,u_bounds_val))
                logging.debug('%s, %s', "constrained", popt)
                
                self.fit_ydata = self.func(self.xdata[fit_slice], *popt)
                fit_label = labeltext % tuple(popt)
                self.ax_constr, = self.axes.plot(self.plotxdata[fit_slice],
                                                 self.fit_ydata, 'g--',
                                                 label= fit_label)
                error_label = 'Std. Dev. Error:\n' + labeltext % tuple(np.sqrt(np.diag(pcov)))
                self.fitResult.setText('Fit values:\n' + fit_label + '\n' + error_label)
                self.fitParams = dict(zip(self.fittingParams.text().split(','),
                                          popt))
                logging.debug('%s', self.fitParams)
    
            except Exception as e: #on fitting failure
                logging.error(str(e))
                self.fitResult.setText(str(e))
                # self.ax_norm = None
                self.ax_constr = None
    
            # self.plotWidget.cursor1.set_ydata(self.axes.get_ybound()) #CHECK
            # self.plotWidget.cursor2.set_ydata(self.axes.get_ybound()) #CHECK
            self.axes.legend()
            # self.axes.text(1, 1, "Test", picker=5)
    ##        self.fig.canvas.draw()
        else:
            if self.ax_constr != None:
                self.axes.get_legend().remove()
                self.axes.lines.remove(self.ax_constr)
                self.ax_constr = None
            self.fitParams = {}
            self.fitResult.setText('Fit values:\n')
        
        self.updatePlot()