Esempio n. 1
0
class Browser:
    """GUI 页面管理中心

    将 feeluown 类比为浏览器:magicbox 是搜索框,RightPanel
    部分是浏览器主体。
    """
    def __init__(self, app):
        self._app = app
        # 保存后退、前进历史的两个栈
        self._back_stack = deque(maxlen=10)
        self._forward_stack = deque(maxlen=10)
        self.router = Router()  # alpha

        self._last_uri = None
        self.current_uri = None

    @property
    def ui(self):
        return self._app.ui

    def goto(self, model=None, uri=None, query=None):
        """跳转到 model 页面或者具体的地址

        必须提供 model 或者 uri 其中一个参数,都提供时,以 model 为准。
        """
        if query:
            qs = urlencode(query)
            uri = uri + '?' + qs
        self._goto(model, uri)
        if self._last_uri is not None and self._last_uri != self.current_uri:
            self._back_stack.append(self._last_uri)
        self._forward_stack.clear()
        self.on_history_changed()

    def back(self):
        try:
            uri = self._back_stack.pop()
        except IndexError:
            logger.warning("Can't go back.")
        else:
            self._goto(uri=uri)
            self._forward_stack.append(self._last_uri)
            self.on_history_changed()

    def forward(self):
        try:
            uri = self._forward_stack.pop()
        except IndexError:
            logger.warning("Can't go forward.")
        else:
            self._goto(uri=uri)
            self._back_stack.append(self._last_uri)
            self.on_history_changed()

    def route(self, rule):
        """路由装饰器 (alpha)"""
        return self.router.route(rule)

    def _goto(self, model=None, uri=None):
        """真正的跳转逻辑"""
        if model is None:
            try:
                model = resolve(uri)
            except ResolveFailed:
                model = None
        else:
            uri = reverse(model)
        if not uri.startswith('fuo://'):
            uri = 'fuo://' + uri
        with self._app.create_action('-> {}'.format(uri)) as action:
            if model is not None:
                self._render_model(model)
            else:
                try:
                    self.router.dispatch(uri, {'app': self._app})
                except NotFound:
                    action.failed('not found.'.format(uri))
                    return
        self._last_uri = self.current_uri
        if model is not None:
            self.current_uri = reverse(model)
        else:
            self.current_uri = uri

    @property
    def can_back(self):
        return len(self._back_stack) > 0

    @property
    def can_forward(self):
        return len(self._forward_stack) > 0

    # --------------
    # UI Controllers
    # --------------

    def _render_model(self, model):
        """渲染 model 页面"""
        self._app.ui.right_panel.show_model(model)

    def _render_coll(self, _, identifier):
        coll = self._app.coll_uimgr.get(int(identifier))
        self._app.ui.right_panel.show_collection(coll)

    def on_history_changed(self):
        self.ui.back_btn.setEnabled(self.can_back)
        self.ui.forward_btn.setEnabled(self.can_forward)

    # --------------
    # initialization
    # --------------

    def initialize(self):
        """browser should be initialized after all ui components are created

        1. bind routes with handler
        """
        urlpatterns = [
            ('/colls/<identifier>', self._render_coll),
        ]
        for url, handler in urlpatterns:
            self.route(url)(handler)
Esempio n. 2
0
class Browser:
    """GUI 页面管理中心

    将 feeluown 类比为浏览器:magicbox 是搜索框,RightPanel
    部分是浏览器主体。
    """

    def __init__(self, app):
        self._app = app
        # 保存后退、前进历史的两个栈
        self._back_stack = deque(maxlen=10)
        self._forward_stack = deque(maxlen=10)
        self._model_parser = ModelParser(self._app.library)
        self.router = Router()  # alpha

        self._last_uri = None
        self.current_uri = None

    @property
    def ui(self):
        return self._app.ui

    def goto(self, model=None, uri=None, query=None):
        """跳转到 model 页面或者具体的地址

        必须提供 model 或者 uri 其中一个参数,都提供时,以 model 为准。
        """
        if query:
            qs = urlencode(query)
            uri = uri + '?' + qs
        self._goto(model, uri)
        if self._last_uri is not None and self._last_uri != self.current_uri:
            self._back_stack.append(self._last_uri)
        self._forward_stack.clear()
        self.on_history_changed()

    def back(self):
        try:
            uri = self._back_stack.pop()
        except IndexError:
            logger.warning("Can't go back.")
        else:
            self._goto(uri=uri)
            self._forward_stack.append(self._last_uri)
            self.on_history_changed()

    def forward(self):
        try:
            uri = self._forward_stack.pop()
        except IndexError:
            logger.warning("Can't go forward.")
        else:
            self._goto(uri=uri)
            self._back_stack.append(self._last_uri)
            self.on_history_changed()

    def route(self, rule):
        """路由装饰器 (alpha)"""
        return self.router.route(rule)

    def _goto(self, model=None, uri=None):
        """真正的跳转逻辑"""
        if model is None:
            model = self._to_model(uri)
        else:
            uri = self._to_uri(model)
        if not uri.startswith('fuo://'):
            uri = 'fuo://' + uri
        with self._app.create_action('-> {}'.format(uri)) as action:
            if model is not None:
                self._render(model)
            else:
                try:
                    self.router.dispatch(uri, {'app': self._app})
                except NotFound:
                    action.failed('not found.'.format(uri))
                    return
        self._last_uri = self.current_uri
        if model is not None:
            self.current_uri = self._to_uri(model)
        else:
            self.current_uri = uri

    @property
    def can_back(self):
        return len(self._back_stack) > 0

    @property
    def can_forward(self):
        return len(self._forward_stack) > 0

    def _to_model(self, uri):
        """从 uri 获取 model"""
        return self._model_parser.parse_line(uri)

    def _to_uri(self, model):
        return get_url(model)

    # --------------
    # UI Controllers
    # --------------

    def _render(self, model):
        """渲染 model 页面"""
        asyncio.ensure_future(self.ui.songs_table_container.show_model(model))

    def on_history_changed(self):
        self.ui.back_btn.setEnabled(self.can_back)
        self.ui.forward_btn.setEnabled(self.can_forward)
Esempio n. 3
0
class Browser:
    """GUI 页面管理中心

    将 feeluown 类比为浏览器:magicbox 是搜索框,RightPanel
    部分是浏览器主体。
    """
    def __init__(self, app):
        self._app = app
        # 保存后退、前进历史的两个栈
        self._back_stack = deque(maxlen=10)
        self._forward_stack = deque(maxlen=10)
        self.router = Router()  # alpha

        self._last_uri = None
        self.current_uri = None

        #: the value in local_storage must be string,
        # please follow the convention
        self.local_storage = {}

        self._app.hotkey_mgr.register([QKeySequence.Back], self.back)
        self._app.hotkey_mgr.register([QKeySequence.Forward], self.forward)

    @property
    def ui(self):
        return self._app.ui

    def goto(self, model=None, path=None, uri=None, query=None):
        """跳转到 model 页面或者具体的地址

        必须提供 model 或者 path 其中一个参数,都提供时,以 model 为准。
        """
        if uri is not None:
            warnings.warn('please use path instead of uri')
            path = uri
        if query:
            qs = urlencode(query)
            path = path + '?' + qs
        self._goto(model, path)
        if self._last_uri is not None and self._last_uri != self.current_uri:
            self._back_stack.append(self._last_uri)
        self._forward_stack.clear()
        self.on_history_changed()

    def back(self):
        try:
            uri = self._back_stack.pop()
        except IndexError:
            logger.warning("Can't go back.")
        else:
            self._goto(uri=uri)
            self._forward_stack.append(self._last_uri)
            self.on_history_changed()

    def forward(self):
        try:
            uri = self._forward_stack.pop()
        except IndexError:
            logger.warning("Can't go forward.")
        else:
            self._goto(uri=uri)
            self._back_stack.append(self._last_uri)
            self.on_history_changed()

    def route(self, rule):
        """路由装饰器 (alpha)"""
        return self.router.route(rule)

    def _goto(self, model=None, uri=None):
        """真正的跳转逻辑"""
        if model is None:
            try:
                model = resolve(uri)
            except ResolveFailed:
                model = None
        else:
            uri = reverse(model)
        if not uri.startswith('fuo://'):
            uri = 'fuo://' + uri
        with self._app.create_action('-> {}'.format(uri)) as action:
            if model is not None:
                self._render_model(model)
            else:
                try:
                    x = self.router.dispatch(uri, {'app': self._app})
                    if inspect.iscoroutine(x):
                        aio.create_task(x)
                except NotFound:
                    action.failed(f'{uri} not found.')
                    return
        self._last_uri = self.current_uri
        if model is not None:
            self.current_uri = reverse(model)
        else:
            self.current_uri = uri

    @property
    def can_back(self):
        return len(self._back_stack) > 0

    @property
    def can_forward(self):
        return len(self._forward_stack) > 0

    # --------------
    # UI Controllers
    # --------------

    def _render_model(self, model):
        """渲染 model 页面"""
        self._app.ui.right_panel.show_model(model)

    def _render_coll(self, _, identifier):
        coll = self._app.coll_uimgr.get(int(identifier))
        self._app.ui.right_panel.show_collection(coll)

    def on_history_changed(self):
        self.ui.back_btn.setEnabled(self.can_back)
        self.ui.forward_btn.setEnabled(self.can_forward)

    # --------------
    # initialization
    # --------------

    def initialize(self):
        """browser should be initialized after all ui components are created

        1. bind routes with handler
        """
        from feeluown.gui.pages import (
            render_search,
            render_player_playlist,
        )

        urlpatterns = [
            ('/colls/<identifier>', self._render_coll),
            ('/search', render_search),
            ('/player_playlist', render_player_playlist),
        ]
        for url, handler in urlpatterns:
            self.route(url)(handler)