Пример #1
0
class EditScreen(Screen):
    # dropdown_wid = ObjectProperty()
    dropdown_btn_wid = ObjectProperty()
    node_info_wid = ObjectProperty()
    refresh_btn = ObjectProperty()
    storyboard_wid = ObjectProperty()

    def __init__(self, **kwargs):
        super(EditScreen, self).__init__(**kwargs)
        self.d = None
        self.init_node_select_dropdown()
        self.story = Story()
        self.current_node = None
        self.refresh_btn.bind(on_release=self.refresh_graph)
        self.current_file = ''

    def init_node_select_dropdown(self):
        self.d = d = DropDown()
        b = Button(text='Add a node...', size_hint_y=None, height=dp(30))
        b.bind(on_release=self.show_add_popup)
        d.add_widget(b)
        d.bind(on_select=lambda _, text: setattr(self.dropdown_btn_wid, 'text', text))
        self.dropdown_btn_wid.bind(on_release=d.open)

    def show_file_browser(self, action):
        p = FileBrowserPopup()

        def update_current_file(instance, text):
            self.current_file = p.current_path + '/' + text.strip()

        if action == 'save as':
            p.filename_inp.bind(text=update_current_file)
            p.action_btn.text = 'Save'

            def save(button):
                self.save_current()
                p.dismiss()
            p.action_btn.bind(on_release=save)
            p.open()
        elif action == 'load':
            p.action_btn.text = 'Load'
            
            def load(button):
                path = p.selected_file
                self.load_story(path)
                p.dismiss()
            p.action_btn.bind(on_release=load)
            p.open()

    def save_current(self):
        if self.current_file:
            filename = self.current_file.strip()
            
            # check whether user has appended '.story' filename extension
            filename = filename.split('.')
            if len(filename) == 1 or filename[-1] != 'story':
                filename.append('story')
            filename = '.'.join(filename)
            print(filename)
            with open(filename, 'w') as f:
                pickle.dump(self.story, f)
        else:
            self.show_file_browser('save as')

    def load_story(self, path):
        # TODO: check whether current file is saved
        with open(path, 'r') as f:
            try:
                obj = pickle.load(f)
            except IOError as e:
                print(str(e))
                return

            if isinstance(obj, Story):
                self.story = obj
            else:
                # TODO: display helpful message
                return

            for node in self.story:
                b = Button(text=node, size_hint_y=None, height=dp(30)) 
                b.bind(on_release=self.show_info_callback)
                self.d.add_widget(b)

            self.refresh_graph()

    def check_current_file(self):
        board = self.ids['board']
        if board.current_file:
            board.save_story(board.current_file)
        else:
            self.show_file_chooser()

    def refresh_graph(self, *args):
        g = self.story
        plt.clf()
        nx.draw_networkx(g, node_size=2000, font_size=dp(16), node_shape=',')

        curr_axes = plt.gca()
        curr_axes.axes.get_xaxis().set_visible(False)
        curr_axes.axes.get_yaxis().set_visible(False)
        
        buf = io.BytesIO()
        plt.savefig(buf, format='png')
        buf.seek(0)
        image = CoreImage(buf, ext='png')
        
        self.storyboard_wid.texture = image.texture
    
    def show_add_popup(self, button):
        # show the add popup
        p = AddNodePopup(attach_to=self)
        p.open()
        
        def add_callback(button):
            """Add the node to the story and update the dropdown menu
            """
            self.d.dismiss()
            node_name = p.node_name
            self.story.add_node(node_name)
            print(self.story.nodes())
            p.dismiss()

            # update the dropdown menu
            b = Button(text=node_name, size_hint_y=None, height=dp(30)) 
            b.bind(on_release=self.show_info_callback)
            self.d.add_widget(b)
        p.submit_btn_wid.bind(on_release=add_callback)

    def show_info_callback(self, button):
        """Shows the node information in the sidebar
        """
        self.current_node = button.text
        self.d.select(button.text)
        # p.dismiss()
        node_info = self.node_info_wid
        node_info.clear_widgets()
        
        node_info.add_widget(Label(text='name:'))
        # TODO: allow node renaming?
        node_info.add_widget(TextInput(text=button.text, multiline=False))

        node_info.add_widget(Label(text='actions:'))
        actions_b = Button(text='edit...')
        actions_b.bind(on_release=self.show_add_action_popup)
        node_info.add_widget(actions_b)

        node_info.add_widget(Label(text='destinations:'))
        dest_b = Button(text='edit...')
        dest_b.bind(on_release=self.show_add_dest_popup)
        node_info.add_widget(dest_b)

        node_info.add_widget(Label(text='origins:'))
        orig_b = Button(text='edit...')
        orig_b.bind(on_release=self.show_add_origin_popup)
        node_info.add_widget(orig_b)

    def show_add_action_popup(self, button):
        p = AddActionPopup(existing_actions=self.story.get_actions(self.current_node),
                attach_to=self)
        p.open()
        def add_action_callback(button):
            a = Action()
            p.add_action(a)
            # p.actions_list_wid.add_widget(a)
            # p.actions_temp.append(a)

        def update_story(action):
            if action.action_type == 'say':
                self.story.add_say(self.current_node, **action.parameters)
            elif action.action_type == 'listen':
                self.story.add_listen(self.current_node, **action.parameters)
            elif action.action_type == 'play':
                self.story.add_play(self.current_node, **action.parameters)

        def save_callback(button):
            actions = p.actions_temp
            for action in actions:
                update_story(action)
            p.dismiss()

        p.add_action_btn_wid.bind(on_release=add_action_callback)
        p.save_btn.bind(on_release=save_callback)

    def show_add_dest_popup(self, button):
        p = AddEdgePopup(title='Add destinations')
        valid_dest = self.story.nodes()
        valid_dest.remove(self.current_node)
        for dest in valid_dest:
            b = ToggleButton(text=dest, size_hint_y=None, height=dp(30))
            if dest in self.story.neighbors(self.current_node):
                b.state = 'down'
            p.node_list.add_widget(b)
        p.node_list.add_widget(Widget())

        def save_callback(button):
            to_add = []
            to_remove = []
            for child in p.node_list.children:
                if isinstance(child, ToggleButton):
                    node_name = child.text
                    if child.state == 'down':
                        to_add.append(node_name)
                    else:
                        to_remove.append(node_name)
            for node in to_add:
                if node not in self.story.neighbors(self.current_node):
                    self.story.add_edge(self.current_node, node)
            for node in to_remove:
                if node in self.story.neighbors(self.current_node):
                    self.story.remove_edge(self.current_node, node)
            p.dismiss()
        p.save_btn.bind(on_release=save_callback)
        
        p.open()

    def show_add_origin_popup(self, button):
        p = AddEdgePopup(title='Add origins')
        valid_orig = self.story.nodes()
        valid_orig.remove(self.current_node)
        for orig in valid_orig:
            b = ToggleButton(text=orig, size_hint_y=None, height=dp(30))
            if orig in self.story.predecessors(self.current_node):
                b.state = 'down'
            p.node_list.add_widget(b)
        p.node_list.add_widget(Widget())

        def save_callback(button):
            to_add = []
            to_remove = []
            for child in p.node_list.children:
                if isinstance(child, ToggleButton):
                    node_name = child.text
                    if child.state == 'down':
                        to_add.append(node_name)
                    else:
                        to_remove.append(node_name)
            for node in to_add:
                if node not in self.story.predecessors(self.current_node):
                    self.story.add_edge(node, self.current_node)
            for node in to_remove:
                if node in self.story.predecessors(self.current_node):
                    self.story.remove_edge(node, self.current_node)
            p.dismiss()
        p.save_btn.bind(on_release=save_callback)
        
        p.open()