[Idle-dev] IDLE Line numbering initial plan
Saimadhav Heblikar
saimadhavheblikar at gmail.com
Tue Jun 17 15:01:49 CEST 2014
Hi,
I have integrated line numbering into IDLE. All the code related to
it, are in idlelib/LineNumber.py for the moment, for easier debugging.
I'd like to say, that there is a memory leak. The leak is, events like
insert, scroll etc cause the memory to increase continuously. AFA I
can tell, the number of objects after every re-render is constant.
Objects are first deleted before new ones are created. I have tried
reusing existing objects and moving them to the required screen
coordinate, instead of deleting and creating anew. This did not work.
I can almost certainly say that it is related MultiCallCreator's
MultiCall class and not with the way LineNumberCanvas rerender's
itself. In the past two days, I have been trying to understand the
MultiCall module and how values related to <<Changed>> virtual event
have to be added to it.
I have not made a breakthrough yet.
The current changes to MultiCall.py goes some way to reduce the
sluggishness. This make me feel more strongly that some optmization in
MultiCall, will fix the memory leak.
I have also added a file text-without-multicall.py to show the above.
In this, using tkinter.Text, the memory usage does not change.
I have attached the patch in this email.
--
Regards
Saimadhav Heblikar
-------------- next part --------------
diff -r 601a08fcb507 Lib/idlelib/EditorWindow.py
--- a/Lib/idlelib/EditorWindow.py Sat Jun 14 18:51:34 2014 -0700
+++ b/Lib/idlelib/EditorWindow.py Tue Jun 17 18:28:18 2014 +0530
@@ -13,6 +13,7 @@
import webbrowser
from idlelib.MultiCall import MultiCallCreator
+from idlelib.LineNumber import LineNumberCanvas, Text
from idlelib import idlever
from idlelib import WindowList
from idlelib import SearchDialog
@@ -227,6 +228,10 @@
vbar['command'] = text.yview
vbar.pack(side=RIGHT, fill=Y)
text['yscrollcommand'] = vbar.set
+ if self.__class__.__name__ != 'PyShell':
+ self.linenumber_canvas = LineNumberCanvas(text_frame)
+ self.linenumber_canvas.connect(text=self.text, side=LEFT, fill=Y)
+
fontWeight = 'normal'
if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):
fontWeight='bold'
diff -r 601a08fcb507 Lib/idlelib/LineNumber.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/idlelib/LineNumber.py Tue Jun 17 18:28:18 2014 +0530
@@ -0,0 +1,65 @@
+import tkinter as tk
+
+class LineNumberCanvas(tk.Canvas):
+ def __init__(self,*args, **kwargs):
+ tk.Canvas.__init__(self, *args, **kwargs)
+
+ def connect(self, text, *arg, **kwargs):
+ self.text = text
+ self.text.bind("<<Changed>>", self.re_render)
+ self.text.bind("<Configure>", self.re_render)
+ self.number_of_digits = 0
+ self.pack(*arg, **kwargs)
+
+ def disconnect(self, text_widget):
+ pass
+
+ def re_render(self, event):
+ """Re-render the line canvas"""
+ self.delete('all')
+ if self.number_of_digits != len(self.text.index('end')):
+ self.number_of_digits = len(self.text.index('end'))
+ self['width'] = self.number_of_digits * 8
+
+ temp = self.text.index("@0,0")
+ while True :
+ dline= self.text.dlineinfo(temp)
+ if not dline:
+ break
+ y, height = dline[1], dline[4]
+ linenum = str(temp).split(".")[0]
+ linenum_text = ' '*(self.number_of_digits - len(linenum)) \
+ + linenum
+ self.create_text(5, y + height/4, anchor="nw", text=linenum_text)
+ temp = self.text.index("%s+1line" % temp)
+
+class Text(tk.Text):
+ def __init__(self, *args, **kwargs):
+ tk.Text.__init__(self, *args, **kwargs)
+
+ self.tk.eval('''
+ proc widget_interceptor {widget command args} {
+
+ set orig_call [uplevel [linsert $args 0 $command]]
+
+ if {
+ ([lindex $args 0] == "insert") ||
+ ([lindex $args 0] == "delete") ||
+ ([lindex $args 0] == "replace") ||
+ ([lrange $args 0 2] == {mark set insert}) ||
+ ([lrange $args 0 1] == {xview moveto}) ||
+ ([lrange $args 0 1] == {xview scroll}) ||
+ ([lrange $args 0 1] == {yview moveto}) ||
+ ([lrange $args 0 1] == {yview scroll})} {
+
+ event generate $widget <<Changed>> -when tail
+ }
+
+ #return original command
+ return $orig_call
+ }
+ ''')
+ self.tk.eval('''
+ rename {widget} new_{widget}
+ interp alias {{}} ::{widget} {{}} widget_interceptor {widget} new_{widget}
+ '''.format(widget=str(self)))
diff -r 601a08fcb507 Lib/idlelib/MultiCall.py
--- a/Lib/idlelib/MultiCall.py Sat Jun 14 18:51:34 2014 -0700
+++ b/Lib/idlelib/MultiCall.py Tue Jun 17 18:28:18 2014 +0530
@@ -34,8 +34,8 @@
import tkinter
# the event type constants, which define the meaning of mc_type
-MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
-MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
+MC_CHANGED=0; MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
+MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
@@ -251,7 +251,7 @@
# define the list of event types to be handled by MultiEvent. the order is
# compatible with the definition of event type constants.
_types = (
- ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
+ ("KeyPress", "Key", "Changed"), ("KeyRelease",), ("ButtonPress", "Button"),
("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
diff -r 601a08fcb507 text-without-multicall.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/text-without-multicall.py Tue Jun 17 18:28:18 2014 +0530
@@ -0,0 +1,27 @@
+from idlelib.LineNumber import LineNumberCanvas, Text
+import tkinter as tk
+
+class EditorWindow(tk.Frame):
+ def __init__(self, *args, **kwargs):
+ tk.Frame.__init__(self, *args, **kwargs)
+ self.text = Text(self)
+ self.scrollbar = tk.Scrollbar(orient="vertical", command=self.text.yview)
+
+ self.text.configure(yscrollcommand=self.scrollbar.set)
+
+ self.linenumbers = LineNumberCanvas(self, width=40)
+ self.linenumbers.connect(self.text)
+
+ self.scrollbar.pack(side="right", fill="y")
+ self.linenumbers.pack(side="left", fill="y")
+
+ self.text.bind("<<Changed>>", self.linenumbers.re_render)
+ self.text.bind("<Configure>", self.linenumbers.re_render)
+ self.text.pack(side="right", fill="both", expand=True)
+ for i in range(1000):
+ self.text.insert('end','sample text'+'\n')
+
+root = tk.Tk()
+editwin = EditorWindow()
+editwin.pack()
+root.mainloop()
More information about the IDLE-dev
mailing list