【问题标题】:wxPython: how to create a bash shell window?wxPython:如何创建一个 bash shell 窗口?
【发布时间】:2010-11-02 14:42:46
【问题描述】:

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

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

【问题讨论】:

    标签: wxpython


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

      • push 是将用户输入的命令发送到解释器的位置
      • getAutoCompleteKeys 返回键 哪个用户可以使用 自动完成命令,例如制表键
      • getAutoCompleteList 返回列表 命令匹配给定文本

      • getCallTip "显示参数规范和 弹出窗口中的文档字符串。因此对于 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()
      

      【讨论】:

      • 这个例子很有用。它仍然存在一些问题,但我认为这足以让我了解可以做什么和不可以做什么。如果在接下来的几天内没有出现更好的答案,我会接受这个。
      • 我知道这个问题很老,但我找不到更好的问题,所以我希望在这个主题上提出一个后续问题。我想在一个窗口中垂直放置两个 ShellFrame!可能吗?使用上面的代码我只能同时打开两个 shellFrame 但它们没有被封装在一个窗口中。
      【解决方案3】:

      去看看我能想出什么。

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

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

      【讨论】:

      • pygtk 不是一个选项,这适用于具有几千行 wxPython 的现有应用程序。就个人而言,如果我有选择的话,我会使用 Tkinter。
      • 您为每个命令生成一个进程?这是行不通的,因为更改目录不会从一个命令持续到下一个命令。您也不处理多行命令。还是谢谢。
      • 没问题。我会尝试在 freenode 上的#wxwidgets 中询问。
      【解决方案4】:

      我搜索过,但似乎没有任何用于 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()
      

      【讨论】:

      • 为什么是 -1 ?请在投反对票后至少发表评论,这样我至少有机会提高自己,我认为没有理由投反对票?
      • 我投了反对票,因为这不是工作代码。鉴于有赏金,这里的标准设置得比正常水平高一点。好的,代码运行没有错误,但它不能按原样使用,而不是在每个命令后只读取一行输出时。我很欣赏指向 wx.py.Shell 小部件的指针以及如何创建一个特殊的解释器,但这仍然不足以让 IMO 获得赏金。
      • 这不应该是一个rentacoder服务,你希望我提供完整的解决方案,我认为我给出的任何内容都是一个很好的起点,我也给出了如何改进它的理由,例如我说我正在使用 readline 是因为读取所有数据会阻塞,所以如果你不喜欢你不喜欢它,我不会得到赏金,但是通过投票,你会阻止我为最终解决方案提供正确的提示
      • 如果您想要一个完整的解决方案,请对rentacoder/elancer等出价,人们将提供完整的解决方案并承担责任
      • Anurag,很抱歉你认为我不公平。老实说,我认为你没有完全回答我的问题。我不需要一个完整的、生产就绪的解决方案,但我确实想要一些比你提供的更能给我提示的东西。显示更多关于 wx.py.Shell API 的解释,或显示一个解决方案,该解决方案没有仅读取单行输出的关键缺陷。我认为您低估了读取输出并以可用的方式将其发布回 shell 小部件的难度。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-06
      • 2020-01-26
      • 1970-01-01
      相关资源
      最近更新 更多