Esempio n. 1
0
    def write(self, file_name, storage_device, mesh_data):
        Logger.log("i", "In X3gWriter.write")
        if "x3g" in file_name:
            scene = Application.getInstance().getController().getScene()
            gcode_list = getattr(scene, "gcode_list")
            if gcode_list:
                # f = storage_device.openFile(file_name, "wt")
                Logger.log("i", "Writing X3g to file %s", file_name)
                p = QProcess()
                p.setReadChannel(1)
                p.setStandardOutputFile(file_name)
                p.start("gpx", ["-i"])
                p.waitForStarted()

                for gcode in gcode_list:
                    p.write(gcode)
                    if p.canReadLine():
                        Logger.log("i", "gpx: %s", p.readLine().data().decode("utf-8"))

                p.closeWriteChannel()
                if p.waitForFinished():
                    Logger.log("i", "gpx: %s", p.readAll().data().decode("utf-8"))
                p.close()
                # storage_device.closeFile(f)
                return True

        return False
Esempio n. 2
0
    def write(self, file_name, storage_device, mesh_data):
        Logger.log("i", "In X3gWriter.write")
        if "x3g" in file_name:
            scene = Application.getInstance().getController().getScene()
            gcode_list = getattr(scene, "gcode_list")
            if gcode_list:
                # f = storage_device.openFile(file_name, "wt")
                Logger.log("i", "Writing X3g to file %s", file_name)
                p = QProcess()
                p.setReadChannel(1)
                p.setStandardOutputFile(file_name)
                p.start("gpx", ["-i"])
                p.waitForStarted()

                for gcode in gcode_list:
                    p.write(gcode)
                    if (p.canReadLine()):
                        Logger.log("i", "gpx: %s", p.readLine().data().decode('utf-8'))

                p.closeWriteChannel()
                if p.waitForFinished():
                    Logger.log("i", "gpx: %s", p.readAll().data().decode('utf-8'))
                p.close()
                # storage_device.closeFile(f)
                return True

        return False
 def run(self):
     for example in self.examples:
         print(example)
         self.example = example
         example_file = self.workDir + '\\inputs\\' + example
         cmd_single = run_cmd(self.source, example_file)
         self.cmd = cmd_single
         cmd = shell_cmd(cmd_single)
         p = QProcess()
         self.process = p
         p.start('cmd')
         p.waitForStarted(1000)
         p.write(bytes(cmd, 'GBK'))
         p.closeWriteChannel()
         p.waitForFinished()
         output = p.readAllStandardOutput()
         # print("output is",output)
         self.CheckFinished.emit(
             example,
             read_out(output, cmd_single) +
             read_out(p.readAllStandardError(), cmd_single))
     # 以下三行代码仅限于最后一次作业
     # t = verify_file('c.std.txt','c.txt')
     # self.CheckFinished.emit('文件比较',t)
     # os.system('del c.txt')
     self.AllFinished.emit()
Esempio n. 4
0
    def connect_speaker_device(self, address):
        if platform.system() == "Darwin":
            print("Pairing...")
            process = QProcess()
            process.setProcessChannelMode(QProcess.SeparateChannels)
            process.start(f"blueutil --pair {address}")
            process.waitForFinished()
            print("Connecting...")
            process.start(f"blueutil --connect {address}")
            process.waitForFinished()
            print("Connected!")
        elif platform.system() == "Linux":
            process = QProcess()
            process.setProcessChannelMode(QProcess.SeparateChannels)
            process.start("bluetoothctl")
            process.waitForStarted()
            process.waitForReadyRead()
            process.writeData(bytes("connect B1:20:B5:86:AA:B9\n", 'utf-8'))
            process.waitForBytesWritten()

            while True:
                process.waitForReadyRead(1500)
                data = bytes(process.readAll()).decode('utf-8').split(" ")
                print(data)

                if any("successful" in x for x in data):
                    print("Connection successful")
                    break
                elif any("Failed" in x for x in data):
                    print(f"ERROR: bluez.failed")
                    self.bluetooth_speaker_cb(
                        BluetoothSpeakerError(
                            "Bluetooth speaker daemon failed to start"))

                time.sleep(1.0)

            process.writeData(bytes("exit\n", 'utf-8'))
            process.waitForBytesWritten()
            process.waitForReadyRead(1500)
            process.closeWriteChannel()
            process.waitForFinished()
            process.close()
            print("Waiting for notify_connect to fire")
        else:
            print("Unsupported platform")
Esempio n. 5
0
    def clear_cached_speaker_device(self):
        if platform.system() == "Linux":
            print("Clearing Bluetooth device cache")

            process = QProcess()
            process.setProcessChannelMode(QProcess.SeparateChannels)
            process.start("bluetoothctl")
            process.waitForStarted()
            time.sleep(0.1)
            process.waitForReadyRead()
            print(bytes(process.readAll()).decode('utf-8'))
            process.writeData(bytes("remove B1:20:B5:86:AA:B9\n", 'utf-8'))
            process.waitForBytesWritten()
            time.sleep(0.1)
            process.waitForReadyRead()
            print(bytes(process.readAll()).decode('utf-8'))
            process.closeWriteChannel()
            process.waitForFinished()
            print("Cleared")
Esempio n. 6
0
def load_cr3(path) -> QPixmap:
    """Extract the thumbnail from the image and initialize QPixmap"""

    process = QProcess()
    process.start(f"exiftool -b -JpgFromRaw {path}")
    process.waitForFinished()

    if process.exitStatus() != QProcess.NormalExit or process.exitCode() != 0:
        stderr = process.readAllStandardError()
        raise ValueError(f"Error calling exiftool: '{stderr.data().decode()}'")

    handler = QImageReader(process, "jpeg".encode())
    handler.setAutoTransform(True)

    process.closeWriteChannel()
    process.terminate()

    # Extract QImage from QImageReader and convert to QPixmap
    pixmap = QPixmap()
    pixmap.convertFromImage(handler.read())

    return pixmap
Esempio n. 7
0
def load_frame(path) -> QPixmap:
    """Extract the first frame from the video and initialize QPixmap"""

    process = QProcess()
    process.start(
        f"ffmpeg -loglevel quiet -i {path} -vframes 1 -f image2 pipe:1")
    process.waitForFinished()

    if process.exitStatus() != QProcess.NormalExit or process.exitCode() != 0:
        stderr = process.readAllStandardError()
        raise ValueError(f"Error calling ffmpeg: '{stderr.data().decode()}'")

    handler = QImageReader(process, "jpeg".encode())
    handler.setAutoTransform(True)

    process.closeWriteChannel()
    process.terminate()

    # Extract QImage from QImageReader and convert to QPixmap
    pixmap = QPixmap()
    pixmap.convertFromImage(handler.read())

    return pixmap
Esempio n. 8
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()
Esempio n. 9
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()
Esempio n. 10
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()
Esempio n. 11
0
class AsyncProcess:
    """
    Wraps a QProcess and provides a neat await-able interface.

    This is not "cross-task-safe", i.e. one AsyncProcess should not be manipulated concurrently
    by more than one task.
    """

    def __init__(self, args=None, qprocess=None, scheduler=None):
        self._scheduler = scheduler
        if qprocess:
            self._process = qprocess
            assert not args
        else:
            self._process = QProcess()
            self._process.setProgram(args[0])
            self._process.setArguments(args[1:])

        self._current_error_occured = self._error_outside_await
        self._process.errorOccurred.connect(self._error_outside_await)
        self._stored_error = None

    def _error_outside_await(self, error):
        self._stored_error = error

    def _set_error_occured(self, swapin):
        swapout = self._current_error_occured
        self._process.errorOccurred.disconnect(swapout)
        self._process.errorOccurred.connect(swapin)
        self._current_error_occured = swapin

    def _error_handling_future(self, source_signal, result_callback, exit_callback=None) -> Future:
        """
        Create future responding to QProcess errors.

        *result_callback* is connected to *source_signal*. *exit_callback* is connected
        to QProcess.finished. Any error occurring until completion of the future will
        set an exception on the future and thus consume the error.

        Signal connections are undone when the future is done.
        """
        def error_occurred(error):
            fut.set_exception(AsyncProcessError(error))

        def process_finished(*args):
            exit_callback()

        def disconnect(fut):
            source_signal.disconnect(result_callback)
            if exit_callback:
                self._process.finished.disconnect(process_finished)
            self._set_error_occured(self._error_outside_await)

        fut = asynker.Future(self._scheduler)
        source_signal.connect(result_callback)
        if exit_callback:
            self._process.finished.connect(process_finished)
        self._set_error_occured(error_occurred)
        fut.add_done_callback(disconnect)

        # Check if there already happened an error
        if self._stored_error is not None:
            error_occurred(self._stored_error)
            self._stored_error = None

        return fut

    def start(self, mode=QProcess.ReadWrite) -> Future:
        """
        Start the process. *mode* is passed to QProcess.start().
        """
        def started():
            fut.set_result(self._process.processId())

        assert self._process.state() == QProcess.NotRunning
        fut = self._error_handling_future(self._process.started, started)
        self._process.start(mode)
        return fut

    def _read(self, ready_read_signal, read_method) -> Future:
        def data_available():
            fut.set_result(read_method().data())

        fut = self._error_handling_future(ready_read_signal, data_available, data_available)
        # Clear out buffered data immediately
        data = read_method().data()
        if data:
            fut.set_result(data)
        elif not data and self._process.state() == QProcess.NotRunning:
            fut.set_result(b'')
        return fut

    def read_stdout(self) -> Future:
        """
        Read some binary data from the process's stdout channel.
        """
        return self._read(self._process.readyReadStandardOutput, self._process.readAllStandardOutput)

    def read_stderr(self) -> Future:
        """
        Read some binary data from the process's stdout channel.
        """
        return self._read(self._process.readyReadStandardError, self._process.readAllStandardError)

    def write(self, data) -> Future:
        """
        Write *data* to the standard input of the attached process.
        """
        def bytes_written(n=None):
            nonlocal pending_bytes
            if n is None and pending_bytes:
                fut.set_exception(AsyncProcessLostWrite)
            elif n:
                pending_bytes -= n
            if not pending_bytes:
                fut.set_result(None)

        assert self._process.state() == QProcess.Running
        fut = self._error_handling_future(self._process.bytesWritten, bytes_written, bytes_written)

        pending_bytes = len(data)
        amount = self._process.write(data)
        if amount == -1:
            fut.set_exception(OSError)
        pending_bytes -= amount
        if not pending_bytes:
            fut.set_result(None)
        return fut

    def write_eof(self):
        """
        Close (send EOF to) the standard input of the attached process.
        """
        self._process.closeWriteChannel()

    def finish(self) -> Future:
        """
        Wait until the process exits. Returns an (exit_code, QProcess.ExitStatus) tuple.
        """
        def finished(exit_code, exit_status):
            fut.set_result((exit_code, exit_status))

        fut = self._error_handling_future(self._process.finished, finished)
        if self._process.state() == QProcess.NotRunning:
            finished(self._process.exitCode(), self._process.exitStatus())
        return fut

    def running(self):
        return self._process.state() == QProcess.Running
Esempio n. 12
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.error.connect(self.on_error)
        self._proc.error.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."""
        msg = ERROR_STRINGS[error]
        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))
        if status == QProcess.CrashExit:
            message.error("{} crashed!".format(self._what.capitalize()))
        elif status == QProcess.NormalExit and code == 0:
            if self.verbose:
                message.info("{} exited successfully.".format(
                    self._what.capitalize()))
        else:
            assert status == QProcess.NormalExit
            # We call this 'status' here as it makes more sense to the user -
            # it's actually 'code'.
            message.error("{} exited with status {}.".format(
                self._what.capitalize(), code))

            stderr = bytes(self._proc.readAllStandardError()).decode('utf-8')
            stdout = bytes(self._proc.readAllStandardOutput()).decode('utf-8')
            if stdout:
                log.procs.error("Process stdout:\n" + stdout.strip())
            if stderr:
                log.procs.error("Process stderr:\n" + stderr.strip())

    @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, mode=None):
        """Convenience wrapper around QProcess::start."""
        log.procs.debug("Starting process.")
        self._pre_start(cmd, args)
        if mode is None:
            self._proc.start(cmd, args)
        else:
            self._proc.start(cmd, args, mode)
        self._proc.closeWriteChannel()

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

        if ok:
            log.procs.debug("Process started.")
            self._started = True
        else:
            message.error("Error while spawning {}: {}".format(
                self._what, ERROR_STRINGS[self._proc.error()]))

    def exit_status(self):
        return self._proc.exitStatus()
Esempio n. 13
0
class PlainTextEdit(QPlainTextEdit):
    commandSignal = pyqtSignal(str)
    commandZPressed = pyqtSignal(str)

    def __init__(self, parent=None, movable=False):
        super(PlainTextEdit, self).__init__()

        self.installEventFilter(self)
        self.setAcceptDrops(True)
        QApplication.setCursorFlashTime(1000)
        self.process = QProcess()
        self.process.readyReadStandardError.connect(
            self.onReadyReadStandardError)
        self.process.readyReadStandardOutput.connect(
            self.onReadyReadStandardOutput)

        self.name = (str(getpass.getuser()) + "@" + str(socket.gethostname()) +
                     ":" + str(os.getcwd()) + "$ ")
        self.appendPlainText(self.name)
        self.commands = [
        ]  # This is a list to track what commands the user has used so we could display them when
        # up arrow key is pressed
        self.tracker = 0
        self.setStyleSheet(
            "QPlainTextEdit{background-color: #212121; color: #f3f3f3; padding: 8;}"
        )
        self.verticalScrollBar().setStyleSheet("background-color: #212121;")
        self.text = None
        self.setFont(QFont("Noto Sans Mono", 8))
        self.previousCommandLength = 0

    def eventFilter(self, source, event):
        if (event.type() == QEvent.DragEnter):
            event.accept()
            print('DragEnter')
            return True
        elif (event.type() == QEvent.Drop):
            print('Drop')
            self.setDropEvent(event)
            return True
        else:
            return False  ### super(QPlainTextEdit).eventFilter(event)

    def setDropEvent(self, event):
        if event.mimeData().hasUrls():
            f = str(event.mimeData().urls()[0].toLocalFile())
            self.insertPlainText(f)
            event.accept()
        elif event.mimeData().hasText():
            ft = event.mimeData().text()
            print("text:", ft)
            self.insertPlainText(ft)
            event.accept()
        else:
            event.ignore()

    def keyPressEvent(self, e):
        cursor = self.textCursor()

        if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_A:
            return

        if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_Z:
            self.commandZPressed.emit("True")
            return

        if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_C:
            self.process.kill()
            self.name = (str(getpass.getuser()) + "@" +
                         str(socket.gethostname()) + ":" + str(os.getcwd()) +
                         "$ ")
            self.appendPlainText("process cancelled")
            self.appendPlainText(self.name)
            self.textCursor().movePosition(QTextCursor.End)
            return

        if e.key() == Qt.Key_Return:  ### 16777220:  # This is the ENTER key
            text = self.textCursor().block().text()

            if text == self.name + text.replace(self.name, "") and text.replace(
                    self.name, ""
            ) != "":  # This is to prevent adding in commands that were not meant to be added in
                self.commands.append(text.replace(self.name, ""))
#                print(self.commands)
            self.handle(text)
            self.commandSignal.emit(text)
            self.appendPlainText(self.name)

            return

        if e.key() == Qt.Key_Up:
            try:
                if self.tracker != 0:
                    cursor.select(QTextCursor.BlockUnderCursor)
                    cursor.removeSelectedText()
                    self.appendPlainText(self.name)

                self.insertPlainText(self.commands[self.tracker])
                self.tracker -= 1

            except IndexError:
                self.tracker = 0

            return

        if e.key() == Qt.Key_Down:
            try:
                cursor.select(QTextCursor.BlockUnderCursor)
                cursor.removeSelectedText()
                self.appendPlainText(self.name)

                self.insertPlainText(self.commands[self.tracker])
                self.tracker += 1

            except IndexError:
                self.tracker = 0

        if e.key() == Qt.Key_Backspace:  ### 16777219:
            if cursor.positionInBlock() <= len(self.name):
                return

            else:
                cursor.deleteChar()

        super().keyPressEvent(e)
        cursor = self.textCursor()
        e.accept()

    def ispressed(self):
        return self.pressed

    def onReadyReadStandardError(self):
        self.error = self.process.readAllStandardError().data().decode()
        self.appendPlainText(self.error.strip('\n'))

    def onReadyReadStandardOutput(self):
        self.result = self.process.readAllStandardOutput().data().decode()
        self.appendPlainText(self.result.strip('\n'))
        self.state = self.process.state()


#        print(self.result)

    def run(self, command):
        """Executes a system command."""
        if self.process.state() != 2:
            self.process.start(command)
            self.process.waitForFinished()
            self.textCursor().movePosition(QTextCursor.End)

    def handle(self, command):
        #        print("begin handle")
        """Split a command into list so command echo hi would appear as ['echo', 'hi']"""
        real_command = command.replace(self.name, "")

        if command == "True" and self.process.state() == 2:
            self.process.kill()
            self.appendPlainText("Program execution killed, press enter")

        if real_command.startswith("python"):
            pass

        command_list = real_command.split() if real_command != "" else None
        """Now we start implementing some commands"""
        if real_command == "clear":
            self.clear()

        elif command_list is not None and command_list[0] == "echo":
            self.appendPlainText(" ".join(command_list[1:]))

        elif real_command == "exit":
            quit()

        elif command_list is not None and command_list[0] == "cd" and len(
                command_list) > 1:
            try:
                os.chdir(" ".join(command_list[1:]))
                self.name = (str(getpass.getuser()) + "@" +
                             str(socket.gethostname()) + ":" +
                             str(os.getcwd()) + "$ ")
                self.textCursor().movePosition(QTextCursor.End)

            except FileNotFoundError as E:
                self.appendPlainText(str(E))

        elif command_list is not None and len(
                command_list) == 1 and command_list[0] == "cd":
            os.chdir(str(Path.home()))
            self.name = (str(getpass.getuser()) + "@" +
                         str(socket.gethostname()) + ":" + str(os.getcwd()) +
                         "$ ")
            self.textCursor().movePosition(QTextCursor.End)

        elif self.process.state() == 2:
            self.process.write(real_command.encode())
            self.process.closeWriteChannel()

        elif command == self.name + real_command:
            self.run(real_command)
Esempio n. 14
0
class HgClient(QObject):
    """
    Class implementing the Mercurial command server interface.
    """
    InputFormat = ">I"
    OutputFormat = ">cI"
    OutputFormatSize = struct.calcsize(OutputFormat)
    ReturnFormat = ">i"
    
    Channels = (b"I", b"L", b"o", b"e", b"r", b"d")
    
    def __init__(self, repoPath, encoding, vcs, parent=None):
        """
        Constructor
        
        @param repoPath root directory of the repository (string)
        @param encoding encoding to be used by the command server (string)
        @param vcs reference to the VCS object (Hg)
        @param parent reference to the parent object (QObject)
        """
        super(HgClient, self).__init__(parent)
        
        self.__server = None
        self.__started = False
        self.__version = None
        self.__encoding = vcs.getEncoding()
        self.__cancel = False
        self.__commandRunning = False
        self.__repoPath = repoPath
        
        # generate command line and environment
        self.__serverArgs = vcs.initCommand("serve")
        self.__serverArgs.append("--cmdserver")
        self.__serverArgs.append("pipe")
        self.__serverArgs.append("--config")
        self.__serverArgs.append("ui.interactive=True")
        if repoPath:
            self.__serverArgs.append("--repository")
            self.__serverArgs.append(repoPath)
        
        if encoding:
            self.__encoding = encoding
            if "--encoding" in self.__serverArgs:
                # use the defined encoding via the environment
                index = self.__serverArgs.index("--encoding")
                del self.__serverArgs[index:index + 2]
    
    def startServer(self):
        """
        Public method to start the command server.
        
        @return tuple of flag indicating a successful start (boolean) and
            an error message (string) in case of failure
        """
        self.__server = QProcess()
        self.__server.setWorkingDirectory(self.__repoPath)
        
        # connect signals
        self.__server.finished.connect(self.__serverFinished)
        
        prepareProcess(self.__server, self.__encoding)
        
        self.__server.start('hg', self.__serverArgs)
        serverStarted = self.__server.waitForStarted(5000)
        if not serverStarted:
            return False, self.tr(
                'The process {0} could not be started. '
                'Ensure, that it is in the search path.'
            ).format('hg')
        
        self.__server.setReadChannel(QProcess.StandardOutput)
        ok, error = self.__readHello()
        self.__started = ok
        return ok, error
    
    def stopServer(self):
        """
        Public method to stop the command server.
        """
        if self.__server is not None:
            self.__server.closeWriteChannel()
            res = self.__server.waitForFinished(5000)
            if not res:
                self.__server.terminate()
                res = self.__server.waitForFinished(3000)
                if not res:
                    self.__server.kill()
                    self.__server.waitForFinished(3000)
            
            self.__started = False
            self.__server.deleteLater()
            self.__server = None
    
    def restartServer(self):
        """
        Public method to restart the command server.
        
        @return tuple of flag indicating a successful start (boolean) and
            an error message (string) in case of failure
        """
        self.stopServer()
        return self.startServer()
    
    def __readHello(self):
        """
        Private method to read the hello message sent by the command server.
        
        @return tuple of flag indicating success (boolean) and an error message
            in case of failure (string)
        """
        ch, msg = self.__readChannel()
        if not ch:
            return False, self.tr("Did not receive the 'hello' message.")
        elif ch != "o":
            return False, self.tr("Received data on unexpected channel.")
        
        msg = msg.split("\n")
        
        if not msg[0].startswith("capabilities: "):
            return False, self.tr(
                "Bad 'hello' message, expected 'capabilities: '"
                " but got '{0}'.").format(msg[0])
        self.__capabilities = msg[0][len('capabilities: '):]
        if not self.__capabilities:
            return False, self.tr("'capabilities' message did not contain"
                                  " any capability.")
        
        self.__capabilities = set(self.__capabilities.split())
        if "runcommand" not in self.__capabilities:
            return False, "'capabilities' did not contain 'runcommand'."
        
        if not msg[1].startswith("encoding: "):
            return False, self.tr(
                "Bad 'hello' message, expected 'encoding: '"
                " but got '{0}'.").format(msg[1])
        encoding = msg[1][len('encoding: '):]
        if not encoding:
            return False, self.tr("'encoding' message did not contain"
                                  " any encoding.")
        self.__encoding = encoding
        
        return True, ""
    
    def __serverFinished(self, exitCode, exitStatus):
        """
        Private slot connected to the finished signal.
        
        @param exitCode exit code of the process (integer)
        @param exitStatus exit status of the process (QProcess.ExitStatus)
        """
        self.__started = False
    
    def __readChannel(self):
        """
        Private method to read data from the command server.
        
        @return tuple of channel designator and channel data
            (string, integer or string or bytes)
        """
        if self.__server.bytesAvailable() > 0 or \
           self.__server.waitForReadyRead(10000):
            data = bytes(self.__server.peek(HgClient.OutputFormatSize))
            if not data or len(data) < HgClient.OutputFormatSize:
                return "", ""
            
            channel, length = struct.unpack(HgClient.OutputFormat, data)
            channel = channel.decode(self.__encoding)
            if channel in "IL":
                self.__server.read(HgClient.OutputFormatSize)
                return channel, length
            else:
                if self.__server.bytesAvailable() < \
                        HgClient.OutputFormatSize + length:
                    return "", ""
                self.__server.read(HgClient.OutputFormatSize)
                data = self.__server.read(length)
                if channel == "r":
                    return (channel, data)
                else:
                    return (channel, str(data, self.__encoding, "replace"))
        else:
            return "", ""
    
    def __writeDataBlock(self, data):
        """
        Private slot to write some data to the command server.
        
        @param data data to be sent (string)
        """
        if not isinstance(data, bytes):
            data = data.encode(self.__encoding)
        self.__server.write(
            QByteArray(struct.pack(HgClient.InputFormat, len(data))))
        self.__server.write(QByteArray(data))
        self.__server.waitForBytesWritten()
    
    def __runcommand(self, args, inputChannels, outputChannels):
        """
        Private method to run a command in the server (low level).
        
        @param args list of arguments for the command (list of string)
        @param inputChannels dictionary of input channels. The dictionary must
            have the keys 'I' and 'L' and each entry must be a function
            receiving the number of bytes to write.
        @param outputChannels dictionary of output channels. The dictionary
            must have the keys 'o' and 'e' and each entry must be a function
            receiving the data.
        @return result code of the command, -1 if the command server wasn't
            started or -10, if the command was canceled (integer)
        @exception RuntimeError raised to indicate an unexpected command
            channel
        """
        if not self.__started:
            return -1
        
        self.__server.write(QByteArray(b'runcommand\n'))
        self.__writeDataBlock('\0'.join(args))
        
        while True:
            QCoreApplication.processEvents()
            
            if self.__cancel:
                return -10
            
            if self.__server is None:
                return -1
            
            if self.__server is None or self.__server.bytesAvailable() == 0:
                QThread.msleep(50)
                continue
            channel, data = self.__readChannel()
            
            # input channels
            if channel in inputChannels:
                input = inputChannels[channel](data)
                if channel == "L":
                    # echo the input to the output if it was a prompt
                    outputChannels["o"](input)
                self.__writeDataBlock(input)
            
            # output channels
            elif channel in outputChannels:
                outputChannels[channel](data)
            
            # result channel, command is finished
            elif channel == "r":
                return struct.unpack(HgClient.ReturnFormat, data)[0]
            
            # unexpected but required channel
            elif channel.isupper():
                raise RuntimeError(
                    "Unexpected but required channel '{0}'.".format(channel))
            
            # optional channels or no channel at all
            else:
                pass
    
    def __prompt(self, size, message):
        """
        Private method to prompt the user for some input.
        
        @param size maximum length of the requested input (integer)
        @param message message sent by the server (string)
        @return data entered by the user (string)
        """
        from .HgClientPromptDialog import HgClientPromptDialog
        input = ""
        dlg = HgClientPromptDialog(size, message)
        if dlg.exec_() == QDialog.Accepted:
            input = dlg.getInput() + '\n'
        return input
    
    def runcommand(self, args, prompt=None, input=None, output=None,
                   error=None):
        """
        Public method to execute a command via the command server.
        
        @param args list of arguments for the command (list of string)
        @keyparam prompt function to reply to prompts by the server. It
            receives the max number of bytes to return and the contents
            of the output channel received so far.
        @keyparam input function to reply to bulk data requests by the server.
            It receives the max number of bytes to return.
        @keyparam output function receiving the data from the server (string).
            If a prompt function is given, this parameter will be ignored.
        @keyparam error function receiving error messages from the server
            (string)
        @return output and errors of the command server (string). In case
            output and/or error functions were given, the respective return
            value will be an empty string.
        """
        self.__commandRunning = True
        outputChannels = {}
        outputBuffer = None
        errorBuffer = None
        
        if prompt is not None or output is None:
            outputBuffer = io.StringIO()
            outputChannels["o"] = outputBuffer.write
        else:
            outputChannels["o"] = output
        if error:
            outputChannels["e"] = error
        else:
            errorBuffer = io.StringIO()
            outputChannels["e"] = errorBuffer.write
        
        inputChannels = {}
        if prompt is not None:
            def func(size):
                reply = prompt(size, outputBuffer.getvalue())
                return reply
            inputChannels["L"] = func
        else:
            def myprompt(size):
                if outputBuffer is None:
                    msg = self.tr("For message see output dialog.")
                else:
                    msg = outputBuffer.getvalue()
                reply = self.__prompt(size, msg)
                return reply
            inputChannels["L"] = myprompt
        if input is not None:
            inputChannels["I"] = input
        
        self.__cancel = False
        self.__runcommand(args, inputChannels, outputChannels)
        if outputBuffer:
            out = outputBuffer.getvalue()
        else:
            out = ""
        if errorBuffer:
            err = errorBuffer.getvalue()
        else:
            err = ""
        
        self.__commandRunning = False
        
        return out, err
    
    def cancel(self):
        """
        Public method to cancel the running command.
        """
        self.__cancel = True
        self.restartServer()
    
    def wasCanceled(self):
        """
        Public method to check, if the last command was canceled.
        
        @return flag indicating the cancel state (boolean)
        """
        return self.__cancel
    
    def isExecuting(self):
        """
        Public method to check, if the server is executing a command.
        
        @return flag indicating the execution of a command (boolean)
        """
        return self.__commandRunning
Esempio n. 15
0
class SevenZipExtractor(Extractor):

    sig_entry_extracted = pyqtSignal(str, str)

    def __init__(self, filename: str, outdir: str) -> None:
        super().__init__()

        self._filename = os.path.abspath(filename)
        self._outdir = outdir

        self._process: Optional[QProcess] = None
        self._errors: List[str] = []

        self._error_summary = False
        self._result: Optional[ExtractorResult] = None

    def interrupt(self):
        if self._process is not None:
            self._process.terminate()
            # self._process.waitForBytesWritten(int msecs = 30000)
            # self._process.kill()

    def extract(self) -> ExtractorResult:
        assert self._process is None

        try:
            self._start_extract(self._outdir)
            self._process.waitForFinished(-1)
            assert self._result is not None
            return self._result
        except Exception as err:
            message = "{}: failure when extracting archive".format(self._filename)
            logger.exception(message)
            message += "\n\n" + traceback.format_exc()
            return ExtractorResult.failure(message)

    def _start_extract(self, outdir: str) -> None:
        program = "7z"
        argv = ["x", "-ba", "-bb1", "-bd", "-aos", "-o" + outdir, self._filename]

        logger.debug("SevenZipExtractorWorker: launching %s %s", program, argv)
        self._process = QProcess()
        self._process.setProgram(program)
        self._process.setArguments(argv)

        self._process.readyReadStandardOutput.connect(self._on_ready_read_stdout)
        self._process.readyReadStandardError.connect(self._on_ready_read_stderr)
        self._process.finished.connect(self._on_process_finished)

        self._process.start()
        self._process.closeWriteChannel()

    def _on_process_finished(self, exit_code, exit_status):
        self._process.setCurrentReadChannel(QProcess.StandardOutput)
        for line in os.fsdecode(self._process.readAll().data()).splitlines():
            self._process_stdout(line)

        self._process.setCurrentReadChannel(QProcess.StandardError)
        for line in os.fsdecode(self._process.readAll().data()).splitlines():
            self._process_stderr(line)

        if self._errors != []:
            message = "7-Zip: " + "\n".join(self._errors)
        else:
            message = ""

        if message:
            logger.error("SevenZipExtractorWorker: errors: %s", message)

        if exit_status != QProcess.NormalExit or exit_code != 0:
            logger.error("SevenZipExtractorWorker: something went wrong: %s  %s", exit_code, exit_status)
            self._result = ExtractorResult.failure(message)
        else:
            logger.debug("SevenZipExtractorWorker: finished successfully: %s  %s", exit_code, exit_status)
            self._result = ExtractorResult.success()

    def _process_stdout(self, line):
        if line.startswith("- "):
            entry = line[2:]
            self.sig_entry_extracted.emit(entry, os.path.join(self._outdir, entry))

    def _process_stderr(self, line):
        if line == "ERRORS:":
            self._error_summary = True
        else:
            if self._error_summary:
                if line != "":
                    self._errors.append(line)

    def _on_ready_read_stdout(self) -> None:
        assert self._process is not None

        while self._process.canReadLine():
            buf: QByteArray = self._process.readLine()
            line = os.fsdecode(buf.data()).rstrip("\n")
            # print("stdout:", repr(line))
            self._process_stdout(line)

    def _on_ready_read_stderr(self) -> None:
        assert self._process is not None

        while self._process.canReadLine():
            # print("stderr:", repr(line))
            buf = self._process.readLine()
            line = os.fsdecode(buf.data()).rstrip("\n")
            self._process_stderr(line)
Esempio n. 16
0
class HgClient(QObject):
    """
    Class implementing the Mercurial command server interface.
    """
    InputFormat = ">I"
    OutputFormat = ">cI"
    OutputFormatSize = struct.calcsize(OutputFormat)
    ReturnFormat = ">i"
    
    Channels = (b"I", b"L", b"o", b"e", b"r", b"d")
    
    def __init__(self, repoPath, encoding, vcs, parent=None):
        """
        Constructor
        
        @param repoPath root directory of the repository (string)
        @param encoding encoding to be used by the command server (string)
        @param vcs reference to the VCS object (Hg)
        @param parent reference to the parent object (QObject)
        """
        super(HgClient, self).__init__(parent)
        
        self.__server = None
        self.__started = False
        self.__version = None
        self.__encoding = vcs.getEncoding()
        self.__cancel = False
        self.__commandRunning = False
        self.__repoPath = repoPath
        
        # generate command line and environment
        self.__serverArgs = vcs.initCommand("serve")
        self.__serverArgs.append("--cmdserver")
        self.__serverArgs.append("pipe")
        self.__serverArgs.append("--config")
        self.__serverArgs.append("ui.interactive=True")
        if repoPath:
            self.__serverArgs.append("--repository")
            self.__serverArgs.append(repoPath)
        
        if encoding:
            self.__encoding = encoding
            if "--encoding" in self.__serverArgs:
                # use the defined encoding via the environment
                index = self.__serverArgs.index("--encoding")
                del self.__serverArgs[index:index + 2]
    
    def startServer(self):
        """
        Public method to start the command server.
        
        @return tuple of flag indicating a successful start (boolean) and
            an error message (string) in case of failure
        """
        self.__server = QProcess()
        self.__server.setWorkingDirectory(self.__repoPath)
        
        # connect signals
        self.__server.finished.connect(self.__serverFinished)
        
        prepareProcess(self.__server, self.__encoding)
        
        self.__server.start('hg', self.__serverArgs)
        serverStarted = self.__server.waitForStarted(5000)
        if not serverStarted:
            return False, self.tr(
                'The process {0} could not be started. '
                'Ensure, that it is in the search path.'
            ).format('hg')
        
        self.__server.setReadChannel(QProcess.StandardOutput)
        ok, error = self.__readHello()
        self.__started = ok
        return ok, error
    
    def stopServer(self):
        """
        Public method to stop the command server.
        """
        if self.__server is not None:
            self.__server.closeWriteChannel()
            res = self.__server.waitForFinished(5000)
            if not res:
                self.__server.terminate()
                res = self.__server.waitForFinished(3000)
                if not res:
                    self.__server.kill()
                    self.__server.waitForFinished(3000)
            
            self.__started = False
            self.__server.deleteLater()
            self.__server = None
    
    def restartServer(self):
        """
        Public method to restart the command server.
        
        @return tuple of flag indicating a successful start (boolean) and
            an error message (string) in case of failure
        """
        self.stopServer()
        return self.startServer()
    
    def __readHello(self):
        """
        Private method to read the hello message sent by the command server.
        
        @return tuple of flag indicating success (boolean) and an error message
            in case of failure (string)
        """
        ch, msg = self.__readChannel()
        if not ch:
            return False, self.tr("Did not receive the 'hello' message.")
        elif ch != "o":
            return False, self.tr("Received data on unexpected channel.")
        
        msg = msg.split("\n")
        
        if not msg[0].startswith("capabilities: "):
            return False, self.tr(
                "Bad 'hello' message, expected 'capabilities: '"
                " but got '{0}'.").format(msg[0])
        self.__capabilities = msg[0][len('capabilities: '):]
        if not self.__capabilities:
            return False, self.tr("'capabilities' message did not contain"
                                  " any capability.")
        
        self.__capabilities = set(self.__capabilities.split())
        if "runcommand" not in self.__capabilities:
            return False, "'capabilities' did not contain 'runcommand'."
        
        if not msg[1].startswith("encoding: "):
            return False, self.tr(
                "Bad 'hello' message, expected 'encoding: '"
                " but got '{0}'.").format(msg[1])
        encoding = msg[1][len('encoding: '):]
        if not encoding:
            return False, self.tr("'encoding' message did not contain"
                                  " any encoding.")
        self.__encoding = encoding
        
        return True, ""
    
    def __serverFinished(self, exitCode, exitStatus):
        """
        Private slot connected to the finished signal.
        
        @param exitCode exit code of the process (integer)
        @param exitStatus exit status of the process (QProcess.ExitStatus)
        """
        self.__started = False
    
    def __readChannel(self):
        """
        Private method to read data from the command server.
        
        @return tuple of channel designator and channel data
            (string, integer or string or bytes)
        """
        if self.__server.bytesAvailable() > 0 or \
           self.__server.waitForReadyRead(10000):
            data = bytes(self.__server.peek(HgClient.OutputFormatSize))
            if not data or len(data) < HgClient.OutputFormatSize:
                return "", ""
            
            channel, length = struct.unpack(HgClient.OutputFormat, data)
            channel = channel.decode(self.__encoding)
            if channel in "IL":
                self.__server.read(HgClient.OutputFormatSize)
                return channel, length
            else:
                if self.__server.bytesAvailable() < \
                        HgClient.OutputFormatSize + length:
                    return "", ""
                self.__server.read(HgClient.OutputFormatSize)
                data = self.__server.read(length)
                if channel == "r":
                    return (channel, data)
                else:
                    return (channel, str(data, self.__encoding, "replace"))
        else:
            return "", ""
    
    def __writeDataBlock(self, data):
        """
        Private slot to write some data to the command server.
        
        @param data data to be sent (string)
        """
        if not isinstance(data, bytes):
            data = data.encode(self.__encoding)
        self.__server.write(
            QByteArray(struct.pack(HgClient.InputFormat, len(data))))
        self.__server.write(QByteArray(data))
        self.__server.waitForBytesWritten()
    
    def __runcommand(self, args, inputChannels, outputChannels):
        """
        Private method to run a command in the server (low level).
        
        @param args list of arguments for the command (list of string)
        @param inputChannels dictionary of input channels. The dictionary must
            have the keys 'I' and 'L' and each entry must be a function
            receiving the number of bytes to write.
        @param outputChannels dictionary of output channels. The dictionary
            must have the keys 'o' and 'e' and each entry must be a function
            receiving the data.
        @return result code of the command, -1 if the command server wasn't
            started or -10, if the command was canceled (integer)
        @exception RuntimeError raised to indicate an unexpected command
            channel
        """
        if not self.__started:
            return -1
        
        self.__server.write(QByteArray(b'runcommand\n'))
        self.__writeDataBlock('\0'.join(args))
        
        while True:
            QCoreApplication.processEvents()
            
            if self.__cancel:
                return -10
            
            if self.__server is None:
                return -1
            
            if self.__server is None or self.__server.bytesAvailable() == 0:
                QThread.msleep(50)
                continue
            channel, data = self.__readChannel()
            
            # input channels
            if channel in inputChannels:
                input = inputChannels[channel](data)
                if channel == "L":
                    # echo the input to the output if it was a prompt
                    outputChannels["o"](input)
                self.__writeDataBlock(input)
            
            # output channels
            elif channel in outputChannels:
                outputChannels[channel](data)
            
            # result channel, command is finished
            elif channel == "r":
                return struct.unpack(HgClient.ReturnFormat, data)[0]
            
            # unexpected but required channel
            elif channel.isupper():
                raise RuntimeError(
                    "Unexpected but required channel '{0}'.".format(channel))
            
            # optional channels or no channel at all
            else:
                pass
    
    def __prompt(self, size, message):
        """
        Private method to prompt the user for some input.
        
        @param size maximum length of the requested input (integer)
        @param message message sent by the server (string)
        @return data entered by the user (string)
        """
        from .HgClientPromptDialog import HgClientPromptDialog
        input = ""
        dlg = HgClientPromptDialog(size, message)
        if dlg.exec_() == QDialog.Accepted:
            input = dlg.getInput() + '\n'
        return input
    
    def runcommand(self, args, prompt=None, input=None, output=None,
                   error=None):
        """
        Public method to execute a command via the command server.
        
        @param args list of arguments for the command (list of string)
        @keyparam prompt function to reply to prompts by the server. It
            receives the max number of bytes to return and the contents
            of the output channel received so far.
        @keyparam input function to reply to bulk data requests by the server.
            It receives the max number of bytes to return.
        @keyparam output function receiving the data from the server (string).
            If a prompt function is given, this parameter will be ignored.
        @keyparam error function receiving error messages from the server
            (string)
        @return output and errors of the command server (string). In case
            output and/or error functions were given, the respective return
            value will be an empty string.
        """
        self.__commandRunning = True
        outputChannels = {}
        outputBuffer = None
        errorBuffer = None
        
        if prompt is not None or output is None:
            outputBuffer = io.StringIO()
            outputChannels["o"] = outputBuffer.write
        else:
            outputChannels["o"] = output
        if error:
            outputChannels["e"] = error
        else:
            errorBuffer = io.StringIO()
            outputChannels["e"] = errorBuffer.write
        
        inputChannels = {}
        if prompt is not None:
            def func(size):
                reply = prompt(size, outputBuffer.getvalue())
                return reply
            inputChannels["L"] = func
        else:
            def myprompt(size):
                if outputBuffer is None:
                    msg = self.tr("For message see output dialog.")
                else:
                    msg = outputBuffer.getvalue()
                reply = self.__prompt(size, msg)
                return reply
            inputChannels["L"] = myprompt
        if input is not None:
            inputChannels["I"] = input
        
        self.__cancel = False
        self.__runcommand(args, inputChannels, outputChannels)
        if outputBuffer:
            out = outputBuffer.getvalue()
        else:
            out = ""
        if errorBuffer:
            err = errorBuffer.getvalue()
        else:
            err = ""
        
        self.__commandRunning = False
        
        return out, err
    
    def cancel(self):
        """
        Public method to cancel the running command.
        """
        self.__cancel = True
        self.restartServer()
    
    def wasCanceled(self):
        """
        Public method to check, if the last command was canceled.
        
        @return flag indicating the cancel state (boolean)
        """
        return self.__cancel
    
    def isExecuting(self):
        """
        Public method to check, if the server is executing a command.
        
        @return flag indicating the execution of a command (boolean)
        """
        return self.__commandRunning
Esempio n. 17
0
class PluginProcessBase(QObject):
    proc_list = []

    def __init__(self, wdir):
        QObject.__init__(self)

        PluginProcess.proc_list.append(self)
        self.is_rebuild = False
        self.is_query_fl = False

        self.sig = QuerySignal()

        self.proc = QProcess()
        self.proc.finished.connect(self._finished_cb)
        self.proc.error.connect(self._error_cb)

        self.proc.setWorkingDirectory(wdir)
        self.wdir = wdir

    def _cleanup(self):
        PluginProcess.proc_list.remove(self)
        if self.err_str != '':
            s = '<b>' + self.p_cmd + '</b><p>' + '<p>'.join(
                self.err_str.splitlines())
            QMessageBox.warning(None, "Seascope", s, QMessageBox.Ok)
        if self.res != '':
            s = '<b>' + self.p_cmd + '</b><p>Summary<p>' + self.res
            QMessageBox.information(None, "Seascope", s, QMessageBox.Ok)

    def _error_cb(self, err):
        err_dict = {
            QProcess.FailedToStart: 'FailedToStart',
            QProcess.Crashed: 'Crashed',
            QProcess.Timedout: 'The last waitFor...() function timed out',
            QProcess.WriteError:
            'An error occurred when attempting to write to the process',
            QProcess.ReadError:
            'An error occurred when attempting to read from the process',
            QProcess.UnknownError: 'An unknown error occurred',
        }
        self.err_str = '<b>' + self.p_cmd + '</b><p>' + err_dict[err]
        self._cleanup()

    def _finished_cb(self, ret):
        res = self.proc.readAllStandardOutput().data().decode()
        self.err_str = self.proc.readAllStandardError().data().decode()

        #print 'output', res
        #print 'cmd:', self.p_cmd
        if self.is_rebuild:
            self.res = res
            self.sig.sig_rebuild.emit()
        elif self.is_query_fl:
            self.res = ''
            res = self.parse_query_fl(res)
            self.sig.sig_query_fl.emit(res)
        else:
            self.res = ''
            self.sig.sig_result_dbg.emit(self.p_cmd, res, self.err_str)
            try:
                res = self.parse_result(res, self.sig)
            except Exception as e:
                print(e)
                res = [[
                    '', '', '', 'error while parsing output of: ' + self.p_cmd
                ]]
            if res != None:
                self.sig.emit_result(res)

        self._cleanup()

    def run_query_process(self, pargs, sym, rquery=None):
        self.sig.sym = sym
        self.sig.rquery = rquery
        self.p_cmd = ' '.join(pargs)
        if os.getenv('SEASCOPE_DEBUG'):
            print(self.p_cmd)
        self.proc.start(pargs[0], pargs[1:])
        if self.proc.waitForStarted() == False:
            return None
        self.proc.closeWriteChannel()
        return [self.sig.sig_result, self.sig.sig_result_dbg]

    def run_rebuild_process(self, pargs):
        self.is_rebuild = True
        self.p_cmd = ' '.join(pargs)
        self.proc.start(pargs[0], pargs[1:])
        if self.proc.waitForStarted() == False:
            return None
        #print 'cmd:', pargs
        self.sig.sig_rebuild.connect(CtagsCache.flush)
        return self.sig.sig_rebuild

    def run_query_fl(self, pargs):
        self.is_query_fl = True
        self.p_cmd = ' '.join(pargs)
        self.proc.start(pargs[0], pargs[1:])
        if self.proc.waitForStarted() == False:
            return None
        return self.sig.sig_query_fl

    def parse_query_fl(self, text):
        fl = []
        for f in re.split('\r?\n', text.strip()):
            if f == '':
                continue
            fl.append(os.path.join(self.wdir, f))
        return fl
Esempio n. 18
0
class RarExtractor(Extractor):

    sig_entry_extracted = pyqtSignal(str, str)

    def __init__(self, filename: str, outdir: str) -> None:
        super().__init__()

        self._filename = os.path.abspath(filename)
        self._outdir = outdir

        self._process: Optional[QProcess] = None
        self._errors: List[str] = []

        self._result: Optional[ExtractorResult] = None

        self._output_state = State.HEADER

    def interrupt(self):
        if self._process is not None:
            self._process.terminate()
            # self._process.kill()

    def extract(self) -> ExtractorResult:
        try:
            self._start_extract(self._outdir)
            self._process.waitForFinished(-1)
            return self._result
        except Exception as err:
            message = "{}: failure when extracting archive".format(self._filename)
            logger.exception(message)
            return ExtractorResult.failure(message)

    def _start_extract(self, outdir: str) -> None:
        # The directory is already created in ArchiveExtractor
        # os.mkdir(outdir)
        assert os.path.isdir(outdir)

        program = "rar"
        argv = ["x", "-p-", "-c-", self._filename]
        # "-w" + outdir has no effect

        logger.debug("RarExtractorWorker: launching %s %s", program, argv)
        self._process = QProcess()
        self._process.setProgram(program)
        self._process.setArguments(argv)
        self._process.setWorkingDirectory(outdir)
        self._process.start()
        self._process.closeWriteChannel()

        self._process.readyReadStandardOutput.connect(self._on_ready_read_stdout)
        self._process.readyReadStandardError.connect(self._on_ready_read_stderr)
        self._process.errorOccurred.connect(self._on_error_occured)
        self._process.finished.connect(self._on_process_finished)

    def _on_process_finished(self, exit_code, exit_status):
        self._process.setCurrentReadChannel(QProcess.StandardOutput)
        for line in os.fsdecode(self._process.readAll().data()).splitlines():
            self._process_stdout(line)

        self._process.setCurrentReadChannel(QProcess.StandardError)
        for line in os.fsdecode(self._process.readAll().data()).splitlines():
            self._process_stderr(line)

        if self._errors != []:
            message = "RAR: " + "\n".join(self._errors)
        else:
            message = ""

        if exit_status != QProcess.NormalExit or exit_code != 0:
            logger.error("RarExtractorWorker: something went wrong: %s  %s", exit_code, exit_status)
            self._result = ExtractorResult.failure(message)
        else:
            logger.debug("RarExtractorWorker: finished successfully: %s  %s", exit_code, exit_status)
            self._result = ExtractorResult.success(message)

    def _on_error_occured(self, error) -> None:
        logger.error("RarExtractorWorker: an error occured: %s", error)
        self._result = ExtractorResult.failure("RarExtractorWorker: an error occured: {}".format(error))

    def _process_stdout(self, line):
        # print("stdout:", repr(line))

        if self._output_state == State.HEADER:
            if BEGIN_RX.match(line):
                self._output_state = State.FILELIST
        elif self._output_state == State.FILELIST:
            m = FILE_RX.match(line)
            if m:
                entry = m.group(1).rstrip(" ")
                self.sig_entry_extracted.emit(entry, os.path.join(self._outdir, entry))
            else:
                m = DIR_RX.match(line)
                if m:
                    entry = m.group(1).rstrip(" ")
                    self.sig_entry_extracted.emit(entry, os.path.join(self._outdir, entry))
                elif line == "":
                    pass  # ignore empty line at the start
                else:
                    # self._errors.append(line)
                    # self._output_state = State.RESULT
                    pass
        else:
            # self._errors.append(line)
            pass

    def _process_stderr(self, line):
        # print("stderr:", repr(line))
        if line:
            self._errors.append(line)

    def _on_ready_read_stdout(self) -> None:
        assert self._process is not None

        self._process.setCurrentReadChannel(QProcess.StandardOutput)
        while self._process.canReadLine():
            buf: QByteArray = self._process.readLine()
            line = os.fsdecode(buf.data())
            line = line.rstrip("\n")
            self._process_stdout(line)

    def _on_ready_read_stderr(self) -> None:
        assert self._process is not None

        self._process.setCurrentReadChannel(QProcess.StandardError)
        while self._process.canReadLine():
            buf: QByteArray = self._process.readLine()
            line = os.fsdecode(buf.data())
            line = line.rstrip("\n")
            self._process_stderr(line)
class ProcessHandler(QObject):

    commandReceived = pyqtSignal(str)
    commandSent = pyqtSignal(str)
    processFinished = pyqtSignal()
    processError = pyqtSignal(str)

    def __init__(self, executable, parent=None):

        QObject.__init__(self, parent)
        self.executable = executable

    def run(self):

        self.process = QProcess()
        self.process.readyReadStandardOutput.connect(self.handleInput)
        self.process.setReadChannel(QProcess.StandardOutput)
        self.process.closeReadChannel(QProcess.StandardError)
        self.process.finished.connect(self.processFinished)
        self.pendingInput = QByteArray()
        self.in_message = False
        self.inputExpected = 0
        self.pendingOutput = QByteArray()
        self.process.start(self.executable)

        # On Qt 5.6 and later, we can use the errorOccurred signal instead.
        if not self.process.waitForStarted(-1):
            self.processError.emit("The application quit unexpectedly.")

    def quit(self):

        print("Terminating %i" % self.process.processId())
        self.process.closeReadChannel(QProcess.StandardOutput)
        self.process.closeReadChannel(QProcess.StandardError)
        self.process.closeWriteChannel()
        self.process.terminate()
        self.process.waitForFinished()
        self.thread().quit()

    def handleInput(self):

        self.pendingInput += self.process.readAllStandardOutput()

        try:
            while self.pendingInput.size() > 0:

                if not self.in_message:
                    space = self.pendingInput.indexOf(b" ")
                    if space == -1:
                        return

                    # Specify UTF-8 instead of falling back on something implicit.
                    self.inputExpected = int(
                        str(self.pendingInput.left(space), "utf8"))

                    # Examine the rest of the input.
                    self.pendingInput = self.pendingInput.mid(space + 1)
                    self.in_message = True

                # Try to read the rest of the message.
                if len(self.pendingInput) >= self.inputExpected:

                    command = self.pendingInput.left(self.inputExpected)

                    self.pendingInput = self.pendingInput.mid(
                        self.inputExpected)
                    self.in_message = False
                    self.inputExpected = 0
                    self.commandReceived.emit(str(command, "utf8"))

                elif self.process.bytesAvailable() > 0:
                    self.pendingInput += self.process.readAllStandardOutput()
                else:
                    return

        except ValueError:
            self.processError.emit(str(self.pendingInput, "utf8"))

    def handleOutput(self, message):

        # Write the length of the message and the message itself.
        message = "%i %s" % (len(message), message)
        self.pendingOutput += QByteArray(bytes(message, "utf8"))

        while self.pendingOutput.size() > 0:

            written = self.process.write(self.pendingOutput)

            if written == -1:
                self.processError.emit("Failed to write to application.")
                return

            # Handle the rest of the output.
            self.pendingOutput = self.pendingOutput.mid(written)

        self.commandSent.emit(message)
Esempio n. 20
0
class Terminal(QWidget):
    errorSignal = pyqtSignal(str)
    outputSignal = pyqtSignal(str)

    def __init__(self, movable=False):
        super().__init__()

        self.setWindowFlags(
            Qt.Widget |
            Qt.WindowCloseButtonHint |
            Qt.WindowStaysOnTopHint |
            Qt.FramelessWindowHint
        )
        self.movable = movable
        self.layout = QVBoxLayout()
        self.pressed = False
        self.process = QProcess(self)
       # self.editor = PlainTextEdit(self, self.movable)

        self.name = None

       # self.layout.addWidget(self.editor)

        self.process.readyReadStandardError.connect(self.onReadyReadStandardError)
        self.process.readyReadStandardOutput.connect(self.onReadyReadStandardOutput)
        self.setLayout(self.layout)
        self.setStyleSheet("QWidget {background-color:invisible;}")

        # self.showMaximized() # comment this if you want to embed this widget

    def ispressed(self):
        return self.pressed

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def add(self):
        self.added()
        self.button = QPushButton("Hide terminal")
        self.button.setStyleSheet("""
        height: 20;
        """)
        self.button.setFixedWidth(100)
        self.editor = PlainTextEdit(self, self.movable)
        self.highlighter = name_highlighter(self.editor.document(), str(getpass.getuser()), str(socket.gethostname()),
                                            str(os.getcwd()))
        self.layout.addWidget(self.button)
        self.layout.addWidget(self.editor)
        self.editor.commandSignal.connect(self.handle)
        self.button.clicked.connect(self.remove)
        self.editor.commandZPressed.connect(self.handle)

    def added(self):
        self.pressed = True

    def remove(self):
        self.editor.deleteLater()
        self.button.deleteLater()
        self.pressed = False

    def mousePressEvent(self, event):
        self.oldPos = event.globalPos()

    def mouseMoveEvent(self, event):
        delta = QPoint(event.globalPos() - self.oldPos)

        self.move(self.x() + delta.x(), self.y() + delta.y())
        self.oldPos = event.globalPos()

    def onReadyReadStandardError(self):
        self.error = self.process.readAllStandardError().data().decode()
        self.editor.appendPlainText(self.error.strip('\n'))
        self.errorSignal.emit(self.error)

    def onReadyReadStandardOutput(self):
        self.result = self.process.readAllStandardOutput().data().decode()
        self.editor.appendPlainText(self.result.strip('\n'))
        self.state = self.process.state()
        self.outputSignal.emit(self.result)

    def run(self, command):
        """Executes a system command."""
        if self.process.state() != 2:
            self.process.start(command)
            print("Command executed, process state is now: " + str(self.process.state()))

    def handle(self, command):

        """Split a command into list so command echo hi would appear as ['echo', 'hi']"""
        real_command = command.replace(self.editor.name, "")

        if command == "True":
            if self.process.state() == 2:
                self.process.kill()
                self.editor.appendPlainText("Program execution killed, press enter")
        if real_command.startswith("python"):
            # TODO: start a python thread....
            pass

        if real_command != "":
            command_list = real_command.split()
        else:
            command_list = None
        """Now we start implementing some commands"""
        if real_command == "clear":
            self.editor.clear()

        elif "&&" in command_list:
            pass
            # print(command_list)
            # print(command_list.index("&&"))
            # print(command_list[command_list.index("&&")+1:])

        elif command_list is not None and command_list[0] == "echo":
            self.editor.appendPlainText(" ".join(command_list[1:]))

        elif real_command == "exit":
            qApp.exit()

        elif command_list is not None and command_list[0] == "cd" and len(command_list) > 1:
            try:
                os.chdir(" ".join(command_list[1:]))
                self.editor.name = "[" + str(getpass.getuser()) + "@" + str(socket.gethostname()) + "]" + "  ~" + str(
                    os.getcwd()) + " >$ "
                if self.highlighter:
                    del self.highlighter
                self.highlighter = name_highlighter(self.editor.document(), str(getpass.getuser()),
                                                    str(socket.gethostname()), str(os.getcwd()))

            except FileNotFoundError as E:
                self.editor.appendPlainText(str(E))

        elif len(command_list) == 1 and command_list[0] == "cd":
            os.chdir(str(Path.home()))
            self.editor.name = "[" + str(getpass.getuser()) + "@" + str(socket.gethostname()) + "]" + "  ~" + str(
                os.getcwd()) + " >$ "

        elif self.process.state() == 2:
            self.process.write(real_command.encode())
            self.process.closeWriteChannel()

        elif command == self.editor.name + real_command:
            self.run(real_command)

        else:
            pass  # When the user does a command like ls and then presses enter then it wont read the line where the cursor is on as a command