2009-06-12 13 views
7

Je veux créer une fenêtre popup en utilisant wxPython qui agit comme un shell bash. Je ne veux pas d'émulateur de terminal, je n'ai pas besoin de contrôle de travail, je veux juste un REPL (Read, Eval, Print Loop) basé sur un processus bash.wxPython: comment créer une fenêtre shell bash?

Existe-t-il un moyen facile de le faire avec wxPython? Je connais le concept de base de mes jours en tant que programmeur tcl/tk mais mon wxPython fu est faible et je ne veux pas avoir à réinventer la roue si je n'ai pas à le faire. J'ai lu un peu à propos de py.shell. Shell mais cela ressemble à créer un shell python et je veux que l'on exécute des commandes bash à la place.

+0

Qu'est-ce qu'un «REPL»? –

+1

Une boucle Read-Eval-Print –

Répondre

1

J'ai trouvé la solution à mon problème. C'est marrant comme ça ne s'est jamais présenté dans google recherches avant maintenant. Ce n'est pas un code prêt pour la production, mais finalement ce que je cherchais - un moyen d'exécuter un shell bash dans une fenêtre wxPython.

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

0

j'ai cherché, mais il ne semble pas y avoir de shell bash pour sortir wxPython si le module wx.py a le module Shell qui est pour python interpretor bonne chose est que vous pouvez passer votre propre interpretor à, donc je venez avec l'interpréteur bash très simple. exemple lit actuellement une seule ligne de stdout bash, sinon il sera coincé, dans votre code, vous devez lire la sortie en fil ou utiliser 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() 
+0

Pourquoi -1? s'il vous plaît au moins commentaire après avoir mis downvote, donc j'ai au moins la chance de m'améliorer, je ne vois pas de raison pour le vote à la baisse? –

+0

Je downvoted parce que ce code ne fonctionne pas. Étant donné qu'il y a une prime, la barre est un peu plus élevée que la normale ici. OK, le code s'exécute sans erreur mais ce n'est pas quelque chose qui peut être utilisé tel quel, pas quand il ne lit qu'une seule ligne de sortie après chaque commande. J'apprécie le pointeur vers le widget wx.py.Shell et la façon de créer un interpréteur spécial, mais ce n'est pas encore assez IMO pour gagner la prime. –

+3

Ce n'est pas censé être un service de location où vous attendez que je donne une solution complète, je pense que tout ce que j'ai donné était un bon point de départ et j'ai également raison de la façon de l'améliorer, par exemple. J'ai dit que j'utilise readline parce que lire toutes les données bloquera alors si vous n'aimez pas vous ne le faites pas et je n'obtiendrai pas la générosité mais en votant en bas vous me découragez de donner les bons conseils pour la solution finale –

0

Aller voir ce que je peux trouver.

Mais si vous changez d'avis et décidez d'utiliser pygtk à la place, la voici:

enjoy!!

EDIT

j'ai commencé à faire une version pauvre homme d'un terminal en utilisant le texte widget de contrôle. Je me suis arrêté parce qu'il y a des failles qui ne peuvent pas être réparées, comme lorsque vous utilisez la commande 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 vraiment voulu, cela pourrait être travaillé.

+0

pygtk n'est pas une option, c'est pour une application existante avec quelques milliers de lignes de wxPython. Personnellement, si j'avais le choix, j'utiliserais Tkinter. –

+0

Vous générer un processus pour chaque commande? Cela ne fonctionnera pas parce que les changements de répertoires ne persisteront pas d'une commande à l'autre. Vous ne gérez pas non plus les commandes multilignes. Merci quand même. –

+0

Aucun problème. J'essaierais de demander #wxwidgets sur freenode. – sqram

6

ok voici un autre essai, qui lit toutes les sorties et les erreurs aussi, dans un fil séparé et communique via la file d'attente. Je sais que ce n'est pas parfait (par exemple commande avec sortie retardée ne fonctionnera pas et la sortie va entrer dans commnd prochaine par exemple tryr sommeil 1; date) et répliquer bash entier non trivial mais pour quelques commandes j'ai testé il semble bien fonctionner

En ce qui concerne API de wx.py.shell je viens de mettre en œuvre la méthode que la classe Shell appelait Interpreter, si vous passez par le code source de Shell, vous comprendrez. essentiellement

  • Push est où l'utilisateur est entré dans la commande est envoyée à un interprète
  • getAutoCompleteKeys retourne les clés qui utilisateur peut l'utilisateur pour commandes complexion auto par exemple onglet clé
  • getAutoCompleteList liste de retour du texte donné correspondant de commande

  • getCallTip « spec argument d'affichage et docstring dans une fenêtre pop-up.donc pour bash nous montrer la page man :)

est ici le code source

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

Cet exemple est utile. Il y a encore quelques problèmes mais je pense que c'est suffisant pour me donner une idée de ce qui peut et ne peut pas être fait. Si une meilleure réponse n'apparaît pas dans les prochains jours, je l'accepterai. –

+0

Je sais que cette question est ancienne mais j'étais incapable de trouver quelque chose de mieux alors j'espère poser une question de suivi dans ce sujet. Je voudrais avoir deux ShellFrame verticalement dans une fenêtre! C'est possible? En utilisant le code ci-dessus, je ne peux ouvrir que deux shellFrame en même temps, mais ils ne sont pas encapsulés dans une seule fenêtre. – theAlse

Questions connexes