Hi to all!

Malcom has right, if you want to  use the shell inside another
application, simply create this with:
gui = Shell_Gui(0)
you will have a frame instead of a window

I'm using Py_Shell inside a bigger app and this is the code I use for
create the shell and put into vpaned widget:

self.main_shell=Py_Shell.Shell_Gui(with_window=0)
glade_file.get_widget("vpaned1").add2(self.main_shell.gui)

Also two things to notice:
if you start the shell with with_window=0, you don't have the exit
button in the button's bar, and the quit method of the Shell_Gui class,
simply hide the frame (if you use with_window=1 this method call a
gtk.main_qiut())


I attached a new version of Py_Shell that fix a couple of problem:
- avoid duplicated entry into the autocompletetion list
- the width of the autocompletation popup is calculated with respect to
the length of the strings
This version requires pygtk 2.0 due to some function (pango.PIXELS) 
not present in previous release (at least 1.99.16)

I'm sorry to post this into the list instead of giving you a link to a
page. I hope to have a web-page for my project (which includes Py_Shell)
in a week or two (I've register it on SF, so the only things that
remains is write the pages.. ). I will post the link ASAP

Best regards!
Pier

  
Il mar, 2003-11-04 alle 02:52, Malcolm Tredinnick ha scritto:
> On Tue, 2003-11-04 at 12:26, Yang Zheng wrote:
> > Instead of a popup, I would like to embed your console into a window
> > with other existing features (constructed with Glade).  Do you have any
> > tips on how to do that?  I am looking into Glade's custom widget, am I
> > going in the right direction?
> 
> I have not tested what I am about to say -- I just had a quick read over
> Pier's code...
> 
> It may not require all the trickiness of using a custom widget. The
> Shell itself is just a frame widget (it you set with_window = 0). If you
> construct the widget as s = Shell_Gui(0), s.gui will be the frame widget
> that you can put into your widget hierarchy at the appropriate point.
> 
> So inside your larger application you will have some kind of
> GtkContainer for holding the embedded shell. Just add the new frame
> widget (s.gui) to this container and it should work. There are probably
> a couple of details missing here, but I think the theory is correct. In
> any case, look at the frame created in the Shell_Gui class and utilise
> that directly.
> 
> Cheers,
> Malcolm
> _______________________________________________
> pygtk mailing list   [EMAIL PROTECTED]
> http://www.daa.com.au/mailman/listinfo/pygtk
> Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
-- 
Pier Carteri <[EMAIL PROTECTED]>
#
#       Py_Shell.py : inserts the python prompt in a gtk interface
#

import sys, code, os
import __builtin__


import gtk, gobject, pango

PS1=">>> "
PS2="... "
TAB_WIDTH=4

BANNER="Python "+sys.version+"\n"




class Completer:
  """
  Taken from rlcompleter, with readline references stripped, and a local dictionary to use.
  """
  def __init__(self,locals):
    self.locals = locals

  def complete(self, text, state):
    """Return the next possible completion for 'text'.
    This is called successively with state == 0, 1, 2, ... until it
    returns None.  The completion should begin with 'text'.

    """
    if state == 0:
      if "." in text:
        self.matches = self.attr_matches(text)
      else:
        self.matches = self.global_matches(text)
    try:
      return self.matches[state]
    except IndexError:
      return None

  def global_matches(self, text):
    """Compute matches when text is a simple name.

    Return a list of all keywords, built-in functions and names
    currently defines in __main__ that match.

    """
    import keyword
    matches = []
    n = len(text)
    for list in [keyword.kwlist, __builtin__.__dict__.keys(), self.locals.keys()]:
      for word in list:
        if word[:n] == text and word != "__builtins__":
          matches.append(word)
    return matches

  def attr_matches(self, text):
    """Compute matches when text contains a dot.

    Assuming the text is of the form NAME.NAME....[NAME], and is
    evaluatable in the globals of __main__, it will be evaluated
    and its attributes (as revealed by dir()) are used as possible
    completions.  (For class instances, class members are are also
    considered.)

    WARNING: this can still invoke arbitrary C code, if an object
    with a __getattr__ hook is evaluated.

    """
    import re
    m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
    if not m:
      return
    expr, attr = m.group(1, 3)
    object = eval(expr, self.locals, self.locals)
    words = dir(object)
    if hasattr(object,'__class__'):
      words.append('__class__')
      words = words + get_class_members(object.__class__)
    matches = []
    n = len(attr)
    for word in words:
      if word[:n] == attr and word != "__builtins__":
        matches.append("%s.%s" % (expr, word))
    return matches

def get_class_members(klass):
  ret = dir(klass)
  if hasattr(klass,'__bases__'):
     for base in klass.__bases__:
       ret = ret + get_class_members(base)
  return ret







class Dummy_File:

    def __init__(self, buffer, tag):
        """Implements a file-like object for redirect the stream to the buffer"""
        
        self.buffer = buffer
        self.tag = tag

    def write(self, text):
        """Write text into the buffer and apply self.tag"""
        iter=self.buffer.get_end_iter()
        self.buffer.insert_with_tags(iter,text,self.tag)

    def writelines(self, l):
        map(self.write, l)

    def flush(self):
        pass

    def isatty(self):
        return 1



class PopUp:

    def __init__(self, text_view, list, position):
        self.text_view=text_view
        
        #avoid duplicate items in list
        tmp={}
        n_chars=0
        for item in list:
            dim=len(item)
            if dim>n_chars:
                n_chars=dim
            tmp[item]=None 
        list=tmp.keys()
        list.sort()
        
        self.list=list
        self.position=position
        self.popup=gtk.Window(gtk.WINDOW_POPUP)
        frame=gtk.Frame()
        sw=gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        model=gtk.ListStore(gobject.TYPE_STRING)
        for item in self.list:
            iter=model.append()
            model.set(iter, 0, item)
        self.list_view=gtk.TreeView(model)
        self.list_view.connect("row-activated", self.hide)
        self.list_view.set_property("headers-visible", gtk.FALSE)
        selection=self.list_view.get_selection()
        selection.connect("changed",self.select_row)
        selection.select_path((0,))
        renderer=gtk.CellRendererText()
        column=gtk.TreeViewColumn("",renderer,text=0)
        self.list_view.append_column(column)
        sw.add(self.list_view)
        frame.add(sw)
        self.popup.add(frame)
        
        #set the width of the popup according with the length of the strings
        contest=self.popup.get_pango_context()
        desc=contest.get_font_description()
        lang=contest.get_language()
        metrics= contest.get_metrics(desc, lang)
        width= pango.PIXELS(metrics.get_approximate_char_width()* n_chars)
        if width>80:
            self.popup.set_size_request(width,90)
        else:
            self.popup.set_size_request(80,90)
        self.show_popup()

 
    def hide(self, *arg):
        self.popup.hide()
         
    def show_popup(self):
        buffer=self.text_view.get_buffer()
        iter=buffer.get_iter_at_mark(buffer.get_insert())
        
        rectangle=self.text_view.get_iter_location(iter)
        absX, absY=self.text_view.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT, 
                                   rectangle.x+rectangle.width+20 ,
                                   rectangle.y+rectangle.height+50)
        parent=self.text_view.get_parent()
        self.popup.move(self.position[0]+absX, self.position[1]+absY)
        self.popup.show_all()

             

    def prev(self):
        sel=self.list_view.get_selection()
        model, iter=sel.get_selected()
        newIter=model.get_path(iter)
        if newIter!=None and newIter[0]>0:
            path=(newIter[0]-1,)
            self.list_view.set_cursor(path)
            

    def next(self):
        sel=self.list_view.get_selection()
        model, iter=sel.get_selected()
        newIter=model.iter_next(iter)
        if newIter!=None:
            path=model.get_path(newIter)
            self.list_view.set_cursor(path)


    def sel_confirmed(self):
        sel=self.list_view.get_selection()
        self.select_row(sel)
        self.hide()

                                                                                                                                                
    def select_row(self, selection):
        model, iter= selection.get_selected()
        name=model.get_value(iter,0)
        buffer=self.text_view.get_buffer()
        end=buffer.get_iter_at_mark(buffer.get_insert())
        start=end.copy()
        start.backward_char()
        while start.get_char() not in " ,()[]":
            start.backward_char()
        start.forward_char()
        buffer.delete(start,end)
        iter=buffer.get_iter_at_mark(buffer.get_insert())
        buffer.insert(iter,name)


class Shell_Gui:

    def __init__(self,with_window=1,banner=BANNER, label_text="Interactive Python Shell"):
        
        
        self.banner=banner
        box=gtk.HBox()
        box.set_homogeneous(gtk.FALSE)
        box.set_border_width(4)
        box.set_spacing(4)
        sw=gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
        t_table=gtk.TextTagTable()
        # creates three tags
        tag_err=gtk.TextTag("error")
        tag_err.set_property("foreground","red")
        tag_err.set_property("font","monospace 10")
        t_table.add(tag_err)
        
        tag_out=gtk.TextTag("output")
        tag_out.set_property("foreground","blue")
        tag_out.set_property("font","monospace 10")
        t_table.add(tag_out)
        
        tag_in=gtk.TextTag("input")
        tag_in.set_property("foreground","black")
        tag_in.set_property("font","monospace 10")
        t_table.add(tag_in)

        tag_no_edit=gtk.TextTag("no_edit")
        tag_no_edit.set_property("editable",gtk.FALSE)
        t_table.add(tag_no_edit)
        
        self.buffer=gtk.TextBuffer(t_table)
        #add the banner
        self.buffer.set_text(self.banner+PS1)
        start,end=self.buffer.get_bounds()
        self.buffer.apply_tag_by_name("output",start,end)
        self.buffer.apply_tag_by_name("no_edit",start,end)

        self.view=gtk.TextView()
        self.view.set_buffer(self.buffer)
        self.view.connect("key_press_event", self.key_press)
        self.view.connect("drag_data_received",self.drag_data_received)
        self.view.set_wrap_mode(gtk.WRAP_CHAR)
        sw.add(self.view)
        box.pack_start(sw)

        #creates  two dummy files
        self.dummy_out=Dummy_File(self.buffer,tag_out)
        self.dummy_err=Dummy_File(self.buffer,tag_err)
        
        #creates the console
        self.core=code.InteractiveConsole()

        #autocompletation capabilities
        self.completer = Completer(self.core.locals)
        self.popup=None
        
        #creates history capabilities
        self.history=[" "]
        self.history_pos=0

        #add buttons
        b_box=gtk.Toolbar()
        b_box.set_orientation(gtk.ORIENTATION_VERTICAL)
        b_box.set_style(gtk.TOOLBAR_ICONS)
        b_box.insert_stock(gtk.STOCK_CLEAR,"Clear the output", None, self.clear_text, None,-1)
        b_box.insert_stock(gtk.STOCK_SAVE,"Save the output", None, self.save_text, None,-1)
        b_box.insert_stock(gtk.STOCK_PREFERENCES,"Preferences", None, None, None,-1)
        if with_window:
            b_box.append_space()
            b_box.insert_stock(gtk.STOCK_QUIT,"Close Shell", None, self.quit, None,-1)
        
        
        box.pack_start(b_box,expand=gtk.FALSE)
        frame=gtk.Frame(label_text)
        frame.show_all()
        frame.add(box)
        
        
        if with_window:
            self.gui=gtk.Window()
            self.gui.add(frame)
            self.gui.connect("delete-event",self.quit)
            self.gui.set_default_size(520,200)
            self.gui.show_all()
        else:
            self.gui=frame
        




    def key_press(self, view, event):
        if self.popup!=None:
            
            if event.keyval ==gtk.gdk.keyval_from_name("Up"):
                self.popup.prev()
                return gtk.TRUE 
            elif event.keyval ==gtk.gdk.keyval_from_name("Down"):
                self.popup.next()
                return gtk.TRUE 
            elif event.keyval ==gtk.gdk.keyval_from_name("Return"):
                self.popup.sel_confirmed()
                self.popup=None
                return gtk.TRUE 
            else:
                self.popup.hide()
                self.popup=None
        else:
            if event.keyval ==gtk.gdk.keyval_from_name("Up"):
                
                if self.history_pos>0:
                    # remove text into the line...
                    end=self.buffer.get_end_iter()
                    start=self.buffer.get_iter_at_line(end.get_line())
                    start.forward_chars(4)
                    self.buffer.delete(start,end)
                    #inset the new text
                    pos=self.buffer.get_end_iter()
                    self.buffer.insert(pos, self.history[self.history_pos])
                    self.history_pos-=1
                else:
                    gtk.gdk.beep()
                self.view.emit_stop_by_name("key-press-event")
                return gtk.TRUE
                
            elif event.keyval ==gtk.gdk.keyval_from_name("Down"):
    
                if self.history_pos<len(self.history)-1:
                    # remove text into the line...
                    end=self.buffer.get_end_iter()
                    start=self.buffer.get_iter_at_line(end.get_line())
                    start.forward_chars(4)
                    self.buffer.delete(start,end)
                    #inset the new text
                    pos=self.buffer.get_end_iter()
                    self.history_pos+=1
                    self.buffer.insert(pos, self.history[self.history_pos])
                    
                else:
                    gtk.gdk.beep()
                self.view.emit_stop_by_name("key-press-event")
                return gtk.TRUE
            
            elif event.keyval ==gtk.gdk.keyval_from_name("Tab"):
                iter=self.buffer.get_iter_at_mark(self.buffer.get_insert())
                self.buffer.insert(iter,TAB_WIDTH*" ")
                return gtk.TRUE
            
            elif event.keyval ==gtk.gdk.keyval_from_name("Return"):
                command=self.get_line()
                self.exec_code(command)
                start,end=self.buffer.get_bounds()
                self.buffer.apply_tag_by_name("no_edit",start,end)
                self.buffer.place_cursor(end)
                return gtk.TRUE
                
            elif event.keyval ==gtk.gdk.keyval_from_name("space") and event.state & gtk.gdk.CONTROL_MASK:
                self.complete_text()
                return gtk.TRUE
            
    

    def clear_text(self,*widget):
        dlg=gtk.Dialog("Clear")
        dlg.add_button("Clear",1)
        dlg.add_button("Reset",2)
        dlg.add_button(gtk.STOCK_CLOSE,gtk.RESPONSE_CLOSE)
        dlg.set_default_size(250,150)
        hbox=gtk.HBox()
        #add an image
        img=gtk.Image()
        img.set_from_stock(gtk.STOCK_CLEAR, gtk.ICON_SIZE_DIALOG)
        hbox.pack_start(img)
        
        #add text
        text="You have two options:\n"
        text+="   -clear only the output window\n"
        text+="   -reset the shell\n"
        text+="\n What do you want to do?"
        label=gtk.Label(text)
        hbox.pack_start(label)
        
        hbox.show_all()
        dlg.vbox.pack_start(hbox)
        
        ans=dlg.run()
        dlg.hide()
        if ans==1:
            self.buffer.set_text(self.banner+PS1)
            start,end=self.buffer.get_bounds()
            self.buffer.apply_tag_by_name("output",start,end)
            self.buffer.apply_tag_by_name("no_edit",start,end)
        elif ans==2:
            self.buffer.set_text(self.banner+PS1)
            start,end=self.buffer.get_bounds()
            self.buffer.apply_tag_by_name("output",start,end)
            self.buffer.apply_tag_by_name("no_edit",start,end)
            #creates the console
            self.core=code.InteractiveConsole()
            #reset history
            self.history=[" "]
            self.history_pos=0
        self.view.grab_focus()


    def save_text(self, *widget):
        dlg=gtk.Dialog("Save to file")
        dlg.add_button("Commands",1)
        dlg.add_button("All",2)
        dlg.add_button(gtk.STOCK_CLOSE,gtk.RESPONSE_CLOSE)
        dlg.set_default_size(250,150)
        hbox=gtk.HBox()
        #add an image
        img=gtk.Image()
        img.set_from_stock(gtk.STOCK_SAVE, gtk.ICON_SIZE_DIALOG)
        hbox.pack_start(img)
        
        #add text
        text="You have two options:\n"
        text+="   -save only commands\n"
        text+="   -save all\n"
        text+="\n What do you want to save?"
        label=gtk.Label(text)
        hbox.pack_start(label)
        
        hbox.show_all()
        dlg.vbox.pack_start(hbox)
        
        ans=dlg.run()
        dlg.hide()
        if ans==1 :
            def ok_save(button, data=None):
                win =button.get_toplevel()
                win.hide()
                name=win.get_filename()
                if os.path.isfile(name):
                    box=gtk.MessageDialog(dlg,
                                      gtk.DIALOG_DESTROY_WITH_PARENT,
                                      gtk.MESSAGE_QUESTION,gtk.BUTTONS_YES_NO,
                                    name+" already exists; do you want to overwrite it?"
                                    )
                    ans=box.run()
                    box.hide()
                    if ans==gtk.RESPONSE_NO:
                        return
                try:
                    file=open(name,'w')
                    for i in self.history:
                        file.write(i)
                        file.write("\n")
                    file.close()
                    
                        
                except Exception, x:
                    box=gtk.MessageDialog(dlg,
                                      gtk.DIALOG_DESTROY_WITH_PARENT,
                                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,
                                    "Unable to write \n"+
                                    name+"\n on disk \n\n%s"%(x)
                                    )
                    box.run()
                    box.hide()
                    
            def cancel_button(button):
                win.get_toplevel()
                win.hide()
                
            win=gtk.FileSelection("Save Commands...")
            win.ok_button.connect_object("clicked", ok_save,win.ok_button)
            win.cancel_button.connect_object("clicked", cancel_button,win.cancel_button)
            win.show()
        elif ans==2:
            def ok_save(button, data=None):
                win =button.get_toplevel()
                win.hide()
                name=win.get_filename()
                if os.path.isfile(name):
                    box=gtk.MessageDialog(dlg,
                                      gtk.DIALOG_DESTROY_WITH_PARENT,
                                      gtk.MESSAGE_QUESTION,gtk.BUTTONS_YES_NO,
                                    name+" already exists; do you want to overwrite it?"
                                    )
                    ans=box.run()
                    box.hide()
                    if ans==gtk.RESPONSE_NO:
                        return
                try:
                    start,end=self.buffer.get_bounds()
                    text=self.buffer.get_text(start,end,0)
                    file=open(name,'w')
                    file.write(text)
                    file.close()
                    
                except Exception, x:
                    box=gtk.MessageDialog(dlg,
                                      gtk.DIALOG_DESTROY_WITH_PARENT,
                                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,
                                    "Unable to write \n"+
                                    name+"\n on disk \n\n%s"%(x)
                                    )
                    box.run()
                    box.hide()
                    
            def cancel_button(button):
                win.get_toplevel()
                win.hide()
                
            win=gtk.FileSelection("Save Log...")
            win.ok_button.connect_object("clicked", ok_save,win.ok_button)
            win.cancel_button.connect_object("clicked", cancel_button,win.cancel_button)
            win.show()
        dlg.destroy()
        self.view.grab_focus()
        

          
    def get_line(self):
        iter=self.buffer.get_iter_at_mark(self.buffer.get_insert())
        line=iter.get_line()
        start=self.buffer.get_iter_at_line(line)
        end=start.copy()
        end.forward_line()
        command=self.buffer.get_text(start,end,0)
        if  (command[:4]==PS1 or command[:4]==PS2):
            command=command[4:]
        return command
        
        
    def complete_text(self):
        end=self.buffer.get_iter_at_mark(self.buffer.get_insert())
        start=end.copy()
        start.backward_char()
        while start.get_char() not in " ,()[]=":
            start.backward_char()
        start.forward_char()
        token=self.buffer.get_text(start,end,0).strip() 
        completions = []
        try:
            p=self.completer.complete(token,len(completions))
            while p != None:
              completions.append(p)
              p=self.completer.complete(token, len(completions))
        except:
            return 
        if len(completions)==1:
            self.buffer.delete(start,end)
            iter=self.buffer.get_iter_at_mark(self.buffer.get_insert())
            self.buffer.insert(iter,completions[0])
        elif len(completions)>1:
            #show a popup 
            if isinstance(self.gui, gtk.Frame):
                rect=self.gui.get_allocation()
                app=self.gui.window.get_root_origin()
                position=(app[0]+rect.x,app[1]+rect.y)
            else:    
                position=self.gui.window.get_root_origin()

            self.popup=PopUp(self.view, completions, position) 

         
    def replace_line(self, text):
        iter=self.buffer.get_iter_at_mark(self.buffer.get_insert())
        line=iter.get_line()
        start=self.buffer.get_iter_at_line(line)
        start.forward_chars(4)
        end=start.copy()
        end.forward_line()
        self.buffer.delete(start,end)
        iter=self.buffer.get_iter_at_mark(self.buffer.get_insert())
        self.buffer.insert(iter, text)
        
        
                          
    def sdt2files(self):
        """switch stdin stdout stderr to my dummy files"""
        self.std_out_saved=sys.stdout
        self.std_err_saved=sys.stderr
        
        sys.stdout=self.dummy_out
        sys.stderr=self.dummy_err
        
        
        
        
        

    def files2sdt(self):
        """switch my dummy files to stdin stdout stderr  """
        sys.stdout=self.std_out_saved
        sys.stderr=self.std_err_saved
        



    def drag_data_received(self, source, drag_context, n1, n2, selection_data, long1, long2):
        print selection_data.data
        
        
    def exec_code(self, text):
        """Execute text into the console and display the output into TextView"""
        
        #update history
        self.history.append(text)
        self.history_pos=len(self.history)-1
        
        self.sdt2files()
        sys.stdout.write("\n")
        action=self.core.push(text)
        if action==0:
            sys.stdout.write(PS1)
        elif action==1:
            sys.stdout.write(PS2)
        self.files2sdt()
        self.view.scroll_mark_onscreen(self.buffer.get_insert())



    def quit(self,*args):
        if __name__=='__main__':
            gtk.main_quit()
        else:
            if self.popup!=None:
                self.popup.hide()
            self.gui.hide()








if __name__=='__main__':
    shell=Shell_Gui(with_window=1)
    gtk.mainloop()
_______________________________________________
pygtk mailing list   [EMAIL PROTECTED]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/

Reply via email to