Esempio n. 1
0
class PMQThreadManager(QObject):
    signal_server_message_received = Signal(dict)
    signal_data_changed = Signal(str)

    def __init__(self,
                 parent=None,
                 work_fcn: Callable = None,
                 loop_fcn: Callable = None,
                 exit_fcn: Callable = None):
        super().__init__(parent)
        self.thread_recv = QThread()
        self.worker_recv = PMGThreadWorker()

        if work_fcn is not None and loop_fcn is not None:
            raise ValueError(
                'work_fcn and loop_fcn cannot be both not None at the same time!'
            )
        if work_fcn is not None:
            self.worker_recv.work = work_fcn
        else:
            self.worker_recv.work_loop_fcn = loop_fcn
        self.worker_recv.moveToThread(self.thread_recv)
        self.thread_recv.started.connect(
            self.worker_recv.work)  # self.worker_recv.work)
        self.worker_recv.on_exit_fcn = exit_fcn
        self.thread_recv.start()

    def shut_down(self):
        logger.info('client quit')
        self.worker_recv.on_exit()
        # self.worker_recv.close_socket()
        self.thread_recv.quit()
        self.thread_recv.wait(500)
Esempio n. 2
0
class PMClient(QObject, BaseClient):
    signal_server_message_received = Signal(dict)
    signal_data_changed = Signal(str)

    def __init__(self, parent=None, name='Anonymous QtClient'):
        super().__init__(parent)
        self.name = name
        self.client = self.init_socket(12306)
        self.thread_recv = QThread()
        self.worker_recv = RecvWork(self.client, self.name)
        self.signal_received = self.worker_recv.signal_received
        self.worker_recv.signal_received.connect(self.on_server_message_received)

        self.worker_recv.moveToThread(self.thread_recv)
        self.thread_recv.started.connect(self.worker_recv.work)
        self.thread_recv.start()

    def shut_down(self):
        logger.info('client quit')
        self.worker_recv.close_socket()
        self.thread_recv.quit()
        self.thread_recv.wait(500)

    def on_server_message_received(self, packet: bytes):
        try:
            dic = json.loads(packet)
            logger.info(dic)
            msg = dic.get('message')
            if msg == 'data_changed':
                data_name = dic.get('data_name')
                if data_name is not None:
                    self.signal_data_changed.emit(data_name)
            self.signal_server_message_received.emit(dic)
        except:
            import traceback
            traceback.print_exc()
            pass

    def send_bytes(self, packet: bytes):
        self.client.sendall(packet)

    @timeit
    def compress(self, byte_str):
        return zlib.compress(byte_str)
Esempio n. 3
0
    def _start(self, worker=None):
        """Start threads and check for inactive workers."""
        if worker:
            self._queue_workers.append(worker)

        if self._queue_workers and self._running_threads < self._max_threads:
            #print('Queue: {0} Running: {1} Workers: {2} '
            #       'Threads: {3}'.format(len(self._queue_workers),
            #                                 self._running_threads,
            #                                 len(self._workers),
            #                                 len(self._threads)))
            self._running_threads += 1
            worker = self._queue_workers.popleft()
            thread = QThread(None)
            if isinstance(worker, PythonWorker):
                worker.moveToThread(thread)
                worker.sig_finished.connect(thread.quit)
                thread.started.connect(worker._start)
                thread.start()
            elif isinstance(worker, ProcessWorker):
                thread.quit()
                thread.wait()
                worker._start()
            self._threads.append(thread)
        else:
            self._timer.start()

        if self._workers:
            for w in self._workers:
                if w.is_finished():
                    self._bag_collector.append(w)
                    self._workers.remove(w)

        if self._threads:
            for t in self._threads:
                if t.isFinished():
                    self._threads.remove(t)
                    self._running_threads -= 1

        if len(self._threads) == 0 and len(self._workers) == 0:
            self._timer.stop()
            self._timer_worker_delete.start()
Esempio n. 4
0
class PMQThreadObject(QObject):
    signal_server_message_received = Signal(dict)
    signal_data_changed = Signal(str)

    def __init__(self, parent=None, worker: QObject = None):
        super().__init__(parent)
        self.thread = QThread()
        self.worker = worker
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.work)  # self.worker_recv.work)
        self.thread.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.start()

    def terminate(self):
        logger.info('client quit')
        self.worker.on_exit()

        self.thread.quit()
        self.thread.wait(500)
Esempio n. 5
0
    def _start(self, worker=None):
        """Start threads and check for inactive workers."""
        if worker:
            self._queue_workers.append(worker)

        if self._queue_workers and self._running_threads < self._max_threads:
            #print('Queue: {0} Running: {1} Workers: {2} '
            #       'Threads: {3}'.format(len(self._queue_workers),
            #                                 self._running_threads,
            #                                 len(self._workers),
            #                                 len(self._threads)))
            self._running_threads += 1
            worker = self._queue_workers.popleft()
            thread = QThread()
            if isinstance(worker, PythonWorker):
                worker.moveToThread(thread)
                worker.sig_finished.connect(thread.quit)
                thread.started.connect(worker._start)
                thread.start()
            elif isinstance(worker, ProcessWorker):
                thread.quit()
                worker._start()
            self._threads.append(thread)
        else:
            self._timer.start()

        if self._workers:
            for w in self._workers:
                if w.is_finished():
                    self._bag_collector.append(w)
                    self._workers.remove(w)

        if self._threads:
            for t in self._threads:
                if t.isFinished():
                    self._threads.remove(t)
                    self._running_threads -= 1

        if len(self._threads) == 0 and len(self._workers) == 0:
            self._timer.stop()
            self._timer_worker_delete.start()
Esempio n. 6
0
class PMGQThreadManager(QObject):
    signal_server_message_received = Signal(dict)
    signal_data_changed = Signal(str)
    signal_finished = Signal()

    def __init__(self, parent=None, worker: QObject = None):
        super().__init__(parent)
        self.thread_recv = QThread()
        self.worker_recv = worker
        self.worker_recv.moveToThread(self.thread_recv)
        self.thread_recv.started.connect(self.worker_recv.work)
        self.thread_recv.start()
        self.thread_recv.finished.connect(self.signal_finished.emit)

    def shut_down(self):
        """
        关闭线程,并且退出。
        :return:
        """
        self.worker_recv.on_exit()
        self.thread_recv.quit()
        self.thread_recv.wait(500)
Esempio n. 7
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)
Esempio n. 8
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
Esempio n. 9
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()
Esempio n. 10
0
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()
Esempio n. 11
0
class SnippetsActor(QObject):
    #: Signal emitted when the Thread is ready
    sig_snippets_ready = Signal()
    sig_snippets_response = Signal(int, dict)
    sig_update_snippets = Signal(dict)
    sig_mailbox = Signal(dict)

    def __init__(self, parent):
        QObject.__init__(self)
        self.stopped = False
        self.daemon = True
        self.mutex = QMutex()
        self.language_snippets = {}
        self.thread = QThread()
        self.moveToThread(self.thread)

        self.thread.started.connect(self.started)
        self.sig_mailbox.connect(self.handle_msg)
        self.sig_update_snippets.connect(self.update_snippets)

    def stop(self):
        """Stop actor."""
        with QMutexLocker(self.mutex):
            logger.debug("Snippets plugin stopping...")
            self.thread.quit()

    def start(self):
        """Start thread."""
        self.thread.start()

    def started(self):
        """Thread started."""
        logger.debug('Snippets plugin starting...')
        self.sig_snippets_ready.emit()

    @Slot(dict)
    def update_snippets(self, snippets):
        """Update available snippets."""
        logger.debug('Updating snippets...')
        for language in snippets:
            lang_snippets = snippets[language]
            lang_trie = Trie()
            for trigger in lang_snippets:
                trigger_descriptions = lang_snippets[trigger]
                lang_trie[trigger] = (trigger, trigger_descriptions)
            self.language_snippets[language] = lang_trie

    @Slot(dict)
    def handle_msg(self, message):
        """Handle one message"""
        msg_type, _id, file, msg = [
            message[k] for k in ('type', 'id', 'file', 'msg')]
        logger.debug(u'Perform request {0} with id {1}'.format(msg_type, _id))
        if msg_type == CompletionRequestTypes.DOCUMENT_COMPLETION:
            language = msg['language']
            current_word = msg['current_word']
            snippets = []

            if current_word is None:
                snippets = {'params': snippets}
                self.sig_snippets_response.emit(_id, snippets)
                return

            if language in self.language_snippets:
                language_snippets = self.language_snippets[language]
                if language_snippets[current_word]:
                    for node in language_snippets[current_word]:
                        trigger, info = node.value
                        for description in info:
                            description_snippet = info[description]
                            text = description_snippet['text']
                            remove_trigger = description_snippet[
                                'remove_trigger']
                            snippets.append({
                                'kind': CompletionItemKind.SNIPPET,
                                'insertText': text,
                                'label': f'{trigger} ({description})',
                                'sortText': f'zzz{trigger}',
                                'filterText': trigger,
                                'documentation': '',
                                'provider': SNIPPETS_COMPLETION,
                                'remove_trigger': remove_trigger
                            })

            snippets = {'params': snippets}
            self.sig_snippets_response.emit(_id, snippets)
Esempio n. 12
0
class FallbackActor(QObject):
    #: Signal emitted when the Thread is ready
    sig_fallback_ready = Signal()
    sig_set_tokens = Signal(int, dict)
    sig_mailbox = Signal(dict)

    def __init__(self, parent):
        QObject.__init__(self)
        self.stopped = False
        self.daemon = True
        self.mutex = QMutex()
        self.file_tokens = {}
        self.diff_patch = diff_match_patch()
        self.thread = QThread()
        self.moveToThread(self.thread)

        self.thread.started.connect(self.started)
        self.sig_mailbox.connect(self.handle_msg)

    def tokenize(self, text, offset, language, current_word):
        """
        Return all tokens in `text` and all keywords associated by
        Pygments to `language`.
        """
        valid = is_prefix_valid(text, offset, language)
        if not valid:
            return []

        # Get language keywords provided by Pygments
        try:
            lexer = get_lexer_by_name(language)
            keywords = get_keywords(lexer)
        except Exception:
            keywords = []
        keyword_set = set(keywords)
        keywords = [{
            'kind': CompletionItemKind.KEYWORD,
            'insertText': keyword,
            'label': keyword,
            'sortText': keyword,
            'filterText': keyword,
            'documentation': '',
            'provider': FALLBACK_COMPLETION
        } for keyword in keywords]

        # Get file tokens
        tokens = get_words(text, offset, language)
        tokens = [{
            'kind': CompletionItemKind.TEXT,
            'insertText': token,
            'label': token,
            'sortText': token,
            'filterText': token,
            'documentation': '',
            'provider': FALLBACK_COMPLETION
        } for token in tokens]
        for token in tokens:
            if token['insertText'] not in keyword_set:
                keywords.append(token)

        # Filter matching results
        if current_word is not None:
            current_word = current_word.lower()
            keywords = [
                k for k in keywords if current_word in k['insertText'].lower()
            ]

        return keywords

    def stop(self):
        """Stop actor."""
        with QMutexLocker(self.mutex):
            logger.debug("Fallback plugin stopping...")
            self.thread.quit()

    def start(self):
        """Start thread."""
        self.thread.start()

    def started(self):
        """Thread started."""
        logger.debug('Fallback plugin starting...')
        self.sig_fallback_ready.emit()

    @Slot(dict)
    def handle_msg(self, message):
        """Handle one message"""
        msg_type, _id, file, msg = [
            message[k] for k in ('type', 'id', 'file', 'msg')
        ]
        logger.debug(u'Perform request {0} with id {1}'.format(msg_type, _id))
        if msg_type == LSPRequestTypes.DOCUMENT_DID_OPEN:
            self.file_tokens[file] = {
                'text': msg['text'],
                'offset': msg['offset'],
                'language': msg['language'],
            }
        elif msg_type == LSPRequestTypes.DOCUMENT_DID_CHANGE:
            if file not in self.file_tokens:
                self.file_tokens[file] = {
                    'text': '',
                    'offset': msg['offset'],
                    'language': msg['language'],
                }
            diff = msg['diff']
            text = self.file_tokens[file]
            text['offset'] = msg['offset']
            text, _ = self.diff_patch.patch_apply(diff, text['text'])
            self.file_tokens[file]['text'] = text
        elif msg_type == LSPRequestTypes.DOCUMENT_DID_CLOSE:
            self.file_tokens.pop(file, {})
        elif msg_type == LSPRequestTypes.DOCUMENT_COMPLETION:
            tokens = []
            if file in self.file_tokens:
                text_info = self.file_tokens[file]
                tokens = self.tokenize(text_info['text'], text_info['offset'],
                                       text_info['language'],
                                       msg['current_word'])
            tokens = {'params': tokens}
            self.sig_set_tokens.emit(_id, tokens)
Esempio n. 13
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
Esempio n. 14
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
Esempio n. 15
0
class ApplicationContainer(PluginMainContainer):

    sig_report_issue_requested = Signal()
    """
    Signal to request reporting an issue to Github.
    """

    sig_load_log_file = Signal(str)
    """
    Signal to load a log file
    """
    def __init__(self, name, plugin, parent=None):
        super().__init__(name, plugin, parent)

        # Keep track of dpi message
        self.current_dpi = None
        self.dpi_messagebox = None

    # ---- PluginMainContainer API
    # -------------------------------------------------------------------------
    def setup(self):
        # Compute dependencies in a thread to not block the interface.
        self.dependencies_thread = QThread(None)

        # Attributes
        self.dialog_manager = DialogManager()
        self.give_updates_feedback = False
        self.thread_updates = None
        self.worker_updates = None
        self.updates_timer = None

        # Actions
        # Documentation actions
        self.documentation_action = self.create_action(
            ApplicationActions.SpyderDocumentationAction,
            text=_("Spyder documentation"),
            icon=self.create_icon("DialogHelpButton"),
            triggered=lambda: start_file(__docs_url__),
            context=Qt.ApplicationShortcut,
            register_shortcut=True,
            shortcut_context="_")

        spyder_video_url = ("https://www.youtube.com/playlist"
                            "?list=PLPonohdiDqg9epClEcXoAPUiK0pN5eRoc")
        self.video_action = self.create_action(
            ApplicationActions.SpyderDocumentationVideoAction,
            text=_("Tutorial videos"),
            icon=self.create_icon("VideoIcon"),
            triggered=lambda: start_file(spyder_video_url))

        # Support actions
        self.trouble_action = self.create_action(
            ApplicationActions.SpyderTroubleshootingAction,
            _("Troubleshooting..."),
            triggered=lambda: start_file(__trouble_url__))
        self.report_action = self.create_action(
            ConsoleActions.SpyderReportAction,
            _("Report issue..."),
            icon=self.create_icon('bug'),
            triggered=self.sig_report_issue_requested)
        self.dependencies_action = self.create_action(
            ApplicationActions.SpyderDependenciesAction,
            _("Dependencies..."),
            triggered=self.show_dependencies,
            icon=self.create_icon('advanced'))
        self.check_updates_action = self.create_action(
            ApplicationActions.SpyderCheckUpdatesAction,
            _("Check for updates..."),
            triggered=self.check_updates)
        self.support_group_action = self.create_action(
            ApplicationActions.SpyderSupportAction,
            _("Spyder support..."),
            triggered=lambda: start_file(__forum_url__))

        # About action
        self.about_action = self.create_action(
            ApplicationActions.SpyderAbout,
            _("About %s...") % "Spyder",
            icon=self.create_icon('MessageBoxInformation'),
            triggered=self.show_about,
            menurole=QAction.AboutRole)

        # Tools actions
        if WinUserEnvDialog is not None:
            self.winenv_action = self.create_action(
                ApplicationActions.SpyderWindowsEnvVariables,
                _("Current user environment variables..."),
                icon=self.create_icon('win_env'),
                tip=_("Show and edit current user environment "
                      "variables in Windows registry "
                      "(i.e. for all sessions)"),
                triggered=self.show_windows_env_variables)
        else:
            self.winenv_action = None

        # Application base actions
        self.restart_action = self.create_action(
            ApplicationActions.SpyderRestart,
            _("&Restart"),
            icon=self.create_icon('restart'),
            tip=_("Restart"),
            triggered=self.restart_normal,
            context=Qt.ApplicationShortcut,
            shortcut_context="_",
            register_shortcut=True)

        self.restart_debug_action = self.create_action(
            ApplicationActions.SpyderRestartDebug,
            _("&Restart in debug mode"),
            tip=_("Restart in debug mode"),
            triggered=self.restart_debug,
            context=Qt.ApplicationShortcut,
            shortcut_context="_",
            register_shortcut=True)

        # Debug logs
        if get_debug_level() >= 2:
            self.menu_debug_logs = self.create_menu(
                ApplicationPluginMenus.DebugLogsMenu, _("Debug logs"))

            # The menu can't be built at startup because Completions can
            # start after Application.
            self.menu_debug_logs.aboutToShow.connect(
                self.create_debug_log_actions)

    def update_actions(self):
        pass

    # ---- Other functionality
    # -------------------------------------------------------------------------
    def on_close(self):
        """To call from Spyder when the plugin is closed."""
        self.dialog_manager.close_all()
        if self.updates_timer is not None:
            self.updates_timer.stop()
        if self.thread_updates is not None:
            self.thread_updates.quit()
            self.thread_updates.wait()
        if self.dependencies_thread is not None:
            self.dependencies_thread.quit()
            self.dependencies_thread.wait()

    @Slot()
    def show_about(self):
        """Show Spyder About dialog."""
        abt = AboutDialog(self)
        abt.show()

    @Slot()
    def show_windows_env_variables(self):
        """Show Windows current user environment variables."""
        self.dialog_manager.show(WinUserEnvDialog(self))

    # ---- Updates
    # -------------------------------------------------------------------------
    def _check_updates_ready(self):
        """Show results of the Spyder update checking process."""

        # `feedback` = False is used on startup, so only positive feedback is
        # given. `feedback` = True is used when after startup (when using the
        # menu action, and gives feeback if updates are, or are not found.
        feedback = self.give_updates_feedback

        # Get results from worker
        update_available = self.worker_updates.update_available
        latest_release = self.worker_updates.latest_release
        error_msg = self.worker_updates.error

        # Release url
        if sys.platform == 'darwin':
            url_r = ('https://github.com/spyder-ide/spyder/releases/latest/'
                     'download/Spyder.dmg')
        else:
            url_r = ('https://github.com/spyder-ide/spyder/releases/latest/'
                     'download/Spyder_64bit_full.exe')
        url_i = 'https://docs.spyder-ide.org/installation.html'

        # Define the custom QMessageBox
        box = MessageCheckBox(icon=QMessageBox.Information, parent=self)
        box.setWindowTitle(_("New Spyder version"))
        box.setAttribute(Qt.WA_ShowWithoutActivating)
        box.set_checkbox_text(_("Check for updates at startup"))
        box.setStandardButtons(QMessageBox.Ok)
        box.setDefaultButton(QMessageBox.Ok)

        # Adjust the checkbox depending on the stored configuration
        option = 'check_updates_on_startup'
        check_updates = self.get_conf(option)
        box.set_checked(check_updates)

        if error_msg is not None:
            msg = error_msg
            box.setText(msg)
            box.set_check_visible(False)
            box.exec_()
            check_updates = box.is_checked()
        else:
            if update_available:
                header = _("<b>Spyder {} is available!</b><br><br>").format(
                    latest_release)
                footer = _(
                    "For more information visit our "
                    "<a href=\"{}\">installation guide</a>.").format(url_i)
                if is_anaconda():
                    content = _(
                        "<b>Important note:</b> Since you installed "
                        "Spyder with Anaconda, please <b>don't</b> use "
                        "<code>pip</code> to update it as that will break "
                        "your installation.<br><br>"
                        "Instead, run the following commands in a "
                        "terminal:<br>"
                        "<code>conda update anaconda</code><br>"
                        "<code>conda install spyder={}</code><br><br>").format(
                            latest_release)
                else:
                    content = _("Click <a href=\"{}\">this link</a> to "
                                "download it.<br><br>").format(url_r)
                msg = header + content + footer
                box.setText(msg)
                box.set_check_visible(True)
                box.show()
                check_updates = box.is_checked()
            elif feedback:
                msg = _("Spyder is up to date.")
                box.setText(msg)
                box.set_check_visible(False)
                box.exec_()
                check_updates = box.is_checked()

        # Update checkbox based on user interaction
        self.set_conf(option, check_updates)

        # Enable check_updates_action after the thread has finished
        self.check_updates_action.setDisabled(False)

        # Provide feeback when clicking menu if check on startup is on
        self.give_updates_feedback = True

    @Slot()
    def check_updates(self, startup=False):
        """Check for spyder updates on github releases using a QThread."""
        # Disable check_updates_action while the thread is working
        self.check_updates_action.setDisabled(True)

        if self.thread_updates is not None:
            self.thread_updates.quit()
            self.thread_updates.wait()

        self.thread_updates = QThread(None)
        self.worker_updates = WorkerUpdates(self, startup=startup)
        self.worker_updates.sig_ready.connect(self._check_updates_ready)
        self.worker_updates.sig_ready.connect(self.thread_updates.quit)
        self.worker_updates.moveToThread(self.thread_updates)
        self.thread_updates.started.connect(self.worker_updates.start)

        # Delay starting this check to avoid blocking the main window
        # while loading.
        # Fixes spyder-ide/spyder#15839
        self.updates_timer = QTimer(self)
        self.updates_timer.setInterval(3000)
        self.updates_timer.setSingleShot(True)
        self.updates_timer.timeout.connect(self.thread_updates.start)
        self.updates_timer.start()

    # ---- Dependencies
    # -------------------------------------------------------------------------
    @Slot()
    def show_dependencies(self):
        """Show Spyder Dependencies dialog."""
        # This is here in case the user tries to display the dialog before
        # dependencies_thread has finished.
        if not dependencies.DEPENDENCIES:
            dependencies.declare_dependencies()

        dlg = DependenciesDialog(self)
        dlg.set_data(dependencies.DEPENDENCIES)
        dlg.show()

    def _compute_dependencies(self):
        """Compute dependencies without errors."""
        # Skip error when trying to register dependencies several times.
        # This can happen if the user tries to display the dependencies
        # dialog before dependencies_thread has finished.
        try:
            dependencies.declare_dependencies()
        except ValueError:
            pass

    def compute_dependencies(self):
        """Compute dependencies."""
        self.dependencies_thread.run = self._compute_dependencies
        self.dependencies_thread.finished.connect(
            self.report_missing_dependencies)

        # This avoids computing missing deps before the window is fully up
        dependencies_timer = QTimer(self)
        dependencies_timer.setInterval(10000)
        dependencies_timer.setSingleShot(True)
        dependencies_timer.timeout.connect(self.dependencies_thread.start)
        dependencies_timer.start()

    @Slot()
    def report_missing_dependencies(self):
        """Show a QMessageBox with a list of missing hard dependencies."""
        missing_deps = dependencies.missing_dependencies()

        if missing_deps:
            InstallerMissingDependencies(missing_deps)

            # We change '<br>' by '\n', in order to replace the '<'
            # that appear in our deps by '&lt' (to not break html
            # formatting) and finally we restore '<br>' again.
            missing_deps = (missing_deps.replace('<br>', '\n').replace(
                '<', '&lt;').replace('\n', '<br>'))

            message = (
                _("<b>You have missing dependencies!</b>"
                  "<br><br><tt>%s</tt><br>"
                  "<b>Please install them to avoid this message.</b>"
                  "<br><br>"
                  "<i>Note</i>: Spyder could work without some of these "
                  "dependencies, however to have a smooth experience when "
                  "using Spyder we <i>strongly</i> recommend you to install "
                  "all the listed missing dependencies.<br><br>"
                  "Failing to install these dependencies might result in bugs."
                  " Please be sure that any found bugs are not the direct "
                  "result of missing dependencies, prior to reporting a new "
                  "issue.") % missing_deps)

            message_box = QMessageBox(self)
            message_box.setIcon(QMessageBox.Critical)
            message_box.setAttribute(Qt.WA_DeleteOnClose)
            message_box.setAttribute(Qt.WA_ShowWithoutActivating)
            message_box.setStandardButtons(QMessageBox.Ok)
            message_box.setWindowModality(Qt.NonModal)
            message_box.setWindowTitle(_('Error'))
            message_box.setText(message)
            message_box.show()

    # ---- Restart
    # -------------------------------------------------------------------------
    @Slot()
    def restart_normal(self):
        """Restart in standard mode."""
        os.environ['SPYDER_DEBUG'] = ''
        self.sig_restart_requested.emit()

    @Slot()
    def restart_debug(self):
        """Restart in debug mode."""
        box = QMessageBox(self)
        box.setWindowTitle(_("Question"))
        box.setIcon(QMessageBox.Question)
        box.setText(_("Which debug mode do you want Spyder to restart in?"))

        button_verbose = QPushButton(_('Verbose'))
        button_minimal = QPushButton(_('Minimal'))
        box.addButton(button_verbose, QMessageBox.AcceptRole)
        box.addButton(button_minimal, QMessageBox.AcceptRole)
        box.setStandardButtons(QMessageBox.Cancel)
        box.exec_()

        if box.clickedButton() == button_minimal:
            os.environ['SPYDER_DEBUG'] = '2'
        elif box.clickedButton() == button_verbose:
            os.environ['SPYDER_DEBUG'] = '3'
        else:
            return

        self.sig_restart_requested.emit()

    # ---- Log files
    # -------------------------------------------------------------------------
    def create_debug_log_actions(self):
        """Create an action for each lsp and debug log file."""
        self.menu_debug_logs.clear_actions()

        files = [os.environ['SPYDER_DEBUG_FILE']]
        files += glob.glob(os.path.join(get_conf_path('lsp_logs'), '*.log'))

        debug_logs_actions = []
        for file in files:
            action = self.create_action(
                file,
                os.path.basename(file),
                tip=file,
                triggered=lambda _, file=file: self.load_log_file(file),
                overwrite=True,
                register_action=False)
            debug_logs_actions.append(action)

        # Add Spyder log on its own section
        self.add_item_to_menu(debug_logs_actions[0],
                              self.menu_debug_logs,
                              section=LogsMenuSections.SpyderLogSection)

        # Add LSP logs
        for action in debug_logs_actions[1:]:
            self.add_item_to_menu(action,
                                  self.menu_debug_logs,
                                  section=LogsMenuSections.LSPLogsSection)

        # Render menu
        self.menu_debug_logs._render()

    def load_log_file(self, file):
        """Load log file in editor"""
        self.sig_load_log_file.emit(file)

    # ---- DPI changes
    # -------------------------------------------------------------------------
    def set_window(self, window):
        """Set window property of main window."""
        self._window = window

    def handle_new_screen(self, new_screen):
        """Connect DPI signals for new screen."""
        if new_screen is not None:
            new_screen_dpi = new_screen.logicalDotsPerInch()
            if self.current_dpi != new_screen_dpi:
                self.show_dpi_change_message(new_screen_dpi)
            else:
                new_screen.logicalDotsPerInchChanged.connect(
                    self.show_dpi_change_message)

    def handle_dpi_change_response(self, result, dpi):
        """Handle dpi change message dialog result."""
        if self.dpi_messagebox.is_checked():
            self.set_conf('show_dpi_message', False)

        self.dpi_messagebox = None

        if result == 0:  # Restart button was clicked
            # Activate HDPI auto-scaling option since is needed for a
            # proper display when using OS scaling
            self.set_conf('normal_screen_resolution', False)
            self.set_conf('high_dpi_scaling', True)
            self.set_conf('high_dpi_custom_scale_factor', False)
            self.sig_restart_requested.emit()
        else:
            # Update current dpi for future checks
            self.current_dpi = dpi

    def show_dpi_change_message(self, dpi):
        """Show message to restart Spyder since the DPI scale changed."""
        if not self.get_conf('show_dpi_message'):
            return

        if self.current_dpi != dpi:
            # Check the window state to not show the message if the window
            # is in fullscreen mode.
            window = self._window.windowHandle()
            if (window.windowState() == Qt.WindowFullScreen
                    and sys.platform == 'darwin'):
                return

            if self.get_conf('high_dpi_scaling'):
                return

            if self.dpi_messagebox is not None:
                self.dpi_messagebox.activateWindow()
                self.dpi_messagebox.raise_()
                return

            self.dpi_messagebox = MessageCheckBox(icon=QMessageBox.Warning,
                                                  parent=self)

            self.dpi_messagebox.set_checkbox_text(_("Don't show again."))
            self.dpi_messagebox.set_checked(False)
            self.dpi_messagebox.set_check_visible(True)

            self.dpi_messagebox.setText(
                _("A monitor scale change was detected. <br><br>"
                  "We recommend restarting Spyder to ensure that it's properly "
                  "displayed. If you don't want to do that, please be sure to "
                  "activate the option<br><br><tt>Enable auto high DPI scaling"
                  "</tt><br><br>in <tt>Preferences > Application > "
                  "Interface</tt>, in case Spyder is not displayed "
                  "correctly.<br><br>"
                  "Do you want to restart Spyder?"))

            self.dpi_messagebox.addButton(_('Restart now'), QMessageBox.NoRole)
            dismiss_button = self.dpi_messagebox.addButton(
                _('Dismiss'), QMessageBox.NoRole)
            self.dpi_messagebox.setDefaultButton(dismiss_button)
            self.dpi_messagebox.finished.connect(
                lambda result: self.handle_dpi_change_response(result, dpi))
            self.dpi_messagebox.open()

            # Show dialog always in the primary screen to prevent not being
            # able to see it if a screen gets disconnected while
            # in suspended state. See spyder-ide/spyder#16390
            dpi_messagebox_width = self.dpi_messagebox.rect().width()
            dpi_messagebox_height = self.dpi_messagebox.rect().height()
            screen_geometry = QGuiApplication.primaryScreen().geometry()
            x = (screen_geometry.width() - dpi_messagebox_width) / 2
            y = (screen_geometry.height() - dpi_messagebox_height) / 2

            # Convert coordinates to int to avoid a TypeError in Python 3.10
            # Fixes spyder-ide/spyder#17677
            self.dpi_messagebox.move(int(x), int(y))
            self.dpi_messagebox.adjustSize()
Esempio n. 16
0
class KiteClient(QObject, KiteMethodProviderMixIn):
    sig_response_ready = Signal(int, dict)
    sig_client_started = Signal(list)
    sig_client_not_responding = Signal()
    sig_perform_request = Signal(int, str, object)

    def __init__(self, parent, enable_code_snippets=True):
        QObject.__init__(self, parent)
        self.endpoint = None
        self.requests = {}
        self.languages = []
        self.mutex = QMutex()
        self.opened_files = {}
        self.thread_started = False
        self.enable_code_snippets = enable_code_snippets
        self.thread = QThread()
        self.moveToThread(self.thread)
        self.thread.started.connect(self.started)
        self.sig_perform_request.connect(self.perform_request)

    def start(self):
        if not self.thread_started:
            self.thread.start()
        logger.debug('Starting Kite HTTP session...')
        self.endpoint = requests.Session()
        self.languages = self.get_languages()
        self.sig_client_started.emit(self.languages)

    def started(self):
        self.thread_started = True

    def stop(self):
        if self.thread_started:
            logger.debug('Closing Kite HTTP session...')
            self.endpoint.close()
            self.thread.quit()

    def get_languages(self):
        verb, url = KITE_ENDPOINTS.LANGUAGES_ENDPOINT
        success, response = self.perform_http_request(verb, url)
        if response is None:
            response = ['python']
        return response

    def perform_http_request(self, verb, url, params=None):
        response = None
        success = False
        http_method = getattr(self.endpoint, verb)
        try:
            http_response = http_method(url, json=params)
        except Exception as error:
            logger.debug('Kite request error: {0}'.format(error))
            return False, None
        success = http_response.status_code == 200
        if success:
            try:
                response = http_response.json()
            except Exception:
                response = http_response.text
                response = None if response == '' else response
        return success, response

    def send(self, method, params, url_params):
        response = None
        if self.endpoint is not None and method in KITE_REQUEST_MAPPING:
            http_verb, path = KITE_REQUEST_MAPPING[method]
            path = path.format(**url_params)
            try:
                success, response = self.perform_http_request(
                    http_verb, path, params)
            except (ConnectionRefusedError, ConnectionError):
                return response
        return response

    def perform_request(self, req_id, method, params):
        if method in self.sender_registry:
            logger.debug('Perform {0} request with id {1}'.format(
                method, req_id))
            handler_name = self.sender_registry[method]
            handler = getattr(self, handler_name)
            response = handler(params)
            if method in self.handler_registry:
                converter_name = self.handler_registry[method]
                converter = getattr(self, converter_name)
                if response is not None:
                    response = converter(response)
            if response is not None:
                self.sig_response_ready.emit(req_id, response)
Esempio n. 17
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
Esempio n. 18
0
class MainApp(QWidget):
    LANDSCAPE = 0
    PORTRAIT = 1

    stop_signal = Signal()
    quality_changed = Signal(str)
    style_changed = Signal(object)
    last_frame_changed = Signal(object)

    def __init__(self):
        QWidget.__init__(self)

        self.styles = load_styles()
        self.image = StyledCapture(QImage(256, 256, QImage.Format_RGB888),
                                   '')  # placeholder
        self.freeze = None
        if isinstance(settings.CAPTURE_HANDLER, string_types):
            self.capture_handler = locate(settings.CAPTURE_HANDLER)
        else:
            self.capture_handler = settings.CAPTURE_HANDLER
        self.setup_ui()

        self.frame_grabber = FrameGrabber(settings.SIZES[0])
        self.frame_thread = QThread()
        # self.frame_grabber.image_signal.connect(self.display_frame)
        self.frame_grabber.last_frame_signal.connect(self.last_frame)
        self.frame_grabber.moveToThread(self.frame_thread)
        self.frame_thread.started.connect(self.frame_grabber.grab)
        self.stop_signal.connect(self.frame_grabber.stop_work)
        self.quality_changed.connect(self.frame_grabber.change_size)
        self.frame_thread.start()

        self.image_processor = ImageProcessor(self.styles[0])
        self.image_thread = QThread()
        self.image_processor.image_signal.connect(self.display_frame)
        self.image_processor.moveToThread(self.image_thread)
        self.image_thread.started.connect(self.image_processor.monitor_images)
        self.stop_signal.connect(self.image_processor.stop_work)
        self.style_changed.connect(self.image_processor.change_style)
        self.last_frame_changed.connect(self.image_processor.change_last_frame)
        self.image_thread.start()

    def closeEvent(self, event):
        self.stop_signal.emit()
        self.frame_thread.quit()
        self.image_thread.quit()
        self.frame_thread.wait()
        self.image_thread.wait()

    def setup_ui(self):
        """Initialize widgets."""
        def switch_style(i):
            view = self.landscape_view if self.view_mode == MainApp.LANDSCAPE else self.portrait_view
            self.style_changed.emit(self.styles[i])
            view.selected_style = self.styles[i]

        for i in range(min(len(self.styles), len(settings.STYLE_SHORTCUTS))):
            QShortcut(QKeySequence(settings.STYLE_SHORTCUTS[i]),
                      self,
                      lambda x=i: switch_style(x))

        self.landscape_view = LandscapeView(self.styles)
        self.landscape_view.style_changed.connect(self.style_button_clicked)
        self.landscape_view.toggle_fullscreen_signal.connect(
            self.toggle_fullscreen)
        self.landscape_view.quality_changed.connect(self.quality_choice)
        self.portrait_view = PortraitView(self.styles)
        self.portrait_view.style_changed.connect(self.style_button_clicked)
        self.portrait_view.toggle_fullscreen_signal.connect(
            self.toggle_fullscreen)
        self.portrait_view.quality_changed.connect(self.quality_choice)

        self.main_layout = QStackedLayout()
        self.main_layout.addWidget(self.landscape_view)
        self.main_layout.addWidget(self.portrait_view)
        self.setLayout(self.main_layout)

        self.view_mode = MainApp.LANDSCAPE

        self.setStyleSheet('background-color:black;'
                           'font-family: Arial;'
                           'font-style: normal;'
                           'font-size: 12pt;'
                           'font-weight: bold;'
                           'color:white;')
        self.setWindowTitle('Stylize')

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape and not settings.KIOSK:
            if self.windowState() & Qt.WindowFullScreen:
                self.showNormal()
        elif event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
            self.image_capture()

    def last_frame(self, frame):
        self.last_frame_changed.emit(frame)

    def display_frame(self, image):
        self.image = image
        if not self.freeze:
            if self.view_mode == MainApp.LANDSCAPE:
                self.landscape_view.set_image(self.image.image)
            else:
                self.portrait_view.set_image(self.image.image)

    def resizeEvent(self, event):
        super(MainApp, self).resizeEvent(event)
        new_view_mode = MainApp.LANDSCAPE if self.width() >= self.height(
        ) else MainApp.PORTRAIT

        if self.view_mode != new_view_mode:
            old_view = self.portrait_view if new_view_mode == MainApp.LANDSCAPE else self.landscape_view
            new_view = self.landscape_view if new_view_mode == MainApp.LANDSCAPE else self.portrait_view

            new_view.quality = old_view.quality
            new_view.selected_style = old_view.selected_style

            self.view_mode = new_view_mode
            self.main_layout.setCurrentIndex(self.view_mode)

    def style_button_clicked(self, style):
        self.style_changed.emit(style)

    def toggle_fullscreen(self):
        if self.windowState() & Qt.WindowFullScreen:
            self.showNormal()
        else:
            self.showFullScreen()

    def quality_choice(self, quality):
        self.quality_changed.emit(quality)

    def image_capture(self):
        self.freeze = self.image.copy()  # prevent background update
        try:
            self.capture_handler(self, self.freeze)
        except Exception:
            msg = QMessageBox(self)
            msg.setIcon(QMessageBox.Critical)
            msg.setText('Error during capture.')
            msg.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)
            msg.exec_()

        self.freeze = None
Esempio n. 19
0
class KiteClient(QObject, KiteMethodProviderMixIn):
    sig_response_ready = Signal(int, dict)
    sig_client_started = Signal(list)
    sig_client_not_responding = Signal()
    sig_perform_request = Signal(int, str, object)

    MAX_SERVER_CONTACT_RETRIES = 40

    def __init__(self, parent):
        QObject.__init__(self, parent)
        self.contact_retries = 0
        self.endpoint = None
        self.requests = {}
        self.languages = []
        self.mutex = QMutex()
        self.opened_files = {}
        self.alive = False
        self.thread_started = False
        self.thread = QThread()
        self.moveToThread(self.thread)
        self.thread.started.connect(self.started)
        self.sig_perform_request.connect(self.perform_request)

    def __wait_http_session_to_start(self):
        logger.debug('Waiting Kite HTTP endpoint to be available...')
        _, url = KITE_ENDPOINTS.ALIVE_ENDPOINT
        try:
            code = requests.get(url).status_code
        except Exception:
            code = 500

        if self.contact_retries == self.MAX_SERVER_CONTACT_RETRIES:
            logger.debug('Kite server is not answering')
            self.sig_client_not_responding.emit()
        elif code != 200:
            self.contact_retries += 1
            QTimer.singleShot(250, self.__wait_http_session_to_start)
        elif code == 200:
            self.alive = True
            self.start_client()

    def start(self):
        if not self.thread_started:
            self.thread.start()
        self.__wait_http_session_to_start()

    def start_client(self):
        logger.debug('Starting Kite HTTP session...')
        self.endpoint = requests.Session()
        self.languages = self.get_languages()
        self.sig_client_started.emit(self.languages)

    def started(self):
        self.thread_started = True

    def stop(self):
        if self.thread_started:
            logger.debug('Closing Kite HTTP session...')
            self.endpoint.close()
            self.thread.quit()

    def get_languages(self):
        verb, url = KITE_ENDPOINTS.LANGUAGES_ENDPOINT
        success, response = self.perform_http_request(verb, url)
        return response

    def perform_http_request(self, verb, url, params=None):
        response = None
        success = False
        http_method = getattr(self.endpoint, verb)
        http_response = http_method(url, json=params)
        success = http_response.status_code == 200
        if success:
            try:
                response = http_response.json()
            except Exception:
                response = http_response.text
                response = None if response == '' else response
        return success, response

    def send(self, method, params, url_params):
        response = None
        if self.endpoint is not None and method in KITE_REQUEST_MAPPING:
            if self.alive:
                http_verb, path = KITE_REQUEST_MAPPING[method]
                path = path.format(**url_params)
                try:
                    success, response = self.perform_http_request(
                        http_verb, path, params)
                except (ConnectionRefusedError, ConnectionError):
                    self.alive = False
                    self.endpoint = None
                    self.contact_retries = 0
                    self.__wait_http_session_to_start()
                    return response
        return response

    def perform_request(self, req_id, method, params):
        if method in self.sender_registry:
            logger.debug('Perform {0} request with id {1}'.format(
                method, req_id))
            handler_name = self.sender_registry[method]
            handler = getattr(self, handler_name)
            response = handler(params)
            if method in self.handler_registry:
                converter_name = self.handler_registry[method]
                converter = getattr(self, converter_name)
                response = converter(response)
            if response is not None:
                self.sig_response_ready.emit(req_id, response)
Esempio n. 20
0
class RefaceDXLibApp(QApplication):
    """The main application object with the central event handling."""

    name = "Reface DX Lib"

    def __init__(self, args=None):
        if args is None:
            args = sys.argv

        super().__init__(args)
        self.setOrganizationName('chrisarndt.de')
        self.setOrganizationDomain('chrisarndt.de')
        self.setApplicationName(self.name)
        QSettings.setDefaultFormat(QSettings.IniFormat)
        self.config = QSettings()
        self.config.setIniCodec('UTF-8')
        QIcon.setThemeName(self.config.value('gui/icon_theme', "tango"))
        self.debug = True if '-v' in args[1:] else self.config.value(
            'application/debug', False)
        logging.basicConfig(
            level=logging.DEBUG if self.debug else logging.INFO,
            format='%(levelname)s - %(message)s')

        self.mainwin = RefaceDXLibMainWin(self.tr(self.name))
        self.load_database(
            self.config.value('database/last_opened', 'refacedx.db'))

        self.midiin_conn = None
        self.midiout_conn = None
        self.setup_midi_thread()

        # signal connections
        self.aboutToQuit.connect(self.quit)
        self.mainwin.action_open.triggered.connect(self.open_database)
        self.mainwin.action_quit.triggered.connect(self.quit)
        self.mainwin.action_import.triggered.connect(self.import_patches)
        self.mainwin.action_export.triggered.connect(self.export_patches)
        self.mainwin.action_send.triggered.connect(self.send_patches)
        self.mainwin.action_request.triggered.connect(self.request_patch)
        self.mainwin.action_delete.triggered.connect(self.delete_patches)

        # dialogs (initialized on-demand)
        self.add_patch_dialog = None

        self.style = DarkAppStyle(self)
        self.mainwin.show()

    def load_database(self, filename):
        db_uri = 'sqlite:///{}'.format(filename)
        self.session = initdb(db_uri,
                              debug=self.config.value('database/debug', False))
        self.patches = PatchlistTableModel(self.session)
        self.mainwin.set_patchtable_model(self.patches)

    def setup_midi_thread(self):
        self.midithread = QThread()
        self.midiworker = MidiWorker(self.config)
        self.midiworker.moveToThread(self.midithread)

        self.midithread.started.connect(self.midiworker.initialize)
        self.midiworker.send_patch_start.connect(
            partial(self.mainwin.set_send_action_enabled, False))
        self.midiworker.send_patch_complete.connect(
            partial(self.mainwin.set_send_action_enabled, True))
        self.midiworker.recv_patch_start.connect(
            partial(self.mainwin.set_request_action_enabled, False))
        self.midiworker.recv_patch_complete.connect(self.receive_patch)
        self.midiworker.recv_patch_failed.connect(
            partial(self.mainwin.set_request_action_enabled, True))
        self.midiworker.input_ports_changed.connect(
            self.build_midi_input_selector)
        self.midiworker.output_ports_changed.connect(
            self.build_midi_output_selector)

        # Start thread
        self.midithread.start()
        self.timer = QTimer()
        self.timer.timeout.connect(self.midiworker.scan_ports.emit)
        self.timer.start(3000)

    @Slot(object)
    def build_midi_input_selector(self, ports):
        log.debug("Building MIDI input selector...")
        cb = self.mainwin.midiin_cb

        if self.midiin_conn:
            cb.currentIndexChanged.disconnect(self.set_midiin_port)

        cb.setEnabled(False)
        cb.clear()
        selected = -1

        for i, (port, is_open) in enumerate(ports):
            cb.addItem(port, port)

            if is_open:
                selected = i

        cb.setCurrentIndex(selected)
        self.midiin_conn = cb.currentIndexChanged.connect(self.set_midiin_port)
        cb.setEnabled(True)
        log.debug("MIDI input selector (re-)built.")

    @Slot(object)
    def build_midi_output_selector(self, ports):
        log.debug("Building MIDI output selector...")
        cb = self.mainwin.midiout_cb

        if self.midiout_conn:
            cb.currentIndexChanged.disconnect(self.set_midiout_port)

        cb.setEnabled(False)
        cb.clear()
        selected = -1

        for i, (port, is_open) in enumerate(ports):
            cb.addItem(port, port)

            if is_open:
                selected = i

        cb.setCurrentIndex(selected)
        self.midiout_conn = cb.currentIndexChanged.connect(
            self.set_midiout_port)
        cb.setEnabled(True)
        log.debug("MIDI output selector (re-)built.")

    def set_midiin_port(self, index):
        log.debug("MIDI input selector index changed: %r", index)
        if index != -1:
            port = self.mainwin.midiin_cb.itemData(index)
            self.midiworker.set_input_port.emit(port)

    def set_midiout_port(self, index):
        log.debug("MIDI output selector index changed: %r", index)
        if index != -1:
            port = self.mainwin.midiout_cb.itemData(index)
            self.midiworker.set_output_port.emit(port)

    # action handlers
    def quit(self):
        self.midiworker.close.emit()
        self.midithread.quit()
        self.midithread.wait()
        self.mainwin.close()

    def open_database(self):
        options = QFileDialog.Options()

        if not self.config.value('native_dialogs', False):
            options |= QFileDialog.DontUseNativeDialog

        filename, _ = QFileDialog.getOpenFileName(
            self.mainwin,
            self.tr("Open patch database"),
            self.config.value('paths/last_database_path', ''),
            "SQLite Database (*.sqlite *.db);;All Files (*)",
            options=options)

        if filename and exists(filename):
            self.config.setValue('paths/last_database_path', dirname(filename))
            log.info(f"Opening database file '{filename}'...")
            try:
                self.load_database(filename)
            except Exception as exc:
                log.exception(f"Error opening database file '{filename}'.")
                dlg = self.create_error_dlg(
                    self.tr("Could not load patch database <i>{}</i>.").format(
                        basename(filename)),
                    detail=str(exc),
                    ignore_buttons=False)
                dlg.exec_()
            else:
                self.config.setValue('database/last_opened', filename)

    def save_patch(self, data, **meta):
        name = meta.get('name', '').strip()

        if not name:
            name = get_patch_name(data)

        with self.session.begin():
            author = meta.get('author', '').strip()
            if author:
                author, created = get_or_create(self.session,
                                                Author,
                                                create_kwargs={'name': author},
                                                displayname=author)
            else:
                author = None

            manufacturer = meta.get('manufacturer', '').strip()
            if manufacturer:
                manufacturer, created = get_or_create(
                    self.session,
                    Manufacturer,
                    create_kwargs={'name': manufacturer},
                    displayname=manufacturer)
            else:
                manufacturer = None

            device = meta.get('device', '').strip()
            if device:
                device, created = get_or_create(self.session,
                                                Device,
                                                create_kwargs={'name': device},
                                                displayname=device)
            else:
                device = None

            patch = Patch(name=name,
                          displayname=meta.get('displayname', '').strip()
                          or name,
                          description=meta.get('description', '').strip()
                          or None,
                          rating=meta.get('rating', 0),
                          author=author,
                          manufacturer=manufacturer,
                          device=device,
                          created=meta.get('created', datetime.now()),
                          data=set_patch_name(data, name))
            self.session.add(patch)

            tags = (tag.strip() for tag in meta.get('tags', '').split(','))
            patch.update_tags(self.session, (tag for tag in tags if tag))

    def request_patch(self):
        self.midiworker.request_patch.emit(None)

    def receive_patch(self, data):
        log.debug("Patch received: %s", get_patch_name(data))

        if self.add_patch_dialog is None:
            self.add_patch_dialog = AddPatchDialog(self)

        metadata = self.add_patch_dialog.new_from_data(
            data, self.tr("Add new patch"))

        if metadata:
            log.debug("Patch meta data: %r", metadata)
            self.save_patch(data, **metadata)
            self.patches._update()
            self.patches.layoutChanged.emit()

        self.mainwin.set_request_action_enabled(True)

    def delete_patches(self):
        if self.mainwin.selection.hasSelection():
            rows = sorted(
                [r.row() for r in self.mainwin.selection.selectedRows()])
            patches = tuple(self.patches.get_row(r).displayname for r in rows)
            msg_box = QMessageBox()

            if len(rows) == 1:
                msg_box.setText(
                    self.tr("Delete patch '{}'?").format(patches[0]))
            else:
                msg_box.setText(
                    self.tr("Delete {} patches?").format(len(rows)))
                msg_box.setDetailedText('\n'.join(patches))

            msg_box.setInformativeText(
                self.tr("Patches can only be restored by re-importing them."))
            msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
            msg_box.setDefaultButton(QMessageBox.Cancel)
            msg_box.setIcon(QMessageBox.Warning)

            if msg_box.exec_() == QMessageBox.Yes:
                with self.session.begin():
                    for n, row in enumerate(rows):
                        self.patches.removeRows(row - n)

    def import_patches(self):
        options = QFileDialog.Options()

        if not self.config.value('native_dialogs', False):
            options |= QFileDialog.DontUseNativeDialog

        files, _ = QFileDialog.getOpenFileNames(
            self.mainwin,
            self.tr("Import SysEx patches"),
            self.config.value('paths/last_import_path', ''),
            "SysEx Files (*.syx);;All Files (*)",
            options=options)

        if files:
            self.config.setValue('paths/last_import_path', dirname(files[0]))

            self.patches.layoutAboutToBeChanged.emit()
            with self.session.begin():
                for file in files:
                    with open(file, 'rb') as syx:
                        data = syx.read()

                    #assert len(data) == 241
                    if is_reface_dx_voice(data):
                        name = get_patch_name(data)
                        displayname = splitext(basename(file))[0].replace(
                            '_', ' ').strip()
                        patch = Patch(name=name,
                                      displayname=displayname,
                                      data=data)
                        self.session.add(patch)

            # TODO: check if any patches were actually added
            self.patches._update()
            self.patches.layoutChanged.emit()

    def export_patches(self):
        if self.mainwin.selection.hasSelection():
            options = QFileDialog.Options()

            if not self.config.value('native_dialogs', False):
                options |= (QFileDialog.DontUseNativeDialog
                            | QFileDialog.ShowDirsOnly
                            | QFileDialog.DontResolveSymlinks)

            dir_ = QFileDialog.getExistingDirectory(
                self.mainwin,
                self.tr("Choose directory to export SysEx patches"),
                self.config.value('paths/last_export_path', ''),
                options=options)

            if dir_:
                self.config.setValue('paths/last_export_path', dir_)

                ignore_all = False
                exported = 0
                for row in self.mainwin.selection.selectedRows():
                    patch = self.patches.get_row(row)

                    try:
                        filename = patch.displayname.replace(' ', '_') + '.syx'
                        with open(join(dir_, filename), 'wb') as syx:
                            syx.write(patch.data)
                    except OSError as exc:
                        log.error("Could not write SysEx file at '%s': %s",
                                  filename, exc)

                        if ignore_all:
                            continue

                        dlg = self.create_error_dlg(
                            self.tr("Could not write SysEx file "
                                    "<i>{}</i>.").format(basename(filename)),
                            info=self.
                            tr("Abort export, ignore error or ignore all errors?"
                               ),
                            detail=str(exc))
                        ret = dlg.exec_()

                        if dlg.clickedButton() == dlg.btn_ignoreall:
                            ignore_all = True
                        elif ret == QMessageBox.Abort:
                            log.warning("Patch export aborted.")
                            break
                        elif ret == QMessageBox.Ignore:
                            pass
                        else:
                            log.error(
                                "Unknown error dialog response. This shouldn't happen."
                            )
                    else:
                        exported += 1

                self.set_status_text(
                    self.tr("{} patches exported.").format(exported))
            else:
                self.set_status_text(self.tr("Patch export cancelled."))

    def send_patches(self):
        if self.mainwin.selection.hasSelection():
            for row in self.mainwin.selection.selectedRows():
                patch = self.patches.get_row(row)
                self.midiworker.send_patch.emit(patch.data)
                log.debug("Sent patch: %s (%s)", patch.displayname, patch.name)

    def create_error_dlg(self,
                         message,
                         info=None,
                         detail=None,
                         ignore_buttons=True):
        dlg = QMessageBox()
        dlg.setText(message)

        if info:
            dlg.setInformativeText(info)

        if detail:
            dlg.setDetailedText(detail)

        dlg.setWindowTitle(self.name + self.tr(" - Error"))
        dlg.setIcon(QMessageBox.Critical)

        if ignore_buttons:
            dlg.setStandardButtons(QMessageBox.Abort | QMessageBox.Ignore)
            dlg.btn_ignoreall = dlg.addButton(self.tr("Ignore A&ll"),
                                              QMessageBox.ActionRole)
            dlg.setDefaultButton(QMessageBox.Ignore)
        else:
            dlg.setDefaultButton(QMessageBox.NoButton)

        return dlg

    def set_status_text(self, message, timeout=5000):
        self.mainwin.statusbar.showMessage(message, timeout)
Esempio n. 21
0
class KiteClient(QObject, KiteMethodProviderMixIn):
    sig_response_ready = Signal(int, dict)
    sig_client_started = Signal(list)
    sig_client_not_responding = Signal()
    sig_perform_request = Signal(int, str, object)
    sig_perform_status_request = Signal(str)
    sig_status_response_ready = Signal((str, ), (dict, ))
    sig_perform_onboarding_request = Signal()
    sig_onboarding_response_ready = Signal(str)

    def __init__(self, parent, enable_code_snippets=True):
        QObject.__init__(self, parent)
        self.endpoint = None
        self.requests = {}
        self.languages = []
        self.mutex = QMutex()
        self.opened_files = {}
        self.opened_files_status = {}
        self.thread_started = False
        self.enable_code_snippets = enable_code_snippets
        self.thread = QThread()
        self.moveToThread(self.thread)
        self.thread.started.connect(self.started)
        self.sig_perform_request.connect(self.perform_request)
        self.sig_perform_status_request.connect(self.get_status)
        self.sig_perform_onboarding_request.connect(self.get_onboarding_file)

    def start(self):
        if not self.thread_started:
            self.thread.start()
        logger.debug('Starting Kite HTTP session...')
        self.endpoint = requests.Session()
        self.languages = self.get_languages()
        self.sig_client_started.emit(self.languages)

    def started(self):
        self.thread_started = True

    def stop(self):
        if self.thread_started:
            logger.debug('Closing Kite HTTP session...')
            self.endpoint.close()
            self.thread.quit()

    def get_languages(self):
        verb, url = KITE_ENDPOINTS.LANGUAGES_ENDPOINT
        success, response = self.perform_http_request(verb, url)
        if response is None:
            response = ['python']
        return response

    def _get_onboarding_file(self):
        """Perform a request to get kite's onboarding file."""
        verb, url = KITE_ENDPOINTS.ONBOARDING_ENDPOINT
        success, response = self.perform_http_request(verb, url)
        return response

    def get_onboarding_file(self):
        """Get onboarding file."""
        onboarding_file = self._get_onboarding_file()
        self.sig_onboarding_response_ready.emit(onboarding_file)

    def _get_status(self, filename):
        """Perform a request to get kite status for a file."""
        verb, url = KITE_ENDPOINTS.STATUS_ENDPOINT
        if filename:
            url_params = {'filename': filename}
        else:
            url_params = {'filetype': 'python'}
        success, response = self.perform_http_request(verb,
                                                      url,
                                                      url_params=url_params)
        return response

    def get_status(self, filename):
        """Get kite status for a given filename."""
        kite_status = self._get_status(filename)
        if not filename or kite_status is None:
            kite_status = status()
            self.sig_status_response_ready[str].emit(kite_status)
        else:
            self.sig_status_response_ready[dict].emit(kite_status)

    def perform_http_request(self, verb, url, url_params=None, params=None):
        response = None
        http_method = getattr(self.endpoint, verb)
        try:
            http_response = http_method(url, params=url_params, json=params)
        except Exception as error:
            return False, None
        success = http_response.status_code == 200
        if success:
            try:
                response = http_response.json()
            except Exception:
                response = http_response.text
                response = None if response == '' else response
        return success, response

    def send(self, method, params, url_params):
        response = None
        if self.endpoint is not None and method in KITE_REQUEST_MAPPING:
            http_verb, path = KITE_REQUEST_MAPPING[method]
            encoded_url_params = {
                key: quote(value) if isinstance(value, TEXT_TYPES) else value
                for (key, value) in url_params.items()
            }
            path = path.format(**encoded_url_params)
            try:
                success, response = self.perform_http_request(http_verb,
                                                              path,
                                                              params=params)
            except (ConnectionRefusedError, ConnectionError):
                return response
        return response

    def perform_request(self, req_id, method, params):
        response = None
        if method in self.sender_registry:
            logger.debug('Perform {0} request with id {1}'.format(
                method, req_id))
            handler_name = self.sender_registry[method]
            handler = getattr(self, handler_name)
            response = handler(params)
            if method in self.handler_registry:
                converter_name = self.handler_registry[method]
                converter = getattr(self, converter_name)
                if response is not None:
                    response = converter(response)
        self.sig_response_ready.emit(req_id, response or {})
Esempio n. 22
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
Esempio n. 23
0
class FallbackActor(QObject):
    #: Signal emitted when the Thread is ready
    sig_fallback_ready = Signal()
    sig_set_tokens = Signal(int, dict)
    sig_mailbox = Signal(dict)

    def __init__(self, parent):
        QObject.__init__(self)
        self.stopped = False
        self.daemon = True
        self.mutex = QMutex()
        self.file_tokens = {}
        self.diff_patch = diff_match_patch()
        self.thread = QThread()
        self.moveToThread(self.thread)

        self.thread.started.connect(self.started)
        self.sig_mailbox.connect(self.handle_msg)

    def tokenize(self, text, language):
        """
        Return all tokens in `text` and all keywords associated by
        Pygments to `language`.
        """
        try:
            lexer = get_lexer_by_name(language)
            keywords = get_keywords(lexer)
        except Exception:
            keywords = []
        keyword_set = set(keywords)
        keywords = [{
            'kind': CompletionItemKind.KEYWORD,
            'insertText': keyword,
            'sortText': u'zz{0}'.format(keyword[0].lower()),
            'filterText': keyword,
            'documentation': ''
        } for keyword in keywords]
        # logger.debug(keywords)
        # tokens = list(lexer.get_tokens(text))
        # logger.debug(tokens)
        tokens = get_words(text, language)
        tokens = [{
            'kind': CompletionItemKind.TEXT,
            'insertText': token,
            'sortText': u'zz{0}'.format(token[0].lower()),
            'filterText': token,
            'documentation': ''
        } for token in tokens]
        for token in tokens:
            if token['insertText'] not in keyword_set:
                keywords.append(token)
        return keywords

    def stop(self):
        """Stop actor."""
        with QMutexLocker(self.mutex):
            logger.debug("Fallback plugin stopping...")
            self.thread.quit()

    def start(self):
        """Start thread."""
        self.thread.start()

    def started(self):
        """Thread started."""
        logger.debug('Fallback plugin starting...')
        self.sig_fallback_ready.emit()

    @Slot(dict)
    def handle_msg(self, message):
        """Handle one message"""
        msg_type, _id, file, msg = [
            message[k] for k in ('type', 'id', 'file', 'msg')
        ]
        logger.debug('Got request id {0}: {1} for file {2}'.format(
            _id, msg_type, file))
        if msg_type == LSPRequestTypes.DOCUMENT_DID_OPEN:
            self.file_tokens[file] = {
                'text': msg['text'],
                'language': msg['language']
            }
        elif msg_type == LSPRequestTypes.DOCUMENT_DID_CHANGE:
            if file not in self.file_tokens:
                self.file_tokens[file] = {
                    'text': '',
                    'language': msg['language']
                }
            diff = msg['diff']
            text = self.file_tokens[file]
            text, _ = self.diff_patch.patch_apply(diff, text['text'])
            self.file_tokens[file]['text'] = text
        elif msg_type == LSPRequestTypes.DOCUMENT_DID_CLOSE:
            self.file_tokens.pop(file, {})
        elif msg_type == LSPRequestTypes.DOCUMENT_COMPLETION:
            tokens = []
            if file in self.file_tokens:
                text_info = self.file_tokens[file]
                tokens = self.tokenize(text_info['text'],
                                       text_info['language'])
            tokens = {'params': tokens}
            self.sig_set_tokens.emit(_id, tokens)
Esempio n. 24
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()