class ANoise: """Control the sound indicator""" def __init__(self): # These 3 are needed GObject.threads_init() DBusGMainLoop(set_as_default=True) Gst.init(None) GLib.set_application_name(_('Ambient Noise')) self.sound_menu = SoundMenuControls('Ambient Noise', 'anoise') self.noise = Noise() self.win_preferences = Preferences(self) try: self.keybinder = Keybinder self.keybinder.init() if self.keybinder.bind('AudioPlay', self._sound_menu_play_toggle, None): self.keybinder.bind('AudioStop', self._sound_menu_stop, None) self.keybinder.bind('AudioPause', self._sound_menu_pause, None) self.keybinder.bind('AudioNext', self._sound_menu_next, None) self.keybinder.bind('AudioPrev', self._sound_menu_previous, None) else: self.keybinder = None except (ValueError, ImportError): self.keybinder = None # Need in a few DE try: self.window = GUI(self) except: pass self.player = Gst.ElementFactory.make(PLAYBIN, "player") self.player.connect("about-to-finish", self._loop) dummy_i18n = (_("Coffee Shop"), _("Fire"), _("Forest"), _("Night"), _("Rain"), _("River"), _("Sea"), _("Storm"), _("Wind") ) # Need i18n # Overwrite libraty methods self.sound_menu._sound_menu_is_playing = self._sound_menu_is_playing self.sound_menu._sound_menu_play = self._sound_menu_play self.sound_menu._sound_menu_pause = self._sound_menu_pause self.sound_menu._sound_menu_next = self._sound_menu_next self.sound_menu._sound_menu_previous = self._sound_menu_previous self.sound_menu._sound_menu_raise = self._sound_menu_raise self.sound_menu._sound_menu_play_toggle = self._sound_menu_play_toggle # Autostart when click on sound indicator icon threading.Timer(1, self._sound_menu_play).start() def _loop(self, message): """Start again the same sound in the EOS""" self.player.set_property('uri', self.noise.get_current_filename_uri()) def _sound_menu_is_playing(self): """Called in the first click""" return self.is_playing def _sound_menu_play_toggle(self, keypress=None, data=None): """Play toggle, media keys have an expectation that play is a toggle""" if self.is_playing: self._sound_menu_pause('AudioPause') else: self._sound_menu_play('AudioPlay') def _sound_menu_play(self, keypress=None, data=None): """Play""" self.is_playing = True # Need to overwrite this for an issue with autstart self.sound_menu.song_changed( self.noise.get_current_index(), '', '', self.noise.get_name(), urllib.parse.quote(self.noise.get_icon_uri(), ':/'), urllib.parse.quote(self.noise.get_current_filename_uri(), ':/')) self.player.set_property('uri', self.noise.get_current_filename_uri()) self.player.set_state(Gst.State.PLAYING) self.sound_menu.signal_playing() def _sound_menu_stop(self, keypress=None, data=None): """Stop, different from pause in that it sets the pointer of the track to the start again""" self.is_playing = False self.player.set_state( Gst.State.READY) # assuming this is akin to stop? self.sound_menu.signal_stopped() def _sound_menu_pause(self, keypress=None, data=None): """Pause""" self.is_playing = False # Need to overwrite this for an issue with autstart self.player.set_state(Gst.State.PAUSED) self.sound_menu.signal_paused() def _set_new_play(self, what): """Next or Previous""" # Get Next/Previous if what == 'next': self.noise.set_next() if what == 'previous': self.noise.set_previous() # From pause? self.player.set_state(Gst.State.READY) # Play if self.is_playing: self._sound_menu_play() else: self.sound_menu.song_changed( self.noise.get_current_index(), '', '', self.noise.get_name(), urllib.parse.quote(self.noise.get_icon_uri(), ':/'), urllib.parse.quote(self.noise.get_current_filename_uri(), ':/')) def _sound_menu_previous(self, keypress=None, data=None): """Previous""" self._set_new_play('previous') def _sound_menu_next(self, keypress=None, data=None): """Next""" self._set_new_play('next') def _sound_menu_raise(self): """Click on player""" self.win_preferences.show() def set_timer(self, enable, seconds): if enable: self.timer = threading.Timer(seconds, self._set_future_pause) self.timer.start() else: self.timer.cancel() def _set_future_pause(self): self.win_preferences.set_show_timer() self._sound_menu_pause()
class GUI: """This will be for DE as MATE 14.10+ which hasn't sound indicator with Gtk3""" def __init__(self, player): self.player = player self.win_preferences = Preferences(self) builder = Gtk.Builder() builder.add_from_file('/usr/share/anoise/anoise.ui') self.win_icon = builder.get_object('icon_noise') self.btn_play = builder.get_object('btn_play') self.lbl_title = builder.get_object('lbl_title') builder.connect_signals(self) self.window = builder.get_object('main_win') self.window.show_all() self._set_window_icon() def _set_window_icon(self): try: self.window.set_icon_from_file( self.player.noise.get_icon().replace('file://', '')) self.win_icon.set_from_file(self.player.noise.get_icon().replace( 'file://', '')) except: self.window.set_icon_from_file( '/usr/share/icons/hicolor/48x48/apps/anoise.png') self.win_icon.set_from_file( '/usr/share/icons/hicolor/48x48/apps/anoise.png') self.lbl_title.set_text(self.player.noise.get_name()) def on_btn_previous_clicked(self, widget, data=None): self.player._set_new_play('previous') image = Gtk.Image(stock=Gtk.STOCK_MEDIA_PAUSE) self.btn_play.set_image(image) self._set_window_icon() def on_btn_next_clicked(self, widget, data=None): self.player._set_new_play('next') image = Gtk.Image(stock=Gtk.STOCK_MEDIA_PAUSE) self.btn_play.set_image(image) self._set_window_icon() def _play(self): self.player.is_playing = True self.player._sound_menu_play() image = Gtk.Image(stock=Gtk.STOCK_MEDIA_PAUSE) self.btn_play.set_image(image) def _pause(self): if self.player.is_playing: self.player.is_playing = False self.player._sound_menu_pause() image = Gtk.Image(stock=Gtk.STOCK_MEDIA_PLAY) self.btn_play.set_image(image) def on_btn_play_pause_clicked(self, widget, data=None): if self.player.is_playing: self._pause() else: self._play() def on_menu_preferences_activate(self, widget, data=None): self.win_preferences.show() def set_timer(self, enable, seconds): if enable: self.timer = threading.Timer(seconds, self._set_future_pause) self.timer.start() else: self.timer.cancel() def _set_future_pause(self): self.win_preferences.set_show_timer() self._pause() def on_menu_about_activate(self, widget, data=None): webbrowser.open_new('http://anoise.tuxfamily.org') def on_main_win_delete_event(self, widget, data=None): try: self.timer.cancel() except: pass Gtk.main_quit()
class ANoise: """Control the sound indicator""" def __init__(self): # These 3 are need GObject.threads_init() DBusGMainLoop(set_as_default=True) Gst.init(None) self.sound_menu = SoundMenuControls('anoise') self.noise = Noise() self.win_preferences = Preferences(self) # Need in a few DE try: self.window = GUI(self) except: pass self.player = Gst.ElementFactory.make(PLAYBIN, "player") self.player.connect("about-to-finish", self._loop) self.player.set_property('uri', self.noise.get_current_filename()) self.is_playing = True dummy_i18n = (_("Coffee Shop"), _("Fire"), _("Forest"), _("Night"), _("Rain"), _("River"), _("Sea"), _("Storm"), _("Wind")) # Need i18n # Overwrite libraty methods self.sound_menu._sound_menu_is_playing = self._sound_menu_is_playing self.sound_menu._sound_menu_play = self._sound_menu_play self.sound_menu._sound_menu_pause = self._sound_menu_pause self.sound_menu._sound_menu_next = self._sound_menu_next self.sound_menu._sound_menu_previous = self._sound_menu_previous self.sound_menu._sound_menu_raise = self._sound_menu_raise # Autostart when click on sound indicator icon threading.Timer(2, self._sound_menu_play).start() def _loop(self, message): """Start again the same sound in the EOS""" self.player.set_property('uri', self.noise.get_current_filename()) def _sound_menu_is_playing(self): """Called in the first click""" return self.is_playing def _sound_menu_play(self): """Play""" self.is_playing = True # Need to overwrite this for an issue with autstart self.sound_menu.song_changed('', '', self.noise.get_name(), self.noise.get_icon()) self.player.set_state(Gst.State.PLAYING) self.sound_menu.signal_playing() def _sound_menu_pause(self): """Pause""" self.is_playing = False # Need to overwrite this for an issue with autstart self.player.set_state(Gst.State.PAUSED) self.sound_menu.signal_paused() def _set_new_play(self, what): """Next or Previous""" self.noise.refresh_all_ogg() # Get Next/Previous if what == 'next': self.noise.set_next() if what == 'previous': self.noise.set_previous() # From pause? self.player.set_state(Gst.State.READY) if not self.is_playing: self.is_playing = True # Set new sound self.player.set_property('uri', self.noise.get_current_filename()) # Play self._sound_menu_play() def _sound_menu_previous(self): """Previous""" self._set_new_play('previous') def _sound_menu_next(self): """Next""" self._set_new_play('next') def _sound_menu_raise(self): """Click on player""" self.win_preferences.show() def set_timer(self, enable, seconds): if enable: self.timer = threading.Timer(seconds, self._set_future_pause) self.timer.start() else: self.timer.cancel() def _set_future_pause(self): self.win_preferences.set_show_timer() self._sound_menu_pause()
class Indicator: # ----------------------------------------------------------------------- # def __init__(self, player): # apparently appindicator will not quit on Ctrl-C by default. fix # bellow allows it to do so in Ctrl-C run the default action kernel # action which allows indicator to exit signal.signal(signal.SIGINT, signal.SIG_DFL) # expose the passing player to tht class self._player = player # remove registration to dbus, disabling MPRIS integration, mainly this # is done because current anoise MPRIS integration does not notify the # GUI element of play/pause/next/forward changes internally an attempt # at dbus manager that listens for anoise mpris notification fails due # to double signaling and handling of multiple MPRIS subscribed clients # and inability to distinguish which come from anoise self._player.sound_menu.remove_from_connection() #: DEBUG SETTING, used during development #: hide window ui, if it's the GUI class (rather then Indicator class) # if self._player.window.__class__.__name__ == 'GUI': # self._player.window.window.hide() # build the preferences window self._preferences_window = Preferences(self) # expose the default gtk settings self._gtk_settings = Gtk.Settings.get_default() # expose the default icon theme self._default_icon_theme = Gtk.IconTheme.get_default() # expose "hicolor" theme as fallback theme self._fallback_icon_theme = Gtk.IconTheme() self._fallback_icon_theme.set_custom_theme('hicolor') # expose found appindicator and appindicator-pause icons self._appindicator_icon, self._appindicator_icon_pause = \ self._get_indicator_icons() # build the appindicator self._appindicator, builder = self._make_appindicator() # expose the play/pause menu item to the class self._menuitem_play = builder.get_object('play_pause_toggle') # expose now playing menu item self._menuitem_now_playing = builder.get_object('now_playing') # expose now playing image self._image_now_playing = builder.get_object('image_now_playing') # expose play image self._image_play = builder.get_object('image_play') # expose pause image self._image_pause = builder.get_object('image_pause') # disable startup autoplay (ugh, terrible approach) # runs before indicator is made visible to show the "paused" icon self._disable_startup_autoplay() # set the indicator status to active self._appindicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) # update the noise icon in the ui and the appindicator self._update_now_playing() # ----------------------------------------------------------------------- # def set_timer(self, enable, seconds): # method is run by preferences window code, which is why it's public # it's strange that this code is run in the UI code. # if window ui and appindicator will both install you will end up with # two timers, so seems like runing both "ui"s is not a good idea # exclusive, unless preferences can be a singleton if enable: self.timer = threading.Timer(seconds, self._set_future_pause) self.timer.start() else: self.timer.cancel() # ----------------------------------------------------------------------- # def _disable_startup_autoplay(self): """ Disable auto play on aplication startup. This is done by patching the self._player._sound_menu_play with function that pauses the player insteady of playing it, and the restores the original function after it was ran once. Unfortunately this is the only way to do this at this point. """ # set all the gui statuses to "pause" self._pause() # preserve the original function original_func = self._player._sound_menu_play # create a patched function which pauses the play when run the FIRST # time and then restores the original _sound_menu_play to the player # object (sad, should not need to patch) def _sound_menu_play(*args, **kwargs): # pause self._player._sound_menu_pause() self._player._sound_menu_play = original_func # now patch the play function with our patched function self._player._sound_menu_play = _sound_menu_play # ----------------------------------------------------------------------- # def _make_appindicator(self): """ Return constructed AppIndicator and it's menu. Also return the menu builder so we can update few of it's items """ # get full glade file. assumption here is that the ui file in the same # folder as this script ui_file_path = os.path.join( os.path.realpath(os.path.dirname(__file__)), 'appindicator.ui') # force showing of images despite whatever the global setting is self._gtk_settings.props.gtk_button_images = True # build the apindicator menu from the glade file builder = Gtk.Builder() builder.add_from_file(ui_file_path) # create the appindicator appindicator = AppIndicator3.Indicator.new( APPINDICATOR_ID, self._appindicator_icon, AppIndicator3.IndicatorCategory.APPLICATION_STATUS) # get the appindicator menu menu = builder.get_object('appindicator_menu') # setup the signals for the appindicator menu items builder.connect_signals(self) # get the play/pause menu iterm menuitem_play = builder.get_object('play_pause_toggle') # setup to trigger play/pause menu action on appindicator middle click appindicator.set_secondary_activate_target(menuitem_play) # set all menu items visible menu.show_all() # attach to the menu to the appindicator appindicator.set_menu(menu) return appindicator, builder # ----------------------------------------------------------------------- # def _get_indicator_icons(self): icons = [] # for over both default and fallback theme to get icons for theme in [self._default_icon_theme, self._fallback_icon_theme]: # try to find both regular and pause icons for icon_name in [APPINDICATOR_ICON, APPINDICATOR_ICON_PAUSE]: # break out of the loop if we failed to find any of the icons # and set the icon collection to an empty list if theme.has_icon(icon_name) is True: icon_info = theme.lookup_icon(icon_name, Gtk.IconSize.MENU, 0) # get icon file path icons.append(icon_info.get_filename()) else: icons = [] break # if we found both icons break out if len(icons) == 2 or all(icons) is True: break # if we could not find 2 icons fallback to very generic icons if len(icons) != 2 or all(icons) is False: icons = APPINDICATOR_ICONS_FALLBACK return icons # ----------------------------------------------------------------------- # def _on_toggle_play_pause_activate(self, widget): if self._player.is_playing: self._player._sound_menu_pause() self._pause() else: self._player._sound_menu_play() self._play() # ----------------------------------------------------------------------- # def _on_next_activate(self, widget): # tell the player to play next track self._player._set_new_play('next') # update noise status self._update_now_playing() # ----------------------------------------------------------------------- # def _on_previous_activate(self, widget): # tell the player to play track self._player._set_new_play('previous') # update noise status self._update_now_playing() # ----------------------------------------------------------------------- # def _on_preferences_window_show_activate(self, widget): self._preferences_window.show() # ----------------------------------------------------------------------- # def _on_about_activate(self, widget): # open default web browser to the homepage webbrowser.open_new('http://anoise.tuxfamily.org') # ----------------------------------------------------------------------- # def _on_quit_activate(self, widget): # try to cancel the timer catching all (really?) exceptions try: self.timer.cancel() except Exception: pass # tell gtk main loop to quit Gtk.main_quit() # ----------------------------------------------------------------------- # def _update_now_playing(self): # try to get the noise icon file, otherwise fallback to the default # note: it does not throw a specific error we can test for so # we are testing for Exception try: # get the icon file name for the existing noise icon_file = self._player.noise.get_icon().replace('file://', '') fallback_icon_name = None except Exception: # retrieve the default application icon from the icon as a pixbuf icon_file = APPINDICATOR_ICON fallback_icon_name = PLAYING_NOW_FALLBACK_ICON # get the now playing noise now_playing = self._player.noise.get_name() # form "Not Playing: <noise>" string for the indicator new_label = "Now Playing: %s" % now_playing # update the indicator now playing label to the noise name self._menuitem_now_playing.set_label(new_label) # update the now playing menu icon # # if fallback icon name is not set then we set the found noise icon # otherwise we set the set the image to the fallback icons if fallback_icon_name is None: self._image_now_playing.set_from_file(icon_file) else: self._image_now_playing.set_from_icon_name(fallback_icon_name, Gtk.IconSize.MENU) # update the now playing menu item with the now playing image self._menuitem_now_playing.set_image(self._image_now_playing) # ----------------------------------------------------------------------- # def _play(self): # tell player to play self._menuitem_play.set_label("P_ause") self._menuitem_play.set_image(self._image_pause) self._appindicator.set_icon(self._appindicator_icon) self._update_now_playing() # ----------------------------------------------------------------------- # def _pause(self): # pause the player self._menuitem_play.set_label("_Play") self._menuitem_play.set_image(self._image_play) self._appindicator.set_icon(self._appindicator_icon_pause) self._update_now_playing() # ----------------------------------------------------------------------- # def _set_future_pause(self): self._preferences_window.set_show_timer() self._player._sound_menu_pause() self._pause()