예제 #1
0
    def __init__(self, master, app_variables, **kwargs):
        """

        Parameters
        ----------
        master
            tkinter.Tk|tkinter.TopLevel
        app_variables : AppVariables
        kwargs
        """

        self._app_variables = app_variables  # type: AppVariables
        Frame.__init__(self, master, **kwargs)
        self.pack(expand=tkinter.TRUE, fill=tkinter.BOTH)

        self.name_panel = NamePanel(self, app_variables)  # type: NamePanel
        self.name_panel.grid(row=0, column=0, sticky='NSEW')

        self.tab_panel = LabelTabControl(
            self, app_variables)  # type: LabelTabControl
        self.tab_panel.grid(row=1, column=0, sticky='NSEW')

        self.button_panel = AnnotateButtons(self)  # type: AnnotateButtons
        self.button_panel.grid(row=2, column=0, sticky='NSEW')

        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)
예제 #2
0
    def __init__(self, primary, reader=None, **kwargs):
        """

        Parameters
        ----------
        primary
            tkinter.Tk|tkinter.TopLevel
        reader : None|str|SICDTypeReader|SICDTypeCanvasImageReader
        kwargs
        """

        self.root = primary
        self.variables = AppVariables()

        Frame.__init__(self, primary, **kwargs)
        WidgetWithMetadata.__init__(self, primary)

        self.row_centered_image_panel = ImagePanel(self, borderwidth=2, relief=tkinter.RIDGE)  # type: ImagePanel
        self.row_centered_image_panel.grid(row=0, column=0, sticky='NSEW')

        self.column_centered_image_panel = ImagePanel(self, borderwidth=2, relief=tkinter.RIDGE)  # type: ImagePanel
        self.column_centered_image_panel.grid(row=0, column=1, sticky='NSEW')
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)

        self.set_title()

        # define menus
        self.menu_bar = tkinter.Menu()
        # file menu
        self.file_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.file_menu.add_command(label="Open Image", command=self.callback_select_files)
        self.file_menu.add_command(label="Open Directory", command=self.callback_select_directory)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="Exit", command=self.exit)
        # menus for informational popups
        self.metadata_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.metadata_menu.add_command(label="Weight Plots", command=self.create_weights_plot)
        self.metadata_menu.add_command(label="Metaicon", command=self.metaicon_popup)
        self.metadata_menu.add_command(label="Metaviewer", command=self.metaviewer_popup)
        # ensure menus cascade
        self.menu_bar.add_cascade(label="File", menu=self.file_menu)
        self.menu_bar.add_cascade(label="Metadata", menu=self.metadata_menu)
        self.root.config(menu=self.menu_bar)

        # handle packing
        self.pack(fill=tkinter.BOTH, expand=tkinter.YES)

        # hide extraneous tool elements
        self.row_centered_image_panel.hide_tools(['shape_drawing', 'select', 'coords', 'measure'])
        self.row_centered_image_panel.hide_shapes()
        self.row_centered_image_panel.hide_select_index()

        self.column_centered_image_panel.hide_tools(['shape_drawing', 'select', 'coords', 'measure'])
        self.column_centered_image_panel.hide_shapes()
        self.column_centered_image_panel.hide_select_index()

        self.update_reader(reader)
예제 #3
0
    def __init__(self, parent, canvas_width=600, canvas_height=400, **kwargs):
        Frame.__init__(self, parent, **kwargs)

        # default dpi is 100, so npix will be 100 times the numbers passed to figsize
        self.fig = pyplot.figure(dpi=100,
                                 figsize=(canvas_width / float(100),
                                          canvas_height / float(100)))
        pyplot.plot(0)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.canvas.get_tk_widget().pack(fill='both')
예제 #4
0
    def __init__(self, master, app_variables, **kwargs):
        """

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

        self.app_variables = app_variables
        self.current_annotation = None  # type: Union[None, LabelFeature]
        Frame.__init__(self, master, **kwargs)

        self.update_label_button = Button(
            self, text='Update Label',
            command=self.callback_update_label)  # type: Button
        self.update_label_button.grid(row=0,
                                      column=0,
                                      columnspan=2,
                                      sticky='NE',
                                      padx=3,
                                      pady=3)

        self.viewer = TreeviewWithScrolling(
            self, columns=('confidence', ),
            selectmode=tkinter.BROWSE)  # type: TreeviewWithScrolling
        self.viewer.heading('#0', text='Label')
        self.viewer.heading('#1', text='Confidence')
        self.viewer.frame.grid(row=1, column=0, sticky='NSEW', padx=3, pady=3)
        # NB: reference the frame for packing, since it's already packed into a frame

        self.label_specifics = LabelSpecificsPanel(self,
                                                   app_variables,
                                                   border=1,
                                                   relief=tkinter.RIDGE)
        self.label_specifics.grid(row=1,
                                  column=1,
                                  sticky='NSEW',
                                  padx=3,
                                  pady=3)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.update_annotation()

        self.viewer.bind('<<TreeviewSelect>>',
                         self.callback_label_selected_on_viewer)
예제 #5
0
    def __init__(self, parent):
        """

        Parameters
        ----------
        parent
        """

        Frame.__init__(self, parent)

        self.parent = parent

        self.button_style = ttk.Style()
        self.button_style.configure('ToggleOff.TButton',
                                    font=('Arial', 24),
                                    foreground="gray50",
                                    background="PaleTurquoise3",
                                    width=3,
                                    sticky='CENTER')
        self.button_style.configure('ToggleOn.TButton',
                                    font=('Arial', 24),
                                    foreground="black",
                                    background="PaleTurquoise1",
                                    width=3,
                                    sticky='CENTER')

        self.button_rev = Button(self.parent,
                                 text="\u25C0",
                                 takefocus=0,
                                 style='ToggleOff.TButton')
        self.button_prev = Button(self.parent,
                                  text="-1",
                                  takefocus=0,
                                  style='ToggleOff.TButton')
        self.button_next = Button(self.parent,
                                  text="+1",
                                  takefocus=0,
                                  style='ToggleOff.TButton')
        self.button_fwd = Button(self.parent,
                                 text="\u25B6",
                                 takefocus=0,
                                 style='ToggleOff.TButton')
예제 #6
0
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)
예제 #7
0
    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)
예제 #8
0
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()
예제 #9
0
    def __init__(self, primary, reader=None, **kwargs):
        """

        Parameters
        ----------
        primary : tkinter.Toplevel|tkinter.Tk
        reader
        kwargs
        """

        self.root = primary
        Frame.__init__(self, primary, **kwargs)
        WidgetWithMetadata.__init__(self, primary)
        self.pack(fill=tkinter.BOTH, expand=tkinter.YES)
        self.primary = tkinter.PanedWindow(self,
                                           sashrelief=tkinter.RIDGE,
                                           orient=tkinter.HORIZONTAL)

        self.variables = AppVariables()

        self.image_panel = ImagePanel(self.primary,
                                      borderwidth=0)  # type: ImagePanel
        self.primary.add(self.image_panel,
                         width=400,
                         height=700,
                         padx=5,
                         pady=5,
                         sticky=tkinter.NSEW,
                         stretch=tkinter.FIRST)

        self.pyplot_panel = PyplotImagePanel(
            self.primary)  # type: PyplotImagePanel
        self.primary.add(self.pyplot_panel,
                         width=400,
                         height=700,
                         padx=5,
                         pady=5,
                         sticky=tkinter.NSEW)

        self.primary.pack(fill=tkinter.BOTH, expand=tkinter.YES)

        self.set_title()

        # define menus
        self.menu_bar = tkinter.Menu()
        # file menu
        self.file_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.file_menu.add_command(label="Open Image",
                                   command=self.callback_select_files)
        self.file_menu.add_command(label="Open Directory",
                                   command=self.callback_select_directory)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="Exit", command=self.exit)
        # menus for informational popups
        self.metadata_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.metadata_menu.add_command(label="Metaicon",
                                       command=self.metaicon_popup)
        self.metadata_menu.add_command(label="Metaviewer",
                                       command=self.metaviewer_popup)
        self._valid_data_shown = tkinter.IntVar(self, value=0)
        self.metadata_menu.add_checkbutton(label='ValidData',
                                           variable=self._valid_data_shown,
                                           command=self.show_valid_data)
        # ensure menus cascade
        self.menu_bar.add_cascade(label="File", menu=self.file_menu)
        self.menu_bar.add_cascade(label="Metadata", menu=self.metadata_menu)

        self.root.config(menu=self.menu_bar)

        # hide extraneous tool elements
        self.image_panel.hide_tools('shape_drawing')
        self.image_panel.hide_shapes()

        # bind canvas events for proper functionality
        # this makes for bad performance on a larger image - do not activate
        # self.image_panel.canvas.bind('<<SelectionChanged>>', self.handle_selection_change)
        self.image_panel.canvas.bind('<<SelectionFinalized>>',
                                     self.handle_selection_change)
        self.image_panel.canvas.bind('<<RemapChanged>>',
                                     self.handle_remap_change)
        self.image_panel.canvas.bind('<<ImageIndexChanged>>',
                                     self.handle_image_index_changed)

        self.update_reader(reader, update_browse=None)
예제 #10
0
    def __init__(self, master, label_schema=None, **kwargs):
        """

        Parameters
        ----------
        master : tkinter.Tk|tkinter.TopLevel
        label_schema : None|str|LabelSchema
        kwargs
            keyword arguments for Frame
        """

        self.variables = AppVariables()
        self.master = master
        Frame.__init__(self, master, **kwargs)

        self.frame1 = Frame(self, borderwidth=1, relief=tkinter.RIDGE)

        self.version_label = Label(self.frame1,
                                   text='Version Number:',
                                   relief=tkinter.RIDGE,
                                   justify=tkinter.LEFT,
                                   padding=5,
                                   width=18)
        self.version_label.grid(row=0, column=0, sticky='NEW')
        self.version_entry = Entry(self.frame1, text='')
        self.version_entry.grid(row=0, column=1, sticky='NEW')

        self.version_date_label = Label(self.frame1,
                                        text='Version Date:',
                                        relief=tkinter.RIDGE,
                                        justify=tkinter.LEFT,
                                        padding=5,
                                        width=18)
        self.version_date_label.grid(row=1, column=0, sticky='NEW')
        self.version_date_entry = Entry(self.frame1, text='')
        self.version_date_entry.grid(row=1, column=1, sticky='NEW')

        self.classification_label = Label(self.frame1,
                                          text='Classification:',
                                          relief=tkinter.RIDGE,
                                          justify=tkinter.LEFT,
                                          padding=5,
                                          width=18)
        self.classification_label.grid(row=2, column=0, sticky='NEW')
        self.classification_entry = Entry(self.frame1, text='')
        self.classification_entry.grid(row=2, column=1, sticky='NEW')

        self.confidence_label = Label(self.frame1,
                                      text='Confidence Values:',
                                      relief=tkinter.RIDGE,
                                      justify=tkinter.LEFT,
                                      padding=5,
                                      width=18)
        self.confidence_label.grid(row=3, column=0, sticky='NEW')
        self.confidence_entry = Entry(self.frame1, text='')
        self.confidence_entry.grid(row=3, column=1, sticky='NEW')

        self.geometries_label = Label(self.frame1,
                                      text='Geometries:',
                                      relief=tkinter.RIDGE,
                                      justify=tkinter.LEFT,
                                      padding=5,
                                      width=18)
        self.geometries_label.grid(row=4, column=0, sticky='NEW')
        self.geometries_entry = Entry(self.frame1, text='')
        self.geometries_entry.grid(row=4, column=1, sticky='NEW')

        self.frame1.grid_columnconfigure(1, weight=1)
        self.frame1.pack(side=tkinter.TOP, fill=tkinter.X)

        self.frame2 = Frame(self, borderwidth=1, relief=tkinter.RIDGE)
        self.new_button = Button(self.frame2, text='New Entry')
        self.new_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.edit_button = Button(self.frame2, text='Edit Entry')
        self.edit_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.delete_button = Button(self.frame2, text='Delete Entry')
        self.delete_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.move_up_button = Button(self.frame2, text='Move Entry Up')
        self.move_up_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.move_down_button = Button(self.frame2, text='Move Entry Down')
        self.move_down_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.frame2.pack(side=tkinter.TOP, fill=tkinter.X)

        self.schema_viewer = SchemaViewer(self)
        self.schema_viewer.frame.pack(side=tkinter.BOTTOM,
                                      expand=tkinter.TRUE,
                                      fill=tkinter.BOTH)
        # NB: it's already packed inside a frame

        self.pack(expand=tkinter.YES, fill=tkinter.BOTH)

        # set up the menu bar
        menu = tkinter.Menu()
        filemenu = tkinter.Menu(menu, tearoff=0)
        filemenu.add_command(label="Open Schema", command=self.callback_open)
        filemenu.add_command(label="New Schema",
                             command=self.callback_new_schema)
        filemenu.add_separator()
        filemenu.add_command(label="Save", command=self.save)
        filemenu.add_command(label="Save As", command=self.callback_save_as)
        filemenu.add_separator()
        filemenu.add_command(label="Exit", command=self.exit)
        menu.add_cascade(label="File", menu=filemenu)
        self.master.config(menu=menu)

        # setup entry configs and some validation callbacks
        self.schema_viewer.bind('<<TreeviewSelect>>',
                                self.item_selected_on_viewer)

        self.version_entry.config(validate='focusout',
                                  validatecommand=self.register(
                                      self._version_entry_validate))
        self.version_date_entry.config(state='disabled')
        self.classification_entry.config(validate='focusout',
                                         validatecommand=self.register(
                                             self._classification_validate))
        self.confidence_entry.config(validate='focusout',
                                     validatecommand=self.register(
                                         self._confidence_validate))
        self.geometries_entry.config(
            validate='focusout',
            validatecommand=(self.register(self._geometry_validate), ),
            invalidcommand=(self.register(self._geometry_invalid), ))
        self.edit_button.config(command=self.callback_edit_entry)
        self.new_button.config(command=self.callback_new_entry)
        self.delete_button.config(command=self.callback_delete_entry)
        self.move_up_button.config(command=self.callback_move_up)
        self.move_down_button.config(command=self.callback_move_down)

        # setup the entry panel
        self.entry_popup = tkinter.Toplevel(self.master)
        self.entry = LabelEntryPanel(self.entry_popup, self.variables)
        self.entry.hide_on_close()
        self.entry_popup.withdraw()

        self.entry.save_button.config(command=self.callback_save_entry)

        self.set_label_schema(label_schema)
예제 #11
0
class SchemaEditor(Frame):
    """
    An editor for a label schema
    """
    def __init__(self, master, label_schema=None, **kwargs):
        """

        Parameters
        ----------
        master : tkinter.Tk|tkinter.TopLevel
        label_schema : None|str|LabelSchema
        kwargs
            keyword arguments for Frame
        """

        self.variables = AppVariables()
        self.master = master
        Frame.__init__(self, master, **kwargs)

        self.frame1 = Frame(self, borderwidth=1, relief=tkinter.RIDGE)

        self.version_label = Label(self.frame1,
                                   text='Version Number:',
                                   relief=tkinter.RIDGE,
                                   justify=tkinter.LEFT,
                                   padding=5,
                                   width=18)
        self.version_label.grid(row=0, column=0, sticky='NEW')
        self.version_entry = Entry(self.frame1, text='')
        self.version_entry.grid(row=0, column=1, sticky='NEW')

        self.version_date_label = Label(self.frame1,
                                        text='Version Date:',
                                        relief=tkinter.RIDGE,
                                        justify=tkinter.LEFT,
                                        padding=5,
                                        width=18)
        self.version_date_label.grid(row=1, column=0, sticky='NEW')
        self.version_date_entry = Entry(self.frame1, text='')
        self.version_date_entry.grid(row=1, column=1, sticky='NEW')

        self.classification_label = Label(self.frame1,
                                          text='Classification:',
                                          relief=tkinter.RIDGE,
                                          justify=tkinter.LEFT,
                                          padding=5,
                                          width=18)
        self.classification_label.grid(row=2, column=0, sticky='NEW')
        self.classification_entry = Entry(self.frame1, text='')
        self.classification_entry.grid(row=2, column=1, sticky='NEW')

        self.confidence_label = Label(self.frame1,
                                      text='Confidence Values:',
                                      relief=tkinter.RIDGE,
                                      justify=tkinter.LEFT,
                                      padding=5,
                                      width=18)
        self.confidence_label.grid(row=3, column=0, sticky='NEW')
        self.confidence_entry = Entry(self.frame1, text='')
        self.confidence_entry.grid(row=3, column=1, sticky='NEW')

        self.geometries_label = Label(self.frame1,
                                      text='Geometries:',
                                      relief=tkinter.RIDGE,
                                      justify=tkinter.LEFT,
                                      padding=5,
                                      width=18)
        self.geometries_label.grid(row=4, column=0, sticky='NEW')
        self.geometries_entry = Entry(self.frame1, text='')
        self.geometries_entry.grid(row=4, column=1, sticky='NEW')

        self.frame1.grid_columnconfigure(1, weight=1)
        self.frame1.pack(side=tkinter.TOP, fill=tkinter.X)

        self.frame2 = Frame(self, borderwidth=1, relief=tkinter.RIDGE)
        self.new_button = Button(self.frame2, text='New Entry')
        self.new_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.edit_button = Button(self.frame2, text='Edit Entry')
        self.edit_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.delete_button = Button(self.frame2, text='Delete Entry')
        self.delete_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.move_up_button = Button(self.frame2, text='Move Entry Up')
        self.move_up_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.move_down_button = Button(self.frame2, text='Move Entry Down')
        self.move_down_button.pack(side=tkinter.LEFT, padx=5, pady=5)
        self.frame2.pack(side=tkinter.TOP, fill=tkinter.X)

        self.schema_viewer = SchemaViewer(self)
        self.schema_viewer.frame.pack(side=tkinter.BOTTOM,
                                      expand=tkinter.TRUE,
                                      fill=tkinter.BOTH)
        # NB: it's already packed inside a frame

        self.pack(expand=tkinter.YES, fill=tkinter.BOTH)

        # set up the menu bar
        menu = tkinter.Menu()
        filemenu = tkinter.Menu(menu, tearoff=0)
        filemenu.add_command(label="Open Schema", command=self.callback_open)
        filemenu.add_command(label="New Schema",
                             command=self.callback_new_schema)
        filemenu.add_separator()
        filemenu.add_command(label="Save", command=self.save)
        filemenu.add_command(label="Save As", command=self.callback_save_as)
        filemenu.add_separator()
        filemenu.add_command(label="Exit", command=self.exit)
        menu.add_cascade(label="File", menu=filemenu)
        self.master.config(menu=menu)

        # setup entry configs and some validation callbacks
        self.schema_viewer.bind('<<TreeviewSelect>>',
                                self.item_selected_on_viewer)

        self.version_entry.config(validate='focusout',
                                  validatecommand=self.register(
                                      self._version_entry_validate))
        self.version_date_entry.config(state='disabled')
        self.classification_entry.config(validate='focusout',
                                         validatecommand=self.register(
                                             self._classification_validate))
        self.confidence_entry.config(validate='focusout',
                                     validatecommand=self.register(
                                         self._confidence_validate))
        self.geometries_entry.config(
            validate='focusout',
            validatecommand=(self.register(self._geometry_validate), ),
            invalidcommand=(self.register(self._geometry_invalid), ))
        self.edit_button.config(command=self.callback_edit_entry)
        self.new_button.config(command=self.callback_new_entry)
        self.delete_button.config(command=self.callback_delete_entry)
        self.move_up_button.config(command=self.callback_move_up)
        self.move_down_button.config(command=self.callback_move_down)

        # setup the entry panel
        self.entry_popup = tkinter.Toplevel(self.master)
        self.entry = LabelEntryPanel(self.entry_popup, self.variables)
        self.entry.hide_on_close()
        self.entry_popup.withdraw()

        self.entry.save_button.config(command=self.callback_save_entry)

        self.set_label_schema(label_schema)

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

        return self.variables.label_schema

    # some entry validation methods
    def _version_entry_validate(self):
        the_value = self.version_entry.get().strip()
        if the_value != '' and self.label_schema.version != the_value:
            self.variables.unsaved_edits = True
            self.label_schema._version = the_value
            self.label_schema.update_version_date(value=None)
            self.version_date_entry.set_text(self.label_schema.version_date)
        return True

    def _classification_validate(self):
        the_value = self.classification_entry.get().strip()
        if self.label_schema.classification != the_value:
            self.variables.unsaved_edits = True
            self.label_schema._classification = the_value
        return True

    def _confidence_validate(self):
        the_value = self.confidence_entry.get().strip()
        if the_value == '':
            the_values = None
        else:
            if ',' in the_value:
                temp_values = [entry.strip() for entry in the_value.split(',')]
            else:
                temp_values = the_value.split()

            # noinspection PyBroadException
            try:
                the_values = [int(entry) for entry in temp_values]
            except Exception:
                the_values = temp_values

            # reformat the contents
            self.confidence_entry.delete(0, len(the_value))
            self.confidence_entry.insert(
                0, ', '.join('{}'.format(entry) for entry in the_values))

        if self.label_schema.confidence_values != the_values:
            self.variables.unsaved_edits = True
            self.label_schema.confidence_values = the_values
        return True

    def _geometry_validate(self):
        the_value = self.geometries_entry.get().strip()

        if the_value == '':
            the_values = None
        else:
            if ',' in the_value:
                the_values = [
                    entry.strip().lower() for entry in the_value.split(',')
                ]
            else:
                the_values = [entry.lower() for entry in the_value.split()]

        if (self.label_schema.permitted_geometries == the_values) or \
                (self.label_schema.permitted_geometries is None and the_values is None):
            return True
        try:
            self.label_schema.permitted_geometries = the_values
            self.variables.unsaved_edits = True

            # reformat the contents
            self.geometries_entry.delete(0, len(the_value))
            self.geometries_entry.insert(
                0, ', '.join(self.label_schema.permitted_geometries))

            return True
        except ValueError:
            return False

    def _geometry_invalid(self):
        showinfo(
            'geometry type guidance',
            message='The contents for geometry should be a space delimited\n'
            'combination of {"point", "line", "polygon"}')
        self.geometries_entry.focus_set()
        temp = self.geometries_entry.get()
        self.geometries_entry.delete(0, len(temp))
        if self.label_schema is None or self.label_schema.permitted_geometries is None:
            self.geometries_entry.insert(0, '')
        else:
            self.geometries_entry.insert(
                0, ' '.join(self.label_schema.permitted_geometries))

    # some helper methods
    def _set_focus_on_entry_popup(self):
        self.entry_popup.deiconify()
        self.entry_popup.focus_set()
        self.entry_popup.lift()
        self.entry_popup.grab_set()

    def _verify_selected(self):
        if self.variables.current_id is None:
            showinfo('No Element Selected',
                     message='Choose element from Viewer')
            return False
        return True

    def _check_save_state(self):
        """
        Checks the save state.

        Returns
        -------
        bool
            Continue (True) or abort (False)
        """

        if not self.variables.unsaved_edits:
            return True

        result = askyesnocancel(
            title="Unsaved Edits",
            message="There are unsaved edits. Save before opening a new file?")
        if result is None:
            return False

        if result is True:
            self.save()
        return True

    def _update_schema_display(self):
        if self.variables.label_schema is None:
            self.version_entry.config(state='disabled')
            self.version_entry.set_text('')
            self.version_date_entry.set_text('')
            self.classification_entry.config(state='disabled')
            self.classification_entry.set_text('')
            self.confidence_entry.config(state='disabled')
            self.confidence_entry.set_text('')
            self.geometries_entry.config(state='disabled')
            self.geometries_entry.set_text('')
        else:
            self.version_entry.config(validate='none')
            self.version_entry.set_text(self.label_schema.version)
            self.version_entry.config(validate='focusout', state='normal')

            self.version_date_entry.set_text(self.label_schema.version_date)

            self.classification_entry.config(validate='none')
            self.classification_entry.set_text(
                self.label_schema.classification)
            self.classification_entry.config(validate='focusout',
                                             state='normal')

            self.confidence_entry.config(validate='none')
            if self.label_schema.confidence_values is None:
                self.confidence_entry.set_text('')
            else:
                self.confidence_entry.set_text(' '.join(
                    '{}'.format(entry)
                    for entry in self.label_schema.confidence_values))
            self.confidence_entry.config(validate='focusout', state='normal')

            self.geometries_entry.config(validate='none')
            if self.label_schema.permitted_geometries is None:
                self.geometries_entry.set_text('')
            else:
                self.geometries_entry.set_text(' '.join(
                    self.label_schema.permitted_geometries))
            self.geometries_entry.config(validate='focusout', state='normal')

        self.schema_viewer.fill_from_label_schema(self.variables.label_schema)
        self.entry.update_label_schema()

    def prompt_for_filename(self):
        schema_file = asksaveasfilename(
            initialdir=self.variables.browse_directory,
            filetypes=[file_filters.json_files, file_filters.all_files])

        if schema_file == '' or schema_file == ():
            # closed or cancelled
            return False

        self.variables.browse_directory = os.path.split(schema_file)[0]
        self.variables.label_file_name = schema_file
        # todo: what if this file name exists? It should prompt already?
        return True

    def set_label_schema(self, label_schema):
        """
        Sets the label schema value.

        Parameters
        ----------
        label_schema : None|str|LabelSchema
        """

        if label_schema is None:
            self.variables.label_file_name = None
            self.variables.label_schema = LabelSchema()
        elif isinstance(label_schema, str):
            the_file = label_schema
            label_schema = LabelSchema.from_file(the_file)
            self.variables.label_file_name = the_file
            self.variables.label_schema = label_schema
            browse_dir = os.path.split(os.path.abspath(the_file))[0]
            self.variables.browse_directory = browse_dir
        elif isinstance(label_schema, LabelSchema):
            self.variables.label_file_name = None
            self.variables.label_schema = label_schema
        else:
            raise TypeError(
                'input must be the path for a label schema file or a LabelSchema instance'
            )

        self.variables.unsaved_edits = False
        self.variables.current_id = None
        self._update_schema_display()

    def set_current_id(self, value):
        if value == '':
            value = None
        if (value is None and self.variables.current_id is None) or \
                (value == self.variables.current_id):
            return

        self.variables.current_id = value
        self.entry.update_current_id()
        if value is not None:
            self.schema_viewer.set_selection_with_expansion(value)

    # callbacks and bound methods
    def save(self):
        """
        Save any current progress.
        """

        if self.variables.label_file_name is None:
            if not self.prompt_for_filename():
                return  # they opted to not pick a file

        self.label_schema.to_file(self.variables.label_file_name)
        self.variables.unsaved_edits = False

    def exit(self):
        """
        Exit the application.

        Returns
        -------
        None
        """

        if self.variables.unsaved_edits:
            save_state = askyesno('Save Progress',
                                  message='There are unsaved edits. Save?')
            if save_state is True:
                self.save()
        self.master.destroy()

    def callback_open(self):
        if not self._check_save_state():
            return

        schema_file = askopenfilename(
            initialdir=self.variables.browse_directory,
            filetypes=[file_filters.json_files, file_filters.all_files])
        if schema_file == '' or schema_file == ():
            # closed or cancelled
            return

        self.set_label_schema(schema_file)

    def callback_new_schema(self):
        if not self._check_save_state():
            return

        self.set_label_schema(LabelSchema())

    def callback_save_as(self):
        if self.prompt_for_filename():
            # they chose the new filename
            self.save()

    def callback_edit_entry(self):
        if not self._verify_selected():
            return
        self._set_focus_on_entry_popup()

    def callback_new_entry(self):
        self.variables.current_id = None
        self.entry.update_current_id()
        self._set_focus_on_entry_popup()

    def callback_delete_entry(self):
        if not self._verify_selected():
            return

        selected = self.variables.current_id
        # does the selected entry have any children?
        children = self.label_schema.subtypes.get(selected, None)
        if children is not None and len(children) > 0:
            response = askyesnocancel(
                'Delete Children?',
                message='Selected entry has children. Delete all children?')
            if response is not True:
                return
        self.schema_viewer.delete_entry(selected)
        self.variables.unsaved_edits = True
        self.set_current_id(None)

    def callback_move_up(self):
        if not self._verify_selected():
            return
        selected = self.variables.current_id

        result = self.label_schema.reorder_child_element(selected, spaces=-1)
        if result:
            self.schema_viewer.rerender_entry(selected)
            self.variables.unsaved_edits = True

    def callback_move_down(self):
        if not self._verify_selected():
            return
        selected = self.variables.current_id

        result = self.label_schema.reorder_child_element(selected, spaces=1)
        if result:
            self.schema_viewer.rerender_entry(selected)
            self.variables.unsaved_edits = True

    def callback_save_entry(self):
        if self.entry.save_function():
            self.schema_viewer.rerender_entry(self.entry.id_changed)
            self.set_current_id(self.entry.id_changed)
            self.entry.close_window()
            self.entry_popup.grab_release()

    # noinspection PyUnusedLocal
    def item_selected_on_viewer(self, event):
        item_id = self.schema_viewer.focus()
        if item_id == '':
            return
        self.set_current_id(item_id)
예제 #12
0
    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()
예제 #13
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()
예제 #14
0
    def __init__(self, primary, reader=None, **kwargs):
        """

        Parameters
        ----------
        primary : tkinter.Toplevel|tkinter.Tk
        reader : None|str|CRSDTypeReader|CRSDTypeCanvasImageReader
        kwargs
            Keyword arguments passed through to the Frame
        """

        self.root = primary
        self.variables = AppVariables()

        Frame.__init__(self, primary, **kwargs)
        WidgetWithMetadata.__init__(self, primary)

        self.pyplot_panel = PyplotImagePanel(
            self, navigation=True)  # type: PyplotImagePanel

        self.pyplot_panel.cmap_name = 'turbo'
        self.pyplot_panel.set_ylabel('Freq (GHz)')
        self.pyplot_panel.set_xlabel('Time (\u03BCsec)')
        self.pyplot_panel.set_title('Pulse Visualization')

        self.scanner_panel = Frame(self, padding=10)  # type: Frame
        self.scanner_panel.columnconfigure(0, weight=0)
        self.scanner_panel.columnconfigure(1, weight=0)
        self.scanner_panel.columnconfigure(2, weight=1)
        self.scanner_panel.columnconfigure(3, weight=0)
        self.scanner_panel.columnconfigure(4, weight=0)

        self.dir_buttons = DirectionWidget(
            self.scanner_panel)  # type: DirectionWidget
        self.slider = SliderWidget(self.scanner_panel)  # type: SliderWidget

        self.dir_buttons.button_rev.grid(row=0, column=0, sticky='w')
        self.dir_buttons.button_prev.grid(row=0, column=1, sticky='w')
        self.slider.grid(row=0, column=2, sticky='ew')
        self.dir_buttons.button_next.grid(row=0, column=3, sticky='e')
        self.dir_buttons.button_fwd.grid(row=0, column=4, sticky='e')

        self.pyplot_panel.pack(side="top", expand=True, fill='both')
        self.scanner_panel.pack(side="bottom", expand=False, fill='x')

        self.rowconfigure(0, weight=1)
        self.rowconfigure(1, weight=0)
        self.pack(expand=True, fill='both')
        self.set_title()
        self._refresh_vdata()

        # define menus
        self.menu_bar = tkinter.Menu()
        # file menu
        self.file_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.file_menu.add_command(label="Open Image",
                                   command=self.callback_select_files)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="Exit", command=self.exit)
        # menus for informational popups
        self.metadata_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.metadata_menu.add_command(label="Metaicon",
                                       command=self.metaicon_popup)
        self.metadata_menu.add_command(label="Metaviewer",
                                       command=self.metaviewer_popup)
        # ensure menus cascade
        self.menu_bar.add_cascade(label="File", menu=self.file_menu)
        self.menu_bar.add_cascade(label="Metadata", menu=self.metadata_menu)

        self.root.config(menu=self.menu_bar)

        self.update_reader(reader)

        # self.image_panel.canvas.bind('<<RemapChanged>>', self.handle_remap_change)
        self.slider.cbx_channel.bind('<<ComboboxSelected>>',
                                     self.handle_image_index_changed)
        self.slider.scale_pulse.bind('<ButtonRelease>',
                                     self.handle_pulse_changed_slider)
        self.slider.entry_pulse.bind('<FocusOut>',
                                     self.handle_pulse_changed_entry)
        self.slider.entry_pulse.on_enter_or_return_key(
            self.handle_pulse_changed_entry)

        self.dir_buttons.button_rev.config(
            command=self.pulse_animate_backwards)
        self.dir_buttons.button_prev.config(command=self.pulse_step_prev)
        self.dir_buttons.button_next.config(command=self.pulse_step_next)
        self.dir_buttons.button_fwd.config(command=self.pulse_animate_forward)
예제 #15
0
class PulseExplorer(Frame, WidgetWithMetadata):
    def __init__(self, primary, reader=None, **kwargs):
        """

        Parameters
        ----------
        primary : tkinter.Toplevel|tkinter.Tk
        reader : None|str|CRSDTypeReader|CRSDTypeCanvasImageReader
        kwargs
            Keyword arguments passed through to the Frame
        """

        self.root = primary
        self.variables = AppVariables()

        Frame.__init__(self, primary, **kwargs)
        WidgetWithMetadata.__init__(self, primary)

        self.pyplot_panel = PyplotImagePanel(
            self, navigation=True)  # type: PyplotImagePanel

        self.pyplot_panel.cmap_name = 'turbo'
        self.pyplot_panel.set_ylabel('Freq (GHz)')
        self.pyplot_panel.set_xlabel('Time (\u03BCsec)')
        self.pyplot_panel.set_title('Pulse Visualization')

        self.scanner_panel = Frame(self, padding=10)  # type: Frame
        self.scanner_panel.columnconfigure(0, weight=0)
        self.scanner_panel.columnconfigure(1, weight=0)
        self.scanner_panel.columnconfigure(2, weight=1)
        self.scanner_panel.columnconfigure(3, weight=0)
        self.scanner_panel.columnconfigure(4, weight=0)

        self.dir_buttons = DirectionWidget(
            self.scanner_panel)  # type: DirectionWidget
        self.slider = SliderWidget(self.scanner_panel)  # type: SliderWidget

        self.dir_buttons.button_rev.grid(row=0, column=0, sticky='w')
        self.dir_buttons.button_prev.grid(row=0, column=1, sticky='w')
        self.slider.grid(row=0, column=2, sticky='ew')
        self.dir_buttons.button_next.grid(row=0, column=3, sticky='e')
        self.dir_buttons.button_fwd.grid(row=0, column=4, sticky='e')

        self.pyplot_panel.pack(side="top", expand=True, fill='both')
        self.scanner_panel.pack(side="bottom", expand=False, fill='x')

        self.rowconfigure(0, weight=1)
        self.rowconfigure(1, weight=0)
        self.pack(expand=True, fill='both')
        self.set_title()
        self._refresh_vdata()

        # define menus
        self.menu_bar = tkinter.Menu()
        # file menu
        self.file_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.file_menu.add_command(label="Open Image",
                                   command=self.callback_select_files)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="Exit", command=self.exit)
        # menus for informational popups
        self.metadata_menu = tkinter.Menu(self.menu_bar, tearoff=0)
        self.metadata_menu.add_command(label="Metaicon",
                                       command=self.metaicon_popup)
        self.metadata_menu.add_command(label="Metaviewer",
                                       command=self.metaviewer_popup)
        # ensure menus cascade
        self.menu_bar.add_cascade(label="File", menu=self.file_menu)
        self.menu_bar.add_cascade(label="Metadata", menu=self.metadata_menu)

        self.root.config(menu=self.menu_bar)

        self.update_reader(reader)

        # self.image_panel.canvas.bind('<<RemapChanged>>', self.handle_remap_change)
        self.slider.cbx_channel.bind('<<ComboboxSelected>>',
                                     self.handle_image_index_changed)
        self.slider.scale_pulse.bind('<ButtonRelease>',
                                     self.handle_pulse_changed_slider)
        self.slider.entry_pulse.bind('<FocusOut>',
                                     self.handle_pulse_changed_entry)
        self.slider.entry_pulse.on_enter_or_return_key(
            self.handle_pulse_changed_entry)

        self.dir_buttons.button_rev.config(
            command=self.pulse_animate_backwards)
        self.dir_buttons.button_prev.config(command=self.pulse_step_prev)
        self.dir_buttons.button_next.config(command=self.pulse_step_next)
        self.dir_buttons.button_fwd.config(command=self.pulse_animate_forward)

    def set_title(self):
        """
        Sets the window title.
        """

        file_name = None if self.variables.image_reader is None \
            else self.variables.image_reader.file_name
        if file_name is None:
            the_title = "Pulse Explorer"
        else:
            the_title = "Pulse Explorer for {}".format(
                os.path.split(file_name)[1])
        self.winfo_toplevel().title(the_title)

    def set_pulse(self, value, override=False):
        """
        Sets the pulse count.

        Parameters
        ----------
        value : int
        override : bool
        """

        if self.variables.image_reader is None:
            return

        value = int(value)
        if value == self.variables.image_reader.pulse and not override:
            return

        if not (0 <= value < self.variables.image_reader.pulse_count):
            showinfo('Invalid pulse',
                     message='Pulse must be in the range [0, {})'.format(
                         self.variables.image_reader.pulse_count))
            return

        self.variables.image_reader.pulse = value
        if self.variables.vcount == 0:
            self.variables.vmin = numpy.min(
                self.variables.image_reader.pulse_data[:, :])
            self.variables.vmax = numpy.max(
                self.variables.image_reader.pulse_data[:, :])
        elif self.variables.vcount < 5:
            self.variables.vmin = min(
                self.variables.vmin,
                numpy.min(self.variables.image_reader.pulse_data[:, :]))
            self.variables.vmax = max(
                self.variables.vmax,
                numpy.max(self.variables.image_reader.pulse_data[:, :]))
        if self.variables.vmin != self.variables.vmax:
            self.variables.vcount += 1

        self.slider.entry_pulse.set_text(str(value))
        self.slider.var_pulse_number.set(value)
        self.display_in_pyplot_frame()

    def exit(self):
        self.root.destroy()

    def _refresh_vdata(self):
        self.variables.vmin = 0
        self.variables.vmax = 0
        self.variables.vcount = 0

    # noinspection PyUnusedLocal
    def handle_remap_change(self, event):
        """
        Handle that the remap for the image canvas has changed.

        Parameters
        ----------
        event
        """

        pass

    # noinspection PyUnusedLocal
    def handle_image_index_changed(self, event):
        """
        Handle that the image index has changed.

        Parameters
        ----------
        event
        """
        if self.variables.image_reader is None:
            return

        self._refresh_vdata()
        self.variables.animating = False
        self.variables.image_reader.index = self.slider.cbx_channel.current()
        self.my_populate_metaicon()
        self.slider.cbx_channel.selection_clear()
        # Update number of pulses
        pulse_count = self.variables.image_reader.pulse_count
        self.slider.fullscale.configure(text=str(pulse_count - 1))
        self.slider.scale_pulse.configure(to=pulse_count - 1)
        self.set_pulse(0, override=True)

    def handle_pulse_changed_entry(self, event):
        if self.variables.image_reader is None:
            return

        if self.variables.animating:
            self.variables.animating = False

        self.set_pulse(int(self.slider.entry_pulse.get()))

    def handle_pulse_changed_slider(self, event):
        if self.variables.image_reader is None:
            return

        if self.variables.animating:
            self.variables.animating = False

        self.set_pulse(int(float(self.slider.var_pulse_number.get())))

    def pulse_step(self, direction):
        if self.variables.image_reader is None:
            return

        current_pulse = int(float(self.slider.var_pulse_number.get()))
        new_pulse = current_pulse + direction
        # todo: should we roll over by default?
        if new_pulse < 0:
            new_pulse += self.variables.image_reader.pulse_count
        elif new_pulse >= self.variables.image_reader.pulse_count:
            new_pulse -= self.variables.image_reader.pulse_count
        self.set_pulse(new_pulse)

    def pulse_step_prev(self):
        if self.variables.animating:
            self.variables.animating = False
        self.pulse_step(-1)

    def pulse_animate_backwards(self):
        if self.variables.animating:
            self.variables.animating = False
            return

        self.variables.animating = True
        self._pulse_animate(-1)

    def pulse_step_next(self):
        if self.variables.animating:
            self.variables.animating = False

        self.pulse_step(+1)

    def pulse_animate_forward(self):
        if self.variables.animating:
            self.variables.animating = False
            return

        self.variables.animating = True
        self._pulse_animate(1)

    def _pulse_animate(self, direction):
        if not self.variables.animating:
            return

        self.pulse_step(direction)
        self.after(self.variables.animation_delay, self._pulse_animate,
                   direction)

    def update_reader(self, the_reader, update_browse=None):
        """
        Update the reader.

        Parameters
        ----------
        the_reader : None|str|CRSDTypeReader|STFTCanvasImageReader
        update_browse : None|str
        """

        if the_reader is None:
            return

        if update_browse is not None:
            self.variables.browse_directory = update_browse
        elif isinstance(the_reader, str):
            self.variables.browse_directory = os.path.split(the_reader)[0]

        if isinstance(the_reader, str):
            the_reader = STFTCanvasImageReader(the_reader)

        if isinstance(the_reader, CRSDTypeReader):
            the_reader = STFTCanvasImageReader(the_reader)

        if not isinstance(the_reader, STFTCanvasImageReader):
            raise TypeError('Got unexpected input for the reader')

        self._refresh_vdata()
        self.variables.animating = False
        self.variables.image_reader = the_reader
        self.display_in_pyplot_frame()
        self.set_title()
        self.my_populate_metaicon()
        self.my_populate_metaviewer()
        identifiers = [
            entry.Identifier
            for entry in the_reader.base_reader.crsd_meta.Channel.Parameters
        ]
        self.update_combobox(identifiers)  # which one is set here...

        self.slider.fullscale['text'] = str(
            self.variables.image_reader.pulse_count - 1)
        self.slider.scale_pulse[
            'to'] = self.variables.image_reader.pulse_count - 1
        self.slider.var_pulse_number.set(0)

    def callback_select_files(self):
        fname = askopenfilename(initialdir=self.variables.browse_directory,
                                filetypes=[crsd_files, all_files])
        if fname is None or fname in ['', ()]:
            return

        the_reader = STFTCanvasImageReader(fname)
        self.update_reader(the_reader, update_browse=os.path.split(fname)[0])

    def display_in_pyplot_frame(self):
        times = 1e6 * self.variables.image_reader.times
        frequencies = 1e-9 * self.variables.image_reader.frequencies
        image_data = self.variables.image_reader.pulse_data[:, :]
        image_data = image_data[::-1, :]
        if self.variables.vcount > 0:
            self.pyplot_panel.update_image(
                image_data,
                aspect='auto',
                vmin=self.variables.vmin,
                vmax=self.variables.vmax,
                extent=[times[0], times[-1], frequencies[0], frequencies[-1]])
        else:
            self.pyplot_panel.update_image(
                image_data,
                aspect='auto',
                extent=[times[0], times[-1], frequencies[0], frequencies[-1]])

    def update_combobox(self, identifiers):
        # Update channels combobox.
        # Get channel identifiers from metadata.
        self.slider.cbx_channel['values'] = identifiers
        self.slider.cbx_channel.set(identifiers[0])
        self.slider.cbx_channel.configure(state='readonly')

    def my_populate_metaicon(self):
        """
        Populate the metaicon.
        """

        self.populate_metaicon(self.variables.image_reader)

    def my_populate_metaviewer(self):
        """
        Populate the metaviewer.
        """

        self.populate_metaviewer(self.variables.image_reader)
예제 #16
0
    def __init__(self, parent):
        """

        Parameters
        ----------
        parent: Frame
        """

        Frame.__init__(self, parent)

        self['padding'] = 5

        self.start_pulse_label = Label(self, text='0')

        self.label_channel = Label(self, text='Channel')
        self.cbx_channel = Combobox(self, state='readonly')
        self.var_cbx_channel = tkinter.StringVar()
        self.cbx_channel.configure(justify='left',
                                   textvariable=self.var_cbx_channel,
                                   width=30,
                                   values=[],
                                   state='disabled')

        self.label_pulse = Label(self, text='Pulse')
        self.entry_pulse = Entry(self)
        self.entry_pulse.set_text('0')
        self.entry_pulse.configure(justify='right', width=6)
        self.entry_callback = self.register(self._only_numeric_input)
        self.entry_pulse.configure(validate="key",
                                   validatecommand=(self.entry_callback, "%P"))

        self.fullscale = Label(self, text='0')

        self.start_pulse_label.grid(row=0, column=0, padx=5, sticky='w')
        self.label_channel.grid(row=0, column=1, padx=5, sticky='e')
        self.cbx_channel.grid(row=0, column=2, padx=5)
        self.label_pulse.grid(row=0, column=3, padx=5)
        self.entry_pulse.grid(row=0, column=4, padx=5, sticky='w')
        self.fullscale.grid(row=0, column=5, padx=5, sticky='e')
        self.columnconfigure(0, weight=0)
        self.columnconfigure(1, weight=1)
        self.columnconfigure(2, weight=0)
        self.columnconfigure(3, weight=0)
        self.columnconfigure(4, weight=1)
        self.columnconfigure(5, weight=0)

        self.var_pulse_number = tkinter.StringVar(value='0')
        self.scale_pulse = Scale(
            self,
            from_=0,
            to=0,
            length=600,
            orient='horizontal',
            command=lambda s: self.var_pulse_number.set(s))
        self.scale_pulse.configure(variable=self.var_pulse_number)
        self.scale_pulse.grid(row=1,
                              column=0,
                              columnspan=6,
                              padx=5,
                              pady=8,
                              sticky='esw')
예제 #17
0
    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)
예제 #18
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