class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.thread = QThread(self)
        self.worker = Worker()

        self.ui.progressBar.setValue(0)

        # connect buttons signals to its slots
        self.ui.pushButton_stop.clicked.connect(self.push_button_stop_click)
        self.ui.pushButton_start.clicked.connect(self.push_button_start_click)

        # now init thread
        self.worker.moveToThread(self.thread)

        # connect button click to worker slot
        # !!! you CAN NOT call thread object "worker" directly
        # so, you have to connect it to any signal in your GUI thread
        self.ui.pushButton_start.clicked.connect(self.worker.do_work)
        self.ui.pushButton_stop.clicked.connect(self.worker.stop)

        self.worker.valueChanged.connect(self.worker_value_changed)
        self.worker.finished.connect(self.worker_work_finished)

    def closeEvent(self, event):
        if not self.thread.isFinished():
            # disable stop with message
            self.worker.finished.disconnect(self.worker_work_finished)

            self.worker.finished.connect(self.stop_thread)
            self.thread.finished.connect(self.close)
            self.ui.pushButton_stop.clicked.emit()

            event.ignore()
        else:
            event.accept()

    def push_button_stop_click(self):
        self.ui.statusbar.showMessage("stop")

    def push_button_start_click(self):
        self.ui.statusbar.showMessage("start")
        self.thread.start()

    def worker_value_changed(self, value):
        self.ui.progressBar.setValue(value)

    def worker_work_finished(self):
        self.stop_thread()
        QMessageBox.information(self, "Information",
                                "Success, your work is done!")

    def stop_thread(self):
        print("stopping thread")
        self.thread.quit()
        self.thread.wait()
class MainWindowController(QMainWindow):
    __storyData: ActionBundle
    # Type hints for .ui objects - same name, do not change here!
    storyStatementList: QTableWidget
    comboLogLevel: QComboBox
    btnPlay: QPushButton
    btnLoadStory: QPushButton
    btnSaveStory: QPushButton
    # Signals
    sigStoryDataChanged = pyqtSignal()  # Must NOT be in constructor !

    @property
    def storyData(self):
        return self.__storyData

    @storyData.setter
    def storyData(self, bundle: ActionBundle):
        self.__storyData = bundle
        self.sigStoryDataChanged.emit()

    def __init__(self, *args, **kwargs):
        logger.debug("Initializing class %s", __class__.__name__)

        # Order of steps is important from here.

        # 1. Load UI file
        super(MainWindowController, self).__init__(*args, **kwargs)
        uic.loadUi("mainWindow.ui",
                   self)  # Loads all widgets of .ui into my instance

        # 2. Init priority properties
        # Set StoryLogHandler which is responsible for logging into a GUI-Widget
        self.storyLogHandler = StoryLogHandler()
        # Thread handling. We later need to access the worker while running,
        # to be able to force stop it. So preserve window lifecycle.
        self.storyWorker = None
        # Keep thread lifecycle, so we don't need to create any new as long as this window lives
        self.workerThread = QThread()

        # Bind GUI elements.
        self.defineConnections()

        # 3. Init general properties which may depend on step 2.
        self.storyData = ActionBundle.createNew()

        # 4. Init UI
        self.initView()

    def initView(self):
        self.setStoryStatementListLayout()
        self.initProgressbar(isVisible=False)
        self.defineComboLogLevel()
        self.btnPlay.setShortcut(QKeySequence("Ctrl+R"))
        self.btnLoadStory.setShortcut(QKeySequence("Ctrl+L"))
        self.btnSaveStory.setShortcut(QKeySequence("Ctrl+S"))

    def defineConnections(self):
        self.storyLogHandler.sigLoggerEmitted.connect(self.addLogText)
        self.sigStoryDataChanged.connect(self.setRunButtonState)
        self.sigStoryDataChanged.connect(self.setStoryTitle)
        self.sigStoryDataChanged.connect(self.setStoryStatementListData)
        self.btnLoadStory.clicked.connect(self.onClickedLoadStory)
        self.btnPlay.clicked.connect(self.onClickedRun)
        self.btnClearLog.clicked.connect(self.onClickedClearLog)
        self.comboLogLevel.currentIndexChanged.connect(
            self.onComboLogLevelChanged)

    def addStoryLogHandlerToLogger(self, level):
        """Add dedicated log handler to some package loggers (hence all its
        children, too) for routing logs into the GUI widget.
        NOTE: We MUST NOT change log levels of the loggers themselves here!

        :param level: The log level
        :return: None
        """
        packagesToChange = (__name__, "storage", "story", "network",
                            "selenium")
        self.storyLogHandler.setLevel(level)

        for package in packagesToChange:
            tmpLogger = clog.getLogger(
                package)  # Side effect: sets new logger if not exists!
            tmpLogger.removeHandler(
                self.storyLogHandler)  # removing only works by reference!
            tmpLogger.addHandler(self.storyLogHandler)

    def setStoryTitle(self):
        self.lblStoryTitle.setText(f"Loaded Story: {self.storyData.name}")

    def setStoryStatementListLayout(self):
        # Set style of story list
        self.storyStatementList.setShowGrid(False)
        self.storyStatementList.setStyleSheet(
            'QTableView::item {border-right: 1px solid #d6d9dc;}')
        # Set the story headers
        headerLabels = [
            "Action", "Property", "Value", "Search Data", "Search Strategy",
            "Result"
        ]
        self.storyStatementList.setColumnCount(len(headerLabels))
        self.storyStatementList.setHorizontalHeaderLabels(headerLabels)

        # Fonts
        headerFont = QFont()
        headerFont.setPointSize(15)
        self.storyStatementList.horizontalHeader().setFont(headerFont)
        self.storyStatementList.verticalHeader().setFont(headerFont)
        # Fit horizontal sizes
        headerH: QHeaderView = self.storyStatementList.horizontalHeader()
        headerH.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        headerH.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        headerH.setSectionResizeMode(2, QHeaderView.Interactive)
        headerH.setSectionResizeMode(3, QHeaderView.Interactive)
        headerH.setSectionResizeMode(4, QHeaderView.ResizeToContents)
        headerH.setSectionResizeMode(5, QHeaderView.Stretch)

    def setStoryStatementListData(self):
        numberOfStatements = len(self.storyData.actions)
        self.storyStatementList.setRowCount(numberOfStatements)
        # Fill
        for row, statement in enumerate(self.storyData.actions):
            # Set cells
            # col id 0
            content = QTableWidgetItem(statement.command.readable)
            content.setTextAlignment(QtCore.Qt.AlignLeft
                                     | QtCore.Qt.AlignVCenter)
            self.storyStatementList.setItem(row, 0, content)
            # col id 1, 2
            content = ("", "")
            if isinstance(statement, TextSendable):
                content = (ActionKey.TEXT_TO_SEND.readable,
                           statement.textToSend)
            elif isinstance(statement, Waitable):
                content = (ActionKey.MAX_WAIT.readable, str(statement.maxWait))
            self.storyStatementList.setItem(row, 1,
                                            QTableWidgetItem(content[0]))
            self.storyStatementList.setItem(row, 2,
                                            QTableWidgetItem(content[1]))
            # col id 3, 4
            content = ("", "")
            if isinstance(statement, Searchable):
                content = (statement.searchConditions.identifier,
                           statement.searchConditions.searchStrategy.readable)
            self.storyStatementList.setItem(row, 3,
                                            QTableWidgetItem(content[0]))
            self.storyStatementList.setItem(row, 4,
                                            QTableWidgetItem(content[1]))
            # col id 5 is set by setStoryStatementIndicatorsToDefault()
            self.setStoryStatementIndicatorsToDefault()

    def setStoryStatementIndicatorsToDefault(self):
        for row, statement in enumerate(self.storyData.actions):
            # Vertical headers
            self.setStoryStatementVerticalHeader(row=row)
            # Results
            self.setStoryStatementResult(row=row, text="")

    def setStoryStatementVerticalHeader(self,
                                        row: int,
                                        rgb: (int, int, int) = None):
        if row < 0:
            return
        counterStr = str(row + 1)
        headerItem = QTableWidgetItem(counterStr)
        if rgb:
            r, g, b = rgb[0], rgb[1], rgb[2]
            brush = QBrush(QColor(r, g, b))
            headerItem.setBackground(brush)
        self.storyStatementList.setVerticalHeaderItem(row, headerItem)

    def setStoryStatementResult(self, row, text: str):
        if row < 0:
            return
        COLUMN = 5
        content = QTableWidgetItem(text)
        content.setTextAlignment(QtCore.Qt.AlignCenter)
        content.setForeground(QBrush(QColor(94, 94, 94)))
        self.storyStatementList.setItem(row, COLUMN, content)

    def defineComboLogLevel(self):
        self.comboLogLevel.disconnect(
        )  # Avoid multiple triggers while setting up

        self.comboLogLevel.clear()
        self.comboLogLevel.addItem("Debug", clog.DEBUG)
        self.comboLogLevel.addItem("Info", clog.INFO)
        self.comboLogLevel.addItem("Warning", clog.WARN)
        self.comboLogLevel.addItem("Error", clog.ERROR)
        self.comboLogLevel.addItem("Critical", clog.CRITICAL)

        self.comboLogLevel.currentIndexChanged.connect(
            self.onComboLogLevelChanged)
        self.comboLogLevel.setCurrentIndex(1)

    def addLogText(self, logText):
        self.logList.appendPlainText(logText)
        self.logList.moveCursor(QtGui.QTextCursor.End)  # implies scrolling

    def initProgressbar(self,
                        isVisible: bool,
                        maximum: int = 100,
                        showText: bool = False):
        if isVisible:
            self.progressBar.setMaximum(maximum)
            self.progressBar.show()
        else:
            self.progressBar.hide()
            self.setProgressbar(0)
        self.progressBar.setTextVisible(showText)

    def setProgressbar(self, val):
        self.progressBar.setValue(val)

    def setRunButtonState(self):
        savedShortcut = self.btnPlay.shortcut()  # We must restore that below
        isEnabled = len(self.storyData.actions) > 0
        self.btnPlay.setEnabled(isEnabled)
        if self.workerThread.isRunning():
            self.btnPlay.setText("Stop")
        else:
            self.btnPlay.setText("Run")
        # Documentation says: Shortcut gets deleted after setText is called. Restore:
        self.btnPlay.setShortcut(savedShortcut)

    def runStory(self):
        actionCount = len(self.storyData.actions)
        if actionCount == 0:
            return
        self.initProgressbar(isVisible=True,
                             maximum=actionCount,
                             showText=True)
        # Setup storyWorker & thread
        self.storyWorker = StoryWorker(self.storyData, doHeadless=True)
        self.workerThread.started.connect(self.storyWorker.work)
        self.storyWorker.sigStarted.connect(self.setRunButtonState)
        self.storyWorker.sigStarted.connect(
            self.setStoryStatementIndicatorsToDefault)
        self.storyWorker.sigCurrentAction.connect(self.onStoryRunNextStatement)
        self.storyWorker.sigCompleted.connect(self.onStoryRunCompleted)
        # Only delete worker object inside the thread after it's been finished work.
        # Notice: We do NOT mark the workerThread for deletion as we will reuse it.
        self.storyWorker.sigCompleted.connect(self.storyWorker.deleteLater)
        # Move worker to thread and start working:
        self.storyWorker.moveToThread(
            self.workerThread)  # Move Worker object to Thread object
        # Start work
        self.workerThread.start()
        self.workerThread.setPriority(QThread.HighPriority)

    def openFileDialog(self, dirPath: pl.Path) -> pl.Path:
        dirStr = ""
        if dirPath is not None and dirPath.is_dir():
            dirStr = str(dirPath)
        # Setup and show dialog
        dlg = QFileDialog()
        options = dlg.HideNameFilterDetails
        pathStr, what = dlg.getOpenFileName(
            parent=self,
            caption="Open a story",
            directory=dirStr,
            filter="Story Files (*.json);; All Files (*)",
            options=options)
        # Transform path string of selected file
        if pathStr:
            return pl.Path(pathStr)

    # ---------------------------------------------------------------------------------
    # Callbacks
    # ---------------------------------------------------------------------------------

    def onClickedLoadStory(self, _, dao=JSONActionBundleDao()):
        path = self.openFileDialog(dao.connection.path.parent)
        if path:
            try:
                dao.connection.path = path
                with dao as connectedDao:
                    self.storyData = connectedDao.loadAll()

            except Exception as e:
                logger.error("Failed to load story. %s", e, exc_info=True)

    def onClickedRun(self, _):
        if self.workerThread.isRunning():
            logger.info("User stopped story execution.")
            self.storyWorker.stop()
        else:
            self.runStory()

    def onClickedClearLog(self, _):
        self.logList.clear()

    def onComboLogLevelChanged(self):
        currentLevel = self.comboLogLevel.currentData()
        self.addStoryLogHandlerToLogger(currentLevel)
        logger.debug("Log level changed - new level: %s", currentLevel)

    def onStoryRunNextStatement(self, actionProgress: int):
        row = actionProgress - 1
        self.setStoryStatementResult(row, text="ok")
        self.setStoryStatementVerticalHeader(row,
                                             rgb=(26, 173, 102))  # mark green
        self.setProgressbar(actionProgress)

    def onStoryRunCompleted(self):
        if not self.workerThread.isRunning():
            return
        # Stop storyWorker's thread and do cleanups
        logger.debug(
            "Work completed. Waiting for storyWorker thread to quit...")
        self.workerThread.quit()
        self.workerThread.wait()
        if self.workerThread.isFinished():
            self.initProgressbar(isVisible=False)
            self.setRunButtonState()
            logger.debug("Worker thread did quit.\n")
Exemple #3
0
class DAQ_Move(Ui_Form, QObject):
    """
        | DAQ_Move object is a module used to control one motor from a specified list.
        |
        | Preset is an optional list of dicts used to managers programatically settings such as the name of the controller from the list of possible controllers, COM address...
        |
        | Init is a boolean to tell the programm to initialize the controller at the start of the programm given the managers options

        ========================= =================================================
        **Attributes**             **Type**
        *command_stage*            instance of pyqtSignal
        *move_done_signal*         instance of pyqtSignal
        *update_settings_signal*   instance of pyqtSignal
        *status_signal*               instance of pyqtSignal
        *bounds_signal*            instance of pyqtSignal
        *params*                   dictionnary list
        *ui*                       instance of UI_Form
        *parent*                   QObject
        *title*                    string
        *wait_time*                int
        *initialized_state*        boolean
        *Move_done*                boolean
        *controller*               instance of the specific controller object
        *stage*                    instance of the stage (axis or wathever) object
        *current_position*         float
        *target_position*          float
        *wait_position_flag*       boolean
        *stage_types*              string list
        ========================= =================================================

        See Also
        --------
        set_enabled_move_buttons, set_setting_tree, stage_changed, quit_fun, ini_stage_fun, move_Abs, move_Rel, move_Home, get_position, stop_Motion, show_settings, show_fine_tuning

        References
        ----------
        QLocale, QObject, pyqtSignal, QStatusBar, ParameterTree
    """
    init_signal = pyqtSignal(bool)
    command_stage = pyqtSignal(ThreadCommand)
    command_tcpip = pyqtSignal(ThreadCommand)
    move_done_signal = pyqtSignal(
        str, float
    )  # to be used in external program to make sure the move has been done, export the current position. str refer to the unique title given to the module
    update_settings_signal = pyqtSignal(edict)
    status_signal = pyqtSignal(str)
    bounds_signal = pyqtSignal(bool)
    params = daq_move_params

    def __init__(self,
                 parent,
                 title="pymodaq Move",
                 preset=None,
                 init=False,
                 controller_ID=-1):
        """DAQ_Move object is a module used to control one motor from a specified list.
        managers is an optional list of dicts used to managers programatically settings such as the name of the
        controller from the list of possible controllers, COM address...
        init is a boolean to tell the programm to initialize the controller at the start of the programm given the
        managers options
        controller_ID is a unique random integer generated by the parent caller.
        To differenciate various instance of this class
        """
        self.logger = utils.set_logger(f'{logger.name}.{title}')
        self.logger.info(f'Initializing DAQ_Move: {title}')
        QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates))
        super().__init__()

        here = Path(__file__).parent
        splash = QtGui.QPixmap(str(here.parent.joinpath('splash.png')))
        self.splash_sc = QtWidgets.QSplashScreen(splash,
                                                 Qt.WindowStaysOnTopHint)

        self.ui = Ui_Form()
        self.ui.setupUi(parent)
        self.ui.Moveto_pb_bis_2.setVisible(False)
        self.parent = parent
        self.ui.title_label.setText(title)
        self.title = title
        self.ui.statusbar = QtWidgets.QStatusBar(parent)
        self.ui.StatusBarLayout.addWidget(self.ui.statusbar)
        self.ui.statusbar.setMaximumHeight(20)

        self.send_to_tcpip = False
        self.tcpclient_thread = None

        self.wait_time = 1000
        self.ui.Ini_state_LED

        self.ui.Ini_state_LED.clickable = False
        self.ui.Ini_state_LED.set_as_false()
        self.ui.Move_Done_LED.clickable = False
        self.ui.Move_Done_LED.set_as_false()
        self.initialized_state = False
        self.ui.Current_position_sb.setReadOnly(False)
        self.move_done_bool = True

        # ###########IMPORTANT############################
        self.controller = None  # the hardware controller/set after initialization and to be used by other modules
        # ################################################

        self.current_position = 0
        self.target_position = 0
        self.wait_position_flag = True

        self.ui.Current_position_sb.setValue(self.current_position)
        self.set_enabled_move_buttons(enable=False)
        self.ui.groupBox.hide()
        self.parent.resize(150, 200)

        # #Setting stages types
        self.stage_types = [mov['name'] for mov in DAQ_Move_Stage_type]
        self.ui.Stage_type_combo.clear()
        self.ui.Stage_type_combo.addItems(self.stage_types)

        # create main parameter tree
        self.ui.settings_tree = ParameterTree()
        self.ui.verticalLayout_2.addWidget(self.ui.settings_tree)
        self.ui.settings_tree.setMinimumWidth(300)

        self.settings = Parameter.create(name='Settings',
                                         type='group',
                                         children=self.params)
        self.ui.settings_tree.setParameters(self.settings, showTop=False)

        # connecting from tree
        self.settings.sigTreeStateChanged.connect(
            self.parameter_tree_changed
        )  # any changes on the settings will update accordingly the detector
        self.ui.settings_tree.setVisible(False)
        self.set_setting_tree()
        self.settings.child('main_settings',
                            'controller_ID').setValue(controller_ID)

        QtWidgets.QApplication.processEvents()
        # #Connecting buttons:
        self.ui.Stage_type_combo.currentIndexChanged.connect(
            self.set_setting_tree)
        self.ui.Stage_type_combo.currentIndexChanged.connect(
            self.stage_changed)

        self.ui.Quit_pb.clicked.connect(self.quit_fun)
        self.ui.IniStage_pb.clicked.connect(self.ini_stage_fun)

        self.update_status("Ready", wait_time=self.wait_time)
        self.ui.Move_Abs_pb.clicked.connect(
            lambda: self.move_Abs(self.ui.Abs_position_sb.value()))
        self.ui.Move_Rel_plus_pb.clicked.connect(
            lambda: self.move_Rel(self.ui.Rel_position_sb.value()))
        self.ui.Move_Rel_minus_pb.clicked.connect(
            lambda: self.move_Rel(-self.ui.Rel_position_sb.value()))
        self.ui.Find_Home_pb.clicked.connect(self.move_Home)
        self.ui.Get_position_pb.clicked.connect(self.get_position)
        self.ui.Stop_pb.clicked.connect(self.stop_Motion)

        self.ui.parameters_pb.clicked.connect(self.show_settings)
        self.ui.fine_tuning_pb.clicked.connect(self.show_fine_tuning)
        self.ui.Abs_position_sb.valueChanged.connect(
            self.ui.Abs_position_sb_bis.setValue)
        self.ui.Abs_position_sb_bis.valueChanged.connect(
            self.ui.Abs_position_sb.setValue)
        self.ui.Moveto_pb_bis.clicked.connect(
            lambda: self.move_Abs(self.ui.Abs_position_sb_bis.value()))

        # set managers options
        if preset is not None:
            for preset_dict in preset:
                # fo instance preset_dict=dict(object='Stage_type_combo',method='setCurrentIndex',value=1)
                if hasattr(self.ui, preset_dict['object']):
                    obj = getattr(self.ui, preset_dict['object'])
                    if hasattr(obj, preset_dict['method']):
                        setattr(obj, preset_dict['method'],
                                preset_dict['value'])
            QtWidgets.QApplication.processEvents()
        # initialize the controller if init=True
        if init:
            self.ui.IniStage_pb.click()

    def ini_stage_fun(self):
        """
            Init :
                * a DAQ_move_stage instance if not exists
                * a linked thread connected by signal to the DAQ_move_main instance

            See Also
            --------
            set_enabled_move_buttons, DAQ_utils.ThreadCommand, DAQ_Move_stage, DAQ_Move_stage.queue_command, thread_status, DAQ_Move_stage.update_settings, update_status
        """
        try:
            if not self.ui.IniStage_pb.isChecked():
                try:
                    self.set_enabled_move_buttons(enable=False)
                    self.ui.Stage_type_combo.setEnabled(True)
                    self.ui.Ini_state_LED.set_as_false()
                    self.command_stage.emit(ThreadCommand(command="close"))
                except Exception as e:
                    self.logger.exception(str(e))

            else:
                self.stage_name = self.ui.Stage_type_combo.currentText()
                stage = DAQ_Move_stage(self.stage_name, self.current_position,
                                       self.title)
                self.stage_thread = QThread()
                stage.moveToThread(self.stage_thread)

                self.command_stage[ThreadCommand].connect(stage.queue_command)
                stage.status_sig[ThreadCommand].connect(self.thread_status)
                self.update_settings_signal[edict].connect(
                    stage.update_settings)

                self.stage_thread.stage = stage
                self.stage_thread.start()

                self.ui.Stage_type_combo.setEnabled(False)
                self.command_stage.emit(
                    ThreadCommand(command="ini_stage",
                                  attributes=[
                                      self.settings.child(
                                          ('move_settings')).saveState(),
                                      self.controller
                                  ]))

        except Exception as e:
            self.logger.exception(str(e))
            self.set_enabled_move_buttons(enable=False)

    def get_position(self):
        """
            Get the current position from the launched thread via the "check_position" Thread Command.

            See Also
            --------
            update_status, DAQ_utils.ThreadCommand
        """
        try:
            self.command_stage.emit(ThreadCommand(command="check_position"))

        except Exception as e:
            self.logger.exception(str(e))

    def move_Abs(self, position, send_to_tcpip=False):
        """
            | Make the move from an absolute position.
            |
            | The move is made if target is in bounds, sending the thread command "Reset_Stop_Motion" and "move_Abs".

            =============== ========== ===========================================
            **Parameters**   **Type**    **Description**

            *position*        float      The absolute target position of the move
            =============== ========== ===========================================

            See Also
            --------
            update_status, check_out_bounds, DAQ_utils.ThreadCommand
        """
        try:
            self.send_to_tcpip = send_to_tcpip
            if not (position == self.current_position
                    and self.stage_name == "Thorlabs_Flipper"):
                self.ui.Move_Done_LED.set_as_false()
                self.move_done_bool = False
                self.target_position = position
                self.update_status("Moving", wait_time=self.wait_time)
                # self.check_out_bounds(position)
                self.command_stage.emit(
                    ThreadCommand(command="Reset_Stop_Motion"))
                self.command_stage.emit(
                    ThreadCommand(command="move_Abs", attributes=[position]))

        except Exception as e:
            self.logger.exception(str(e))

    def move_Home(self, send_to_tcpip=False):
        """
            Send the thread commands "Reset_Stop_Motion" and "move_Home" and update the status.

            See Also
            --------
            update_status, DAQ_utils.ThreadCommand
        """
        self.send_to_tcpip = send_to_tcpip
        try:
            self.ui.Move_Done_LED.set_as_false()
            self.move_done_bool = False
            self.update_status("Moving", wait_time=self.wait_time)
            self.command_stage.emit(ThreadCommand(command="Reset_Stop_Motion"))
            self.command_stage.emit(ThreadCommand(command="move_Home"))

        except Exception as e:
            self.logger.exception(str(e))

    def move_Rel_p(self):
        self.ui.Move_Rel_plus_pb.click()

    def move_Rel_m(self, send_to_tcpip=False):
        self.ui.Move_Rel_minus_pb.click()

    def move_Rel(self, rel_position, send_to_tcpip=False):
        """
            | Make a move from the given relative psition and the current one.
            |
            | The move is done if (current position + relative position) is in bounds sending Threads Commands "Reset_Stop_Motion" and "move_done"

            =============== ========== ===================================================
            **Parameters**   **Type**    **Description**

            *position*        float     The relative target position from the current one
            =============== ========== ===================================================

            See Also
            --------
            update_status, check_out_bounds, DAQ_utils.ThreadCommand
        """
        try:
            self.send_to_tcpip = send_to_tcpip
            self.ui.Move_Done_LED.set_as_false()
            self.move_done_bool = False
            self.target_position = self.current_position + rel_position
            self.update_status("Moving", wait_time=self.wait_time)
            # self.check_out_bounds(self.target_position)
            self.command_stage.emit(ThreadCommand(command="Reset_Stop_Motion"))
            self.command_stage.emit(
                ThreadCommand(command="move_Rel", attributes=[rel_position]))

        except Exception as e:
            self.logger.exception(str(e))

    def parameter_tree_changed(self, param, changes):
        """
            | Check eventual changes in the changes list parameter.
            |
            | In case of changed values, emit the signal containing the current path and parameter via update_settings_signal to the connected hardware.

            =============== ====================================    ==================================================
            **Parameters**   **Type**                                **Description**

             *param*         instance of pyqtgraph parameter         The parameter to be checked

             *changes*       (parameter,change,infos) tuple list     The (parameter,change,infos) list to be treated
            =============== ====================================    ==================================================
        """

        for param, change, data in changes:
            path = self.settings.childPath(param)
            if path is not None:
                childName = '.'.join(path)
            else:
                childName = param.name()
            if change == 'childAdded':
                if 'main_settings' not in path:
                    self.update_settings_signal.emit(
                        edict(path=path,
                              param=data[0].saveState(),
                              change=change))

            elif change == 'value':

                if param.name() == 'connect_server':
                    if param.value():
                        self.connect_tcp_ip()
                    else:
                        self.command_tcpip.emit(ThreadCommand('quit'))

                elif param.name() == 'ip_address' or param.name == 'port':
                    self.command_tcpip.emit(
                        ThreadCommand(
                            'update_connection',
                            dict(ipaddress=self.settings.child(
                                'main_settings', 'tcpip',
                                'ip_address').value(),
                                 port=self.settings.child(
                                     'main_settings', 'tcpip',
                                     'port').value())))

                if path is not None:
                    if 'main_settings' not in path:
                        self.update_settings_signal.emit(
                            edict(path=path, param=param, change=change))
                        if self.settings.child('main_settings', 'tcpip',
                                               'tcp_connected').value():
                            self.command_tcpip.emit(
                                ThreadCommand('send_info',
                                              dict(path=path, param=param)))

            elif change == 'parent':
                if param.name() not in putils.iter_children(
                        self.settings.child('main_settings'), []):
                    self.update_settings_signal.emit(
                        edict(path=['move_settings'],
                              param=param,
                              change=change))

    def connect_tcp_ip(self):
        if self.settings.child('main_settings', 'tcpip',
                               'connect_server').value():
            self.tcpclient_thread = QThread()

            tcpclient = TCPClient(self.settings.child('main_settings', 'tcpip',
                                                      'ip_address').value(),
                                  self.settings.child('main_settings', 'tcpip',
                                                      'port').value(),
                                  self.settings.child(('move_settings')),
                                  client_type="ACTUATOR")
            tcpclient.moveToThread(self.tcpclient_thread)
            self.tcpclient_thread.tcpclient = tcpclient
            tcpclient.cmd_signal.connect(self.process_tcpip_cmds)

            self.command_tcpip[ThreadCommand].connect(tcpclient.queue_command)

            self.tcpclient_thread.start()
            tcpclient.init_connection()

    @pyqtSlot(ThreadCommand)
    def process_tcpip_cmds(self, status):
        if 'move_abs' in status.command:
            self.move_Abs(status.attributes[0], send_to_tcpip=True)

        elif 'move_rel' in status.command:
            self.move_Rel(status.attributes[0], send_to_tcpip=True)

        elif 'move_home' in status.command:
            self.move_Home(send_to_tcpip=True)

        elif 'check_position' in status.command:
            self.send_to_tcpip = True
            self.command_stage.emit(ThreadCommand('check_position'))

        elif status.command == 'connected':
            self.settings.child('main_settings', 'tcpip',
                                'tcp_connected').setValue(True)

        elif status.command == 'disconnected':
            self.settings.child('main_settings', 'tcpip',
                                'tcp_connected').setValue(False)

        elif status.command == 'Update_Status':
            self.thread_status(status)

        elif status.command == 'set_info':
            param_dict = ioxml.XML_string_to_parameter(status.attributes[1])[0]
            param_tmp = Parameter.create(**param_dict)
            param = self.settings.child('move_settings',
                                        *status.attributes[0][1:])

            param.restoreState(param_tmp.saveState())

    def quit_fun(self):
        """
            Leave the current instance of DAQ_Move_Main closing the parent widget.
        """
        # insert anything that needs to be closed before leaving
        try:
            if self.initialized_state:
                self.ui.IniStage_pb.click()

            self.parent.close()  # close the parent widget
            try:
                self.parent.parent().parent().close(
                )  # the dock parent (if any)
            except Exception as e:
                self.logger.info('No dock parent to close')

        except Exception as e:
            icon = QtGui.QIcon()
            icon.addPixmap(
                QtGui.QPixmap(":/Labview_icons/Icon_Library/close2.png"),
                QtGui.QIcon.Normal, QtGui.QIcon.Off)
            msgBox = QtWidgets.QMessageBox(parent=None)
            msgBox.addButton(QtWidgets.QMessageBox.Yes)
            msgBox.addButton(QtWidgets.QMessageBox.No)
            msgBox.setWindowTitle("Error")
            msgBox.setText(
                str(e) +
                " error happened when uninitializing the stage.\nDo you still want to quit?"
            )
            msgBox.setDefaultButton(QtWidgets.QMessageBox.Yes)
            ret = msgBox.exec()
            if ret == QtWidgets.QMessageBox.Yes:
                self.parent.close()

    @pyqtSlot()
    def raise_timeout(self):
        """
            Update status with "Timeout occured" statement.

            See Also
            --------
            update_status
        """
        self.update_status("Timeout occured", wait_time=self.wait_time)
        self.wait_position_flag = False

    def set_enabled_move_buttons(self, enable=False):
        """
            Set the move buttons enabled (or not) in User Interface from the gridLayout_buttons course.

            =============== ========== ================================================
            **Parameters**   **Type**   **Description**

             *enable*        boolean    The parameter making enable or not the buttons
            =============== ========== ================================================

        """
        Nchildren = self.ui.gridLayout_buttons.count()
        for ind in range(Nchildren):
            widget = self.ui.gridLayout_buttons.itemAt(ind).widget()
            if widget is not None:
                widget.setEnabled(enable)
        self.ui.Moveto_pb_bis.setEnabled(enable)
        self.ui.Abs_position_sb_bis.setEnabled(enable)
        self.ui.Current_position_sb.setEnabled(enable)

    @pyqtSlot(int)
    def set_setting_tree(self, index=0):
        """
            Set the move settings parameters tree, clearing the current tree and setting the 'move_settings' node.

            See Also
            --------
            update_status
        """
        self.stage_name = self.ui.Stage_type_combo.currentText()
        self.settings.child('main_settings',
                            'move_type').setValue(self.stage_name)
        try:
            for child in self.settings.child(('move_settings')).children():
                child.remove()
            parent_module = utils.find_dict_in_list_from_key_val(
                DAQ_Move_Stage_type, 'name', self.stage_name)
            class_ = getattr(
                getattr(parent_module['module'],
                        'daq_move_' + self.stage_name),
                'DAQ_Move_' + self.stage_name)
            params = getattr(class_, 'params')
            move_params = Parameter.create(name='move_settings',
                                           type='group',
                                           children=params)

            self.settings.child(
                ('move_settings')).addChildren(move_params.children())

        except Exception as e:
            self.logger.exception(str(e))

    def show_fine_tuning(self):
        """
          Make GroupBox visible if User Interface corresponding attribute is checked to show fine tuning in.
        """
        if self.ui.fine_tuning_pb.isChecked():
            self.ui.groupBox.show()
        else:
            self.ui.groupBox.hide()

    def show_settings(self):
        """
          Make settings tree visible if User Interface corresponding attribute is checked to show the settings tree in.
        """
        if self.ui.parameters_pb.isChecked():

            self.ui.settings_tree.setVisible(True)
        else:
            self.ui.settings_tree.setVisible(False)

    @pyqtSlot(int)
    def stage_changed(self, index=0):
        """

            See Also
            --------
            move_Abs
        """
        pass

    def stop_Motion(self):
        """
            stop any motion via the launched thread with the "stop_Motion" Thread Command.

            See Also
            --------
            update_status, DAQ_utils.ThreadCommand
        """
        try:
            self.command_stage.emit(ThreadCommand(command="stop_Motion"))
        except Exception as e:
            self.logger.exception(str(e))

    @pyqtSlot(ThreadCommand)
    def thread_status(
        self, status
    ):  # general function to get datas/infos from all threads back to the main
        """
            | General function to get datas/infos from all threads back to the main0
            |

            Interpret a command from the command given by the ThreadCommand status :
                * In case of **'Update_status'** command, call the update_status method with status attributes as parameters
                * In case of **'ini_stage'** command, initialise a Stage from status attributes
                * In case of **'close'** command, close the launched stage thread
                * In case of **'check_position'** command, set the Current_position value from status attributes
                * In case of **'move_done'** command, set the Current_position value, make profile of move_done and send the move done signal with status attributes
                * In case of **'Move_Not_Done'** command, set the current position value from the status attributes, make profile of Not_Move_Done and send the Thread Command "Move_abs"
                * In case of **'update_settings'** command, create child "Move Settings" from  status attributes (if possible)

            ================ ================= ======================================================
            **Parameters**     **Type**         **Description**

            *status*          ThreadCommand()   instance of ThreadCommand containing two attributes :

                                                 * *command*    str
                                                 * *attributes* list

            ================ ================= ======================================================

            See Also
            --------
            update_status, set_enabled_move_buttons, get_position, DAQ_utils.ThreadCommand, parameter_tree_changed, raise_timeout
        """

        if status.command == "Update_Status":
            if len(status.attributes) > 2:
                self.update_status(status.attributes[0],
                                   wait_time=self.wait_time,
                                   log_type=status.attributes[1])
            else:
                self.update_status(status.attributes[0],
                                   wait_time=self.wait_time)

        elif status.command == "ini_stage":
            # status.attributes[0]=edict(initialized=bool,info="", controller=)
            self.update_status("Stage initialized: {:} info: {:}".format(
                status.attributes[0]['initialized'],
                status.attributes[0]['info']),
                               wait_time=self.wait_time)
            if status.attributes[0]['initialized']:
                self.controller = status.attributes[0]['controller']
                self.set_enabled_move_buttons(enable=True)
                self.ui.Ini_state_LED.set_as_true()
                self.initialized_state = True
            else:
                self.initialized_state = False
            if self.initialized_state:
                self.get_position()
            self.init_signal.emit(self.initialized_state)

        elif status.command == "close":
            try:
                self.update_status(status.attributes[0],
                                   wait_time=self.wait_time)
                self.stage_thread.exit()
                self.stage_thread.wait()
                finished = self.stage_thread.isFinished()
                if finished:
                    pass
                    delattr(self, 'stage_thread')
                else:
                    self.update_status('thread is locked?!', self.wait_time,
                                       'log')
            except Exception as e:
                self.logger.exception(str(e))
            self.initialized_state = False
            self.init_signal.emit(self.initialized_state)

        elif status.command == "check_position":
            self.ui.Current_position_sb.setValue(status.attributes[0])
            self.current_position = status.attributes[0]
            if self.settings.child(
                    'main_settings', 'tcpip',
                    'tcp_connected').value() and self.send_to_tcpip:
                self.command_tcpip.emit(
                    ThreadCommand('position_is', status.attributes))

        elif status.command == "move_done":
            self.ui.Current_position_sb.setValue(status.attributes[0])
            self.current_position = status.attributes[0]
            self.move_done_bool = True
            self.ui.Move_Done_LED.set_as_true()
            self.move_done_signal.emit(self.title, status.attributes[0])
            if self.settings.child(
                    'main_settings', 'tcpip',
                    'tcp_connected').value() and self.send_to_tcpip:
                self.command_tcpip.emit(
                    ThreadCommand('move_done', status.attributes))

        elif status.command == "Move_Not_Done":
            self.ui.Current_position_sb.setValue(status.attributes[0])
            self.current_position = status.attributes[0]
            self.move_done_bool = False
            self.ui.Move_Done_LED.set_as_false()
            self.command_stage.emit(
                ThreadCommand(command="move_Abs",
                              attributes=[self.target_position]))

        elif status.command == 'update_main_settings':
            # this is a way for the plugins to update main settings of the ui (solely values, limits and options)
            try:
                if status.attributes[2] == 'value':
                    self.settings.child('main_settings',
                                        *status.attributes[0]).setValue(
                                            status.attributes[1])
                elif status.attributes[2] == 'limits':
                    self.settings.child('main_settings',
                                        *status.attributes[0]).setLimits(
                                            status.attributes[1])
                elif status.attributes[2] == 'options':
                    self.settings.child(
                        'main_settings',
                        *status.attributes[0]).setOpts(**status.attributes[1])
            except Exception as e:
                self.logger.exception(str(e))

        elif status.command == 'update_settings':
            # ThreadCommand(command='update_settings',attributes=[path,data,change]))
            try:
                self.settings.sigTreeStateChanged.disconnect(
                    self.parameter_tree_changed
                )  # any changes on the settings will update accordingly the detector
            except Exception:
                pass
            try:
                if status.attributes[2] == 'value':
                    self.settings.child('move_settings',
                                        *status.attributes[0]).setValue(
                                            status.attributes[1])
                elif status.attributes[2] == 'limits':
                    self.settings.child('move_settings',
                                        *status.attributes[0]).setLimits(
                                            status.attributes[1])
                elif status.attributes[2] == 'options':
                    self.settings.child(
                        'move_settings',
                        *status.attributes[0]).setOpts(**status.attributes[1])
                elif status.attributes[2] == 'childAdded':
                    child = Parameter.create(name='tmp')
                    child.restoreState(status.attributes[1][0])
                    self.settings.child('move_settings',
                                        *status.attributes[0]).addChild(
                                            status.attributes[1][0])

            except Exception as e:
                self.logger.exception(str(e))
            self.settings.sigTreeStateChanged.connect(
                self.parameter_tree_changed
            )  # any changes on the settings will update accordingly the detector

        elif status.command == 'raise_timeout':
            self.raise_timeout()

        elif status.command == 'outofbounds':
            self.bounds_signal.emit(True)

        elif status.command == 'show_splash':
            self.ui.settings_tree.setEnabled(False)
            self.splash_sc.show()
            self.splash_sc.raise_()
            self.splash_sc.showMessage(status.attributes[0], color=Qt.white)

        elif status.command == 'close_splash':
            self.splash_sc.close()
            self.ui.settings_tree.setEnabled(True)

    def update_status(self, txt, wait_time=0):
        """
            Show the given txt message in the status bar with a delay of wait_time ms if specified (0 by default).

            ================ ========== =================================
            **Parameters**    **Type**   **Description**
             *txt*            string     The message to show
             *wait_time*      int        The delay time of showing
            ================ ========== =================================

        """

        self.ui.statusbar.showMessage(txt, wait_time)
        self.status_signal.emit(txt)
        self.logger.info(txt)
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.image = np.ndarray
        self.lprnet_model = LPRNet('../material/LPRNet.pb')
        self.vehicle_detection_model = VehicleDetection(
            '../material/ObjectModel.tflite')
        self.lp_recognition_model = LicensePlateRecognition(
            '../material/LicensePlateRecognition.tflite')
        self.label = None
        self.cap = cv2.VideoCapture
        self.fps = 0
        self.timer = QTimer(self)
        self.thread = QThread()
        self.plates = None
        self.last_time = 0
        self.init_slots()

    def init_slots(self):
        self.ui.actionLPRNet.triggered.connect(self.set_lprnet_model)
        self.ui.actionObject_Detection.triggered.connect(
            self.set_vehicle_detection_model)
        self.ui.actionLicense_Plate_Recognition.triggered.connect(
            self.set_license_plate_recognition_model)
        self.ui.Image_path.clicked.connect(self.load_image)
        self.ui.Video_path.clicked.connect(self.load_video)
        self.ui.play_buttom.clicked.connect(self.play_pause)
        self.timer.timeout.connect(self.timer_tick)

    def show_recognition_rate(self, t):
        t = int(t * 1000)
        self.ui.recognition_rate.setText(str(t - self.last_time) + ' ms')
        self.last_time = t

    def play_pause(self):
        if self.timer.isActive():
            self.timer.stop()
            self.ui.play_buttom.setText('Play')
        else:
            self.timer.start(1000 / self.fps)
            self.ui.play_buttom.setText('Pause')

    def set_plate(self, plates):
        self.plates = plates
        '''
        Utils.bounding_box(self, self.image, self.plates[0],
                           self.plates[1], self.plates[2],
                           self.plates[3], None)
        '''
        for plate in self.plates:
            Utils.bounding_box(self, self.image, plate[0][0], plate[0][1],
                               plate[0][2], plate[0][3], plate[1])
        #Utils.show_image(self, self.image, self.ui.image_holder)

    def detection_threading(self, use_image):
        if not self.thread.isRunning():
            self.thread = QThread()
            self.worker = Detection(self.image, self.vehicle_detection_model,
                                    self.lp_recognition_model,
                                    self.lprnet_model)
            self.worker.moveToThread(self.thread)
            self.thread.started.connect(self.worker.image_recognize)
            self.worker.finished.connect(self.thread.quit)
            self.worker.finished.connect(self.worker.deleteLater)
            # self.thread.finished.connect(self.thread.deleteLater)
            self.worker.result.connect(self.set_plate)
            self.worker.recognition_rate.connect(self.show_recognition_rate)
            if use_image:
                self.worker.finished.connect(lambda: Utils.show_image(
                    self, self.image, self.ui.image_holder))
            self.thread.start()
        else:
            if self.plates is not None:
                self.set_plate(self.plates)

    def timer_tick(self):
        ret, self.image = self.cap.read()
        if not ret:
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
            ret, self.image = self.cap.read()
        self.detection_threading(use_image=False)
        Utils.show_image(self, self.image, self.ui.video_holder)

    def set_lprnet_model(self):
        ofd = Utils.open_file_dialog(self, '*.pb, *.pbtxt(*.pb *.pbtxt)')
        if ofd:
            self.lprnet_model = LPRNet(model_filepath=ofd[0])
            self.ui.statusbar.showMessage('LPRNet success loaded !', 2000)

    def set_vehicle_detection_model(self):
        ofd = Utils.open_file_dialog(self, '*.tflite(*.tflite)')
        if ofd:
            self.vehicle_detection_model = VehicleDetection(
                model_filepath=ofd[0])
            self.ui.statusbar.showMessage(
                'Vehicle detection model success loaded !', 2000)

    def set_license_plate_recognition_model(self):
        ofd = Utils.open_file_dialog(self, '*.tflite(*.tflite)')
        if ofd:
            self.lp_recognition_model = LicensePlateRecognition(
                model_filepath=ofd[0])
            self.ui.statusbar.showMessage(
                'License plate recognition model success loaded !', 2000)

    def load_label(self):
        ofd = Utils.open_file_dialog(self, '*.txt(*.txt)')
        if ofd:
            self.label = ofd

    def load_image(self):
        ofd = Utils.open_file_dialog(self, '*.jpg, *.jpge(*.jpg *.jpge)')
        if not ofd:
            return
        self.ui.Image_path_text.setText(ofd[0])
        self.image = cv2.imread(ofd[0])
        self.detection_threading(use_image=True)
        while self.thread.isFinished():
            Utils.show_image(self, self.image, self.ui.image_holder)
        '''
        if self.object_detection_model or self.lprnet_model:
            self.image_recognize(self.ui.image_holder)
        else:
            msg = QMessageBox()
            msg.setWindowTitle("No model error")
            msg.setText("No model loaded !")
            msg.setStandardButtons(QMessageBox.Ok)
            msg.exec_()
        '''

    def load_video(self):
        ofd = Utils.open_file_dialog(self, '*.mp4, *.avi(*.mp4 *.avi)')
        if not ofd:
            return
        self.ui.video_path_text.setText(ofd[0])
        self.cap = cv2.VideoCapture(ofd[0])
        self.fps = self.cap.get(cv2.CAP_PROP_FPS)
class main_principal(QWidget):
    fecha_actual = datetime.datetime.now(
    )  # datetime es la libreria que nos ayuda con las fechas horas minutos y hsta segundos
    #en este caso nos referimos a que nos de la fecha actual
    contador_hilo = 1
    cursor = None
    lista_hilos = []

    def banner_anonymous(self):
        #banner un bannner es solo un mensaje de texto
        #el       \32 es para indicarle que queremos un espacio en blanco
        #puedes ver que hay muchos \32 en este paratado eso es debido a que
        #se necesitan espacios para imprimir bien el banner
        self.textarea_proceso.append(
            "$$$$$\32$\32\32\32\32$\32\32$$$$$\32$\32\32\32\32\32$$$$$$$\32\32\32\32\32\32######\32#####\32######\32##\32\32\32##"
        )
        self.textarea_proceso.append(
            "$\32\32\32\32\32\32\32\32$\32\32\32\32$\32\32\32\32\32$\32\32\32\32$\32\32\32\32\32$\32\32\32\32\32$\32\32\32\32\32$\32\32\32\32\32\32#\32\32\32\32\32\32#"
        )
        self.textarea_proceso.append(
            "$$$$$\32\32\32$\32\32\32\32\32$$$$$\32$\32\32\32\32\32$\32\32\32\32\32$||||||\32\32\32\32\32######\32#####\32######\32#\32\32\32\32\32#"
        )
        self.textarea_proceso.append(
            "$\32\32\32\32\32\32$\32\32$\32\32\32$\32\32\32\32\32$\32\32\32\32\32$\32\32\32\32\32\32$\32\32\32\32\32\32\32\32\32\32\32#|#\32\32\32\32\32#\32\32\32\32#\32#\32\32\32\32\32#"
        )
        self.textarea_proceso.append(
            "$$$$$\32$\32\32\32\32$\32\32$\32\32\32\32\32$$$$$\32$$$$$$$\32\32\32\32\32\32######\32#\32\32\32\32\32#\32\32\32\32#\32#\32\32\32\32\32#"
        )
        self.textarea_proceso.append(
            "Autor: Aldair Martinez Alias Hans Krammler Junior"
        )  # Aqui declaramos mi nombre jajaj XD

    def spam_uno_a_uno(self):
        self.banner_anonymous()
        print(self.fecha_actual)

    def spam_uno_a_varios(self):
        self.banner_anonymous()
        print(self.fecha_actual)
        print(Fore.GREEN + "HAZ ELEGIDO ENVIAR DESDE TU COREO A OTROS CORREO")

    def mensaje(self):
        print("SE TERMINO EL HILO")

    contador_hilo_aux = 0
    lista_hilos = []
    workers = []
    threads = []

    def incializa_hilos(self):
        if self.contador_hilo == 1:
            self.thread = QThread()
            self.thread.setObjectName("Hilo_1")
            self.worker = Worker(self.contador_hilo, self.textarea_proceso,
                                 self.label_proceso_verificacion, None)
            self.worker.moveToThread(self.thread)
            self.thread.setTerminationEnabled(True)
            self.thread.started.connect(self.worker.run)
            self.worker.finished.connect(self.terminado)
            self.thread.finished.connect(
                lambda: self.verificar_resultado_hilo(self.thread))
            self.worker.progressed.connect(
                lambda: self.reportProgress(self.worker))
            self.worker.siguiente.connect(
                lambda: self.siguiente_hilo(self.thread))
            self.thread.finished.connect(self.thread.quit)
            self.lista_hilos.append(self.thread)
            return self.lista_hilos
        else:
            self.lista_hilos = []
            self.threads.append(QThread())
            self.threads[self.contador_hilo_aux].setObjectName("Hilo_2")
            if self.contador_hilo == 2:
                print("Aqui tambien funciona")
                self.workers.append(
                    Worker(self.contador_hilo, self.textarea_proceso, None,
                           self.label_proceso_verificacion2))
            elif self.contador_hilo == 3:
                self.workers.append(
                    Worker(self.contador_hilo, self.textarea_proceso, None,
                           self.label_proceso_verificacion3))
            self.workers[self.contador_hilo_aux].moveToThread(
                self.threads[self.contador_hilo_aux])
            self.threads[self.contador_hilo_aux].started.connect(
                self.workers[self.contador_hilo_aux].run)
            self.workers[self.contador_hilo_aux].finished.connect(
                self.terminado)
            print("Pasando terminado")
            self.threads[self.contador_hilo_aux].finished.connect(
                lambda: self.verificar_resultado_hilo(self.threads[
                    self.contador_hilo_aux]))
            print("Pasando verificar resultado")
            self.workers[self.contador_hilo_aux].progressed.connect(
                lambda: self.reportProgress(self.workers[self.contador_hilo_aux
                                                         ]))
            print("Pasando reportprogress")
            self.workers[self.contador_hilo_aux].siguiente.connect(
                lambda: self.siguiente_hilo(self.threads[self.contador_hilo_aux
                                                         ]))
            self.threads[self.contador_hilo_aux].finished.connect(
                self.threads[self.contador_hilo_aux].quit)
            print("Pasando quit")
            self.lista_hilos.append(self.threads[self.contador_hilo_aux])
            return self.lista_hilos
        return None

    def siguiente_hilo(self, hilo):
        if self.contador_hilo == 1:
            if hilo.isRunning() == False:
                if hilo.isFinished() == True:
                    print("si funciono")
                    hilo.finished.connect(hilo.deleteLater)
                    hilo.finished.connect(hilo.terminate)
                    self.contador_hilo = self.contador_hilo + 1
                    self.incializa_hilos()[0].start()
        elif self.contador_hilo > 1:
            if self.get_verificado() == -1:
                return -1
            elif self.get_verificado() == -2:
                return -2
            else:
                return -3
            if hilo.isRunning() == False:
                if hilo.isFinished() == True:
                    self.contador_hilo = self.contador_hilo + 1
                    self.contador_hilo_aux = self.contador_hilo_aux + 1
                    self.incializa_hilos()[0].start()

        return 0

    cursor = None
    estado_hilo_x = 0
    lista_verificado = []

    def set_verificado(self, estado):
        self.estado_hilo_x = estado

    def get_verificado(self):
        self.lista_verificado.append(self.estado_hilo_x)
        print("get.lista.verificado" + str(self.lista_verificado[0]))
        return int(self.lista_verificado[0])

    def terminado(self):
        if self.contador_hilo == 1:
            print("TRABAJO TERMINADO")
            self.thread.finished.emit()
        elif self.contador_hilo > 1:
            self.threads[self.contador_hilo_aux].finished.emit()

    def verificar_resultado_hilo(self, thread):
        if self.contador_hilo == 1:
            if self.thread.isRunning() == False:
                print("EL HILO 1 HA TERMINADO SU TAREA")
            if self.thread.isFinished() == True:
                print("EL HILI 1 TERMINO CON EXITO")
                self.worker.progressed.emit()
        elif self.contador_hilo > 1:
            print(str(self.contador_hilo_aux))
            if thread.isRunning() == False:
                print("EL HILO" + str(self.contador_hilo_aux) +
                      "ESTA CORRIENDO")
            if thread.isFinished() == True:
                print("EL HILO" + str(self.contador_hilo_aux) + "HA TERMINADO")
                self.workers[self.contador_hilo_aux].progressed.emit()

    porcentaje = 10
    _i = 0
    lista_progreso = ["INICIANDO HILO DE EJECUCION......"]

    def reportProgress(self, worker):
        print(self.contador_hilo)
        if self.contador_hilo == 1:
            print("REPORTANDO HILO")
            #print("POSICION ACTUAL DEL CURSOR"+str(int(self.textarea_proceso.textCursor().position())))
            worker.siguiente.emit()
        else:
            if self.contador_hilo == 2:
                email = self.line_edit_user_email.text()
                password = self.line_edit_user_password.text()
                longitud_email = len(email)
                longitud_password = len(password)
                if longitud_email == 0 or longitud_password == 0:

                    self.set_verificado(-1)
            elif self.contador_hilo == 3:
                email = self.line_edit_user_email.text()
                email_to_list = list(email)
                contador = 1
                for _x in len(email):
                    if email_to_list[_x] == "@":
                        contador = contador + 1
                if contador > 1:
                    self.set_verificado(-2)
            elif self.contador_hilo == 4:
                contador = 1
                email_dominio = email.split(".")
                textarea_proceso.append("DIVIDIENDO TU EMAIL--->" +
                                        email_dominio)
                longitud_dominio_email = len(email_dominio)
                textarea_proceso.append(
                    "VERIFICANDO COINCIDENCIA DE DOMINIO DE EMAIL....")
                dominios = [
                    "gmail.com", "outlook.com", "outlook.es", "hotmail.com",
                    "hotmail.mx", "hotmail.es"
                ]
                longitud_dominio_gmail = len(dominios[0])
                longitud_dominio_outlook_com = len(dominios[1])
                longitud_dominio_outlook_es = len(dominios[2])
                longitud_dominio_hotmail_com = len(dominios[3])
                longitud_dominio_hotmail_mx = len(dominios[4])
                longitud_dominio_hotmail_es = len(dominios[5])
                for _i in range(longitud_dominio_email):
                    for _j in (range(longitud_dominio_gmail)
                               or range(longitud_dominio_outlook_com)
                               or range(longitud_dominio_outlook_es)
                               or range(longitud_dominio_hotmail_com)
                               or range(longitud_dominio_hotmail_mx)
                               or range(longitud_dominio_hotmail_es)):
                        if _j < longitud_dominio_gmail:
                            if dominios[0][_j] == email_dominio[1][_j]:
                                print(dominios[0][j])
                                contador = contador + 1
                        elif _j < longitud_dominio_outlook_com:
                            if dominios[1][_j] == email_dominio[1][_j]:
                                contador = contador + 1
                        elif _j < longitud_dominio_outlook_es:
                            if dominios[2][_j] == email_dominio[1][_j]:
                                contador = contador + 1
                        elif _j < longitud_dominio_hotmail_com:
                            if dominios[3][_j] == email_dominio[1][_j]:
                                contador = contador + 1
                        elif _j < longitud_dominio_hotmail_mx:
                            if dominios[4][_j] == email_dominio[1][_j]:
                                contador = contador + 1
                        elif _j < longitud_dominio_hotmail_es:
                            if dominios[5][_j] == email_dominio[1][_j]:
                                contador = contador + 1
                    if contador > longitud_dominio_gmail:
                        return -3
                    else:
                        contador = 1
            print("AQUI MEN")
            posicion_textarea = len(self.lista_progreso[self._i])
            self.cursor = self.textarea_proceso.textCursor()
            print("CURSOR ACTUAL" + str(self.cursor))
            print("POSICION CURSOR ACTUAL++" + str(self.cursor.position()))
            if self.cursor.atEnd() == True:
                print("FIN DEL CURSOR True")
                print("TEXTO SELECCIONADO ANTES" + self.cursor.selectedText())
                posicion_inicial = (self.cursor.position() -
                                    1) - (posicion_textarea - 1) + 1
                print("posicion inicial" + str(posicion_inicial))
                time.sleep(0.4)
                self.cursor.setPosition(posicion_inicial,
                                        QTextCursor.KeepAnchor)
                print("TEXTO SELECCIONADO AHORA" + self.cursor.selectedText())
                print("POSICION CURSOR ACTUAL--" + str(self.cursor.position()))
                self.progreso = self.cursor.selectedText() + str(
                    self.porcentaje)
                self.porcentaje = self.porcentaje + 10
                self.textarea_proceso.append(self.progreso)
                print("POSICION CURSOR ACTUAL++" + str(self.cursor.position()))
            self._i = self._i + 1
            worker.siguiente.emit()

    def verificar_opcion(self):
        pass

    def validar_oprciones(self):
        if self.checkbox_opcion_spam_uno_a_uno.isChecked(
        ) == True and self.checkbox_opcion_spam_uno_a_varios.isChecked():
            self.label_estado_proceso.setText(
                Fore.RED +
                "LO SIENTO NO PUEDES SELECCIONAR DOS OPCIONES A LA VEZ")
            return False
        else:
            return True

    def escannear_red(self):
        scan = nmap.PortScanner()
        informacion_sistema = os.uname()

    def presionado(self):
        resultado_validar_opciones = self.validar_oprciones()
        if resultado_validar_opciones == True:
            self.inciar_hilo_principal()
            self.resultado_verificacion_email = 0
            if self.resultado_verificacion_email == 0:
                print("PASANDO AL SIGUIENTE HILO DE EJECUCION")
            if self.resultado_verificacion_email == 1:
                if self.checkbox_opcion_spam_uno_a_uno.isChecked() == True:
                    print(
                        "LAS OCPIONES SELECCIONADAAS EN LOS CASILLA SON VALIDAS"
                    )
            else:
                if self.resultado_verificacion_email == -1 or self.resultado_verificacion_email == -2 or self.resultado_verificacion_email == -3:
                    if self.resultado_verificacion_email == -1:
                        self.label_estado_proceso.setText(
                            "NO HAS INGRESADO NADA EN EL PRIMER CAMPO")
                        self.textarea_proceso.append(
                            "ERROR NO HAS INGRESADO NADA EN EL PRIMER CAMPO")
                    elif self.resultado_verificacion_email == -2:
                        self.label_estado_proceso.setText(
                            "HAS MAS DE UN ARROBA EN TU CORREO")
                        self.textarea_proceso.append(
                            "ERROR HAY MAS DE UN ARROBA EN TU CORREO")
                    elif self.resultado_verificacion_email == -3:
                        self.label_estado_proceso.setText(
                            "TERMINACION DE CORREO NO VALIDA")

        else:
            self.textarea_proceso.append(
                "INTENTE DE NUEVO RELLENAR LOS CAMPOS ADECUADAMENTE")

    def __init__(self, parent=None):
        #Parte de intefaz grafica
        super().__init__(parent)

        self.color = QColor(0, 0, 0, 0.5)
        self.setGeometry(550, 100, 1500, 500)
        self.pallette = QPalette(self.color)
        self.pallette.setColor(QPalette.Text, Qt.cyan)

        self.titulo_ventana = "ENVIO DE CORREO PARA HACER SPAM"
        self.setWindowTitle(self.titulo_ventana)
        self.setStyleSheet(
            "border-color: cyan; border-style: dashed; border-width: 2px; color:white"
        )
        self.setPalette(self.pallette)
        self.checkbox_opcion_spam_uno_a_uno = QCheckBox(
            "REALIZAR SPAM UNO A UN CORREO")
        self.checkbox_opcion_spam_uno_a_uno.clicked.connect(
            self.spam_uno_a_uno)
        self.checkbox_opcion_spam_uno_a_varios = QCheckBox(
            "REALIZAR SPAM UNO A VARIOS COOREOS")
        self.checkbox_opcion_spam_uno_a_varios.clicked.connect(
            self.spam_uno_a_varios)

        self.label_user_email = QLabel("Ingresa tu direccion de correo: ")
        self.label_user_email.setFont(QFont("Times", 14, QFont.Bold, True))
        self.label_user_email.setStyleSheet("color: white")

        self.line_edit_user_email = QLineEdit(self)
        self.line_edit_user_email.setPlaceholderText(
            "Ingresa tu direccion de cooreo:")
        self.line_edit_user_email.setFont(QFont("Times", 14, QFont.Bold, True))
        self.line_edit_user_email.setStyleSheet("color : black")

        self.label_user_password = QLabel("Ingresa tu password de correo")
        self.label_user_password.setFont(QFont("Times", 14, QFont.Bold, True))
        self.label_user_password.setStyleSheet("color: white")

        self.line_edit_user_password = QLineEdit(self)
        self.line_edit_user_password.setPlaceholderText(
            "Ingresa tu password de tu correo:")
        self.line_edit_user_password.setFont(
            QFont("Times", 14, QFont.Bold, True))
        self.line_edit_user_password.setStyleSheet("color : black")

        self.label_victima_email = QLabel(
            "Ingresa direccion de correo destinatario: ")
        self.label_victima_email.setFont(QFont("Times", 14, QFont.Bold, True))
        self.label_victima_email.setStyleSheet("color: white")

        self.line_edit_victima_email = QLineEdit(self)
        self.line_edit_victima_email.setPlaceholderText(
            "Ingresa tu direccion de cooreo:")
        self.line_edit_victima_email.setFont(
            QFont("Times", 14, QFont.Bold, True))
        self.line_edit_victima_email.setStyleSheet("color : black")

        self.label_user_asunto = QLabel("Ingresa el asunto ")
        self.label_user_asunto.setFont(QFont("Times", 14, QFont.Bold, True))
        self.label_user_asunto.setStyleSheet("color: white")

        self.line_edit_user_asunto = QLineEdit(self)
        self.line_edit_user_asunto.setPlaceholderText(
            "Ingresa tu direccion de cooreo:")
        self.line_edit_user_asunto.setFont(QFont("Times", 14, QFont.Bold,
                                                 True))
        self.line_edit_user_asunto.setStyleSheet("color : black")

        self.pushbutton_enviar = QPushButton("ENVIAR SPAM")
        self.pushbutton_enviar.setFont(QFont("Times", 14, QFont.Bold, True))
        self.pushbutton_enviar.setPalette(self.pallette)

        self.label_estado_proceso = QLabel("")
        self.label_estado_proceso.setText("ESPERANDO DATOS....")

        self.label_proceso = QLabel("Estado verificacion email y password")
        self.label_proceso_verificacion = QLabel("")
        self.label_proceso2 = QLabel(
            "Estado verificacion arroba de email origen y destino")
        self.label_proceso_verificacion2 = QLabel("")
        self.label_proceso3 = QLabel(
            "Estado verificacion terminacion correo origen y destino")
        self.label_proceso_verificacion3 = QLabel("")
        self.combobox_dominios_from = QComboBox()
        self.combobox_dominios_from.addItem("Gmail.com")
        self.combobox_dominios_from.insertSeparator(1)
        self.combobox_dominios_from.addItem("Outlook.com")
        self.combobox_dominios_from.addItem("Outlook.es")
        self.combobox_dominios_from.insertSeparator(4)
        self.combobox_dominios_from.addItem("Hotmail.com")
        self.combobox_dominios_from.addItem("Hotmail.es")
        self.combobox_dominios_from.addItem("Hotmail.mx")
        self.combobox_dominios_from.setStyleSheet(
            "background-color:black; color: cyan")

        self.combobox_dominios_to = QComboBox()
        self.combobox_dominios_to.addItem("Gmail.com")
        self.combobox_dominios_to.insertSeparator(1)
        self.combobox_dominios_to.addItem("Outlook.com")
        self.combobox_dominios_to.addItem("Outlook.es")
        self.combobox_dominios_to.insertSeparator(4)
        self.combobox_dominios_to.addItem("Hotmail.com")
        self.combobox_dominios_to.addItem("Hotmail.es")
        self.combobox_dominios_to.addItem("Hotmail.mx")
        self.combobox_dominios_to.setStyleSheet(
            "background-color:black; color: cyan")

        self.combobox_dominios_from.currentTextChanged.connect(
            self.verificar_opcion)
        self.combobox_dominios_to.currentTextChanged.connect(
            self.verificar_opcion)
        self.pushbutton_enviar.clicked.connect(self.presionado)

        self.layout_main = QHBoxLayout(self)

        self.layout_principal = QVBoxLayout()
        self.layout_principal.addWidget(self.label_user_email)
        self.layout_principal.addWidget(self.line_edit_user_email)
        self.layout_principal.addWidget(self.label_user_password)
        self.layout_principal.addWidget(self.line_edit_user_password)
        self.layout_principal.addWidget(self.label_victima_email)
        self.layout_principal.addWidget(self.line_edit_victima_email)
        self.layout_principal.addWidget(self.label_user_asunto)
        self.layout_principal.addWidget(self.line_edit_user_asunto)
        self.layout_principal.addWidget(self.checkbox_opcion_spam_uno_a_uno)
        self.layout_principal.addWidget(self.checkbox_opcion_spam_uno_a_varios)
        self.layout_principal.addWidget(self.pushbutton_enviar)
        self.layout_principal.addWidget(self.label_estado_proceso)
        self.layout_principal.addWidget(self.combobox_dominios_from)
        self.layout_principal.addWidget(self.combobox_dominios_to)
        self.layout_principal.addWidget(self.label_proceso)
        self.layout_principal.addWidget(self.label_proceso_verificacion)
        self.layout_principal.addWidget(self.label_proceso2)
        self.layout_principal.addWidget(self.label_proceso_verificacion2)
        self.layout_principal.addWidget(self.label_proceso3)
        self.layout_principal.addWidget(self.label_proceso_verificacion3)

        self.layout_secundario = QVBoxLayout()
        self.textarea_proceso = QTextEdit()
        self.textarea_proceso.setPlaceholderText("ESPERANDO PARA PROCESAR")
        self.textarea_proceso.setFont(QFont("Times", 14, QFont.Bold, True))
        self.textarea_proceso.setGeometry(0, 0, 500, 400)
        self.textarea_proceso.setStyleSheet(
            "color: yellow; background-color:black; border-style:solid")
        self.textarea_proceso.setReadOnly(False)
        self.textarea_proceso.setOverwriteMode(QTextEdit.WidgetWidth)
        self.layout_secundario.addWidget(self.textarea_proceso)

        self.layout_main.addLayout(self.layout_principal, 4)
        self.layout_main.addLayout(self.layout_secundario, 4)

    def inciar_hilo_principal(self):
        self.incializa_hilos()[0].start()
        return 0
Exemple #6
0
class ReTestButton(QWidget):
    def __init__(self, win_idx: int, top_item: ResultTreeWidgetItem):
        super().__init__()
        self.top_item = top_item
        self.win_idx = win_idx

        self.retest_bn = QPushButton(clicked=self.handle_click)
        self.retest_bn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

        self.step = QThread()
        self.step.start()
        self.step.quit()

        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addStretch(1)
        layout.addWidget(self.retest_bn)

        self.setLayout(layout)

    def handle_click(self, checked):
        if self.step.isFinished():
            self.step = step.Step(self.win_idx, self.top_item.tag)
            self.step.sig_item_result.connect(self.handle_item_result)
            self.step.sig_child_item.connect(self.handle_child_item)
            self.step.sig_finish.connect(self.handle_finish)
            self.retest_bn.setIcon(self.style().standardIcon(
                QStyle.SP_MediaStop))
            self.top_item.takeChildren()
            self.top_item.setIcon(0, QIcon())
            self.top_item.setText(0, self.top_item.tag)
            self.step.start()
            for idx in range(self.top_item.columnCount()):
                self.top_item.setBackground(idx, Config.COLOR_GRAY)
        elif self.step.isRunning():
            self.step.stop()

    def handle_finish(self, sec):
        origin_text = self.top_item.text(0)
        self.top_item.setText(0, '{} 测试时长:{:.2f}s'.format(origin_text, sec))
        self.retest_bn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

    def handle_child_item(self, child_item: mix.ChildItem):
        child_widget = QTreeWidgetItem(self.top_item)
        child_widget.setText(0, child_item.tag)
        child_widget.setText(1, child_item.msg)

        if child_item.result:
            bcolor = Config.COLOR_GREEN
            icon = QIcon(Config.PASS_IMG)
        else:
            bcolor = Config.COLOR_RED
            icon = QIcon(Config.FAIL_IMG)

        child_widget.setIcon(0, icon)
        # for i in range(child_widget.columnCount()):
        #     child_widget.setBackground(i, bcolor)

        self.top_item.setExpanded(True)

    def handle_item_result(self, item_result: mix.ItemResult):
        if item_result.result:
            bcolor = Config.COLOR_GREEN
            icon = QIcon(Config.PASS_IMG)
        else:
            bcolor = Config.COLOR_RED
            icon = QIcon(Config.FAIL_IMG)

        self.top_item.setIcon(0, icon)
        self.top_item.setText(
            0, '{}[{}]'.format(item_result.tag, item_result.total))
        for i in range(self.top_item.columnCount()):
            self.top_item.setBackground(i, bcolor)
Exemple #7
0
class TestUnitFrame(QFrame):
    def __init__(self, win_idx: int):
        super().__init__()
        self.setFrameStyle(QFrame.StyledPanel)
        self.setAutoFillBackground(True)

        self.script = QThread()
        self.script.start()
        self.script.quit()

        self.win_idx = win_idx

        #COM Label
        self.com_label = QLabel('COM')
        #状态Label
        self.status_label = QLabel('状态')
        #异常信息
        self.error_label = QLabel('')
        self.error_label.setStyleSheet('.QLabel {color:#A57A80;}')

        leftLayout = QVBoxLayout()
        leftLayout.setSpacing(5)
        leftLayout.addWidget(self.com_label)
        leftLayout.addWidget(self.status_label)
        leftLayout.addWidget(self.error_label)
        leftLayout.addStretch(1)

        ####显示关键信息
        self.productmodel_label = QLabel('')
        self.softversoin_label = QLabel('')
        self.chipid_label = QLabel('')

        #测试按钮
        self.start_button = QPushButton('测试', clicked=self.onUnitTest)
        self.start_button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))

        rightLayout = QVBoxLayout()
        rightLayout.addWidget(self.productmodel_label)
        rightLayout.addWidget(self.softversoin_label)
        rightLayout.addWidget(self.chipid_label)

        rightLayout.addStretch(1)
        rightLayout.addWidget(self.start_button)
        rightLayout.setAlignment(self.start_button, Qt.AlignRight)

        #总布局
        mainlayout = QHBoxLayout()
        mainlayout.addLayout(leftLayout)
        mainlayout.addLayout(rightLayout)
        mainlayout.setStretchFactor(leftLayout, 4)
        mainlayout.setStretchFactor(rightLayout, 1)
        self.setLayout(mainlayout)
        self.devInitState()

    def devInitState(self):
        dev = Config.RC[self.win_idx]['dev']
        if dev.status == DevStatus.exception:
            self.com_label.setText('{}[error]'.format(dev.name))
            self.status_label.setText('串口异常')
            self.status_label.setStyleSheet('color: red;')
        elif dev.status == DevStatus.opened:
            self.com_label.setText('{name}[{baudrate}]'.format(**dev.settings))
            self.status_label.setStyleSheet('')
            self.status_label.setText('连接成功')

    def onUnitTest(self, checked):
        dev = Config.RC[self.win_idx]['dev']
        self.error_label.setText('')
        if self.script.isFinished(
        ) and dev.status == DevStatus.opened and Config.PRODUCT_XML:
            pub.sendMessage(Config.TOPIC_STARTTEST, win=self)
        elif Config.PRODUCT_XML is None:
            self.status_label.setStyleSheet('color: red;')
            self.status_label.setText('未选择产品型号')
        elif dev.status in [DevStatus.closed, DevStatus.exception]:
            self.status_label.setStyleSheet('color: red;')
            self.status_label.setText('串口未设置')

    def contextMenuEvent(self, event):
        menu = QMenu()
        item = menu.addAction('单元设置')
        menu.addAction('停止测试', self.onStop)
        menu.addAction('串口设置', lambda: self.onSetting(event))
        menu.addAction('断开串口', self.onDisconnect)
        menu.addAction('重连串口', self.onReconnect)

        item.setEnabled(False)
        menu.move(event.globalPos())
        menu.exec()

    def onStop(self):
        if self.script.isRunning():
            self.script.stop()

    def onSetting(self, event):
        dlg = SerialSettingDialog(self)
        dlg.move(event.globalPos())
        dev = Config.RC[self.win_idx]['dev']
        if dlg.exec():
            settings = dlg.getValue()
            dev.apply_settings(**settings)
            if dev.status == DevStatus.opened:
                dev.close()
            dev.connect()
            if dev.status == DevStatus.exception:
                self.com_label.setText('{}[error]'.format(settings['name']))
                self.status_label.setText('串口异常')
                self.status_label.setStyleSheet('color: red;')
            else:
                self.com_label.setText('{name}[{baudrate}]'.format(**settings))
                self.status_label.setStyleSheet('')
                self.status_label.setText('连接成功')
                if Config.FIXED_WIN_NUM == 2 and not Config.SCREEN_MODE:
                    tab = Config.RC['tab']
                    tab.setCurrentIndex(self.win_idx)
                    tab.setTabText(self.win_idx, settings['name'])

    def onDisconnect(self):
        dev = Config.RC[self.win_idx]['dev']
        if dev.status == DevStatus.opened:
            dev.close()
            self.status_label.setStyleSheet('')
            self.status_label.setText('断开成功')

    def onReconnect(self):
        dev = Config.RC[self.win_idx]['dev']
        if dev.status == DevStatus.closed:
            dev.connect()
            if dev.status == DevStatus.opened:
                self.status_label.setStyleSheet('')
                self.status_label.setText('重连成功')
            else:
                self.status_label.setStyleSheet('color: red;')
                self.status_label.setText('重连失败')