AW: [PythonCE] Minature IDLE for PythonCE

Anne Wangnick anne.wangnick at t-online.de
Wed Jan 5 20:31:39 CET 2005


Dear all,

I've managed to find a workaround for the Tkinter text widget selection
problems. I've filed the issue also to http://wiki.tcl.tk/9184 to get it
solved in the long run. You have to add something like the following code to
your applications using text widgets:

	b1motion = root.bind_class('Text','<B1-Motion>')
	root.bind_class('Text','<B1-Motion>','if {![info exists
::tk::Priv(ignoreB1Motion)]} {%s}'%b1motion)
	root.bind_class('Text','<B1-Leave>','set ::tk::Priv(ignoreB1Motion) 1')
	root.bind_class('Text','<B1-Enter>','array unset ::tk::Priv
ignoreB1Motion')

Ed, an updated version of your most recent distribution of IdleCE is
attached.

Regards,
Sebastian

-----Ursprungliche Nachricht-----
Von: pythonce-bounces at python.org [mailto:pythonce-bounces at python.org]Im
Auftrag von Ed Blake
Gesendet: Mittwoch, 5. Januar 2005 01:58
An: pythonce at python.org
Betreff: [PythonCE] Mini IDLE update and response.


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.

-----Ursprungliche Nachricht-----
Von: pythonce-bounces at python.org [mailto:pythonce-bounces at python.org]Im
Auftrag von Anne Wangnick
Gesendet: Mittwoch, 5. Januar 2005 19:12
An: Ed Blake
Cc: pythonce at python.org
Betreff: AW: [PythonCE] Minature IDLE for PythonCE


Hi Ed,

Nice work of yours!

Herewith some improvements (real menus on top, support for "foreign" Windows
CE versions like my German one, where the installation is in
C:\\Programme\\Python\\lib, support for VGA screen with HI_RES_AWARE python,
hide ugly additional Tkinter titlebar below PDA top taskbar).

Please give it a try on a real 240x320 PDA -- I can test it only in the
240x320 emulation mode of my VGA PDA.

Regards,
Sebastian Wangnick

-----Ursprungliche Nachricht-----
Von: pythonce-bounces at python.org [mailto:pythonce-bounces at python.org]Im
Auftrag von Ed Blake
Gesendet: Donnerstag, 30. Dezember 2004 23:09
An: pythonce at python.org
Betreff: [PythonCE] Minature IDLE for PythonCE



Something fun, and maybe usefull.  Since popup-menus work I will probably
move most of the edit menu there.
-------------- next part --------------
#IdleCE

import sys
for p in sys.path:
    if p[-12:].lower()=="python23.zip":
        sys.path.append(p+"\\lib-tk")
        break
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(1, weight=1)
        root.grid_columnconfigure(0, weight=1)
        
        frame = Frame(root)
        frame.grid(row=0,column=0,columnspan=2,sticky=NSEW)

        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)

        btn = Menubutton(frame,text="File",padx=1,pady=1)
        btn.pack(side=LEFT)
        submenu = Menu(btn,tearoff=False)
        btn["menu"] = submenu
        submenu.add_command(label="New",command=self.new)
        submenu.add_command(label="Open",command=self.open)
        submenu.add_command(label="Save",command=self.save)
        submenu.add_command(label="Save as",command=self.saveas)
        submenu.add_command(label="Exit",command=self.exit)
        btn = Menubutton(frame,text="Edit",padx=1,pady=1)
        btn.pack(side=LEFT)
        submenu = Menu(btn,tearoff=False)
        btn["menu"] = submenu
        submenu.add_command(label="Undo",command=self.editor.edit_undo)
        submenu.add_command(label="Redo",command=self.editor.edit_redo)
        submenu.add_command(label="Cut",command=self.editor.cut)
        submenu.add_command(label="Copy",command=self.editor.copy)
        submenu.add_command(label="Paste",command=self.editor.paste)       
        btn = Menubutton(frame,text="Help",padx=1,pady=1)
        btn.pack(side=LEFT)
        submenu = Menu(btn,tearoff=False)
        btn["menu"] = submenu
        submenu.add_command(label="About",command=self.about_dialog)
       
        self.root.protocol("WM_DELETE_WINDOW", self.exit)

    def about_dialog(self):
        """Sillyness"""
        top = Toplevel()
        top.title("about")
        top.resizable(0,0)
        top.focus_set()

        about = """

        IdleCE v0.6.2

    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,padx=0,pady=0)
        # 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=='ce':
        sizex, sizey = root.wm_maxsize()
	root.wm_geometry("%dx%d+0+%d"%(sizex-6,sizey*37/64+1,sizey/90))
	b1motion = root.bind_class('Text','<B1-Motion>')
        root.bind_class('Text','<B1-Motion>','if {![info exists ::tk::Priv(ignoreB1Motion)]} {%s}'%b1motion)
        root.bind_class('Text','<B1-Leave>','set ::tk::Priv(ignoreB1Motion) 1')
        root.bind_class('Text','<B1-Enter>','array unset ::tk::Priv ignoreB1Motion')
    app = idle(root)
    root.mainloop()



More information about the PythonCE mailing list