Exemplo n.º 1
0
class LabelEntryPanel(Frame):
    """
    Panel for viewing and editing the details of a given label schema entry.
    """
    def __init__(self, master, app_variables, **kwargs):
        """

        Parameters
        ----------
        master : tkinter.Tk|tkinter.ToplLevel
        app_variables : AppVariables
        kwargs
            keyword arguments passed through for frame
        """

        self._app_variables = app_variables
        self._current_id = None
        self._parent_id = None
        self._new_entry = False
        self.id_changed = None  # state variable for external usage

        self.master = master
        Frame.__init__(self, master, **kwargs)
        self.header_message = Label(self, text='', padding=5)
        self.header_message.grid(row=0,
                                 column=0,
                                 sticky='NSEW',
                                 padx=3,
                                 pady=3)

        self.frame2 = Frame(self, borderwidth=1, relief=tkinter.RIDGE)
        self.id_label = Label(self.frame2,
                              text='ID:',
                              borderwidth=1,
                              relief=tkinter.RIDGE,
                              padding=5,
                              width=10)
        self.id_label.grid(row=0, column=0, sticky='NW', padx=3, pady=3)
        self.id_entry = Entry(self.frame2, text='')
        self.id_entry.grid(row=0, column=1, sticky='NEW', padx=3, pady=3)

        self.name_label = Label(self.frame2,
                                text='Name:',
                                borderwidth=1,
                                relief=tkinter.RIDGE,
                                padding=5,
                                width=10)
        self.name_label.grid(row=1, column=0, sticky='NW', padx=3, pady=3)
        self.name_entry = Entry(self.frame2, text='')
        self.name_entry.grid(row=1, column=1, sticky='NEW', padx=3, pady=3)

        self.parent_label = Label(self.frame2,
                                  text='Parent:',
                                  borderwidth=1,
                                  relief=tkinter.RIDGE,
                                  padding=5,
                                  width=10)
        self.parent_label.grid(row=2, column=0, sticky='NW', padx=3, pady=3)
        self.parent_button = Button(self.frame2, text='<Choose>')
        self.parent_button.grid(row=2, column=1, sticky='NEW', padx=3, pady=3)

        self.frame2.grid_columnconfigure(1, weight=1)
        self.frame2.grid(row=1, column=0, sticky='NSEW', padx=3, pady=3)

        self.frame3 = Frame(self, borderwidth=1, relief=tkinter.RIDGE)
        self.cancel_button = Button(self.frame3, text='Cancel')
        self.cancel_button.pack(side=tkinter.RIGHT)
        self.save_button = Button(self.frame3, text='Save')
        self.save_button.pack(side=tkinter.RIGHT)
        self.frame3.grid(row=2, column=0, sticky='NSEW', padx=3, pady=3)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.pack(fill=tkinter.BOTH, expand=tkinter.TRUE)

        # callbacks
        self.parent_button.config(command=self.parent_callback)
        self.cancel_button.config(command=self.cancel_callback)
        # save_button bound by controlling parent

        self.update_label_schema()

    @property
    def label_schema(self):
        """
        None|LabelSchema : The label schema.
        """

        return self._app_variables.label_schema

    def update_label_schema(self):
        self.update_current_id()

    @property
    def current_id(self):
        """
        None|str: The current id.
        """

        return self._current_id

    def _set_parent_text(self):
        if self._parent_id is None or self._parent_id == '':
            self.parent_button.set_text('<Top Level>')
        else:
            self.parent_button.set_text(
                self.label_schema.labels[self._parent_id])

    def update_current_id(self):
        self._current_id = self._app_variables.current_id
        if self.label_schema is None:
            self._new_entry = False
            self._parent_id = None
            self.header_message.set_text('No label schema defined.')
            self.id_entry.set_text('')
            self.id_entry.config(state='disabled')
            self.name_entry.set_text('')
            self.name_entry.config(state='disabled')
            self.parent_button.set_text('')
            self.parent_button.config(state='disabled')
        elif self._current_id is None:
            self._parent_id = None
            self._new_entry = True

            self.header_message.set_text(
                'New entry - <ID> is immutable once initialized and <Name> is for simple interpretation.'
            )

            id_suggestion = self.label_schema.suggested_next_id
            str_id_suggestion = '<ID>' if id_suggestion is None else str(
                id_suggestion)

            self.id_entry.set_text(str_id_suggestion)
            self.id_entry.config(state='normal')
            self.name_entry.set_text('<Name>')
            self.name_entry.config(state='normal')
            self._set_parent_text()
            self.parent_button.config(state='normal')
        else:
            self._new_entry = False
            self._parent_id = self.label_schema.get_parent(self._current_id)

            self.header_message.set_text(
                '<ID> is immutable, <Name> for simple interpretation.')
            self.id_entry.set_text(self._current_id)
            self.id_entry.config(state='disabled')
            self.name_entry.set_text(
                self.label_schema.labels[self._current_id])
            self.name_entry.config(state='normal')
            self._set_parent_text()
            self.parent_button.config(state='normal')

    def parent_callback(self):
        """
        Edit or populate the parent id.
        """

        if self.label_schema is None:
            return

        self._parent_id = select_schema_entry(self.label_schema,
                                              start_id=self._parent_id)
        self._set_parent_text()

    def cancel_callback(self):
        if self.label_schema is None:
            return

        self.update_current_id()
        self.close_window()
        self.master.grab_release()

    def save_function(self):
        self.id_changed = None
        if self.label_schema is None:
            return True

        the_id = self.id_entry.get()
        the_name = self.name_entry.get()
        the_parent = '' if self._parent_id is None else self._parent_id

        if self._new_entry:
            # if this is a new entry, then verify that both id and name are set
            if the_id == '<ID>' or the_name == '<Name>':
                showinfo('Entries Not Initialized',
                         message='Both `ID` and `Name` must be set.')
                return False

            try:
                self.label_schema.add_entry(the_id,
                                            the_name,
                                            the_parent=the_parent)
                self.id_changed = the_id
            except Exception as e:
                showinfo("Creation Error",
                         message="Creation error - {}".format(e))
                return False

            self._new_entry = False
            self.update_current_id()
        else:

            try:
                if self.label_schema.change_entry(the_id, the_name,
                                                  the_parent):
                    self.id_changed = the_id
            except Exception as e:
                showinfo("Edit Error", message="Editing error - {}".format(e))
                return False

        return True

    def hide_on_close(self):
        self.master.protocol("WM_DELETE_WINDOW", self.close_window)

    def close_window(self):
        self.master.withdraw()
Exemplo n.º 2
0
class LabelSpecificsPanel(Frame):
    """
    Edit/Display widget for LabelMetadata object
    """
    def __init__(self, master, app_variables, **kwargs):
        """

        Parameters
        ----------
        master
        app_variables : AppVariables
        kwargs
        """

        self.app_variables = app_variables
        self.current_metadata_list = None  # type: Union[None, LabelMetadataList]
        self.in_progess_metadata = None  # type: Union[None, LabelMetadata]
        self.has_confidence = False
        Frame.__init__(self, master, **kwargs)

        self.object_type_label = Label(self,
                                       text='Type:',
                                       relief=tkinter.RIDGE,
                                       width=15,
                                       padding=3)
        self.object_type_label.grid(row=0,
                                    column=0,
                                    sticky='NEW',
                                    padx=3,
                                    pady=3)
        self.object_type_button = Button(self, text='<Choose>')
        self.object_type_button.grid(row=0,
                                     column=1,
                                     sticky='NEW',
                                     padx=3,
                                     pady=3)

        self.comment_label = Label(self,
                                   text='Comment:',
                                   relief=tkinter.RIDGE,
                                   width=15,
                                   padding=3)
        self.comment_label.grid(row=1, column=0, sticky='NEW', padx=3, pady=3)
        self.comment_text = TextWithScrolling(self, height=5)
        self.comment_text.frame.grid(row=1,
                                     column=1,
                                     sticky='NSEW',
                                     padx=3,
                                     pady=3)

        self.confidence_label = Label(self,
                                      text='Confidence:',
                                      relief=tkinter.RIDGE,
                                      width=15,
                                      padding=3)
        self.confidence_label.grid(row=2,
                                   column=0,
                                   sticky='NEW',
                                   padx=3,
                                   pady=3)
        self.confidence_combo = Combobox(self)
        self.confidence_combo.grid(row=2,
                                   column=1,
                                   sticky='NEW',
                                   padx=3,
                                   pady=3)

        self.user_id_label = Label(self,
                                   text='User ID:',
                                   relief=tkinter.RIDGE,
                                   width=15,
                                   padding=3)
        self.user_id_label.grid(row=3, column=0, sticky='NEW', padx=3, pady=3)
        self.user_id_value = Entry(self,
                                   text='',
                                   relief=tkinter.RIDGE,
                                   width=15,
                                   state='disabled')
        self.user_id_value.grid(row=3, column=1, sticky='NEW', padx=3, pady=3)

        self.timestamp_label = Label(self,
                                     text='Timestamp:',
                                     relief=tkinter.RIDGE,
                                     width=15,
                                     padding=3)
        self.timestamp_label.grid(row=4,
                                  column=0,
                                  sticky='NEW',
                                  padx=3,
                                  pady=3)
        self.timestamp_value = Entry(self,
                                     text='',
                                     relief=tkinter.RIDGE,
                                     width=15,
                                     state='disabled')
        self.timestamp_value.grid(row=4,
                                  column=1,
                                  sticky='NEW',
                                  padx=3,
                                  pady=3)

        self.frame1 = Frame(self, borderwidth=1, relief=tkinter.RIDGE)
        self.cancel_button = Button(self.frame1, text='Cancel')
        self.cancel_button.pack(side=tkinter.RIGHT)
        self.submit_button = Button(self.frame1, text='Submit')
        self.submit_button.pack(side=tkinter.RIGHT)
        self.frame1.grid(row=5, column=0, columnspan=2, sticky='NSEW')
        self.grid_rowconfigure(2, weight=1)
        self.grid_columnconfigure(1, weight=1)

        self.object_type_button.config(
            command=self.callback_select_object_type)
        self.cancel_button.config(command=self.callback_cancel)

    def _populate_values_into_inprogress(self):
        msg = ''
        status = True
        if self.in_progess_metadata.label_id is None:
            msg += 'The label ID must be set\n'
            status = False
        comment = self.comment_text.get_value()
        self.in_progess_metadata.comment = None if comment == '' else comment

        if self.has_confidence:
            confidence = self.confidence_combo.get()
            self.in_progess_metadata.confidence = None if confidence == '' else confidence
        return status, msg

    def _fill_metadata(self, entry):
        """
        Populates the values from entry.

        Parameters
        ----------
        entry : None|LabelMetadata
        """

        if entry is None:
            self.object_type_button.set_text('')
            self.comment_text.set_value('')
            self.confidence_combo.set_text('')
            self.user_id_value.set_text('')
            self.timestamp_value.set_text('')
        else:
            self.object_type_button.set_text(
                '<Choose>' if entry.label_id is None else self.app_variables.
                get_label(entry.label_id))
            self.comment_text.set_value(
                '' if entry.comment is None else entry.comment)
            self.confidence_combo.set_text(
                '' if entry.confidence is None else str(entry.confidence))
            self.user_id_value.set_text(entry.user_id)
            self.timestamp_value.set_text(
                datetime.utcfromtimestamp(entry.timestamp).isoformat('T'))

    def set_entry(self, index):
        """
        Sets the entry to display

        Parameters
        ----------
        index : None|int
        """
        def disable():
            self.object_type_button.config(state='disabled')
            self.comment_text.config(state='disabled')
            self.confidence_combo.config(state='disabled')
            self.cancel_button.config(state='disabled')
            self.submit_button.config(state='disabled')

        def enable():
            self.object_type_button.config(state='normal')
            self.comment_text.config(state='normal')
            if self.has_confidence:
                self.confidence_combo.config(state='normal')
            self.cancel_button.config(state='normal')
            self.submit_button.config(state='normal')

        if index is not None:
            entry = self.current_metadata_list[index]
            self._fill_metadata(entry)
            disable()
            return

        if self.current_metadata_list is None:
            # nothing to be done
            self._fill_metadata(None)
            disable()
            return

        if len(self.current_metadata_list) == 0:
            self.in_progess_metadata = LabelMetadata()
        else:
            self.in_progess_metadata = self.current_metadata_list[0].replicate(
            )
            self.in_progess_metadata.comment = None
        self._fill_metadata(self.in_progess_metadata)
        enable()

    def update_annotation(self):
        feature = self.app_variables.get_current_annotation_object()
        if feature is None:
            self.current_metadata_list = None
            self.set_entry(None)
        else:
            self.current_metadata_list = feature.properties.parameters
            if len(self.current_metadata_list) > 0:
                self.set_entry(0)
            else:
                self.set_entry(None)

    def update_annotation_collection(self):
        if self.app_variables.file_annotation_collection is not None:
            confidence_values = self.app_variables.file_annotation_collection.label_schema.confidence_values
            if confidence_values is None:
                self.has_confidence = False
                self.confidence_combo.update_combobox_values([])
            else:
                self.has_confidence = True
                self.confidence_combo.update_combobox_values(
                    ['{}'.format(entry) for entry in confidence_values])
        self.update_annotation()

    def callback_select_object_type(self):
        if self.app_variables is None or \
                self.app_variables.file_annotation_collection is None or \
                self.in_progess_metadata is None:
            return  # this would be broken state, and should not happen

        current_value = self.in_progess_metadata.label_id
        value = select_schema_entry(self.app_variables.label_schema,
                                    start_id=current_value)
        if value is None:
            return

        self.in_progess_metadata.label_id = value
        self.object_type_button.set_text(self.app_variables.get_label(value))

    def callback_cancel(self):
        self.in_progess_metadata = None
        self.update_annotation()

    def callback_submit(self):
        # NB: this should be called by the controlling parent for holistic state change everywhere
        status, msg = self._populate_values_into_inprogress()
        if not status:
            showinfo('Incomplete data population', message=msg)
            return status

        self.current_metadata_list.insert_new_element(self.in_progess_metadata)
        self.in_progess_metadata = None
        return status