[gnome-music/wip/jfelder/tracker3] Add NotificationManager
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/tracker3] Add NotificationManager
- Date: Mon, 24 Aug 2020 22:07:14 +0000 (UTC)
commit af382c5d68d06ab0c03440d828caac37fed80c7f
Author: Marinus Schraal <mschraal gnome org>
Date: Mon Aug 24 23:57:30 2020 +0200
Add NotificationManager
A managing class for notifications, thin wrapper around the notification
widgets for now.
Currently implemented to handle an async issue with flatpak:
Traceback (most recent call last):
File "/app/lib/python3.8/site-packages/gnomemusic/coregrilo.py", line 154, in _on_source_added
new_wrapper = GrlTrackerWrapper(
File "/app/lib/python3.8/site-packages/gnomemusic/grilowrappers/grltrackerwrapper.py", line 107, in
__init__
self._initial_songs_fill()
File "/app/lib/python3.8/site-packages/gnomemusic/grilowrappers/grltrackerwrapper.py", line 409, in
_initial_songs_fill
self._window.notifications_popup.push_loading()
AttributeError: 'NoneType' object has no attribute 'notifications_popup'
_window might not be available yet, NotificationManager does an
availability check before sending the notification.
gnomemusic/application.py | 13 ++++++
gnomemusic/grilowrappers/grltrackerplaylists.py | 33 ++++++-------
gnomemusic/grilowrappers/grltrackerwrapper.py | 50 ++++++++++----------
gnomemusic/notificationmanager.py | 61 +++++++++++++++++++++++++
4 files changed, 116 insertions(+), 41 deletions(-)
---
diff --git a/gnomemusic/application.py b/gnomemusic/application.py
index e917672a..c92a6aa1 100644
--- a/gnomemusic/application.py
+++ b/gnomemusic/application.py
@@ -40,6 +40,7 @@ from gnomemusic.coreselection import CoreSelection
from gnomemusic.inhibitsuspend import InhibitSuspend
from gnomemusic.mpris import MPRIS
from gnomemusic.musiclogger import MusicLogger
+from gnomemusic.notificationmanager import NotificationManager
from gnomemusic.pauseonsuspend import PauseOnSuspend
from gnomemusic.player import Player
from gnomemusic.scrobbler import LastFmScrobbler
@@ -66,6 +67,7 @@ class Application(Gtk.Application):
self._log = MusicLogger()
self._search = Search()
+ self._notificationmanager = NotificationManager(self)
self._coreselection = CoreSelection()
self._coremodel = CoreModel(self)
# Order is important: CoreGrilo initializes the Grilo sources,
@@ -175,6 +177,16 @@ class Application(Gtk.Application):
"""
return self._search
+ @GObject.Property(
+ type=NotificationManager, flags=GObject.ParamFlags.READABLE)
+ def notificationmanager(self):
+ """Get notification manager
+
+ :returns: notification manager
+ :rtype: NotificationManager
+ """
+ return self._notificationmanager
+
def _set_actions(self):
action_entries = [
('about', self._about, None),
@@ -216,6 +228,7 @@ class Application(Gtk.Application):
def do_activate(self):
if not self._window:
self._window = Window(self)
+ self.notify("window")
self._window.set_default_icon_name(self.props.application_id)
if self.props.application_id == "org.gnome.Music.Devel":
self._window.get_style_context().add_class('devel')
diff --git a/gnomemusic/grilowrappers/grltrackerplaylists.py b/gnomemusic/grilowrappers/grltrackerplaylists.py
index c7854dfc..b5890418 100644
--- a/gnomemusic/grilowrappers/grltrackerplaylists.py
+++ b/gnomemusic/grilowrappers/grltrackerplaylists.py
@@ -76,6 +76,7 @@ class GrlTrackerPlaylists(GObject.GObject):
self._songs_hash = songs_hash
self._tracker = tracker_wrapper.props.local_db
self._tracker_wrapper = tracker_wrapper
+ self._notificationmanager = application.props.notificationmanager
self._window = application.props.window
self._user_model_filter.set_filter_func(self._user_playlists_filter)
@@ -105,7 +106,7 @@ class GrlTrackerPlaylists(GObject.GObject):
for playlist in smart_playlists.values():
self._model.append(playlist)
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
SELECT DISTINCT
%(media_type)s AS ?type
@@ -130,10 +131,10 @@ class GrlTrackerPlaylists(GObject.GObject):
self, source, op_id, media, remaining, data=None, error=None):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
playlist = Playlist(
@@ -185,9 +186,9 @@ class GrlTrackerPlaylists(GObject.GObject):
break
self._model_filter.set_filter_func(self._playlists_filter)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
DELETE {
?playlist a rdfs:Resource .
@@ -234,7 +235,7 @@ class GrlTrackerPlaylists(GObject.GObject):
query, self.METADATA_KEYS, options, self._add_user_playlist,
callback)
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
INSERT {
_:playlist a nmm:Playlist ;
@@ -325,7 +326,7 @@ class Playlist(GObject.GObject):
self._songs_hash = songs_hash
self._tracker = tracker_wrapper.props.local_db
self._tracker_wrapper = tracker_wrapper
- self._window = application.props.window
+ self._notificationmanager = application.props.notificationmanager
self._fast_options = Grl.OperationOptions()
self._fast_options.set_resolution_flags(
@@ -347,7 +348,7 @@ class Playlist(GObject.GObject):
self._model = value
def _populate_model(self):
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
SELECT
@@ -404,7 +405,7 @@ class Playlist(GObject.GObject):
if not media:
self.props.count = self._model.get_n_items()
self.emit("playlist-loaded")
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
coresong = CoreSong(self._application, media)
@@ -454,7 +455,7 @@ class Playlist(GObject.GObject):
:param str new_name: new playlist name
"""
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
def update_cb(conn, res, data):
try:
@@ -466,7 +467,7 @@ class Playlist(GObject.GObject):
else:
self._title = new_name
finally:
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
self.thaw_notify()
query = """
@@ -520,9 +521,9 @@ class Playlist(GObject.GObject):
def update_cb(conn, res, data):
# FIXME: Check for failure.
conn.update_finish(res)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
INSERT OR REPLACE {
?entry nfo:listPosition ?position .
@@ -746,18 +747,18 @@ class SmartPlaylist(Playlist):
if self._model is None:
self._model = Gio.ListStore.new(CoreSong)
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
def _add_to_model(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
self.emit("playlist-loaded")
return
if not media:
self.props.count = self._model.get_n_items()
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
self.emit("playlist-loaded")
return
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index 83fc41a8..338d17a9 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -91,7 +91,7 @@ class GrlTrackerWrapper(GObject.GObject):
self._content_changed_timeout = None
self._tracker_playlists = None
self._tracker_wrapper = tracker_wrapper
- self._window = application.props.window
+ self._notificationmanager = application.props.notificationmanager
self._song_search_tracker = Gfm.FilterListModel.new(self._songs_model)
self._song_search_tracker.set_filter_func(lambda a: False)
@@ -406,19 +406,19 @@ class GrlTrackerWrapper(GObject.GObject):
options, _update_changed_media)
def _initial_songs_fill(self):
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
songs_added = []
def _add_to_model(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
self._songs_model.splice(
self._songs_model.get_n_items(), 0, songs_added)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
# Initialize the playlists subwrapper after the initial
# songs model fill, the playlists expect a filled songs
@@ -483,19 +483,19 @@ class GrlTrackerWrapper(GObject.GObject):
query, self.METADATA_KEYS, options, _add_to_model)
def _initial_albums_fill(self):
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
albums_added = []
def _add_to_albums_model(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
self._albums_model.splice(
self._albums_model.get_n_items(), 0, albums_added)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
album = CoreAlbum(self._application, media)
@@ -552,19 +552,19 @@ class GrlTrackerWrapper(GObject.GObject):
query, self.METADATA_KEYS, options, _add_to_albums_model)
def _initial_artists_fill(self):
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
artists_added = []
def _add_to_artists_model(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
self._artists_model.splice(
self._artists_model.get_n_items(), 0, artists_added)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
artist = CoreArtist(self._application, media)
@@ -618,7 +618,7 @@ class GrlTrackerWrapper(GObject.GObject):
:param Grl.Media media: The media with the artist id
:param Gfm.FilterListModel model: The model to fill
"""
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
artist_id = media.get_id()
query = """
@@ -659,12 +659,12 @@ class GrlTrackerWrapper(GObject.GObject):
def query_cb(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
model.set_filter_func(albums_filter, albums)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
albums.append(media)
@@ -686,7 +686,7 @@ class GrlTrackerWrapper(GObject.GObject):
:param Grl.Media media: The media with the album id
:param Gfm.SortListModel disc_model: The model to fill
"""
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
album_id = media.get_id()
query = """
@@ -720,11 +720,11 @@ class GrlTrackerWrapper(GObject.GObject):
def _disc_nr_cb(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
disc_nr = media.get_album_disc_number()
@@ -811,7 +811,7 @@ class GrlTrackerWrapper(GObject.GObject):
GLib.utf8_casefold(text, -1), -1, GLib.NormalizeMode.NFKD))
# Artist search
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
SELECT
@@ -872,12 +872,12 @@ class GrlTrackerWrapper(GObject.GObject):
def artist_search_cb(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
self._artist_search_model.set_filter_func(artist_filter)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
artist_filter_ids.append(media.get_id())
@@ -887,7 +887,7 @@ class GrlTrackerWrapper(GObject.GObject):
query, self.METADATA_KEYS, options, artist_search_cb)
# Album search
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
SELECT
@@ -940,12 +940,12 @@ class GrlTrackerWrapper(GObject.GObject):
def albums_search_cb(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
self._album_search_model.set_filter_func(album_filter)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
album_filter_ids.append(media.get_id())
@@ -955,7 +955,7 @@ class GrlTrackerWrapper(GObject.GObject):
query, self.METADATA_KEYS, options, albums_search_cb)
# Song search
- self._window.notifications_popup.push_loading()
+ self._notificationmanager.push_loading()
query = """
SELECT
@@ -1013,12 +1013,12 @@ class GrlTrackerWrapper(GObject.GObject):
def songs_search_cb(source, op_id, media, remaining, error):
if error:
self._log.warning("Error: {}".format(error))
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
if not media:
self._song_search_tracker.set_filter_func(songs_filter)
- self._window.notifications_popup.pop_loading()
+ self._notificationmanager.pop_loading()
return
filter_ids.append(media.get_id())
diff --git a/gnomemusic/notificationmanager.py b/gnomemusic/notificationmanager.py
new file mode 100644
index 00000000..6ffee620
--- /dev/null
+++ b/gnomemusic/notificationmanager.py
@@ -0,0 +1,61 @@
+# Copyright 2020 The GNOME Music Developers
+#
+# GNOME Music is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GNOME Music is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The GNOME Music authors hereby grant permission for non-GPL compatible
+# GStreamer plugins to be used and distributed together with GStreamer
+# and GNOME Music. This permission is above and beyond the permissions
+# granted by the GPL license by which GNOME Music is covered. If you
+# modify this code, you may extend this exception to your version of the
+# code, but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version.
+
+from gi.repository import GObject
+
+
+class NotificationManager(GObject.Object):
+ """Managing wrapper around the notification widgets
+ """
+
+ def __init__(self, application):
+ """Initialize the notification manager
+
+ :param Application application: The Application instance
+ """
+ super().__init__()
+
+ self._application = application
+ self._pushed = False
+ self._window = application.props.window
+
+ if self._window is None:
+ application.connect(
+ "notify::window", self._on_window_changed)
+
+ def _on_window_changed(self, klass, value):
+ self._window = self._application.props.window
+
+ def push_loading(self):
+ """Push a loading notifcation."""
+ if self._window:
+ # This makes sure push/pop are in sync when starting.
+ self._pushed = True
+ self._window.notifications_popup.push_loading()
+
+ def pop_loading(self):
+ """Pop a loading notification."""
+ if (self._window
+ and self._pushed):
+ self._window.notifications_popup.pop_loading()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]