示例#1
0
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)

    def on_app_startup(self, app):
        self.conf = Config.load_conf()
        self.icon_theme = Gtk.IconTheme.get_default()
        self.icon_theme.append_search_path(Config.ICON_PATH)

        GLib.set_application_name(Config.APPNAME)
        self.window = Gtk.ApplicationWindow(application=app)
        self.window.set_default_size(*self.conf['window-size'])
        self.window.set_title(Config.APPNAME)
        self.window.set_default_icon_name(Config.NAME)
        self.window.props.hide_titlebar_when_maximized = True
        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)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.window.add(box)

        self.osdlrc = OSDLrc(self)
        self.notify = None
        self.init_notify()

        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.artists.after_init()
        self.player.after_init()
        self.search.after_init()
        self.shortcut = Shortcut(self.player)
        self.osdlrc.after_init()

        # Cleanup temporary media files
        Net.cleanup_temp_files(self.conf['song-dir'])
        Net.cleanup_temp_files(self.conf['mv-dir'])

    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():
            if self.player.default_cursor:
                gdk_win.set_cursor(self.player.default_cursor)
            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 load_styles(self):
        '''Load default CssStyle.'''
        if Config.GTK_LE_36:
            css = '\n'.join([
                '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;',
                '}',
                'GtkScale {',
                    'outline-color: transparent;',
                    'outline-offset: 0;',
                    'outline-style: none;',
                    'outline-width: 0;',
                '}',
                'GtkTextView.lrc_tv {',
                    'transition-property: font-size;',
                    'transition: 500ms ease-in-out;',
                    '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;',
                '}',
                ])

        Widgets.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):
        def on_status_icon_popup_menu(status_icon, event_button,
                                      event_time):
            menu.popup(None, None,
                    lambda a,b: Gtk.StatusIcon.position_menu(menu, status_icon),
                    None, event_button, event_time)

        def on_status_icon_activate(tatus_icon):
            if self.window.props.visible:
                self.window.hide()
            else:
                self.window.present()

        if not self.conf['use-status-icon']:
            return

        menu = Gtk.Menu()

        show_item = Gtk.MenuItem(_('Show App'))
        show_item.connect('activate', lambda item: self.window.present())
        menu.append(show_item)

        sep_item = Gtk.SeparatorMenuItem()
        menu.append(sep_item)

        previous_item = Gtk.MenuItem(_('Previous Song'))
        previous_item.connect('activate', lambda item: self.player.load_prev())
        menu.append(previous_item)

        play_item = Gtk.MenuItem()
        play_item.props.related_action = self.player.playback_action
        menu.append(play_item)

        next_item = Gtk.MenuItem(_('Next Song'))
        next_item.connect('activate', lambda item: self.player.load_next())
        menu.append(next_item)

        sep_item = Gtk.SeparatorMenuItem()
        menu.append(sep_item)

        show_osd_item = Gtk.MenuItem()
        show_osd_item.props.related_action = self.osdlrc.show_window_action
        menu.append(show_osd_item)

        lock_osd_item = Gtk.MenuItem()
        lock_osd_item.props.related_action = self.osdlrc.lock_window_action
        menu.append(lock_osd_item)

        sep_item = Gtk.SeparatorMenuItem()
        menu.append(sep_item)
        
        quit_item = Gtk.MenuItem(_('Quit'))
        quit_item.connect('activate', lambda item: self.quit())
        menu.append(quit_item)

        menu.show_all()
        self.status_menu = menu

        if 'AppIndicator' in globals():
            self.status_icon = AppIndicator.Indicator.new(Config.NAME,
                    Config.NAME,
                    AppIndicator.IndicatorCategory.APPLICATION_STATUS)
            self.status_icon.set_menu(menu)
            self.status_icon.set_status(AppIndicator.IndicatorStatus.ACTIVE)
        else: 
            self.status_icon = Gtk.StatusIcon.new_from_icon_name(Config.NAME)
            # left click
            self.status_icon.connect('activate', on_status_icon_activate)
            # right click
            self.status_icon.connect('popup_menu', on_status_icon_popup_menu)

    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)

    def init_notify(self):
        try:
            self.notify = Notify.Notification.new(Config.APPNAME, '',
                                                  Config.NAME)
        except Exception:
            logger.warn('App.init_notify: %s' % traceback.format_exc())
            self.notify = None

    # Open API
    def toast(self, text):
        '''在用户界面显示一个消息通知.

        可以使用系统提供的Notification工具, 也可以在窗口的最下方滚动弹出
        这个消息
        '''
        if self.notify:
            self.notify.update(Config.APPNAME, text, Config.NAME)
            self.notify.show()
示例#2
0
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)

        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):
        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 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)

    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;',
                    '}',
                '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)

    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()
示例#3
0
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)

    def on_app_startup(self, app):
        self.conf = Config.load_conf()
        self.icon_theme = Gtk.IconTheme.get_default()
        self.icon_theme.append_search_path(Config.ICON_PATH)

        GLib.set_application_name(Config.APPNAME)
        self.window = Gtk.ApplicationWindow(application=app)
        self.window.set_default_size(*self.conf['window-size'])
        self.window.set_title(Config.APPNAME)
        self.window.set_default_icon_name(Config.NAME)
        self.window.props.hide_titlebar_when_maximized = True
        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)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.window.add(box)

        self.osdlrc = OSDLrc(self)
        self.notify = None
        self.init_notify()

        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.artists.after_init()
        self.player.after_init()
        self.search.after_init()
        self.shortcut = Shortcut(self.player)
        self.osdlrc.after_init()

        # Cleanup temporary media files
        Net.cleanup_temp_files(self.conf['song-dir'])
        Net.cleanup_temp_files(self.conf['mv-dir'])

    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():
            if self.player.default_cursor:
                gdk_win.set_cursor(self.player.default_cursor)
            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 load_styles(self):
        '''Load default CssStyle.'''
        if Config.GTK_LE_36:
            css = '\n'.join([
                '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;',
                '}',
                'GtkScale {',
                'outline-color: transparent;',
                'outline-offset: 0;',
                'outline-style: none;',
                'outline-width: 0;',
                '}',
                'GtkTextView.lrc_tv {',
                'transition-property: font-size;',
                'transition: 500ms ease-in-out;',
                '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;',
                '}',
            ])

        Widgets.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):
        def on_status_icon_popup_menu(status_icon, event_button, event_time):
            menu.popup(
                None, None,
                lambda a, b: Gtk.StatusIcon.position_menu(menu, status_icon),
                None, event_button, event_time)

        def on_status_icon_activate(tatus_icon):
            if self.window.props.visible:
                self.window.hide()
            else:
                self.window.present()

        if not self.conf['use-status-icon']:
            return

        menu = Gtk.Menu()

        show_item = Gtk.MenuItem(_('Show App'))
        show_item.connect('activate', lambda item: self.window.present())
        menu.append(show_item)

        sep_item = Gtk.SeparatorMenuItem()
        menu.append(sep_item)

        previous_item = Gtk.MenuItem(_('Previous Song'))
        previous_item.connect('activate', lambda item: self.player.load_prev())
        menu.append(previous_item)

        play_item = Gtk.MenuItem()
        play_item.props.related_action = self.player.playback_action
        menu.append(play_item)

        next_item = Gtk.MenuItem(_('Next Song'))
        next_item.connect('activate', lambda item: self.player.load_next())
        menu.append(next_item)

        sep_item = Gtk.SeparatorMenuItem()
        menu.append(sep_item)

        show_osd_item = Gtk.MenuItem()
        show_osd_item.props.related_action = self.osdlrc.show_window_action
        menu.append(show_osd_item)

        lock_osd_item = Gtk.MenuItem()
        lock_osd_item.props.related_action = self.osdlrc.lock_window_action
        menu.append(lock_osd_item)

        sep_item = Gtk.SeparatorMenuItem()
        menu.append(sep_item)

        quit_item = Gtk.MenuItem(_('Quit'))
        quit_item.connect('activate', lambda item: self.quit())
        menu.append(quit_item)

        menu.show_all()
        self.status_menu = menu

        if 'AppIndicator' in globals():
            self.status_icon = AppIndicator.Indicator.new(
                Config.NAME, Config.NAME,
                AppIndicator.IndicatorCategory.APPLICATION_STATUS)
            self.status_icon.set_menu(menu)
            self.status_icon.set_status(AppIndicator.IndicatorStatus.ACTIVE)
        else:
            self.status_icon = Gtk.StatusIcon.new_from_icon_name(Config.NAME)
            # left click
            self.status_icon.connect('activate', on_status_icon_activate)
            # right click
            self.status_icon.connect('popup_menu', on_status_icon_popup_menu)

    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)

    def init_notify(self):
        try:
            self.notify = Notify.Notification.new(Config.APPNAME, '',
                                                  Config.NAME)
        except Exception:
            logger.warn('App.init_notify: %s' % traceback.format_exc())
            self.notify = None

    # Open API
    def toast(self, text):
        '''在用户界面显示一个消息通知.

        可以使用系统提供的Notification工具, 也可以在窗口的最下方滚动弹出
        这个消息
        '''
        if self.notify:
            self.notify.update(Config.APPNAME, text, Config.NAME)
            self.notify.show()
示例#4
0
文件: App.py 项目: fneig/kwplayer
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)
示例#5
0
文件: App.py 项目: sumous/kwplayer
class App:
    def __init__(self):
        self.app = Gtk.Application.new('org.gtk.kuwo', 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 = 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)

        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')
        # Add 2 pix to left-margin to solve Fullscreen problem.
        #self.notebook.props.margin_left = 2
        box.pack_start(self.notebook, True, True, 0)

        # signal should be connected after all pages in notebook
        # all added.
        self.init_notebook()
        self.notebook.connect('switch-page',
                self.on_notebook_switch_page)
        self.init_status_icon()

        # load styles
        self.load_styles()

    def on_app_activate(self, app):
        self.window.show_all()
        # make some changes after main window is shown.
        self.lrc.after_init()
        self.player.after_init()
        self.search.after_init()

    def run(self, argv):
        self.app.run(argv)

    def quit(self):
        self.window.destroy()
        self.app.quit()

    def on_app_shutdown(self, app):
        Config.dump_conf(self.conf)

    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.lrc = Lrc(self)
        self.lrc.app_page = self.notebook.append_page(
                self.lrc, Gtk.Label(_('Lyrics')))

        self.playlist = PlayList(self)
        self.playlist.app_page = self.notebook.append_page(
                self.playlist, Gtk.Label(_('Playlist')))

        self.search = Search(self)
        self.search.app_page = self.notebook.append_page(
                self.search, Gtk.Label(_('Search')))

        self.toplist = TopList(self)
        self.toplist.app_page = self.notebook.append_page(
                self.toplist, Gtk.Label(_('Top List')))

        self.radio = Radio(self)
        self.radio.app_page = self.notebook.append_page(
                self.radio, Gtk.Label(_('Radio')))

        self.mv = MV(self)
        self.mv.app_page = self.notebook.append_page(
                self.mv, Gtk.Label(_('MV')))

        self.artists = Artists(self)
        self.artists.app_page = self.notebook.append_page(
                self.artists, Gtk.Label(_('Artists')))

        self.topcategories = TopCategories(self)
        self.topcategories.app_page = self.notebook.append_page(
                self.topcategories, Gtk.Label(_('Categories')))

        self.themes = Themes(self)
        self.themes.app_page = self.notebook.append_page(
                self.themes, Gtk.Label(_('Themes')))

    def on_notebook_switch_page(self, notebook, page, page_num):
        page.first()

    def popup_page(self, page):
        self.notebook.set_current_page(page)

    def load_styles(self):
        font_size = str(self.conf['lrc-text-size'])
        if Gtk.MINOR_VERSION > 6:
            font_size += 'px;'
        # CssProvider needs bytecode
        css = '\n'.join([
            'GtkTextView.lrc_tv {',
                'font-size: ' + font_size,
                'color: ' + self.conf['lrc-text-color'] + ';',
                'background-color: rgba(0, 0, 0, 0);',
            '}',
            '.info-label {',
              'color: rgb(136, 139, 132);',
              'font-size: 9px;',
            '}',
            ]).encode()

        style_provider = Gtk.CssProvider()
        style_provider.load_from_data(css)
        Gtk.StyleContext.add_provider_for_screen(
                Gdk.Screen.get_default(), style_provider,
                Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    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):
        is_visible = self.window.is_visible()
        if is_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):
        if self.player.is_playing():
            self.player.pause_player()
        else:
            self.player.start_player()

    def on_status_icon_next_activate(self, menuitem):
        self.player.load_next()

    def on_status_icon_quit_activate(self, menuitem):
        self.quit()
示例#6
0
文件: App.py 项目: weakish/kwplayer
class App:
    def __init__(self):
        self.app = Gtk.Application.new('org.gtk.kuwo', 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 = Config.load_theme(self.conf['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)

        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')
        # Add 2 pix to left-margin to solve Fullscreen problem.
        self.notebook.props.margin_left = 2
        box.pack_start(self.notebook, True, True, 0)

        self.builder = Gtk.Builder()
        for ui in Config.UI_FILES:
            self.builder.add_from_file(ui)
        self.builder.connect_signals(self)
        appmenu = self.builder.get_object('appmenu')
        app.set_app_menu(appmenu)
        
        self.add_simple_action('preferences', 
                self.on_action_preferences_activate)
        self.add_simple_action('about', self.on_action_about_activate)
        self.add_simple_action('quit', self.on_action_quit_activate)

    def on_app_activate(self, app):
        # signal should be connected after all pages in notebook all added.
        self.init_notebook()
        self.notebook.connect('switch-page', self.on_notebook_switch_page)

        # load styles
        self.load_styles()
        self.window.show_all()
        self.init_status_icon()

        # make some changes after main window is shown.
        self.lrc.after_init()
        self.player.after_init()

    def run(self, argv):
        self.app.run(argv)

    def quit(self):
        self.window.destroy()
        self.app.quit()

    def on_app_shutdown(self, app):
        Config.dump_conf(self.conf)

    def on_main_window_resized(self, window, event=None):
        self.conf['window-size'] = window.get_size()

    def on_main_window_deleted(self, window, event):
        window.hide()
        return True

    def on_action_preferences_activate(self, action, param):
        dialog = Preferences(self)
        dialog.run()
        dialog.destroy()

    def on_action_about_activate(self, action, param):
        dialog = Gtk.AboutDialog()
        dialog.set_program_name(Config.APPNAME)
        dialog.set_logo(self.theme['app-logo'])
        dialog.set_version(Config.VERSION)
        dialog.set_comments(_('A simple music player for Linux Desktop'))
        dialog.set_copyright('Copyright (c) 2013 LiuLang')
        dialog.set_website(Config.HOMEPAGE)
        dialog.set_license_type(Gtk.License.GPL_3_0)
        dialog.set_authors(Config.AUTHORS)
        dialog.run()
        dialog.destroy()

    def on_action_quit_activate(self, action, param):
        self.quit()

    def add_simple_action(self, name, callback):
        action = Gio.SimpleAction.new(name, None)
        action.connect('activate', callback)
        self.app.add_action(action)

    def init_notebook(self):
        self.lrc = Lrc(self)
        self.lrc.app_page = self.notebook.append_page(
                self.lrc, Gtk.Label(_('Lyrics')))

        self.playlist = PlayList(self)
        self.playlist.app_page = self.notebook.append_page(
                self.playlist, Gtk.Label(_('Playlist')))

        self.search = Search(self)
        self.search.app_page = self.notebook.append_page(
                self.search, Gtk.Label(_('Search')))

        self.toplist = TopList(self)
        self.toplist.app_page = self.notebook.append_page(
                self.toplist, Gtk.Label(_('Top List')))

        self.radio = Radio(self)
        self.radio.app_page = self.notebook.append_page(
                self.radio, Gtk.Label(_('Radio')))

        self.mv = MV(self)
        self.mv.app_page = self.notebook.append_page(
                self.mv, Gtk.Label(_('MV')))

        self.artists = Artists(self)
        self.artists.app_page = self.notebook.append_page(
                self.artists, Gtk.Label(_('Artists')))

        self.topcategories = TopCategories(self)
        self.topcategories.app_page = self.notebook.append_page(
                self.topcategories, Gtk.Label(_('Categories')))

        self.themes = Themes(self)
        self.themes.app_page = self.notebook.append_page(
                self.themes, Gtk.Label(_('Themes')))

    def on_notebook_switch_page(self, notebook, page, page_num):
        page.first()

    def popup_page(self, page):
        self.notebook.set_current_page(page)

    def load_styles(self):
        style_file = os.path.join(self.conf['theme'], 'main.css')
        with open(style_file, 'rb') as fh:
            css = fh.read()

        style_provider = Gtk.CssProvider()
        style_provider.load_from_data(css)
        Gtk.StyleContext.add_provider_for_screen(
                Gdk.Screen.get_default(),
                style_provider,
                Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
                )

    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)
        #self.status_icon.set_screen(self.window.get_screen())
        #self.status_icon.set_tooltip_text('tray icon')

    def on_status_icon_activate(self, status_icon):
        is_visible = self.window.is_visible()
        if is_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):
        if self.player.is_playing():
            self.player.pause_player()
        else:
            self.player.start_player()

    def on_status_icon_next_activate(self, menuitem):
        self.player.load_next()

    def on_status_icon_quit_activate(self, menuitem):
        self.quit()
示例#7
0
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)