Example #1
0
class QBaseServer(QObject):
    """Server base for QtPyNetwork."""
    connected = Signal(Device, str, int)
    disconnected = Signal(Device)
    message = Signal(Device, bytes)
    error = Signal(Device, str)

    server_error = Signal(str)
    closed = Signal()

    def __init__(self, loggerName=None):
        super(QBaseServer, self).__init__()
        if loggerName:
            self.__logger = logging.getLogger(loggerName)
        else:
            self.__logger = logging.getLogger(self.__class__.__name__)
        self.__ip = None
        self.__port = None

        self.__devices = []
        self.__deviceModel = Device

        self.__handler = None
        self.__handler_thread = None
        self.__handlerClass = None
        self.__server = None

    @Slot(str, int, bytes)
    def start(self, ip: str, port: int):
        """Start server."""
        if self.__handlerClass:
            ip = QHostAddress(ip)
            self.__ip = ip
            self.__port = port
            self.__handler = self.__handlerClass()
            self.__handler_thread = QThread()
            self.__handler.moveToThread(self.__handler_thread)

            self.__handler.connected.connect(
                self.__on_handler_successful_connection)
            self.__handler.message.connect(self.__on_handler_device_message)
            self.__handler.error.connect(self.__on_handler_device_error)
            self.__handler.disconnected.connect(
                self.__on_handler_device_disconnected)
            self.__handler.closed.connect(self.on_closed)

            self.__handler_thread.started.connect(self.__handler.start)
            self.__handler.started.connect(self.__setup_server)
            self.__handler_thread.start()
        else:
            raise Exception("Handler class not set!")

    @Slot()
    def __setup_server(self):
        """Create QTCPServer, start listening for connections."""
        self.__server = TCPServer()
        self.__server.connection.connect(self.__handler.on_incoming_connection)
        if self.__server.listen(self.__ip, self.__port):
            self.__logger.info("Started listening for connections")
        else:
            e = self.__server.errorString()
            self.__logger.error(e)
            self.server_error.emit(e)

    @Slot(int, str, int)
    def __on_handler_successful_connection(self, device_id, ip, port):
        """When client connects to server successfully."""
        device = self.__deviceModel(self, device_id, ip, port)
        self.__devices.append(device)
        self.__logger.info("Added new CLIENT-{} with address {}:{}".format(
            device_id, ip, port))
        self.on_connected(device, ip, port)

    @Slot(int, bytes)
    def __on_handler_device_message(self, device_id: int, message: bytes):
        """When server receives message from bot."""
        self.on_message(self.get_device_by_id(device_id), message)

    @Slot(int)
    def __on_handler_device_disconnected(self, device_id):
        """When bot disconnects from server."""
        device = self.get_device_by_id(device_id)
        device.set_connected(False)
        if device in self.__devices:
            self.__devices.remove(device)
        self.on_disconnected(device)

    @Slot(int, str)
    def __on_handler_device_error(self, device_id, error):
        self.on_error(self.get_device_by_id(device_id), error)

    @Slot(Device, str, int)
    def on_connected(self, device: Device, ip: str, port: int):
        """Called when new client connects to server.
        Emits connected signal.

        Args:
            device (Device): Device object.
            ip (str): Client ip address.
            port (int): Client port.
        """
        self.connected.emit(device, ip, port)

    @Slot(Device, bytes)
    def on_message(self, device: Device, message: bytes):
        """Called when server receives message from client.
        Emits message signal.

        Args:
            device (Device): Message sender.
            message (bytes): Message.
        """
        self.message.emit(device, message)

    @Slot(Device)
    def on_disconnected(self, device: Device):
        """Called when device disconnects from server.
        Emits disconnected signal.

        Args:
            device (Device): Disconnected device.
        """
        self.disconnected.emit(device)

    @Slot(Device, str)
    def on_error(self, device: Device, error: str):
        """Called when a socket error occurs.
        Emits error signal.

        Args:
            device (Device): Device object.
            error (str): Error string.
        """
        self.error.emit(device, error)

    @Slot()
    def on_closed(self):
        self.closed.emit()

    @Slot(Device, bytes)
    def write(self, device: Device, data: bytes):
        """Write data to device."""
        if not self.__server or not self.__handler:
            raise ServerNotRunning("Server is not running")
        if not device.is_connected():
            raise NotConnectedError("Client is not connected")
        self.__handler.write.emit(device.id(), data)

    @Slot(bytes)
    def write_all(self, data: bytes):
        """Write data to all devices."""
        if not self.__server or not self.__handler:
            raise ServerNotRunning("Server is not running")
        self.__handler.write_all.emit(data)

    @Slot()
    def kick(self, device: Device):
        """Disconnect device from server."""
        if not self.__server or not self.__handler:
            raise ServerNotRunning("Server is not running")
        if not device.is_connected():
            raise NotConnectedError("Client is not connected")
        self.__handler.kick.emit(device.id())

    @Slot()
    def close(self):
        """Disconnect clients and close server."""
        self.__logger.info("Closing server...")
        if self.__server:
            self.__server.close()
        if self.__handler:
            self.__handler.close()
            self.__handler_thread.quit()

    def set_device_model(self, model):
        """Set model to use for device when client connects.

        Note:
            Model should be subclassing Device.
        """
        if self.is_running():
            raise Exception("Set device model before starting server!")

        if not issubclass(model, Device):
            raise ValueError("Model should be subclassing Device class.")

        try:
            model(QBaseServer(), 0, "127.0.0.1", 5000)
        except TypeError as e:
            raise TypeError(
                "Model is not valid class! Exception: {}".format(e))

        self.__deviceModel = model

    def is_running(self):
        """Check if server is running."""
        if self.__handler_thread:
            return self.__handler_thread.isRunning()
        return False

    def wait(self):
        """Wait for server thread to close."""
        if self.__handler_thread:
            return self.__handler_thread.wait()
        return True

    @Slot(int)
    def get_device_by_id(self, device_id: int) -> Device:
        """Returns device with associated ID.

        Args:
            device_id (int): Device ID.
        """
        for device in self.__devices:
            if device.id() == device_id:
                return device
        raise Exception("CLIENT-{} not found".format(device_id))

    def get_devices(self):
        """Returns list with devices."""
        return self.__devices

    def set_handler_class(self, handler):
        """Set handler to use. This should not be used
        outside this library."""
        if self.is_running():
            raise Exception("Set socket handler before starting server!")
        try:
            handler()
        except TypeError as e:
            raise TypeError(
                "Handler is not valid class! Exception: {}".format(e))
        self.__handlerClass = handler
Example #2
0
class GistWindow(QDialog):
    def __init__(self):
        super(GistWindow, self).__init__()

        self.thread = None
        self.worker = None
        self.gistToken = config.gistToken
        self.connected = False

        self.setWindowTitle("Gist")
        self.setMinimumWidth(380)
        self.layout = QVBoxLayout()

        self.testStatus = QLabel("")
        self.layout.addWidget(self.testStatus)

        self.layout.addWidget(QLabel("Gist Token"))
        self.gistTokenInput = QLineEdit()
        self.gistTokenInput.setText(self.gistToken)
        self.gistTokenInput.setMaxLength(40)
        self.gistTokenInput.textChanged.connect(self.enableButtons)
        self.layout.addWidget(self.gistTokenInput)

        self.testButton = QPushButton("Test Connection")
        self.testButton.setEnabled(False)
        self.testButton.clicked.connect(self.checkStatus)
        self.layout.addWidget(self.testButton)

        # self.syncHighlightsButton = QPushButton("Synch Highlights")
        # self.syncHighlightsButton.setEnabled(False)
        # self.syncHighlightsButton.clicked.connect(self.syncHighlights)
        # self.layout.addWidget(self.syncHighlightsButton)

        self.syncBibleNotesButton = QPushButton("Synch Bibles Notes")
        self.syncBibleNotesButton.setEnabled(False)
        self.syncBibleNotesButton.clicked.connect(self.syncBibleNotes)
        self.layout.addWidget(self.syncBibleNotesButton)

        buttons = QDialogButtonBox.Ok
        self.buttonBox = QDialogButtonBox(buttons)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.accepted.connect(self.stopSync)
        self.layout.addWidget(self.buttonBox)
        self.setLayout(self.layout)

        self.enableButtons()
        self.checkStatus()

    def enableButtons(self):
        if len(self.gistTokenInput.text()) >= 40:
            self.testButton.setEnabled(True)
        else:
            self.testButton.setEnabled(False)
            self.connected = False
            self.setStatus("Not connected", False)
        if self.connected:
            self.testButton.setEnabled(False)
            self.syncBibleNotesButton.setEnabled(True)
        else:
            self.syncBibleNotesButton.setEnabled(False)

    def checkStatus(self):
        if len(self.gistTokenInput.text()) < 40:
            self.setStatus("Not connected", False)
            self.connected = False
        else:
            self.gh = GitHubGist(self.gistTokenInput.text())
            if self.gh.connected:
                self.setStatus("Connected to " + self.gh.user.name, True)
                self.connected = True
                config.gistToken = self.gistTokenInput.text()
            else:
                self.setStatus("Not connected", False)
                self.connected = False
        self.enableButtons()

    def setStatus(self, message, connected=True):
        self.testStatus.setText("Status: " + message)
        if connected:
            self.testStatus.setStyleSheet("color: rgb(128, 255, 7);")
        else:
            self.testStatus.setStyleSheet("color: rgb(253, 128, 8);")
        QApplication.processEvents()

    def syncBibleNotes(self):
        self.setStatus("Syncing ...", True)
        self.syncBibleNotesButton.setEnabled(False)

        self.thread = QThread()
        self.worker = SyncNotesWithGist()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.worker.deleteLater)
        self.worker.finished.connect(self.syncCompleted)
        self.worker.progress.connect(self.setStatus)
        self.thread.start()

    def syncCompleted(self, count):
        self.setStatus("Done! Processed {0} notes".format(count), True)

    def stopSync(self):
        if self.thread and self.thread.isRunning():
            self.thread.quit()
Example #3
0
class PMGIpythonConsole(RichJupyterWidget):
    def __init__(self, *args, **kwargs):
        super(PMGIpythonConsole, self).__init__(*args, **kwargs)
        self.is_first_execution = True
        self.confirm_restart = False

        self.commands_pool = []
        self.command_callback_pool: Dict[str, Callable] = {}

    def change_ui_theme(self, style: str):
        """
        改变界面主题颜色
        :param style:
        :return:
        """
        style = style.lower()
        if style == 'fusion':
            self.style_sheet = default_light_style_sheet
            self.syntax_style = default_light_syntax_style

        elif style == 'qdarkstyle':
            self.style_sheet = default_dark_style_sheet
            self.syntax_style = default_dark_syntax_style

        elif style.lower() == 'windowsvista':
            self.style_sheet = default_light_style_sheet
            self.syntax_style = default_light_syntax_style

        elif style.lower() == 'windows':
            self.style_sheet = default_light_style_sheet
            self.syntax_style = default_light_syntax_style

    def _handle_kernel_died(self, since_last_heartbit):
        self.is_first_execution = True
        self.restart_kernel(None, True)
        self.initialize_ipython_builtins()
        self.execute_command('')
        return True

    def _handle_execute_input(self, msg):
        super()._handle_execute_result(msg)

    def setup_ui(self):
        self.kernel_manager = None
        self.kernel_client = None
        # initialize by thread
        self.init_thread = QThread(self)
        self.console_object = ConsoleInitThread()
        self.console_object.moveToThread(self.init_thread)
        self.console_object.initialized.connect(self.slot_initialized)
        self.init_thread.finished.connect(self.console_object.deleteLater)
        self.init_thread.finished.connect(self.init_thread.deleteLater)
        self.init_thread.started.connect(self.console_object.run)
        self.init_thread.start()
        cursor: QTextCursor = self._prompt_cursor
        cursor.movePosition(QTextCursor.End)

    def _context_menu_make(self, pos: 'QPoint') -> QMenu:
        menu = super(PMGIpythonConsole, self)._context_menu_make(pos)
        _translate = QCoreApplication.translate
        trans_dic = {
            'Cut': _translate("PMGIpythonConsole", 'Cut'),
            'Copy': _translate("PMGIpythonConsole", 'Copy'),
            'Copy (Raw Text)': _translate("PMGIpythonConsole",
                                          'Copy(Raw Text)'),
            'Paste': _translate("PMGIpythonConsole", 'Paste'),
            'Select All': _translate("PMGIpythonConsole", 'Select All'),
            'Save as HTML/XML': _translate("PMGIpythonConsole",
                                           'Save as HTML/XML'),
            'Print': _translate("PMGIpythonConsole", 'Print')
        }
        for action in menu.actions():
            trans = trans_dic.get(action.text())
            trans = trans if trans is not None else action.text()
            action.setText(trans)
        restart_action = menu.addAction(
            _translate("PMGIpythonConsole", 'Restart'))
        restart_action.triggered.connect(self.slot_restart_kernel)

        stop_action = menu.addAction(
            _translate("PMGIpythonConsole", 'Interrupt'))
        # stop_action.triggered.connect(self.request_interrupt_kernel)
        stop_action.triggered.connect(self.on_interrupt_kernel)
        # stop_action.setEnabled(self._executing)

        return menu

    def on_interrupt_kernel(self):
        """
        当点击中断执行时。
        IPython会出现一个奇怪的问题——当中断执行时,可能_executing恒为False。
        因此干脆不屏蔽了。
        Returns:

        """
        self.interrupt_kernel()

    def _custom_context_menu_requested(self, pos):
        super(PMGIpythonConsole, self)._custom_context_menu_requested(pos)

    def slot_restart_kernel(self, arg):
        ret = QMessageBox.warning(self, '提示', '是否要重启控制台?\n一切变量都将被重置。',
                                  QMessageBox.Ok | QMessageBox.Cancel,
                                  QMessageBox.Cancel)
        if ret == QMessageBox.Ok:
            self._restart_kernel(arg)

    def _restart_kernel(self, arg1):

        self.is_first_execution = True
        self.restart_kernel(None, True)
        self.initialize_ipython_builtins()
        self.execute_command('')
        return True

    def slot_initialized(self, kernel_manager, kernel_client):
        """
        Args:
            kernel_manager: `qtconsole.manager.QtKernelManager`
            kernel_client: `qtconsole.manager.QtKernelManager.client`

        Returns:
        """
        self.kernel_manager = kernel_manager
        self.kernel_client = kernel_client
        self.initialize_ipython_builtins()

    def initialize_ipython_builtins(self):
        return

    def _update_list(self):
        try:
            super(PMGIpythonConsole, self)._update_list()
        except BaseException:
            import traceback
            traceback.print_exc()

    def _banner_default(self):
        """
        自定义控制台开始的文字
        Returns:
        """
        return 'Welcome To PMGWidgets Ipython Console!\n'

    def closeEvent(self, event):
        if self.init_thread.isRunning():
            self.console_object.stop()
            self.init_thread.quit()
            self.init_thread.wait(500)
        super(PMGIpythonConsole, self).closeEvent(event)

    def execute_file(self, file: str, hidden: bool = False):
        if not os.path.exists(file) or not file.endswith('.py'):
            raise FileNotFoundError(f'{file} not found or invalid')
        base = os.path.basename(file)
        cmd = os.path.splitext(base)[0]
        with open(file, 'r', encoding='utf-8') as f:
            source = f.read()

        self.execute_command(source, hidden=hidden, hint_text=cmd)

    def execute_command(self,
                        source,
                        hidden: bool = False,
                        hint_text: str = '') -> str:
        """

        :param source:
        :param hidden:
        :param hint_text: 运行代码前显示的提示
        :return: str 执行命令的 msgid
        """
        cursor: QTextCursor = self._prompt_cursor
        cursor.movePosition(QTextCursor.End)
        # 运行文件时,显示文件名,无换行符,执行选中内容时,包含换行符
        # 检测换行符,在ipy console中显示执行脚本内容
        hint_row_list = hint_text.split("\n")
        for hint in hint_row_list:
            if hint != "":
                cursor.insertText('%s\n' % hint)
                self._insert_continuation_prompt(cursor)
        else:
            # 删除多余的continuation_prompt
            self.undo()

        self._finalize_input_request(
        )  # display input string buffer in console.
        cursor.movePosition(QTextCursor.End)
        if self.kernel_client is None:
            self.commands_pool.append((source, hidden, hint_text))
            return ''
        else:
            return self.pmexecute(source, hidden)

    def _handle_stream(self, msg):
        parent_header = msg.get('parent_header')
        if parent_header is not None:
            msg_id = parent_header.get(
                'msg_id')  # 'fee0bee5-074c00d093b1455be6d166b1_10'']
            if msg_id in self.command_callback_pool.keys():
                callback = self.command_callback_pool.pop(msg_id)
                assert callable(callback)
                callback()
        cursor: QTextCursor = self._prompt_cursor
        cursor.movePosition(QTextCursor.End)
        super()._handle_stream(msg)

    def append_stream(self, text):
        """重写的方法。原本before_prompt属性是False。"""
        self._append_plain_text(text, before_prompt=False)

    def pmexecute(self, source: str, hidden: bool = False) -> str:
        """
        执行代码并且返回Msgid
        :param source:
        :param hidden:
        :return:
        """
        is_legal, msg = self.is_source_code_legal(source)
        if not is_legal:
            QMessageBox.warning(self, '警告', msg)
            source = ''
        msg_id = self.kernel_client.execute(source, hidden)
        self._request_info['execute'][msg_id] = self._ExecutionRequest(
            msg_id, 'user')
        self._hidden = hidden
        if not hidden:
            self.executing.emit(source)
        return msg_id
        # super()._execute(source, hidden)

    def is_source_code_legal(self, source_code: str) -> Tuple[bool, str]:
        """判断注入到shell中的命令是否合法。

        如果命令不合法,应当避免执行该命令。

        Args:
            source_code: 注入到shell中的命令。

        Returns:
            * 是否合法;
            * 如不合法,返回原因;如合法,返回空字符串。

        """
        return True, ''

    @staticmethod
    def install_translator():
        global _trans
        app = QApplication.instance()
        assert app is not None
        _trans = QTranslator()
        _trans.load(QLocale.system(),
                    'qt_zh_CN.qm',
                    directory=os.path.join(os.path.dirname(__file__),
                                           'translations'))
        app.installTranslator(_trans)
class DownloadBibleMp3Dialog(QDialog):
    def __init__(self, parent):
        super().__init__()

        self.bibles = {
            "BBE (British accent)":
            ("BBE", "otseng/UniqueBible_MP3_BBE_british", "british"),
            "KJV (American accent)":
            ("KJV", "otseng/UniqueBible_MP3_KJV", "default"),
            "KJV (American soft music)":
            ("KJV", "otseng/UniqueBible_MP3_KJV_soft_music", "soft-music"),
            "NHEB (Indian accent)":
            ("NHEB", "otseng/UniqueBible_MP3_NHEB_indian", "indian"),
            "WEB (American accent)": ("WEB", "otseng/UniqueBible_MP3_WEB",
                                      "default"),
            "CUV (Chinese)": ("CUV", "otseng/UniqueBible_MP3_CUV", "default"),
            "HHBD (Hindi)": ("HHBD", "otseng/UniqueBible_MP3_HHBD", "default"),
            "RVA (Spanish)": ("RVA", "otseng/UniqueBible_MP3_RVA", "default"),
            "TR (Modern Greek)": ("TR", "otseng/UniqueBible_MP3_TR", "modern"),
        }
        self.parent = parent
        self.setWindowTitle(config.thisTranslation["gitHubBibleMp3Files"])
        self.setMinimumSize(150, 450)
        self.selectedRendition = None
        self.selectedText = None
        self.selectedRepo = None
        self.selectedDirectory = None
        self.settingBibles = False
        self.thread = None
        self.setupUI()

    def setupUI(self):
        mainLayout = QVBoxLayout()

        title = QLabel(config.thisTranslation["gitHubBibleMp3Files"])
        mainLayout.addWidget(title)

        self.versionsLayout = QVBoxLayout()
        self.renditionsList = QListWidget()
        self.renditionsList.itemClicked.connect(self.selectItem)
        for rendition in self.bibles.keys():
            self.renditionsList.addItem(rendition)
        self.renditionsList.setMaximumHeight(100)
        self.versionsLayout.addWidget(self.renditionsList)
        mainLayout.addLayout(self.versionsLayout)

        self.downloadTable = QTableView()
        self.downloadTable.setEnabled(False)
        self.downloadTable.setFocusPolicy(Qt.StrongFocus)
        self.downloadTable.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.downloadTable.setSortingEnabled(True)
        self.dataViewModel = QStandardItemModel(self.downloadTable)
        self.downloadTable.setModel(self.dataViewModel)
        mainLayout.addWidget(self.downloadTable)

        buttonsLayout = QHBoxLayout()
        selectAllButton = QPushButton(config.thisTranslation["selectAll"])
        selectAllButton.setFocusPolicy(Qt.StrongFocus)
        selectAllButton.clicked.connect(self.selectAll)
        buttonsLayout.addWidget(selectAllButton)
        selectNoneButton = QPushButton(config.thisTranslation["selectNone"])
        selectNoneButton.setFocusPolicy(Qt.StrongFocus)
        selectNoneButton.clicked.connect(self.selectNone)
        buttonsLayout.addWidget(selectNoneButton)
        otButton = QPushButton("1-39")
        otButton.setFocusPolicy(Qt.StrongFocus)
        otButton.clicked.connect(self.selectOT)
        buttonsLayout.addWidget(otButton)
        ntButton = QPushButton("40-66")
        ntButton.setFocusPolicy(Qt.StrongFocus)
        ntButton.clicked.connect(self.selectNT)
        buttonsLayout.addWidget(ntButton)
        # buttonsLayout.addStretch()
        mainLayout.addLayout(buttonsLayout)

        self.downloadButton = QPushButton(config.thisTranslation["download"])
        self.downloadButton.setFocusPolicy(Qt.StrongFocus)
        self.downloadButton.setAutoDefault(True)
        self.downloadButton.setFocus()
        self.downloadButton.clicked.connect(self.download)
        mainLayout.addWidget(self.downloadButton)

        self.status = QLabel("")
        mainLayout.addWidget(self.status)

        buttonLayout = QHBoxLayout()
        self.closeButton = QPushButton(config.thisTranslation["close"])
        self.closeButton.setFocusPolicy(Qt.StrongFocus)
        self.closeButton.clicked.connect(self.closeDialog)
        buttonLayout.addWidget(self.closeButton)
        mainLayout.addLayout(buttonLayout)

        self.setLayout(mainLayout)

        self.renditionsList.item(0).setSelected(True)
        bible = self.renditionsList.item(0).text()
        self.selectRendition(bible)

        self.downloadButton.setDefault(True)
        QTimer.singleShot(0, self.downloadButton.setFocus)

    def selectItem(self, item):
        self.selectRendition(item.text())

    def selectRendition(self, rendition):
        from util.GithubUtil import GithubUtil

        self.selectedRendition = rendition
        self.downloadTable.setEnabled(True)
        self.selectedText, self.selectedRepo, self.selectedDirectory = self.bibles[
            self.selectedRendition]
        self.github = GithubUtil(self.selectedRepo)
        self.repoData = self.github.getRepoData()
        self.settingBibles = True
        self.dataViewModel.clear()
        rowCount = 0
        for file in self.repoData.keys():
            if len(str(file)) > 3:
                engFullBookName = file[3:]
            else:
                engFullBookName = BibleBooks().eng[str(int(file))][1]
            item = QStandardItem(file[:3].strip())
            folder = os.path.join("audio", "bibles", self.selectedText,
                                  self.selectedDirectory, file)
            folderWithName = os.path.join("audio", "bibles", self.selectedText,
                                          self.selectedDirectory,
                                          file + " " + engFullBookName)
            if os.path.exists(folder) or os.path.exists(folderWithName):
                item.setCheckable(False)
                item.setCheckState(Qt.Unchecked)
                item.setEnabled(False)
            else:
                item.setCheckable(True)
                item.setCheckState(Qt.Checked)
                item.setEnabled(True)
            self.dataViewModel.setItem(rowCount, 0, item)
            item = QStandardItem(engFullBookName)
            self.dataViewModel.setItem(rowCount, 1, item)
            if os.path.exists(folder) or os.path.exists(folderWithName):
                item = QStandardItem("Installed")
                self.dataViewModel.setItem(rowCount, 2, item)
            else:
                item = QStandardItem("")
                self.dataViewModel.setItem(rowCount, 2, item)
            rowCount += 1
        self.dataViewModel.setHorizontalHeaderLabels([
            config.thisTranslation["menu_book"],
            config.thisTranslation["name"], ""
        ])
        self.downloadTable.setColumnWidth(0, 90)
        self.downloadTable.setColumnWidth(1, 125)
        self.downloadTable.setColumnWidth(2, 125)
        # self.downloadTable.resizeColumnsToContents()
        self.settingBibles = False

    def selectAll(self):
        for index in range(self.dataViewModel.rowCount()):
            item = self.dataViewModel.item(index)
            if item.isEnabled():
                item.setCheckState(Qt.Checked)

    def selectNone(self):
        for index in range(self.dataViewModel.rowCount()):
            item = self.dataViewModel.item(index)
            item.setCheckState(Qt.Unchecked)

    def selectOT(self):
        for index in range(self.dataViewModel.rowCount()):
            item = self.dataViewModel.item(index)
            bookNum = int(item.text())
            if bookNum <= 39:
                if item.isEnabled():
                    item.setCheckState(Qt.Checked)
                else:
                    item.setCheckState(Qt.Unchecked)
            else:
                item.setCheckState(Qt.Unchecked)

    def selectNT(self):
        for index in range(self.dataViewModel.rowCount()):
            item = self.dataViewModel.item(index)
            bookNum = int(item.text())
            if bookNum >= 40:
                if item.isEnabled():
                    item.setCheckState(Qt.Checked)
                else:
                    item.setCheckState(Qt.Unchecked)
            else:
                item.setCheckState(Qt.Unchecked)

    def download(self):
        self.downloadButton.setEnabled(False)
        self.setStatus(config.thisTranslation["message_installing"])
        self.closeButton.setEnabled(False)
        folder = os.path.join("audio", "bibles")
        if not os.path.exists(folder):
            os.mkdir(folder)
        folder = os.path.join("audio", "bibles", self.selectedText)
        if not os.path.exists(folder):
            os.mkdir(folder)
        folder = os.path.join("audio", "bibles", self.selectedText,
                              self.selectedDirectory)
        if not os.path.exists(folder):
            os.mkdir(folder)
        self.thread = QThread()
        self.worker = DownloadFromGitHub(self.github, self.repoData,
                                         self.dataViewModel, self.selectedText,
                                         self.selectedDirectory)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.worker.deleteLater)
        self.worker.finished.connect(self.finishedDownloading)
        self.worker.progress.connect(self.setStatus)
        self.thread.start()

    def finishedDownloading(self, count):
        self.selectRendition(self.selectedRendition)
        self.setStatus("")
        self.downloadButton.setEnabled(True)
        self.closeButton.setEnabled(True)
        if count > 0:
            self.parent.displayMessage(
                config.thisTranslation["message_installed"])

    def setStatus(self, message):
        self.status.setText(message)
        QApplication.processEvents()

    def closeDialog(self):
        if self.thread:
            if self.thread.isRunning():
                self.thread.quit()
        self.close()
Example #5
0
class Runner:
    """
    init -> register -> start
    init -> start -> register
    """
    def __init__(self, task_cls: Type[Task], *args, **kwargs):
        super().__init__()

        self.task: Optional[Task] = None
        self.thread: Optional[QThread] = None

        self._started = False

        self.task_cls = task_cls
        self._args = args
        self._kwargs = kwargs

    def init(self) -> None:
        if self._started:
            raise RuntimeError("Cannot init runner when task already started!")

        self.thread = QThread()

        self.task = self.task_cls(*self._args, **self._kwargs)
        self.task.moveToThread(self.thread)
        self.task.finished.connect(self.task.deleteLater)
        self.task.finished.connect(self.thread.quit)
        self.task.failed.connect(self.fail)
        self.thread.started.connect(
            self.task.execute)  # lambdas don't work here hm
        self.thread.finished.connect(self.thread.deleteLater)

    def register(self,
                 *,
                 started: Callable = None,
                 finished: Callable = None,
                 progress: Callable = None,
                 failed: Callable = None,
                 result: Callable = None) -> None:
        if started is not None:
            self.task.started.connect(started)
        if finished is not None:
            self.task.finished.connect(finished)
        if progress is not None:
            self.task._progressThrottled.connect(progress)
        if failed is not None:
            self.task.failed.connect(failed)
        if result is not None:
            self.task.result.connect(result)

    def start(self) -> None:
        self._started = True
        self.thread.start()

    def stop(self) -> None:
        if self.task is not None:
            self.task.stop()

        if self.thread is not None:
            try:
                self.thread.quit()
                self.thread.wait()
            except RuntimeError:
                pass

        self._started = False

    def fail(self, e: Exception) -> None:
        raise e

    @property
    def is_running(self) -> bool:
        """
        Without this try-except block, Qt throws an error saying that the thread has
        already been deleted, even if `self.thread` is not None.
        """
        is_running = False
        try:
            if self.thread is not None:
                is_running = self.thread.isRunning()
        except RuntimeError:
            pass

        return is_running
Example #6
0
class ProcessConsole(QTextEdit):
    signal_stop_qthread = Signal()
    signal_process_stopped = Signal()
    signal_process_started = Signal()

    insert_mode = ''

    def __init__(self, args: list = None):
        super().__init__()
        self._is_running = False
        self.auto_scroll = True
        self.args = args  #
        self.setContentsMargins(20, 20, 0, 0)
        self.monitor_thread: 'ProcessMonitorThread' = None
        self.out_thread: 'QThread' = None

    def set_args(self, args: list = None):
        self.args = args

    def is_running(self) -> bool:
        """
        返回进程是否在运行
        Returns:

        """
        if self.monitor_thread is not None:
            process = self.get_subprocess()
            if process is not None:
                if process.poll() is None:
                    return True
        return False

    def start_process(self):
        if not self.is_running():
            self.out_thread = QThread(self)
            self.monitor_thread = ProcessMonitorThread()
            self.monitor_thread.args = self.args
            self.monitor_thread.moveToThread(self.out_thread)

            self.out_thread.started.connect(self.monitor_thread.run)
            self.out_thread.start()

            self.monitor_thread.on_out.connect(self.on_stdout)
            self.monitor_thread.on_err.connect(self.on_stderr)

            self.signal_stop_qthread.connect(self.monitor_thread.stop)

            self.out_thread.finished.connect(self.out_thread.deleteLater)
            self.out_thread.finished.connect(self.monitor_thread.deleteLater)

            self.monitor_thread.on_finished.connect(self.terminate_process)

    def on_stdout(self, text):
        if self.insert_mode == 'error':
            self.insertHtml('<p style="color:black;">' + '========' +
                            '<br/></p>')
            self.insert_mode = 'stdout'

        self.insertPlainText(text)
        if self.auto_scroll:
            self.ensureCursorVisible()

    def on_stderr(self, text):
        self.insert_mode = 'error'
        self.insertHtml('<p style="color:red;">' + text + '<br/></p>')
        print(text)
        if self.auto_scroll:
            self.ensureCursorVisible()

    def terminate_process(self):
        if self.monitor_thread is not None:
            self.monitor_thread.process_terminated = True
            self.monitor_thread.process.process.terminate()
            if self.out_thread.isRunning():
                self.signal_stop_qthread.emit()
                self.out_thread.quit()
                self.out_thread.wait(500)
        self.monitor_thread = None
        self.out_thread = None
        self.signal_process_stopped.emit()

    def keyPressEvent(self, e: 'QKeyEvent'):
        if e.key() == Qt.Key_Backspace or e.key() == Qt.Key_Delete:
            return
        print(e.key(), e.text())
        if e.key() == Qt.Key_Return:
            text = '\n'
        else:
            text = e.text()
        if text != '' and self.monitor_thread is not None:
            try:
                print('sent:', text)
                self.monitor_thread.process.process.stdin.write(
                    text.encode('utf8'))
                self.monitor_thread.process.process.stdin.flush()
            except:
                import traceback
                traceback.print_exc()
        super(ProcessConsole, self).keyPressEvent(e)

    def get_subprocess(self):
        """
        返回子进程
        Returns:

        """
        if hasattr(self.monitor_thread, 'process'):
            proc = self.monitor_thread.process
            if hasattr(proc, 'process'):
                return proc.process
        return None
class TaskManagerBase(QObject):
    """
    A basic manager to handle tasks that need to be executed in a different
    thread than that of the main application to avoid blocking the GUI event
    loop.
    """
    sig_run_tasks_finished = Signal()

    def __init__(self):
        super().__init__()
        self._worker = None

        self._task_callbacks = {}
        self._task_data = {}

        self._running_tasks = []
        self._queued_tasks = []
        self._pending_tasks = []
        # Queued tasks are tasks whose execution has not been requested yet.
        # This happens when we want the Worker to execute a list of tasks
        # in a single run. All queued tasks are dumped in the list of pending
        # tasks when `run_task` is called.
        #
        # Pending tasks are tasks whose execution was postponed due to
        # the fact that the worker was busy. These tasks are run as soon
        # as the worker becomes available.
        #
        # Running tasks are tasks that are being executed by the worker.

    def run_tasks(self):
        """
        Execute all the tasks that were added to the stack.
        """
        self._run_tasks()

    def add_task(self, task, callback, *args, **kargs):
        self._add_task(task, callback, *args, **kargs)

    def worker(self):
        """Return the worker that is installed on this manager."""
        return self._worker

    def set_worker(self, worker):
        """"Install the provided worker on this manager"""
        self._worker = worker
        self._thread = QThread()
        self._worker.moveToThread(self._thread)
        self._thread.started.connect(self._worker.run_tasks)

        # Connect the worker signals to handlers.
        self._worker.sig_task_completed.connect(self._exec_task_callback)

    # ---- Private API
    @Slot(object, object)
    def _exec_task_callback(self, task_uuid4, returned_values):
        """
        This is the (only) slot that is called after a task is completed
        by the worker.
        """
        # Run the callback associated with the specified task UUID if any.
        if self._task_callbacks[task_uuid4] is not None:
            try:
                self._task_callbacks[task_uuid4](*returned_values)
            except TypeError:
                self._task_callbacks[task_uuid4]()

        # Clean up internal variables.
        del self._task_callbacks[task_uuid4]
        del self._task_data[task_uuid4]
        self._running_tasks.remove(task_uuid4)

        if len(self._running_tasks) == 0:
            # This means all tasks sent to the worker were completed.
            if len(self._pending_tasks) > 0:
                self._run_pending_tasks()
            else:
                self.sig_run_tasks_finished.emit()
                print('All pending tasks were executed.')

    def _add_task(self, task, callback, *args, **kargs):
        task_uuid4 = uuid.uuid4()
        self._task_callbacks[task_uuid4] = callback
        self._queued_tasks.append(task_uuid4)
        self._task_data[task_uuid4] = (task, args, kargs)

    def _run_tasks(self):
        """
        Execute all the tasks that were added to the stack.
        """
        self._pending_tasks.extend(self._queued_tasks)
        self._queued_tasks = []
        self._run_pending_tasks()

    def _run_pending_tasks(self):
        """Execute all pending tasks."""
        if len(self._running_tasks) == 0:
            print('Executing {} pending tasks...'.format(
                len(self._pending_tasks)))
            # Even though the worker has executed all its tasks,
            # we may still need to wait a little for it to stop properly.
            i = 0
            while self._thread.isRunning():
                sleep(0.1)
                i += 1
                if i > 100:
                    print("Error: unable to stop {}'s working thread.".format(
                        self.__class__.__name__))

            self._running_tasks = self._pending_tasks.copy()
            self._pending_tasks = []
            for task_uuid4 in self._running_tasks:
                task, args, kargs = self._task_data[task_uuid4]
                self._worker.add_task(task_uuid4, task, *args, **kargs)
            self._thread.start()
Example #8
0
class DAQ_PID(QObject):
    """
    """
    command_pid = Signal(ThreadCommand)
    curr_points_signal = Signal(dict)
    setpoints_signal = Signal(dict)
    emit_curr_points_sig = Signal()

    models = get_models()

    params = [
        {'title': 'Models', 'name': 'models', 'type': 'group', 'expanded': True, 'visible': True, 'children': [
            {'title': 'Models class:', 'name': 'model_class', 'type': 'list',
             'limits': [d['name'] for d in models]},
            {'title': 'Model params:', 'name': 'model_params', 'type': 'group', 'children': []},
        ]},
        {'title': 'Move settings:', 'name': 'move_settings', 'expanded': True, 'type': 'group', 'visible': False,
         'children': [
             {'title': 'Units:', 'name': 'units', 'type': 'str', 'value': ''}]},
        # here only to be compatible with DAQ_Scan, the model could update it

        {'title': 'Main Settings:', 'name': 'main_settings', 'expanded': True, 'type': 'group', 'children': [
            {'title': 'Acquisition Timeout (ms):', 'name': 'timeout', 'type': 'int', 'value': 10000},
            {'title': 'epsilon', 'name': 'epsilon', 'type': 'float', 'value': 0.01,
             'tooltip': 'Precision at which move is considered as done'},
            {'title': 'PID controls:', 'name': 'pid_controls', 'type': 'group', 'children': [
                {'title': 'Sample time (ms):', 'name': 'sample_time', 'type': 'int', 'value': 10},
                {'title': 'Refresh plot time (ms):', 'name': 'refresh_plot_time', 'type': 'int', 'value': 200},
                {'title': 'Output limits:', 'name': 'output_limits', 'expanded': True, 'type': 'group', 'children': [
                    {'title': 'Output limit (min):', 'name': 'output_limit_min_enabled', 'type': 'bool',
                     'value': False},
                    {'title': 'Output limit (min):', 'name': 'output_limit_min', 'type': 'float', 'value': 0},
                    {'title': 'Output limit (max):', 'name': 'output_limit_max_enabled', 'type': 'bool',
                     'value': False},
                    {'title': 'Output limit (max:', 'name': 'output_limit_max', 'type': 'float', 'value': 100},
                ]},
                {'title': 'Auto mode:', 'name': 'auto_mode', 'type': 'bool', 'value': False, 'readonly': True},
                {'title': 'Prop. on measurement:', 'name': 'proportional_on_measurement', 'type': 'bool',
                 'value': False},
                {'title': 'PID constants:', 'name': 'pid_constants', 'type': 'group', 'children': [
                    {'title': 'Kp:', 'name': 'kp', 'type': 'float', 'value': 0.1, 'min': 0},
                    {'title': 'Ki:', 'name': 'ki', 'type': 'float', 'value': 0.01, 'min': 0},
                    {'title': 'Kd:', 'name': 'kd', 'type': 'float', 'value': 0.001, 'min': 0},
                ]},

            ]},

        ]},
    ]

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

        self.settings = Parameter.create(title='PID settings', name='pid_settings', type='group', children=self.params)
        self.title = 'PyMoDAQ PID'

        self.Initialized_state = False
        self.model_class = None
        self._curr_points = dict([])
        self._setpoints = dict([])

        self.modules_manager = None

        self.dock_area = dockarea
        self.check_moving = False
        self.setupUI()

        self.enable_controls_pid(False)

        self.enable_controls_pid_run(False)

        self.emit_curr_points_sig.connect(self.emit_curr_points)

    def set_module_manager(self, detector_modules, actuator_modules):
        self.modules_manager = ModulesManager(detector_modules, actuator_modules)

    def ini_PID(self):

        if self.ini_PID_action.isChecked():
            output_limits = [None, None]
            if self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                   'output_limit_min_enabled').value():
                output_limits[0] = self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                                       'output_limit_min').value()
            if self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                   'output_limit_max_enabled').value():
                output_limits[1] = self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                                       'output_limit_max').value()

            self.PIDThread = QThread()
            pid_runner = PIDRunner(self.model_class, self.modules_manager, setpoints=self.setpoints,
                                   params=dict(Kp=self.settings.child('main_settings', 'pid_controls', 'pid_constants',
                                                                      'kp').value(),
                                               Ki=self.settings.child('main_settings', 'pid_controls', 'pid_constants',
                                                                      'ki').value(),
                                               Kd=self.settings.child('main_settings', 'pid_controls', 'pid_constants',
                                                                      'kd').value(),
                                               sample_time=self.settings.child('main_settings', 'pid_controls',
                                                                               'sample_time').value() / 1000,
                                               output_limits=output_limits,
                                               auto_mode=False),
                                   )

            self.PIDThread.pid_runner = pid_runner
            pid_runner.pid_output_signal.connect(self.process_output)
            pid_runner.status_sig.connect(self.thread_status)
            self.command_pid.connect(pid_runner.queue_command)

            pid_runner.moveToThread(self.PIDThread)

            self.PIDThread.start()
            self.pid_led.set_as_true()
            self.enable_controls_pid_run(True)

        else:
            if hasattr(self, 'PIDThread'):
                if self.PIDThread.isRunning():
                    try:
                        self.PIDThread.quit()
                    except Exception:
                        pass
            self.pid_led.set_as_false()
            self.enable_controls_pid_run(False)

        self.Initialized_state = True

    def process_output(self, datas):
        self.output_viewer.show_data([[dat] for dat in datas['output']])
        self.input_viewer.show_data([[dat] for dat in datas['input']])
        self.curr_points = datas['input']

    def enable_controls_pid(self, enable=False):
        self.ini_PID_action.setEnabled(enable)
        #self.setpoint_sb.setOpts(enabled=enable)

    def enable_controls_pid_run(self, enable=False):
        self.run_action.setEnabled(enable)
        self.pause_action.setEnabled(enable)

    def setupUI(self):

        self.dock_pid = Dock('PID controller', self.dock_area)
        self.dock_area.addDock(self.dock_pid)

        widget = QtWidgets.QWidget()
        widget_toolbar = QtWidgets.QWidget()
        verlayout = QtWidgets.QVBoxLayout()
        widget.setLayout(verlayout)
        self.toolbar_layout = QtWidgets.QGridLayout()
        widget_toolbar.setLayout(self.toolbar_layout)

        iconquit = QtGui.QIcon()
        iconquit.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/close2.png"), QtGui.QIcon.Normal,
                           QtGui.QIcon.Off)
        self.quit_action = QtWidgets.QPushButton(iconquit, "Quit")
        self.quit_action.setToolTip('Quit the application')
        self.toolbar_layout.addWidget(self.quit_action, 0, 0, 1, 2)
        self.quit_action.clicked.connect(self.quit_fun)

        iconini = QtGui.QIcon()
        iconini.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/ini.png"), QtGui.QIcon.Normal,
                          QtGui.QIcon.Off)
        self.ini_model_action = QtWidgets.QPushButton(iconini, "Init Model")
        self.ini_model_action.setToolTip('Initialize the chosen model')
        self.toolbar_layout.addWidget(self.ini_model_action, 2, 0)
        self.ini_model_action.clicked.connect(self.ini_model)
        self.model_led = QLED()
        self.toolbar_layout.addWidget(self.model_led, 2, 1)

        self.ini_PID_action = QtWidgets.QPushButton(iconini, "Init PID")
        self.ini_PID_action.setToolTip('Initialize the PID loop')
        self.toolbar_layout.addWidget(self.ini_PID_action, 2, 2)
        self.ini_PID_action.setCheckable(True)
        self.ini_PID_action.clicked.connect(self.ini_PID)
        self.pid_led = QLED()
        self.toolbar_layout.addWidget(self.pid_led, 2, 3)

        self.iconrun = QtGui.QIcon()
        self.iconrun.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/run2.png"), QtGui.QIcon.Normal,
                               QtGui.QIcon.Off)
        self.icon_stop = QtGui.QIcon()
        self.icon_stop.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/stop.png"))
        self.run_action = QtWidgets.QPushButton(self.iconrun, "", None)
        self.run_action.setToolTip('Start PID loop')
        self.run_action.setCheckable(True)
        self.toolbar_layout.addWidget(self.run_action, 0, 2)
        self.run_action.clicked.connect(self.run_PID)

        iconpause = QtGui.QIcon()
        iconpause.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/pause.png"), QtGui.QIcon.Normal,
                            QtGui.QIcon.Off)
        self.pause_action = QtWidgets.QPushButton(iconpause, "", None)
        self.pause_action.setToolTip('Pause PID')
        self.pause_action.setCheckable(True)
        self.toolbar_layout.addWidget(self.pause_action, 0, 3)
        self.pause_action.setChecked(True)
        self.pause_action.clicked.connect(self.pause_PID)

        lab = QtWidgets.QLabel('Target Value:')
        self.toolbar_layout.addWidget(lab, 3, 0, 1, 2)


        lab1 = QtWidgets.QLabel('Current Value:')
        self.toolbar_layout.addWidget(lab1, 4, 0, 1, 2)


        # create main parameter tree
        self.settings_tree = ParameterTree()
        self.settings_tree.setParameters(self.settings, showTop=False)

        verlayout.addWidget(widget_toolbar)
        verlayout.addWidget(self.settings_tree)

        self.dock_output = Dock('PID output')
        widget_output = QtWidgets.QWidget()
        self.output_viewer = Viewer0D(widget_output)
        self.dock_output.addWidget(widget_output)
        self.dock_area.addDock(self.dock_output, 'right', self.dock_pid)

        self.dock_input = Dock('PID input')
        widget_input = QtWidgets.QWidget()
        self.input_viewer = Viewer0D(widget_input)
        self.dock_input.addWidget(widget_input)
        self.dock_area.addDock(self.dock_input, 'bottom', self.dock_output)

        if len(self.models) != 0:
            self.get_set_model_params(self.models[0]['name'])

        # connecting from tree
        self.settings.sigTreeStateChanged.connect(
            self.parameter_tree_changed)  # any changes on the settings will update accordingly the detector
        self.dock_pid.addWidget(widget)

    def get_set_model_params(self, model_name):
        self.settings.child('models', 'model_params').clearChildren()
        models = get_models()
        if len(models) > 0:
            model_class = find_dict_in_list_from_key_val(models, 'name', model_name)['class']
            params = getattr(model_class, 'params')
            self.settings.child('models', 'model_params').addChildren(params)

    def run_PID(self):
        if self.run_action.isChecked():
            self.run_action.setIcon(self.icon_stop)
            self.command_pid.emit(ThreadCommand('start_PID', []))
            QtWidgets.QApplication.processEvents()

            QtWidgets.QApplication.processEvents()

            self.command_pid.emit(ThreadCommand('run_PID', [self.model_class.curr_output]))
        else:
            self.run_action.setIcon(self.iconrun)
            self.command_pid.emit(ThreadCommand('stop_PID'))

            QtWidgets.QApplication.processEvents()

    def pause_PID(self):
        for setp in self.setpoints_sb:
            setp.setEnabled(not self.pause_action.isChecked())
        self.command_pid.emit(ThreadCommand('pause_PID', [self.pause_action.isChecked()]))


    def stop_moves(self, overshoot):
        """
            Foreach module of the move module object list, stop motion.

            See Also
            --------
            stop_scan,  DAQ_Move_main.daq_move.stop_Motion
        """
        self.overshoot = overshoot
        for mod in self.modules_manager.actuators:
            mod.stop_Motion()

    def set_model(self):
        model_name = self.settings.child('models', 'model_class').value()
        self.model_class = find_dict_in_list_from_key_val(self.models, 'name', model_name)['class'](self)
        self.set_setpoints_buttons()
        self.model_class.ini_model()
        self.settings.child('main_settings', 'epsilon').setValue(self.model_class.epsilon)

    def ini_model(self):
        try:
            if self.model_class is None:
                self.set_model()

            self.modules_manager.selected_actuators_name = self.model_class.actuators_name
            self.modules_manager.selected_detectors_name = self.model_class.detectors_name

            self.enable_controls_pid(True)
            self.model_led.set_as_true()
            self.ini_model_action.setEnabled(False)

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

    @property
    def setpoints(self):
        return [sp.value() for sp in self.setpoints_sb]

    @setpoints.setter
    def setpoints(self, values):
        for ind, sp in enumerate(self.setpoints_sb):
            sp.setValue(values[ind])

    def setpoints_external(self, values_dict):
        for key in values_dict:
            index = self.model_class.setpoints_names.index(key)
            self.setpoints_sb[index].setValue(values_dict[key])

    @property
    def curr_points(self):
        return [sp.value() for sp in self.currpoints_sb]

    @curr_points.setter
    def curr_points(self, values):
        for ind, sp in enumerate(self.currpoints_sb):
            sp.setValue(values[ind])

    def emit_curr_points(self):
        if self.model_class is not None:
            self.curr_points_signal.emit(dict(zip(self.model_class.setpoints_names, self.curr_points)))

    def set_setpoints_buttons(self):
        self.setpoints_sb = []
        self.currpoints_sb = []
        for ind_set in range(self.model_class.Nsetpoints):

            self.setpoints_sb.append(SpinBox())
            self.setpoints_sb[-1].setMinimumHeight(40)
            font = self.setpoints_sb[-1].font()
            font.setPointSizeF(20)
            self.setpoints_sb[-1].setFont(font)
            self.setpoints_sb[-1].setDecimals(6)
            self.toolbar_layout.addWidget(self.setpoints_sb[-1], 3, 2+ind_set, 1, 1)
            self.setpoints_sb[-1].valueChanged.connect(self.update_runner_setpoints)

            self.currpoints_sb.append(SpinBox())
            self.currpoints_sb[-1].setMinimumHeight(40)
            self.currpoints_sb[-1].setReadOnly(True)
            self.currpoints_sb[-1].setDecimals(6)
            self.currpoints_sb[-1].setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
            font = self.currpoints_sb[-1].font()
            font.setPointSizeF(20)
            self.currpoints_sb[-1].setFont(font)
            self.toolbar_layout.addWidget(self.currpoints_sb[-1], 4, 2+ind_set, 1, 1)

        self.setpoints_signal.connect(self.setpoints_external)

    def quit_fun(self):
        """
        """
        try:
            try:
                self.PIDThread.exit()
            except Exception as e:
                print(e)

            areas = self.dock_area.tempAreas[:]
            for area in areas:
                area.win.close()
                QtWidgets.QApplication.processEvents()
                QThread.msleep(1000)
                QtWidgets.QApplication.processEvents()

            self.dock_area.parent().close()

        except Exception as e:
            print(e)

    def parameter_tree_changed(self, param, changes):
        """
            Foreach value changed, update :
                * Viewer in case of **DAQ_type** parameter name
                * visibility of button in case of **show_averaging** parameter name
                * visibility of naverage in case of **live_averaging** parameter name
                * scale of axis **else** (in 2D pymodaq type)

            Once done emit the update settings signal to link the commit.

            =============== =================================== ================================================================
            **Parameters**    **Type**                           **Description**
            *param*           instance of ppyqtgraph parameter   the parameter to be checked
            *changes*         tuple list                         Contain the (param,changes,info) list listing the changes made
            =============== =================================== ================================================================
        """

        for param, change, data in changes:
            path = self.settings.childPath(param)
            if change == 'childAdded':
                pass

            elif change == 'value':
                if param.name() == 'model_class':
                    self.get_set_model_params(param.value())

                elif param.name() == 'refresh_plot_time' or param.name() == 'timeout':
                    self.command_pid.emit(ThreadCommand('update_timer', [param.name(), param.value()]))

                elif param.name() == 'sample_time':
                    self.command_pid.emit(ThreadCommand('update_options', dict(sample_time=param.value())))

                elif param.name() in putils.iter_children(
                        self.settings.child('main_settings', 'pid_controls', 'output_limits'), []):


                    output_limits = convert_output_limits(
                        self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                            'output_limit_min').value(),
                        self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                           'output_limit_min_enabled').value(),
                        self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                                               'output_limit_max').value(),
                        self.settings.child('main_settings', 'pid_controls', 'output_limits',
                                           'output_limit_max_enabled').value())

                    self.command_pid.emit(ThreadCommand('update_options', dict(output_limits=output_limits)))

                elif param.name() in putils.iter_children(
                        self.settings.child('main_settings', 'pid_controls', 'pid_constants'), []):
                    Kp = self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'kp').value()
                    Ki = self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'ki').value()
                    Kd = self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'kd').value()
                    self.command_pid.emit(ThreadCommand('update_options', dict(tunings=(Kp, Ki, Kd))))

                elif param.name() in putils.iter_children(self.settings.child('models', 'model_params'), []):
                    if self.model_class is not None:
                        self.model_class.update_settings(param)

                elif param.name() == 'detector_modules':
                    self.model_class.update_detector_names()

            elif change == 'parent':
                pass

    def update_runner_setpoints(self):
        self.command_pid.emit(ThreadCommand('update_setpoints', self.setpoints))

    @Slot(list)
    def thread_status(self, status):  # general function to get datas/infos from all threads back to the main
        """

        """
        pass
Example #9
0
class QThreadedClient(QObject):
    """Threaded socket client.

    Available signals:
        - finished(): When client thread stops.
        - closed(): After closing socket.
        - connected(ip: str, port: int): Successfully connected to server.
        - message(data: dict): Received message from server.
        - disconnected(): Disconnected from server.
        - error(error_string: str): Socket error.
        - failed_to_connect(ip: str, port: int): Failed to connect to server.

    Available slots:
        - start(): Start client.
        - write(data: bytes): Write message to server.
        - reconnect(): Reconnect to server.
        - close(): Close connection.
        - disconnect_from_server(): Disconnect from server.
        - connect_to(ip: str, port: int): (Re)connect to server.
    """
    finished = Signal()
    closed = Signal()
    connected = Signal(str, int)
    message = Signal(bytes)
    disconnected = Signal()
    error = Signal(str)
    failed_to_connect = Signal(str, int)

    def __init__(self, loggerName=None):
        super(QThreadedClient, self).__init__(None)
        self.__ip = None
        self.__port = None

        self.__client = None
        self.__client_thread = None
        self.__logger_name = loggerName

    @Slot(str, int)
    def start(self, ip: str, port: int):
        """Start client thread and connect to server."""
        self.__ip = ip
        self.__port = port

        self.__client = _SocketClient(self.__ip,
                                      self.__port,
                                      loggerName=self.__logger_name)
        self.__client_thread = QThread()

        self.__client_thread.started.connect(self.__client.start)
        self.__client_thread.finished.connect(self.finished.emit)

        self.__client.moveToThread(self.__client_thread)

        self.__client.connected.connect(self.on_connected)
        self.__client.failed_to_connect.connect(self.on_failed_to_connect)
        self.__client.message.connect(self.on_message)
        self.__client.disconnected.connect(self.on_disconnected)
        self.__client.error.connect(self.on_error)
        self.__client.closed.connect(self.on_closed)

        self.__client_thread.start()

    @Slot(str, int)
    def on_connected(self, ip, port):
        """Called when client connects to server.
        Emits connected signal.

        Args:
            ip (str): Client ip address.
            port (int): Client port.
        """
        self.connected.emit(ip, port)

    @Slot(str, int)
    def on_failed_to_connect(self, ip, port):
        """Called when client fails to connect to server.
        Emits failed_to_connect signal.

        Args:
            ip (str): Client ip address.
            port (int): Client port.
        """
        self.failed_to_connect.emit(ip, port)

    @Slot(bytes)
    def on_message(self, message: bytes):
        """Called when client receives message from server.
        Emits message signal.

        Args:
            message (bytes): Message.
        """
        self.message.emit(message)

    @Slot()
    def on_disconnected(self):
        """Called when device disconnects from server.
        Emits disconnected signal."""
        self.disconnected.emit()

    @Slot(str)
    def on_error(self, error: str):
        """Called when a socket error occurs.
        Emits error signal.

        Args:
            error (str): Error string.
        """
        self.error.emit(error)

    @Slot()
    def on_closed(self):
        """Called when when the socket is closed.
        Emits closed signal."""
        self.closed.emit()

    @Slot(bytes)
    def write(self, data: bytes):
        """Write data to server.

        Args:
            data (bytes): Data to write.
        """
        self.__client.write_signal.emit(data)

    @Slot()
    def close(self):
        """Disconnect from server and close socket."""
        if self.__client and self.__client_thread:
            self.__client.close_signal.emit()
            self.__client_thread.quit()
        else:
            self.error.emit("Client not running")

    @Slot()
    def disconnect_from_server(self):
        """Disconnect from server."""
        self.__client.disconnect_signal.emit()

    @Slot(str, int)
    def connect_to(self, ip: str, port: int):
        """(Re)connect to server.

        Args:
            ip (str): IP address.
            port (int): Port.
        """
        self.__client.connect_signal.emit(ip, port)

    @Slot()
    def reconnect(self):
        self.__client.reconnect_signal.emit()

    @Slot()
    def is_running(self):
        """Check if server is running"""
        if self.__client_thread:
            return self.__client_thread.isRunning()
        return False

    @Slot()
    def wait(self):
        """Wait for server thread to finish."""
        if self.__client_thread:
            return self.__client_thread.wait()
        return True
Example #10
0
class ProcessConsole(QTextEdit):
    signal_stop_qthread = Signal()
    signal_process_stopped = Signal()
    signal_process_started = Signal()
    signal_hyperlink_clicked = Signal(str)
    signal_goto_file = Signal(str, int)
    insert_mode = ''

    def __init__(self, args: list = None):
        super().__init__()
        self.anchor = None
        self._is_running = False
        self.auto_scroll = True
        self.args = args  #
        self.setContentsMargins(20, 20, 0, 0)
        self.monitor_thread: 'ProcessMonitorThread' = None
        self.out_thread: 'QThread' = None
        self.signal_hyperlink_clicked.connect(self.on_hyperlink_clicked)

    def set_args(self, args: list = None):
        self.args = args

    def is_running(self):
        if self.monitor_thread is not None:
            if self.monitor_thread.process.process.poll() is None:
                return True
        return False

    def start_process(self):
        if not self.is_running():
            self.out_thread = QThread(self)
            self.monitor_thread = ProcessMonitorThread()
            self.monitor_thread.args = self.args
            self.monitor_thread.moveToThread(self.out_thread)

            self.out_thread.started.connect(self.monitor_thread.run)
            self.out_thread.start()

            self.monitor_thread.on_out.connect(self.on_stdout)
            self.monitor_thread.on_err.connect(self.on_stderr)

            self.signal_stop_qthread.connect(self.monitor_thread.stop)

            self.out_thread.finished.connect(self.out_thread.deleteLater)
            self.out_thread.finished.connect(self.monitor_thread.deleteLater)

            self.monitor_thread.on_finished.connect(self.terminate_process)
            self.clear()
            self.insertHtml('<p style="color:#0063c5;">' +
                            ' '.join(self.args) + '<br/></p>')
            self.insertHtml('<p style="color:#aaaaaa;">' + '' + '</p>')

    def on_stdout(self, text):

        if self.insert_mode == 'error':
            self.insert_mode = 'stdout'

        self.insertHtml(
            self.insertHtml('<p style="color:#aaaaaa;">' + text + '<br/></p>'))
        if self.auto_scroll:
            self.ensureCursorVisible()

    def on_stderr(self, text):
        self.insert_mode = 'error'
        # result = re.search(r'(/|([a-zA-Z]:((\\)|/))).*:[0-9].*', text)
        result = re.search(r'(/|([a-zA-Z]:((\\)|/))).* line [0-9].*,', text)
        print(result)
        if result is None:
            self.insertHtml('<p style="color:red;">' + text + '<br/></p>')
        else:
            span = result.span()
            self.insertHtml('<p style="color:red;">' + text[:span[0]] + '</p>')
            print(result.group())
            self.insert_hyperlink(text[span[0]:span[1]])
            # self.insertHtml('<p style="color:red;">' + text[result[0]:result[1]] + '</p>')
            self.insertHtml('<p style="color:red;">' + text[span[1]:] +
                            '<br/></p>')
        if self.auto_scroll:
            self.ensureCursorVisible()

    def terminate_process(self):
        if self.monitor_thread is not None:
            self.monitor_thread.process_terminated = True
            self.monitor_thread.process.process.terminate()
            if self.out_thread.isRunning():
                self.signal_stop_qthread.emit()
                self.out_thread.quit()
                self.out_thread.wait(500)
        self.monitor_thread = None
        self.out_thread = None
        self.signal_process_stopped.emit()

    def keyPressEvent(self, e: 'QKeyEvent'):
        if e.key() == Qt.Key_Backspace or e.key() == Qt.Key_Delete:
            return
        print(e.key(), e.text())
        if e.key() == Qt.Key_Return:
            text = '\n'
        else:
            text = e.text()
        if text != '' and self.monitor_thread is not None:
            try:
                print('sent:', text)
                self.monitor_thread.process.process.stdin.write(
                    text.encode('utf8'))
                self.monitor_thread.process.process.stdin.flush()
            except:
                import traceback
                traceback.print_exc()
        super(ProcessConsole, self).keyPressEvent(e)

    def mousePressEvent(self, e):
        super(ProcessConsole, self).mousePressEvent(e)
        self.anchor = self.anchorAt(e.pos())
        # if self.anchor:
        #     QApplication.setOverrideCursor(Qt.PointingHandCursor)

    def mouseMoveEvent(self, e):
        super(ProcessConsole, self).mouseMoveEvent(e)
        self.anchor = self.anchorAt(e.pos())
        if not self.anchor:
            QApplication.setOverrideCursor(Qt.ArrowCursor)
        else:
            QApplication.setOverrideCursor(Qt.PointingHandCursor)

    def mouseReleaseEvent(self, e):
        super(ProcessConsole, self).mouseReleaseEvent(e)
        if self.anchor:
            # QDesktopServices.openUrl(QUrl(self.anchor))
            # QApplication.setOverrideCursor(Qt.ArrowCursor)
            self.signal_hyperlink_clicked.emit(self.anchor)
            self.anchor = None

    def insert_hyperlink(self, link: str, text=''):
        cursor = self.textCursor()
        fmt = cursor.charFormat()
        fmt.setForeground(QColor('#0063c5'))
        # address = 'http://example.com'
        fmt.setAnchor(True)
        fmt.setAnchorHref(link)
        fmt.setToolTip(link)
        if text == '':
            cursor.insertText(link, fmt)
        else:
            cursor.insertText(text, fmt)

    def on_hyperlink_clicked(self, hyperlink_text: str):
        if re.search(r'(/|([a-zA-Z]:((\\)|/))).* line [0-9].*,',
                     hyperlink_text) is not None:
            try:
                l = hyperlink_text.split('line')
                assert len(l) == 2, l
                for i in [0, 1]:
                    l[i] = l[i].strip(', \"\'')
                file_path, row_str = l
                row = int(row_str)
                if not os.path.exists(file_path):
                    QMessageBox.warning(self, self.tr('Warning'),
                                        self.tr('文件%s不存在!' % file_path))
                else:
                    self.signal_goto_file.emit(file_path, row)
                    logger.info('goto file %s, line %d' % (file_path, row))
            except:
                import traceback
                traceback.print_exc()