2009-06-12 20 views
7

Quiero crear una ventana emergente usando wxPython que actúa como un shell bash. No quiero un emulador de terminal, no necesito control de trabajo, solo quiero un REPL (Leer, Evaluar, Imprimir Loop) basado en un proceso bash.wxPython: ¿cómo crear una ventana de shell bash?

¿Hay una manera fácil de hacer eso con wxPython? Conozco el concepto básico de mis días como programador tcl/tk pero mi wxPython fu es débil y no quiero tener que reinventar la rueda si no es necesario. He leído un poco sobre py.shell. Shell, pero parece que crea un shell python y quiero que uno ejecute comandos bash en su lugar.

+0

¿Qué es un "REPL"? –

+1

A Read-Eval-Print-Loop –

Respuesta

0

He buscado pero no parece haber ninguna cáscara del golpe de salir de wxPython embargo módulo wx.py tiene módulo de Shell, que es para interpretor pitón Lo bueno es que puede pasar su propio intérprete a la misma, por lo que tengo ven con un intérprete bash muy simple. ejemplo lee actualmente sólo una línea de salida estándar fiesta, de lo contrario, se queda bloqueado, en código real que debe leer la salida de hilo o utilizar seleccione

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

¿Por qué? por favor al menos comente después de votar, así que tengo al menos la oportunidad de mejorarme, ¿no veo ninguna razón para votar a la baja? –

+0

Bajé la votación porque no funciona el código. Dado que hay una recompensa, la barra está un poco más alta de lo normal aquí. OK, el código se ejecuta sin error pero no es algo que pueda usarse tal como está, no cuando solo lee una línea de salida después de cada comando. Agradezco el puntero al widget wx.py.Shell y cómo crear un intérprete especial, pero eso aún no es suficiente IMO para ganar la recompensa. –

+3

Se supone que esto no es un servicio de buscador de rentas en el que espera que yo dé una solución completa, creo que lo que di fue un buen punto de partida y también tengo motivos para mejorarlo, p. Dije que estoy usando readline porque leer todo dat a bloqueará así que si no te gusta no lo haces y no recibiré recompensas, pero al votar abajo me desalientas de dar las pistas correctas para la solución final –

0

ir a ver lo que puedo llegar a.

Pero si usted cambia de opinión y decide utilizar pygtk lugar, aquí está:

enjoy!!

EDITAR

empecé a hacer la versión de un hombre pobre de un terminal usando el texto widget de control Paré porque hay fallas que no se pueden arreglar, como cuando se usa el comando 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() 

Si realmente quisiera, podría trabajar en esto.

+0

pygtk no es una opción, esto es para una aplicación existente con unas pocas miles de líneas de wxPython. Personalmente, si tuviera una opción, usaría Tkinter. –

+0

¿Generas un proceso para cada comando? Eso no funcionará porque el cambio de directorios no persistirá de un comando al siguiente. Tampoco maneja comandos de varias líneas. Gracias de cualquier manera. –

+0

No hay problema. Intentaría preguntar en #wxwidgets en freenode. – sqram

6

bien aquí está otra prueba, que también lee todos los resultados y errores, en un hilo separado y se comunica a través de Queue. Sé que no es perfecto (por ejemplo, el comando con salida diferida no funcionará y la salida entrará en el siguiente servidor, por ejemplo tryr sleep 1; date) y replicar bash completo no trivial, pero para algunos comandos probé parece funcionar bien

En cuanto a la API de wx.py.shell, simplemente implementé el método que la clase Shell llamaba para el intérprete, si va a través del código fuente de Shell lo entenderá. básicamente

  • empuje es donde entra el comando de usuario se envía al intérprete
  • getAutoCompleteKeys devuelve claves la que el usuario puede de usuario para completar comandos de automóviles, por ejemplo, pestaña clave
  • getAutoCompleteList lista de devolución de juego orden dada texto

  • getCallTip "especificación argumento Pantalla y cadena de documentación en una ventana emergente.por lo que para fiesta podemos mostrar la página hombre :)

aquí está el código fuente

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

Este ejemplo es útil. Todavía tiene algunos problemas, pero creo que es suficiente para darme una idea de lo que se puede y no se puede hacer. Si una respuesta mejor no aparece en los próximos días, aceptaré esta. –

+0

Sé que esta pregunta es antigua pero no pude encontrar nada mejor, así que espero hacer una pregunta de seguimiento sobre este tema. ¡Me gustaría tener dos ShellFrame verticalmente en una ventana! ¿Es posible? Usando el código anterior, solo puedo abrir dos ShellFrame al mismo tiempo, pero no están encapsulados en una ventana. – theAlse

Cuestiones relacionadas