示例#1
0
class CanvasItem(object):
    """Canvas Item
    Has figure, canvas
    """

    def __init__(self, main_controller):
        """ Initializes figure. Sets a default alpha. Initializes
        configuration dictionary. Initializes context menu. Connects
        button release event on canvas.
        """
        self.main_controller = main_controller
        self.figure = Figure()
        self.figure.patch.set_alpha(0.0)
        self.canvas = FigureCanvas(self.figure)

        self.customize_window = None
        self.config = OrderedDict()

        self._context_menu = gtk.Menu()
        self._build_context_menu()
        self.canvas.connect("button_release_event", self.on_button_release,
                            self.canvas)

    def _build_context_menu(self):
        """ Context menu has menu items for exporting to PDF
        and customizing plot.
        """
        export_pdf_item = gtk.MenuItem("Export to PDF...")
        export_pdf_item.connect("activate", self.on_export_pdf, self.canvas)
        customize_item = gtk.MenuItem("Customize...")
        customize_item.connect("activate", self.show_customize)

        self._context_menu.append(export_pdf_item)
        self._context_menu.append(customize_item)

        self._context_menu.show_all()

    def init_default_config(self):
        """ Empty abstract default config.
        """
        pass

    def get_options_dict(self):
        """ Return configuration dictionary.
        """
        return self.config

    def get_config_values(self):
        """ Gets values of all items in configuration dictionary.
        Returns values as keys in new dictionary.
        """
        return {key: configuration.value for key, configuration in
                self.config.iteritems()}

    def set_config_values(self, config):
        """ Sets current configuration values to ones from
        configuration dictionary in parameter.
        """
        if (config):
            for key, val in config.items():
                self.config[key].value = val

    def apply_config(self, revert_data=None, get_function=None):
        """ For each configuration, if revert data exists,
        revert configuration. If no revert data is provided,
        set configuration value to result from get function.
        If no get function is specified, use current value from
        configuration.

        Calls the function within the configuration with the
        revert/get function/current value.

        Calls queue_draw to update after applying configuration.
        """
        for option_name in self.get_options_dict():
            if (self.config[option_name].configurable):
                if (self.config[option_name].function):
                    function = self.config[option_name].function
                    if (revert_data):
                        new_val = revert_data[option_name]
                    elif (get_function):
                        new_val = get_function(option_name)
                    else:
                        new_val = self.config[option_name].value

                    self.config[option_name].value = new_val
                    function(new_val)

        self.canvas.queue_draw()

    @property
    def title(self):
        """ Abstract title getter.
        """
        raise NotImplementedError

    def on_export_pdf(self, widget, canvas):
        """ Calls controller's export pdf function with plot title.
        """
        self.main_controller.on_export_pdf(None, canvas, self.title)

    def on_button_release(self, widget, event, canvas):
        """ TODO: is this needed for some behavoiur?
        ie. the disappearing context menu.
        """
        if event.button == settings.EVENT_BUTTON_RIGHT_CLICK:
            self._context_menu.popup(None, None, None, None, event.button,
                                     event.time)
            return True
        return False

    def show_customize(self, event):
        """ If plot has customize window already, show that menu.
        Else, instantiate new customize window and show it.
        """
        if (self.customize_window and self.customize_window.not_destroyed):
            self.customize_window.window.show()
        else:
            self.customize_window = CustomizeWindow(self)
示例#2
0
class ConanReportGraphsPresenter(Presenter[Gtk.Stack]):
    spinner: TemplateChild[Gtk.Spinner] = TemplateChild('spinner')
    figure_container = TemplateChild('figure_container')  # type: TemplateChild[Gtk.Container]

    _analyses = ()

    @inject
    def __init__(self, graphs_service: ConanReportGraphsService) -> None:
        self.graphs_service = graphs_service

    def after_view_init(self) -> None:
        self.figure = Figure(tight_layout=False)

        self.figure_canvas = FigureCanvas(self.figure)
        self.figure_canvas.props.hexpand = True
        self.figure_canvas.props.vexpand = True
        self.figure_canvas.props.visible = True
        self.figure_container.add(self.figure_canvas)

        self.figure_canvas_mapped = False

        self.figure_canvas.connect('map', self.canvas_map)
        self.figure_canvas.connect('unmap', self.canvas_unmap)
        self.figure_canvas.connect('size-allocate', self.canvas_size_allocate)

        left_ca_ax, right_ca_ax = self.figure.subplots(2, 1, sharex='col')

        left_ca_ax.set_ylabel('Left CA [°]')
        left_ca_ax.tick_params(axis='x', direction='inout')

        right_ca_ax.xaxis.set_ticks_position('both')
        right_ca_ax.set_ylabel('Right CA [°]')
        right_ca_ax.tick_params(axis='x', direction='inout')

        left_ca_ax.tick_params(axis='y', left=False, labelleft=False, right=True, labelright=True)
        right_ca_ax.tick_params(axis='y', left=False, labelleft=False, right=True, labelright=True)

        # Format the labels to scale to the right units.
        left_ca_ax.get_yaxis().set_major_formatter(
            FuncFormatter(lambda x, _: '{:.4g}'.format(math.degrees(x)))
        )
        right_ca_ax.get_yaxis().set_major_formatter(
            FuncFormatter(lambda x, _: '{:.4g}'.format(math.degrees(x)))
        )

        left_ca_ax.grid(axis='x', linestyle='--', color="#dddddd")
        left_ca_ax.grid(axis='y', linestyle='-', color="#dddddd")

        right_ca_ax.grid(axis='x', linestyle='--', color="#dddddd")
        right_ca_ax.grid(axis='y', linestyle='-', color="#dddddd")

        self._left_ca_ax = left_ca_ax
        self._left_ca_line = left_ca_ax.plot([], marker='o', color='#0080ff')[0]
        self._right_ca_line = right_ca_ax.plot([], marker='o', color='#ff8000')[0]

        self.graphs_service.connect('notify::left-angle', self.data_changed)
        self.graphs_service.connect('notify::right-angle', self.data_changed)

        self.data_changed()

    def canvas_map(self, *_) -> None:
        self.figure_canvas_mapped = True
        self.figure_canvas.draw_idle()

    def canvas_unmap(self, *_) -> None:
        self.figure_canvas_mapped = False

    def canvas_size_allocate(self, *_) -> None:
        self.figure.tight_layout(pad=2.0, h_pad=0)
        self.figure.subplots_adjust(hspace=0)

    @install
    @GObject.Property
    def analyses(self) -> Sequence[ConanAnalysisJob]:
        return self._analyses

    @analyses.setter
    def analyses(self, analyses: Iterable[ConanAnalysisJob]) -> None:
        self._analyses = tuple(analyses)
        self.graphs_service.analyses = analyses

    def data_changed(self, *_) -> None:
        left_angle_data = self.graphs_service.left_angle
        right_angle_data = self.graphs_service.right_angle

        if left_angle_data.shape[1] <= 1 or right_angle_data.shape[1] <=1:
            self.show_waiting_placeholder()
            return

        self.hide_waiting_placeholder()

        self._left_ca_line.set_data(left_angle_data)
        self._right_ca_line.set_data(right_angle_data)

        self.update_xlim()

        self._left_ca_line.axes.relim()
        self._left_ca_line.axes.margins(y=0.1)
        self._right_ca_line.axes.relim()
        self._right_ca_line.axes.margins(y=0.1)

        self.figure_canvas.draw()

    def update_xlim(self) -> None:
        all_xdata = (
            *self._left_ca_line.get_xdata(),
            *self._right_ca_line.get_xdata(),
        )

        if len(all_xdata) <= 1:
            return

        xmin = min(all_xdata)
        xmax = max(all_xdata)

        if xmin == xmax:
            return

        self._left_ca_ax.set_xlim(xmin, xmax)

    def show_waiting_placeholder(self) -> None:
        self.host.set_visible_child(self.spinner)
        self.spinner.start()

    def hide_waiting_placeholder(self) -> None:
        self.host.set_visible_child(self.figure_container)
        self.spinner.stop()
示例#3
0
class CanvasItem(object):
    """Canvas Item
    Has figure, canvas
    """
    def __init__(self, main_controller):
        """ Initializes figure. Sets a default alpha. Initializes
        configuration dictionary. Initializes context menu. Connects
        button release event on canvas.
        """
        self.main_controller = main_controller
        self.figure = Figure()
        self.figure.patch.set_alpha(0.0)
        self.canvas = FigureCanvas(self.figure)

        self.customize_window = None
        self.config = OrderedDict()

        self._context_menu = gtk.Menu()
        self._build_context_menu()
        self.canvas.connect("button_release_event", self.on_button_release,
                            self.canvas)

    def _build_context_menu(self):
        """ Context menu has menu items for exporting to PDF
        and customizing plot.
        """
        export_pdf_item = gtk.MenuItem("Export to PDF...")
        export_pdf_item.connect("activate", self.on_export_pdf, self.canvas)
        customize_item = gtk.MenuItem("Customize...")
        customize_item.connect("activate", self.show_customize)

        self._context_menu.append(export_pdf_item)
        self._context_menu.append(customize_item)

        self._context_menu.show_all()

    def init_default_config(self):
        """ Empty abstract default config.
        """
        pass

    def get_options_dict(self):
        """ Return configuration dictionary.
        """
        return self.config

    def get_config_values(self):
        """ Gets values of all items in configuration dictionary.
        Returns values as keys in new dictionary.
        """
        return {
            key: configuration.value
            for key, configuration in self.config.iteritems()
        }

    def set_config_values(self, config):
        """ Sets current configuration values to ones from
        configuration dictionary in parameter.
        """
        if (config):
            for key, val in config.items():
                self.config[key].value = val

    def apply_config(self, revert_data=None, get_function=None):
        """ For each configuration, if revert data exists,
        revert configuration. If no revert data is provided,
        set configuration value to result from get function.
        If no get function is specified, use current value from
        configuration.

        Calls the function within the configuration with the
        revert/get function/current value.

        Calls queue_draw to update after applying configuration.
        """
        for option_name in self.get_options_dict():
            if (self.config[option_name].configurable):
                if (self.config[option_name].function):
                    function = self.config[option_name].function
                    if (revert_data):
                        new_val = revert_data[option_name]
                    elif (get_function):
                        new_val = get_function(option_name)
                    else:
                        new_val = self.config[option_name].value

                    self.config[option_name].value = new_val
                    function(new_val)

        self.canvas.queue_draw()

    @property
    def title(self):
        """ Abstract title getter.
        """
        raise NotImplementedError

    def on_export_pdf(self, widget, canvas):
        """ Calls controller's export pdf function with plot title.
        """
        self.main_controller.on_export_pdf(None, canvas, self.title)

    def on_button_release(self, widget, event, canvas):
        """ TODO: is this needed for some behavoiur?
        ie. the disappearing context menu.
        """
        if event.button == settings.EVENT_BUTTON_RIGHT_CLICK:
            self._context_menu.popup(None, None, None, None, event.button,
                                     event.time)
            return True
        return False

    def show_customize(self, event):
        """ If plot has customize window already, show that menu.
        Else, instantiate new customize window and show it.
        """
        if (self.customize_window and self.customize_window.not_destroyed):
            self.customize_window.window.show()
        else:
            self.customize_window = CustomizeWindow(self)
示例#4
0
class IFTReportGraphsPresenter(Presenter[Gtk.Stack]):
    spinner: TemplateChild[Gtk.Spinner] = TemplateChild('spinner')
    figure_container: TemplateChild[Gtk.Container] = TemplateChild(
        'figure_container')

    _analyses = ()

    @inject
    def __init__(self, graphs_service: IFTReportGraphsService) -> None:
        self.graphs_service = graphs_service

    def after_view_init(self) -> None:
        figure = Figure(tight_layout=False)
        self.figure = figure

        self.figure_canvas = FigureCanvas(figure)
        self.figure_canvas.props.hexpand = True
        self.figure_canvas.props.vexpand = True
        self.figure_canvas.props.visible = True
        self.figure_container.add(self.figure_canvas)

        self.figure_canvas_mapped = False

        self.figure_canvas.connect('map', self.hdl_canvas_map)
        self.figure_canvas.connect('unmap', self.hdl_canvas_unmap)
        self.figure_canvas.connect('size-allocate',
                                   self.hdl_canvas_size_allocate)

        self.ift_axes, volume_axes, surface_area_axes = figure.subplots(
            3, 1, sharex='col')
        self.ift_axes.set_ylabel('IFT [mN/m]')
        self.ift_axes.tick_params(axis='x', direction='inout')
        volume_axes.xaxis.set_ticks_position('both')
        volume_axes.tick_params(axis='x', direction='inout')
        volume_axes.set_ylabel('V [mm³]')
        surface_area_axes.xaxis.set_ticks_position('both')
        surface_area_axes.tick_params(axis='x', direction='inout')
        surface_area_axes.set_ylabel('SA [mm²]')

        self.ift_axes.tick_params(axis='y',
                                  left=False,
                                  labelleft=False,
                                  right=True,
                                  labelright=True)
        volume_axes.tick_params(axis='y',
                                left=False,
                                labelleft=False,
                                right=True,
                                labelright=True)
        surface_area_axes.tick_params(axis='y',
                                      left=False,
                                      labelleft=False,
                                      right=True,
                                      labelright=True)

        self.ift_axes.grid(axis='x', linestyle='--', color="#dddddd")
        volume_axes.grid(axis='x', linestyle='--', color="#dddddd")
        surface_area_axes.grid(axis='x', linestyle='--', color="#dddddd")

        self.ift_axes.grid(axis='y', linestyle='-', color="#dddddd")
        volume_axes.grid(axis='y', linestyle='-', color="#dddddd")
        surface_area_axes.grid(axis='y', linestyle='-', color="#dddddd")

        # Format the labels to scale to the right units.
        self.ift_axes.get_yaxis().set_major_formatter(
            ticker.FuncFormatter(lambda x, pos: '{:.4g}'.format(x * 1e3)))
        volume_axes.get_yaxis().set_major_formatter(
            ticker.FuncFormatter(lambda x, pos: '{:.4g}'.format(x * 1e9)))
        surface_area_axes.get_yaxis().set_major_formatter(
            ticker.FuncFormatter(lambda x, pos: '{:.4g}'.format(x * 1e6)))

        self.ift_line = self.ift_axes.plot([], marker='o', color='red')[0]
        self.volume_line = volume_axes.plot([], marker='o', color='blue')[0]
        self.surface_area_line = surface_area_axes.plot([],
                                                        marker='o',
                                                        color='green')[0]

        self.graphs_service.connect('notify::ift', self.hdl_model_data_changed)
        self.graphs_service.connect('notify::volume',
                                    self.hdl_model_data_changed)
        self.graphs_service.connect('notify::surface-area',
                                    self.hdl_model_data_changed)

        self.hdl_model_data_changed()

    def hdl_canvas_map(self, *_) -> None:
        self.figure_canvas_mapped = True
        self.figure_canvas.draw_idle()

    def hdl_canvas_unmap(self, *_) -> None:
        self.figure_canvas_mapped = False

    def hdl_canvas_size_allocate(self, *_) -> None:
        self.figure.tight_layout(pad=2.0, h_pad=0)
        self.figure.subplots_adjust(hspace=0)

    @install
    @GObject.Property
    def analyses(self) -> Sequence[PendantAnalysisJob]:
        return self._analyses

    @analyses.setter
    def analyses(self, analyses: Iterable[PendantAnalysisJob]) -> None:
        self._analyses = tuple(analyses)
        self.graphs_service.set_analyses(analyses)

    def hdl_model_data_changed(self, *args) -> None:
        ift_data = self.graphs_service.ift
        volume_data = self.graphs_service.volume
        surface_area_data = self.graphs_service.surface_area

        if (len(ift_data[0]) <= 1 and len(volume_data[0]) <= 1
                and len(surface_area_data[0]) <= 1):
            self.show_waiting_placeholder()
            return

        self.hide_waiting_placeholder()

        self.set_ift_data(ift_data)
        self.set_volume_data(volume_data)
        self.set_surface_area_data(surface_area_data)

        if self.figure_canvas_mapped:
            self.figure.tight_layout(pad=2.0, h_pad=0)
            self.figure.subplots_adjust(hspace=0)

        self.figure_canvas.draw_idle()

    def show_waiting_placeholder(self) -> None:
        self.host.set_visible_child(self.spinner)
        self.spinner.start()

    def hide_waiting_placeholder(self) -> None:
        self.host.set_visible_child(self.figure_container)
        self.spinner.stop()

    def set_ift_data(self, data: Sequence[Tuple[float, float]]) -> None:
        if len(data[0]) <= 1:
            return

        self.ift_line.set_data(data)

        self.update_xlim()

        self.ift_axes.relim()
        self.ift_axes.margins(y=0.1)

    def set_volume_data(self, data: Sequence[Tuple[float, float]]) -> None:
        if len(data[0]) <= 1:
            return

        self.volume_line.set_data(data)

        self.update_xlim()

        self.volume_line.axes.relim()
        self.volume_line.axes.margins(y=0.1)

    def set_surface_area_data(self, data: Sequence[Tuple[float,
                                                         float]]) -> None:
        if len(data[0]) <= 1:
            return

        self.surface_area_line.set_data(data)

        self.update_xlim()

        self.surface_area_line.axes.relim()
        self.surface_area_line.axes.margins(y=0.1)

    def update_xlim(self) -> None:
        all_xdata = (
            *self.ift_line.get_xdata(),
            *self.volume_line.get_xdata(),
            *self.surface_area_line.get_xdata(),
        )

        if len(all_xdata) <= 1:
            return

        xmin = min(all_xdata)
        xmax = max(all_xdata)

        if xmin == xmax:
            return

        self.ift_axes.set_xlim(xmin, xmax)
class App:

    def __init__(self):
    
        self.xsize, self.ysize = 600, 600
        
        self.xmin, self.xmax = -1.5, 1.5
        self.ymin, self.ymax = -1.5, 1.5
        
        self.x, self.y = (-0.4, 0.6)
    
        self.n     = 2
        self.zmax  = 4.0
        self.niter = 256
        
        self.dpi  = 100
        self.cmap = 'Set3'
        
        self.digits = 12
        
        self.entries = {}
        
        # create app interface
        self.setup_interface()
        
        self.display_image()
        
        
    def setup_interface(self):
    
        # create main window
        self.main_window = Gtk.Window(title="Julia Fractals")
        self.main_window.set_border_width(10)
        
        self.main_window.connect("delete-event", Gtk.main_quit)
        
        
        # setup header bar
        self.setup_header_bar()
        
        box = Gtk.Box(orientation='horizontal', spacing=10)
        self.main_window.add(box)
        
        sep = Gtk.Separator(orientation='vertical')
        box.add(sep)
               
        # setup left panel -- container with image parameters
        self.left_box = Gtk.Box(orientation='vertical', spacing=10)
        self.setup_left_box()
        box.add(self.left_box)
        
        sep = Gtk.Separator(orientation='vertical')
        box.add(sep)
        
        # setup right panel -- container with image parameters
        self.right_box = Gtk.Box(orientation='vertical', spacing=10)

        self.setup_right_box()
        box.add(self.right_box)
        
        for name, entry in self.entries.items():
            # copy current values to the defaults
            setattr(self, name + "_default", getattr(self, name))
            self.set_entry_value(entry)
            
        
        sep = Gtk.Separator(orientation='vertical')
        box.add(sep)
        
        # setup image panel -- container with image output
        self.image_box = Gtk.Box(orientation='vertical')
        self.setup_image_box()
        box.add(self.image_box)
        
    
    def setup_header_bar(self):
        '''
        '''
        self.hb = Gtk.HeaderBar()
        self.hb.set_show_close_button(True)
        self.hb.props.title = "Julia Fractal"
        self.main_window.set_titlebar(self.hb)
        
        self.button_save = Gtk.Button(label='Save')
        self.button_save.connect("clicked", self.on_button_save_clicked)
        self.hb.pack_end(self.button_save)        
        
        
    def setup_left_box(self):
        
        # box for 
        box = Gtk.Box(orientation='vertical', spacing=6)
        self.left_box.pack_start(box, False, False, 0)
        
        sep = Gtk.Separator(orientation='horizontal')
        box.add(sep)
        
        self.namecombo = Gtk.ComboBoxText()
        self.namecombo.connect("changed", self.on_namecombo_changed)
        
        for item in ['Julia', 'Mandelbrot']:
            self.namecombo.append_text(item)
        self.namecombo.set_active(0)
        
        box.pack_start(self.namecombo, True, True, 0)
        
        label = Gtk.Label("z = z**n + C", halign=Gtk.Align.START)
        box.pack_start(label, True, True, 0)
        
        label = Gtk.Label("C = x + yi", halign=Gtk.Align.START)
        box.pack_start(label, True, True, 0)
        
        names = ['n', 'x', 'y', 'zmax', 'niter']
        
        entries = {name: self.create_labeled_entry(name, box, 
                              orientation='vertical', spacing=5, xpad=8) 
                   for name in names}
        self.entries.update(entries)                   
        
        sep = Gtk.Separator(orientation='horizontal')
        box.add(sep)
        
        # apply rotation
        button_apply = Gtk.Button(label='Apply')
        button_apply.connect("clicked", self.on_button_apply_clicked)
        self.left_box.pack_start(button_apply, False, False, 0)
        
        # reset rotation
        button_reset = Gtk.Button(label='Reset')
        button_reset.connect("clicked", self.on_button_reset_clicked)
        self.left_box.pack_start(button_reset, False, False, 0)
        
        
    def setup_right_box(self):
    
        # box for 
        box = Gtk.Box(orientation='vertical', spacing=10)
        self.right_box.pack_start(box, False, False, 0)
        
        sep = Gtk.Separator(orientation='horizontal')
        box.add(sep)
        
        names = ['xmin', 'xmax', 'ymin', 'ymax', 'xsize', 'ysize']
        entries = {name: self.create_labeled_entry(name, box, 
                              orientation='horizontal') 
                   for name in names}
        self.entries.update(entries)    
        
        sep = Gtk.Separator(orientation='horizontal')
        box.add(sep)

        label = Gtk.Label("Colormap", halign=Gtk.Align.START)
        box.pack_start(label, True, True, 0)
        
        cmapstore = Gtk.ListStore(str, GdkPixbuf.Pixbuf)
        cmaps = sorted(pl.cm.datad)
        for item in cmaps:
            cmapstore.append([item, None])
            
        self.cmapcombo = Gtk.ComboBox.new_with_model(cmapstore)
        self.cmapcombo.connect("changed", self.on_cmapcombo_changed)
        self.cmapcombo.set_active(0)
        
        renderer = Gtk.CellRendererText()
        self.cmapcombo.pack_start(renderer, True)
        self.cmapcombo.add_attribute(renderer, "text", 0)

        renderer = Gtk.CellRendererPixbuf()
        self.cmapcombo.pack_start(renderer, False)
        self.cmapcombo.add_attribute(renderer, "pixbuf", 1)
        
        box.pack_start(self.cmapcombo, True, True, 0)

        
    def setup_image_box(self):
        
        xx = self.xsize / float(self.dpi)    # inches
        yy = self.ysize / float(self.dpi)    # inches
        
        # TODO: make it resizable
        self.fig = Figure(figsize=(xx, yy), dpi=self.dpi)
        self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea 
        
        # setup drawing canvas
        self.canvas.set_size_request(self.xsize, self.ysize) 
        self.canvas.connect('button-press-event'  , self.on_canvas_button_press)
        self.canvas.connect('button-release-event', self.on_canvas_button_release)
        self.canvas.connect('motion-notify-event' , self.on_canvas_motion_notify)
        self.image_box.add(self.canvas)  
        

    def create_labeled_entry(self, name, parent, orientation='horizontal', spacing=10, xpad=0):
    
        box = Gtk.Box(orientation=orientation, spacing=spacing)
        parent.pack_start(box, True, True, 0)
        
        label = Gtk.Label(label=name, halign=Gtk.Align.START, xpad=xpad)
        box.pack_start(label, True, False, 0)
    
        entry = Gtk.Entry(name=name)
        box.pack_start(entry, True, True, 0)
        
        return entry
        
        
    def set_entry_value(self, entry):
        
        name  = entry.get_name()
        value = getattr(self, name)
        
        if int(value) == value:
            value_str = "{0}".format(value)
        else:
            value_str = "{0:.{1}f}".format(value, self.digits).rstrip("0")
        
        entry.set_text(value_str)
        
        
    def get_entry_value(self, entry, dtype):
        
        text = entry.get_text()
        try:
            val = float(text)
            if dtype == int:
                val = int(val)
        except ValueError:
            val = None
        
        return val


    def on_button_save_clicked(self, widget):
        pass       
        
    
    def on_cmapcombo_changed(self, widget):     
        
        tree_iter = widget.get_active_iter()
        if tree_iter != None:
            model = widget.get_model()
            name, image = model[tree_iter]
            
            self.cmap = name
            if hasattr(self, 'fig'):
                self.display_image()
                self.canvas.draw_idle()
       
        
    def on_namecombo_changed(self, widget):
        
        text = widget.get_active_text()
        print(text)
        if text == 'Mandelbrot':
            self.x = 0
            self.y = 0
            self.set_entry_value(self.entries['x'])
            self.set_entry_value(self.entries['y'])
        
        if hasattr(self, 'fig'):
            self.update_image()
        
        
    def on_button_apply_clicked(self, widget):  
    
        self.update_image()
        
    def on_button_reset_clicked(self, widget):  
        
        for name, entry in self.entries.items():
            setattr(self, name, getattr(self, name + "_default"))
            self.set_entry_value(entry)
    
    
    
    def on_canvas_button_release(self, widget, event):
    
        mapping = {1: 0.75, 3: 1.5}
        if event.button in mapping:
        
            if (self.posx1, self.posy1) == (self.posx2, self.posy2):
                
                factor = mapping[event.button]
                xc = np.interp(event.x, [0, self.xsize-1], [self.xmin, self.xmax])
                yc = np.interp(event.y, [0, self.ysize-1], [self.ymin, self.ymax])
                
                xlen = factor * (self.xmax - self.xmin) 
                ylen = factor * (self.ymax - self.ymin)
                
                self.xmin = xc - xlen/2.0
                self.xmax = xc + xlen/2.0
                
                self.ymin = yc - ylen/2.0
                self.ymax = yc + ylen/2.0
            
            else:
            
                self.xmin = self.posx1
                self.xmax = self.posx2
                
                self.ymin = self.posy1
                self.ymax = self.posy2

            
            for entry in self.entries.values():
                self.set_entry_value(entry)
    
        self.update_image()
        
    
    def on_canvas_motion_notify(self, widget, event):
    
        self.posx2 = np.interp(event.x, [0, self.xsize-1], [self.xmin, self.xmax])
        self.posy2 = np.interp(event.y, [0, self.ysize-1], [self.ymin, self.ymax])

        if event.state & Gdk.EventMask.BUTTON_PRESS_MASK:
            
            if event.state & Gdk.ModifierType.CONTROL_MASK: 
                self.posy2 = self.posy1 + self.posx2 - self.posx1  
            
            if hasattr(self, 'rectangle1'):
                if self.rectangle1 in self.fig.gca().patches:
                    self.rectangle1.remove()
                    self.rectangle2.remove()
            
            self.rectangle1 = Rectangle((self.posx2, (self.ymax + self.ymin) - self.posy2), 
                                        (self.posx1 - self.posx2), -(self.posy1 - self.posy2), 
                                        fill=False, edgecolor='white', linewidth=0.5)
            self.rectangle2 = Rectangle((self.posx2, (self.ymax + self.ymin) - self.posy2), 
                                        (self.posx1 - self.posx2), -(self.posy1 - self.posy2), 
                                        fill=False, edgecolor='black', linestyle='dotted', linewidth=0.5)
            ax = self.fig.gca()                                   
            ax.add_patch(self.rectangle1)
            ax.add_patch(self.rectangle2)
            
            self.canvas.draw_idle()
    
        
    
    def on_canvas_button_press(self, widget, event):
    
        self.posx1 = np.interp(event.x, [0, self.xsize-1], [self.xmin, self.xmax])
        self.posy1 = np.interp(event.y, [0, self.ysize-1], [self.ymin, self.ymax]) 
        
        self.posx2 = self.posx1
        self.posy2 = self.posy1
        
        #self.posx = self.transform_x(event.x)
        #self.posy = self.transform_y(event.y)
            
    
    def run(self):
    
        self.main_window.show_all()
        Gtk.main()
        
    def transform_x(self, xval):
    
        return two_point_interp(xval, 0, self.xsize-1, self.xmin, self.xmin) 
        
    def transform_y(self, yval):
    
        return two_point_interp(yval, 0, self.ysize-1, self.ymin, self.ymin)         
        

    def compute_image(self):
    
        C    = complex(self.x, self.y) 
        xlim = (self.xmin, self.xmax)
        ylim = (self.ymin, self.ymax)
        zz = complex_grid(xlim, ylim, self.xsize, self.ysize)
        
        text = self.namecombo.get_active_text()
        if text == 'Julia':
            return fractal(zz, C, self.n, self.zmax, self.niter)         
        elif text == 'Mandelbrot':
            return fractal(C, zz, self.n, self.zmax, self.niter)    
                
    def display_image(self):
    
        # plot and save the image
        img = self.compute_image()
        
        # clear previous figure
        self.fig.clf()
        # setup plot 
        ax = Axes(self.fig, [0, 0, 1, 1]) # remove outer border  
        ax.set_axis_off()                 # disable axis
        ax.set_xlim((self.xmin, self.xmax))
        ax.set_ylim((self.ymin, self.ymax))
        ax.set_xticklabels([])
        ax.set_yticklabels([])
        ax.imshow(img, cmap=pl.get_cmap(self.cmap), interpolation='nearest', 
                  extent=[self.xmin, self.xmax, self.ymin, self.ymax],
                  origin='upper', aspect=1.0)
                  
        self.fig.add_axes(ax)

    
    def update_image(self):
    
        for name, entry in self.entries.items():
            
            dtype = int if name in ['n', 'niter', 'xsize', 'ysize'] else float
            val   = self.get_entry_value(entry, dtype)
            if val is not None:
                setattr(self, name, val)
        
        for entry in self.entries.values():
            self.set_entry_value(entry)
        
        self.display_image()
        self.canvas.draw_idle()