2016-06-10 13 views
4

Mein Code (siehe unten) soll eine Start/Stop-Taste, eine Skalentaste zur Auswahl der Geschwindigkeit der Visualisierung und eine Leinwand mit vielen Rechtecken anzeigen, die zufällig die Farbe mit der Zeit ändern.Itemconfigure() und Speicherverlust mit tkinter

Wenn ich diesen Codeabschnitt ausführe, erhöht sich die Speichernutzung dramatisch mit der Zeit (wenn Sie ausführen, müssen Sie die Geschwindigkeit auf etwa 10 erhöhen, um es einfacher zu sehen). Bei der Arbeit (auf einer Windows 7-Workstation), wo ich es zuerst getestet habe, wird es nach wenigen Minuten praktisch unbrauchbar (es wird sehr langsam), während es auf meinem Mac-Laptop ein wenig länger überlebt, obwohl die Speicherauslastung stetig steigt.

Nach dem Täter sah zu haben, ich habe über mehrere Threads kommen einschließlich this one von Tk-Toolkit, das bis 2010 zurück geht, wo sie erwähnen, dass es ein Problem mit itemconfigure() ist, wenn es verwendet wird, die Farbe zu ändern, das ist genau das, was ich mache ich.

die "self.update_canvas()" Funktion in der self.run_InfiniteT_MC() Funktion löst so weit das Problem kommentiert ich sehen kann und scheint mit dem Diagnose zu bestätigen, dass itemconfigure() Farbe ändern noch problematisch sein kann.

Beachten Sie, dass ich auch versucht habe, die sich ändernden Rechtecke über den Befehl "self.canvas.delete (self.rect [i])" zu löschen und sie dann neu zu erstellen, aber das mein Speicherproblem überhaupt nicht ändert.

Ich habe auch versucht, die gesamte Leinwand über "self.canvas.destroy()" zu zerstören und alles von Grund auf neu zu erstellen, jedes Mal in der Notwendigkeit, das Bild zu aktualisieren, aber das löst nicht mein Speicherproblem.

Gibt es etwas, was ich tun kann, um dieses Speicherproblem zu lösen, ohne meinen gesamten Code zu ändern (hier ist das nur ein kleiner Teil davon)?

EDIT: Nach dem Einrücken des Befehls self.after ordnungsgemäß das Problem verschwand; so ist der itemconfigure() Befehl überhaupt nicht schuld, zumindest nicht für dieses Problem.

from tkinter import * 
from numpy import * 
from random import randint 

class Application(Frame): 
    def __init__(self,master): 
     Frame.__init__(self,master) 
     self.columnconfigure(0, pad = 10) 
     self.grid() 
     self.count = 0 
     self.create_widgets() 


def create_widgets(self): 
     "create an array of cells all initiated with the same value" 
     self.nx = 70 
     self.ny = self.nx 
     self.ntot = self.nx*self.ny 
     self.state = [0 for x in range(self.ntot)] 
     for x in range(self.ntot): 
      self.state[x] = 0 #0 is down, 1 is right, 2 is up, 3 is left ...modulo 4 
     "create a scale button to choose speed of dynamics" 
     self.ScaleSpeedVar = IntVar 
     self.ScaleSpeed = Scale(self, from_=1, to =20, orient = HORIZONTAL, label = "SimuSpeed", variable = self.ScaleSpeedVar, font =('Helvetica','18')) 
     self.ScaleSpeed.grid() 
     self.ScaleSpeed.set(1) 

"create a button that starts/stops the dynamics" 
     self.do_run = False 
     self.startclick = True 
     self.buttonStartStop = Button(self, text = "Start/Stop", font =('Helvetica','18')) 
     self.buttonStartStop["command"] = self.start_stop_simu 
     self.buttonStartStop.grid() 
"create a big canva to contain the simulation cells" 
     self.size = 500 
     self.canvas = Canvas(self, width=self.size, height=self.size, bg ="red") 
     self.canvas.grid() 

     self.width = 1 
     self.rect = [0 for x in range(self.ntot)] 
     for i in range(self.ntot): 
      self.rectsize = self.size/self.nx 
      self.rect[i] = self.canvas.create_rectangle((i%(self.nx))*self.rectsize, self.rectsize*(i//self.nx), (i%(self.nx))*self.rectsize+self.rectsize, self.rectsize*(i//self.nx)+self.rectsize, fill="red", tag = i, width = self.width) 

def start_stop_simu(self): 
    if self.startclick: 
     self.start_simu() 
     self.startclick = False 
    else : 
     self.stop_simu() 
     self.startclick = True 

def start_simu(self): 
    self.do_run = True 
    self.run_InfiniteT_MC() 

def stop_simu(self): 
    self.do_run = False 

def run_InfiniteT_MC(self): 
    if self.do_run: 
     self.simuspeed = pow(2,self.ScaleSpeed.get()) 
     for i in range(self.simuspeed): 
      self.cellID = randint(0,self.ntot-1) 
      self.angle = 2*randint(0,1)-1 
      self.state[self.cellID] = (self.state[self.cellID]+self.angle)%4 
     self.update_canvas() 
    self.after(1, self.run_InfiniteT_MC) 

def update_canvas(self): 
    for i in range(self.ntot): 
     if self.state[i] == 0: 
      self.canvas.itemconfig(self.rect[i], fill = "red") 
     if self.state[i] == 2: 
      self.canvas.itemconfig(self.rect[i], fill = "blue") 
     if self.state[i] == 1: 
      self.canvas.itemconfig(self.rect[i], fill = "green") 
     if self.state[i] == 3: 
      self.canvas.itemconfig(self.rect[i], fill = "yellow") 
    self.canvas.update_idletasks() 


root = Tk() 
root.title("Problematic code") 
root.geometry("800x600") 
app = Application(root) 

root.mainloop() 
+0

Wenn Sie die Farben in hex Werte ändern (zB: '" # ff0000 "' anstelle von '" rot "', hat das irgendeinen Effekt? –

+0

das ist ein guter Punkt, ich werde das versuchen – gatsu

Antwort

1

Ich glaube, das Problem ist mit dem Anruf self.after. Jedes Mal, wenn Sie die Simulation starten, wird eine neue Endlosschleife erstellt (die niemals gestoppt wird). Das Einrücken der Zeile mit self.after, so dass es in der if-Anweisung ist, sollte Ihr Problem lösen, da dann eine solche Schleife beendet wird, wenn self.do_runFalse wird.

+0

Ja, das ist genau richtig; Das war nur eine schlechte Programmierung. – gatsu

1

Wenn das Problem ein Speicherleck im tk Kern ist, wenn Farben Zuweisung, dann ist die einzige Lösung ist, die itemconfigure zu vermeiden, tun die Farbe zu ändern. Eine Lösung wäre, vier Rechtecke für jede Position zu erzeugen, eine für jede Farbe. Sie können dann die Stapelreihenfolge so ändern, dass die gewünschte Farbe oben angezeigt wird. Dadurch werden die anderen Farben ausgeblendet.

Diese Lösung erfordert natürlich die vierfache Anzahl von Canvas-Objekten, aber wenn Sie nicht Hunderttausende zeichnen, sollte das keine Rolle spielen. Die Leinwand kann Zehntausende von Objekten ziemlich gut behandeln.

+0

ja ich dachte darüber nach ein und das würde die Arbeit für den visuellen Zweck tun, über den ich hier spreche, aber das Problem ist, dass in dem aktuellen Projekt ich jedes Rechteck haben soll klickbar sein sollte, so dass Sie auch die Farbe ändern können, wenn Sie darauf klicken.Es funktioniert ganz gut so, wie es ist, wenn es kein Speicherleck gab ... – gatsu

+0

Ok, nehmen wir an, ich möchte am Ende auf deine Idee eingehen. Wie kann ich tatsächlich die Stapelreihenfolge auf der Ebene einzelner Quadrate ändern? Kann ich ein 'grid_forget()' auf der Ebene eines einzelnen Quadrats 'self.rect [i]' im obigen Code tun? – gatsu