Exemplo n.º 1
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._graph = BuildGraph()  # type: Union[BuildGraph, ParseGraph]
        self.photo_image = None

        self.resize_condition = threading.Condition()
        self.resize_request = True
        self.resize_thread = threading.Thread(target=self._resize_thread, daemon=True)

        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        self.vertical_scrollbar = Scrollbar(self, orient=VERTICAL)
        self.vertical_scrollbar.grid(row=0, column=1, sticky='nse')

        self.horizontal_scrollbar = Scrollbar(self, orient=HORIZONTAL)
        self.horizontal_scrollbar.grid(row=1, column=0, sticky='wes')

        self.canvas = Canvas(self, width=300, height=300,
                             xscrollcommand=self.horizontal_scrollbar.set,
                             yscrollcommand=self.vertical_scrollbar.set,
                             background='white')
        self.canvas.grid(row=0, column=0, sticky='news')

        self.vertical_scrollbar.config(command=self.canvas.yview)
        self.horizontal_scrollbar.config(command=self.canvas.xview)

        self.canvas.bind("<Configure>", self.resize_canvas)

        self.resize_thread.start()
Exemplo n.º 2
0
    def __init__(self, parent, model: Model, graph: BuildGraph, *args, graph_change_callback=None,
                 **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.model = model
        self.graph = graph
        self.link_types = sorted(str(label) for label in self.model.link_types)
        self.graph_change_callback = graph_change_callback

        self.token_listing = ['%s [%s]' % (token.spelling, index)
                              for index, token in enumerate(graph.tokens)]
        assert len(self.token_listing) == len(graph.tokens)

        self.token_labels = [Label(self, text='%s [%s]' % (token.spelling, index))
                             for index, token in enumerate(graph.tokens)]
        assert len(self.token_labels) == len(graph.tokens)

        self.separators = [Separator(self, orient=HORIZONTAL) for _ in graph.tokens]
        assert len(self.separators) == len(graph.tokens)

        link_sets = []
        for source in range(len(graph.tokens)):
            link_set = set()
            for sink in graph.get_sinks(source):
                for label in graph.get_labels(source, sink):
                    link_set.add((str(label), sink))
            link_sets.append(link_set)
        assert len(link_sets) == len(graph.tokens)

        self.link_selector_maps = []  # type: List[Dict[Tuple[Optional[Property], int], Tuple]]
        for source, link_set in enumerate(link_sets):
            link_selector_map = {}
            for label, sink in link_set:
                label_drop_down = Combobox(self, values=self.link_types)
                label_drop_down.current(self.link_types.index(label))
                label_drop_down.bind("<<ComboboxSelected>>",
                                     (lambda *a, r=(source, label, sink), v=label_drop_down, **k:
                                      self.modify_link(r, label=v.get())))
                sink_drop_down = Combobox(self, values=self.token_listing)
                sink_drop_down.current(sink)
                sink_drop_down.bind('<<ComboboxSelected>>',
                                    (lambda *a, r=(source, label, sink), v=sink_drop_down, **k:
                                     self.modify_link(r, sink=self.token_listing.index(v.get()))))
                remove_button = Button(self, text='-',
                                       command=lambda r=(source, label, sink): self.modify_link(r))
                link_selector_map[label, sink] = label_drop_down, sink_drop_down, remove_button
            self.link_selector_maps.append(link_selector_map)
        assert len(self.link_selector_maps) == len(graph.tokens)

        self.new_link_selectors = []  # type: List[Tuple[Combobox, Combobox]]
        for source in range(len(self.graph.tokens)):
            label_drop_down = Combobox(self, values=[''] + self.link_types)
            label_drop_down.current(0)
            label_drop_down.bind('<<ComboboxSelected>>',
                                 (lambda *a, r=(source, None, None): self.modify_link(r, new=True)))
            sink_drop_down = Combobox(self, values=[''] + self.token_listing)
            sink_drop_down.current(0)
            sink_drop_down.bind('<<ComboboxSelected>>',
                                (lambda *a, r=(source, None, None): self.modify_link(r, new=True)))
            self.new_link_selectors.append((label_drop_down, sink_drop_down))
Exemplo n.º 3
0
    def graph(self, graph: BuildGraph) -> None:
        self._graph = graph
        for index in range(len(graph.tokens)):
            props = (self.model.top_level_properties &
                     graph.get_phrase_category(index).positive_properties)
            category = Category(self.model.default_restriction.name, props)
            self.graph.set_phrase_category(index, category)
        self.category = self.model.default_restriction
        for index in sorted(graph.find_roots()):
            self.category = graph.get_phrase_category(index)
        for prop in self.properties:
            has_prop = prop in self.category.positive_properties
            self.properties[prop][0].set(has_prop)

        self.token_editing_frame.destroy()
        self.token_editing_frame = TokenEditingFrame(
            self.scrollable_tef_container.inner_frame,
            self.model, graph,
            graph_change_callback=self.graph_change_callback
        )
        self.token_editing_frame.grid(row=0, column=0, sticky='nwes')
        self.on_property_change()
Exemplo n.º 4
0
    def __init__(self, parent, model, *args, graph_change_callback=None, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.model = model
        self._graph = BuildGraph()
        self.category = model.default_restriction
        self.graph_change_callback = graph_change_callback

        self.rowconfigure(2, weight=1)
        self.columnconfigure(0, weight=1)

        self.category_frame = Frame(self)
        self.category_frame.grid(row=0, column=0, sticky='wen')
        self.property_frame = Frame(self)
        self.property_frame.grid(row=1, column=0, sticky='wen')

        self.scrollable_tef_container = ScrollableFrame(self)
        self.scrollable_tef_container.grid(row=2, column=0, sticky='wens')
        self.scrollable_tef_container.inner_frame.rowconfigure(0, weight=1)
        self.scrollable_tef_container.inner_frame.columnconfigure(0, weight=1)
        self.token_editing_frame = TokenEditingFrame(self.scrollable_tef_container.inner_frame,
                                                     model, self._graph,
                                                     graph_change_callback=graph_change_callback)
        self.token_editing_frame.grid(row=0, column=0, sticky='nwes')

        self.category_readout = ReadoutFrame(self.category_frame, ['Category'])
        self.category_readout.grid(row=0, column=0, sticky='nw')
        self.category_readout.set('Category', self.category)

        props_per_row = 5
        top_row = {'statement', 'question', 'command', 'complete'}
        self.properties = {}
        for index, prop in enumerate(sorted(model.top_level_properties,
                                            key=lambda p: (str(p) not in top_row, str(p)))):
            variable = IntVar()
            checkbox = Checkbutton(self.property_frame, text=str(prop), variable=variable,
                                   command=self.on_property_change)
            checkbox.property_name = prop
            checkbox.variable = variable
            checkbox.grid(row=index // props_per_row, column=index % props_per_row, sticky='nw')
            self.properties[prop] = variable, checkbox
Exemplo n.º 5
0
 def utterance(self, utterance: str) -> None:
     self._utterance = utterance
     self.readout_frame.set('Utterance', utterance)
     forests = Parser(self.model).parse(utterance,
                                        timeout=time.time() + self.settings['timeout'])[0]
     if forests and not forests[0].has_gaps():
         graphs = tuple(forests[0].get_parse_graphs())
         combined_graph = BuildGraph.from_parse_graphs(graphs)
     else:
         combined_graph = BuildGraph()
         for spelling, _, _ in self.model.tokenizer.tokenize(utterance):
             combined_graph.append_token(spelling)
     for index in range(len(combined_graph.tokens)):
         combined_graph.clear_token_category(index)  # Not interested in these...
     for index in combined_graph.find_roots():
         category = combined_graph.get_phrase_category(index)
         props = (self.model.default_restriction.positive_properties &
                  category.positive_properties)
         revised_category = Category(self.model.default_restriction.name, props, ())
         combined_graph.set_phrase_category(index, revised_category)
     self._graph = combined_graph
     self.editing_frame.graph = combined_graph
     self.visualization_frame.graph = combined_graph
     self.on_graph_change()
     self.reset_button['state'] = 'normal'
Exemplo n.º 6
0
    def __init__(self, parent, model, settings, utterances: Sequence[str], on_accept, on_reject,
                 on_modify, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.model = model
        self.settings = settings
        self._utterance_index = None
        self._utterance = None
        self._graph = BuildGraph()
        self.utterances = utterances
        self.on_accept = on_accept
        self.on_reject = on_reject
        self.on_modify = on_modify

        # Frames
        self.header_frame = Frame(self, relief='groove', borderwidth=1)
        self.header_frame.grid(row=0, column=0, sticky='nwe')
        self.middle_frame = Frame(self)
        self.middle_frame.grid(row=1, column=0, sticky='news')
        self.left_frame = Frame(self.middle_frame, relief='groove', borderwidth=1)
        self.left_frame.grid(row=0, column=0, sticky='wnse')
        self.right_frame = Frame(self.middle_frame, relief='groove', borderwidth=1)
        self.right_frame.grid(row=0, column=1, sticky='ensw')
        self.footer_frame = Frame(self, relief='groove', borderwidth=1)
        self.footer_frame.grid(row=2, column=0, sticky='s')

        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        self.header_frame.columnconfigure(0, weight=1)
        self.middle_frame.rowconfigure(0, weight=1)
        self.middle_frame.columnconfigure(0, weight=1)
        self.middle_frame.columnconfigure(1, weight=10)
        for frame in self.left_frame, self.right_frame:
            frame.rowconfigure(0, weight=1)
            frame.columnconfigure(0, weight=1)

        # Header
        self.readout_frame = ReadoutFrame(self.header_frame,
                                          ['Annotation Set', 'Utterance List', 'Utterance',
                                           'Annotation'])
        self.readout_frame.grid(row=0, column=0, sticky='we')

        # Right
        self.visualization_frame = GraphVisualizationFrame(self.right_frame)
        self.visualization_frame.grid(row=0, column=0, sticky='news')

        # Left
        self.editing_frame = GraphEditingFrame(self.left_frame, model,
                                               graph_change_callback=self.on_graph_change)
        self.editing_frame.grid(row=0, column=0, sticky='news')

        # Footer
        self.first_button = Button(self.footer_frame, text='<<', state='disabled',
                                   command=self.go_to_first)
        self.first_button.grid(row=0, column=0, sticky='n')
        self.previous_button = Button(self.footer_frame, text='<', state='disabled',
                                      command=self.go_to_previous)
        self.previous_button.grid(row=0, column=1, sticky='n')
        self.reset_button = Button(self.footer_frame, text='Reset', state='disabled',
                                   command=self.reset_graph)
        self.reset_button.grid(row=0, column=2, sticky='n')
        self.reject_button = Button(self.footer_frame, text='Reject', state='disabled',
                                    command=self.reject)
        self.reject_button.grid(row=0, column=3, sticky='n')
        self.accept_button = Button(self.footer_frame, text='Accept', state='disabled',
                                    command=self.accept)
        self.accept_button.grid(row=0, column=4, sticky='n')
        self.next_button = Button(self.footer_frame, text='>', state='disabled',
                                  command=self.go_to_next)
        self.next_button.grid(row=0, column=5, sticky='n')
        self.last_button = Button(self.footer_frame, text='>>', state='disabled',
                                  command=self.go_to_last)
        self.last_button.grid(row=0, column=6, sticky='n')

        self.go_to_first()
Exemplo n.º 7
0
class AnnotationFrame(Frame):

    def __init__(self, parent, model, settings, utterances: Sequence[str], on_accept, on_reject,
                 on_modify, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.model = model
        self.settings = settings
        self._utterance_index = None
        self._utterance = None
        self._graph = BuildGraph()
        self.utterances = utterances
        self.on_accept = on_accept
        self.on_reject = on_reject
        self.on_modify = on_modify

        # Frames
        self.header_frame = Frame(self, relief='groove', borderwidth=1)
        self.header_frame.grid(row=0, column=0, sticky='nwe')
        self.middle_frame = Frame(self)
        self.middle_frame.grid(row=1, column=0, sticky='news')
        self.left_frame = Frame(self.middle_frame, relief='groove', borderwidth=1)
        self.left_frame.grid(row=0, column=0, sticky='wnse')
        self.right_frame = Frame(self.middle_frame, relief='groove', borderwidth=1)
        self.right_frame.grid(row=0, column=1, sticky='ensw')
        self.footer_frame = Frame(self, relief='groove', borderwidth=1)
        self.footer_frame.grid(row=2, column=0, sticky='s')

        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        self.header_frame.columnconfigure(0, weight=1)
        self.middle_frame.rowconfigure(0, weight=1)
        self.middle_frame.columnconfigure(0, weight=1)
        self.middle_frame.columnconfigure(1, weight=10)
        for frame in self.left_frame, self.right_frame:
            frame.rowconfigure(0, weight=1)
            frame.columnconfigure(0, weight=1)

        # Header
        self.readout_frame = ReadoutFrame(self.header_frame,
                                          ['Annotation Set', 'Utterance List', 'Utterance',
                                           'Annotation'])
        self.readout_frame.grid(row=0, column=0, sticky='we')

        # Right
        self.visualization_frame = GraphVisualizationFrame(self.right_frame)
        self.visualization_frame.grid(row=0, column=0, sticky='news')

        # Left
        self.editing_frame = GraphEditingFrame(self.left_frame, model,
                                               graph_change_callback=self.on_graph_change)
        self.editing_frame.grid(row=0, column=0, sticky='news')

        # Footer
        self.first_button = Button(self.footer_frame, text='<<', state='disabled',
                                   command=self.go_to_first)
        self.first_button.grid(row=0, column=0, sticky='n')
        self.previous_button = Button(self.footer_frame, text='<', state='disabled',
                                      command=self.go_to_previous)
        self.previous_button.grid(row=0, column=1, sticky='n')
        self.reset_button = Button(self.footer_frame, text='Reset', state='disabled',
                                   command=self.reset_graph)
        self.reset_button.grid(row=0, column=2, sticky='n')
        self.reject_button = Button(self.footer_frame, text='Reject', state='disabled',
                                    command=self.reject)
        self.reject_button.grid(row=0, column=3, sticky='n')
        self.accept_button = Button(self.footer_frame, text='Accept', state='disabled',
                                    command=self.accept)
        self.accept_button.grid(row=0, column=4, sticky='n')
        self.next_button = Button(self.footer_frame, text='>', state='disabled',
                                  command=self.go_to_next)
        self.next_button.grid(row=0, column=5, sticky='n')
        self.last_button = Button(self.footer_frame, text='>>', state='disabled',
                                  command=self.go_to_last)
        self.last_button.grid(row=0, column=6, sticky='n')

        self.go_to_first()

    @property
    def utterance(self) -> str:
        return self._utterance

    @utterance.setter
    def utterance(self, utterance: str) -> None:
        self._utterance = utterance
        self.readout_frame.set('Utterance', utterance)
        forests = Parser(self.model).parse(utterance,
                                           timeout=time.time() + self.settings['timeout'])[0]
        if forests and not forests[0].has_gaps():
            graphs = tuple(forests[0].get_parse_graphs())
            combined_graph = BuildGraph.from_parse_graphs(graphs)
        else:
            combined_graph = BuildGraph()
            for spelling, _, _ in self.model.tokenizer.tokenize(utterance):
                combined_graph.append_token(spelling)
        for index in range(len(combined_graph.tokens)):
            combined_graph.clear_token_category(index)  # Not interested in these...
        for index in combined_graph.find_roots():
            category = combined_graph.get_phrase_category(index)
            props = (self.model.default_restriction.positive_properties &
                     category.positive_properties)
            revised_category = Category(self.model.default_restriction.name, props, ())
            combined_graph.set_phrase_category(index, revised_category)
        self._graph = combined_graph
        self.editing_frame.graph = combined_graph
        self.visualization_frame.graph = combined_graph
        self.on_graph_change()
        self.reset_button['state'] = 'normal'

    @property
    def graph(self) -> BuildGraph:
        return self._graph

    def reset_graph(self):
        self.utterance = self.utterance

    def go_to(self, index):
        self._utterance_index = index
        if self.utterances:
            self.utterance = self.utterances[self._utterance_index]
        back_enabled = self.utterances and self._utterance_index > 0
        forward_enabled = self.utterances and self._utterance_index < len(self.utterances) - 1
        self.first_button['state'] = 'normal' if back_enabled else 'disabled'
        self.previous_button['state'] = 'normal' if back_enabled else 'disabled'
        self.next_button['state'] = 'normal' if forward_enabled else 'disabled'
        self.last_button['state'] = 'normal' if forward_enabled else 'disabled'

    def go_to_first(self):
        self.go_to(0)

    def go_to_previous(self):
        if self.utterances and self._utterance_index > 0:
            self.go_to(self._utterance_index - 1)

    def go_to_next(self):
        if self.utterances and self._utterance_index < len(self.utterances) - 1:
            self.go_to(self._utterance_index + 1)

    def go_to_last(self):
        if self.utterances:
            self.go_to(len(self.utterances) - 1)

    def accept(self):
        if self.on_accept:
            self.on_accept(self._utterance_index, self._utterance, self._graph,
                           self.readout_frame.get('Annotation'))
        self.go_to_next()

    def reject(self):
        if self.on_reject:
            self.on_reject(self._utterance_index, self._utterance, self._graph,
                           self.readout_frame.get('Annotation'))
        self.go_to_next()

    def on_graph_change(self):
        self.editing_frame.refresh()
        self.visualization_frame.refresh()
        annotations = self.graph.get_annotations()
        annotation_string = ('[%s]' % ']  ['.join(annotations)) if annotations else ''
        self.readout_frame.set('Annotation', annotation_string)
        self.accept_button['state'] = ('normal' if self.on_accept and self._graph.is_tree()
                                       else 'disabled')
        self.reject_button['state'] = 'normal' if self.on_reject else 'disabled'
        if self.on_modify:
            self.on_modify(self._utterance_index, self._utterance, self._graph, annotation_string)
Exemplo n.º 8
0
class GraphVisualizationFrame(Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._graph = BuildGraph()  # type: Union[BuildGraph, ParseGraph]
        self.photo_image = None

        self.resize_condition = threading.Condition()
        self.resize_request = True
        self.resize_thread = threading.Thread(target=self._resize_thread, daemon=True)

        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        self.vertical_scrollbar = Scrollbar(self, orient=VERTICAL)
        self.vertical_scrollbar.grid(row=0, column=1, sticky='nse')

        self.horizontal_scrollbar = Scrollbar(self, orient=HORIZONTAL)
        self.horizontal_scrollbar.grid(row=1, column=0, sticky='wes')

        self.canvas = Canvas(self, width=300, height=300,
                             xscrollcommand=self.horizontal_scrollbar.set,
                             yscrollcommand=self.vertical_scrollbar.set,
                             background='white')
        self.canvas.grid(row=0, column=0, sticky='news')

        self.vertical_scrollbar.config(command=self.canvas.yview)
        self.horizontal_scrollbar.config(command=self.canvas.xview)

        self.canvas.bind("<Configure>", self.resize_canvas)

        self.resize_thread.start()

    @property
    def graph(self) -> Union[BuildGraph, ParseGraph]:
        return self._graph

    @graph.setter
    def graph(self, graph: Union[BuildGraph, ParseGraph]):
        self._graph = graph
        self.refresh()

    def refresh(self):
        if not self._graph:
            self.canvas.delete("IMG")
            return

        width = self.canvas.winfo_width()
        height = self.canvas.winfo_height()

        if width <= 0 or height <= 0:
            return

        height_pixels_per_mm = self.canvas.winfo_screenheight() / self.canvas.winfo_screenmmheight()
        height_pixels_per_inch = height_pixels_per_mm * MM_PER_INCH
        height_inches = (height / height_pixels_per_inch)

        width_pixels_per_mm = self.canvas.winfo_screenwidth() / self.canvas.winfo_screenmmwidth()
        width_pixels_per_inch = width_pixels_per_mm * MM_PER_INCH
        width_inches = (width / width_pixels_per_inch)

        gv_graph = Digraph()
        gv_graph.graph_attr.update(size="%s,%s" % (width_inches / 2, height_inches / 2),
                                   ratio="expand",
                                   dpi=str(2 * max(height_pixels_per_mm, width_pixels_per_inch)))

        self._graph.visualize(gv_graph)

        image_data = gv_graph.pipe(format='png')
        original = PIL.Image.open(BytesIO(image_data))

        if width / height < original.width / original.height:
            size = (width, int(width / original.width * original.height))
        else:
            size = (int(height / original.height * original.width), height)

        if any(value <= 0 for value in size):
            return

        resized = original.resize(size, PIL.Image.ANTIALIAS)

        self.photo_image = PhotoImage(resized)
        self.canvas.delete("IMG")
        self.canvas.create_image(0, 0, image=self.photo_image, anchor='nw', tags="IMG")

    # noinspection PyUnusedLocal
    def resize_canvas(self, event):
        with self.resize_condition:
            self.resize_request = True
            self.resize_condition.notify()

    def _resize_thread(self):
        while True:
            requested = False
            with self.resize_condition:
                if self.resize_request:
                    requested = True
                    self.resize_request = False
                else:
                    self.resize_condition.wait()
            if requested:
                try:
                    self.refresh()  # TODO: Why are we getting a runtime error?
                except RuntimeError:
                    traceback.print_exc()