class App: def __init__(self): self.app = Gtk.Application.new(DBUS_APP_NAME, 0) self.app.connect("startup", self.on_app_startup) self.app.connect("activate", self.on_app_activate) self.app.connect("shutdown", self.on_app_shutdown) self.conf = Config.load_conf() self.theme, self.theme_path = Config.load_theme() def on_app_startup(self, app): self.window = Gtk.ApplicationWindow(application=app) self.window.set_default_size(*self.conf["window-size"]) self.window.set_title(Config.APPNAME) self.window.props.hide_titlebar_when_maximized = True self.window.set_icon(self.theme["app-logo"]) app.add_window(self.window) self.window.connect("check-resize", self.on_main_window_resized) self.window.connect("delete-event", self.on_main_window_deleted) self.accel_group = Gtk.AccelGroup() self.window.add_accel_group(self.accel_group) self.fullscreen_sid = 0 self.fullscreen_timestamp = 0 box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.window.add(box) self.player = Player(self) box.pack_start(self.player, False, False, 0) self.notebook = Gtk.Notebook() self.notebook.props.tab_pos = Gtk.PositionType.BOTTOM self.notebook.get_style_context().add_class("main_tab") box.pack_start(self.notebook, True, True, 0) self.init_notebook() self.notebook.connect("switch-page", self.on_notebook_switch_page) self.init_status_icon() # load default styles when all widgets have been constructed. self.load_styles() def on_app_activate(self, app): self.window.show_all() # Make some changes after main window is shown. # Like hiding some unnecessory widgets. self.lrc.after_init() self.artists.after_init() self.player.after_init() self.search.after_init() self.shortcut = Shortcut(self.player) def on_app_shutdown(self, app): Config.dump_conf(self.conf) def run(self, argv): self.app.run(argv) def quit(self): gdk_win = self.window.get_window() if gdk_win and not gdk_win.is_destroyed(): self.window.destroy() self.shortcut.quit() self.app.quit() def on_main_window_resized(self, window, event=None): self.conf["window-size"] = window.get_size() def on_main_window_deleted(self, window, event): if self.conf["use-status-icon"]: window.hide() return True else: return False def init_notebook(self): self.tab_first_show = [] self.lrc = Lrc(self) self.append_page(self.lrc) self.playlist = PlayList(self) self.append_page(self.playlist) self.search = Search(self) self.append_page(self.search) self.toplist = TopList(self) self.append_page(self.toplist) self.radio = Radio(self) self.append_page(self.radio) self.mv = MV(self) self.append_page(self.mv) self.artists = Artists(self) self.append_page(self.artists) self.topcategories = TopCategories(self) self.append_page(self.topcategories) self.themes = Themes(self) self.append_page(self.themes) def append_page(self, widget): """Append a new widget to notebook.""" label = Gtk.Label(widget.title) widget.app_page = self.notebook.append_page(widget, label) def popup_page(self, page): """Switch to this widget in notebook.""" self.notebook.set_current_page(page) def apply_css(self, widget, css, old_provider=None, overall=False): """Update CssProvider of this widget.""" # CssProvider needs bytecode style_provider = Gtk.CssProvider() css_encoded = css.encode() style_provider.load_from_data(css_encoded) if overall: Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) if old_provider: Gtk.StyleContext.remove_provider_for_screen(Gdk.Screen.get_default(), style_provider) else: widget.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) if old_provider: widget.get_style_context().remove_provider(old_provider) return style_provider def load_styles(self): """Load default CssStyle.""" if Config.GTK_LE_36: css = "\n".join( [ # transition-property is not supported in Gtk3.4 #'GtkScrolledWindow.lrc_window {', # 'transition-property: background-image;', # 'transition-duration: 1s;', # '}', "GtkScale {", "outline-color: transparent;", "outline-offset: 0;", "outline-style: none;", "outline-width: 0;", "}", "GtkTextView.lrc_tv {", "font-size: {0};".format(self.conf["lrc-text-size"]), "color: {0};".format(self.conf["lrc-text-color"]), "border-radius: 0 25 0 50;", "border-width: 5;", "background-color: {0};".format(self.conf["lrc-back-color"]), "}", ".info-label {", "color: rgb(136, 139, 132);", "font-size: 9;", "}", ] ) else: css = "\n".join( [ "GtkScrolledWindow.lrc_window {", "transition-property: background-image;", "transition-duration: 1s;", #'background-repeat: no-repeat;', #'background-repeat: repeat;', #'background-position: center;', "}", "GtkScale {", "outline-color: transparent;", "outline-offset: 0;", "outline-style: none;", "outline-width: 0;", "}", "GtkTextView.lrc_tv {", "font-size: {0}px;".format(self.conf["lrc-text-size"]), "color: {0};".format(self.conf["lrc-text-color"]), "border-radius: 0px 25px 0px 50px;", "border-width: 5px;", "background-color: {0};".format(self.conf["lrc-back-color"]), "}", ".info-label {", "color: rgb(136, 139, 132);", "font-size: 9px;", "}", ] ) self.apply_css(self.window, css, overall=True) settings = Gtk.Settings.get_default() settings.props.gtk_application_prefer_dark_theme = self.conf.get("use-dark-theme", False) def init_status_icon(self): # set status_icon as class property, to keep its life # after function exited self.status_icon = Gtk.StatusIcon() self.status_icon.set_from_pixbuf(self.theme["app-logo"]) # left click self.status_icon.connect("activate", self.on_status_icon_activate) # right click self.status_icon.connect("popup_menu", self.on_status_icon_popup_menu) def on_status_icon_activate(self, status_icon): if self.window.props.visible: self.window.hide() else: self.window.present() def on_status_icon_popup_menu(self, status_icon, event_button, event_time): menu = Gtk.Menu() show_item = Gtk.MenuItem(label=_("Show App")) show_item.connect("activate", self.on_status_icon_show_app_activate) menu.append(show_item) pause_item = Gtk.MenuItem(label=_("Pause/Resume")) pause_item.connect("activate", self.on_status_icon_pause_activate) menu.append(pause_item) next_item = Gtk.MenuItem(label=_("Next Song")) next_item.connect("activate", self.on_status_icon_next_activate) menu.append(next_item) sep_item = Gtk.SeparatorMenuItem() menu.append(sep_item) quit_item = Gtk.MenuItem(label=_("Quit")) quit_item.connect("activate", self.on_status_icon_quit_activate) menu.append(quit_item) menu.show_all() menu.popup( None, None, lambda a, b: Gtk.StatusIcon.position_menu(menu, status_icon), None, event_button, event_time ) def on_status_icon_show_app_activate(self, menuitem): self.window.present() def on_status_icon_pause_activate(self, menuitem): self.player.play_pause() def on_status_icon_next_activate(self, menuitem): self.player.load_next() def on_status_icon_quit_activate(self, menuitem): self.quit() def on_notebook_switch_page(self, notebook, page, page_num): if page not in self.tab_first_show: page.first() self.tab_first_show.append(page) if page_num == 0: # fullscreen self.player.hide() self.notebook.set_show_tabs(False) self.fullscreen_sid = self.window.connect("motion-notify-event", self.on_window_motion_notified) else: # unfullscreen self.player.show() self.notebook.set_show_tabs(True) self.fullscreen_sid = 0 def on_window_motion_notified(self, window, event): if self.notebook.get_current_page() != 0: return lower = 70 upper = self.window.get_size()[1] - 40 if lower < event.y < upper: return if event.y < lower: self.player.show_all() elif event.y > upper: self.notebook.set_show_tabs(True) # delay 2 seconds and hide them self.fullscreen_timestamp = time.time() GLib.timeout_add(100, self.player.playbin.expose) GLib.timeout_add(2000, self.hide_control_panel_and_label, self.fullscreen_timestamp) def hide_control_panel_and_label(self, timestamp): if timestamp == self.fullscreen_timestamp and self.fullscreen_sid > 0: self.notebook.set_show_tabs(False) self.player.hide() GLib.timeout_add(100, self.player.playbin.expose)
class App: def __init__(self): self.app = Gtk.Application.new(DBUS_APP_NAME, 0) self.app.connect('startup', self.on_app_startup) self.app.connect('activate', self.on_app_activate) self.app.connect('shutdown', self.on_app_shutdown) self.conf = Config.load_conf() self.theme, self.theme_path = Config.load_theme() def on_app_startup(self, app): self.window = Gtk.ApplicationWindow(application=app) self.window.set_default_size(*self.conf['window-size']) self.window.set_title(Config.APPNAME) self.window.props.hide_titlebar_when_maximized = True self.window.set_icon(self.theme['app-logo']) app.add_window(self.window) self.window.connect('check-resize', self.on_main_window_resized) self.window.connect('delete-event', self.on_main_window_deleted) self.accel_group = Gtk.AccelGroup() self.window.add_accel_group(self.accel_group) self.fullscreen_sid = 0 self.fullscreen_timestamp = 0 box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.window.add(box) self.player = Player(self) box.pack_start(self.player, False, False, 0) self.notebook = Gtk.Notebook() self.notebook.props.tab_pos = Gtk.PositionType.BOTTOM self.notebook.get_style_context().add_class('main_tab') box.pack_start(self.notebook, True, True, 0) self.init_notebook() self.notebook.connect('switch-page', self.on_notebook_switch_page) self.init_status_icon() # load default styles when all widgets have been constructed. self.load_styles() def on_app_activate(self, app): self.window.show_all() # Make some changes after main window is shown. # Like hiding some unnecessory widgets. self.lrc.after_init() self.artists.after_init() self.player.after_init() self.search.after_init() self.shortcut = Shortcut(self.player) def on_app_shutdown(self, app): Config.dump_conf(self.conf) def run(self, argv): self.app.run(argv) def quit(self): gdk_win = self.window.get_window() if gdk_win and not gdk_win.is_destroyed(): self.window.destroy() self.shortcut.quit() self.app.quit() def on_main_window_resized(self, window, event=None): self.conf['window-size'] = window.get_size() def on_main_window_deleted(self, window, event): if self.conf['use-status-icon']: window.hide() return True else: return False def init_notebook(self): self.tab_first_show = [] self.lrc = Lrc(self) self.append_page(self.lrc) self.playlist = PlayList(self) self.append_page(self.playlist) self.search = Search(self) self.append_page(self.search) self.toplist = TopList(self) self.append_page(self.toplist) self.radio = Radio(self) self.append_page(self.radio) self.mv = MV(self) self.append_page(self.mv) self.artists = Artists(self) self.append_page(self.artists) self.topcategories = TopCategories(self) self.append_page(self.topcategories) self.themes = Themes(self) self.append_page(self.themes) def append_page(self, widget): '''Append a new widget to notebook.''' label = Gtk.Label(widget.title) widget.app_page = self.notebook.append_page(widget, label) def popup_page(self, page): '''Switch to this widget in notebook.''' self.notebook.set_current_page(page) def apply_css(self, widget, css, old_provider=None, overall=False): '''Update CssProvider of this widget.''' # CssProvider needs bytecode style_provider = Gtk.CssProvider() css_encoded = css.encode() style_provider.load_from_data(css_encoded) if overall: Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) if old_provider: Gtk.StyleContext.remove_provider_for_screen( Gdk.Screen.get_default(), style_provider) else: widget.get_style_context().add_provider( style_provider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) if old_provider: widget.get_style_context().remove_provider(old_provider) return style_provider def load_styles(self): '''Load default CssStyle.''' if Config.GTK_LE_36: css = '\n'.join([ # transition-property is not supported in Gtk3.4 #'GtkScrolledWindow.lrc_window {', # 'transition-property: background-image;', # 'transition-duration: 1s;', # '}', 'GtkScale {', 'outline-color: transparent;', 'outline-offset: 0;', 'outline-style: none;', 'outline-width: 0;', '}', 'GtkTextView.lrc_tv {', 'font-size: {0};'.format(self.conf['lrc-text-size']), 'color: {0};'.format(self.conf['lrc-text-color']), 'border-radius: 0 25 0 50;', 'border-width: 5;', 'background-color: {0};'.format( self.conf['lrc-back-color']), '}', '.info-label {', 'color: rgb(136, 139, 132);', 'font-size: 9;', '}', ]) else: css = '\n'.join([ 'GtkScrolledWindow.lrc_window {', 'transition-property: background-image;', 'transition-duration: 1s;', #'background-repeat: no-repeat;', #'background-repeat: repeat;', #'background-position: center;', '}', 'GtkScale {', 'outline-color: transparent;', 'outline-offset: 0;', 'outline-style: none;', 'outline-width: 0;', '}', 'GtkTextView.lrc_tv {', 'font-size: {0}px;'.format(self.conf['lrc-text-size']), 'color: {0};'.format(self.conf['lrc-text-color']), 'border-radius: 0px 25px 0px 50px;', 'border-width: 5px;', 'background-color: {0};'.format( self.conf['lrc-back-color']), '}', '.info-label {', 'color: rgb(136, 139, 132);', 'font-size: 9px;', '}', ]) self.apply_css(self.window, css, overall=True) settings = Gtk.Settings.get_default() settings.props.gtk_application_prefer_dark_theme = \ self.conf.get('use-dark-theme', False) def init_status_icon(self): # set status_icon as class property, to keep its life # after function exited self.status_icon = Gtk.StatusIcon() self.status_icon.set_from_pixbuf(self.theme['app-logo']) # left click self.status_icon.connect('activate', self.on_status_icon_activate) # right click self.status_icon.connect('popup_menu', self.on_status_icon_popup_menu) def on_status_icon_activate(self, status_icon): if self.window.props.visible: self.window.hide() else: self.window.present() def on_status_icon_popup_menu(self, status_icon, event_button, event_time): menu = Gtk.Menu() show_item = Gtk.MenuItem(label=_('Show App') ) show_item.connect('activate', self.on_status_icon_show_app_activate) menu.append(show_item) pause_item = Gtk.MenuItem(label=_('Pause/Resume')) pause_item.connect('activate', self.on_status_icon_pause_activate) menu.append(pause_item) next_item = Gtk.MenuItem(label=_('Next Song')) next_item.connect('activate', self.on_status_icon_next_activate) menu.append(next_item) sep_item = Gtk.SeparatorMenuItem() menu.append(sep_item) quit_item = Gtk.MenuItem(label=_('Quit')) quit_item.connect('activate', self.on_status_icon_quit_activate) menu.append(quit_item) menu.show_all() menu.popup(None, None, lambda a,b: Gtk.StatusIcon.position_menu(menu, status_icon), None, event_button, event_time) def on_status_icon_show_app_activate(self, menuitem): self.window.present() def on_status_icon_pause_activate(self, menuitem): self.player.play_pause() def on_status_icon_next_activate(self, menuitem): self.player.load_next() def on_status_icon_quit_activate(self, menuitem): self.quit() def on_notebook_switch_page(self, notebook, page, page_num): if page not in self.tab_first_show: page.first() self.tab_first_show.append(page) if page_num == 0: # fullscreen self.player.hide() self.notebook.set_show_tabs(False) self.fullscreen_sid = self.window.connect( 'motion-notify-event', self.on_window_motion_notified) else: # unfullscreen self.player.show() self.notebook.set_show_tabs(True) self.fullscreen_sid = 0 def on_window_motion_notified(self, window, event): if self.notebook.get_current_page() != 0: return lower = 70 upper = self.window.get_size()[1] - 40 if lower < event.y < upper: return if event.y < lower: self.player.show_all() elif event.y > upper: self.notebook.set_show_tabs(True) # delay 2 seconds and hide them self.fullscreen_timestamp = time.time() GLib.timeout_add(100, self.player.playbin.expose) GLib.timeout_add( 2000, self.hide_control_panel_and_label, self.fullscreen_timestamp) def hide_control_panel_and_label(self, timestamp): if (timestamp == self.fullscreen_timestamp and self.fullscreen_sid > 0): self.notebook.set_show_tabs(False) self.player.hide() GLib.timeout_add(100, self.player.playbin.expose)