15

因此,已经有一个 Python 程序设置在我必须构建的控制台上运行。我将使用 Javascript 为应用程序构建一个 Web GUI 界面。我将如何:

一种。在不接触原始代码的情况下处理这个 Python 程序的输入/输出。

湾。通过 Javascript 调用向 Python 程序发送控制台输入。我研究了原始 HTTP 请求/AJAX,但我不确定我将如何将其作为输入发送到 Python 程序。

4

4 回答 4

6

一种。处理程序的输入/输出:Pexpect。它相当容易使用,阅读一些随它分发的示例应该可以教会你足够的知识来掌握基础知识。

湾。Javascript接口:

好吧,我使用 gevent,它是内置的 WSGI 服务器。(查看什么是WSGI 服务器另一个))。我应该注意,这个程序将保持一个状态,因此您可以通过将会话 ID 返回到您的 javascript 客户端并将您的 pexpect 会话存储在全局变量或其他容器中来管理打开的会话,以便您可以完成程序的输入和输出跨多个独立的 AJAX 请求。然而,我把它留给你,因为这并不简单。

我的示例将在单击您选择的某些内容后将 POST 请求放入其中。(它实际上不起作用,因为一些变量没有设置。设置它们。)

以下是相关部分:

<!-- JavaScript -->
<script src="jquery.js"></script>
<script type="text/javascript">
function toPython(usrdata){
    $.ajax({
        url: "http://yoursite.com:8080",
        type: "POST",
        data: { information : "You have a very nice website, sir." , userdata : usrdata },
        dataType: "json",
        success: function(data) {
            <!-- do something here -->
            $('#somediv').html(data);
        }});
$("#someButton").bind('click', toPython(something));
</script>

然后服务器:

# Python and Gevent
from gevent.pywsgi import WSGIServer
from gevent import monkey
monkey.patch_all() # makes many blocking calls asynchronous

def application(environ, start_response):
    if environ["REQUEST_METHOD"]!="POST": # your JS uses post, so if it isn't post, it isn't you
        start_response("403 Forbidden", [("Content-Type", "text/html; charset=utf-8")])
        return "403 Forbidden"
    start_response("200 OK", [("Content-Type", "text/html; charset=utf-8")])
    r = environ["wsgi.input"].read() # get the post data
    return r

address = "youraddresshere", 8080
server = WSGIServer(address, application)
server.backlog = 256
server.serve_forever()

如果您的程序是面向对象的,那么集成它会相当容易。编辑:不需要面向对象。我现在已经包含了一些 Pexpect 代码

global d
d = someClass()
def application(environ, start_response):
    # get the instruction
    password = somethingfromwsgi # read the tutorials on WSGI to get the post stuff
    # figure out WHAT to do
    global d
    success = d.doSomething()
    # or success = funccall()
    prog = pexpect.spawn('python someprogram.py')
    prog.expect("Password: ")
    prog.sendline(password)
    i = prog.expect(["OK","not OK", "error"])
    if i==0:
        start_response("200 OK", [("Content-Type", "text/html; charset=utf-8")])
        return "Success"
    elif i==1:
        start_response("500 Internal Server Error", [("Content-Type", "text/html; charset=utf-8")])
        return "Failure"
    elif i==2:
        start_response("500 Internal Server Error", [("Content-Type", "text/html; charset=utf-8")])
        return "Error"

我建议的另一个选择是 Nginx + uWSGI。如果你愿意,我也可以给你一些例子。它为您提供了将网络服务器合并到设置中的好处。

于 2012-07-30T19:42:50.223 回答
6

要将您的数据从 javascript 透明地传递到外部 Python 程序,您可以使用 WebSocket 协议连接您的服务器和 javascript,并使用 stdin/stdout 从服务器与外部程序进行通信。

这是一个示例 Python 程序client.py

#!/usr/bin/env python
"""Convert stdin to upper case."""
for line in iter(raw_input, 'quit'):
    print line.upper()

我已经使用来自hello world websocket 示例的代码创建了一个服务器,并回答了如何在每个传入连接上创建一个新进程并将所有输入数据重定向到进程的 stdin

#!/usr/bin/python
"""WebSocket CLI interface."""
import sys
from twisted.application import strports # pip install twisted
from twisted.application import service
from twisted.internet    import protocol
from twisted.python      import log
from twisted.web.server  import Site
from twisted.web.static  import File

from txws import WebSocketFactory # pip install txws


class Protocol(protocol.Protocol):
    def connectionMade(self):
        from twisted.internet import reactor
        log.msg("launch a new process on each new connection")
        self.pp = ProcessProtocol()
        self.pp.factory = self
        reactor.spawnProcess(self.pp, sys.executable,
                             [sys.executable, '-u', 'client.py'])
    def dataReceived(self, data):
        log.msg("redirect received data to process' stdin: %r" % data)
        self.pp.transport.write(data)
    def connectionLost(self, reason):
        self.pp.transport.loseConnection()

    def _send(self, data):
        self.transport.write(data) # send back


class ProcessProtocol(protocol.ProcessProtocol):
    def connectionMade(self):
        log.msg("connectionMade")
    def outReceived(self, data):
        log.msg("send stdout back %r" % data)
        self._sendback(data)
    def errReceived(self, data):
        log.msg("send stderr back %r" % data)
        self._sendback(data)
    def processExited(self, reason):
        log.msg("processExited")
    def processEnded(self, reason):
        log.msg("processEnded")

    def _sendback(self, data):
        self.factory._send(data)


application = service.Application("ws-cli")

_echofactory = protocol.Factory()
_echofactory.protocol = Protocol
strports.service("tcp:8076:interface=127.0.0.1",
                 WebSocketFactory(_echofactory)).setServiceParent(application)

resource = File('.') # serve current directory INCLUDING *.py files
strports.service("tcp:8080:interface=127.0.0.1",
                 Site(resource)).setServiceParent(application)

网络客户端部分sendkeys.html

<!doctype html>
<title>Send keys using websocket and echo the response</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js">
</script>
<script src="sendkeys.js"></script>
<input type=text id=entry value="type something">
<div id=output>Here you should see the typed text in UPPER case</div>

sendkeys.js

// send keys to websocket and echo the response
$(document).ready(function() {
    // create websocket
    if (! ("WebSocket" in window)) WebSocket = MozWebSocket; // firefox
    var socket = new WebSocket("ws://localhost:8076");

    // open the socket
    socket.onopen = function(event) {
    socket.send('connected\n');

    // show server response
    socket.onmessage = function(e) {
        $("#output").text(e.data);
    }

    // for each typed key send #entry's text to server
    $("#entry").keyup(function (e) {
        socket.send($("#entry").attr("value")+"\n");
    });
    }
});

尝试一下:

  • 下载这个要点
  • 安装twistedtxws

    $ pip install twisted txws
    
  • 跑:

    $ twistd -ny wscli.py
    
  • 访问http://localhost:8080/

  • 点击sendkeys.html并输入一些东西

于 2012-07-30T20:58:13.757 回答
1

您可能需要Flask以及json 模块

Django是另一种选择,但可能对您的需求来说太高级了。

于 2012-07-30T18:13:05.393 回答
1

这取决于您要包装的应用程序类型以及您的 GUI 选项如何转换为应用程序命令。但是你在这里有两个目标:

  1. 编写一个包装器以允许您读取程序的输出并为其提供输入。

  2. 制作一个网络服务器来接收 GUI 事件并将它们转换为命令以传递给您的“包装器”

我做了一些你需要做的事情。

  1. 本质上,您需要将套接字流转换为谨慎的命令。实际的工具是expect,以及它的任何包装器(我使用过pexpect,Python 包装器,并且对它有很好的体验)。

  2. 这部分可能并不简单。问题是你的底层程序一直在运行,所以你的网络服务器应该是有状态的,以了解跨请求的程序。另一种选择是让您的网络服务器简单地重新附加到进程并发出命令,并在标准输出流中遇到它时发回响应,但您最终可能会得到很长的响应时间,具体取决于程序是。此外,AJAX 请求是异步的,而您的底层程序是同步的,这也是不匹配的。所以是的,这可能会变得非常复杂。这真的取决于你的程序。如果您可以添加一些有关程序和 GUI 的详细信息,它会有所帮助。

于 2012-07-30T18:51:38.193 回答