[Idle-dev] IDLE extension
martin
mc.liebmann@t-online.de
Sat, 2 Mar 2002 20:06:43 +0100
This is a multi-part message in MIME format.
------=_NextPart_000_0005_01C1C225.C63009A0
Content-Type: multipart/alternative;
boundary="----=_NextPart_001_0006_01C1C225.C63009A0"
------=_NextPart_001_0006_01C1C225.C63009A0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Hi,
the attached files implement a (quick and dirty but useful) extension to =
IDLE 0.8 (Python 2.2).
Similar to "CallTips" this extension shows member functions and =
attributes after hitting a "."
The toplevel help window supports scrolling (up- and down-Keys). The =
'space'-key is used to
insert the topmost item into the IDLE text window.
Martin
------=_NextPart_001_0006_01C1C225.C63009A0
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2600.0" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Hi,</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>the attached files implement a (quick =
and dirty but=20
useful) extension to IDLE 0.8 (Python 2.2).</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>Similar to "CallTips" this extension =
shows member=20
functions and attributes after hitting a "."</FONT></DIV>
<DIV><FONT face=3DArial size=3D2>The toplevel help window supports =
scrolling (up-=20
and down-Keys). The 'space'-key is used to</FONT></DIV>
<DIV><FONT face=3DArial size=3D2>insert the topmost item into the =
IDLE text=20
window.</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>Martin</FONT></DIV></BODY></HTML>
------=_NextPart_001_0006_01C1C225.C63009A0--
------=_NextPart_000_0005_01C1C225.C63009A0
Content-Type: text/plain;
name="CallTips.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="CallTips.py"
# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a =
floating window that
# displays parameter information as you open parens.
import string
import sys
import types
import inspect
class CallTips:
menudefs =3D [
]
keydefs =3D {
'<<paren-open>>': ['<Key-parenleft>'],
'<<paren-close>>': ['<Key-parenright>'],
'<<calltip-cancel>>': ['<ButtonPress>', '<Key-Escape>'], =20
'<<dot-check>>': ['<Key-.>'],
'<<check-key>>': ['<Key>'],
'<<calltip-down>>': ['<Key-Down>'],
'<<calltip-up>>': ['<Key-Up>'],
'<<check-calltip-cancel>>': ['<KeyPress-BackSpace>'],
}
windows_keydefs =3D {
}
unix_keydefs =3D {
}
def __init__(self, editwin):
self.editwin =3D editwin
self.text =3D editwin.text
self.calltip =3D None
=20
self.line =3D 0
self.member =3D None
self.members =3D []
self.sub_members =3D []
self.word =3D ""
self.wordchars =3D string.replace(string.printable, '.', '')
self.pattern =3D ""
=20
if hasattr(self.text, "make_calltip_window"):
self._make_calltip_window =3D self.text.make_calltip_window
else:
self._make_calltip_window =3D self._make_tk_calltip_window
def close(self):
self._make_calltip_window =3D None
# Makes a Tk based calltip window. Used by IDLE, but not Pythonwin.
# See __init__ above for how this is used.
def _make_tk_calltip_window(self):
import CallTipWindow
return CallTipWindow.CallTip(self.text)
def _remove_calltip_window(self):
if self.calltip:
self.calltip.hidetip()
self.calltip =3D None
def paren_open_event(self, event):
self.member =3D None
self._remove_calltip_window()
arg_text =3D get_arg_text(self.get_object_at_cursor())
if arg_text:
self.calltip_start =3D self.text.index("insert+1c")
self.calltip =3D self._make_calltip_window()
list =3D []
list.append(arg_text)
self.calltip.showtip(list)
return "" #so the event is handled normally.
def paren_close_event(self, event):
# Now just hides, but later we should check if other
# paren'd expressions remain open.
self._remove_calltip_window()
return "" #so the event is handled normally.
def _dir_main(self,obj):
"Execute dir(obj) within namespace __main__"
from __main__ import *
return dir(obj)
=20
def dot_check_event(self, event):
self._remove_calltip_window()
obj =3D self.get_object_at_cursor()
=20
if obj: =20
=20
names =3D self._dir_main(obj)
names.sort()
self.member =3D 'true'
self.members =3D []
self.word =3D ""
self.line =3D 0
for name in names:
if (string.find(name,"__",0) =3D=3D 0):
pass
else:
subobj =3D getattr(obj, name) =20
if inspect.isclass(subobj):
self.members.append(name + "(...)")
elif inspect.isfunction(subobj): =
=20
self.members.append(name + "(...)")
elif inspect.ismethod(subobj):
self.members.append(name + "(...)")
elif inspect.ismodule(subobj):
pass
elif inspect.isroutine(subobj):
self.members.append(name + "(...)")
else:
self.members.append(name)
=20
if self.members:
self.calltip_start =3D self.text.index("insert+1c")
self.calltip =3D self._make_calltip_window()
self.sub_members =3D self.members
self.calltip.showtip(self.members,1)
return "" #so the event is handled normally. =20
=20
def check_key_event(self, event):
if not self.member:
return ""
=20
if self.calltip:
if event.keysym =3D=3D 'space':
self._remove_calltip_window()
=20
if self.sub_members:
first =3D len(self.word)
last =3D string.rfind(self.sub_members[self.line], =
"(")
if last < 0:
last =3D len(self.sub_members[self.line])
self.text.insert("insert", =
self.sub_members[self.line][first:last])
return "break"
=20
elif (string.find(self.wordchars,event.keysym) > -1):
text =3D self.text
chars =3D text.get("insert linestart", "insert")
i =3D len(chars)
while i and chars[i-1] in self.wordchars:
i =3D i-1
self.word =3D chars[i:] + event.keysym
if self.word:
self.sub_members =3D []
self.line =3D 0
for member in self.members:
if string.find(member, self.word) =3D=3D 0:
self.sub_members.append(member)
if len(self.sub_members) =3D=3D 0:
self._remove_calltip_window()
else:
self._remove_calltip_window()
# self.calltip_start =3D =
self.text.index("insert")
self.calltip =3D self._make_calltip_window()
=
self.calltip.showtip(self.sub_members,1-len(self.word))
else:
pass
return "" #so the event is handled normally.
def calltip_down_event(self, event):
if self.calltip:
if self.line < (len(self.sub_members) - 1):
self.line +=3D 1
self._remove_calltip_window()
self.calltip =3D self._make_calltip_window()
=
self.calltip.showtip(self.sub_members,-len(self.word),self.line) =
=20
return "break" #skip further event handling.
else:
return "" #so the event is handled normally.
def calltip_up_event(self, event):
if self.calltip:
if self.line > 0:
self.line -=3D 1
self._remove_calltip_window()
self.calltip =3D self._make_calltip_window()
=
self.calltip.showtip(self.sub_members,-len(self.word),self.line) =20
return "break" #skip further event handling.
else:
return "" #so the event is handled normally.
=20
def check_calltip_cancel_event(self, event):
if self.calltip:
# If we have moved before the start of the calltip,
# or off the calltip line, then cancel the tip.
# (Later need to be smarter about multi-line, etc)
if self.text.compare("insert", "<=3D", self.calltip_start) =
or \
self.text.compare("insert", ">", self.calltip_start + " =
lineend"):
self._remove_calltip_window()
=20
return "" #so the event is handled normally.
def calltip_cancel_event(self, event):
self._remove_calltip_window()
return "" #so the event is handled normally.
def get_object_at_cursor(self,
wordchars=3D"._" + string.uppercase + =
string.lowercase + string.digits):
# XXX - This needs to be moved to a better place
# so the "." attribute lookup code can also use it.
text =3D self.text
chars =3D text.get("insert linestart", "insert")
i =3D len(chars)
while i and chars[i-1] in wordchars:
i =3D i-1
word =3D chars[i:]
if word:
# How is this for a hack!
import sys, __main__
namespace =3D sys.modules.copy()
namespace.update(__main__.__dict__)
try:
return eval(word, namespace)
except:
pass
return None # Can't find an object.
def _find_constructor(class_ob):
# Given a class object, return a function object used for the
# constructor (ie, __init__() ) or None if we can't find one.
try:
return class_ob.__init__.im_func
except AttributeError:
for base in class_ob.__bases__:
rc =3D _find_constructor(base)
if rc is not None: return rc
return None
def get_arg_text(ob):
# Get a string describing the arguments for the given object.
argText =3D ""
if ob is not None:
argOffset =3D 0
if type(ob)=3D=3Dtypes.ClassType:
# Look for the highest __init__ in the class chain.
fob =3D _find_constructor(ob)
if fob is None:
fob =3D lambda: None
else:
argOffset =3D 1
elif type(ob)=3D=3Dtypes.MethodType:
# bit of a hack for methods - turn it into a function
# but we drop the "self" param.
fob =3D ob.im_func
argOffset =3D 1
else:
fob =3D ob
# Try and build one for Python defined functions
if type(fob) in [types.FunctionType, types.LambdaType]:
try:
realArgs =3D =
fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
defaults =3D fob.func_defaults or []
defaults =3D list(map(lambda name: "=3D%s" % name, =
defaults))
defaults =3D [""] * (len(realArgs)-len(defaults)) + =
defaults
items =3D map(lambda arg, dflt: arg+dflt, realArgs, =
defaults)
if fob.func_code.co_flags & 0x4:
items.append("...")
if fob.func_code.co_flags & 0x8:
items.append("***")
argText =3D string.join(items , ", ")
argText =3D "(%s)" % argText
except:
pass
# See if we can use the docstring
doc =3D getattr(ob, "__doc__", "")
if doc:
while doc[:1] in " \t\n":
doc =3D doc[1:]
pos =3D doc.find("\n")
if pos < 0 or pos > 70:
pos =3D 70
if argText:
argText +=3D "\n"
argText +=3D doc[:pos]
return argText
#################################################
#
# Test code
#
if __name__=3D=3D'__main__':
def t1(): "()"
def t2(a, b=3DNone): "(a, b=3DNone)"
def t3(a, *args): "(a, ...)"
def t4(*args): "(...)"
def t5(a, *args): "(a, ...)"
def t6(a, b=3DNone, *args, **kw): "(a, b=3DNone, ..., ***)"
class TC:
"(a=3DNone, ...)"
def __init__(self, a=3DNone, *b): "(a=3DNone, ...)"
def t1(self): "()"
def t2(self, a, b=3DNone): "(a, b=3DNone)"
def t3(self, a, *args): "(a, ...)"
def t4(self, *args): "(...)"
def t5(self, a, *args): "(a, ...)"
def t6(self, a, b=3DNone, *args, **kw): "(a, b=3DNone, ..., =
***)"
def test( tests ):
failed=3D[]
for t in tests:
expected =3D t.__doc__ + "\n" + t.__doc__
if get_arg_text(t) !=3D expected:
failed.append(t)
print "%s - expected %s, but got %s" % (t, `expected`, =
`get_arg_text(t)`)
print "%d of %d tests failed" % (len(failed), len(tests))
tc =3D TC()
tests =3D t1, t2, t3, t4, t5, t6, \
TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6
test(tests)
------=_NextPart_000_0005_01C1C225.C63009A0
Content-Type: text/plain;
name="CallTipWindow.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="CallTipWindow.py"
# A CallTip window class for Tkinter/IDLE.
# After ToolTip.py, which uses ideas gleaned from PySol
# Used by the CallTips IDLE extension.
import os
import string
from Tkinter import *
class CallTip:
def __init__(self, widget):
self.widget = widget
self.tipwindow = None
self.id = None
self.x = self.y = 0
def showtip(self, texts, xoffset=0, line=0, maxcount=10):
self.texts = texts # List of text strings
if self.tipwindow or not self.texts:
return
# Setup visible region
if maxcount < 0:
maxcount = 1
first = 0
last = len(self.texts)
if line > first:
first = line
if first > last:
first = last
if (last - first + 1) > maxcount:
last = first + maxcount
# Extract visible region
self.text = string.join(self.texts[first:last], "\n")
self.widget.see("insert")
# To avoid incorrect values for cx take "insert - 1 chars"
x, y, cx, cy = self.widget.bbox("insert - 1 chars")
# Estimate current cursor position
x = x + cx
# Top left coordinates for the new toplevel widget
x = x + cx*xoffset + self.widget.winfo_rootx() - 1
y = y + cy + self.widget.winfo_rooty()
self.tipwindow = tw = Toplevel(self.widget)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d" % (x, y))
label = Label(tw, text=self.text, justify=LEFT,
background="#ffffe0", relief=SOLID, borderwidth=1,
font = self.widget['font'])
label.pack()
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw:
tw.destroy()
###############################
#
# Test Code
#
class container: # Conceptually an editor_window
def __init__(self):
root = Tk()
text = self.text = Text(root)
text.pack(side=LEFT, fill=BOTH, expand=1)
text.insert("insert", "string.split")
root.update()
self.calltip = CallTip(text)
text.event_add("<<calltip-show>>", "(")
text.event_add("<<calltip-hide>>", ")")
text.bind("<<calltip-show>>", self.calltip_show)
text.bind("<<calltip-hide>>", self.calltip_hide)
text.focus_set()
# root.mainloop() # not in idle
def calltip_show(self, event):
self.calltip.showtip(["Hello world"])
def calltip_hide(self, event):
self.calltip.hidetip()
def main():
# Test code
c=container()
if __name__=='__main__':
main()
------=_NextPart_000_0005_01C1C225.C63009A0--