From 6f17098b62c54913871c5f959aac3a44b1982faa Mon Sep 17 00:00:00 2001 From: noisymime Date: Fri, 21 Mar 2008 00:08:38 +0000 Subject: [PATCH] - More work on image row - Added default image system --- modules/music_player/backends/myth_music.py | 3 +- modules/music_player/music_object_row.py | 8 +- modules/music_player/music_objects/artist.py | 18 ++-- modules/music_player/music_objects/song.py | 19 +++-- modules/music_player/music_player.py | 12 ++- themes/Pear/music.xml | 4 + themes/Pear/music/default_cover.png | Bin 0 -> 5555 bytes ui_elements/ReflectionTexture.py | 26 +++++- ui_elements/image_frame.py | 84 +++++++++++++------ ui_elements/image_row.py | 72 ++++------------ 10 files changed, 142 insertions(+), 104 deletions(-) create mode 100755 themes/Pear/music/default_cover.png diff --git a/modules/music_player/backends/myth_music.py b/modules/music_player/backends/myth_music.py index 9fc99da..e3ebec2 100644 --- a/modules/music_player/backends/myth_music.py +++ b/modules/music_player/backends/myth_music.py @@ -15,9 +15,8 @@ class Backend: #Returns a list of artist objects def get_artists(self): - #Generate some SQL to retrieve videos that were in the final_file_list #Load the videos into the cover viewer - sql = "SELECT * FROM music_artists" + sql = "SELECT * FROM music_artists ORDER BY artist_name" if self.music_player.glossMgr.debug: print "Music Artist SQL: " + sql results = self.dbMgr.run_sql(sql) diff --git a/modules/music_player/music_object_row.py b/modules/music_player/music_object_row.py index 3bb75f1..27c346d 100644 --- a/modules/music_player/music_object_row.py +++ b/modules/music_player/music_object_row.py @@ -20,16 +20,18 @@ class MusicObjectRow(ImageRow): for i in range(start, end): object = self.objectLibrary[i] - print "loading: " + object.name + #print "loading: " + object.name pixbuf = object.get_image() time.sleep(self.music_player.sleep_time) - tmpImage = clutter.Texture() if pixbuf == object.PENDING_DOWNLOAD: + #Get the temporary image + object.get_default_image() + tmpImage = ImageFrame(None, self.image_size, use_reflection = True, quality = ImageFrame.QUALITY_FAST) #clutter.Texture() object.connect("image-found", self.set_image_cb, object, tmpImage) elif not pixbuf is None: #If we're performing this loading as a seperate thread, we need to lock the Clutter threads if as_thread: clutter.threads_enter() - tmpImage = ImageFrame(pixbuf, self.image_size, use_reflection=False) + tmpImage = ImageFrame(pixbuf, self.image_size, use_reflection=True, quality = ImageFrame.QUALITY_FAST) if as_thread: clutter.threads_leave() self.add_texture_group(tmpImage) diff --git a/modules/music_player/music_objects/artist.py b/modules/music_player/music_objects/artist.py index ef944e3..56d2424 100644 --- a/modules/music_player/music_objects/artist.py +++ b/modules/music_player/music_objects/artist.py @@ -48,8 +48,8 @@ class artist(MusicObject): try: pixbuf = gtk.gdk.pixbuf_new_from_file(filename) except gobject.GError, e: - print "Music_Player: Attempted to open file '%s', but it does not exist" % (filename) - return None + print "Music_Player: Attempted to open file '%s', but it does not exist. Using defualt image." % (filename) + return self.get_default_image() return pixbuf #If self.image is a eqaul to 'unset', means the column exists but that the image entry is blank, we should try to find one @@ -62,8 +62,12 @@ class artist(MusicObject): def get_image_from_lastFM(self, thread_data): pixbuf = self.music_player.lastFM.get_artist_image(self.name) - if not pixbuf is None: self.save_image(pixbuf) - return pixbuf + if not pixbuf is None: + self.save_image(pixbuf) + return pixbuf + else: + #We have failed to find an image + return None #Saves an image (pixbuf) to file and updates the Myth db def save_image(self, pixbuf): @@ -94,4 +98,8 @@ class artist(MusicObject): self.image = filename_short #Let off a signal to say the image is ready - self.emit("image-found") \ No newline at end of file + self.emit("image-found") + + #Returns the pixbuf of the default artist image + def get_default_image(self): + return self.music_player.default_artist_cover \ No newline at end of file diff --git a/modules/music_player/music_objects/song.py b/modules/music_player/music_objects/song.py index 5f4978f..3e016f8 100644 --- a/modules/music_player/music_objects/song.py +++ b/modules/music_player/music_objects/song.py @@ -66,12 +66,15 @@ class song: tag.link(filename) images = tag.getImages() - for img in images: - #print "Image Mine Type: " + str(img.mimeType) - data = img.imageData - loader = gtk.gdk.PixbufLoader() - loader.write(data) - loader.close() - return loader.get_pixbuf() - + for img in images: + try: + #print "Image Mine Type: " + str(img.mimeType) + data = img.imageData + loader = gtk.gdk.PixbufLoader() + loader.write(data) + loader.close() + return loader.get_pixbuf() + except gobject.GError: + if self.music_player.glossMgr.debug: + print "Music_Player: Found image in ID3 for song '%s' but could not load it" % (self.filename) diff --git a/modules/music_player/music_player.py b/modules/music_player/music_player.py index 72505eb..e5f4e23 100644 --- a/modules/music_player/music_player.py +++ b/modules/music_player/music_player.py @@ -5,7 +5,7 @@ import thread, time from modules.music_player.backends.myth_music import Backend from modules.music_player.lastFM_interface import lastFM_interface from modules.music_player.music_object_row import MusicObjectRow - +from ui_elements.image_frame import ImageFrame class Module: title = "Music" @@ -38,6 +38,9 @@ class Module: def setup_ui(self): self.menu_image = self.glossMgr.themeMgr.get_texture("music_menu_image", None, None) + + self.default_artist_cover = self.glossMgr.themeMgr.get_texture("music_default_artist_image", None, None).get_pixbuf() + #Get the images dir setting our of the DB #But also creates the setting if it doesn't already exist @@ -68,7 +71,7 @@ class Module: if (event.keyval == clutter.keysyms.Left) or (event.keyval == clutter.keysyms.Right): #calculate a period of time the loading threads should sleep for when a timline is in progress - self.sleep_time = float(MusicObjectRow.frames) / float(MusicObjectRow.fps) + #self.sleep_time = float(MusicObjectRow.frames) / float(MusicObjectRow.fps) self.artistImageRow.input_queue.input(event) #Just a little test code @@ -115,8 +118,9 @@ class Module: self.stage.add(self.tmpLabel) #The preview img - self.main_img = clutter.Texture() - self.main_img.set_position(0, 400) + self.main_img = ImageFrame(None, 300, True) #clutter.Texture() + self.main_img.set_position(30, 400) + self.main_img.set_rotation(clutter.Y_AXIS, 45, self.main_img.get_width(), 0, 0) self.main_img.show() self.stage.add(self.main_img) diff --git a/themes/Pear/music.xml b/themes/Pear/music.xml index 6f844e6..a94a9ff 100644 --- a/themes/Pear/music.xml +++ b/themes/Pear/music.xml @@ -10,4 +10,8 @@ 0 + + + music/default_cover.png + diff --git a/themes/Pear/music/default_cover.png b/themes/Pear/music/default_cover.png new file mode 100755 index 0000000000000000000000000000000000000000..1de9c14589378b20832bf61943cb32fdfb3421a2 GIT binary patch literal 5555 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 B pixbuf.get_width(): - xy_ratio = float(pixbuf.get_width()) / pixbuf.get_height() - height = img_size - width = int(img_size * xy_ratio) - x = (img_size - width)/2 - #x = int(cover_size / 2) - #x = x + (cover_size - width) - else: - xy_ratio = float(pixbuf.get_height()) / float(pixbuf.get_width()) - width = img_size - height = int(img_size * xy_ratio) - y = (img_size - height)/2 - #y = y + (cover_size - height) - pixbuf = pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR) - self.main_pic.set_pixbuf(pixbuf) - self.main_pic.show() - - #If a reflection is desired, add it on + + + #pixbuf can be None, it just means that nothing appears initially + if pixbuf is None: + self.add(self.main_pic) + + if use_reflection: + self.reflection = Texture_Reflection(self.main_pic) + self.add(self.reflection) + self.reflection.show() + else: + self.reflection = None + + return + + pixbuf = self.resize_pixbuf(pixbuf) + + self.main_pic.set_pixbuf(pixbuf) + self.main_pic.set_position(self.x, self.y) + self.main_pic.show() + if use_reflection: self.reflection = Texture_Reflection(self.main_pic) self.add(self.reflection) self.reflection.show() else: - self.reflection = None + self.reflection = None + + self.add(self.main_pic) - self.main_pic.set_position(x, y) - self.add(self.main_pic) \ No newline at end of file + def resize_pixbuf(self, pixbuf): + #New method of resizing changes size of pixbuf rather than texture.... MUCH better performance :) + (self.x, self.y) = (0, 0) + if pixbuf.get_height() > pixbuf.get_width(): + xy_ratio = float(pixbuf.get_width()) / pixbuf.get_height() + height = self.img_size + width = int(self.img_size * xy_ratio) + self.x = (self.img_size - width)/2 + #x = int(cover_size / 2) + #x = x + (cover_size - width) + else: + xy_ratio = float(pixbuf.get_height()) / float(pixbuf.get_width()) + width = self.img_size + height = int(self.img_size * xy_ratio) + self.y = (self.img_size - height)/2 + #y = y + (cover_size - height) + + #Set the conversion mode / quality + if self.quality == self.QUALITY_FAST: conversion_mode = gtk.gdk.INTERP_NEAREST #gtk.gdk.INTERP_TILES + elif self.quality == self.QUALITY_NORMAL: conversion_mode = gtk.gdk.INTERP_BILINEAR + elif self.quality == self.QUALITY_SLOW: conversion_mode = gtk.gdk.INTERP_HYPER + pixbuf = pixbuf.scale_simple(width, height, conversion_mode) + + return pixbuf + + def set_pixbuf(self, pixbuf): + pixbuf = self.resize_pixbuf(pixbuf) + self.main_pic.set_pixbuf(pixbuf) + self.main_pic.set_position(self.x, self.y) + self.main_pic.show() \ No newline at end of file diff --git a/ui_elements/image_row.py b/ui_elements/image_row.py index 2914102..a4c90da 100644 --- a/ui_elements/image_row.py +++ b/ui_elements/image_row.py @@ -14,7 +14,7 @@ class ImageRow(clutter.Group): scaleFactor = 1.4 inactiveOpacity = 150 - images_size_percent = 0.90 #This is the percentage of the total group size that the covers will take + images_size_percent = 1 #0.90 #This is the percentage of the total group size that the covers will take fps = 35 frames = 25 @@ -67,6 +67,7 @@ class ImageRow(clutter.Group): self.currentSelection = 0 self.add(self.images_group_clip) + #self.add(self.images_group) covers_x = int(width * (1-self.images_size_percent)/2) covers_y = int(height * (1-self.images_size_percent)/2) #self.images_group.set_position(covers_x, covers_y) @@ -89,7 +90,7 @@ class ImageRow(clutter.Group): tempGroup.set_position(x, y) #If we're past the maximum rows, make the pics invistible - if self.num_images > (self.num_columns-self.center): + if self.num_images > (self.num_columns-self.center-1): tempGroup.set_opacity(0) else: self.images_group.add(tempGroup) @@ -109,22 +110,26 @@ class ImageRow(clutter.Group): #Do the stuff for edge cases (ie the textures coming in and going out) if incomingItem > outgoingItem: direction = self.DIRECTION_RIGHT - edge_texture_incoming_no = outgoingItem + (self.center-1) - edge_texture_outgoing_no = outgoingItem - (self.center) + edge_texture_incoming_no = outgoingItem + (self.center) + edge_texture_outgoing_no = outgoingItem - (self.center-1) else: direction = self.DIRECTION_LEFT edge_texture_incoming_no = outgoingItem - (self.center) edge_texture_outgoing_no = outgoingItem + (self.center-1) + + if edge_texture_incoming_no < len(self.textureLibrary): + edge_texture_incoming = self.textureLibrary[edge_texture_incoming_no] + self.images_group.add(edge_texture_incoming) - edge_texture_incoming = self.textureLibrary[edge_texture_incoming_no] + self.behaviourEdgeIncomingOpacity = clutter.BehaviourOpacity(opacity_start=0, opacity_end=self.inactiveOpacity, alpha=alpha) + self.behaviourEdgeIncomingOpacity.apply(edge_texture_incoming) if edge_texture_outgoing_no >= 0: edge_texture_outgoing = self.textureLibrary[edge_texture_outgoing_no] self.timeline.connect('completed', self.remove_item, edge_texture_outgoing_no) - self.images_group.add(edge_texture_incoming) - self.behaviourEdgeIncomingOpacity = clutter.BehaviourOpacity(opacity_start=0, opacity_end=self.inactiveOpacity, alpha=alpha) - self.behaviourEdgeIncomingOpacity.apply(edge_texture_incoming) - + self.behaviourEdgeOutgoingOpacity = clutter.BehaviourOpacity(opacity_start=self.inactiveOpacity, opacity_end=0, alpha=alpha) + self.behaviourEdgeOutgoingOpacity.apply(edge_texture_outgoing) + #Now do the stuff for selecting the image in the middle self.behaviourNew_scale = clutter.BehaviourScale(x_scale_start=1, y_scale_start=1, x_scale_end=self.scaleFactor, y_scale_end=self.scaleFactor, alpha=alpha) #clutter.GRAVITY_CENTER) @@ -197,55 +202,8 @@ class ImageRow(clutter.Group): self.timeline.start() - #This moves the visible row of covers left and right - def rollViewer(self, direction, timeline): - if direction == self.DIRECTION_LEFT: - new_y = self.images_group.get_y() - self.image_size - self.max_visible_column += 1 - self.min_visible_column += 1 - - #Define the row of image that now needs to disapear / appear - outgoing = self.min_visible_column - 1 - incoming = self.max_visible_column - 1 - - #Quick check to make sure that max_incoming isn't greater than the max number of images (This occurs when the final row is incomplete) - if incoming > self.num_covers: - return None - - elif direction == self.DIRECTION_RIGHT: - new_y = self.images_group.get_y() + self.image_size - self.max_visible_column -= 1 - self.min_visible_column -= 1 - - #Define the row of covers that now needs to disapear / appear - outgoing = self.min_visible_column + 1 - incoming = self.max_visible_column + 1 - - #Quick check to make sure that max_outgoing isn't greater than the max number of images (This occurs when the final row is incomplete) - if outgoing > self.num_images: - return None - - #Need to add the new image to the group - self.images_group.add(self.textureLibrary[incoming]) - #And set the outgoing row to remove after the timeline finishes - self.timeline.connect('completed', self.removeItem, outgoing) - - - knots = (\ - (self.images_group.get_x(), self.images_group.get_y()),\ - (self.images_group.get_x(), new_y) \ - ) - - alpha = clutter.Alpha(timeline, clutter.ramp_inc_func) - self.behaviour_path = clutter.BehaviourPath(alpha, knots) - self.behaviour_incoming = clutter.BehaviourOpacity(opacity_start=0, opacity_end=self.inactiveOpacity, alpha=alpha) - self.behaviour_outgoing = clutter.BehaviourOpacity(opacity_start=self.inactiveOpacity, opacity_end=0, alpha=alpha) - - self.behaviour_path.apply(self.images_group) - self.behaviour_outgoing.apply(self.textureLibrary[incoming]) - self.behaviour_incoming.apply(self.textureLibrary[outgoing]) - def remove_item(self, timeline = None, itemNo = None): + self.textureLibrary[itemNo].set_opacity(0) self.images_group.remove(self.textureLibrary[itemNo])