class LabelCollectionPanel(AnnotationCollectionPanel): def __init__(self, master, app_variables, **kwargs): """ Parameters ---------- master The master widget app_variables : AppVariables kwargs keyword arguments passed through to the Frame constructor """ Frame.__init__(self, master, **kwargs) self.button_panel = Frame(self, relief=tkinter.RIDGE, borderwidth=2) # type: Frame self.new_button = Button(self.button_panel, text='New Annotation', width=28) # type: Button self.new_button.grid(row=0, column=0, sticky='NW') self.edit_button = Button(self.button_panel, text='Edit Selected Annotation', width=28) # type: Button self.edit_button.grid(row=1, column=0, sticky='NW') self.zoom_button = Button(self.button_panel, text='Zoom to Selected Annotation', width=28) # type: Button self.zoom_button.grid(row=2, column=0, sticky='NW') self.move_button = Button(self.button_panel, text='Move Selected Annotation', width=28) # type: Button self.move_button.grid(row=3, column=0, sticky='NW') self.button_panel.grid(row=0, column=0, sticky='NSEW') self.viewer = LabelCollectionViewer(self, app_variables) self.viewer.frame.grid(row=1, column=0, sticky='NSEW') self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1)
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()
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
class RCSCollectionPanel(AnnotationCollectionPanel): def __init__(self, master, app_variables, **kwargs): """ Parameters ---------- master The master widget app_variables : AppVariables kwargs keyword arguments passed through to the Frame constructor """ self.app_variables = app_variables Frame.__init__(self, master, **kwargs) self.button_panel = Frame(self, relief=tkinter.RIDGE, borderwidth=2) # type: Frame self.new_button = Button(self.button_panel, text='New Annotation', width=28) # type: Button self.new_button.grid(row=0, column=0, sticky='NW') self.edit_button = Button(self.button_panel, text='Edit Selected Annotation', width=28) # type: Button self.edit_button.grid(row=1, column=0, sticky='NW') self.zoom_button = Button(self.button_panel, text='Zoom to Selected Annotation', width=28) # type: Button self.zoom_button.grid(row=2, column=0, sticky='NW') self.move_button = Button(self.button_panel, text='Move Selected Annotation', width=28) # type: Button self.move_button.grid(row=3, column=0, sticky='NW') self.frame1 = Frame(self.button_panel) self.units_label = Label(self.frame1, text='Units:', relief=tkinter.RIDGE) self.units_label.pack(side=tkinter.LEFT) self.units_value = Combobox(self.frame1, text='') self.units_value.pack(side=tkinter.LEFT) self.frame1.grid(row=4, column=0, sticky='NW') self.button_panel.grid(row=0, column=0, sticky='NSEW') self.viewer = RCSCollectionViewer(self, app_variables) self.viewer.frame.grid(row=1, column=0, sticky='NSEW') self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1) self.units_value.on_selection(self.callback_units_select) # noinspection PyUnusedLocal def callback_units_select(self, event): self.app_variables.rcs_viewer_units = self.units_value.get() self.update_annotation_collection() def update_annotation_collection(self): """ Should be called on an update of the annotation collection """ current_units = self.units_value.get() values = self.app_variables.get_rcs_units() self.units_value.update_combobox_values(values) if len(values) == 0: self.units_value.set_text('') self.app_variables.rcs_viewer_units = '' elif current_units in values: self.units_value.set_text(current_units) self.app_variables.rcs_viewer_units = current_units elif self.app_variables.rcs_viewer_units in values: self.units_value.set_text(self.app_variables.rcs_viewer_units) else: self.units_value.set(values[0]) self.app_variables.rcs_viewer_units = values[0] self.viewer.update_annotation_collection()