示例#1
0
class RulesView(Widget):
    """The view to edit a rule

    Presents three tabs, one each for trigger, prereq, and action. Each has a
    deckbuilder in it with a column of used functions and a column of unused actions.

    """
    engine = ObjectProperty()
    rulebook = ObjectProperty()
    entity = ObjectProperty()
    rule = ObjectProperty(allownone=True)

    def on_rule(self, *args):
        """Make sure to update when the rule changes"""
        if self.rule is None:
            return
        self.rule.connect(self._listen_to_rule)

    def _listen_to_rule(self, rule, **kwargs):
        if rule is not self.rule:
            rule.disconnect(self._listen_to_rule)
            return
        if 'triggers' in kwargs:
            self.pull_triggers()
        if 'prereqs' in kwargs:
            self.pull_prereqs()
        if 'actions' in kwargs:
            self.pull_actions()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.finalize()

    def finalize(self, *args):
        """Add my tabs"""
        if not self.canvas:
            Clock.schedule_once(self.finalize, 0)
            return

        deck_builder_kwargs = {
            'pos_hint': {
                'x': 0,
                'y': 0
            },
            'starting_pos_hint': {
                'x': 0.05,
                'top': 0.95
            },
            'card_size_hint': (0.3, 0.4),
            'card_hint_step': (0, -0.1),
            'deck_x_hint_step': 0.4
        }

        self._tabs = TabbedPanel(size=self.size,
                                 pos=self.pos,
                                 do_default_tab=False)
        self.bind(size=self._tabs.setter('size'), pos=self._tabs.setter('pos'))
        self.add_widget(self._tabs)

        for functyp in 'trigger', 'prereq', 'action':
            tab = TabbedPanelItem(text=functyp.capitalize())
            setattr(self, '_{}_tab'.format(functyp), tab)
            self._tabs.add_widget(getattr(self, '_{}_tab'.format(functyp)))
            builder = DeckBuilderView(**deck_builder_kwargs)
            setattr(self, '_{}_builder'.format(functyp), builder)
            builder.bind(
                decks=getattr(self, '_trigger_push_{}s'.format(functyp)))
            scroll_left = DeckBuilderScrollBar(size_hint_x=0.01,
                                               pos_hint={
                                                   'x': 0,
                                                   'y': 0
                                               },
                                               deckbuilder=builder,
                                               deckidx=0,
                                               scroll_min=0)
            setattr(self, '_scroll_left_' + functyp, scroll_left)
            scroll_right = DeckBuilderScrollBar(size_hint_x=0.01,
                                                pos_hint={
                                                    'right': 1,
                                                    'y': 0
                                                },
                                                deckbuilder=builder,
                                                deckidx=1,
                                                scroll_min=0)
            setattr(self, '_scroll_right_' + functyp, scroll_right)
            layout = FloatLayout()
            setattr(self, '_{}_layout'.format(functyp), layout)
            tab.add_widget(layout)
            layout.add_widget(builder)
            layout.add_widget(scroll_left)
            layout.add_widget(scroll_right)
            layout.add_widget(
                Label(text='Used',
                      pos_hint={
                          'center_x': 0.1,
                          'center_y': 0.98
                      },
                      size_hint=(None, None)))
            layout.add_widget(
                Label(text='Unused',
                      pos_hint={
                          'center_x': 0.5,
                          'center_y': 0.98
                      },
                      size_hint=(None, None)))
            self.bind(rule=getattr(self, '_trigger_pull_{}s'.format(functyp)))

    def get_functions_cards(self, what, allfuncs):
        """Return a pair of lists of Card widgets for used and unused functions.

        :param what: a string: 'trigger', 'prereq', or 'action'
        :param allfuncs: a sequence of functions' (name, sourcecode, signature)

        """
        if not self.rule:
            return [], []
        rulefuncnames = getattr(self.rule, what + 's')
        unused = [
            Card(ud={
                'type': what,
                'funcname': name,
                'signature': sig
            },
                 headline_text=name,
                 show_art=False,
                 midline_text=what.capitalize(),
                 text=source) for (name, source, sig) in allfuncs
            if name not in rulefuncnames
        ]
        used = [
            Card(ud={
                'type': what,
                'funcname': name,
            },
                 headline_text=name,
                 show_art=False,
                 midline_text=what.capitalize(),
                 text=str(getattr(getattr(self.engine, what), name)))
            for name in rulefuncnames
        ]
        return used, unused

    def set_functions(self, what, allfuncs):
        """Set the cards in the ``what`` builder to ``allfuncs``

        :param what: a string, 'trigger', 'prereq', or 'action'
        :param allfuncs: a sequence of triples of (name, sourcecode, signature) as taken by my
        ``get_function_cards`` method.

        """
        setattr(getattr(self, '_{}_builder'.format(what)), 'decks',
                self.get_functions_cards(what, allfuncs))

    def _pull_functions(self, what):
        return self.get_functions_cards(
            what,
            list(
                map(self.inspect_func,
                    getattr(self.engine, what)._cache.items())))

    def pull_triggers(self, *args):
        """Refresh the cards in the trigger builder"""
        self._trigger_builder.decks = self._pull_functions('trigger')

    _trigger_pull_triggers = trigger(pull_triggers)

    def pull_prereqs(self, *args):
        """Refresh the cards in the prereq builder"""
        self._prereq_builder.decks = self._pull_functions('prereq')

    _trigger_pull_prereqs = trigger(pull_prereqs)

    def pull_actions(self, *args):
        """Refresh the cards in the action builder"""
        self._action_builder.decks = self._pull_functions('action')

    _trigger_pull_actions = trigger(pull_actions)

    def inspect_func(self, namesrc):
        """Take a function's (name, sourcecode) and return a triple of (name, sourcecode, signature)"""
        (name, src) = namesrc
        glbls = {}
        lcls = {}
        exec(src, glbls, lcls)
        assert name in lcls
        func = lcls[name]
        return name, src, signature(func)

    def update_builders(self, *args):
        for attrn in '_trigger_builder', '_prereq_builder', '_action_builder':
            if not hasattr(self, attrn):
                dbg('RulesView: no {}'.format(attrn))
                Clock.schedule_once(self.update_builders, 0)
                return
        self._trigger_builder.clear_widgets()
        self._prereq_builder.clear_widgets()
        self._action_builder.clear_widgets()
        if self.rule is None:
            dbg('RulesView: no rule')
            return
        if hasattr(self, '_list'):
            self._list.redata()
        self.pull_triggers()
        self.pull_prereqs()
        self.pull_actions()

    _trigger_update_builders = trigger(update_builders)

    def _upd_unused(self, what):
        """Make sure to have exactly one copy of every valid function in the
        "unused" pile on the right.

        Doesn't read from the database.

        :param what: a string, 'trigger', 'prereq', or 'action'

        """
        builder = getattr(self, '_{}_builder'.format(what))
        updtrig = getattr(self, '_trigger_upd_unused_{}s'.format(what))
        builder.unbind(decks=updtrig)
        funcs = OrderedDict()
        cards = list(self._action_builder.decks[1])
        cards.reverse()
        for card in cards:
            funcs[card.ud['funcname']] = card
        for card in self._action_builder.decks[0]:
            if card.ud['funcname'] not in funcs:
                funcs[card.ud['funcname']] = card.copy()
        unused = list(funcs.values())
        unused.reverse()
        builder.decks[1] = unused
        builder.bind(decks=updtrig)

    def upd_unused_actions(self, *args):
        self._upd_unused('action')

    _trigger_upd_unused_actions = trigger(upd_unused_actions)

    def upd_unused_triggers(self, *args):
        self._upd_unused('trigger')

    _trigger_upd_unused_triggers = trigger(upd_unused_triggers)

    def upd_unused_prereqs(self, *args):
        self._upd_unused('prereq')

    _trigger_upd_unused_prereqs = trigger(upd_unused_prereqs)

    def _push_funcs(self, what):
        if not self.rule:
            Logger.debug(
                "RulesView: not pushing {} for lack of rule".format(what))
            return
        funcs = [
            card.ud['funcname']
            for card in getattr(self, '_{}_builder'.format(what)).decks[0]
        ]
        funlist = getattr(self.rule, what + 's')
        if funlist != funcs:
            setattr(self.rule, what + 's', funcs)

    def push_actions(self, *args):
        self._push_funcs('action')

    _trigger_push_actions = trigger(push_actions)

    def push_prereqs(self, *args):
        self._push_funcs('prereq')

    _trigger_push_prereqs = trigger(push_prereqs)

    def push_triggers(self, att, *args):
        self._push_funcs('trigger')

    _trigger_push_triggers = trigger(push_triggers)
示例#2
0
class RulesView(Widget):
    """The view to edit a rule

    Presents three tabs, one each for trigger, prereq, and action. Each has a
    deckbuilder in it with a column of used functions and a column of unused actions.

    """
    engine = ObjectProperty()
    rulebook = ObjectProperty()
    entity = ObjectProperty()
    rule = ObjectProperty(allownone=True)

    def on_rule(self, *args):
        """Make sure to update when the rule changes"""
        if self.rule is None:
            return
        self.rule.connect(self._listen_to_rule)

    def _listen_to_rule(self, rule, **kwargs):
        if rule is not self.rule:
            rule.disconnect(self._listen_to_rule)
            return
        if 'triggers' in kwargs:
            self.pull_triggers()
        if 'prereqs' in kwargs:
            self.pull_prereqs()
        if 'actions' in kwargs:
            self.pull_actions()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.finalize()

    def finalize(self, *args):
        """Add my tabs"""
        if not self.canvas:
            Clock.schedule_once(self.finalize, 0)
            return

        deck_builder_kwargs = {
            'pos_hint': {'x': 0, 'y': 0},
            'starting_pos_hint': {'x': 0.05, 'top': 0.95},
            'card_size_hint': (0.3, 0.4),
            'card_hint_step': (0, -0.1),
            'deck_x_hint_step': 0.4
        }

        self._tabs = TabbedPanel(
            size=self.size,
            pos=self.pos,
            do_default_tab=False
        )
        self.bind(
            size=self._tabs.setter('size'),
            pos=self._tabs.setter('pos')
        )
        self.add_widget(self._tabs)

        for functyp in 'trigger', 'prereq', 'action':
            tab = TabbedPanelItem(text=functyp.capitalize())
            setattr(self, '_{}_tab'.format(functyp), tab)
            self._tabs.add_widget(getattr(self, '_{}_tab'.format(functyp)))
            builder = DeckBuilderView(**deck_builder_kwargs)
            setattr(self, '_{}_builder'.format(functyp), builder)
            builder.bind(decks=getattr(self, '_trigger_push_{}s'.format(functyp)))
            scroll_left = DeckBuilderScrollBar(
                size_hint_x=0.01,
                pos_hint={'x': 0, 'y': 0},
                deckbuilder=builder,
                deckidx=0,
                scroll_min=0
            )
            setattr(self, '_scroll_left_' + functyp, scroll_left)
            scroll_right = DeckBuilderScrollBar(
                size_hint_x=0.01,
                pos_hint={'right': 1, 'y': 0},
                deckbuilder=builder,
                deckidx=1,
                scroll_min=0
            )
            setattr(self, '_scroll_right_' + functyp, scroll_right)
            layout = FloatLayout()
            setattr(self, '_{}_layout'.format(functyp), layout)
            tab.add_widget(layout)
            layout.add_widget(builder)
            layout.add_widget(scroll_left)
            layout.add_widget(scroll_right)
            layout.add_widget(
                Label(
                    text='Used',
                    pos_hint={'center_x': 0.1, 'center_y': 0.98},
                    size_hint=(None, None)
                )
            )
            layout.add_widget(
                Label(
                    text='Unused',
                    pos_hint={'center_x': 0.5, 'center_y': 0.98},
                    size_hint=(None, None)
                )
            )
            self.bind(rule=getattr(self, '_trigger_pull_{}s'.format(functyp)))

    def get_functions_cards(self, what, allfuncs):
        """Return a pair of lists of Card widgets for used and unused functions.

        :param what: a string: 'trigger', 'prereq', or 'action'
        :param allfuncs: a sequence of functions' (name, sourcecode, signature)

        """
        if not self.rule:
            return [], []
        rulefuncnames = getattr(self.rule, what+'s')
        unused = [
            Card(
                ud={
                    'type': what,
                    'funcname': name,
                    'signature': sig
                },
                headline_text=name,
                show_art=False,
                midline_text=what.capitalize(),
                text=source
            )
            for (name, source, sig) in allfuncs if name not in rulefuncnames
        ]
        used = [
            Card(
                ud={
                    'type': what,
                    'funcname': name,
                },
                headline_text=name,
                show_art=False,
                midline_text=what.capitalize(),
                text=str(getattr(getattr(self.engine, what), name))
            )
            for name in rulefuncnames
        ]
        return used, unused

    def set_functions(self, what, allfuncs):
        """Set the cards in the ``what`` builder to ``allfuncs``

        :param what: a string, 'trigger', 'prereq', or 'action'
        :param allfuncs: a sequence of triples of (name, sourcecode, signature) as taken by my
        ``get_function_cards`` method.

        """
        setattr(getattr(self, '_{}_builder'.format(what)), 'decks', self.get_functions_cards(what, allfuncs))

    def _pull_functions(self, what):
        return self.get_functions_cards(what, list(map(self.inspect_func, getattr(self.engine, what)._cache.items())))

    def pull_triggers(self, *args):
        """Refresh the cards in the trigger builder"""
        self._trigger_builder.decks = self._pull_functions('trigger')
    _trigger_pull_triggers = trigger(pull_triggers)

    def pull_prereqs(self, *args):
        """Refresh the cards in the prereq builder"""
        self._prereq_builder.decks = self._pull_functions('prereq')
    _trigger_pull_prereqs = trigger(pull_prereqs)

    def pull_actions(self, *args):
        """Refresh the cards in the action builder"""
        self._action_builder.decks = self._pull_functions('action')
    _trigger_pull_actions = trigger(pull_actions)

    def inspect_func(self, namesrc):
        """Take a function's (name, sourcecode) and return a triple of (name, sourcecode, signature)"""
        (name, src) = namesrc
        glbls = {}
        lcls = {}
        exec(src, glbls, lcls)
        assert name in lcls
        func = lcls[name]
        return name, src, signature(func)

    def update_builders(self, *args):
        for attrn in '_trigger_builder', '_prereq_builder', '_action_builder':
            if not hasattr(self, attrn):
                dbg('RulesView: no {}'.format(attrn))
                Clock.schedule_once(self.update_builders, 0)
                return
        self._trigger_builder.clear_widgets()
        self._prereq_builder.clear_widgets()
        self._action_builder.clear_widgets()
        if self.rule is None:
            dbg('RulesView: no rule')
            return
        if hasattr(self, '_list'):
            self._list.redata()
        self.pull_triggers()
        self.pull_prereqs()
        self.pull_actions()
    _trigger_update_builders = trigger(update_builders)

    def _upd_unused(self, what):
        """Make sure to have exactly one copy of every valid function in the
        "unused" pile on the right.

        Doesn't read from the database.

        :param what: a string, 'trigger', 'prereq', or 'action'

        """
        builder = getattr(self, '_{}_builder'.format(what))
        updtrig = getattr(self, '_trigger_upd_unused_{}s'.format(what))
        builder.unbind(decks=updtrig)
        funcs = OrderedDict()
        cards = list(self._action_builder.decks[1])
        cards.reverse()
        for card in cards:
            funcs[card.ud['funcname']] = card
        for card in self._action_builder.decks[0]:
            if card.ud['funcname'] not in funcs:
                funcs[card.ud['funcname']] = card.copy()
        unused = list(funcs.values())
        unused.reverse()
        builder.decks[1] = unused
        builder.bind(decks=updtrig)

    def upd_unused_actions(self, *args):
        self._upd_unused('action')
    _trigger_upd_unused_actions = trigger(upd_unused_actions)

    def upd_unused_triggers(self, *args):
        self._upd_unused('trigger')
    _trigger_upd_unused_triggers = trigger(upd_unused_triggers)

    def upd_unused_prereqs(self, *args):
        self._upd_unused('prereq')
    _trigger_upd_unused_prereqs = trigger(upd_unused_prereqs)

    def _push_funcs(self, what):
        if not self.rule:
            Logger.debug("RulesView: not pushing {} for lack of rule".format(what))
            return
        funcs = [
            card.ud['funcname'] for card in
            getattr(self, '_{}_builder'.format(what)).decks[0]
        ]
        funlist = getattr(self.rule, what+'s')
        if funlist != funcs:
            setattr(self.rule, what+'s', funcs)

    def push_actions(self, *args):
        self._push_funcs('action')
    _trigger_push_actions = trigger(push_actions)

    def push_prereqs(self, *args):
        self._push_funcs('prereq')
    _trigger_push_prereqs = trigger(push_prereqs)

    def push_triggers(self, att, *args):
        self._push_funcs('trigger')
    _trigger_push_triggers = trigger(push_triggers)