wxPython:如何创建一个bash shell窗口?

时间:2009-06-12 21:25:59

标签: wxpython

我想使用wxPython创建一个弹出窗口,其作用类似于bash shell。我不想要一个终端模拟器,我不需要作业控制,我只想要一个基于bash过程的REPL(读取,评估,打印循环)。

使用wxPython有一种简单的方法吗?我知道我作为一名tcl / tk程序员的基本概念,但我的wxPython fu很弱,如果我不需要,我不想重新发明轮子。我已经阅读了一些关于py.shell的内容。 Shell,但看起来它创建了一个python shell,我想要一个运行bash命令。

4 个答案:

答案 0 :(得分:6)

这里是另一个尝试,它在一个单独的线程中读取所有输出和错误,并通过Queue进行通信。 我知道这不是完美的(例如,延迟输出的命令将不起作用,并且输出将进入下一个提交,例如tryr sleep 1; date)并且复制整个bash不是微不足道的但是对于我测试的几个命令它似乎工作正常

关于wx.py.shell的API,我刚刚实现了Shell类为Interpreter调用的那些方法,如果你通过Shell的源代码,你就会理解。 基本上

  • push是用户输入的命令发送给解释器的地方
  • getAutoCompleteKeys返回键 哪个用户可以使用 自动完成命令例如标签键
  • getAutoCompleteList返回列表 命令匹配给定文本

  • getCallTip“显示参数规范和 弹出窗口中的docstring。因此对于 bash我们可能会显示手册页:)

这是源代码

import threading
import Queue
import time

import wx
import wx.py
from subprocess import Popen, PIPE

class BashProcessThread(threading.Thread):
    def __init__(self, readlineFunc):
        threading.Thread.__init__(self)

        self.readlineFunc = readlineFunc
        self.outputQueue = Queue.Queue()
        self.setDaemon(True)

    def run(self):
        while True:
            line = self.readlineFunc()
            self.outputQueue.put(line)

    def getOutput(self):
        """ called from other thread """
        lines = []
        while True:
            try:
                line = self.outputQueue.get_nowait()
                lines.append(line)
            except Queue.Empty:
                break
        return ''.join(lines)

class MyInterpretor(object):
    def __init__(self, locals, rawin, stdin, stdout, stderr):
        self.introText = "Welcome to stackoverflow bash shell"
        self.locals = locals
        self.revision = 1.0
        self.rawin = rawin
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr

        self.more = False

        # bash process
        self.bp = Popen('bash', shell=False, stdout=PIPE, stdin=PIPE, stderr=PIPE)

        # start output grab thread
        self.outputThread = BashProcessThread(self.bp.stdout.readline)
        self.outputThread.start()

        # start err grab thread
        self.errorThread = BashProcessThread(self.bp.stderr.readline)
        self.errorThread.start()

    def getAutoCompleteKeys(self):
        return [ord('\t')]

    def getAutoCompleteList(self, *args, **kwargs):
        return []

    def getCallTip(self, command):
        return ""

    def push(self, command):
        command = command.strip()
        if not command: return

        self.bp.stdin.write(command+"\n")
        # wait a bit
        time.sleep(.1)

        # print output
        self.stdout.write(self.outputThread.getOutput())

        # print error
        self.stderr.write(self.errorThread.getOutput())

app = wx.PySimpleApp()
frame = wx.py.shell.ShellFrame(InterpClass=MyInterpretor)
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()

答案 1 :(得分:1)

我找到了解决问题的方法。有趣的是它之前从未在谷歌搜索中出现过。它不是生产就绪代码,但最终我正在寻找 - 一种在wxPython窗口中运行bash shell的方法。

http://sivachandran.blogspot.com/2008/04/termemulator-10-released.html

答案 2 :(得分:0)

我搜索过但似乎没有任何针对wxPython的退出bash shell 虽然wx.py模块有Shell模块,用于python解释器 好的是你可以将自己的解释器传递给它,所以我带来了非常简单的bash解释器。  示例当前只从bash stdout读取一行,否则会卡住, 在实际代码中,您必须在线程中读取输出或使用选择

import wx
import wx.py
from subprocess import Popen, PIPE

class MyInterpretor(object):
    def __init__(self, locals, rawin, stdin, stdout, stderr):
        self.introText = "Welcome to stackoverflow bash shell"
        self.locals = locals
        self.revision = 1.0
        self.rawin = rawin
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr

        #
        self.more = False

        # bash process
        self.bp = Popen('bash', shell=False, stdout=PIPE, stdin=PIPE, stderr=PIPE)


    def getAutoCompleteKeys(self):
        return [ord('\t')]

    def getAutoCompleteList(self, *args, **kwargs):
        return []

    def getCallTip(self, command):
        return ""

    def push(self, command):
        command = command.strip()
        if not command: return

        self.bp.stdin.write(command+"\n")
        self.stdout.write(self.bp.stdout.readline())

app = wx.PySimpleApp()
frame = wx.py.shell.ShellFrame(InterpClass=MyInterpretor)
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()

答案 3 :(得分:0)

去看看我能想出什么。

但如果你改变主意并决定改用pygtk,那么它就是:

enjoy!!

修改

我开始使用文本控件小部件创建一个穷人的终端版本。 我停止了因为存在无法修复的缺陷,例如当你使用sudo命令时。

import wx
import subprocess

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MyFrame.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)

        self.prompt = "user@stackOvervlow:~ "
        self.textctrl = wx.TextCtrl(self, -1, '', style=wx.TE_PROCESS_ENTER|wx.TE_MULTILINE)
        self.default_txt = self.textctrl.GetDefaultStyle()
        self.textctrl.AppendText(self.prompt)

        self.__set_properties()
        self.__do_layout()
        self.__bind_events()


    def __bind_events(self):
        self.Bind(wx.EVT_TEXT_ENTER, self.__enter)


    def __enter(self, e):
        self.value = (self.textctrl.GetValue())
        self.eval_last_line()
        e.Skip()


    def __set_properties(self):
        self.SetTitle("Poor Man's Terminal")
        self.SetSize((800, 600))
        self.textctrl.SetFocus()

    def __do_layout(self):
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add(self.textctrl, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_1)
        self.Layout()

    def eval_last_line(self):
        nl = self.textctrl.GetNumberOfLines()
        ln =  self.textctrl.GetLineText(nl-1)
        ln = ln[len(self.prompt):]
        args = ln.split(" ")

        proc = subprocess.Popen(args, stdout=subprocess.PIPE)
        retvalue = proc.communicate()[0]

        c = wx.Colour(239, 177, 177)
        tc = wx.TextAttr(c)
        self.textctrl.SetDefaultStyle(tc)
        self.textctrl.AppendText(retvalue)
        self.textctrl.SetDefaultStyle(self.default_txt)
        self.textctrl.AppendText(self.prompt)
        self.textctrl.SetInsertionPoint(GetLastPosition() - 1)

if __name__ == "__main__":
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame_1 = MyFrame(None, -1, "")
    app.SetTopWindow(frame_1)
    frame_1.Show()
    app.MainLoop()

如果真的想要,可以解决这个问题。