Seite 1 von 1
Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 09:39
von hpr
Hi,
habe ein kleines Testprogramm auf Lenny_amd64 Stable (Python 2.5.2) laufen lassen. Alles IO.
Auf Squeeze_amd64 (Python2.6.6) wird der Speicher (Top, VIRT und RES) immer mehr verbraucht.
Im echten Programm werden die Ressourcen (4 GB Hauptspeicher +Swap) innerhalb eines Wochenendes verbraten. Der Rechner ist dann kaum noch bedienbar.
Im Beispiel habe ich Pme eliminiert.
Mit "loop" sieht man das Problem am schnellsten.
Code: Alles auswählen
title = 'Text Speicher'
import time
import Tkinter
class Demo:
def __init__(self, parent):
self.parent=parent
self.sb= Tkinter.Scrollbar(parent)
self.st = Tkinter.Text(parent,yscrollcommand=self.sb.set,undo='false',maxundo=1)
self.writeButton = Tkinter.Button(root, text = 'write', command = self.write)
self.clearButton = Tkinter.Button(root, text = 'clear', command = self.clear)
self.loopButton = Tkinter.Button(root, text = 'loop', command = self.loop)
self.sb.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)
self.st.pack(padx = 5, pady = 5, fill = 'both', expand = 1)
self.writeButton.pack(side=Tkinter.LEFT)
self.clearButton.pack(side=Tkinter.LEFT)
self.loopButton.pack(side=Tkinter.LEFT)
#################################################################
def write(self):
print 'You clicked on write'
for i in range(5000):
self.st.insert('end',str(i)+"xxxxxxxxxxxxxxxxxxxxxx\n")
self.st.see('end')
self.st.update()
def clear(self, num=0):
print 'You clicked on clear'
self.st.delete('1.0','end')
self.st.insert('end',num)
self.st.insert('end',"\n")
self.st.update()
self.st.clipboard_clear()
def loop(self):
for x in range(200):
self.write()
time.sleep(1)
self.clear(x)
######################################################################
# Create demo in root window for testing.
if __name__ == '__main__':
root = Tkinter.Tk()
root.title(title)
exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
exitButton.pack(side = 'bottom')
widget = Demo(root)
root.mainloop()
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 09:57
von BlackJack
@hpr: Also unter einem 32-Bit Ubuntu 10.04 ist auch alles in Ordnung:
Code: Alles auswählen
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import Tkinter
>>> Tkinter.TkVersion
8.5
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 11:40
von Xynon1
Also ich weiß auch nicht wo das Problem liegen soll, zugegeben im extrem Test schafft man es den RAM vollzubekommen, da der GC nicht hinterherkommt bzw. den Speicher noch nicht freigeben will. Aber voher macht der CPU schlapp da das wrappen des Textes auf das Tk enorme Last verursacht. Jedes Zeichen entspricht laut Tk-Faustregel 2-3 Bytes.
Dennoch kann man deinen Code um einiges Optimieren.
1. Wichtig: Eine Schleife macht das Text-Widget langsam füg immer den gesammten Text ein.
2. Es gibt schon ein Text-Widget mit Scrollbars im Modul ScrolledText.
3. Nach dem löschen "\n" einzufügen ist ziemlich Sinnlos genau wie das "update" und "clipboard_clear" danach.
Hier mal ein sauberes Beispiel wie man es machen könnte:
Code: Alles auswählen
!#/usr/bin/env python
import Tkinter as tkinter
import ScrolledText as tktext
class MyText(tkinter.Frame):
def __init__(self, master, **kw):
tkinter.Frame.__init__(self, master, kw)
self.text = tktext.ScrolledText(master)
self.text.pack(expand=True, fill="both")
menu = tkinter.Frame(master)
menu.pack(expand=True, fill="x")
button_clear = tkinter.Button(menu, text="clear", command=self.clear)
button_clear.pack(fill="y", side="left")
def write(self, text):
self.text.insert("end", text)
self.text.see("end")
def clear(self):
self.text.delete('1.0', 'end')
def _loop(mytext, text):
for _ in xrange(200000):
mytext.clear()
mytext.update()
mytext.write("\n".join(20000*(text,)))
mytext.update()
if __name__ == '__main__':
text = """
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd
gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum
dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor
invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero
eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet."""
root = tkinter.Tk()
root.title("MyText")
mytext = MyText(root)
mytext.pack(expand=True, fill="both")
command_write = lambda: mytext.write("\n".join(20000*(text,)))
button_write = tkinter.Button(mytext, text="write", command=command_write)
button_write.pack(fill="y", side="left")
button_loop = tkinter.Button(mytext, text="loop")
button_loop.config(command=lambda: _loop(mytext, text))
button_loop.pack(fill="y", side="left")
button_exit = tkinter.Button(mytext, text='exit', command=root.destroy)
button_exit.pack(fill="y", side="left")
root.mainloop()
Die "_loop"-Funktion halte ich immer noch für unsinnig, aber damit du einen Vergleich hast. Wenn es schneller gehen soll solltest du dort die erste "update"-Funktion entfernen, die habe ich nur eingebaut damit man es flackern sieht. Ist aber eigentlich ziemlich Sinnfrei. Mindestens eines braucht man dort aber, sonst frisst sich die GUI fest. Generell sollte man "update" nur selten verwenden.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 12:06
von hpr
erst mal Danke,
mein Problem ist der Speicher weshalb ich z.B. Pmw welches ich eigentlich verwendet habe für das Beispiel gelöscht habe. Das Problem Tritt auch auf Suse 11.3_64 auf, auf Suse10.2_32 nicht.
Es soll die möglichkeit bestehen Prozessdaten die aus einem anderen Thread zyklisch kommen zu betrachten und erforderlichenfalls zu scrollen. Das ganze natürlich auf 20 Tab's. Performaceprobleme gibt es nicht, Nur ohne Speicher...
Sicherlich könnte man den Text selbst verwalten und per scrollbutton den gewünschten Text darstellen, aber bisher lief mein Programm auf NT4 ohne Probleme. Entwickelt hatte ich es auf Suse9.0_32. Nun habe ich den NT4 Rechner in Rente geschickt und einen aktuelle Rechner auf Lennu_64 aufgesetzt (vorgenannte Speicherprobleme). Um die Probleme zu analysieren habe ich Sqeeze_64 verwendet und dabei bin ich jetzt am verzweifeln, dabei habe ich auch weitere varianten als clear, update usw schon probiert, aber ohne Erfolg.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 12:21
von BlackJack
@hpr: Du könntest ja mal versuchen das Testprogramm in Tk/Tcl zu programmieren um das Problem weiter einzugrenzen. Wenn das auch Speicher frisst, wäre Python's Tk-Anbindung schon mal aus der Gleichung raus.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 12:35
von Xynon1
Eventuell auch erstmal mein Schnipsel auf den Plattformen ausprobieren, bevor man das ganze in Tk/Tcl schreibt. Wenn mein Programm nicht diese Speicherprobleme auslöst, kann es nur noch pmw oder deinem Quellcode liegen.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 12:48
von hpr
@Xynon1,
nach
- 1c1
< #!/usr/bin/env python
---
> !#/usr/bin/env python
3c3
< import Pmw as tktext
---
> import ScrolledText as tktext
bringt dein Programm unter Sqeeze_amd64 leider auch das Speicher Problem.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 12:51
von Xynon1
import Pmw as tktext

- ist eine ziemlich ulkige Zeile
Zudem, kannst du mal die Fehlermeldung posten ?
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 13:02
von hpr
@Xynon1,
bei "import ScrolledText as tktext" gibt es in deinem Script den Fehler
Traceback (most recent call last):
File "BS_ex.py", line 49, in <module>
mytext = MyText(root)
File "BS_ex.py", line 11, in __init__
self.text = tktext.ScrolledText(master)
AttributeError: 'module' object has no attribute 'ScrolledText'
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 13:06
von Xynon1
Logisch, du hast ja auch "import Pmw as tktext", dort stehen. "as" dient doch nur dazu dem Objekt, also deinem "Pmw" Modul in diesem Fall, einen anderen Namen zu geben. Deshalb kann er auch ScrolledText nicht finden, da die nicht im "Pmw" Modul verfügbar ist.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 13:12
von hpr
@Xynon1,
du irrst,
in deiner Variante tritt der Fehler auf.
In Pmp ist sehr wohl ScrolledText definiert.
Nach meiner Korrektur siehe DIFF funktioniert dein Script.
Leider aber mit dem Speicherproblem
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 13:17
von Xynon1
Dann stellt sich mir folgende Fragen, welche Python Version und welche Tk/Tcl Version hast du?
Und stimmt hast recht Pwm hat ein ScrolledText.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 13:28
von hpr
python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5]
also Debian Sqeeze Stable
tcl8.5
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 7. April 2011, 13:36
von Xynon1
Dann sollte das Modul eigentlich dabei sein. Kannst ja mal nachschauen ob es physisch existiert, ansonsten nimm mal zum Test einfach nur "tkinter.Text".
Es ging mir einfach nur darum "pmw" als Fehlerquelle auszuschließen. Sonst wirst du dich wohl an BlackJacks Vorschlag halten müssen.
Re: Speicherverbrauch Tkinter
Verfasst: Freitag 8. April 2011, 06:34
von hpr
1.) der Fehler mit "Import ScrolledText as tktext" und dann "tktext.ScrolledText(..)" "module has no attribute" ist bei mit jetzt nicht mehr vorhanden. Habe auch auf anderem Rechner getestet funktioniert auch dort (bei Squeeze mit Speicherfraß). Das Schreiben in größeren blöcken brigt echt Performance.
2.) Mein Speicherproblem schein wirklich ein Tk/Tcl Problem zu sein. Squezze_amd64 und Suse11.3 haben es beide.
3.) Das Problem mit den neuen Versionen hat mich vom eigenlichen Problem abgebracht. Werde mein Programm jetzt unter Lenny debuggen.
Re: Speicherverbrauch Tkinter
Verfasst: Donnerstag 14. April 2011, 20:56
von hpr
1. ein Klassiker es gab im Verzeichnis eine Datei ScrolledText.py vom testen. hat sich also erledigt
2. Habe das Beispiel mal in wish ausprobiert. Kein Problem in Lenny oder Squeeze.
Jetzt das Script von Xynon1 in
valgrind --leak-check=full --show-reachable=yes python py.py
ausgeführt. definitely lost: schlägt bei Lenny sofort zu. Es reicht schon
Code: Alles auswählen
from Tkinter import *
root= Tk()
root.title('Toplevel'
Label(root,text='This is the Toplevel').pack(pady=10)
root.mainloop()
Muss die detaillierten Ausschriften aber erst noch auswerten.
3. Bin mit dem Einbau von Queue für die Kommunikation Tkinter <-> Thread fast fertig. Funktioniert hat es auch ohne, aber eben mit Speicherklau.