예제 #1
0
    def run(self, argv, error_message, in_build_dir=False, timeout=30000):
        """ Execute a command and capture the output. """

        if in_build_dir:
            project = self._project

            saved_cwd = os.getcwd()
            build_dir = project.path_from_user(project.build_dir)
            build_dir = QDir.toNativeSeparators(build_dir)
            os.chdir(build_dir)
            self._message_handler.verbose_message(
                "{0} is now the current directory".format(build_dir))
        else:
            saved_cwd = None

        self._message_handler.verbose_message("Running '{0}'".format(
            ' '.join(argv)))

        QCoreApplication.processEvents()

        process = QProcess()

        process.readyReadStandardOutput.connect(
            lambda: self._message_handler.progress_message(
                QTextCodec.codecForLocale().toUnicode(
                    process.readAllStandardOutput()).strip()))

        stderr_output = QByteArray()
        process.readyReadStandardError.connect(
            lambda: stderr_output.append(process.readAllStandardError()))

        process.start(argv[0], argv[1:])
        finished = process.waitForFinished(timeout)

        if saved_cwd is not None:
            os.chdir(saved_cwd)
            self._message_handler.verbose_message(
                "{0} is now the current directory".format(saved_cwd))

        if not finished:
            raise UserException(error_message, process.errorString())

        if process.exitStatus() != QProcess.NormalExit or process.exitCode(
        ) != 0:
            raise UserException(
                error_message,
                QTextCodec.codecForLocale().toUnicode(stderr_output).strip())
예제 #2
0
파일: builder.py 프로젝트: doudz/pyqtdeploy
    def run(self, argv, error_message, in_build_dir=False):
        """ Execute a command and capture the output. """

        if in_build_dir:
            project = self._project

            saved_cwd = os.getcwd()
            build_dir = project.path_from_user(project.build_dir)
            build_dir = QDir.toNativeSeparators(build_dir)
            os.chdir(build_dir)
            self._message_handler.verbose_message(
                    "{0} is now the current directory".format(build_dir))
        else:
            saved_cwd = None

        self._message_handler.verbose_message(
                "Running '{0}'".format(' '.join(argv)))

        QCoreApplication.processEvents()

        process = QProcess()

        process.readyReadStandardOutput.connect(
                lambda: self._message_handler.progress_message(
                        QTextCodec.codecForLocale().toUnicode(
                                process.readAllStandardOutput()).strip()))

        stderr_output = QByteArray()
        process.readyReadStandardError.connect(
                lambda: stderr_output.append(process.readAllStandardError()))

        process.start(argv[0], argv[1:])
        finished = process.waitForFinished()

        if saved_cwd is not None:
            os.chdir(saved_cwd)
            self._message_handler.verbose_message(
                    "{0} is now the current directory".format(saved_cwd))

        if not finished:
            raise UserException(error_message, process.errorString())

        if process.exitStatus() != QProcess.NormalExit or process.exitCode() != 0:
            raise UserException(error_message,
                    QTextCodec.codecForLocale().toUnicode(stderr_output).strip())
예제 #3
0
class GUIProcess(QObject):
    """An external process which shows notifications in the GUI.

    Args:
        cmd: The command which was started.
        args: A list of arguments which gets passed.
        verbose: Whether to show more messages.
        _output_messages: Show output as messages.
        _started: Whether the underlying process is started.
        _proc: The underlying QProcess.
        _what: What kind of thing is spawned (process/editor/userscript/...).
               Used in messages.

    Signals:
        error/finished/started signals proxied from QProcess.
    """

    error = pyqtSignal(QProcess.ProcessError)
    finished = pyqtSignal(int, QProcess.ExitStatus)
    started = pyqtSignal()

    def __init__(self,
                 what,
                 *,
                 verbose=False,
                 additional_env=None,
                 output_messages=False,
                 parent=None):
        super().__init__(parent)
        self._what = what
        self.verbose = verbose
        self._output_messages = output_messages
        self._started = False
        self.cmd = None
        self.args = None

        self.final_stdout: str = ""
        self.final_stderr: str = ""

        self._proc = QProcess(self)
        self._proc.errorOccurred.connect(self._on_error)
        self._proc.errorOccurred.connect(self.error)
        self._proc.finished.connect(self._on_finished)
        self._proc.finished.connect(self.finished)
        self._proc.started.connect(self._on_started)
        self._proc.started.connect(self.started)

        if additional_env is not None:
            procenv = QProcessEnvironment.systemEnvironment()
            for k, v in additional_env.items():
                procenv.insert(k, v)
            self._proc.setProcessEnvironment(procenv)

    @pyqtSlot(QProcess.ProcessError)
    def _on_error(self, error):
        """Show a message if there was an error while spawning."""
        if error == QProcess.Crashed and not utils.is_windows:
            # Already handled via ExitStatus in _on_finished
            return

        what = f"{self._what} {self.cmd!r}"
        error_descriptions = {
            QProcess.FailedToStart: f"{what.capitalize()} failed to start",
            QProcess.Crashed: f"{what.capitalize()} crashed",
            QProcess.Timedout: f"{what.capitalize()} timed out",
            QProcess.WriteError: f"Write error for {what}",
            QProcess.WriteError: f"Read error for {what}",
        }
        error_string = self._proc.errorString()
        msg = ': '.join([error_descriptions[error], error_string])

        # We can't get some kind of error code from Qt...
        # https://bugreports.qt.io/browse/QTBUG-44769
        # However, it looks like those strings aren't actually translated?
        known_errors = ['No such file or directory', 'Permission denied']
        if (': ' in error_string and  # pragma: no branch
                error_string.split(': ', maxsplit=1)[1] in known_errors):
            msg += f'\n(Hint: Make sure {self.cmd!r} exists and is executable)'

        message.error(msg)

    @pyqtSlot(int, QProcess.ExitStatus)
    def _on_finished(self, code, status):
        """Show a message when the process finished."""
        self._started = False
        log.procs.debug("Process finished with code {}, status {}.".format(
            code, status))

        encoding = locale.getpreferredencoding(do_setlocale=False)
        stderr = self._proc.readAllStandardError().data().decode(
            encoding, 'replace')
        stdout = self._proc.readAllStandardOutput().data().decode(
            encoding, 'replace')

        if self._output_messages:
            if stdout:
                message.info(stdout.strip())
            if stderr:
                message.error(stderr.strip())

        if status == QProcess.CrashExit:
            exitinfo = "{} crashed.".format(self._what.capitalize())
            message.error(exitinfo)
        elif status == QProcess.NormalExit and code == 0:
            exitinfo = "{} exited successfully.".format(
                self._what.capitalize())
            if self.verbose:
                message.info(exitinfo)
        else:
            assert status == QProcess.NormalExit
            # We call this 'status' here as it makes more sense to the user -
            # it's actually 'code'.
            exitinfo = ("{} exited with status {}, see :messages for "
                        "details.").format(self._what.capitalize(), code)
            message.error(exitinfo)

            if stdout:
                log.procs.error("Process stdout:\n" + stdout.strip())
            if stderr:
                log.procs.error("Process stderr:\n" + stderr.strip())

        qutescheme.spawn_output = self._spawn_format(exitinfo, stdout, stderr)
        self.final_stdout = stdout
        self.final_stderr = stderr

    def _spawn_format(self, exitinfo, stdout, stderr):
        """Produce a formatted string for spawn output."""
        stdout = (stdout or "(No output)").strip()
        stderr = (stderr or "(No output)").strip()

        spawn_string = ("{}\n"
                        "\nProcess stdout:\n {}"
                        "\nProcess stderr:\n {}").format(
                            exitinfo, stdout, stderr)
        return spawn_string

    @pyqtSlot()
    def _on_started(self):
        """Called when the process started successfully."""
        log.procs.debug("Process started.")
        assert not self._started
        self._started = True

    def _pre_start(self, cmd, args):
        """Prepare starting of a QProcess."""
        if self._started:
            raise ValueError("Trying to start a running QProcess!")
        self.cmd = cmd
        self.args = args
        fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
        log.procs.debug("Executing: {}".format(fake_cmdline))
        if self.verbose:
            message.info('Executing: ' + fake_cmdline)

    def start(self, cmd, args):
        """Convenience wrapper around QProcess::start."""
        log.procs.debug("Starting process.")
        self._pre_start(cmd, args)
        self._proc.start(cmd, args)
        self._proc.closeWriteChannel()

    def start_detached(self, cmd, args):
        """Convenience wrapper around QProcess::startDetached."""
        log.procs.debug("Starting detached.")
        self._pre_start(cmd, args)
        ok, _pid = self._proc.startDetached(cmd, args,
                                            None)  # type: ignore[call-arg]

        if not ok:
            message.error("Error while spawning {}".format(self._what))
            return False

        log.procs.debug("Process started.")
        self._started = True
        return True

    def exit_status(self):
        return self._proc.exitStatus()
예제 #4
0
class VideoService(QObject):
    def __init__(self, parent):
        super(VideoService, self).__init__(parent)
        self.parent = parent
        self.consoleOutput = ''
        if sys.platform == 'win32':
            self.backend = os.path.join(self.getAppPath(), 'bin', 'ffmpeg.exe')
            if not os.path.exists(self.backend):
                self.backend = find_executable('ffmpeg.exe')
        elif sys.platform == 'darwin':
            self.backend = os.path.join(self.getAppPath(), 'bin', 'ffmpeg')
        else:
            for exe in ('ffmpeg', 'avconv'):
                exe_path = find_executable(exe)
                if exe_path is not None:
                    self.backend = exe_path
                    break
        self.initProc()

    def initProc(self) -> None:
        self.proc = QProcess(self.parent)
        self.proc.setProcessChannelMode(QProcess.MergedChannels)
        env = QProcessEnvironment.systemEnvironment()
        self.proc.setProcessEnvironment(env)
        self.proc.setWorkingDirectory(self.getAppPath())
        if hasattr(self.proc, 'errorOccurred'):
            self.proc.errorOccurred.connect(self.cmdError)

    def capture(self, source: str, frametime: str) -> QPixmap:
        img, capres = None, QPixmap()
        try:
            img = QTemporaryFile(os.path.join(QDir.tempPath(), 'XXXXXX.jpg'))
            if img.open():
                imagecap = img.fileName()
                args = '-ss %s -i "%s" -vframes 1 -s 100x70 -y %s' % (
                    frametime, source, imagecap)
                if self.cmdExec(self.backend, args):
                    capres = QPixmap(imagecap, 'JPG')
        finally:
            del img
        return capres

    def cut(self, source: str, output: str, frametime: str,
            duration: str) -> bool:
        args = '-i "%s" -ss %s -t %s -vcodec copy -acodec copy -y "%s"' \
               % (source, frametime, duration, QDir.fromNativeSeparators(output))
        return self.cmdExec(self.backend, args)

    def join(self, filelist: list, output: str) -> bool:
        args = '-f concat -safe 0 -i "%s" -c copy -y "%s"' % (
            filelist, QDir.fromNativeSeparators(output))
        return self.cmdExec(self.backend, args)

    def cmdExec(self, cmd: str, args: str = None) -> bool:
        if os.getenv('DEBUG', False):
            print('VideoService CMD: %s %s' % (cmd, args))
        if self.proc.state() == QProcess.NotRunning:
            self.proc.start(cmd, shlex.split(args))
            self.proc.waitForFinished(-1)
            if self.proc.exitStatus(
            ) == QProcess.NormalExit and self.proc.exitCode() == 0:
                return True
        return False

    @pyqtSlot(QProcess.ProcessError)
    def cmdError(self, error: QProcess.ProcessError) -> None:
        if error != QProcess.Crashed:
            QMessageBox.critical(self.parent.parent,
                                 '',
                                 '<h4>%s Error:</h4>' % self.backend +
                                 '<p>%s</p>' % self.proc.errorString(),
                                 buttons=QMessageBox.Close)
            qApp.quit()

    def getAppPath(self) -> str:
        if getattr(sys, 'frozen', False):
            return sys._MEIPASS
        return QFileInfo(__file__).absolutePath()
예제 #5
0
class GUIProcess(QObject):

    """An external process which shows notifications in the GUI.

    Args:
        cmd: The command which was started.
        args: A list of arguments which gets passed.
        verbose: Whether to show more messages.
        _started: Whether the underlying process is started.
        _proc: The underlying QProcess.
        _what: What kind of thing is spawned (process/editor/userscript/...).
               Used in messages.

    Signals:
        error/finished/started signals proxied from QProcess.
    """

    error = pyqtSignal(QProcess.ProcessError)
    finished = pyqtSignal(int, QProcess.ExitStatus)
    started = pyqtSignal()

    def __init__(self, what, *, verbose=False, additional_env=None,
                 parent=None):
        super().__init__(parent)
        self._what = what
        self.verbose = verbose
        self._started = False
        self.cmd = None
        self.args = None

        self._proc = QProcess(self)
        self._proc.errorOccurred.connect(self._on_error)
        self._proc.errorOccurred.connect(self.error)
        self._proc.finished.connect(self._on_finished)
        self._proc.finished.connect(self.finished)
        self._proc.started.connect(self._on_started)
        self._proc.started.connect(self.started)

        if additional_env is not None:
            procenv = QProcessEnvironment.systemEnvironment()
            for k, v in additional_env.items():
                procenv.insert(k, v)
            self._proc.setProcessEnvironment(procenv)

    @pyqtSlot()
    def _on_error(self):
        """Show a message if there was an error while spawning."""
        msg = self._proc.errorString()
        message.error("Error while spawning {}: {}".format(self._what, msg))

    @pyqtSlot(int, QProcess.ExitStatus)
    def _on_finished(self, code, status):
        """Show a message when the process finished."""
        self._started = False
        log.procs.debug("Process finished with code {}, status {}.".format(
            code, status))

        encoding = locale.getpreferredencoding(do_setlocale=False)
        stderr = bytes(self._proc.readAllStandardError()).decode(
            encoding, 'replace')
        stdout = bytes(self._proc.readAllStandardOutput()).decode(
            encoding, 'replace')

        if status == QProcess.CrashExit:
            exitinfo = "{} crashed!".format(self._what.capitalize())
            message.error(exitinfo)
        elif status == QProcess.NormalExit and code == 0:
            exitinfo = "{} exited successfully.".format(
                self._what.capitalize())
            if self.verbose:
                message.info(exitinfo)
        else:
            assert status == QProcess.NormalExit
            # We call this 'status' here as it makes more sense to the user -
            # it's actually 'code'.
            exitinfo = ("{} exited with status {}, see :messages for "
                        "details.").format(self._what.capitalize(), code)
            message.error(exitinfo)

            if stdout:
                log.procs.error("Process stdout:\n" + stdout.strip())
            if stderr:
                log.procs.error("Process stderr:\n" + stderr.strip())

        glimpsescheme.spawn_output = self._spawn_format(exitinfo, stdout, stderr)

    def _spawn_format(self, exitinfo, stdout, stderr):
        """Produce a formatted string for spawn output."""
        stdout = (stdout or "(No output)").strip()
        stderr = (stderr or "(No output)").strip()

        spawn_string = ("{}\n"
                        "\nProcess stdout:\n {}"
                        "\nProcess stderr:\n {}").format(exitinfo,
                                                         stdout, stderr)
        return spawn_string

    @pyqtSlot()
    def _on_started(self):
        """Called when the process started successfully."""
        log.procs.debug("Process started.")
        assert not self._started
        self._started = True

    def _pre_start(self, cmd, args):
        """Prepare starting of a QProcess."""
        if self._started:
            raise ValueError("Trying to start a running QProcess!")
        self.cmd = cmd
        self.args = args
        fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
        log.procs.debug("Executing: {}".format(fake_cmdline))
        if self.verbose:
            message.info('Executing: ' + fake_cmdline)

    def start(self, cmd, args):
        """Convenience wrapper around QProcess::start."""
        log.procs.debug("Starting process.")
        self._pre_start(cmd, args)
        self._proc.start(cmd, args)
        self._proc.closeWriteChannel()

    def start_detached(self, cmd, args):
        """Convenience wrapper around QProcess::startDetached."""
        log.procs.debug("Starting detached.")
        self._pre_start(cmd, args)
        ok, _pid = self._proc.startDetached(cmd, args, None)

        if not ok:
            message.error("Error while spawning {}".format(self._what))
            return False

        log.procs.debug("Process started.")
        self._started = True
        return True

    def exit_status(self):
        return self._proc.exitStatus()
예제 #6
0
class GUIProcess(QObject):

    """An external process which shows notifications in the GUI.

    Args:
        cmd: The command which was started.
        args: A list of arguments which gets passed.
        verbose: Whether to show more messages.
        running: Whether the underlying process is started.
        what: What kind of thing is spawned (process/editor/userscript/...).
              Used in messages.
        _output_messages: Show output as messages.
        _proc: The underlying QProcess.

    Signals:
        error/finished/started signals proxied from QProcess.
    """

    error = pyqtSignal(QProcess.ProcessError)
    finished = pyqtSignal(int, QProcess.ExitStatus)
    started = pyqtSignal()

    def __init__(
            self,
            what: str,
            *,
            verbose: bool = False,
            additional_env: Mapping[str, str] = None,
            output_messages: bool = False,
            parent: QObject = None,
    ):
        super().__init__(parent)
        self.what = what
        self.verbose = verbose
        self._output_messages = output_messages
        self.outcome = ProcessOutcome(what=what)
        self.cmd: Optional[str] = None
        self.args: Optional[Sequence[str]] = None
        self.pid: Optional[int] = None

        self.stdout: str = ""
        self.stderr: str = ""

        self._cleanup_timer = usertypes.Timer(self, 'process-cleanup')
        self._cleanup_timer.setTimerType(Qt.VeryCoarseTimer)
        self._cleanup_timer.setInterval(3600 * 1000)  # 1h
        self._cleanup_timer.timeout.connect(self._on_cleanup_timer)
        self._cleanup_timer.setSingleShot(True)

        self._proc = QProcess(self)
        self._proc.errorOccurred.connect(self._on_error)
        self._proc.errorOccurred.connect(self.error)
        self._proc.finished.connect(self._on_finished)
        self._proc.finished.connect(self.finished)
        self._proc.started.connect(self._on_started)
        self._proc.started.connect(self.started)
        self._proc.readyReadStandardOutput.connect(self._on_ready_read_stdout)
        self._proc.readyReadStandardError.connect(self._on_ready_read_stderr)

        if additional_env is not None:
            procenv = QProcessEnvironment.systemEnvironment()
            for k, v in additional_env.items():
                procenv.insert(k, v)
            self._proc.setProcessEnvironment(procenv)

    def __str__(self) -> str:
        if self.cmd is None or self.args is None:
            return f'<unknown {self.what} command>'
        return ' '.join(shlex.quote(e) for e in [self.cmd] + list(self.args))

    def _decode_data(self, qba: QByteArray) -> str:
        """Decode data coming from a process."""
        encoding = locale.getpreferredencoding(do_setlocale=False)
        return qba.data().decode(encoding, 'replace')

    def _process_text(self, data: QByteArray, attr: str) -> None:
        """Process new stdout/stderr text.

        Arguments:
            data: The new process data.
            attr: Either 'stdout' or 'stderr'.
        """
        text = self._decode_data(data)

        if '\r' in text and not utils.is_windows:
            # Crude handling of CR for e.g. progress output.
            # Discard everything before the last \r in the new input, then discard
            # everything after the last \n in self.stdout/self.stderr.
            text = text.rsplit('\r', maxsplit=1)[-1]
            existing = getattr(self, attr)
            if '\n' in existing:
                new = existing.rsplit('\n', maxsplit=1)[0] + '\n'
            else:
                new = ''
            setattr(self, attr, new)

        if attr == 'stdout':
            self.stdout += text
        elif attr == 'stderr':
            self.stderr += text
        else:
            raise utils.Unreachable(attr)

    @pyqtSlot()
    def _on_ready_read_stdout(self) -> None:
        if not self._output_messages:
            return

        self._process_text(self._proc.readAllStandardOutput(), 'stdout')
        message.info(self._elide_output(self.stdout), replace=f"stdout-{self.pid}")

    @pyqtSlot()
    def _on_ready_read_stderr(self) -> None:
        if not self._output_messages:
            return
        self._process_text(self._proc.readAllStandardError(), 'stderr')
        message.error(self._elide_output(self.stderr), replace=f"stderr-{self.pid}")

    @pyqtSlot(QProcess.ProcessError)
    def _on_error(self, error: QProcess.ProcessError) -> None:
        """Show a message if there was an error while spawning."""
        if error == QProcess.Crashed and not utils.is_windows:
            # Already handled via ExitStatus in _on_finished
            return

        what = f"{self.what} {self.cmd!r}"
        error_descriptions = {
            QProcess.FailedToStart: f"{what.capitalize()} failed to start",
            QProcess.Crashed: f"{what.capitalize()} crashed",
            QProcess.Timedout: f"{what.capitalize()} timed out",
            QProcess.WriteError: f"Write error for {what}",
            QProcess.ReadError: f"Read error for {what}",
        }
        error_string = self._proc.errorString()
        msg = ': '.join([error_descriptions[error], error_string])

        # We can't get some kind of error code from Qt...
        # https://bugreports.qt.io/browse/QTBUG-44769
        # However, it looks like those strings aren't actually translated?
        known_errors = ['No such file or directory', 'Permission denied']
        if (': ' in error_string and  # pragma: no branch
                error_string.split(': ', maxsplit=1)[1] in known_errors):
            msg += f'\nHint: Make sure {self.cmd!r} exists and is executable'
            if version.is_flatpak():
                msg += ' inside the Flatpak container'

        message.error(msg)

    def _elide_output(self, output: str) -> str:
        """Shorten long output before showing it."""
        output = output.strip()
        lines = output.splitlines()
        count = len(lines)
        threshold = 20

        if count > threshold:
            lines = [
                f'[{count - threshold} lines hidden, see :process for the full output]'
            ] + lines[-threshold:]
            output = '\n'.join(lines)

        return output

    @pyqtSlot(int, QProcess.ExitStatus)
    def _on_finished(self, code: int, status: QProcess.ExitStatus) -> None:
        """Show a message when the process finished."""
        log.procs.debug("Process finished with code {}, status {}.".format(
            code, status))

        self.outcome.running = False
        self.outcome.code = code
        self.outcome.status = status

        self.stderr += self._decode_data(self._proc.readAllStandardError())
        self.stdout += self._decode_data(self._proc.readAllStandardOutput())

        if self._output_messages:
            if self.stdout:
                message.info(
                    self._elide_output(self.stdout), replace=f"stdout-{self.pid}")
            if self.stderr:
                message.error(
                    self._elide_output(self.stderr), replace=f"stderr-{self.pid}")

        if self.outcome.was_successful():
            if self.verbose:
                message.info(str(self.outcome))
            self._cleanup_timer.start()
        else:
            if self.stdout:
                log.procs.error("Process stdout:\n" + self.stdout.strip())
            if self.stderr:
                log.procs.error("Process stderr:\n" + self.stderr.strip())
            message.error(str(self.outcome) + " See :process for details.")

    @pyqtSlot()
    def _on_started(self) -> None:
        """Called when the process started successfully."""
        log.procs.debug("Process started.")
        assert not self.outcome.running
        self.outcome.running = True

    def _pre_start(self, cmd: str, args: Sequence[str]) -> None:
        """Prepare starting of a QProcess."""
        if self.outcome.running:
            raise ValueError("Trying to start a running QProcess!")
        self.cmd = cmd
        self.args = args
        log.procs.debug(f"Executing: {self}")
        if self.verbose:
            message.info(f'Executing: {self}')

    def start(self, cmd: str, args: Sequence[str]) -> None:
        """Convenience wrapper around QProcess::start."""
        log.procs.debug("Starting process.")
        self._pre_start(cmd, args)
        self._proc.start(cmd, args)
        self._post_start()
        self._proc.closeWriteChannel()

    def start_detached(self, cmd: str, args: Sequence[str]) -> bool:
        """Convenience wrapper around QProcess::startDetached."""
        log.procs.debug("Starting detached.")
        self._pre_start(cmd, args)
        ok, self.pid = self._proc.startDetached(
            cmd, args, None)  # type: ignore[call-arg]

        if not ok:
            message.error("Error while spawning {}".format(self.what))
            return False

        log.procs.debug("Process started.")
        self.outcome.running = True
        self._post_start()
        return True

    def _post_start(self) -> None:
        """Register this process and remember the process ID after starting."""
        self.pid = self._proc.processId()
        all_processes[self.pid] = self
        global last_pid
        last_pid = self.pid

    @pyqtSlot()
    def _on_cleanup_timer(self) -> None:
        """Remove the process from all registered processes."""
        log.procs.debug(f"Cleaning up data for {self.pid}")
        assert self.pid in all_processes
        all_processes[self.pid] = None
        self.deleteLater()

    def terminate(self, kill: bool = False) -> None:
        """Terminate or kill the process."""
        if kill:
            self._proc.kill()
        else:
            self._proc.terminate()
예제 #7
0
class GUIProcess(QObject):

    """An external process which shows notifications in the GUI.

    Args:
        cmd: The command which was started.
        args: A list of arguments which gets passed.
        verbose: Whether to show more messages.
        _started: Whether the underlying process is started.
        _proc: The underlying QProcess.
        _what: What kind of thing is spawned (process/editor/userscript/...).
               Used in messages.

    Signals:
        error/finished/started signals proxied from QProcess.
    """

    error = pyqtSignal(QProcess.ProcessError)
    finished = pyqtSignal(int, QProcess.ExitStatus)
    started = pyqtSignal()

    def __init__(self, what, *, verbose=False, additional_env=None,
                 parent=None):
        super().__init__(parent)
        self._what = what
        self.verbose = verbose
        self._started = False
        self.cmd = None
        self.args = None

        self._proc = QProcess(self)
        self._proc.errorOccurred.connect(self._on_error)
        self._proc.errorOccurred.connect(self.error)
        self._proc.finished.connect(self._on_finished)
        self._proc.finished.connect(self.finished)
        self._proc.started.connect(self._on_started)
        self._proc.started.connect(self.started)

        if additional_env is not None:
            procenv = QProcessEnvironment.systemEnvironment()
            for k, v in additional_env.items():
                procenv.insert(k, v)
            self._proc.setProcessEnvironment(procenv)

    @pyqtSlot()
    def _on_error(self):
        """Show a message if there was an error while spawning."""
        msg = self._proc.errorString()
        message.error("Error while spawning {}: {}".format(self._what, msg))

    @pyqtSlot(int, QProcess.ExitStatus)
    def _on_finished(self, code, status):
        """Show a message when the process finished."""
        self._started = False
        log.procs.debug("Process finished with code {}, status {}.".format(
            code, status))

        encoding = locale.getpreferredencoding(do_setlocale=False)
        stderr = bytes(self._proc.readAllStandardError()).decode(
            encoding, 'replace')
        stdout = bytes(self._proc.readAllStandardOutput()).decode(
            encoding, 'replace')

        if status == QProcess.CrashExit:
            exitinfo = "{} crashed!".format(self._what.capitalize())
            message.error(exitinfo)
        elif status == QProcess.NormalExit and code == 0:
            exitinfo = "{} exited successfully.".format(
                self._what.capitalize())
            if self.verbose:
                message.info(exitinfo)
        else:
            assert status == QProcess.NormalExit
            # We call this 'status' here as it makes more sense to the user -
            # it's actually 'code'.
            exitinfo = ("{} exited with status {}, see :messages for "
                        "details.").format(self._what.capitalize(), code)
            message.error(exitinfo)

            if stdout:
                log.procs.error("Process stdout:\n" + stdout.strip())
            if stderr:
                log.procs.error("Process stderr:\n" + stderr.strip())

        qutescheme.spawn_output = self._spawn_format(exitinfo, stdout, stderr)

    def _spawn_format(self, exitinfo, stdout, stderr):
        """Produce a formatted string for spawn output."""
        stdout = (stdout or "(No output)").strip()
        stderr = (stderr or "(No output)").strip()

        spawn_string = ("{}\n"
                        "\nProcess stdout:\n {}"
                        "\nProcess stderr:\n {}").format(exitinfo,
                                                         stdout, stderr)
        return spawn_string

    @pyqtSlot()
    def _on_started(self):
        """Called when the process started successfully."""
        log.procs.debug("Process started.")
        assert not self._started
        self._started = True

    def _pre_start(self, cmd, args):
        """Prepare starting of a QProcess."""
        if self._started:
            raise ValueError("Trying to start a running QProcess!")
        self.cmd = cmd
        self.args = args
        fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
        log.procs.debug("Executing: {}".format(fake_cmdline))
        if self.verbose:
            message.info('Executing: ' + fake_cmdline)

    def start(self, cmd, args):
        """Convenience wrapper around QProcess::start."""
        log.procs.debug("Starting process.")
        self._pre_start(cmd, args)
        self._proc.start(cmd, args)
        self._proc.closeWriteChannel()

    def start_detached(self, cmd, args):
        """Convenience wrapper around QProcess::startDetached."""
        log.procs.debug("Starting detached.")
        self._pre_start(cmd, args)
        ok, _pid = self._proc.startDetached(cmd, args, None)

        if not ok:
            message.error("Error while spawning {}".format(self._what))
            return False

        log.procs.debug("Process started.")
        self._started = True
        return True

    def exit_status(self):
        return self._proc.exitStatus()
예제 #8
0
class Downloader(QDialog):
    dltool_cmd = 'aria2c'
    dltool_args = '-x 12 -d {dl_path} {dl_link}'

    def __init__(self, link_url: str, dl_path: str, parent=None):
        super(Downloader, self).__init__(parent)
        self.parent = parent
        self.dltool_cmd = find_executable(self.download_cmd)
        self.download_link = link_url
        self.download_path = dl_path
        if self.dltool_cmd.strip():
            self.dltool_args = self.dltool_args.format(
                dl_path=self.download_path, dl_link=self.download_link)
            self.console = QTextEdit(self.parent)
            self.console.setWindowTitle('%s Downloader' %
                                        qApp.applicationName())
            self.proc = QProcess(self.parent)
            layout = QVBoxLayout()
            layout.addWidget(self.console)
            self.setLayout(layout)
            self.setFixedSize(QSize(400, 300))
        else:
            QMessageBox.critical(
                self.parent,
                'DOWNLOADER ERROR',
                '<p>The <b>aria2c</b> executable binary could not ' +
                'be found in your installation folders. The binary comes packaged with this '
                +
                'application so it is likely that it was accidentally deleted via human '
                +
                'intervntion or incorrect file permissions are preventing access to it.</p>'
                +
                '<p>You may either download and install <b>aria2</b> manually yourself, ensuring '
                +
                'its installation location is globally accessible via PATH environmnt variables or '
                +
                'simply reinstall this application again. If the issue is not resolved then try '
                +
                'to download the application again incase the orignal you installed already was '
                + 'corrupted/broken.',
                buttons=QMessageBox.Close)

    def __del__(self) -> None:
        self.proc.terminate()
        if not self.proc.waitForFinished(10000):
            self.proc.kill()

    @staticmethod
    def get_machine_code() -> str:
        mcode = ''
        if sys.platform == 'darwin':
            mcode = 'macOS'
        elif sys.platform == 'win32' and platform.machine().endswith('86'):
            mcode = 'win32'
        elif sys.platform == 'win32' and platform.machine().endswith('64'):
            mcode = 'win64'
        elif sys.platform.startswith('linux') and platform.machine().endswith(
                '86'):
            mcode = 'linux32'
        elif sys.platform.startswith('linux') and platform.machine().endswith(
                '64'):
            mcode = 'linux64'
        return mcode

    @staticmethod
    def setup_aria() -> bool:
        aria_zip = Downloader.aria_clients()[
            Downloader.get_machine_code()]['bin_archive']
        aria_install = Downloader.aria_clients()[
            Downloader.get_machine_code()]['target_path']
        if os.path.exists(aria_zip):
            with ZipFile(aria_zip) as archive:
                target_path, target_file = os.path.split(aria_install)
                extracted_path = archive.extract(target_file, path=target_path)
                if extracted_path == aria_install and os.path.exists(
                        extracted_path):
                    if sys.platform is not 'win32':
                        os.chmod(extracted_path, 0o755)
                    return True
        return False

    def init_proc(self) -> None:
        self.proc.setProcessChannelMode(QProcess.MergedChannels)
        self.proc.readyRead.connect(self.console_output)
        self.proc.setProgram(self.aria2_cmd)
        self.proc.setArguments(shlex.split(self.aria2_args))

    def start(self) -> None:
        self.init_proc()
        self.show()
        self.proc.start()

    @pyqtSlot()
    def console_output(self) -> None:
        self.console.append(str(self.proc.readAllStandardOutput()))

    @pyqtSlot(QProcess.ProcessError)
    def cmd_error(self, error: QProcess.ProcessError) -> None:
        if error != QProcess.Crashed:
            QMessageBox.critical(self.parent,
                                 'Error calling an external process',
                                 self.proc.errorString(),
                                 buttons=QMessageBox.Close)

    @staticmethod
    def get_path(path: str) -> str:
        prefix = sys._MEIPASS if getattr(
            sys, 'frozen', False) else QFileInfo(__file__).absolutePath()
        return os.path.join(prefix, path)
예제 #9
0
class Updater(QThread):
    updateAvailable = pyqtSignal(bool, str)

    pypi_api_endpoint = 'https://pypi.python.org/pypi/vidcutter/json'
    github_api_endpoint = 'https://api.github.com/repos/ozmartian/vidcutter/releases/latest'
    latest_release_webpage = 'https://github.com/ozmartian/vidcutter/releases/latest'

    def __init__(self):
        QThread.__init__(self)

    def __del__(self) -> None:
        self.wait()

    @staticmethod
    def restart_app():
        os.execl(sys.executable, sys.executable, *sys.argv)

    def cmd_exec(self, cmd: str, args: str = None) -> bool:
        self.proc = QProcess(self)
        self.proc.setProcessChannelMode(QProcess.MergedChannels)
        self.proc.setWorkingDirectory(QFileInfo(__file__).absolutePath())
        if hasattr(self.proc, 'errorOccurred'):
            self.proc.errorOccurred.connect(self.cmdError)
        if self.proc.state() == QProcess.NotRunning:
            self.proc.start(cmd, shlex.split(args))
            self.proc.waitForFinished(-1)
            if self.proc.exitStatus(
            ) == QProcess.NormalExit and self.proc.exitCode() == 0:
                return True
        return False

    @pyqtSlot(QProcess.ProcessError)
    def cmdError(self, error: QProcess.ProcessError) -> None:
        if error != QProcess.Crashed:
            QMessageBox.critical(None,
                                 "Error calling an external process",
                                 self.proc.errorString(),
                                 buttons=QMessageBox.Close)

    def check_latest_github(self) -> None:
        try:
            res = json.loads(
                urlopen(self.github_api_endpoint).read().decode('utf-8'))
            if 'tag_name' in res.keys():
                latest_release = str(res['tag_name'])
                if LooseVersion(latest_release) > LooseVersion(
                        qApp.applicationVersion()):
                    # self.notify_update(version=latest_release, installer=self.install_update)
                    self.updateAvailable.emit(True, latest_release)
                    return
            self.updateAvailable.emit(False, None)
        except HTTPError:
            self.updateAvailable.emit(False, None)

    def check_latest_pypi(self) -> None:
        try:
            res = json.loads(
                urlopen(self.pypi_api_endpoint).read().decode('utf-8'))
            if 'info' in res.keys():
                latest_release = str(res['info']['version'])
                if LooseVersion(latest_release) > LooseVersion(
                        qApp.applicationVersion()):
                    # self.notify_update(version=latest_release, installer=self.install_update)
                    self.updateAvailable.emit(True, latest_release)
                    return
            self.updateAvailable.emit(False, None)
        except HTTPError:
            self.updateAvailable.emit(False, None)

    def install_update(self, parent: QWidget) -> None:
        returncode = self.cmd_exec(
            'x-terminal-emulator',
            '-title "VidCutter Updater" -e "sudo pip3 install ' +
            '--upgrade vidcutter"')
        self.confirm_update(parent, returncode)

    def run(self) -> None:
        if getattr(sys, 'frozen', False):
            self.check_latest_github()
        else:
            self.check_latest_pypi()

    @staticmethod
    def notify_update(parent: QWidget, version: str) -> QMessageBox.ButtonRole:
        mbox = QMessageBox(parent)
        mbox.setIconPixmap(qApp.windowIcon().pixmap(64, 64))
        mbox.setWindowTitle('%s UPDATER' % qApp.applicationName())
        mbox.setText(
            '<table border="0" width="350"><tr><td><h4 align="center">Your Version: %s <br/> Available Version: %s'
            % (qApp.applicationVersion(), version) +
            '</h4></td></tr></table><br/>')
        mbox.setInformativeText(
            'A new version of %s has been detected. Would you like to update now?'
            % qApp.applicationName())
        install_btn = mbox.addButton('Install Update', QMessageBox.AcceptRole)
        reject_btn = mbox.addButton('Not Now', QMessageBox.RejectRole)
        mbox.setDefaultButton(install_btn)
        return mbox.exec_()

    @staticmethod
    def notify_no_update(parent: QWidget) -> None:
        mbox = QMessageBox(parent)
        mbox.setIconPixmap(QIcon(':/images/thumbsup.png').pixmap(64, 64))
        mbox.setWindowTitle('%s UPDATER' % qApp.applicationName())
        mbox.setText('<h3 style="color:#6A4572;">%s %s</h3>' %
                     (qApp.applicationName(), qApp.applicationVersion()))
        mbox.setInformativeText(
            'You are already running the latest version.' +
            '<table width="350"><tr><td></td></tr></table>')
        mbox.setStandardButtons(QMessageBox.Close)
        mbox.setDefaultButton(QMessageBox.Close)
        return mbox.exec_()

    @staticmethod
    def notify_restart(parent: QWidget) -> bool:
        mbox = QMessageBox(parent)
        mbox.setIconPixmap(qApp.windowIcon().pixmap(64, 64))
        mbox.setWindowTitle('%s UPDATER' % qApp.applicationName())
        mbox.setText(
            '<h3 style="color:#6A4572;">INSTALLATION COMPLETE</h3>' +
            '<table border="0" width="350"><tr><td><p>The application needs to be restarted in order to use '
            +
            'the newly installed version.</p><p>Would you like to restart now?</td></tr></table><br/>'
        )
        restart_btn = mbox.addButton('Yes', QMessageBox.AcceptRole)
        restart_btn.clicked.connect(Updater.restart_app)
        reject_btn = mbox.addButton('No', QMessageBox.RejectRole)
        mbox.setDefaultButton(restart_btn)
        return mbox.exec_()

    @staticmethod
    def confirm_update(parent: QWidget, update_success: bool) -> None:
        if update_success and QMessageBox.question(
                parent,
                '%s UPDATER' % qApp.applicationName(),
                '<h3>UPDATE COMPLETE</h3><p>To begin using the newly installed '
                + 'version the application needs to be restarted.</p>' +
                '<p>Would you like to restart now?</p><br/>',
                buttons=(QMessageBox.Yes | QMessageBox.No)):
            Updater.restart_app()
예제 #10
0
class VideoService(QObject):
    def __init__(self, parent):
        super(VideoService, self).__init__(parent)
        self.parent = parent
        self.consoleOutput = ''
        self.backend = 'ffmpeg'
        self.arch = 'x86' if platform.architecture()[0] == '32bit' else 'x64'
        if sys.platform == 'win32':
            self.backend = os.path.join(self.getAppPath(), 'bin', self.arch,
                                        'ffmpeg.exe')
        self.initProc()

    def initProc(self) -> None:
        self.proc = QProcess(self.parent)
        self.proc.setProcessChannelMode(QProcess.MergedChannels)
        self.proc.setWorkingDirectory(self.getAppPath())
        if hasattr(self.proc, 'errorOccurred'):
            self.proc.errorOccurred.connect(self.cmdError)

    def capture(self, source: str, frametime: str) -> QPixmap:
        img, capres = None, QPixmap()
        try:
            img = QTemporaryFile(os.path.join(QDir.tempPath(), 'XXXXXX.jpg'))
            if img.open():
                imagecap = img.fileName()
                args = '-ss %s -i "%s" -vframes 1 -s 100x70 -y %s' % (
                    frametime, source, imagecap)
                if self.cmdExec(self.backend, args):
                    capres = QPixmap(imagecap, 'JPG')
        finally:
            del img
        return capres

    def cut(self, source: str, output: str, frametime: str,
            duration: str) -> bool:
        args = '-i "%s" -ss %s -t %s -vcodec copy -acodec copy -y "%s"'\
               % (source, frametime, duration, QDir.fromNativeSeparators(output))
        return self.cmdExec(self.backend, args)

    def join(self, filelist: list, output: str) -> bool:
        args = '-f concat -safe 0 -i "%s" -c copy -y "%s"' % (
            filelist, QDir.fromNativeSeparators(output))
        return self.cmdExec(self.backend, args)

    def cmdExec(self, cmd: str, args: str = None) -> bool:
        if self.proc.state() == QProcess.NotRunning:
            self.proc.start(cmd, shlex.split(args))
            self.proc.waitForFinished(-1)
            if self.proc.exitStatus(
            ) == QProcess.NormalExit and self.proc.exitCode() == 0:
                return True
        return False

    @pyqtSlot(QProcess.ProcessError)
    def cmdError(self, error: QProcess.ProcessError) -> None:
        if error != QProcess.Crashed:
            QMessageBox.critical(self.parent.parent,
                                 "Error calling an external process",
                                 self.proc.errorString(),
                                 buttons=QMessageBox.Cancel)

    def getAppPath(self) -> str:
        if getattr(sys, 'frozen', False):
            return sys._MEIPASS
        return QFileInfo(__file__).absolutePath()
예제 #11
0
class LoaderLauncher:
    BIN_PREFIX = '/bin/'
    EXE_NAME = 'EDIICore'

    @staticmethod
    def check_edii_path(path):
        return os.path.isfile(LoaderLauncher.make_paths(path)[0])

    @staticmethod
    def default_path():
        return os.getcwd() + '/EDII'

    @staticmethod
    def exe_suffix():
        ostype = platform.system()
        if ostype == 'Windows':
            return '.exe'
        else:
            return ''

    @staticmethod
    def _path_tail():
        return LoaderLauncher.BIN_PREFIX + LoaderLauncher.EXE_NAME + LoaderLauncher.exe_suffix(
        )

    @staticmethod
    def make_paths(path):
        if platform.system() == 'Windows':
            path = path.lower()
        tail = LoaderLauncher._path_tail()
        if platform.system() == 'Windows':
            tail = tail.lower()

        if path.endswith(tail):
            return (path, path[:-len(tail)] + LoaderLauncher.BIN_PREFIX)
        return (path + tail, path + LoaderLauncher.BIN_PREFIX)

    def __init__(self):
        self._ldrProcess = QProcess()

    def launchIfNeeded(self, loaderpath):
        self._setup_path(loaderpath)

        try:
            if signaltraceloaderfactory.serviceAvailable():
                return
        except signaltraceloader.SignalTraceLoaderError as ex:
            raise EDIIConnectionError(str(ex))

        self._ldrProcess.start()
        if not self._ldrProcess.waitForStarted(2000):
            raise EDIIStartError(self._ldrProcess.errorString())
        time.sleep(0.25)

        try:
            if not signaltraceloaderfactory.serviceAvailable():
                raise EDIIConnectionError('EDII service is not available')
        except signaltraceloader.SignalTraceLoaderError as ex:
            raise EDIIConnectionError(str(ex))

    def _setup_path(self, loaderpath):
        if isinstance(loaderpath, str) is False:
            raise EDIIStartError('Invalid path to EDII service')

        path = ''
        if not os.path.isabs(loaderpath):
            path = os.path.abspath(loaderpath)
        else:
            path = loaderpath

        exe_path, wd_path = LoaderLauncher.make_paths(path)

        self._ldrProcess.setProgram(exe_path)
        self._ldrProcess.setWorkingDirectory(wd_path)

    def terminate(self):
        self._ldrProcess.terminate()
        self._ldrProcess.waitForFinished(3000)
예제 #12
0
 def start(self, projectDir):
     """
     Public method to populate the status list.
     
     @param projectDir name of the project directory
     @type str
     """
     # find the root of the repo
     self.__repodir = projectDir
     while not os.path.isdir(os.path.join(self.__repodir,
                                          self.__vcs.adminDir)):
         self.__repodir = os.path.dirname(self.__repodir)
         if os.path.splitdrive(self.__repodir)[1] == os.sep:
             return
     
     self.errorGroup.hide()
     self.errors.clear()
     self.statusList.clear()
     self.buttonBox.setEnabled(False)
     
     args = self.__vcs.initCommand("submodule")
     args.append("status")
     if self.recursiveCheckBox.isChecked():
         args.append("--recursive")
     if self.indexCheckBox.isChecked():
         args.append("--cached")
     
     process = QProcess()
     process.setWorkingDirectory(self.__repodir)
     process.start('git', args)
     procStarted = process.waitForStarted(5000)
     if procStarted:
         finished = process.waitForFinished(30000)
         if finished and process.exitCode() == 0:
             ioEncoding = Preferences.getSystem("IOEncoding")
             output = str(process.readAllStandardOutput(),
                          ioEncoding, 'replace')
             error = str(process.readAllStandardError(),
                         ioEncoding, 'replace')
             if error:
                 self.errors.setText(error)
                 self.errorGroup.show()
             self.__processOutput(output)
         else:
             if not finished:
                 self.errors.setText(self.tr(
                     "The process {0} did not finish within 30 seconds.")
                     .format("git"))
             else:
                 self.errors.setText(self.tr(
                     "The process {0} finished with an error.\n"
                     "Error: {1}")
                     .format("git", process.errorString()))
             self.errorGroup.show()
     else:
         self.errors.setText(self.tr(
             "The process {0} could not be started. "
             "Ensure, that it is in the search path.").format("git"))
         self.errorGroup.show()
     
     self.buttonBox.setEnabled(True)
     self.buttonBox.setFocus()
예제 #13
0
class runV2raycore(QObject):
    """
    you should emit a signal to start or stop a program.
    """
    start = pyqtSignal()
    stop = pyqtSignal()

    def __init__(self,
                 outputTextEdit,
                 v2rayPath="v2ray",
                 v2rayOption="",
                 bridgetreasureChest=False):
        super().__init__()
        self.outputTextEdit = outputTextEdit
        self.v2rayPath = v2rayPath
        self.v2rayOption = v2rayOption
        self.bridgetreasureChest = bridgetreasureChest
        if not self.bridgetreasureChest:
            from bridgehouse.extension import bridgetreasureChest
            self.bridgetreasureChest = bridgetreasureChest.bridgetreasureChest(
            )

        self.v2rayProcess = QProcess()
        self.v2rayProcess.setProcessChannelMode(QProcess.MergedChannels)
        self.v2rayProcess.setProcessEnvironment(
            QProcessEnvironment.systemEnvironment())

        self.v2rayProcess.readyRead.connect(self.setoutputTextEdit)
        self.v2rayProcess.started.connect(self.oncreatePIDFile)
        self.start.connect(self.onstart)
        self.stop.connect(self.onstop)
        self.translate = QCoreApplication.translate
        self.pidFile = ".v2rayPID"

    def onstart(self):
        if (self.v2rayProcess.state() == QProcess.NotRunning):
            self.outputTextEdit.clear()
            command = self.translate("runV2raycore",
                                     "v2ray file path had no seted.")
            if (self.v2rayPath):
                checkSpaces = re.search(" ", self.v2rayPath)
                if checkSpaces:
                    # in fact, you can just keep this line.
                    # do not need check spaces
                    command = '"' + self.v2rayPath + '" ' + self.v2rayOption
                else:
                    command = "{} {}".format(self.v2rayPath, self.v2rayOption)
                self.killOrphanProcess()
                self.v2rayProcess.start(command, QIODevice.ReadWrite)
                self.outputTextEdit.insertPlainText("{}\n\n".format(command))

            if (self.v2rayProcess.state() == QProcess.NotRunning):
                self.outputTextEdit.moveCursor(QTextCursor.End)
                self.outputTextEdit.append("\n")
                self.outputTextEdit.insertPlainText(str(
                    "{}\n".format(command)))
                self.outputTextEdit.insertPlainText(
                    str(
                        self.translate("runV2raycore",
                                       "{}   Error Code:{}").format(
                                           self.v2rayProcess.errorString(),
                                           self.v2rayProcess.error())))
                self.outputTextEdit.moveCursor(QTextCursor.End)

            self.outputTextEdit.textChanged.connect(self.getV2raycoreVersion)

    def killOrphanProcess(self):
        openFile = QFileInfo(self.pidFile)

        fileName = openFile.fileName()
        if QFile.exists(fileName):
            openFile = QFile(fileName)
        else:
            return
        v2rayPID = None

        try:
            openFile.open(QIODevice.ReadOnly | QIODevice.Text)
            v2rayPID = str(openFile.readAll(), "utf-8")
        except Exception:
            pass

        try:
            os.kill(int(v2rayPID), signal.SIGTERM)
        except Exception:
            pass

    def oncreatePIDFile(self):
        if self.v2rayProcess.state() == QProcess.NotRunning:
            return

        outFile = QFileInfo(self.pidFile)
        fileName = outFile.fileName()
        if QFile.exists(fileName):
            QFile.remove(fileName)
        outFile = QFile(fileName)

        v2rayPID = str(self.v2rayProcess.processId())
        qDebug("process ID is: {}".format(v2rayPID))
        try:
            outFile.open(QIODevice.WriteOnly | QIODevice.Text)
            outFile.write(codecs.encode(v2rayPID, "utf-8"))
        except Exception:
            pass
        outFile.close()

    def getV2raycoreVersion(self):
        text = self.outputTextEdit.toPlainText()
        version = re.findall("V2Ray v\d\.\d{1,2}", text)
        failtostart = re.findall("Failed to start App", text)
        if (version):
            version = version[0].split(" ")[1]
            self.bridgetreasureChest.setV2raycoreVersion(version)
        if (failtostart):
            self.outputTextEdit.textChanged.disconnect(
                self.getV2raycoreVersion)
            self.onstop()

    def onstop(self):
        if (self.v2rayProcess.state() == QProcess.Running):
            self.v2rayProcess.close()
            self.v2rayProcess.kill()
            self.outputTextEdit.moveCursor(QTextCursor.End)
            self.outputTextEdit.append("\n\n")
            self.outputTextEdit.insertPlainText(
                str(
                    self.translate("runV2raycore", "{} is stop now...").format(
                        self.v2rayPath)))
            self.outputTextEdit.insertPlainText(
                str(
                    self.translate("runV2raycore",
                                   "\n{} is ready to run...").format(
                                       self.v2rayPath)))
            self.outputTextEdit.moveCursor(QTextCursor.End)

    def setoutputTextEdit(self):
        self.outputTextEdit.moveCursor(QTextCursor.End)
        self.outputTextEdit.insertPlainText(
            str(self.v2rayProcess.readAllStandardOutput(), "utf-8"))
        self.outputTextEdit.insertPlainText(
            str(self.v2rayProcess.readAllStandardError(), "utf-8"))
        self.outputTextEdit.moveCursor(QTextCursor.End)