<commit_before># coding: utf-8
'''
GTK widget for viewing and navigating within a DataCube
Uses GTK as a widget library

@author: <NAME>
'''

import gtk
import matplotlib
import numpy
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg, NavigationToolbar2GTKAgg

class CubeViewWidget(gtk.VBox):

    def __init__(self, cube, parent_window):
        gtk.VBox.__init__(self, False)
        
        self.cube = cube
        self._x, self._y, self._z = 0,0,0 # Coordinates of our current view in the cube. Read/write via the .x .y and .z properties
        self.last_drawn_x,self.last_drawn_y, self.last_drawn_z = 0,0,0 # what the coordinates were last time we drew   
        
        fig = matplotlib.figure.Figure()
        self.fig = fig # Save a reference to the figure
        axes = fig.add_subplot(111)
        self.imgplot = axes.imshow(cube.data[:,:,self._z].transpose(1,0), cmap="spectral",vmin=0, vmax=numpy.nanmax(cube.data))
        fig.colorbar(self.imgplot) # Add a color scale at the right-hand side
        axes.set_xlabel(u"Right Ascension \u03b1")
        axes.xaxis.set_major_formatter(self._AxisFormatter(self.cube))
        axes.set_ylabel(u"Declination \u03b4")
        axes.yaxis.set_major_formatter(self._AxisFormatter(self.cube))
        axes.set_ylim(0,cube.data.shape[1])
        
        self.xline = axes.axvline(x=self._x, linewidth=4, color="white", alpha=0.5)
        self.yline = axes.axhline(y=self._y, linewidth=4, color="white", alpha=0.5)
        
        canvas = FigureCanvasGTKAgg(fig)  # a gtk.DrawingArea
        self.pack_start(canvas)
        
        # The toolbar:
        self.pack_start(gtk.HSeparator(), False,False)
        self.toolbar = self._NavigationToolbar(cube, canvas, parent_window) # parent_window is needed for the "Save image..." file chooser dialog
        self.toolbar.remove(self.toolbar.get_nth_item(6)) # Remove the "Configure subplots" button which causes rendering issues if used
        self.pack_start(self.toolbar, False, False)
        
        canvas.mpl_connect('button_press_event', self._figure_mousedown)
        canvas.mpl_connect('button_release_event', self._figure_mouseup)
        canvas.mpl_connect('motion_notify_event', self._figure_mousemoved)
        self._is_mouse_down = False # is the mouse button currently pressed?
        
        # The velocity navigation:
        self.pack_start(gtk.HSeparator(), False,False)
        scale = gtk.HScale()
        self.scale = scale
        scale.set_range(0, cube.data.shape[2]-1)
        scale.set_digits(0)
        scale.set_value(self._z)
        scale.set_draw_value(False)# Hide the built in display of the current value since it's not in real units (not in km/s)
        vel_middle = int(cube.data.shape[2]/2)
        scale.add_mark(0, gtk.POS_BOTTOM, "{vel} {units}".format(vel=cube.velocity_at(z=0,decimals=0), units="km/s"))
        scale.add_mark(vel_middle, gtk.POS_BOTTOM, "{vel} {units}".format(vel=cube.velocity_at(z=vel_middle,decimals=0), units="km/s"))
        scale.add_mark(cube.data.shape[2]-1, gtk.POS_BOTTOM, "{vel} {units}".format(vel=cube.velocity_at(z=cube.data.shape[2]-1,decimals=0), units="km/s"))
        self.pack_start(scale, False, False)
        scale.connect("value-changed", self._update_velocity)
        
        self.needs_redraw = False # Set this to True if you want the canvas to be repainted
        gtk.idle_add(CubeViewWidget._check_redraw, self) # we only want to re re-drawing when the GUI is idle, for maximum interactivity
        
        self.toolbar.update_mouseout_message()
    @property
    def x(self): return self._x
    @x.setter
    def x(self, value):
        if type(value) is float and value >= 0 and value <= 1:
            # Allow people to enter a float like 0.5 to set x to the exact midpoint value:
            self._x = round((self.cube.data.shape[0]-1) * value) 
        else:
            try:
                self._x = int(value) % self.cube.data.shape[0]
            except:
                raise ValueError("Invalid x value given") 
        self.needs_redraw = True # We have changed the current coordinates, so will need to redraw
        self.toolbar.update_mouseout_message()
    
    @property
    def y(self): return self._y
    @y.setter
    def y(self, value): 
        if type(value) is float and value >= 0 and value <= 1:
            # Allow people to enter a float like 0.5 to set x to the exact midpoint value:
            self._y = round((self.cube.data.shape[1]-1) * value) 
        else:
            try:
                self._y = int(value) % self.cube.data.shape[1]
            except:
                raise ValueError("Invalid y value given")
        self.needs_redraw = True # We have changed the current coordinates, so will need to redraw
        self.toolbar.update_mouseout_message()
    
    @property
    def z(self): return self._z
    @z.setter
    def z(self, value):
        if type(value) is float and value >= 0 and value <= 1:
            # Allow people to enter a float like 0.5 to set x to the exact midpoint value:
            self._z = round((self.cube.data.shape[2]-1) * value) 
        else:
            try:
                self._z = int(value) % self.cube.data.shape[2]
            except:
                raise ValueError("Invalid z value given")
        self.needs_redraw = True # We have changed the current coordinates, so will need to redraw
        if self.scale.get_value() != self._z:
            self.scale.set_value(self._z)

    def _update_velocity(self, scale_widget):
        self.z = int(scale_widget.get_value())
        self.toolbar.update_mouseout_message() # the current velocity shown in the status message must be updated.
    
    def _figure_mousedown(self, event):
        if event.xdata != None and event.ydata != None: # If we're in the canvas:
            self.x = int(event.xdata)
            self.y = int(event.ydata)
            self._is_mouse_down = True
    def _figure_mouseup(self, event):
        if event.xdata != None and event.ydata != None: # If we're in the canvas:
            self.x = int(event.xdata)
            self.y = int(event.ydata)
        self._is_mouse_down = False
    def _figure_mousemoved(self, event):
        if self._is_mouse_down and (event.xdata != None and event.ydata != None): # If we're in the canvas:
            self.x = int(event.xdata)
            self.y = int(event.ydata)
        # Note other mouse motion updates get processed below in _NavigationToolbar.mouse_move

    def _check_redraw(self):
        ''' Update this widget's display if needed. Called only when the main event loop is idle '''
        if self.needs_redraw:
            if self._x != self.last_drawn_x:
                self.xline.set_xdata([self._x, self._x])
                self.last_drawn_x = self._x
            if self._y != self.last_drawn_y:
                self.yline.set_ydata([self._y, self._y])
                self.last_drawn_y = self._y
            if self._z != self.last_drawn_z: # If z has changed since we last drew:
                self.imgplot.set_data(self.cube.data[:,:,self._z].transpose(1,0))
                self.last_drawn_z = self._z
            self.fig.canvas.draw()
            self.needs_redraw = False<commit_msg>Add support for registering event handlers on click<commit_after># coding: utf-8
'''
GTK widget for viewing and navigating within a DataCube
Uses GTK as a widget library

@author: <NAME>
'''

import gtk
import matplotlib
import numpy
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg, NavigationToolbar2GTKAgg

class CubeViewWidget(gtk.VBox):

    def __init__(self, cube, parent_window):
        gtk.VBox.__init__(self, False)
        
        self.cube = cube
        self._x, self._y, self._z = 0,0,0 # Coordinates of our current view in the cube. Read/write via the .x .y and .z properties
        self.last_drawn_x,self.last_drawn_y, self.last_drawn_z = 0,0,0 # what the coordinates were last time we drew   
        
        fig = matplotlib.figure.Figure()
        self.fig = fig # Save a reference to the figure
        axes = fig.add_subplot(111)
        self.imgplot = axes.imshow(cube.data[:,:,self._z].transpose(1,0), cmap="spectral",vmin=0, vmax=numpy.nanmax(cube.data))
        fig.colorbar(self.imgplot) # Add a color scale at the right-hand side
        axes.set_xlabel(u"Right Ascension \u03b1")
        axes.xaxis.set_major_formatter(self._AxisFormatter(self.cube))
        axes.set_ylabel(u"Declination \u03b4")
        axes.yaxis.set_major_formatter(self._AxisFormatter(self.cube))
        axes.set_ylim(0,cube.data.shape[1])
        
        self.xline = axes.axvline(x=self._x, linewidth=4, color="white", alpha=0.5)
        self.yline = axes.axhline(y=self._y, linewidth=4, color="white", alpha=0.5)
        
        canvas = FigureCanvasGTKAgg(fig)  # a gtk.DrawingArea
        self.pack_start(canvas)
        
        # The toolbar:
        self.pack_start(gtk.HSeparator(), False,False)
        self.toolbar = self._NavigationToolbar(cube, canvas, parent_window) # parent_window is needed for the "Save image..." file chooser dialog
        self.toolbar.remove(self.toolbar.get_nth_item(6)) # Remove the "Configure subplots" button which causes rendering issues if used
        self.pack_start(self.toolbar, False, False)
        
        canvas.mpl_connect('button_press_event', self._figure_mousedown)
        canvas.mpl_connect('button_release_event', self._figure_mouseup)
        canvas.mpl_connect('motion_notify_event', self._figure_mousemoved)
        self._is_mouse_down = False # is the mouse button currently pressed?
        
        # A list of methods to call when the user clicks on a point. Passes an (x,y,z) tuple and a flux value to each function
        self.click_notify = [] 
        
        # The velocity navigation:
        self.pack_start(gtk.HSeparator(), False,False)
        scale = gtk.HScale()
        self.scale = scale
        scale.set_range(0, cube.data.shape[2]-1)
        scale.set_digits(0)
        scale.set_value(self._z)
        scale.set_draw_value(False)# Hide the built in display of the current value since it's not in real units (not in km/s)
        vel_middle = int(cube.data.shape[2]/2)
        scale.add_mark(0, gtk.POS_BOTTOM, "{vel} {units}".format(vel=cube.velocity_at(z=0,decimals=0), units="km/s"))
        scale.add_mark(vel_middle, gtk.POS_BOTTOM, "{vel} {units}".format(vel=cube.velocity_at(z=vel_middle,decimals=0), units="km/s"))
        scale.add_mark(cube.data.shape[2]-1, gtk.POS_BOTTOM, "{vel} {units}".format(vel=cube.velocity_at(z=cube.data.shape[2]-1,decimals=0), units="km/s"))
        self.pack_start(scale, False, False)
        scale.connect("value-changed", self._update_velocity)
        
        self.needs_redraw = False # Set this to True if you want the canvas to be repainted
        gtk.idle_add(CubeViewWidget._check_redraw, self) # we only want to re re-drawing when the GUI is idle, for maximum interactivity
        
        self.toolbar.update_mouseout_message()
    @property
    def x(self): return self._x
    @x.setter
    def x(self, value):
        if type(value) is float and value >= 0 and value <= 1:
            # Allow people to enter a float like 0.5 to set x to the exact midpoint value:
            self._x = round((self.cube.data.shape[0]-1) * value) 
        else:
            try:
                self._x = int(value) % self.cube.data.shape[0]
            except:
                raise ValueError("Invalid x value given") 
        self.needs_redraw = True # We have changed the current coordinates, so will need to redraw
        self.toolbar.update_mouseout_message()
    
    @property
    def y(self): return self._y
    @y.setter
    def y(self, value): 
        if type(value) is float and value >= 0 and value <= 1:
            # Allow people to enter a float like 0.5 to set x to the exact midpoint value:
            self._y = round((self.cube.data.shape[1]-1) * value) 
        else:
            try:
                self._y = int(value) % self.cube.data.shape[1]
            except:
                raise ValueError("Invalid y value given")
        self.needs_redraw = True # We have changed the current coordinates, so will need to redraw
        self.toolbar.update_mouseout_message()
    
    @property
    def z(self): return self._z
    @z.setter
    def z(self, value):
        if type(value) is float and value >= 0 and value <= 1:
            # Allow people to enter a float like 0.5 to set x to the exact midpoint value:
            self._z = round((self.cube.data.shape[2]-1) * value) 
        else:
            try:
                self._z = int(value) % self.cube.data.shape[2]
            except:
                raise ValueError("Invalid z value given")
        self.needs_redraw = True # We have changed the current coordinates, so will need to redraw
        if self.scale.get_value() != self._z:
            self.scale.set_value(self._z)

    def _update_velocity(self, scale_widget):
        self.z = int(scale_widget.get_value())
        self.toolbar.update_mouseout_message() # the current velocity shown in the status message must be updated.
    
    def _figure_mousedown(self, event):
        if event.xdata != None and event.ydata != None: # If we're in the canvas:
            self.x = int(event.xdata)
            self.y = int(event.ydata)
            self._is_mouse_down = True
    def _figure_mouseup(self, event):
        if event.xdata != None and event.ydata != None: # If we're in the canvas:
            self.x = int(event.xdata)
            self.y = int(event.ydata)
            for func in self.click_notify:
                func((self._x, self._y, self._z), self.cube.data[self._x, self._y, self._z])
        self._is_mouse_down = False
    def _figure_mousemoved(self, event):
        if self._is_mouse_down and (event.xdata != None and event.ydata != None): # If we're in the canvas:
            self.x = int(event.xdata)
            self.y = int(event.ydata)
        # Note other mouse motion updates get processed below in _NavigationToolbar.mouse_move
    
    def on_click(self, func):
        if not func in self.click_notify:
            self.click_notify += [func] 

    def _check_redraw(self):
        ''' Update this widget's display if needed. Called only when the main event loop is idle '''
        if self.needs_redraw:
            if self._x != self.last_drawn_x:
                self.xline.set_xdata([self._x, self._x])
                self.last_drawn_x = self._x
            if self._y != self.last_drawn_y:
                self.yline.set_ydata([self._y, self._y])
                self.last_drawn_y = self._y
            if self._z != self.last_drawn_z: # If z has changed since we last drew:
                self.imgplot.set_data(self.cube.data[:,:,self._z].transpose(1,0))
                self.last_drawn_z = self._z
            self.fig.canvas.draw()
            self.needs_redraw = False
{'fraction_non_alphanumeric': 0.1069611780455154, 'fraction_numerical': 0.00896921017402945, 'mean_word_length': 2.4394567219152856, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3670480', 'n_tokens_mistral': 4872, 'n_tokens_neox': 4671, 'n_words': 1213}