代码之家  ›  专栏  ›  技术社区  ›  Bryan Oakley

wxPython:如何创建bashshell窗口?

  •  7
  • Bryan Oakley  · 技术社区  · 15 年前

    我想使用wxPython创建一个类似bashshell的弹出窗口。我不需要终端仿真器,我不需要作业控制,我只需要一个基于bash进程的REPL(Read、Eval、Print循环)。

    用wxPython有没有简单的方法?我在做tcl/tk程序员的时候就知道这个基本概念,但是我的wxPython fu很弱,如果不需要的话,我不想重新发明轮子。我读了一些关于py.shell的书。Shell,但这看起来像是创建了一个pythonshell,我想让它运行bash命令。

    4 回复  |  直到 15 年前
        1
  •  6
  •   Anurag Uniyal    15 年前

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

    关于wx.py.shell的API,我刚刚实现了shell类为解释器调用的那个些方法,若你们浏览一下shell的源代码,你们就会明白。 大体上

    • getAutoCompleteKeys返回键 您可以为哪个用户使用 自动完成命令,例如tab键
    • 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()
    
        2
  •  1
  •   Bryan Oakley    14 年前

    我找到了解决我问题的办法。有趣的是,它以前从未出现在谷歌搜索中。这不是生产代码,但最终,我在寻找一种在wxPython窗口中运行bashshell的方法。

    http://sivachandran.blogspot.com/2008/04/termemulator-10-released.html on webarchive https://sourceforge.net/projects/termemulator/files/TermEmulator/1.0/

        3
  •  0
  •   Anurag Uniyal    15 年前

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

    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()
    
        4
  •  0
  •   sqram    15 年前

    去看看我能想出什么。

    但如果您改变主意,决定改用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()
    

    如果真的需要的话,这是可以解决的。