Example #1
0
    def __init__(
            self,
            port=30321,
            nogui=True,
            open="python-interface-help.pd",
            cmd=None,
            path=["patches"],
            extra=None,
            stderr=True,
            pdexe=None
    ):
        """
        port - what port to connect to [netreceive] on.
        nogui - boolean: whether to start Pd with or without a gui.
            Defaults to nogui=True
        open - string: full path to a .pd file to open on startup.
        cmd - message to send to Pd on startup.
        path - an array of paths to add to Pd startup path.
        extra - a string containing extra command line arguments to pass to Pd.
        """
        self.connectCallback = None

        args = [self._getPdExe(pdexe)]

        if stderr:
            args.append("-stderr")

        if nogui:
            args.append("-nogui")

        if open:
            if not os.path.isabs(open):
                open = os.path.join(os.path.dirname(__file__), "patches", open)
            args.append("-open")
            args.append(open)

        if cmd:
            args.append("-send")
            args.append(cmd)

        for p in path:
            args.append("-path")
            args.append(p)

        if extra:
            args += extra.split(" ")

        #print "COMMAND:", " ".join(args)
        try:
            self.pd = Popen(args, stdin=None, stderr=PIPE, stdout=PIPE,
                            close_fds=(sys.platform != "win32"))
        except OSError:
            raise PdException(
                "Problem running `{}` from '{}'".format(pdexe, os.getcwd()))

        if port:
            self.port = port

        self._map = {}
        self._pdSend = PdSend(map=self._map)
        self._pdReceive = PdReceive(self, map=self._map)
Example #2
0
class Pd:
    """
        Start Pure Data in a subprocess.

        >>> from time import time, sleep
        >>> from os import path, getcwd
        >>>
        >>> start = time()
        >>> # launching pd
        >>> pd = Pd(nogui=False)
        >>> pd.Send(["test message", 1, 2, 3])
        >>>
        >>> def Pd_hello(self, message):
        ...     print "Pd called Pd_hello(%s)" % message
        ...
        >>> pd.Pd_hello = Pd_hello
        >>>
        >>> sentexit = False
        >>> # running a bunch of stuff for up to 20 seconds
        >>> while time() - start < 20 and pd.Alive():
        ...     if time() - start > 0.5 and not sentexit:
        ...         pd.Send(["exit"])
        ...         sentexit = True
        ...     pd.Update()
        ...
        ...
        Pd called Pd_hello(['this', 'is', 'my', 'message', 'to', 'python'])
        untrapped message: ['this', 'is', 'another', 'message']
        untrapped stderr output: "connecting to port 30322"
        untrapped stderr output: "python-connected: 0"
        untrapped stderr output: "python-connected: 1"
        untrapped stderr output: "from-python: test message 1 2 3"
        untrapped stderr output: "closing audio..."
        untrapped stderr output: "pd_gui: pd process exited"
        untrapped stderr output: "closing MIDI..."...
        Pd died!
        >>> pd.Exit()
    """
    errorCallbacks = {}

    def _getPdExe(self, pdexe):
        if pdexe is None:
            if "PD_BIN" in os.environ:
                return os.environ["PD_BIN"]
            else:
                if sys.platform == "win32":
                    return os.path.join("pd", "bin", "pd.exe")
                elif sys.platform == "linux2":
                    return "pd"
                elif sys.platform == "darwin":
                    return os.path.join("", "Applications", "Pd.app",
                                        "Contents", "Resources", "bin", "pd")
                else:
                    raise PdException("Unknown Pd executable location"
                                      "on your platform ('%s')." % sys.platform)
        else:
            return pdexe

    def __init__(
            self,
            port=30321,
            nogui=True,
            open="python-interface-help.pd",
            cmd=None,
            path=["patches"],
            extra=None,
            stderr=True,
            pdexe=None
    ):
        """
        port - what port to connect to [netreceive] on.
        nogui - boolean: whether to start Pd with or without a gui.
            Defaults to nogui=True
        open - string: full path to a .pd file to open on startup.
        cmd - message to send to Pd on startup.
        path - an array of paths to add to Pd startup path.
        extra - a string containing extra command line arguments to pass to Pd.
        """
        self.connectCallback = None

        args = [self._getPdExe(pdexe)]

        if stderr:
            args.append("-stderr")

        if nogui:
            args.append("-nogui")

        if open:
            if not os.path.isabs(open):
                open = os.path.join(os.path.dirname(__file__), "patches", open)
            args.append("-open")
            args.append(open)

        if cmd:
            args.append("-send")
            args.append(cmd)

        for p in path:
            args.append("-path")
            args.append(p)

        if extra:
            args += extra.split(" ")

        #print "COMMAND:", " ".join(args)
        try:
            self.pd = Popen(args, stdin=None, stderr=PIPE, stdout=PIPE,
                            close_fds=(sys.platform != "win32"))
        except OSError:
            raise PdException(
                "Problem running `{}` from '{}'".format(pdexe, os.getcwd()))

        if port:
            self.port = port

        self._map = {}
        self._pdSend = PdSend(map=self._map)
        self._pdReceive = PdReceive(self, map=self._map)

    def Update(self):
        poll(map=self._map)
        stdin = self.pd.recv()
        stderr = self.pd.recv_err()
        if stdin:
            [self.CheckStart(t) for t in cr.split(stdin) if t]
        if stderr:
            [self.Error(t) for t in cr.split(stderr) if t]

    def Send(self, msg):
        """
        Send an array of data to Pd.
        It will arrive at the [python-interface] object as a space delimited list.

        p.Send(["my", "test", "yay"])
        """
        self._pdSend.Send(msg)

    def PdMessage(self, data):
        """
        Override this method to receive messages from Pd.
        """
        print("untrapped message:", data)

    def Connect(self, addr):
        self._pdSend.Connect((addr[0], self.port))

    def CheckStart(self, msg):
        if "_Start() called" in msg:
            self.PdStarted()

    def Error(self, error):
        """
        Override this to catch anything sent by Pd to stderr
        (e.g. [print] objects).
        """
        errors = error.split(" ")
        method = getattr(self, 'Error_' + errors[0], None)
        if method:
            method(errors)
        elif error in self.errorCallbacks:
            self.errorCallbacks[error]()
        else:
            print('untrapped stderr output: "' + error + '"')

    def Dead(self):
        self.pd = None
        self.PdDied()

    def PdStarted(self):
        """ Override this to catch the definitive start of Pd. """
        pass

    def PdDied(self):
        """
        Override this to catch the Pd subprocess exiting.
        """
        print("Pd died!")

    def Alive(self):
        """
        Check whether the Pd subprocess is still alive.
        """
        return bool(self.pd and self.pd.poll() != 0)

    def Exit(self):
        """
        Kill the Pd process right now.
        """
        if self.Alive():
            #self.close()
            if sys.platform == "win32":
                # Kill the process using pywin32
                import win32api
                win32api.TerminateProcess(int(self.pd._handle), -1)
            else:
                # kill the process using os.kill
                from os import kill
                kill(self.pd.pid, signal.SIGINT)
            self._pdReceive.close()
        if self.pd:
            self.pd.wait()