[PythonCE] Mini IDLE update and response.

Michael Foord mike at pcblokes.com
Wed Jan 5 09:29:49 CET 2005


Hello Ed,

is great - it's the first useful python app. for windows CE that I've 
seen - awesome. MANY MANY thanks for your work.

It's been such a pain coding with pocket notepad !
I've noticed the problem with selection as well - I guess it's a problem 
with the WinCE port of Tk. The best you can do is fix the keybindings to 
the cursor keys, so that selection can be done with the shift-arrow 
keys. Maybe even implement ctrl-something-or-other to select a block at 
a time !

Anyway - I have a bit of a wish list for IdleCE. *Mainly* related to key 
bindings, but not entirely. If you could bear these in mind, I would be 
personally very grateful.

In order of importance :

max 1 edit and file menu open at a time
key bindings to ctrl-c, ctr-x, ctrl-a, ctrl-v, ctrl-s (copy, cut, select 
all, paste, save)
Also shift-<arrow keys> for selection

tab for indenting selected area
ctrl-[ and ctrl-] for indenting/unindenting selected area

tab is four spaces rather than tab
(and/or a spaces to tabs/tabs to spaces option)

comment in/comment out areas - with key bindings


As I said though... I already use IdleCE and am grateful for your work. 
Do you want this put online somewhere ? Contact me off-list if you do.

Regards,

Fuzzyman
http://www.voidspace.org.uk/python/index.shtml



Ed Blake wrote:

>I've managed to get popup menus that work.  Highlight some text, tap the
>selection and a menu will appear.  It is equivalent to the edit menu/window
>but seems to work better for cut/copy.  I have a problem with this though on
>my own device (HP Ipaq, WinCE 2003).  When I try to highlight text the
>selection flickers and changes, like the selection is being made over and
>over.  If anyone can figure out what causes this I would be interested in
>hearing about it.
>
>RE the license:  I think the example code I based most of this app on was
>released GPL so unless I can track down the example again and verify its
>license...  I suppose if I verify that the example was other than GPL I
>should use the python license as that is the license used on IDLE.
>
>------------------------------------------------------------------------
>
>#IdleCE
>
>import sys
>sys.path.append('\\Program Files\\Python\\lib\\Python23.zip\\lib-tk')
>import sys
>import os
>import re
>from Tkinter import *
>import tkMessageBox
>import tkFileDialog
>
>import keyword
>from string import ascii_letters, digits, punctuation
>
>OS = os.name.lower() #Should be 'ce' on WinCE
>
>class idle:
>    """The base class for the mini idle."""
>    def __init__(self,root):
>        self.root = root
>        root.grid_rowconfigure(0, weight=1)
>        root.grid_columnconfigure(0, weight=1)
>        
>        frame = Frame(root)
>        frame.grid(row=0,column=0,sticky=N+W)
>        
>        btn = Button(frame,text="file",command=self.file_dialog)
>        btn.pack(side=LEFT)
>        
>        btn = Button(frame,text="edit",command=self.edit_dialog)
>        btn.pack(side=LEFT)
>        
>        btn = Button(frame,text="about",command=self.about_dialog)
>        btn.pack(side=LEFT)
>        
>        self.editor = SyntaxHighlightingText(root)
>        self.editor.grid(row=1,column=0,columnspan=2,sticky=N+S)
>        
>        panbar = Scrollbar(root,orient=HORIZONTAL)
>        panbar.grid(row=2,column=0,columnspan=2,sticky=E+W)
>        
>        scrollbar = Scrollbar(root)
>        scrollbar.grid(row=1,column=2,sticky=N+S)
>        
>        self.editor.configure(xscrollcommand=panbar.set)
>        self.editor.configure(yscrollcommand=scrollbar.set)
>        
>        scrollbar.config(command=self.editor.yview)
>        panbar.config(command=self.editor.xview)
>
>        self.root.protocol("WM_DELETE_WINDOW", self.exit)
>
>    def file_dialog(self):
>        """Used instead of a menu since menus don't work on WinCE."""
>        top = Toplevel(padx=20)
>        top.title("file")
>        top.resizable(0,0)
>        top.focus_set()
>
>        btn_task = Button(top, text="New",command=self.new)
>        btn_task.pack(fill=X)
>
>        btn_task = Button(top, text="Open",command=self.open)
>        btn_task.pack(fill=X)
>
>        btn_task = Button(top, text="Save",command=self.save)
>        btn_task.pack(fill=X)
>        
>        btn_task = Button(top, text="Save As",command=self.saveas)
>        btn_task.pack(fill=X)
>
>        btn = Button(top, text="Exit", command=self.exit)
>        btn.pack(fill=X)
>
>    def edit_dialog(self):
>        """These buttons all work on the desktop, but don't seem to work right
>on WinCE.  Luckly copy and paste are accessable via mouse clicks."""
>        top = Toplevel(padx=20)
>        top.title("edit")
>        top.resizable(0,0)
>        top.focus_set()
>
>        btn_undo = Button(top, text="Undo",command=self.editor.edit_undo)
>        btn_undo.pack(fill=X)
>
>        btn_redo = Button(top, text="Redo",command=self.editor.edit_redo)
>        btn_redo.pack(fill=X)
>
>        btn_task = Button(top, text="Cut",command=self.editor.cut)
>        btn_task.pack(fill=X)
>
>        btn_task = Button(top, text="Copy",command=self.editor.copy)
>        btn_task.pack(fill=X)
>
>        btn_task = Button(top, text="Paste",command=self.editor.paste)
>        btn_task.pack(fill=X)
>
>        btn = Button(top, text="Dismiss", command=top.destroy)
>        btn.pack(fill=X)
>
>    def about_dialog(self):
>        """Sillyness"""
>        top = Toplevel()
>        top.title("about")
>        top.resizable(0,0)
>        top.focus_set()
>
>        about = """
>
>        IdleCE v0.6
>
>    A miniaturized imitation of
>    the python ide: idle.
>
>    This software is distibuted
>    under the Gnu-GPL. Please Visit
>    http://www.gnu.org/licenses/gpl.txt
>    to see the license.
>        """
>        info = Label(top,text=about)
>        info.pack(side=TOP,padx=6)
>
>        button = Button(top, text="Dismiss", command=top.destroy)
>        button.pack(side=BOTTOM,fill=X)
>
>    def open(self):
>        # Opens a file and colorizes it
>        self.filename = tkFileDialog.askopenfilename()
>        if OS == 'ce': # Just passing filename fails...
>            self.filename = self.filename.replace('/','\\')
>        try:
>            file = open(self.filename)
>            self.editor.delete("1.0",END)
>            text = file.readlines()
>            file.close()
>            for i in range(len(text)-1):
>                self.editor.insert(END,text[i])
>                self.editor.colorize(i+1,len(text[i])) #colorize(textline,lastcolumn)
>            self.editor.see("1.0")
>        except IOError:
>            print self.filename
>
>    def saveas(self):
>        # Called if no filename is set or Saveas is picked
>        self.filename = tkFileDialog.asksaveasfilename()
>        if OS == 'ce':
>            self.filename = self.filename.replace('/','\\')
>        try:
>            file = open(self.filename,'w')
>            text = self.editor.get("1.0",END)
>            file.write(text)
>            file.flush()
>            file.close()
>        except Exception, info:
>            tkMessageBox.showerror('Exception!',info)
>
>    def save(self):
>        try:
>            file = open(self.filename,'w')
>            text = self.editor.get("1.0",END)
>            file.write(text)
>            file.flush()
>            file.close()
>        except:
>            self.saveas() # If no file has been accessed
>
>    def new(self):
>        if len(self.editor.get("1.0",END)) >= 2:
>            if tkMessageBox.askokcancel("New File","Discard current file?"):
>                self.editor.delete("1.0",END)
>                self.filename = ""
>        else:
>            self.editor.delete("1.0",END)
>            self.filename = ""
>
>    def exit(self):
>        # End the program firmly.
>        root.destroy()
>        root.quit()
>            
>class SyntaxHighlightingText(Text):
>    """A syntax highlighting text widget from the web customized with
>some methods from Idle and some special methods."""
>    tags = {'com':'#C00',   #comment
>            'str':'#0A0',   #string
>            'kw': 'orange', #keyword
>            'obj':'#00F',   #function/class name
>            'int': 'blue'   #integers
>            }
>
>    def __init__(self, root):
>        if OS == 'ce':
>            w = 40
>            h = 10
>        else:
>            w = 80
>            h = 25
>        Text.__init__(self,root,wrap=NONE,bd=0,width=w,height=h,undo=1,maxundo=50)
>        # Non-wrapping, no border, undo turned on, max undo 50
>        self.text = self # For the methods taken from IDLE
>        self.root = root
>        self.config_tags()
>        self.characters = ascii_letters + digits + punctuation
>        self.tabwidth = 8    # for IDLE use, must remain 8 until Tk is fixed
>        self.indentwidth = 4 # Should perhaps be 2 due to the small screen??
>        self.indention = 0   # The current indention level
>        self.set_tabwidth(self.indentwidth) # IDLE...
>
>        # create a popup menu
>        self.menu = Menu(root, tearoff=0)
>        self.menu.add_command(label="Undo", command=self.edit_undo)
>        self.menu.add_command(label="Redo", command=self.edit_redo)
>        #self.menu.add_command(type="separator")
>        self.menu.add_command(label="Cut", command=self.cut)
>        self.menu.add_command(label="Copy", command=self.copy)
>        self.menu.add_command(label="Paste", command=self.paste)
>
>        self.bind('<Key>', self.key_press)      # For scanning input
>        self.bind('<Return>',self.autoindent)   # Overides default binding
>        self.bind('<Tab>',self.autoindent)      # increments self.indention
>        self.bind('<BackSpace>',self.autoindent)# decrements self.indention
>        self.bind('<Double-Button-1>',self.paste)# decrements self.indention
>        self.tag_bind(SEL,'<Button-1>',self.popup)
>
>    def popup(self, event):
>        """Edit popup menu with a special attribute for cut and copy."""
>        Selection=self.get_selection_indices()
>        if len(Selection)>0:
>            SelectedText = self.get(Selection[0],Selection[1])
>        self.sel_store = list(Selection)
>        self.sel_store.extend([SelectedText])
>        self.menu.post(event.x_root, event.y_root)
>
>    def get_tabwidth(self):
>        # From IDLE
>        current = self['tabs'] or 5000
>        return int(current)
>    
>    def set_tabwidth(self, newtabwidth):
>        # From IDLE
>        text = self
>        if self.get_tabwidth() != newtabwidth:
>            pixels = text.tk.call("font", "measure", text["font"],
>                                  "-displayof", text.master,
>                                  "n" * newtabwidth)
>            text.configure(tabs=pixels)
>        
>    def config_tags(self):
>        # Sets up the tags and their colors
>        for tag, val in self.tags.items():
>            self.tag_config(tag, foreground=val)
>
>    def remove_tags(self, start, end):
>        for tag in self.tags.keys():
>            self.tag_remove(tag, start, end)
>
>    def get_selection_indices(self):
>         # If a selection is defined in the text widget, return (start,
>        # end) as Tkinter text indices, otherwise return (None, None)
>        try:
>            first = self.text.index("sel.first")
>            last = self.text.index("sel.last")
>            return first, last
>        except TclError:
>            return None, None
>
>    def cut(self,event=0):
>        self.clipboard_clear()
>        if self.sel_store: # Sent by the popup
>            self.delete(self.sel_store[0],self.sel_store[1])
>            self.clipboard_append(self.sel_store[2])
>            self.sel_store = []
>        else: # Sent by menu
>            Selection=self.get_selection_indices()
>            if len(Selection)>0:
>                SelectedText = self.get(Selection[0],Selection[1])
>            self.delete(Selection[0],Selection[1])
>            self.clipboard_append(SelectedText)
>
>    def copy(self,event=0):
>        self.clipboard_clear()
>        if self.sel_store: # Sent by the popup
>            self.clipboard_append(self.sel_store[2])
>            self.sel_store = []
>        else: # Sent by menu
>            Selection=self.get_selection_indices()
>            if len(Selection)>0:
>                SelectedText = self.get(Selection[0],Selection[1])
>            self.clipboard_append(SelectedText)
>
>    def paste(self,event=0):
>        # This should call colorize for the pasted lines.
>        SelectedText = self.root.selection_get(selection='CLIPBOARD')
>        self.insert(INSERT, SelectedText)
>        return "break"
>
>    def autoindent(self,event):
>        if event.keysym == 'Return':
>            self.edit_separator() # For undo/redo
>            index = self.index(INSERT).split('.')
>            print index
>            line = int(index[0])
>            column = int(index[1])
>            if self.get('%s.%d'%(line, column-1)) == ':':
>                self.indention += 1
>            print '\n',
>            print '\t'*self.indention
>            self.insert(INSERT,'\n')
>            self.insert(INSERT,'\t'*self.indention)
>            return 'break' # Overides standard bindings
>        elif event.keysym == 'Tab':
>            self.edit_separator()
>            self.indention += 1
>            print self.indention
>        elif event.keysym == 'BackSpace':
>            self.edit_separator()
>            index = self.index(INSERT).split('.')
>            print index
>            line = int(index[0])
>            column = int(index[1])
>            if self.get('%s.%d'%(line, column-1)) == '\t':
>                self.indention -= 1
>            
>    def key_press(self, key):
>        """This function was origonaly the home of the colorize code.
>Now it is mostly useless unless you want to watch for a certain character."""
>        if key.char in ' :[(]),"\'':
>            self.edit_separator() # For undo/redo
>
>        cline = self.index(INSERT).split('.')[0]
>        lastcol = 0
>        char = self.get('%s.%d'%(cline, lastcol))
>        while char != '\n':
>            lastcol += 1
>            char = self.get('%s.%d'%(cline, lastcol))
>
>        self.colorize(cline,lastcol)
>
>    def colorize(self,cline,lastcol):
>        """Not so simple syntax highlighting."""
>        buffer = self.get('%s.%d'%(cline,0),'%s.%d'%(cline,lastcol))
>        tokenized = buffer.split(' ')
>
>        self.remove_tags('%s.%d'%(cline, 0), '%s.%d'%(cline, lastcol))
>
>        quotes = 0
>        start = 0
>        for i in range(len(buffer)):
>            if buffer[i] in ['"',"'"]: # Doesn't distinguish between single and double quotes...
>                if quotes:
>                   self.tag_add('str', '%s.%d'%(cline, start), '%s.%d'%(cline, i+1))
>                   quotes = 0
>                else:
>                    start = i
>                    quotes = 1
>            elif buffer[i] == '#':
>                self.tag_add('com', '%s.%d'%(cline, i), '%s.%d'%(cline, len(buffer)))
>                break
>
>        start, end = 0, 0
>        obj_flag = 0
>        for token in tokenized:
>            end = start + len(token)
>            if obj_flag:
>                self.tag_add('obj', '%s.%d'%(cline, start), '%s.%d'%(cline, end))
>                obj_flag = 0
>            if token.strip() in keyword.kwlist:
>                self.tag_add('kw', '%s.%d'%(cline, start), '%s.%d'%(cline, end))
>                if token.strip() in ['def','class']:
>                    obj_flag = 1
>            else:
>                for index in range(len(token)):
>                    try:
>                        int(token[index])
>                    except ValueError:
>                        pass
>                    else:
>                        self.tag_add('int', '%s.%d'%(cline, start+index))
>
>            start += len(token)+1
>
>if __name__ == '__main__':
>    root = Tk()
>    root.title('IdleCE')
>    if OS is 'ce':
>	root.maxsize(240,320)
>    app = idle(root)
>    root.mainloop()
>
>  
>
>------------------------------------------------------------------------
>
>_______________________________________________
>PythonCE mailing list
>PythonCE at python.org
>http://mail.python.org/mailman/listinfo/pythonce
>  
>



More information about the PythonCE mailing list