class SidePanel(WidgetPanel): _widget_list = ("buttons", "info_panel") buttons = widget_descriptors.PanelDescriptor( "buttons", ButtonPanel, default_text="") # type: ButtonPanel info_panel = widget_descriptors.PanelDescriptor( "info_panel", InfoPanel, default_text="") # type: InfoPanel def __init__(self, parent): WidgetPanel.__init__(self, parent) self.init_w_vertical_layout()
class Panel1(WidgetPanel): _widget_list = ( 'button', 'combobox', 'scale', 'label', 'label_frame', 'entry', # 'text', # omit this one for now, because it's broken 'spinbox', 'radio_button_panel', 'update_radio_selection', 'check_button') button = widget_descriptors.ButtonDescriptor("button") # type: basic_widgets.Button combobox = widget_descriptors.ComboboxDescriptor("combobox") # type: basic_widgets.Combobox scale = widget_descriptors.ScaleDescriptor("scale") # type: basic_widgets.Scale label = widget_descriptors.LabelDescriptor("label") # type: basic_widgets.Label label_frame = widget_descriptors.LabelFrameDescriptor("label_frame") # type: basic_widgets.LabelFrame entry = widget_descriptors.EntryDescriptor("entry", default_text="") # type: basic_widgets.Entry text = widget_descriptors.TextDescriptor("text") # type: basic_widgets.Text spinbox = widget_descriptors.SpinboxDescriptor("spinbox", default_text="") # type: basic_widgets.Spinbox radio_button_panel = widget_descriptors.PanelDescriptor("radio_button_panel", RadioPanel) # type: RadioPanel update_radio_selection = widget_descriptors.ButtonDescriptor("update_radio_selection") # type: basic_widgets.Button check_button = widget_descriptors.CheckButtonDescriptor("check_button") # type: basic_widgets.CheckButton def __init__(self, parent): WidgetPanel.__init__(self, parent) self.parent = parent self.init_w_vertical_layout() # callbacks self.update_radio_selection.on_left_mouse_click(self.callback_update_radio_selection) def callback_update_radio_selection(self, event): selection = self.radio_button_panel.selection() if selection == self.radio_button_panel.button_1: self.radio_button_panel.set_text("button 1") elif selection == self.radio_button_panel.button_2: self.radio_button_panel.set_text("button 2") elif selection == self.radio_button_panel.button_3: self.radio_button_panel.set_text("button 3")
class ImageCanvasPanel(WidgetPanel): _widget_list = ("image_canvas",) image_canvas = widget_descriptors.PanelDescriptor("image_canvas", AxesImageCanvas) def __init__(self, parent, ): WidgetPanel.__init__(self, parent) self.init_w_vertical_layout() self.pack(fill=tkinter.BOTH, expand=1)
class PrimaryPanel(WidgetPanel): _widget_list = ('button_1', 'panel_1') button_1 = widget_descriptors.ButtonDescriptor("button_1", default_text="asdf") # type: basic_widgets.Button button_2 = widget_descriptors.ButtonDescriptor("button_2") # type: basic_widgets.Button panel_1 = widget_descriptors.PanelDescriptor("panel_1", Panel1) # type: Panel1 def __init__(self, primary): primary_frame = tkinter.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_horizontal_layout() primary_frame.pack()
class ImageViewer(WidgetPanel, WidgetWithMetadata): _widget_list = ("image_panel", "pyplot_panel") image_panel = widget_descriptors.ImagePanelDescriptor( "image_panel") # type: ImagePanel pyplot_panel = widget_descriptors.PanelDescriptor( "pyplot_panel", PyplotImagePanel) # type: PyplotImagePanel def __init__(self, primary): """ Parameters ---------- primary : tkinter.Toplevel|tkinter.Tk """ self.root = primary self.primary_frame = basic_widgets.Frame(primary) WidgetPanel.__init__(self, self.primary_frame) WidgetWithMetadata.__init__(self, primary) self.variables = AppVariables() self.init_w_horizontal_layout() self.set_title() # define menus menubar = tkinter.Menu() # file menu filemenu = tkinter.Menu(menubar, tearoff=0) filemenu.add_command(label="Open Image", command=self.callback_select_files) filemenu.add_command(label="Open Directory", command=self.callback_select_directory) filemenu.add_separator() filemenu.add_command(label="Exit", command=self.exit) # menus for informational popups popups_menu = tkinter.Menu(menubar, tearoff=0) popups_menu.add_command(label="Metaicon", command=self.metaicon_popup) popups_menu.add_command(label="Metaviewer", command=self.metaviewer_popup) # ensure menus cascade menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Metadata", menu=popups_menu) # handle packing self.primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) primary.config(menu=menubar) # 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) 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 = "Image Viewer" elif isinstance(file_name, (list, tuple)): the_title = "Image Viewer, Multiple Files" else: the_title = "Image Viewer for {}".format( os.path.split(file_name)[1]) self.winfo_toplevel().title(the_title) def exit(self): self.root.destroy() # noinspection PyUnusedLocal def handle_selection_change(self, event): """ Handle a change in the selection area. Parameters ---------- event """ if self.variables.image_reader is None: return full_image_width = self.image_panel.canvas.variables.state.canvas_width fill_image_height = self.image_panel.canvas.variables.state.canvas_height self.image_panel.canvas.zoom_to_canvas_selection( (0, 0, full_image_width, fill_image_height)) self.display_canvas_rect_selection_in_pyplot_frame() # noinspection PyUnusedLocal def handle_remap_change(self, event): """ Handle that the remap for the image canvas has changed. Parameters ---------- event """ if self.variables.image_reader is not None: self.display_canvas_rect_selection_in_pyplot_frame() #noinspection PyUnusedLocal def handle_image_index_changed(self, event): """ Handle that the image index has changed. Parameters ---------- event """ self.my_populate_metaicon() def update_reader(self, the_reader): """ Update the reader. Parameters ---------- the_reader : ImageReader """ # change the tool to view self.image_panel.canvas.set_current_tool_to_view() self.image_panel.canvas.set_current_tool_to_view() # update the reader self.variables.image_reader = the_reader self.image_panel.set_image_reader(the_reader) self.set_title() # refresh appropriate GUI elements self.pyplot_panel.make_blank() self.my_populate_metaicon() self.my_populate_metaviewer() def callback_select_files(self): fnames = askopenfilenames(initialdir=self.variables.browse_directory, filetypes=common_use_collection) if fnames is None or fnames in ['', ()]: return # update the default directory for browsing self.variables.browse_directory = os.path.split(fnames[0])[0] the_reader = None if len(fnames) > 1: the_reader = ComplexImageReader(fnames) if the_reader is None: try: the_reader = ComplexImageReader(fnames[0]) except IOError: the_reader = None if the_reader is None: the_reader = DerivedImageReader(fnames[0]) if the_reader is None: showinfo( 'Opener not found', message='File {} was not successfully opened as a SICD type ' 'or SIDD type file.'.format(fnames)) return self.update_reader(the_reader) def callback_select_directory(self): dirname = askdirectory(initialdir=self.variables.browse_directory, mustexist=True) if dirname is None or dirname in [(), '']: return # update the default directory for browsing self.variables.browse_directory = os.path.split(dirname)[0] # TODO: handle non-complex data possibilities here? the_reader = ComplexImageReader(dirname) self.update_reader(the_reader) def display_canvas_rect_selection_in_pyplot_frame(self): def get_extent(coords): left = min(coords[1::2]) right = max(coords[1::2]) top = max(coords[0::2]) bottom = min(coords[0::2]) return left, right, top, bottom threshold = self.image_panel.canvas.variables.config.select_size_threshold select_id = self.image_panel.canvas.variables.select_rect.uid rect_coords = self.image_panel.canvas.get_shape_image_coords(select_id) extent = get_extent(rect_coords) if abs(extent[1] - extent[0]) < threshold or abs(extent[2] - extent[3]) < threshold: self.pyplot_panel.make_blank() else: image_data = self.image_panel.canvas.get_image_data_in_canvas_rect_by_id( select_id) if image_data is not None: self.pyplot_panel.update_image(image_data, extent=extent) else: self.pyplot_panel.make_blank() def my_populate_metaicon(self): """ Populate the metaicon. """ if self.image_panel.canvas.variables.canvas_image_object is None or \ self.image_panel.canvas.variables.canvas_image_object.image_reader is None: image_reader = None the_index = 0 else: image_reader = self.image_panel.canvas.variables.canvas_image_object.image_reader the_index = self.image_panel.canvas.get_image_index() self.populate_metaicon(image_reader, the_index) def my_populate_metaviewer(self): """ Populate the metaviewer. """ if self.image_panel.canvas.variables.canvas_image_object is None: image_reader = None else: image_reader = self.image_panel.canvas.variables.canvas_image_object.image_reader self.populate_metaviewer(image_reader)
class GeotiffViewer(WidgetPanel): """ A geotiff viewer prototype. """ _widget_list = ("band_selection_panel", "controls_panel", "geotiff_image_panel", "zoom_image_panel") geotiff_image_panel = widget_descriptors.AxesImageCanvasDescriptor( "geotiff_image_panel") # type: AxesImageCanvas zoom_image_panel = widget_descriptors.AxesImageCanvasDescriptor( "zoom_image_panel") # type: AxesImageCanvas band_selection_panel = widget_descriptors.PanelDescriptor( "band_selection_panel", BandSelection) # type: BandSelection controls_panel = widget_descriptors.PanelDescriptor( "controls_panel", Controls) # type: Controls image_reader = None # type: GeotiffImageReader def __init__(self, primary): """ Parameters ---------- primary The primary widget. """ self.primary = primary primary_frame = tkinter.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_horizontal_layout() self.geotiff_image_panel.set_canvas_size(800, 1080) self.geotiff_image_panel.canvas.set_current_tool_to_pan() menubar = Menu() filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Open", command=self.select_file) filemenu.add_separator() filemenu.add_command(label="Exit", command=self.exit) # create more pulldown menus popups_menu = Menu(menubar, tearoff=0) popups_menu.add_command(label="Main Controls", command=self.exit) menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Popups", menu=popups_menu) primary.config(menu=menubar) primary_frame.pack() self.band_selection_panel.red_selection.on_selection( self.callback_update_red_band) self.band_selection_panel.green_selection.on_selection( self.callback_update_green_band) self.band_selection_panel.blue_selection.on_selection( self.callback_update_blue_band) self.band_selection_panel.alpha_selection.on_selection( self.callback_update_alpha_band) self.controls_panel.pan.on_left_mouse_click(self.callback_set_to_pan) self.controls_panel.select.on_left_mouse_click( self.callback_set_to_select) self.geotiff_image_panel.canvas.on_left_mouse_release( self.callback_select) def exit(self): """ Exits/destroys the widget. Returns ------- None """ self.quit() def select_file(self, fname=None): """ File selector action. Will open a file selector dialog if `None`. Parameters ---------- fname : str|None Returns ------- None """ if fname is None: fname = filedialog.askopenfilename( initialdir=os.path.expanduser("~"), title="Select file", filetypes=(("tiff files", ("*.tif", "*.tiff", "*.TIF", "*.TIFF")), ("all files", "*.*"))) self.image_reader = GeotiffImageReader(fname) self.geotiff_image_panel.set_image_reader(self.image_reader) self.populate_band_selections() def populate_band_selections(self): """ Helper method for populating the band selection. Returns ------- None """ bands = self.image_reader.n_bands band_selections = [str(band) for band in range(bands)] band_selections.append("None") self.band_selection_panel.red_selection.update_combobox_values( band_selections) self.band_selection_panel.green_selection.update_combobox_values( band_selections) self.band_selection_panel.blue_selection.update_combobox_values( band_selections) self.band_selection_panel.alpha_selection.update_combobox_values( band_selections) self.band_selection_panel.red_selection.set( str(self.image_reader.display_bands[0])) self.band_selection_panel.green_selection.set( str(self.image_reader.display_bands[1])) self.band_selection_panel.blue_selection.set( str(self.image_reader.display_bands[2])) if len(self.image_reader.display_bands) > 3: self.band_selection_panel.alpha_selection.set( str(self.image_reader.display_bands[3])) else: self.band_selection_panel.alpha_selection.set("None") def callback_update_red_band(self, event): """ Update the read band. Parameters ---------- event Returns ------- None """ red_band = self.band_selection_panel.red_selection.get() band_num = 0 if red_band == "None": if band_num not in self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands: self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands.append( band_num) else: if band_num in self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands: self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands.remove( band_num) self.image_reader.display_bands[band_num] = int(red_band) self.geotiff_image_panel.canvas.update_current_image() def callback_update_green_band(self, event): """ Update the green band. Parameters ---------- event Returns ------- None """ green_band = self.band_selection_panel.green_selection.get() band_num = 1 if green_band == "None": if band_num not in self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands: self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands.append( 1) else: if band_num in self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands: self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands.remove( 1) self.image_reader.display_bands[1] = int(green_band) self.geotiff_image_panel.canvas.update_current_image() def callback_update_blue_band(self, event): """ Update the blue band. Parameters ---------- event Returns ------- None """ band_num = 2 blue_band = self.band_selection_panel.blue_selection.get() if blue_band == "None": if band_num not in self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands: self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands.append( band_num) else: if band_num in self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands: self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands.remove( band_num) self.image_reader.display_bands[band_num] = int(blue_band) self.geotiff_image_panel.canvas.update_current_image() def callback_update_alpha_band(self, event): """ Update the alpha channel. Parameters ---------- event Returns ------- None """ alpha_band = self.band_selection_panel.alpha_selection.get() band_num = 3 if len(self.image_reader.display_bands) == 3: self.image_reader.display_bands.append(band_num) if alpha_band == "None": self.image_reader.display_bands = self.image_reader.display_bands[ 0:3] else: if band_num in self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands: self.geotiff_image_panel.canvas.variables.canvas_image_object.drop_bands.remove( band_num) self.image_reader.display_bands[band_num] = int(alpha_band) self.geotiff_image_panel.canvas.update_current_image() def callback_set_to_pan(self, event): self.geotiff_image_panel.canvas.set_current_tool_to_pan() def callback_set_to_select(self, event): self.geotiff_image_panel.canvas.set_current_tool_to_selection_tool() def callback_select(self, event): data = self.geotiff_image_panel.canvas.get_image_data_in_canvas_rect_by_id( self.geotiff_image_panel.canvas.variables.select_rect_id, decimation=1) data = data[:, :, 0] reader = NumpyImageReader(data) self.zoom_image_panel.set_image_reader(reader) self.zoom_image_panel.canvas.update_current_image()
class PyplotPanel(WidgetPanel): """ Panel that displays pyplot plots and plot animations. """ _widget_list = ( "pyplot_canvas", "control_panel", ) pyplot_canvas = widget_descriptors.PyplotCanvasDescriptor( "pyplot_canvas") # type: PyplotCanvas control_panel = widget_descriptors.PanelDescriptor( "control_panel", PyplotControlPanel) # type: PyplotControlPanel def __init__(self, primary): WidgetPanel.__init__(self, primary) self.variables = AppVariables() self.pyplot_utils = PlotStyleUtils() self.init_w_vertical_layout() canvas_size_pixels = self.pyplot_canvas.canvas.figure.get_size_inches( ) * self.pyplot_canvas.canvas.figure.dpi self.control_panel.scale.config(length=canvas_size_pixels[0] * 0.75) self.variables.animation_related_controls = [ self.control_panel.scale, self.control_panel.rescale_y_axis_per_frame, self.control_panel.animate, self.control_panel.fps_entry, self.control_panel.fps_label ] self.control_panel.n_colors_label.set_text("n colors") self.control_panel.n_colors.set_text(self.pyplot_utils.n_color_bins) # set listeners self.control_panel.scale.on_left_mouse_motion( self._callback_update_from_slider) self.control_panel.rescale_y_axis_per_frame.on_selection( self._callback_set_y_rescale) self.control_panel.animate.config(command=self.animate) self.control_panel.color_palette.on_selection( self.callback_update_plot_colors) self.control_panel.n_colors.on_enter_or_return_key( self.callback_update_n_colors) self.control_panel.n_colors.config(command=self.update_n_colors) self.hide_control_panel() @property def title(self): return self.variables.title @title.setter def title(self, val): """ sets the plot's title Parameters ---------- val: str Returns ------- """ self.variables.title = val self.pyplot_canvas.axes.set_title(val) self.update_plot() @property def y_label(self): return self.variables.y_label @y_label.setter def y_label(self, val): """ sets the plot's y label Parameters ---------- val: str Returns ------- """ self.variables.y_label = val self.pyplot_canvas.axes.set_ylabel(val) self.update_plot() @property def x_label(self): return self.variables.y_label @x_label.setter def x_label(self, val): """ sets the plot's x label Parameters ---------- val: str Returns ------- """ self.variables.x_label = val self.pyplot_canvas.axes.set_xlabel(val) self.update_plot() def hide_control_panel(self): """ hides the control panel """ self.control_panel.pack_forget() def show_control_panel(self): """ shows / unhides the control panel """ self.control_panel.pack() def hide_animation_related_controls(self): """ hides all controls related to animation settings. Used by default when non time-series data is displayed. """ for widget in self.variables.animation_related_controls: widget.pack_forget() def show_animation_related_controls(self): """ shows / unhides animation related controls. Used by default when time-series data is displayed. """ for widget in self.variables.animation_related_controls: widget.pack() def set_y_margin_percent( self, percent_0_to_100=5 # type: float ): """ sets the y margin """ self.variables.y_margin = percent_0_to_100 * 0.01 def set_data(self, plot_data, x_axis=None): """ sets the data to be displayed. Data can be in following formats: 1d: single plot to be displayed 2d: several plots to be displayed as overplots. The length of first dimension is the total length of each plot The length of the second dimension is the total number of plots to be displayed 3d: time series of overplots. Ex1: data of shape [100, 1, 50] would represent a single plot of length 100 with 50 time series steps. Ex2: data of shape[100, 10, 50] would represent 10 overplots, each of length 100, with 50 times series steps. Parameters ---------- plot_data : numpy.ndarray x_axis : numpy.ndarray Returns ------- """ x = x_axis n_frames = 1 if len(plot_data.shape) == 1: self.hide_animation_related_controls() nx = len(plot_data) segments = numpy.zeros((1, nx, 2)) segments[0, :, 1] = plot_data elif len(plot_data.shape) == 2: self.hide_animation_related_controls() nx = len(plot_data[:, 0]) n_overplots = len(plot_data[0]) segments = numpy.zeros((n_overplots, nx, 2)) for i in range(n_overplots): segments[i, :, 1] = plot_data[:, i] elif len(plot_data.shape) == 3: self.show_animation_related_controls() nx = numpy.shape(plot_data)[0] n_overplots = numpy.shape(plot_data)[1] n_frames = numpy.shape(plot_data)[2] segments = numpy.zeros((n_overplots, nx, 2)) for i in range(n_overplots): segments[i, :, 1] = plot_data[:, i, 0] if x is None: x = numpy.arange(nx) segments[:, :, 0] = x self.variables.xmin = x.min() self.variables.xmax = x.max() y_range = plot_data.max() - plot_data.min() self.variables.ymin = plot_data.min( ) - y_range * self.variables.y_margin self.variables.ymax = plot_data.max( ) + y_range * self.variables.y_margin self.variables.x_axis = x self.variables.plot_data = plot_data self.variables.segments = segments self.variables.n_frames = n_frames self.control_panel.scale.config(to=n_frames - 1) if len(plot_data.shape) == 3: self.update_plot_animation(0) else: self.pyplot_canvas.axes.clear() self.pyplot_canvas.axes.plot(self.variables.plot_data) self.pyplot_canvas.axes.set_ylim(self.variables.ymin, self.variables.ymax) self.pyplot_canvas.canvas.draw() def update_plot_animation(self, animation_index): """ Updates the plot based on the time series / animation index Parameters ---------- animation_index: int Returns ------- str """ self.control_panel.scale.set(animation_index) self.variables.animation_index = animation_index self.update_plot() def _callback_update_from_slider(self, event): self.variables.animation_index = int( numpy.round(self.control_panel.scale.get())) self.update_plot() # define custom callbacks here def _callback_set_y_rescale(self, event): selection = self.control_panel.rescale_y_axis_per_frame.get() if selection == SCALE_Y_AXIS_PER_FRAME_TRUE: self.variables.set_y_margins_per_frame = True else: self.variables.set_y_margins_per_frame = False y_range = self.variables.plot_data.max( ) - self.variables.plot_data.min() self.variables.ymin = self.variables.plot_data.min( ) - y_range * self.variables.y_margin self.variables.ymax = self.variables.plot_data.max( ) + y_range * self.variables.y_margin self.update_plot() def animate(self): """ Animates the plot based on the plot data's time series. and other inputs supplied in the control panel, such as fps """ start_frame = 0 stop_frame = self.variables.n_frames fps = float(self.control_panel.fps_entry.get()) time_between_frames = 1 / fps for i in range(start_frame, stop_frame): tic = time.time() self.update_plot_animation(i) toc = time.time() time_to_update_plot = toc - tic if time_between_frames > time_to_update_plot: time.sleep(time_between_frames - time_to_update_plot) else: pass def callback_update_plot_colors(self, event): """ Updates the plot colors based on the color palette selected in the control panel """ color_palette_text = self.control_panel.color_palette.get() self.pyplot_utils.set_palette_by_name(color_palette_text) self.update_plot() def callback_update_n_colors(self, event): """ Updates the number of colors to cycle through based on the selection in the control panel """ n_colors = int(self.control_panel.n_colors.get()) self.pyplot_utils.set_n_colors(n_colors) self.update_plot() def update_n_colors(self): """ sets number of colors to run through in the pyplot color cycler. """ n_colors = int(self.control_panel.n_colors.get()) self.pyplot_utils.set_n_colors(n_colors) self.update_plot() def update_plot(self): """ clears canvas and updates plot based on the animation index """ if len(self.variables.plot_data.shape) < 3: self.pyplot_canvas.canvas.draw() elif self.variables.plot_data is not None: n_overplots = numpy.shape(self.variables.segments)[0] animation_index = int(self.control_panel.scale.get()) for i in range(n_overplots): self.variables.segments[ i, :, 1] = self.variables.plot_data[:, i, animation_index] self.pyplot_canvas.axes.clear() self.pyplot_canvas.axes.set_xlim(self.variables.xmin, self.variables.xmax) line_segments = LineCollection( self.variables.segments, self.pyplot_utils.linewidths, linestyle=self.pyplot_utils.linestyle) line_segments.set_color(self.pyplot_utils.rgb_array_full_palette) if self.variables.set_y_margins_per_frame: plot_data = self.variables.segments[:, :, 1] y_range = plot_data.max() - plot_data.min() self.variables.ymin = plot_data.min( ) - y_range * self.variables.y_margin self.variables.ymax = plot_data.max( ) + y_range * self.variables.y_margin self.pyplot_canvas.axes.set_ylim(self.variables.ymin, self.variables.ymax) self.pyplot_canvas.axes.add_collection(line_segments) self.pyplot_canvas.canvas.draw() else: pass
class CanvasResize(WidgetPanel): _widget_list = ("image_panel", "button_panel") image_panel = widget_descriptors.ImagePanelDescriptor( "image_panel") # type: ImagePanel button_panel = widget_descriptors.PanelDescriptor("button_panel", Buttons) # type: Buttons def __init__(self, primary): self._shape_ids = {} self.primary = primary primary_frame = tkinter.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_horizontal_layout() image_npix_x = 2000 image_npix_y = 1500 self.image_panel.set_max_canvas_size(image_npix_x, image_npix_y) image_data = numpy.linspace(0, 255, image_npix_x * image_npix_y) image_data = numpy.reshape(image_data, (image_npix_y, image_npix_x)) image_reader = NumpyImageReader(image_data) self.image_panel.set_image_reader(image_reader) self.drag_xlim_1 = image_npix_x * 0.1 self.drag_xlim_2 = image_npix_x * 0.9 self.drag_ylim_1 = image_npix_y * 0.1 self.drag_ylim_2 = image_npix_y * 0.9 self.image_panel.current_tool = ToolConstants.PAN self.image_panel.set_min_canvas_size(100, 100) primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) self.button_panel.draw_rect.config(command=self.callback_draw_rect) self.button_panel.draw_line.config(command=self.callback_draw_line) self.button_panel.draw_arrow.config(command=self.callback_draw_arrow) self.button_panel.draw_point.config(command=self.callback_draw_point) self.button_panel.draw_ellipse.config( command=self.callback_draw_ellipse) self.button_panel.draw_polygon.config( command=self.callback_draw_polygon) # handle the image creation event self.image_panel.canvas.bind('<<ShapeCreate>>', self.handle_new_shape) @property def point_id(self): """ None|int: The point id. """ return self._shape_ids.get(ShapeTypeConstants.POINT, None) @property def line_id(self): """ None|int: The line id. """ return self._shape_ids.get(ShapeTypeConstants.LINE, None) @property def arrow_id(self): """ None|int: The arrow id. """ return self._shape_ids.get(ShapeTypeConstants.ARROW, None) @property def rect_id(self): """ None|int: The rectangle id. """ return self._shape_ids.get(ShapeTypeConstants.RECT, None) @property def ellipse_id(self): """ None|int: The ellipse id. """ return self._shape_ids.get(ShapeTypeConstants.ELLIPSE, None) @property def polygon_id(self): """ None|int: polygon id. """ return self._shape_ids.get(ShapeTypeConstants.POLYGON, None) def callback_draw_point(self): self.image_panel.canvas.set_current_tool_to_draw_point(self.point_id) def callback_draw_line(self): self.image_panel.canvas.set_current_tool_to_draw_line(self.line_id) def callback_draw_arrow(self): self.image_panel.canvas.set_current_tool_to_draw_arrow(self.arrow_id) def callback_draw_rect(self): self.image_panel.canvas.set_current_tool_to_draw_rect(self.rect_id) def callback_draw_ellipse(self): self.image_panel.canvas.set_current_tool_to_draw_ellipse( self.ellipse_id) def callback_draw_polygon(self): self.image_panel.canvas.set_current_tool_to_draw_polygon( self.polygon_id) def handle_new_shape(self, event): """ Handle the creation of a new shape. Parameters ---------- event """ # we'll be tracking the most recently created self._shape_ids[event.y] = event.x def exit(self): self.quit()
class PyplotPanel(WidgetPanel): _widget_list = ( "pyplot_canvas", "control_panel", ) pyplot_canvas = widget_descriptors.PyplotCanvasDescriptor( "pyplot_canvas") # type: PyplotCanvas control_panel = widget_descriptors.PanelDescriptor( "control_panel", PyplotControlPanel) # type: PyplotControlPanel def __init__(self, primary): WidgetPanel.__init__(self, primary) self.variables = AppVariables() self.pyplot_utils = PlotStyleUtils() self.init_w_vertical_layout() canvas_size_pixels = self.pyplot_canvas.canvas.figure.get_size_inches( ) * self.pyplot_canvas.canvas.figure.dpi self.control_panel.scale.config(length=canvas_size_pixels[0] * 0.75) self.variables.animation_related_controls = [ self.control_panel.scale, self.control_panel.rescale_y_axis_per_frame, self.control_panel.animate, self.control_panel.fps_entry, self.control_panel.fps_label ] self.control_panel.n_colors_label.set_text("n colors") self.control_panel.n_colors.set_text(self.pyplot_utils.n_color_bins) # set listeners self.control_panel.scale.on_left_mouse_motion( self.callback_update_from_slider) self.control_panel.rescale_y_axis_per_frame.on_selection( self.callback_set_y_rescale) self.control_panel.animate.config(command=self.animate) self.control_panel.color_palette.on_selection( self.callback_update_plot_colors) self.control_panel.n_colors.on_enter_or_return_key( self.callback_update_n_colors) self.control_panel.n_colors.config(command=self.update_n_colors) # self.hide_animation_related_controls() self.hide_control_panel() @property def title(self): return self.variables.title @title.setter def title(self, val): self.variables.title = val self.pyplot_canvas.axes.set_title(val) self.update_plot() @property def y_label(self): return self.variables.y_label @y_label.setter def y_label(self, val): self.variables.y_label = val self.pyplot_canvas.axes.set_ylabel(val) self.update_plot() @property def x_label(self): return self.variables.y_label @x_label.setter def x_label(self, val): self.variables.x_label = val self.pyplot_canvas.axes.set_xlabel(val) self.update_plot() def hide_control_panel(self): self.control_panel.pack_forget() def show_control_panel(self): self.control_panel.pack() def hide_animation_related_controls(self): for widget in self.variables.animation_related_controls: widget.pack_forget() def show_animation_related_controls(self): for widget in self.variables.animation_related_controls: widget.pack() def set_y_margin_percent( self, percent_0_to_100=5 # type: float ): self.variables.y_margin = percent_0_to_100 * 0.01 def set_data(self, plot_data, x_axis=None): """ Parameters ---------- plot_data : numpy.ndarray x_axis : numpy.ndarray Returns ------- """ x = x_axis n_frames = 1 if len(plot_data.shape) == 1: self.hide_animation_related_controls() nx = len(plot_data) segments = numpy.zeros((1, nx, 2)) segments[0, :, 1] = plot_data elif len(plot_data.shape) == 2: self.hide_animation_related_controls() nx = len(plot_data[:, 0]) n_overplots = len(plot_data[0]) segments = numpy.zeros((n_overplots, nx, 2)) for i in range(n_overplots): segments[i, :, 1] = plot_data[:, i] elif len(plot_data.shape) == 3: self.show_animation_related_controls() nx = numpy.shape(plot_data)[0] n_overplots = numpy.shape(plot_data)[1] n_frames = numpy.shape(plot_data)[2] segments = numpy.zeros((n_overplots, nx, 2)) for i in range(n_overplots): segments[i, :, 1] = plot_data[:, i, 0] if x is None: x = numpy.arange(nx) segments[:, :, 0] = x self.variables.xmin = x.min() self.variables.xmax = x.max() y_range = plot_data.max() - plot_data.min() self.variables.ymin = plot_data.min( ) - y_range * self.variables.y_margin self.variables.ymax = plot_data.max( ) + y_range * self.variables.y_margin self.variables.x_axis = x self.variables.plot_data = plot_data self.variables.segments = segments self.variables.n_frames = n_frames self.control_panel.scale.config(to=n_frames - 1) if len(plot_data.shape) == 3: self.update_plot_animation(0) else: self.pyplot_canvas.axes.clear() self.pyplot_canvas.axes.plot(self.variables.plot_data) self.pyplot_canvas.axes.set_ylim(self.variables.ymin, self.variables.ymax) self.pyplot_canvas.canvas.draw() def update_plot_animation(self, animation_index): self.update_animation_index(animation_index) self.update_plot() def update_animation_index(self, animation_index): self.control_panel.scale.set(animation_index) self.variables.animation_index = animation_index def callback_update_from_slider(self, event): self.variables.animation_index = int( numpy.round(self.control_panel.scale.get())) self.update_plot() # define custom callbacks here def callback_set_y_rescale(self, event): selection = self.control_panel.rescale_y_axis_per_frame.get() if selection == SCALE_Y_AXIS_PER_FRAME_TRUE: self.variables.set_y_margins_per_frame = True else: self.variables.set_y_margins_per_frame = False y_range = self.variables.plot_data.max( ) - self.variables.plot_data.min() self.variables.ymin = self.variables.plot_data.min( ) - y_range * self.variables.y_margin self.variables.ymax = self.variables.plot_data.max( ) + y_range * self.variables.y_margin self.update_plot() def animate(self): start_frame = 0 stop_frame = self.variables.n_frames fps = float(self.control_panel.fps_entry.get()) time_between_frames = 1 / fps for i in range(start_frame, stop_frame): tic = time.time() self.update_animation_index(i) self.update_plot() toc = time.time() time_to_update_plot = toc - tic if time_between_frames > time_to_update_plot: time.sleep(time_between_frames - time_to_update_plot) else: pass def callback_update_plot_colors(self, event): color_palette_text = self.control_panel.color_palette.get() self.pyplot_utils.set_palette_by_name(color_palette_text) self.update_plot() def callback_update_n_colors(self, event): n_colors = int(self.control_panel.n_colors.get()) self.pyplot_utils.set_n_colors(n_colors) self.update_plot() def update_n_colors(self): n_colors = int(self.control_panel.n_colors.get()) self.pyplot_utils.set_n_colors(n_colors) self.update_plot() def update_plot(self): if len(self.variables.plot_data.shape) < 3: self.pyplot_canvas.canvas.draw() elif self.variables.plot_data is not None: n_overplots = numpy.shape(self.variables.segments)[0] animation_index = int(self.control_panel.scale.get()) for i in range(n_overplots): self.variables.segments[ i, :, 1] = self.variables.plot_data[:, i, animation_index] self.pyplot_canvas.axes.clear() self.pyplot_canvas.axes.set_xlim(self.variables.xmin, self.variables.xmax) line_segments = LineCollection( self.variables.segments, self.pyplot_utils.linewidths, linestyle=self.pyplot_utils.linestyle) line_segments.set_color(self.pyplot_utils.rgb_array_full_palette) if self.variables.set_y_margins_per_frame: plot_data = self.variables.segments[:, :, 1] y_range = plot_data.max() - plot_data.min() self.variables.ymin = plot_data.min( ) - y_range * self.variables.y_margin self.variables.ymax = plot_data.max( ) + y_range * self.variables.y_margin self.pyplot_canvas.axes.set_ylim(self.variables.ymin, self.variables.ymax) self.pyplot_canvas.axes.add_collection(line_segments) self.pyplot_canvas.canvas.draw() else: pass
class CanvasResize(WidgetPanel): _widget_list = ("image_panel", "button_panel") image_panel = widget_descriptors.ImagePanelDescriptor("image_panel") # type: ImagePanel button_panel = widget_descriptors.PanelDescriptor("button_panel", Buttons) # type: Buttons def __init__(self, primary): self.rect_id = None self.line_id = None self.arrow_id = None self.point_id = None self.polygon_id = None self.n_shapes = 0 self.primary = primary primary_frame = tkinter.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_horizontal_layout() self.image_panel.image_frame.set_canvas_size(800, 600) self.image_panel.resizeable = True image_npix_x = 1200 image_npix_y = 500 image_data = numpy.random.random((image_npix_y, image_npix_x)) image_data = image_data * 255 image_reader = NumpyImageReader(image_data) self.image_panel.set_image_reader(image_reader) self.drag_xlim_1 = image_npix_x * 0.25 self.drag_xlim_2 = image_npix_x * 0.75 self.drag_ylim_1 = image_npix_y * 0.1 self.drag_ylim_2 = image_npix_y * 0.9 self.image_panel.current_tool = ToolConstants.PAN_TOOL self.image_panel.axes_canvas.image_x_min_val = 500 self.image_panel.axes_canvas.image_x_max_val = 1200 self.image_panel.axes_canvas.image_y_min_val = 5000 self.image_panel.axes_canvas.image_y_max_val = 2000 primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) self.image_panel.canvas.set_canvas_size(800, 800) self.button_panel.draw_rect.on_left_mouse_click(self.callback_draw_rect) self.button_panel.draw_line.on_left_mouse_click(self.callback_draw_line) self.button_panel.draw_arrow.on_left_mouse_click(self.callback_draw_arrow) self.button_panel.draw_point.on_left_mouse_click(self.callback_draw_point) self.button_panel.draw_polygon.on_left_mouse_click(self.callback_draw_polygon) self.button_panel.edit_shape.on_left_mouse_click(self.callback_edit_shape) self.image_panel.canvas.on_left_mouse_release(self.callback_on_left_mouse_release) def callback_draw_rect(self, event): self.image_panel.canvas.set_current_tool_to_draw_rect(self.rect_id) def callback_draw_line(self, event): self.image_panel.canvas.set_current_tool_to_draw_line_by_dragging(self.line_id) def callback_draw_arrow(self, event): self.image_panel.canvas.set_current_tool_to_draw_arrow_by_dragging(self.arrow_id) def callback_draw_point(self, event): self.image_panel.canvas.set_current_tool_to_draw_point(self.point_id) def callback_draw_polygon(self, event): self.image_panel.canvas.set_current_tool_to_draw_polygon_by_clicking(self.polygon_id) def callback_edit_shape(self, event): self.image_panel.canvas.set_current_tool_to_edit_shape() def callback_on_left_mouse_release(self, event): self.image_panel.canvas.callback_handle_left_mouse_release(event) n_shapes = len(self.image_panel.canvas.get_non_tool_shape_ids()) if n_shapes > self.n_shapes: if self.image_panel.current_tool == ToolConstants.DRAW_RECT_BY_DRAGGING: self.rect_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_LINE_BY_DRAGGING: self.line_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_ARROW_BY_DRAGGING: self.arrow_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_POINT_BY_CLICKING: self.point_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_POLYGON_BY_CLICKING: self.polygon_id = self.image_panel.canvas.variables.current_shape_id self.image_panel.canvas.get_vector_object( self.image_panel.canvas.variables.current_shape_id).image_drag_limits = (self.drag_ylim_1, self.drag_xlim_1, self.drag_ylim_2, self.drag_xlim_2) def exit(self): self.quit()
class PlotDemo(WidgetPanel): """ Basic plot demo gui. """ _widget_list = ("button_panel", "pyplot_panel") button_panel = widget_descriptors.PanelDescriptor( "button_panel", ButtonPanel, default_text="plot buttons") # type: ButtonPanel pyplot_panel = widget_descriptors.PyplotPanelDescriptor( "pyplot_panel") # type: PyplotPanel def __init__(self, primary): """ Parameters ---------- primary The primary widget. """ # set the primary frame primary_frame = tkinter.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_vertical_layout() # need to pack both primary frame and self, since this is the main app window. primary_frame.pack() # set up event listeners self.button_panel.single_plot.config(command=self.single_plot) self.button_panel.multi_plot.config(command=self.multi_plot) self.button_panel.animated_plot.config(command=self.animated_plot) self.pyplot_panel.set_y_margin_percent(5) self.pyplot_panel.variables.set_y_margins_per_frame = True self.single_plot() self.pyplot_panel.show_control_panel() def single_plot(self): """ A single plot callback. Returns ------- None """ plot_data = self.mockup_animation_data_1() self.pyplot_panel.set_data(plot_data) self.pyplot_panel.title = "single plot" def multi_plot(self): """ A multiplot callback. Returns ------- None """ plot_data = self.mockup_animation_data_2() data_shape = numpy.shape(plot_data) x_axis_points = data_shape[0] n_overplots = data_shape[1] print("plot data has dimensions of: " + str(data_shape)) print("with " + str(x_axis_points) + " data points along the x axis") print("and " + str(n_overplots) + " overplots") self.pyplot_panel.set_data(plot_data) def animated_plot(self): """ Animated callback plot. Returns ------- None """ plot_data = self.mockup_animation_data_3() data_shape = numpy.shape(plot_data) x_axis_points = data_shape[0] n_overplots = data_shape[1] n_animation_frames = data_shape[2] print("plot data has dimensions of: " + str(data_shape)) print("with " + str(x_axis_points) + " data points along the x axis") print("and " + str(n_overplots) + " overplots") print("and " + str(n_animation_frames) + " animation frames") self.pyplot_panel.set_data(plot_data) @staticmethod def mockup_animation_data_3(): """ Mock annimation. Returns ------- numpy.ndarray """ n_overplots = 10 nx = 200 n_times = 100 x_axis = numpy.linspace(0, numpy.pi, nx) y_data_1 = numpy.sin(x_axis) y_data_2 = numpy.zeros((len(x_axis), n_overplots)) y_data_3 = numpy.zeros((len(x_axis), n_overplots, n_times)) scaling_factors = numpy.linspace(0.7, 1, n_overplots) for i in range(n_overplots): y_data_2[:, i] = y_data_1 * scaling_factors[i] x_over_time = numpy.zeros((nx, n_times)) x_over_time_start = numpy.linspace(0, numpy.pi, n_times) for i in range(n_times): x_start = x_over_time_start[i] x = numpy.linspace(x_start, numpy.pi + x_start, nx) x_over_time[:, i] = x y = numpy.sin(x) for j in range(n_overplots): y_data_3[:, j, i] = y * scaling_factors[j] return y_data_3 @staticmethod def mockup_animation_data_2(): """ Mock animation. Returns ------- numpy.ndarray """ n_overplots = 10 nx = 200 x_axis = numpy.linspace(0, 2 * numpy.pi, nx) y_data_1 = x_axis y_data_2 = numpy.zeros((len(x_axis), n_overplots)) scaling_factors = numpy.linspace(0.7, 1, n_overplots) for i in range(n_overplots): y_data_2[:, i] = y_data_1 * scaling_factors[i] return y_data_2 @staticmethod def mockup_animation_data_1(): """ Mock animation. Returns ------- numpy.ndarray """ x = numpy.linspace(-5, 5, 200) y = numpy.sinc(x) return y
class MetaIconDemo(WidgetPanel): # The metaicon will be a popup, so leave the widget list unpopulated _widget_list = () metaicon = widget_descriptors.PanelDescriptor("metaicon", MetaIcon) # type: MetaIcon def __init__(self, primary): self.primary = primary primary_frame = basic_widgets.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_horizontal_layout() lat = 35.05452800184999 lon = -106.59258099877832 collect_start = numpy.datetime64('2016-09-21T16:41:07.000000') collect_duration = 14.47132 collector_name = 'Sandia FARAD X-band' core_name = '0508C01_PS0009_CC000000_N03_M1_PC054036_HH_wfcc_sv' azimuth = 241.15240495122976 graze = 28.403774480669846 layover = 263.96070589564016 shadow = -90.0 multipath = 66.5880303387554 side_of_track = 'R' col_impulse_response_width = 0.1903215223097113 row_impulse_response_width = 0.1606955380577428 grid_column_sample_spacing = 0.04462 grid_row_sample_spacing = 0.03767 image_plane = 'SLANT' tx_rf_bandwidth = 2843.7592056795997 rniirs = None polarization = 'H:H' data_container = MetaIconDataContainer(lat=lat, lon=lon, collect_start=collect_start, collect_duration=collect_duration, collector_name=collector_name, core_name=core_name, azimuth=azimuth, graze=graze, layover=layover, shadow=shadow, multipath=multipath, side_of_track=side_of_track, col_impulse_response_width=col_impulse_response_width, row_impulse_response_width=row_impulse_response_width, grid_column_sample_spacing=grid_column_sample_spacing, grid_row_sample_spacing=grid_row_sample_spacing, image_plane=image_plane, tx_rf_bandwidth=tx_rf_bandwidth, rniirs=rniirs, polarization=polarization, ) self.metaicon_popup_panel = tkinter.Toplevel(self.primary) self.metaicon = MetaIcon(self.metaicon_popup_panel) self.metaicon.create_from_metaicon_data_container(data_container) # hide the main window so just the metaicon popup is showing self.primary.withdraw() # quit the program when the user closes the metaicon popup self.metaicon_popup_panel.protocol("WM_DELETE_WINDOW", self.primary.quit)
class ImagePanel(WidgetPanel): _widget_list = ( "toolbar", "image_frame", ) image_frame = widget_descriptors.PanelDescriptor( "image_frame", ImageFrame) # type: ImageFrame toolbar = widget_descriptors.PanelDescriptor("toolbar", Toolbar) # type: Toolbar canvas = widget_descriptors.ImageCanvasDescriptor( "canvas") # type: ImageCanvas axes_canvas = widget_descriptors.AxesImageCanvasDescriptor( "axes_canvas") # type: AxesImageCanvas def __init__(self, parent): WidgetPanel.__init__(self, parent) self.variables = AppVariables() self.init_w_vertical_layout() self.pack(fill=tkinter.BOTH, expand=tkinter.YES) self.toolbar.left_margin.config(width=5) self.toolbar.right_margin.config(width=5) self.toolbar.top_margin.config(width=5) self.toolbar.bottom_margin.config(width=5) self.toolbar.left_margin_label.master.forget() self.toolbar.title_label.master.forget() # set up callbacks self.toolbar.save_canvas.on_left_mouse_click(self.callback_save_canvas) self.toolbar.save_image.on_left_mouse_click(self.callback_save_image) self.toolbar.left_margin.on_enter_or_return_key( self.callback_update_axes) self.toolbar.right_margin.on_enter_or_return_key( self.callback_update_axes) self.toolbar.top_margin.on_enter_or_return_key( self.callback_update_axes) self.toolbar.bottom_margin.on_enter_or_return_key( self.callback_update_axes) self.toolbar.title.on_enter_or_return_key(self.callback_update_axes) self.toolbar.x.on_enter_or_return_key(self.callback_update_axes) self.toolbar.y.on_enter_or_return_key(self.callback_update_axes) self.toolbar.zoom_in.on_left_mouse_click(self.callback_set_to_zoom_in) self.toolbar.zoom_out.on_left_mouse_click( self.callback_set_to_zoom_out) self.toolbar.pan.on_left_mouse_click(self.callback_set_to_pan) self.toolbar.margins_checkbox.config( command=self.callback_hide_show_margins) self.toolbar.axes_labels_checkbox.config( command=self.callback_hide_show_axes_controls) self.toolbar.pack(expand=tkinter.YES, fill=tkinter.X) self.canvas = self.image_frame.outer_canvas.canvas self.axes_canvas = self.image_frame.outer_canvas def callback_set_to_zoom_in(self, event): self.current_tool = ToolConstants.ZOOM_IN_TOOL def callback_set_to_zoom_out(self, event): self.current_tool = ToolConstants.ZOOM_OUT_TOOL def callback_set_to_pan(self, event): self.current_tool = ToolConstants.PAN_TOOL def callback_save_canvas(self, event): save_fname = asksaveasfilename() if "." not in os.path.basename(save_fname): save_fname = save_fname + ".png" self.axes_canvas.save_full_canvas_as_png(save_fname) def callback_save_image(self, event): save_fname = asksaveasfilename() if "." not in os.path.basename(save_fname): save_fname = save_fname + ".png" self.canvas.save_full_canvas_as_png(save_fname) def callback_hide_show_margins(self): show_margins = self.toolbar.margins_checkbox.is_selected() if show_margins is False: self.toolbar.left_margin_label.master.forget() else: self.toolbar.left_margin_label.master.pack() def callback_hide_show_axes_controls(self): show_axes_controls = self.toolbar.axes_labels_checkbox.is_selected() if show_axes_controls is False: self.toolbar.title_label.master.forget() else: self.toolbar.title_label.master.pack() def callback_update_axes(self, event): self.image_frame.outer_canvas.title = self.toolbar.title.get() self.image_frame.outer_canvas.x_label = self.toolbar.x.get() self.image_frame.outer_canvas.y_label = self.toolbar.y.get() if self.toolbar.top_margin.get() == "0": self.toolbar.top_margin.set_text( str(self.image_frame.outer_canvas.top_margin_pixels)) if self.toolbar.bottom_margin.get() == "0": self.toolbar.bottom_margin.set_text( str(self.image_frame.outer_canvas.bottom_margin_pixels)) if self.toolbar.left_margin.get() == "0": self.toolbar.left_margin.set_text( str(self.image_frame.outer_canvas.left_margin_pixels)) if self.toolbar.right_margin.get() == "0": self.toolbar.right_margin.set_text( str(self.image_frame.outer_canvas.right_margin_pixels)) self.image_frame.outer_canvas.left_margin_pixels = int( self.toolbar.left_margin.get()) self.image_frame.outer_canvas.right_margin_pixels = int( self.toolbar.right_margin.get()) self.image_frame.outer_canvas.top_margin_pixels = int( self.toolbar.top_margin.get()) self.image_frame.outer_canvas.bottom_margin_pixels = int( self.toolbar.bottom_margin.get()) self.update_everything() def set_image_reader(self, image_reader): self.image_frame.outer_canvas.set_image_reader(image_reader) @property def resizeable(self): return self.variables.resizeable @resizeable.setter def resizeable(self, value): self.variables.resizeable = value if value is False: self.pack(expand=tkinter.NO) self.image_frame.resizeable = False if self.resizeable: self.on_resize(self.callback_resize) @property def current_tool(self): return self.image_frame.outer_canvas.canvas.variables.current_tool @current_tool.setter def current_tool(self, value): if value is None: self.canvas.set_current_tool_to_none() elif value == ToolConstants.EDIT_SHAPE_TOOL: self.canvas.set_current_tool_to_edit_shape() elif value == ToolConstants.PAN_TOOL: self.canvas.set_current_tool_to_pan() elif value == ToolConstants.SELECT_TOOL: self.canvas.set_current_tool_to_selection_tool() elif value == ToolConstants.ZOOM_IN_TOOL: self.canvas.set_current_tool_to_zoom_in() elif value == ToolConstants.ZOOM_OUT_TOOL: self.canvas.set_current_tool_to_zoom_out() elif value == ToolConstants.DRAW_ARROW_BY_DRAGGING: self.canvas.set_current_tool_to_draw_arrow_by_dragging() elif value == ToolConstants.DRAW_RECT_BY_DRAGGING: self.canvas.set_current_tool_to_draw_rect() def callback_resize(self, event): self.update_everything() def update_everything(self): self.image_frame.outer_canvas.delete("all") width = self.winfo_width() height = self.winfo_height() parent_geometry = self.image_frame.master.winfo_geometry() outer_canvas_geometry = self.image_frame.winfo_geometry() parent_geom_width = int(parent_geometry.split("x")[0]) parent_geom_height = int(parent_geometry.split("x")[1].split("+")[0]) parent_geom_pad_x = int(parent_geometry.split("+")[1]) parent_geom_pad_y = int(parent_geometry.split("+")[2]) outer_canvas_width = int(outer_canvas_geometry.split("x")[0]) outer_canvas_height = int( outer_canvas_geometry.split("x")[1].split("+")[0]) outer_canvas_pad_x = int(outer_canvas_geometry.split("+")[1]) outer_canvas_pad_y = int(outer_canvas_geometry.split("+")[2]) constant_offset_x = WidgetPanel.padx constant_offset_y = WidgetPanel.pady width_offset = parent_geom_width - outer_canvas_width + outer_canvas_pad_x + parent_geom_pad_x - constant_offset_x height_offset = parent_geom_height - outer_canvas_height + outer_canvas_pad_y + parent_geom_pad_y - constant_offset_y adjusted_canvas_width = width - width_offset adjusted_canvas_height = height - height_offset if parent_geom_width > 1 and outer_canvas_width > 1: self.image_frame.set_canvas_size(adjusted_canvas_width, adjusted_canvas_height) self.axes_canvas.set_canvas_size(adjusted_canvas_width, adjusted_canvas_height) self.canvas.set_canvas_size( self.axes_canvas.variables.canvas_width - self.axes_canvas.left_margin_pixels - self.axes_canvas.right_margin_pixels, self.axes_canvas.variables.canvas_height - self.axes_canvas.top_margin_pixels - self.axes_canvas.bottom_margin_pixels) self.canvas.update_current_image() display_image_dims = numpy.shape( self.canvas.variables.canvas_image_object.display_image) self.axes_canvas.set_canvas_size( display_image_dims[1] + self.axes_canvas.right_margin_pixels + self.axes_canvas.left_margin_pixels, display_image_dims[0] + self.axes_canvas.top_margin_pixels + self.axes_canvas.bottom_margin_pixels + 5) self.canvas.set_canvas_size( self.axes_canvas.variables.canvas_width - self.image_frame.outer_canvas.left_margin_pixels - self.image_frame.outer_canvas.right_margin_pixels, self.image_frame.outer_canvas.variables.canvas_height - self.image_frame.outer_canvas.top_margin_pixels - self.image_frame.outer_canvas.bottom_margin_pixels) self.canvas.update_current_image() self.axes_canvas.delete("all") self.image_frame.create_window(0, 0, anchor=tkinter.NW, window=self.image_frame.outer_canvas) self.image_frame.outer_canvas.create_window( self.image_frame.outer_canvas.left_margin_pixels, self.image_frame.outer_canvas.top_margin_pixels, anchor=tkinter.NW, window=self.image_frame.outer_canvas.canvas) self.canvas.redraw_all_shapes() self.image_frame.outer_canvas._update_y_axis() self.image_frame.outer_canvas._update_y_label() self.image_frame.outer_canvas._update_title() self.image_frame.outer_canvas._update_x_axis() self.image_frame.outer_canvas._update_x_label()
class CanvasDemo(WidgetPanel): _widget_list = ("button_panel", "canvas_demo_image_panel", "pyplot_panel") button_panel = widget_descriptors.PanelDescriptor( "button_panel", CanvasDemoButtonPanel) # type: CanvasDemoButtonPanel pyplot_panel = widget_descriptors.PyplotImagePanelDescriptor( "pyplot_panel") # type: PyplotImagePanel canvas_demo_image_panel = widget_descriptors.ImagePanelDescriptor( "canvas_demo_image_panel") # type: ImagePanel def __init__(self, primary): primary_frame = basic_widgets.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.variables = AppVariables() self.init_w_horizontal_layout() primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) self.button_panel.pack(fill=tkinter.X, expand=tkinter.NO) self.pyplot_panel.pack(expand=tkinter.YES) self.canvas_demo_image_panel.pack(expand=tkinter.YES) # bind events to callbacks here self.button_panel.fname_select.config( command=self.callback_initialize_canvas_image) self.button_panel.rect_select.config( command=self.callback_set_to_select) self.button_panel.draw_line.config(command=self.callback_draw_line) self.button_panel.draw_arrow.config(command=self.callback_draw_arrow) self.button_panel.draw_rect.config(command=self.callback_draw_rect) self.button_panel.draw_polygon.config( command=self.callback_draw_polygon) self.button_panel.draw_point.config(command=self.callback_draw_point) self.button_panel.edit.config(command=self.callback_edit) self.button_panel.color_selector.config( command=self.callback_activate_color_selector) self.button_panel.remap_dropdown.on_selection(self.callback_remap) self.canvas_demo_image_panel.canvas.on_left_mouse_click( self.callback_handle_canvas_left_mouse_click) self.canvas_demo_image_panel.canvas.on_left_mouse_release( self.callback_handle_canvas_left_mouse_release) def callback_save_kml(self): kml_save_fname = asksaveasfilename( initialdir=os.path.expanduser("~/Downloads")) kml_util = KmlUtil() for shape_id in self.canvas_demo_image_panel.canvas.variables.shape_ids: image_coords = self.canvas_demo_image_panel.canvas.get_shape_image_coords( shape_id) shape_type = self.canvas_demo_image_panel.canvas.get_shape_type( shape_id) if image_coords: sicd_meta = self.canvas_demo_image_panel.canvas.variables.canvas_image_object.reader_object.sicdmeta image_points = numpy.zeros((int(len(image_coords) / 2), 2)) image_points[:, 0] = image_coords[0::2] image_points[:, 1] = image_coords[1::2] ground_points_ecf = point_projection.image_to_ground( image_points, sicd_meta) ground_points_latlon = geocoords.ecf_to_geodetic( ground_points_ecf) world_y_coordinates = ground_points_latlon[:, 0] world_x_coordinates = ground_points_latlon[:, 1] xy_point_list = [ (x, y) for x, y in zip(world_x_coordinates, world_y_coordinates) ] if shape_id == self.canvas_demo_image_panel.canvas.variables.zoom_rect.uid: pass elif shape_type == self.canvas_demo_image_panel.canvas.variables.select_rect.uid: pass elif shape_type == ShapeTypeConstants.POINT: kml_util.add_point(str(shape_id), xy_point_list[0]) elif shape_type == ShapeTypeConstants.LINE: kml_util.add_linestring(str(shape_id), xy_point_list) elif shape_type == ShapeTypeConstants.POLYGON: kml_util.add_polygon(str(shape_id), xy_point_list) elif shape_type == ShapeTypeConstants.RECT: kml_util.add_polygon(str(shape_id), xy_point_list) kml_util.write_to_file(kml_save_fname) def callback_handle_canvas_left_mouse_click(self, event): self.canvas_demo_image_panel.canvas.callback_handle_left_mouse_click( event) current_shape = self.canvas_demo_image_panel.canvas.current_shape_id if current_shape: self.variables.shapes_in_selector.append(current_shape) self.variables.shapes_in_selector = sorted( list(set(self.variables.shapes_in_selector))) def callback_handle_canvas_left_mouse_release(self, event): self.canvas_demo_image_panel.canvas.callback_handle_left_mouse_release( event) if self.canvas_demo_image_panel.canvas.variables.select_rect.uid == self.canvas_demo_image_panel.canvas.current_shape_id: self.update_selection() def callback_edit(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_edit_shape( select_closest_first=True) def callback_activate_color_selector(self): self.canvas_demo_image_panel.canvas.activate_color_selector() def callback_draw_line(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_line() def callback_draw_arrow(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_arrow() def callback_draw_rect(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_rect() def callback_draw_polygon(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_polygon() def callback_draw_point(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_point() def callback_set_to_select(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_select() # define custom callbacks here def callback_remap(self): self.update_selection() def update_selection(self): remap_dict = {entry[0]: entry[1] for entry in remap.get_remap_list()} selection = self.button_panel.remap_dropdown.get() self.variables.image_reader.set_remap_type(remap_dict[selection]) image_data = self.canvas_demo_image_panel.canvas.get_image_data_in_canvas_rect_by_id( self.canvas_demo_image_panel.canvas.variables.select_rect.uid) self.pyplot_panel.update_image(image_data) self.canvas_demo_image_panel.canvas.update_current_image() def callback_initialize_canvas_image(self): image_file_extensions = ['*.nitf', '*.NITF', '*.ntf', '*.NTF'] ftypes = [ ('NITF files', image_file_extensions), ('All files', '*'), ] new_fname = askopenfilename(initialdir=self.variables.browse_directory, filetypes=ftypes) if new_fname: self.variables.browse_directory = os.path.split(new_fname)[0] self.variables.image_reader = ComplexImageReader(new_fname) self.canvas_demo_image_panel.set_image_reader( self.variables.image_reader)