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)
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()
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)
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()