Meine pygtk Anwendung leakt memory

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

Meine pygtk Anwendung leakt memory

Beitragvon Vortex » 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:

Beitragvon Vortex » 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)

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder