Tkinter - memory leak with canvasWhich Python memory profiler is recommended?Install tkinter for Pythontkinter: using scrollbars on a canvasHow to clear Tkinter Canvas?Tkinter Canvas create_window()Tkinter Canvas Update Memory LeakMove a tkinter canvas with MouseTkinter Text and Itemconfigure Memory LeakItemconfigure() and memory leak with tkinterTkinter canvas zoom + move/pan
Pre-Employment Background Check With Consent For Future Checks
Are Captain Marvel's powers affected by Thanos breaking the Tesseract and claiming the stone?
How do I fix the group tension caused by my character stealing and possibly killing without provocation?
Why would five hundred and five be same as one?
What is this high flying aircraft over Pennsylvania?
What (the heck) is a Super Worm Equinox Moon?
Is there a RAID 0 Equivalent for RAM?
Do people actually use the word "kaputt" in conversation?
Ways of geometrical multiplication
What is the meaning of "You've never met a graph you didn't like?"
When is "ei" a diphthong?
How to test the sharpness of a knife?
Why is the Sun approximated as a black body at ~ 5800 K?
How to make money from a browser who sees 5 seconds into the future of any web page?
Would a primitive species be able to learn English from reading books alone?
Why does a 97 / 92 key piano exist by Bösendorfer?
Given this phrasing in the lease, when should I pay my rent?
Showing mass murder in a kid's book
Check if object is null and return null
Review your own paper in Mathematics
Isometric embedding of a genus g surface
Do I have to know the General Relativity theory to understand the concept of inertial frame?
Why Shazam when there is already Superman?
How to reduce predictors the right way for a logistic regression model
Tkinter - memory leak with canvas
Which Python memory profiler is recommended?Install tkinter for Pythontkinter: using scrollbars on a canvasHow to clear Tkinter Canvas?Tkinter Canvas create_window()Tkinter Canvas Update Memory LeakMove a tkinter canvas with MouseTkinter Text and Itemconfigure Memory LeakItemconfigure() and memory leak with tkinterTkinter canvas zoom + move/pan
I have a Python script that handles Modbus communications. One feature I added was a "graph" that shows the response times along with a color coded line that indicates if the response was successful, had an exception, or an error. The graph is just a scrollable canvas widget from Tkinter.
After graphing a certain number of lines old lines will be deleted and then a new one will be added to the end. For this example I have it set to 10, which means there will never be more than 10 lines on the canvas at once.
The code works correctly but there is a memory leak somewhere in this function. I let it run for about 24 hours and it took about 6x more memory after 24 hours. The function is part of a larger class.
My current guess is that my code causes the canvas size to constantly "expand," which slowly eats up the memory.
self.lineList = []
self.xPos = 0
def UpdateResponseTimeGraph(self):
if not self.graphQueue.empty():
temp = self.graphQueue.get() #pull from queue. A separate thread handles calculating the length and color of the line.
self.graphQueue.task_done()
lineName = temp[0] #assign queue values to variables
lineLength = temp[1]
lineColor = temp[2]
if len(self.lineList) >= 10: #if more than 10 lines are on the graph, delete the first one.
self.responseTimeCanvas.delete(self.lineList[0])
del self.lineList[0]
#Add line to canvas and a list so it can be referenced.
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-lineLength,
fill=lineColor, outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.graphFrame.after(10, self.UpdateResponseTimeGraph)
One solution could be loop back to the start of the graph once a limit is reached but I would rather not do this since it may be confusing where the graph starts. Usually I have far more responses than 10.
EDIT:
I'm still doing to trail and error stuff but it looks like the memory leak can be eliminated with Bryan's suggestion as long as the line attributes are not changed via itemconfig. The code below should be able to run as is, if you're on python 2.7 change the import statement from tkinter to Tkinter (lower case vs uppercase t). This code will have the memory leak in it. Comment out the itemconfig line and it will be eliminated.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
#if i comment out the line below the memory leak goes away.
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
python tkinter tkinter-canvas
add a comment |
I have a Python script that handles Modbus communications. One feature I added was a "graph" that shows the response times along with a color coded line that indicates if the response was successful, had an exception, or an error. The graph is just a scrollable canvas widget from Tkinter.
After graphing a certain number of lines old lines will be deleted and then a new one will be added to the end. For this example I have it set to 10, which means there will never be more than 10 lines on the canvas at once.
The code works correctly but there is a memory leak somewhere in this function. I let it run for about 24 hours and it took about 6x more memory after 24 hours. The function is part of a larger class.
My current guess is that my code causes the canvas size to constantly "expand," which slowly eats up the memory.
self.lineList = []
self.xPos = 0
def UpdateResponseTimeGraph(self):
if not self.graphQueue.empty():
temp = self.graphQueue.get() #pull from queue. A separate thread handles calculating the length and color of the line.
self.graphQueue.task_done()
lineName = temp[0] #assign queue values to variables
lineLength = temp[1]
lineColor = temp[2]
if len(self.lineList) >= 10: #if more than 10 lines are on the graph, delete the first one.
self.responseTimeCanvas.delete(self.lineList[0])
del self.lineList[0]
#Add line to canvas and a list so it can be referenced.
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-lineLength,
fill=lineColor, outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.graphFrame.after(10, self.UpdateResponseTimeGraph)
One solution could be loop back to the start of the graph once a limit is reached but I would rather not do this since it may be confusing where the graph starts. Usually I have far more responses than 10.
EDIT:
I'm still doing to trail and error stuff but it looks like the memory leak can be eliminated with Bryan's suggestion as long as the line attributes are not changed via itemconfig. The code below should be able to run as is, if you're on python 2.7 change the import statement from tkinter to Tkinter (lower case vs uppercase t). This code will have the memory leak in it. Comment out the itemconfig line and it will be eliminated.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
#if i comment out the line below the memory leak goes away.
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
python tkinter tkinter-canvas
I don't see anything obviously wrong...but you could try changing the existingCanvas
object inlineList[0]
via the widget'sitemconfigure()
method and modifying the rectangle that already exists, instead of deleting it and creating a new one as you're now doing.
– martineau
Mar 7 at 22:30
That's my plan B right now. I think it looks better when constantly adding to the end and it makes it more clear cut what order things happened. I have an older version of this script and the leak exists but doesn't cause problems unless I'm running it for multiple days while doing rapid reads. After that it will freeze up or crash.
– Dave1551
Mar 7 at 22:36
You can change the order of the existing items in aCanvas
display list by calling the widget'stag_Lower()
ortag_raise()
methods.
– martineau
Mar 7 at 22:40
add a comment |
I have a Python script that handles Modbus communications. One feature I added was a "graph" that shows the response times along with a color coded line that indicates if the response was successful, had an exception, or an error. The graph is just a scrollable canvas widget from Tkinter.
After graphing a certain number of lines old lines will be deleted and then a new one will be added to the end. For this example I have it set to 10, which means there will never be more than 10 lines on the canvas at once.
The code works correctly but there is a memory leak somewhere in this function. I let it run for about 24 hours and it took about 6x more memory after 24 hours. The function is part of a larger class.
My current guess is that my code causes the canvas size to constantly "expand," which slowly eats up the memory.
self.lineList = []
self.xPos = 0
def UpdateResponseTimeGraph(self):
if not self.graphQueue.empty():
temp = self.graphQueue.get() #pull from queue. A separate thread handles calculating the length and color of the line.
self.graphQueue.task_done()
lineName = temp[0] #assign queue values to variables
lineLength = temp[1]
lineColor = temp[2]
if len(self.lineList) >= 10: #if more than 10 lines are on the graph, delete the first one.
self.responseTimeCanvas.delete(self.lineList[0])
del self.lineList[0]
#Add line to canvas and a list so it can be referenced.
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-lineLength,
fill=lineColor, outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.graphFrame.after(10, self.UpdateResponseTimeGraph)
One solution could be loop back to the start of the graph once a limit is reached but I would rather not do this since it may be confusing where the graph starts. Usually I have far more responses than 10.
EDIT:
I'm still doing to trail and error stuff but it looks like the memory leak can be eliminated with Bryan's suggestion as long as the line attributes are not changed via itemconfig. The code below should be able to run as is, if you're on python 2.7 change the import statement from tkinter to Tkinter (lower case vs uppercase t). This code will have the memory leak in it. Comment out the itemconfig line and it will be eliminated.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
#if i comment out the line below the memory leak goes away.
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
python tkinter tkinter-canvas
I have a Python script that handles Modbus communications. One feature I added was a "graph" that shows the response times along with a color coded line that indicates if the response was successful, had an exception, or an error. The graph is just a scrollable canvas widget from Tkinter.
After graphing a certain number of lines old lines will be deleted and then a new one will be added to the end. For this example I have it set to 10, which means there will never be more than 10 lines on the canvas at once.
The code works correctly but there is a memory leak somewhere in this function. I let it run for about 24 hours and it took about 6x more memory after 24 hours. The function is part of a larger class.
My current guess is that my code causes the canvas size to constantly "expand," which slowly eats up the memory.
self.lineList = []
self.xPos = 0
def UpdateResponseTimeGraph(self):
if not self.graphQueue.empty():
temp = self.graphQueue.get() #pull from queue. A separate thread handles calculating the length and color of the line.
self.graphQueue.task_done()
lineName = temp[0] #assign queue values to variables
lineLength = temp[1]
lineColor = temp[2]
if len(self.lineList) >= 10: #if more than 10 lines are on the graph, delete the first one.
self.responseTimeCanvas.delete(self.lineList[0])
del self.lineList[0]
#Add line to canvas and a list so it can be referenced.
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-lineLength,
fill=lineColor, outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.graphFrame.after(10, self.UpdateResponseTimeGraph)
One solution could be loop back to the start of the graph once a limit is reached but I would rather not do this since it may be confusing where the graph starts. Usually I have far more responses than 10.
EDIT:
I'm still doing to trail and error stuff but it looks like the memory leak can be eliminated with Bryan's suggestion as long as the line attributes are not changed via itemconfig. The code below should be able to run as is, if you're on python 2.7 change the import statement from tkinter to Tkinter (lower case vs uppercase t). This code will have the memory leak in it. Comment out the itemconfig line and it will be eliminated.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
#if i comment out the line below the memory leak goes away.
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
python tkinter tkinter-canvas
python tkinter tkinter-canvas
edited Mar 8 at 14:52
Dave1551
asked Mar 7 at 22:06
Dave1551Dave1551
284
284
I don't see anything obviously wrong...but you could try changing the existingCanvas
object inlineList[0]
via the widget'sitemconfigure()
method and modifying the rectangle that already exists, instead of deleting it and creating a new one as you're now doing.
– martineau
Mar 7 at 22:30
That's my plan B right now. I think it looks better when constantly adding to the end and it makes it more clear cut what order things happened. I have an older version of this script and the leak exists but doesn't cause problems unless I'm running it for multiple days while doing rapid reads. After that it will freeze up or crash.
– Dave1551
Mar 7 at 22:36
You can change the order of the existing items in aCanvas
display list by calling the widget'stag_Lower()
ortag_raise()
methods.
– martineau
Mar 7 at 22:40
add a comment |
I don't see anything obviously wrong...but you could try changing the existingCanvas
object inlineList[0]
via the widget'sitemconfigure()
method and modifying the rectangle that already exists, instead of deleting it and creating a new one as you're now doing.
– martineau
Mar 7 at 22:30
That's my plan B right now. I think it looks better when constantly adding to the end and it makes it more clear cut what order things happened. I have an older version of this script and the leak exists but doesn't cause problems unless I'm running it for multiple days while doing rapid reads. After that it will freeze up or crash.
– Dave1551
Mar 7 at 22:36
You can change the order of the existing items in aCanvas
display list by calling the widget'stag_Lower()
ortag_raise()
methods.
– martineau
Mar 7 at 22:40
I don't see anything obviously wrong...but you could try changing the existing
Canvas
object in lineList[0]
via the widget's itemconfigure()
method and modifying the rectangle that already exists, instead of deleting it and creating a new one as you're now doing.– martineau
Mar 7 at 22:30
I don't see anything obviously wrong...but you could try changing the existing
Canvas
object in lineList[0]
via the widget's itemconfigure()
method and modifying the rectangle that already exists, instead of deleting it and creating a new one as you're now doing.– martineau
Mar 7 at 22:30
That's my plan B right now. I think it looks better when constantly adding to the end and it makes it more clear cut what order things happened. I have an older version of this script and the leak exists but doesn't cause problems unless I'm running it for multiple days while doing rapid reads. After that it will freeze up or crash.
– Dave1551
Mar 7 at 22:36
That's my plan B right now. I think it looks better when constantly adding to the end and it makes it more clear cut what order things happened. I have an older version of this script and the leak exists but doesn't cause problems unless I'm running it for multiple days while doing rapid reads. After that it will freeze up or crash.
– Dave1551
Mar 7 at 22:36
You can change the order of the existing items in a
Canvas
display list by calling the widget's tag_Lower()
or tag_raise()
methods.– martineau
Mar 7 at 22:40
You can change the order of the existing items in a
Canvas
display list by calling the widget's tag_Lower()
or tag_raise()
methods.– martineau
Mar 7 at 22:40
add a comment |
2 Answers
2
active
oldest
votes
The underlying tk canvas doesn't reuse or recycle object identifiers. Whenever you create a new object, a new identifier is generated. The memory of these objects is never reclaimed.
Note: this is memory inside the embedded tcl interpreter, rather than memory managed by python.
The solution is to reconfigure old, no longer used elements rather than deleting them and creating new ones.
I don't know the internals like you do, but it sounds like you're confirming that my suggestion would avoid the issue.
– martineau
Mar 7 at 22:46
1
@martineau: I didn't see your suggestion until now, but yes, we agree on the solution.
– Bryan Oakley
Mar 7 at 22:57
Thanks. @martineau I may have misinterpreted your suggestion. I changed my code to hopefully fix this issue. Rather than deleting the line when it reaches the limit I take the first line and place it at the end. For now I don't change any other attributes. This means the canvas is still 'expanding' but it looks like there is no longer a memory leak. I'll have to let it run for a few hours before I can be sure. I'll update this thread once it runs long enough
– Dave1551
Mar 7 at 23:10
@Dave1551: FWIW, that sounds basically correct as I don't think not also having a call toitemconfigure()
to change the object's attributes will matter - but again I don't know a lot about the internal details.
– martineau
Mar 7 at 23:19
The memory leak went away if i don't use the itemconfig to change the lines' color. When just moving the line the memory started at 6.8MB, then went up to 7.2 pretty quickly, then eventually dropped to 2MB which I can't explain. When changing the color with itemcofig the memory slowly increases. I added standalone code to my original post if anyone want's to experiment with this too.
– Dave1551
Mar 8 at 14:55
|
show 2 more comments
Here's the code with no memory leak. The original source of the leak was me deleting the old line then creating a new one. This solution moves the first the line to the end then change's its attributes as necessary. I had a second 'leak' in my example code where I was picking a random color each time which lead to the number of colors used eating up a lot of memory. This code just prints green lines but the length will be random.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(100, 255, 100))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(100, 255, 100), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later.
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55053568%2ftkinter-memory-leak-with-canvas%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
The underlying tk canvas doesn't reuse or recycle object identifiers. Whenever you create a new object, a new identifier is generated. The memory of these objects is never reclaimed.
Note: this is memory inside the embedded tcl interpreter, rather than memory managed by python.
The solution is to reconfigure old, no longer used elements rather than deleting them and creating new ones.
I don't know the internals like you do, but it sounds like you're confirming that my suggestion would avoid the issue.
– martineau
Mar 7 at 22:46
1
@martineau: I didn't see your suggestion until now, but yes, we agree on the solution.
– Bryan Oakley
Mar 7 at 22:57
Thanks. @martineau I may have misinterpreted your suggestion. I changed my code to hopefully fix this issue. Rather than deleting the line when it reaches the limit I take the first line and place it at the end. For now I don't change any other attributes. This means the canvas is still 'expanding' but it looks like there is no longer a memory leak. I'll have to let it run for a few hours before I can be sure. I'll update this thread once it runs long enough
– Dave1551
Mar 7 at 23:10
@Dave1551: FWIW, that sounds basically correct as I don't think not also having a call toitemconfigure()
to change the object's attributes will matter - but again I don't know a lot about the internal details.
– martineau
Mar 7 at 23:19
The memory leak went away if i don't use the itemconfig to change the lines' color. When just moving the line the memory started at 6.8MB, then went up to 7.2 pretty quickly, then eventually dropped to 2MB which I can't explain. When changing the color with itemcofig the memory slowly increases. I added standalone code to my original post if anyone want's to experiment with this too.
– Dave1551
Mar 8 at 14:55
|
show 2 more comments
The underlying tk canvas doesn't reuse or recycle object identifiers. Whenever you create a new object, a new identifier is generated. The memory of these objects is never reclaimed.
Note: this is memory inside the embedded tcl interpreter, rather than memory managed by python.
The solution is to reconfigure old, no longer used elements rather than deleting them and creating new ones.
I don't know the internals like you do, but it sounds like you're confirming that my suggestion would avoid the issue.
– martineau
Mar 7 at 22:46
1
@martineau: I didn't see your suggestion until now, but yes, we agree on the solution.
– Bryan Oakley
Mar 7 at 22:57
Thanks. @martineau I may have misinterpreted your suggestion. I changed my code to hopefully fix this issue. Rather than deleting the line when it reaches the limit I take the first line and place it at the end. For now I don't change any other attributes. This means the canvas is still 'expanding' but it looks like there is no longer a memory leak. I'll have to let it run for a few hours before I can be sure. I'll update this thread once it runs long enough
– Dave1551
Mar 7 at 23:10
@Dave1551: FWIW, that sounds basically correct as I don't think not also having a call toitemconfigure()
to change the object's attributes will matter - but again I don't know a lot about the internal details.
– martineau
Mar 7 at 23:19
The memory leak went away if i don't use the itemconfig to change the lines' color. When just moving the line the memory started at 6.8MB, then went up to 7.2 pretty quickly, then eventually dropped to 2MB which I can't explain. When changing the color with itemcofig the memory slowly increases. I added standalone code to my original post if anyone want's to experiment with this too.
– Dave1551
Mar 8 at 14:55
|
show 2 more comments
The underlying tk canvas doesn't reuse or recycle object identifiers. Whenever you create a new object, a new identifier is generated. The memory of these objects is never reclaimed.
Note: this is memory inside the embedded tcl interpreter, rather than memory managed by python.
The solution is to reconfigure old, no longer used elements rather than deleting them and creating new ones.
The underlying tk canvas doesn't reuse or recycle object identifiers. Whenever you create a new object, a new identifier is generated. The memory of these objects is never reclaimed.
Note: this is memory inside the embedded tcl interpreter, rather than memory managed by python.
The solution is to reconfigure old, no longer used elements rather than deleting them and creating new ones.
answered Mar 7 at 22:40
Bryan OakleyBryan Oakley
220k22273433
220k22273433
I don't know the internals like you do, but it sounds like you're confirming that my suggestion would avoid the issue.
– martineau
Mar 7 at 22:46
1
@martineau: I didn't see your suggestion until now, but yes, we agree on the solution.
– Bryan Oakley
Mar 7 at 22:57
Thanks. @martineau I may have misinterpreted your suggestion. I changed my code to hopefully fix this issue. Rather than deleting the line when it reaches the limit I take the first line and place it at the end. For now I don't change any other attributes. This means the canvas is still 'expanding' but it looks like there is no longer a memory leak. I'll have to let it run for a few hours before I can be sure. I'll update this thread once it runs long enough
– Dave1551
Mar 7 at 23:10
@Dave1551: FWIW, that sounds basically correct as I don't think not also having a call toitemconfigure()
to change the object's attributes will matter - but again I don't know a lot about the internal details.
– martineau
Mar 7 at 23:19
The memory leak went away if i don't use the itemconfig to change the lines' color. When just moving the line the memory started at 6.8MB, then went up to 7.2 pretty quickly, then eventually dropped to 2MB which I can't explain. When changing the color with itemcofig the memory slowly increases. I added standalone code to my original post if anyone want's to experiment with this too.
– Dave1551
Mar 8 at 14:55
|
show 2 more comments
I don't know the internals like you do, but it sounds like you're confirming that my suggestion would avoid the issue.
– martineau
Mar 7 at 22:46
1
@martineau: I didn't see your suggestion until now, but yes, we agree on the solution.
– Bryan Oakley
Mar 7 at 22:57
Thanks. @martineau I may have misinterpreted your suggestion. I changed my code to hopefully fix this issue. Rather than deleting the line when it reaches the limit I take the first line and place it at the end. For now I don't change any other attributes. This means the canvas is still 'expanding' but it looks like there is no longer a memory leak. I'll have to let it run for a few hours before I can be sure. I'll update this thread once it runs long enough
– Dave1551
Mar 7 at 23:10
@Dave1551: FWIW, that sounds basically correct as I don't think not also having a call toitemconfigure()
to change the object's attributes will matter - but again I don't know a lot about the internal details.
– martineau
Mar 7 at 23:19
The memory leak went away if i don't use the itemconfig to change the lines' color. When just moving the line the memory started at 6.8MB, then went up to 7.2 pretty quickly, then eventually dropped to 2MB which I can't explain. When changing the color with itemcofig the memory slowly increases. I added standalone code to my original post if anyone want's to experiment with this too.
– Dave1551
Mar 8 at 14:55
I don't know the internals like you do, but it sounds like you're confirming that my suggestion would avoid the issue.
– martineau
Mar 7 at 22:46
I don't know the internals like you do, but it sounds like you're confirming that my suggestion would avoid the issue.
– martineau
Mar 7 at 22:46
1
1
@martineau: I didn't see your suggestion until now, but yes, we agree on the solution.
– Bryan Oakley
Mar 7 at 22:57
@martineau: I didn't see your suggestion until now, but yes, we agree on the solution.
– Bryan Oakley
Mar 7 at 22:57
Thanks. @martineau I may have misinterpreted your suggestion. I changed my code to hopefully fix this issue. Rather than deleting the line when it reaches the limit I take the first line and place it at the end. For now I don't change any other attributes. This means the canvas is still 'expanding' but it looks like there is no longer a memory leak. I'll have to let it run for a few hours before I can be sure. I'll update this thread once it runs long enough
– Dave1551
Mar 7 at 23:10
Thanks. @martineau I may have misinterpreted your suggestion. I changed my code to hopefully fix this issue. Rather than deleting the line when it reaches the limit I take the first line and place it at the end. For now I don't change any other attributes. This means the canvas is still 'expanding' but it looks like there is no longer a memory leak. I'll have to let it run for a few hours before I can be sure. I'll update this thread once it runs long enough
– Dave1551
Mar 7 at 23:10
@Dave1551: FWIW, that sounds basically correct as I don't think not also having a call to
itemconfigure()
to change the object's attributes will matter - but again I don't know a lot about the internal details.– martineau
Mar 7 at 23:19
@Dave1551: FWIW, that sounds basically correct as I don't think not also having a call to
itemconfigure()
to change the object's attributes will matter - but again I don't know a lot about the internal details.– martineau
Mar 7 at 23:19
The memory leak went away if i don't use the itemconfig to change the lines' color. When just moving the line the memory started at 6.8MB, then went up to 7.2 pretty quickly, then eventually dropped to 2MB which I can't explain. When changing the color with itemcofig the memory slowly increases. I added standalone code to my original post if anyone want's to experiment with this too.
– Dave1551
Mar 8 at 14:55
The memory leak went away if i don't use the itemconfig to change the lines' color. When just moving the line the memory started at 6.8MB, then went up to 7.2 pretty quickly, then eventually dropped to 2MB which I can't explain. When changing the color with itemcofig the memory slowly increases. I added standalone code to my original post if anyone want's to experiment with this too.
– Dave1551
Mar 8 at 14:55
|
show 2 more comments
Here's the code with no memory leak. The original source of the leak was me deleting the old line then creating a new one. This solution moves the first the line to the end then change's its attributes as necessary. I had a second 'leak' in my example code where I was picking a random color each time which lead to the number of colors used eating up a lot of memory. This code just prints green lines but the length will be random.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(100, 255, 100))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(100, 255, 100), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later.
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
add a comment |
Here's the code with no memory leak. The original source of the leak was me deleting the old line then creating a new one. This solution moves the first the line to the end then change's its attributes as necessary. I had a second 'leak' in my example code where I was picking a random color each time which lead to the number of colors used eating up a lot of memory. This code just prints green lines but the length will be random.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(100, 255, 100))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(100, 255, 100), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later.
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
add a comment |
Here's the code with no memory leak. The original source of the leak was me deleting the old line then creating a new one. This solution moves the first the line to the end then change's its attributes as necessary. I had a second 'leak' in my example code where I was picking a random color each time which lead to the number of colors used eating up a lot of memory. This code just prints green lines but the length will be random.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(100, 255, 100))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(100, 255, 100), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later.
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
Here's the code with no memory leak. The original source of the leak was me deleting the old line then creating a new one. This solution moves the first the line to the end then change's its attributes as necessary. I had a second 'leak' in my example code where I was picking a random color each time which lead to the number of colors used eating up a lot of memory. This code just prints green lines but the length will be random.
import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random
def RGB(r, g, b):
return '#:02x:02x:02x'.format(r, g, b)
class MainUI:
def __init__(self, master):
self.master = master
self.lineList = []
self.xPos = 0
self.maxLine = 122
self.responseIndex = 0
self.responseWidth = 100
self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
self.responseTimeCanvas.pack()
self.UpdateResponseTimeGraph()
def UpdateResponseTimeGraph(self):
self.lineLength = random.randint(10,99)
if len(self.lineList) >= self.maxLine:
self.lineLength = random.randint(5,95)
self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)
self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(100, 255, 100))
else:
self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength,
fill=RGB(100, 255, 100), outline=''))
self.xPos += 5 #will cause the next line to start 5 pixels later.
self.responseIndex += 1
self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))
self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.
self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)
mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()
answered Mar 9 at 16:45
Dave1551Dave1551
284
284
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55053568%2ftkinter-memory-leak-with-canvas%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I don't see anything obviously wrong...but you could try changing the existing
Canvas
object inlineList[0]
via the widget'sitemconfigure()
method and modifying the rectangle that already exists, instead of deleting it and creating a new one as you're now doing.– martineau
Mar 7 at 22:30
That's my plan B right now. I think it looks better when constantly adding to the end and it makes it more clear cut what order things happened. I have an older version of this script and the leak exists but doesn't cause problems unless I'm running it for multiple days while doing rapid reads. After that it will freeze up or crash.
– Dave1551
Mar 7 at 22:36
You can change the order of the existing items in a
Canvas
display list by calling the widget'stag_Lower()
ortag_raise()
methods.– martineau
Mar 7 at 22:40