wxPython: как создать окно оболочки bash?

Я хочу создать всплывающее окно, используя wxPython, который действует как shell bash. Я не хочу эмулятора терминала, мне не нужен контроль над заданиями, я просто хочу REPL (Read, Eval, Print Loop) на основе процесса bash.

Есть ли простой способ сделать это с помощью wxPython? Я знаю основную концепцию с моих дней как программист tcl / tk, но мой wxPython fu слаб, и я не хочу, чтобы изобретать колесо, если мне это не нужно. Я немного читал о py.shell. Shell, но похоже, что он создает оболочку python, и я хочу, чтобы вместо этого выполнялись команды bash.

ok здесь другая попытка, которая также считывает все выходные данные и ошибки, в отдельном streamе и передает через Queue. Я знаю, что это не идеально (например, команда с задержкой вывода не будет работать, и выход будет попадать в следующую команду, например, tryr sleep 1; date) и реплицировать весь bash не тривиально, но для нескольких команд, которые я тестировал, кажется, работает нормально

Что касается API wx.py.shell, я только что реализовал тот метод, который class оболочки вызывал для Interpreter, если вы пойдете через исходный код Shell, который вы поймете. в основном

  • push – это когда пользователь вводит команду, отправляемую интерпретатору
  • getAutoCompleteKeys возвращает ключи, которые пользователь может использовать для автоматического завершения команд, например, клавиша табуляции
  • getAutoCompleteList возвращает список команд, соответствующих заданному тексту

  • getCallTip “Показать аргумент spec и docstring во всплывающем окне, поэтому для bash мы можем отобразить man-страницу 🙂

вот исходный код

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() 

Я нашел решение для своей проблемы. Забавно, как он никогда не появлялся в поисковых запросах Google до сих пор. Это не готовый к производству код, но в конечном итоге это то, что я искал – способ запустить оболочку bash в окне wxPython.

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

я искал, но, похоже, не существует какой-либо выходящей оболочки bash для wxPython, хотя модуль wx.py имеет модуль Shell, который для интерпретатора python хорош, вы можете передать свой собственный интерпретатор, поэтому я пришел с очень простым интерпретатором bash , пример в настоящее время читает только одну строку из bash stdout, иначе она застрянет, в реальном коде вы должны прочитать вывод в streamе или выбрать 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() 

Иду посмотреть, что я могу придумать.

Но если вы передумаете и решите использовать pygtk вместо этого, вот оно:

наслаждаться!!

РЕДАКТИРОВАТЬ

Я начал использовать версию терминала для бедного человека, используя виджет управления текстом. Я остановился, потому что есть недостатки, которые невозможно исправить, например, когда вы используете команду 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 = "[email protected]:~ " 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() 

Если это действительно нужно, это можно сработать.

Давайте будем гением компьютера.