def test_on_first_map(self): w = Window() calls = [] def foo(*args): calls.append(args) on_first_map(w, foo, 1) w.show() self.assertEqual(calls, [(1,)]) on_first_map(w, foo, 2) self.assertEqual(calls, [(1,), (2,)]) w.destroy()
def test_on_first_map(self): w = Window() calls = [] def foo(*args): calls.append(args) on_first_map(w, foo, 1) w.show() self.assertEqual(calls, [(1, )]) on_first_map(w, foo, 2) self.assertEqual(calls, [(1, ), (2, )]) w.destroy()
def __init__(self, library, player, headless=False, restore_cb=None): super(QuodLibetWindow, self).__init__(dialog=False) self.last_dir = get_home_dir() self.__destroyed = False self.__update_title(player) self.set_default_size(550, 450) main_box = Gtk.VBox() self.add(main_box) # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, True) keyval, mod = Gtk.accelerator_parse("<control><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # dbus app menu # Unity puts the app menu next to our menu bar. Since it only contains # menu items also available in the menu bar itself, don't add it. if not util.is_unity(): AppMenu(self, ui.get_action_groups()[0]) # custom accel map accel_fn = os.path.join(quodlibet.get_user_dir(), "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.show_all() self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after('drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = SongListScroller( ui.get_widget("/Menu/View/SongList")) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander(ui.get_widget("/Menu/View/Queue"), library, player) self.playlist = PlaylistMux(player, self.qexpander.model, self.songlist.model) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Align(bottom=3) main_box.pack_start(self.__browserbox, True, True, 0) statusbox = StatusBarBox(self.songlist.model, player) self.order = statusbox.order self.repeat = statusbox.repeat self.statusbar = statusbox.statusbar main_box.pack_start(Align(statusbox, border=3, top=-3, right=3), False, True, 0) self.songpane = ConfigRVPaned("memory", "queue_position", 0.75) self.songpane.pack1(self.song_scroller, resize=True, shrink=False) self.songpane.pack2(self.qexpander, resize=True, shrink=False) self.__handle_position = self.songpane.get_property("position") def songpane_button_press_cb(pane, event): """If we start to drag the pane handle while the queue expander is unexpanded, expand it and move the handle to the bottom, so we can 'drag' the queue out """ if event.window != pane.get_handle_window(): return False if not self.qexpander.get_expanded(): self.qexpander.set_expanded(True) pane.set_relative(1.0) return False self.songpane.connect("button-press-event", songpane_button_press_cb) self.song_scroller.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::expanded', self.__expand_or) self.qexpander.connect('draw', self.__qex_size_allocate) self.songpane.connect('notify', self.__moved_pane_handle) try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() self._playback_error_dialog = None connect_destroy(player, 'song-started', self.__song_started) connect_destroy(player, 'paused', self.__update_paused, True) connect_destroy(player, 'unpaused', self.__update_paused, False) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__restore_cb = restore_cb self.__first_browser_set = True restore_browser = not headless try: self.select_browser(self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(0)) config.save() raise self.showhide_playlist(ui.get_widget("/Menu/View/SongList")) self.showhide_playqueue(ui.get_widget("/Menu/View/Queue")) self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_time) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, DND_URI_LIST)] targets = [Gtk.TargetEntry.new(*t) for t in targets] self.drag_dest_set(Gtk.DestDefaults.ALL, targets, Gdk.DragAction.COPY) self.connect('drag-data-received', self.__drag_data_received) if not headless: on_first_map(self, self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")
def main(argv): import quodlibet quodlibet.init_cli() try: # we want basic commands not to import gtk (doubles process time) assert "gi.repository.Gtk" not in sys.modules sys.modules["gi.repository.Gtk"] = None startup_actions, cmds_todo = process_arguments(argv) finally: sys.modules.pop("gi.repository.Gtk", None) quodlibet.init() from quodlibet import app from quodlibet.qltk import add_signal_watch, Icons add_signal_watch(app.quit) import quodlibet.player import quodlibet.library from quodlibet import config from quodlibet import browsers from quodlibet import util app.name = "Quod Libet" app.id = "quodlibet" quodlibet.set_application_info(Icons.QUODLIBET, app.id, app.name) config.init(os.path.join(quodlibet.get_user_dir(), "config")) library_path = os.path.join(quodlibet.get_user_dir(), "songs") print_d("Initializing main library (%s)" % (quodlibet.util.path.unexpand(library_path))) library = quodlibet.library.init(library_path) app.library = library # this assumes that nullbe will always succeed from quodlibet.player import PlayerError wanted_backend = os.environ.get("QUODLIBET_BACKEND", config.get("player", "backend")) backend_traceback = None for backend in [wanted_backend, "nullbe"]: try: player = quodlibet.player.init_player(backend, app.librarian) except PlayerError: backend_traceback = format_exc() else: break app.player = player os.environ["PULSE_PROP_media.role"] = "music" os.environ["PULSE_PROP_application.icon_name"] = "quodlibet" browsers.init() from quodlibet.qltk.songlist import SongList, get_columns from quodlibet.util.collection import Album try: cover_size = config.getint("browsers", "cover_size") except config.Error: pass else: if cover_size > 0: Album.COVER_SIZE = cover_size headers = get_columns() SongList.set_all_column_headers(headers) for opt in config.options("header_maps"): val = config.get("header_maps", opt) util.tags.add(opt, val) in_all = ("~filename ~uri ~#lastplayed ~#rating ~#playcount ~#skipcount " "~#added ~#bitrate ~current ~#laststarted ~basename " "~dirname").split() for Kind in browsers.browsers: if Kind.headers is not None: Kind.headers.extend(in_all) Kind.init(library) pm = quodlibet.init_plugins("no-plugins" in startup_actions) if hasattr(player, "init_plugins"): player.init_plugins() from quodlibet.qltk import unity unity.init("quodlibet.desktop", player) from quodlibet.qltk.songsmenu import SongsMenu SongsMenu.init_plugins() from quodlibet.util.cover import CoverManager app.cover_manager = CoverManager() app.cover_manager.init_plugins() from quodlibet.plugins.playlist import PLAYLIST_HANDLER PLAYLIST_HANDLER.init_plugins() from quodlibet.plugins.query import QUERY_HANDLER QUERY_HANDLER.init_plugins() from gi.repository import GLib def exec_commands(*args): for cmd in cmds_todo: try: resp = cmd_registry.run(app, *cmd) except CommandError: pass else: if resp is not None: print_(resp, end="") from quodlibet.qltk.quodlibetwindow import QuodLibetWindow, PlayerOptions # Call exec_commands after the window is restored, but make sure # it's after the mainloop has started so everything is set up. app.window = window = QuodLibetWindow( library, player, restore_cb=lambda: GLib.idle_add(exec_commands, priority=GLib.PRIORITY_HIGH)) app.player_options = PlayerOptions(window) from quodlibet.qltk.debugwindow import MinExceptionDialog from quodlibet.qltk.window import on_first_map if backend_traceback is not None: def show_backend_error(window): d = MinExceptionDialog( window, _("Audio Backend Failed to Load"), _("Loading the audio backend '%(name)s' failed. " "Audio playback will be disabled.") % {"name": wanted_backend}, backend_traceback) d.run() # so we show the main window first on_first_map(app.window, show_backend_error, app.window) from quodlibet.plugins.events import EventPluginHandler pm.register_handler(EventPluginHandler(library.librarian, player)) from quodlibet.mmkeys import MMKeysHandler from quodlibet.remote import Remote, RemoteError from quodlibet.commands import registry as cmd_registry, CommandError from quodlibet.qltk.tracker import SongTracker, FSInterface try: from quodlibet.qltk.dbus_ import DBusHandler except ImportError: DBusHandler = lambda player, library: None mmkeys_handler = MMKeysHandler(app) mmkeys_handler.start() current_path = os.path.join(quodlibet.get_user_dir(), "current") fsiface = FSInterface(current_path, player) remote = Remote(app, cmd_registry) try: remote.start() except RemoteError: exit_(1, True) DBusHandler(player, library) tracker = SongTracker(library.librarian, player, window.playlist) from quodlibet.qltk import session session.init("quodlibet") quodlibet.enable_periodic_save(save_library=True) if "start-playing" in startup_actions: player.paused = False # restore browser windows from quodlibet.qltk.browser import LibraryBrowser GLib.idle_add(LibraryBrowser.restore, library, player, priority=GLib.PRIORITY_HIGH) def before_quit(): print_d("Saving active browser state") try: app.browser.save() except NotImplementedError: pass print_d("Shutting down player device %r." % player.version_info) player.destroy() quodlibet.main(window, before_quit=before_quit) app.player_options.destroy() quodlibet.finish_first_session(app.id) mmkeys_handler.quit() remote.stop() fsiface.destroy() tracker.destroy() quodlibet.library.save() config.save() print_d("Finished shutdown.")
def __init__(self, library, player, headless=False, restore_cb=None): super().__init__(dialog=False) self.__destroyed = False self.__update_title(player) self.set_default_size(600, 480) main_box = Gtk.VBox() self.add(main_box) self.side_book = qltk.Notebook() # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after('drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = ScrolledWindow() self.song_scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.song_scroller.set_shadow_type(Gtk.ShadowType.IN) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander(library, player) self.qexpander.set_no_show_all(True) self.qexpander.set_visible(config.getboolean("memory", "queue")) def on_queue_visible(qex, param): config.set("memory", "queue", str(qex.get_visible())) self.qexpander.connect("notify::visible", on_queue_visible) self.playlist = PlaylistMux(player, self.qexpander.model, self.songlist.model) self.__player = player # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, None, True) keyval, mod = Gtk.accelerator_parse("<Primary><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # custom accel map accel_fn = os.path.join(quodlibet.get_user_dir(), "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Align(bottom=3) self.__paned = paned = ConfigRHPaned("memory", "sidebar_pos", 0.25) paned.pack1(self.__browserbox, resize=True) # We'll pack2 when necessary (when the first sidebar plugin is set up) main_box.pack_start(paned, True, True, 0) play_order = PlayOrderWidget(self.songlist.model, player) statusbox = StatusBarBox(play_order, self.qexpander) self.order = play_order self.statusbar = statusbox.statusbar main_box.pack_start(Align(statusbox, border=3, top=-3), False, True, 0) self.songpane = SongListPaned(self.song_scroller, self.qexpander) self.songpane.show_all() try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() self._playback_error_dialog = None connect_destroy(player, 'song-started', self.__song_started) connect_destroy(player, 'paused', self.__update_paused, True) connect_destroy(player, 'unpaused', self.__update_paused, False) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__restore_cb = restore_cb self.__first_browser_set = True restore_browser = not headless try: self._select_browser(self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(browsers.default)) config.save() raise self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_totals) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, DND_URI_LIST)] targets = [Gtk.TargetEntry.new(*t) for t in targets] self.drag_dest_set(Gtk.DestDefaults.ALL, targets, Gdk.DragAction.COPY) self.connect('drag-data-received', self.__drag_data_received) if not headless: on_first_map(self, self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")
def __init__(self, library, player, headless=False, restore_cb=None): super(QuodLibetWindow, self).__init__(dialog=False) self.last_dir = get_home_dir() self.__destroyed = False self.__update_title(player) self.set_default_size(550, 450) main_box = Gtk.VBox() self.add(main_box) # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, True) keyval, mod = Gtk.accelerator_parse("<control><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # dbus app menu # Unity puts the app menu next to our menu bar. Since it only contains # menu items also available in the menu bar itself, don't add it. if not util.is_unity(): AppMenu(self, ui.get_action_groups()[0]) # custom accel map accel_fn = os.path.join(quodlibet.get_user_dir(), "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.show_all() self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after( 'drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = SongListScroller( ui.get_widget("/Menu/View/SongList")) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander( ui.get_widget("/Menu/View/Queue"), library, player) self.playlist = PlaylistMux( player, self.qexpander.model, self.songlist.model) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Align(bottom=3) main_box.pack_start(self.__browserbox, True, True, 0) statusbox = StatusBarBox(self.songlist.model, player) self.order = statusbox.order self.repeat = statusbox.repeat self.statusbar = statusbox.statusbar main_box.pack_start( Align(statusbox, border=3, top=-3, right=3), False, True, 0) self.songpane = ConfigRVPaned("memory", "queue_position", 0.75) self.songpane.pack1(self.song_scroller, resize=True, shrink=False) self.songpane.pack2(self.qexpander, resize=True, shrink=False) self.__handle_position = self.songpane.get_property("position") def songpane_button_press_cb(pane, event): """If we start to drag the pane handle while the queue expander is unexpanded, expand it and move the handle to the bottom, so we can 'drag' the queue out """ if event.window != pane.get_handle_window(): return False if not self.qexpander.get_expanded(): self.qexpander.set_expanded(True) pane.set_relative(1.0) return False self.songpane.connect("button-press-event", songpane_button_press_cb) self.song_scroller.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::expanded', self.__expand_or) self.qexpander.connect('draw', self.__qex_size_allocate) self.songpane.connect('notify', self.__moved_pane_handle) try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() self._playback_error_dialog = None connect_destroy(player, 'song-started', self.__song_started) connect_destroy(player, 'paused', self.__update_paused, True) connect_destroy(player, 'unpaused', self.__update_paused, False) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__restore_cb = restore_cb self.__first_browser_set = True restore_browser = not headless try: self.select_browser( self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(0)) config.save() raise self.showhide_playlist(ui.get_widget("/Menu/View/SongList")) self.showhide_playqueue(ui.get_widget("/Menu/View/Queue")) self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_time) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, DND_URI_LIST)] targets = [Gtk.TargetEntry.new(*t) for t in targets] self.drag_dest_set( Gtk.DestDefaults.ALL, targets, Gdk.DragAction.COPY) self.connect('drag-data-received', self.__drag_data_received) if not headless: on_first_map(self, self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")
def main(argv): import quodlibet quodlibet.init_cli() try: # we want basic commands not to import gtk (doubles process time) assert "gi.repository.Gtk" not in sys.modules sys.modules["gi.repository.Gtk"] = None startup_actions, cmds_todo = process_arguments(argv) finally: sys.modules.pop("gi.repository.Gtk", None) quodlibet.init() from quodlibet import app from quodlibet.qltk import add_signal_watch, Icons add_signal_watch(app.quit) import quodlibet.player import quodlibet.library from quodlibet import config from quodlibet import browsers from quodlibet import util from quodlibet.util.string import decode app.name = "Quod Libet" app.id = "quodlibet" quodlibet.set_application_info(Icons.QUODLIBET, app.id, app.name) config.init(os.path.join(quodlibet.get_user_dir(), "config")) library_path = os.path.join(quodlibet.get_user_dir(), "songs") print_d("Initializing main library (%s)" % ( quodlibet.util.path.unexpand(library_path))) library = quodlibet.library.init(library_path) app.library = library # this assumes that nullbe will always succeed from quodlibet.player import PlayerError wanted_backend = os.environ.get( "QUODLIBET_BACKEND", config.get("player", "backend")) backend_traceback = None for backend in [wanted_backend, "nullbe"]: try: player = quodlibet.player.init_player(backend, app.librarian) except PlayerError: backend_traceback = decode(traceback.format_exc()) else: break app.player = player os.environ["PULSE_PROP_media.role"] = "music" os.environ["PULSE_PROP_application.icon_name"] = "quodlibet" browsers.init() from quodlibet.qltk.songlist import SongList, get_columns from quodlibet.util.collection import Album try: cover_size = config.getint("browsers", "cover_size") except config.Error: pass else: if cover_size > 0: Album.COVER_SIZE = cover_size headers = get_columns() SongList.set_all_column_headers(headers) for opt in config.options("header_maps"): val = config.get("header_maps", opt) util.tags.add(opt, val) in_all = ("~filename ~uri ~#lastplayed ~#rating ~#playcount ~#skipcount " "~#added ~#bitrate ~current ~#laststarted ~basename " "~dirname").split() for Kind in browsers.browsers: if Kind.headers is not None: Kind.headers.extend(in_all) Kind.init(library) pm = quodlibet.init_plugins("no-plugins" in startup_actions) if hasattr(player, "init_plugins"): player.init_plugins() from quodlibet.qltk import unity unity.init("quodlibet.desktop", player) from quodlibet.qltk.songsmenu import SongsMenu SongsMenu.init_plugins() from quodlibet.util.cover import CoverManager app.cover_manager = CoverManager() app.cover_manager.init_plugins() from quodlibet.plugins.playlist import PLAYLIST_HANDLER PLAYLIST_HANDLER.init_plugins() from gi.repository import GLib def exec_commands(*args): for cmd in cmds_todo: try: resp = cmd_registry.run(app, *cmd) except CommandError: pass else: if resp is not None: print_(resp, end="") from quodlibet.qltk.quodlibetwindow import QuodLibetWindow, PlayerOptions # Call exec_commands after the window is restored, but make sure # it's after the mainloop has started so everything is set up. app.window = window = QuodLibetWindow( library, player, restore_cb=lambda: GLib.idle_add(exec_commands, priority=GLib.PRIORITY_HIGH)) app.player_options = PlayerOptions(window) from quodlibet.qltk.debugwindow import MinExceptionDialog from quodlibet.qltk.window import on_first_map if backend_traceback is not None: def show_backend_error(window): d = MinExceptionDialog(window, _("Audio Backend Failed to Load"), _("Loading the audio backend '%(name)s' failed. " "Audio playback will be disabled.") % {"name": wanted_backend}, backend_traceback) d.run() # so we show the main window first on_first_map(app.window, show_backend_error, app.window) from quodlibet.plugins.events import EventPluginHandler pm.register_handler(EventPluginHandler(library.librarian, player)) from quodlibet.mmkeys import MMKeysHandler from quodlibet.remote import Remote, RemoteError from quodlibet.commands import registry as cmd_registry, CommandError from quodlibet.qltk.tracker import SongTracker, FSInterface try: from quodlibet.qltk.dbus_ import DBusHandler except ImportError: DBusHandler = lambda player, library: None mmkeys_handler = MMKeysHandler(app.name, window, player) if "QUODLIBET_NO_MMKEYS" not in os.environ: mmkeys_handler.start() current_path = os.path.join(quodlibet.get_user_dir(), "current") fsiface = FSInterface(current_path, player) remote = Remote(app, cmd_registry) try: remote.start() except RemoteError: exit_(1, True) DBusHandler(player, library) tracker = SongTracker(library.librarian, player, window.playlist) from quodlibet.qltk import session session.init("quodlibet") quodlibet.enable_periodic_save(save_library=True) if "start-playing" in startup_actions: player.paused = False # restore browser windows from quodlibet.qltk.browser import LibraryBrowser GLib.idle_add(LibraryBrowser.restore, library, player, priority=GLib.PRIORITY_HIGH) def before_quit(): print_d("Saving active browser state") try: app.browser.save() except NotImplementedError: pass print_d("Shutting down player device %r." % player.version_info) player.destroy() quodlibet.main(window, before_quit=before_quit) app.player_options.destroy() quodlibet.finish_first_session(app.id) mmkeys_handler.quit() remote.stop() fsiface.destroy() tracker.destroy() quodlibet.library.save() config.save() print_d("Finished shutdown.")
def __init__(self, library, player, headless=False, restore_cb=None): super(QuodLibetWindow, self).__init__(dialog=False) self.__destroyed = False self.__update_title(player) self.set_default_size(600, 480) main_box = Gtk.VBox() self.add(main_box) self.side_book = qltk.Notebook() # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after( 'drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = ScrolledWindow() self.song_scroller.set_policy( Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.song_scroller.set_shadow_type(Gtk.ShadowType.IN) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander(library, player) self.qexpander.set_no_show_all(True) self.qexpander.set_visible(config.getboolean("memory", "queue")) def on_queue_visible(qex, param): config.set("memory", "queue", str(qex.get_visible())) self.qexpander.connect("notify::visible", on_queue_visible) self.playlist = PlaylistMux( player, self.qexpander.model, self.songlist.model) self.__player = player # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, None, True) keyval, mod = Gtk.accelerator_parse("<Primary><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # custom accel map accel_fn = os.path.join(quodlibet.get_user_dir(), "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Align(bottom=3) self.__paned = paned = ConfigRHPaned("memory", "sidebar_pos", 0.25) paned.pack1(self.__browserbox, resize=True) # We'll pack2 when necessary (when the first sidebar plugin is set up) main_box.pack_start(paned, True, True, 0) play_order = PlayOrderWidget(self.songlist.model, player) statusbox = StatusBarBox(play_order, self.qexpander) self.order = play_order self.statusbar = statusbox.statusbar main_box.pack_start( Align(statusbox, border=3, top=-3), False, True, 0) self.songpane = SongListPaned(self.song_scroller, self.qexpander) self.songpane.show_all() try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() self._playback_error_dialog = None connect_destroy(player, 'song-started', self.__song_started) connect_destroy(player, 'paused', self.__update_paused, True) connect_destroy(player, 'unpaused', self.__update_paused, False) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__restore_cb = restore_cb self.__first_browser_set = True restore_browser = not headless try: self._select_browser( self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(browsers.default)) config.save() raise self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_totals) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, DND_URI_LIST)] targets = [Gtk.TargetEntry.new(*t) for t in targets] self.drag_dest_set( Gtk.DestDefaults.ALL, targets, Gdk.DragAction.COPY) self.connect('drag-data-received', self.__drag_data_received) if not headless: on_first_map(self, self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")