Meine pygtk Anwendung leakt memory

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Vortex
User
Beiträge: 16
Registriert: Sonntag 13. August 2006, 14:12
Kontaktdaten:

Sonntag 13. August 2006, 14:29

Hallo zusammen,

ich schreibe gerade einen pygtk-client für xmms2 (hauptsächlich für Lernzwecke, bin noch Anfänger :wink: ).

Der client speichert die Playlist in einem gtk.ListStore. Befinden sich viele Einträge in der Playlist, dann steigt natürlich auch die Speicherauslastung des clients.

Das Problem ist jetzt folgendes:

Erhält der client vom server die Mitteilung, dass die Playlist gelöscht wurde, führt er ein "ListStore.clear()" aus (siehe Funktion "clear" weiter unten). Dadurch senkt sich aber die Speicherauslastung nicht!

Durch wiederholtes löschen und neu befüllen der Playlist kann man die Speicherauslastung von meinem Programm bis ins unendliche treiben.

Ich versuche mal den code von meiner Playlist hier zu posten, ich hoffe es ist nicht zu lang für dieses Forum (knapp 400 Zeilen).

Ich hoffe mir kann da jemand helfen, bei anderen pygtk clients für xmms2 besteht dieses Problem nämlich nicht, doch ich werde als Anfänger aus deren Code noch nicht ganz schlau. :oops:

EDIT: Beim durchsuchen des Forums bin ich jetzt hierauf gestoßen:
Hm. Also ich hab das jetzt mal auf meinem Linux Rechner getestet, da kann ich das Problem leider nicht reproduzieren. Solltest du die Windows Version haben, könnte es sein, dass die Entwickler dort vergessen haben Speicher frei zu machen ;-(
Leider verwende ich selbst Gentoo Linux als Betriebssystem und das Problem besteht dort ja auch. Es kann sich also kaum um einen Programmierfehler handeln. Außerdem tritt das Problem, wie schon erwähnt, bei anderen pygtk clients für xmms2 nicht auf. Leider verstehe ich deren Playlist-Handhabung nicht wirklich...
Zuletzt geändert von Vortex am Dienstag 15. August 2006, 12:51, insgesamt 2-mal geändert.
Vortex
User
Beiträge: 16
Registriert: Sonntag 13. August 2006, 14:12
Kontaktdaten:

Sonntag 13. August 2006, 14:30

Hier der Inhalt von playlist.py:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*- 

import gtk, gobject, pango
import sys
from functions import *

class Playlist(gobject.GObject):
	COLUMN_MAP = { "ID" : 1, "Title" : 2, "Artist" : 3, "Album" : 4, "Duration" : 5, "Bitrate" : 6 }
	
	__gproperties__ = {
		"initital-playlist-complete" : (gobject.TYPE_BOOLEAN,
			"population of playlist is complete",
			"when this is True, the Playlist has been downloaded from the server",
			False,
			gobject.PARAM_READWRITE),
			
		"items-count" : (gobject.TYPE_INT,
			"number of playlist items",
			"for checking whether the initial population is complete and for showing how many items are in playlist",
			-1,
			sys.maxint,
			0,
			gobject.PARAM_READWRITE),
			
		"ids" : (gobject.TYPE_PYOBJECT,
			"list of playlist ids",
			"this list is downloaded from the server when the playlist is first initialized",
			gobject.PARAM_READWRITE),
			
		"current-pos" : (gobject.TYPE_INT,
			"the current position in the playlist",
			"which playlist item is playing?",
			-1,
			sys.maxint,
			0,
			gobject.PARAM_READWRITE),
			
		"marked" : (gobject.TYPE_INT,
			"the current marked entry",
			"the items that is currently highlighted (should be the currently playing one)",
			-1,
			sys.maxint,
			-1,
			gobject.PARAM_READWRITE),
			
		"column-dict" : (gobject.TYPE_PYOBJECT,
			"list of columns to show",
			"defines which columns are shown on the treeview and their initial size",
			gobject.PARAM_READWRITE),
			
		"change" : (gobject.TYPE_PYOBJECT,
			"a change to the playlist",
			"this gets updated whenever the server notifies me of a change to the playlist",
			gobject.PARAM_READWRITE),
			
		"plfilter" : (gobject.TYPE_STRING,
			"the current filter",
			"this gets updated whenever the user types something into the Filter: TextBox",
			"",
			gobject.PARAM_READWRITE)
	}
	
	def do_get_property(self, property):
		if property.name == "initital-playlist-complete":
		    return self.initital_playlist_complete
		elif property.name == "items-count":
		    return self.items_count
		elif property.name == "ids":
		    return self.ids
		elif property.name == "current-pos":
		    return self.current_pos
		elif property.name == "marked":
		    return self.marked
		elif property.name == "column-dict":
		    return self.column_dict
		elif property.name == "change":
		    return self.change
		elif property.name == "plfilter":
		    return self.plfilter
		else:
		    raise AttributeError, "Unknown property %s" % property.name

	def do_set_property(self, property, value):
		if property.name == "initital-playlist-complete":
		    self.initital_playlist_complete = value
		elif property.name == "items-count":
		    self.items_count = value
		elif property.name == "ids":
		    self.ids = value
		elif property.name == "current-pos":
		    self.current_pos = value
		elif property.name == "marked":
		    self.marked = value
		elif property.name == "column-dict":
		    self.column_dict = value
		elif property.name == "change":
		    self.change = value
		elif property.name == "plfilter":
		    self.plfilter = value
		else:
		    raise AttributeError, "Unknown property %s" % property.name
	
	def __init__(self, player):
		self.player = player
		self.remove_handler = None
		#self.set_property("marked", -1)
		#self.set_property("filter", "")
		
		self.view = gtk.TreeView()
		self.view.set_enable_search(False)
		self.view.set_rules_hint(True)
		#self.view.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 0), ('text/plain', 0, 1)], gtk.gdk.ACTION_COPY)
		self.view.enable_model_drag_dest([('text/plain', 0, 0), ('text/uri-list', 0, 1)],
						gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE)
		self.view.connect('row-activated', self.on_view_row_activated)
		self.view.connect("drag-drop", self.on_view_drag_drop)
		self.view.connect("drag-data-received", self.on_view_drag_data_received)
		
		self.store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
		
		self.storefilter = self.store.filter_new()
		self.storefilter.set_visible_func(self.visible_func)
		self.selection = self.view.get_selection()
		self.selection.set_mode(gtk.SELECTION_MULTIPLE)
		
		gobject.GObject.__init__(self)
		self.initital_playlist_complete = False
		self.items_count = 0
		self.ids = [ ]
		self.current_pos = -1
		self.marked = -1
		self.column_dict = { }
		self.change = { }
		self.plfilter = ""
		
		self.window = gtk.ScrolledWindow()
		self.window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		self.window.add(self.view)
		self.view.show()
		
		
		self.connect("notify::ids", self.populate)
		self.connect("notify::initital-playlist-complete", self.attach_filter)
		self.connect("notify::items-count", self.initial_check_complete_cb)
		self.connect("notify::current-pos", self.update_marked)
		self.connect("notify::change", self.update)
		self.connect("notify::plfilter", self.refilter)
		self.connect("notify::column-dict", self.view_modify_columns)
		
	def visible_func(self, model, iter):
		udata = self.get_property("plfilter")
		try:
			title = model.get_value(iter, 2).lower()
		except AttributeError:
			title = ""
		try:
			artist =  model.get_value(iter, 3).lower()
		except AttributeError:
			artist = ""
		try:
			album = model.get_value(iter, 4).lower()
		except AttributeError:
			album = ""
		udata = udata.lower()
		if udata in title or udata in artist or udata in album:
			a = True
		else:
			a = False
		return a
		
		
	def attach_filter(self, *args):
		value = self.get_property("initital-playlist-complete")
		if value == True:
			self.view.set_model(self.storefilter)
			print "attached filter to playlist"
		else:
			self.view.set_model(None)
			print "detached filter from playlist"
			
	def populate(self, *args):
		playlist = self.get_property("ids")
		self.store.clear()
		self.set_property("initital-playlist-complete", False)
		self.set_property("items-count", 0)
		for entry in playlist:
			self.player.client.medialib_get_info(entry, self.populate_cb)
			
	def populate_cb(self, res):
		mlib_info = res.value()
		final_row = format_playlist_entry(mlib_info)
		self.store.append(final_row)
		count = self.get_property("items-count") + 1
		self.set_property("items-count", count)
		
	def initial_check_complete_cb(self, *args):
		value = self.get_property("items-count")
		target = len(self.get_property("ids"))
		complete = self.get_property("initital-playlist-complete")
		if complete == False:
			if target == value:
				self.set_property("initital-playlist-complete", True)
				print "done populating playlist"
				pos = self.get_property("current-pos")
				self.update_marked(pos)
			else:
				return
				
	def update_marked(self, *args):
		marked = self.get_property("marked")
		pos = self.get_property("current-pos")
		if marked >= 0:
			try:
				iiter = self.store.get_iter(marked)
				self.store.set_value(iiter, 0, None)
			except ValueError:
				#assume that the marked entry got removed
				self.set_property("marked", -1)
		if pos >= 0:
			iiter = self.store.get_iter(pos)
			#color = u'#ffc600'
			color = '#ffd850'
			self.store.set_value(iiter, 0, color)
			self.set_property("marked", pos)
		
	def update(self, *args):
		entry = self.get_property("change")
		action = entry['type']
		print "playlist change action:", action
		if action == 0:
			ident = entry['id']
			print "new entry with id", ident, "added"
			self.add_entry(ident)
		elif action == 1:
			pos = entry['position']
			ident = entry['id']
			print "new entry with id", ident, "inserted at position", pos
			self.insert_entry(pos, ident)
		elif action == 2:
			print "playlist shuffled, redownloading playlist"
			self.player.playlist_refresh()
		elif action == 3:
			pos = entry['position']
			print "entry at position", pos, "was removed"
			self.remove_entry(pos)
		elif action == 4:
			print "playlist cleared"
			self.clear()
		elif action == 5:
			pos = entry['position']
			newpos = entry['newposition']
			ident = entry['id']
			self.move_entry(pos, newpos, ident)
		else:
			print "unknown operation:", entry
	def add_entry(self, ident):
		self.player.client.medialib_get_info(ident, self.add_entry_cb)
		
	def add_entry_cb(self, res):
		mlib_info = res.value()
		final_row = format_playlist_entry(mlib_info)
		self.store.append(final_row)
		count = self.get_property("items-count") + 1
		self.set_property("items-count", count)
		
	def insert_entry(self, pos, ident):
		self.to_insert.append(pos)
		self.player.client.medialib_get_info(ident, self.insert_entry_cb)
		
	def insert_entry_cb(self, res):
		pos = self.to_insert[0]
		mlib_info = res.value()
		final_row = format_playlist_entry(mlib_info)
		self.store.insert(pos, final_row)
		print "entry inserted at", pos
		count = self.properties.get_property('items-count') + 1
		self.properties.update('items-count', count)
		del self.to_insert[0]
			
	def remove_entry(self, pos):
		riter = self.store.get_iter((pos))
		self.store.remove(riter)
		print "removed entry at ", pos
		count = self.get_property('items-count') - 1
		self.set_property('items-count', count)
		print "updated property"
		
	def move_entry(self, pos, newpos, ident):
		if pos < newpos:
			print "moving entry with id", ident, "at", pos, "after", newpos
			iterator = self.store.get_iter(pos)
			target_iterator = self.store.get_iter(newpos)
			self.store.move_after(iterator, target_iterator)
		elif pos > newpos:
			print "moving entry with id", ident, "at", pos, "before", newpos
			iterator = self.store.get_iter(pos)
			target_iterator = self.store.get_iter(newpos)
			self.store.move_before(iterator, target_iterator)
			
	def clear(self):
		self.store.clear()
		self.set_property('items_count', 0)
		
	def remove_selected(self, *args):
		def remove_by_reference():
			ref = self.remove_ref_list[0]
			current_path = ref.get_path()
			current_path = current_path[0]
			print "removing", current_path
			self.player.client.playlist_remove(current_path)
			del self.remove_ref_list[0]
			
		model, paths = self.selection.get_selected_rows()
		if self.remove_handler:
			if self.remove_ref_list != []:
				remove_by_reference()
				return
			else:
				self.disconnect(self.remove_handler)
				self.remove_handler = None
				return
		
		if paths == []:
			return
		self.remove_ref_list = []
		for path in paths:
			child_path = self.storefilter.convert_path_to_child_path(path)
			ref = gtk.TreeRowReference(self.store, child_path)
			self.remove_ref_list.append(ref)
			
		self.remove_handler = self.connect("notify::change", self.remove_selected)
		
		remove_by_reference()
		
	def refilter(self, *args):
		gobject.idle_add(self.storefilter.refilter)
		
	def on_view_row_activated(self, treeview, path, view_column):
		iter = self.storefilter.get_iter(path)
		iter_child = self.storefilter.convert_iter_to_child_iter(iter)
		pos = self.store.get_path(iter_child)[0]
		self.player.client.playlist_set_next(pos)
		self.player.client.playback_tickle()
		status = self.player.get_property('playback_status')
		if not status == 1:
			self.player.playback_start()
			
	def on_view_drag_drop(self, widget, drag_context, x, y, timestamp):
		#self.view.emit_stop_by_name("drag-drop")
		print ">>>drag and drop"
		print "drag_context:", drag_context
		print "x, y:", x, y
		#print "selection_data:", selection_data
		#print "info:", info
		print "time:", timestamp
		#print "data", data
		return True
		
	def on_view_drag_data_received(widget, drag_context, x, y, selection_data, info, timestamp):
		#self.view.emit_stop_by_name("drag-data-received")
		print ">>>drag data received"
		print "drag_context:", drag_context
		print "x, y:", x, y
		print "selection_data:", selection_data
		print " > data:", selection_data.data
		print "info:", info
		print "time:", timestamp
		return True
			
	def view_modify_columns(self, *args):
		new_columns = self.get_property("column_dict").keys()
		current_columns = self.view.get_columns()
		current_columns_names = [ ]
		for col in current_columns:
			name = col.get_title()
			current_columns_names.append(name)
		for col in current_columns:
			if col.get_title() in new_columns:
				continue
			elif not col.get_title() in new_columns:
				self.view.remove_column(col)
		for new_col in new_columns:
			if not new_col in current_columns_names:
				column = gtk.TreeViewColumn(new_col)
				
				pl_text_renderer = gtk.CellRendererText()
				pl_text_renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
				pl_text_renderer.set_property("width-chars", self.get_property("column_dict")[new_col])
				
				column.pack_start(pl_text_renderer, True)
				column.add_attribute(pl_text_renderer, 'text', self.COLUMN_MAP[new_col])
				column.add_attribute(pl_text_renderer, 'background', 0)
				column.set_resizable(True)
				
				self.view.append_column(column)
Antworten