示例#1
0
class TickerChart(Gtk.Box):
    __gsignals__ = {
        'cursor-time': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
    }

    def __init__(self, interval=100, view=20, keep=None, linewidth=1):
        super().__init__()
        self.fig = Figure(dpi=72)
        self.canvas = FigureCanvas(self.fig)
        self.pack_start(self.canvas, True, True, 0)

        self.data = {}
        self.plots = {}
        self.info = {}
        self.alternates = set()
        self.active = None

        self.axes = []
        self.axes.append(self.fig.add_subplot(111))
        self.fig.subplots_adjust(left=0.12, right=0.88)

        self.axes[0].set_xlabel('seconds ago')
        self.interval = interval  # milliseconds
        self.view_range = view  # seconds

        self.keep_range = keep or (view * 4)  # seconds
        self.linewidth = linewidth

        self.keep_size = int(self.keep_range * 1000 / self.interval)
        self.view_step = view // 2
        self.deviation = 10

        self.view_time = time.time()
        self.add_data('time')
        self.paused = False
        self.show_all()

    def pause(self):
        self.paused = True

    def resume(self):
        self.paused = False

    def zoom_out(self):
        self.view_range = max(self.view_range - self.view_step, self.view_step)
        self.update()

    def zoom_in(self):
        self.view_range = min(self.view_range + self.view_step,
                              self.keep_range)
        self.update()

    def incr_margin(self):
        self.deviation = min(self.deviation + 5, 50)
        self.update()

    def decr_margin(self):
        self.deviation = max(self.deviationi - 5, 5)
        self.update()

    def add_data(self, name):
        if name in self.data:
            return
        self.data[name] = numpy.empty(self.keep_size)
        self.data[name][:] = numpy.nan

    def resize_data(self):
        for name, data in list(self.data.items()):
            self.data[name] = numpy.empty(self.keep_size)
            if self.max_samples > len(data):
                self.data[name][-len(data):] = data
            else:
                self.data[name] = data[-self.keep_size:]

    def select_active(self, name):
        if name in self.alternates:
            self.active = name
            self.axes[1].set_ylabel(name)

    def add_alternate(self, name):
        self.alternates.add(name)

    def shift_data(self):
        for name, data in list(self.data.items()):
            data[:-1] = data[1:]

    def add_plot(self,
                 name,
                 color=None,
                 linestyle='-',
                 axis=0,
                 alternate=False):
        assert axis in [0, 1], 'axis must be 0 or 1'
        if axis == 1 and len(self.axes) == 1:
            self.axes.append(self.axes[0].twinx())
        if not color:
            color = COLORS[len(self.plots)]
        self.plots[name] = Line2D([], [],
                                  color=color,
                                  linewidth=self.linewidth,
                                  linestyle=linestyle)
        self.axes[axis].add_line(self.plots[name])
        self.axes[axis].set_ylabel(name, color=color)
        self.info[name] = {
            'color': color,
            'linestyle': linestyle,
            'axis': axis
        }
        if alternate:
            self.add_alternate(name)
            self.select_active(name)
        self.add_data(name)

    def clear(self):
        for name, line in list(self.plots.items()):
            self.data[name][:] = numpy.nan

    def update(self):
        if self.paused:
            return
        selector = ~numpy.isnan(self.data['time'])
        selector[selector] = ((self.data['time'][selector] >
                               (self.view_time - self.view_range))
                              &
                              (self.data['time'][selector] <= self.view_time))

        if selector.sum() < 2:
            return
        now = self.data['time'][selector][-1]
        x_data = self.data['time'][selector] - now
        xmin, xmax = min(x_data.min(), -self.view_range), x_data.max()

        extrema = defaultdict(lambda: (numpy.nan, numpy.nan))

        for name, line in list(self.plots.items()):
            if name in self.alternates and name != self.active: continue
            axis = self.info[name]['axis']
            ymin, ymax = extrema[axis]
            y_data = self.data[name][selector]
            mn, mx = misc.get_min_max(y_data,
                                      ldev=self.deviation,
                                      rdev=self.deviation)
            ymin, ymax = numpy.nanmin([ymin, mn]), numpy.nanmax([ymax, mx])
            extrema[axis] = (ymin, ymax)
            line.set_data(x_data, y_data)

        for i, (ymin, ymax) in list(extrema.items()):
            if ymin != ymax:
                self.axes[i].set_ylim(ymin, ymax)
            if xmin != xmax:
                self.axes[i].set_xlim(xmin, xmax)

    def redraw(self):
        self.canvas.draw_idle()

    def animate(self, i):
        self.update()
        return list(self.plots.values())

    def save(self):
        dialog = Gtk.FileChooserDialog(
            "Save Chart ...", dialogs.MAIN_WINDOW, Gtk.FileChooserAction.SAVE,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE,
             Gtk.ResponseType.OK))
        dialog.set_size_request(600, 300)
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            img_filename = dialog.get_filename()
            if os.access(os.path.dirname(img_filename), os.W_OK):
                self.fig.savefig(img_filename)
        dialog.destroy()
示例#2
0
文件: plotter.py 项目: michel4j/mxdc
class Plotter(Gtk.Alignment):
    def __init__(self, loop=False, buffer_size=2500, xformat='%g', dpi=80):
        super().__init__()
        self.set(0.5, 0.5, 1, 1)

        self.format_x = FormatStrFormatter(xformat)
        self.ring_buffer = loop
        self.buffer_size = buffer_size
        self.colormap = cm.get_cmap('Dark2')
        self.axis_space = 0.92
        self.cursor_line = None
        self.cursor_points = {}
        self.plot_scales = {}
        self.lines = {}

        self.axis = {}
        self.data_type = {}
        self.values = None

        self.grid_mode = False
        self.grid_specs = {}
        self.grid_image = None
        self.grid_norm = Normalize()
        self.grid_snake = False

        self.fig = Figure(figsize=(10, 6), dpi=dpi)
        self.clear()

        self.canvas = FigureCanvas(self.fig)  # a Gtk.DrawingArea
        self.canvas.mpl_connect('motion_notify_event', self.on_mouse_motion)
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.toolbar = PlotterToolbar(self.canvas, dialogs.MAIN_WINDOW)

        box.pack_start(self.canvas, True, True, 0)
        box.pack_start(self.toolbar, False, False, 0)
        self.add(box)
        self.show_all()

    def clear(self, specs=None):
        """
        Clear the plot and configure it for the given specifications.

        :param specs: dictionary containing configuration parameters
        """
        self.fig.clear()
        self.fig.subplots_adjust(bottom=0.1,
                                 left=0.05,
                                 top=0.90,
                                 right=self.axis_space)
        specs = {} if specs is None else specs
        self.grid_mode = 'grid' in specs.get('scan_type', '')
        self.data_type = specs.get('data_type')
        self.values = misc.RecordArray(self.data_type,
                                       size=self.buffer_size,
                                       loop=self.ring_buffer)
        self.cursor_line = None
        self.lines = {}
        self.grid_snake = specs.get('grid_snake', False)
        self.grid_specs = {}
        self.grid_image = None
        self.grid_norm = Normalize()

        ax = self.fig.add_subplot(111)
        ax.yaxis.tick_right()
        ax.yaxis.set_major_formatter(ScalarFormatter())
        self.axis = {'default': ax}

        if specs:
            names = self.data_type['names'][1:]
            scales = specs.get('data_scale')
            if scales:
                self.plot_scales = {
                    ('default' if i == 0 else 'axis-{}'.format(i)): scale
                    for i, scale in enumerate(scales)
                }
            else:
                self.plot_scales = {
                    ('default' if i == 0 else 'axis-{}'.format(i)): (name, )
                    for i, name in enumerate(names)
                }

    def get_axis_for(self, name):
        """
        Return the axis for the named line

        :param name: line name
        :return: an axis object
        """
        return self.lines[name].axes

    def add_axis(self, name=None, label=""):
        """
        Add a named axis to the plot with the

        :param name: axis name
        :param label: axis label
        :return: matplotlib axis object
        """
        name = 'axis-{}'.format(len(self.axis)) if not name else name
        default = self.axis.get('default')
        index = len(self.axis) + 1
        axis_position = 1 / (self.axis_space**(index - 1))
        self.fig.subplots_adjust(right=self.axis_space**index)
        ax = self.fig.add_axes(default.get_position(),
                               sharex=default,
                               frameon=False)
        ax.spines['right'].set_position(('axes', axis_position))
        ax.yaxis.set_major_formatter(ScalarFormatter())
        ax.set_frame_on(True)
        ax.patch.set_visible(False)

        ax.yaxis.tick_right()
        ax.yaxis.set_label_position('right')
        ax.set_ylabel(label)
        for label in ax.get_xticklabels():
            label.set_visible(False)
        self.axis[name] = ax
        self.plot_scales[name] = ()
        return ax

    def add_line(self,
                 xpoints,
                 ypoints,
                 style='-',
                 name='',
                 lw=1,
                 axis="default",
                 alpha=1.0,
                 color=None,
                 redraw=True,
                 markevery=[]):
        """
        Add a named line to the plot

        :param xpoints: initial x axis values
        :param ypoints: initial y axis values
        :param style: matplotlib line style string
        :param name: line name, optional
        :param lw: line width
        :param axis: optional name of axis of add line to
        :param alpha: line transparency
        :param color: line color
        :param redraw: whether to redraw the line or note
        :param markevery: matplotlit 'markevery' parameter, set to None to show markers at every point
        """
        assert (len(xpoints) == len(ypoints))

        if axis not in self.axis:
            self.add_axis(axis)

        name = 'line-{}'.format(len(self.lines)) if not name else name
        color = self.colormap(len(self.lines)) if not color else color
        self.axis[axis].autoscale(False)
        xmin_current, xmax_current = self.axis[axis].get_xlim()
        ymin_current, ymax_current = self.axis[axis].get_ylim()
        line, = self.axis[axis].plot(xpoints,
                                     ypoints,
                                     '.',
                                     ls=style,
                                     lw=lw,
                                     markersize=8,
                                     label=name,
                                     alpha=alpha,
                                     markevery=markevery,
                                     color=color)

        # adjust axes limits as necessary
        xmin, xmax = misc.get_min_max(xpoints, ldev=0, rdev=0)
        ymin, ymax = misc.get_min_max(ypoints, ldev=1, rdev=1)

        xmin, xmax = min(xmin, xmin_current), max(xmax, xmax_current)
        ymin, ymax = min(ymin, ymin_current), max(ymax, ymax_current)
        line.axes.set_xlim(xmin, xmax)
        line.axes.set_ylim(ymin, ymax)
        self.lines[name] = line
        if name not in self.plot_scales[axis]:
            self.plot_scales[axis] += (name, )

        if len(xpoints) > 1:
            self.values.add_func(name, xpoints, ypoints)

        if redraw:
            self.redraw()

    def add_point(self, row, redraw=True):
        """
        Add a row of scan points to the data table

        :param row: sequence of values to add
        :param redraw: Whether to redraw the plot
        """

        if numpy.nan in row:
            return

        self.values.append(row)
        x_name = self.data_type['names'][0]
        if self.grid_mode:
            # no lines for grid mode
            self.update_grid_data()
        elif not self.lines:
            count = 0
            for axis, lines in self.plot_scales.items():
                if axis != 'default':
                    self.add_axis(name=axis)
                for line in lines:
                    self.add_line(self.values.data[x_name],
                                  self.values.data[line],
                                  color=self.colormap(count),
                                  name=line,
                                  axis=axis,
                                  markevery=[-1])
                    count += 1
        else:
            xmin, xmax = misc.get_min_max(self.values.data[x_name],
                                          ldev=0,
                                          rdev=0)
            for axis, lines in self.plot_scales.items():
                ymin = ymax = None
                ax = None
                for name in lines:
                    line = self.lines[name]
                    line.set_data(self.values.data[x_name],
                                  self.values.data[name])
                    ax = line.axes
                    ylo, yhi = misc.get_min_max(self.values.data[name],
                                                ldev=0.5,
                                                rdev=0.5)
                    if ymin is None:
                        ymin, ymax = ylo, yhi
                    else:
                        ymin, ymax = min(ymin, ylo), max(ymax, yhi)
                        ymin, ymax = ymin, ymax

                # adjust axes limits as necessary
                if ax is not None:
                    offset = (ymax - ymin) * .1
                    ax.set_ylim(ymin - offset, ymax + offset)
                    ax.set_xlim(xmin, xmax)

            if len(self.lines) > 1:
                default = self.axis.get('default')
                xmin_current, xmax_current = default.get_xlim()
                default.set_xlim(min(xmin, xmin_current),
                                 max(xmax, xmax_current))

        if redraw:
            self.redraw()

    def new_row(self, index):
        """
        Prepare for A new row of data
        :param index: row index for next row
        """

        if self.grid_mode and index > 1:
            # for slew grid scans, data needs to be padded/truncated
            y_name = self.data_type['names'][1]
            yo = self.values.data[y_name]

            x_size = (yo == yo[0]).sum()
            y_size = index
            pad = x_size * y_size - yo.shape[0]
            if pad == 0:
                return
            elif pad > 0:
                for i in range(pad):
                    self.values.append(self.values.data[-1])  # padding
            elif pad < 0:
                self.values.length = x_size * y_size
            self.update_grid_data()

    def update_grid_data(self):
        """
        Update the grid image values
        """
        x_name, y_name, counts_name = self.data_type['names'][:3]
        xo = self.values.data[x_name]
        yo = self.values.data[y_name]
        counts = self.values.data[counts_name]

        x_min, x_max = xo.min(), xo.max()
        y_min, y_max = yo.min(), yo.max()

        self.grid_norm.autoscale(counts)

        xsize = (yo == yo[0]).sum()
        ysize = int(numpy.ceil(yo.shape[0] / xsize))

        # pad unfilled values with nan
        blanks = xsize * ysize - counts.shape[0]
        if blanks:
            counts = numpy.pad(counts, (0, blanks),
                               'constant',
                               constant_values=(numpy.nan, numpy.nan))

        count_data = numpy.resize(counts, (ysize, xsize))

        # flip alternate rows
        if self.grid_snake:
            count_data[1::2, :] = count_data[1::2, ::-1]

        self.grid_specs.update({
            'x': xo,
            'y': yo,
            'counts': count_data,
        })
        extent = [
            x_min,
            x_max,
            y_min,
            y_max,
        ]
        if self.grid_image is None:
            default = self.axis.get('default')
            self.grid_image = default.imshow(
                self.grid_specs['counts'],
                cmap=cm.get_cmap(GRID_COLORMAP),
                origin='lower',
                norm=self.grid_norm,
                extent=extent,
                aspect='auto',
                interpolation=GRID_INTERPOLATION,
            )
        else:
            self.grid_image.set_data(self.grid_specs['counts'])
            self.grid_image.set_extent(extent)

        # set axis limits
        self.grid_image.axes.set_xlim(extent[:2])
        self.grid_image.axes.set_ylim(extent[-2:])
        self.redraw()

    def get_records(self):
        """
        Return the data array manager for the plot
        """
        return self.values

    def set_labels(self, title="", x_label="", y1_label=""):
        default = self.axis.get('default')
        default.set_xlabel(x_label, ha='right', va='top')
        default.set_ylabel(y1_label)
        default.xaxis.set_label_coords(1.0, -0.075)

    def set_time_labels(self, labels, fmt, maj_int, min_int):
        default = self.axis.get('default')
        default.xaxis.set_major_locator(MinuteLocator(interval=maj_int))
        default.xaxis.set_minor_locator(SecondLocator(interval=min_int))
        if len(default.xaxis.get_major_ticks()) < len(labels):
            labels.pop(0)
        default.set_xticklabels(
            [d != ' ' and d.strftime(fmt) or '' for d in labels])

    def redraw(self):
        if not self.grid_mode:
            lines = list(self.lines.values())
            labels = list(self.lines.keys())
            self.axis['default'].legend(lines,
                                        labels,
                                        loc='upper left',
                                        bbox_to_anchor=(0, 1.075),
                                        ncol=8,
                                        fancybox=False,
                                        framealpha=0.0,
                                        edgecolor='inherit',
                                        borderaxespad=0,
                                        fontsize=9)
        self.canvas.draw_idle()

    def on_mouse_motion(self, event):
        default = self.axis.get('default')

        if event.inaxes and self.lines and not self.grid_mode:
            x, y = event.xdata, event.ydata

            if self.cursor_line is None:
                self.cursor_line = default.axvline(x,
                                                   lw=1,
                                                   color='#3a7ca8',
                                                   antialiased=None)
                for axis, lines in self.plot_scales.items():
                    for name in lines:
                        y_value = self.values(name, x)
                        ax = self.axis[axis]
                        if name in self.lines:
                            line = self.lines[name]
                            trans = transforms.blended_transform_factory(
                                ax.get_yticklabels()[0].get_transform(),
                                ax.transData)
                            self.cursor_points[name] = ax.text(
                                1,
                                y_value,
                                "< {}".format(name),
                                color=line.get_color(),
                                transform=trans,
                                ha="left",
                                va="center")
            else:
                self.cursor_line.set_xdata(x)
                for axis, lines in self.plot_scales.items():
                    for name in lines:
                        if name in self.lines:
                            y_value = self.values(name, x)
                            if name in self.cursor_points:
                                self.cursor_points[name].set_position(
                                    (1, y_value))
            self.canvas.draw_idle()
        else:
            if self.cursor_line:
                self.cursor_line.remove()
                self.cursor_line = None
                for name in list(self.cursor_points.keys()):
                    mark = self.cursor_points.pop(name)
                    mark.remove()
                self.canvas.draw_idle()
示例#3
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()
示例#4
0
class Calculator():

    def __init__(self):
        self.history_ex = ["","","","","","","","","",""]
        self.hisotry_page = [0,0,0,0,0,0,0,0,0,0]
        self.x = []
        self.y = []
        self.init_gui()

    def init_gui(self):
        self.builder = Gtk.Builder()
        self.builder.add_from_file("finally.glade")

        self.builder.connect_signals( 
                                    {
                                      "window_destroy"  : self.window_destroy,
                                      "press_button"    : self.press_button,
                                      "clear_text"      : self.clear_text,
                                      "remove_last_char": self.remove_last_char,
                                      "calculate"       : self.calculate,
                                      "switch_page"     : self.switch_page,
                                      "num_base_change" : self.num_base_change,
                                      "fix_cursor"      : self.fix_cursor,
                                      "enable_textview" : self.enable_textview,
                                      "disable_textview": self.disable_textview,
                                      "prog_calc"       : self.prog_calc,
                                      "back_to_future"  : self.back_to_future,
                                      "plot"            : self.plot
                                    }
                                    )

        self.window = self.builder.get_object("main_window")
        self.window.set_size_request(250,305)
        self.window.set_title("The Calculator")
        self.window.set_icon_from_file("/usr/share/pixmaps/thecalculator-icon.png")
        self.text_buff = self.builder.get_object("class_17").get_buffer()  # Access buffer from TextView
        self.builder.get_object("class_17").grab_focus()
        self.builder.get_object("radiobutton3").set_active(True)
        self.num_base_change(self.builder.get_object("radiobutton3"))

        ############### PLOT FUNCTION ##############

        sw = self.builder.get_object("scrolledwindow1")
        sw2 = self.builder.get_object("scrolledwindow2")

        fig = Figure(figsize=(5,5),dpi=120)
        self.ax = fig.add_subplot(111)

        self.x = arange(-3.1415,3.1415,0.01)
        self.y = sin(self.x)

        self.ax.plot(self.x,self.y,label="sin(x)")
        self.ax.set_xlim(-3.2,3.2)
        self.ax.set_ylim(-1.2,1.2)
        self.ax.grid(True)

        fig.set_size_inches(9.5, 5.5, forward=True)

        fig.tight_layout()

        self.canvas = FigureCanvas(fig)
        sw.add_with_viewport(self.canvas)
        self.canvas.show()

        toolbar = NavigationToolbar(self.canvas, self.window)
        sw2.add_with_viewport(toolbar)

    def plot(self,w,data=None,from_x=-10,to_x=10):
        text = self.builder.get_object("plot_1").get_text()
        y_lim = []
        # ([+-]?\d*\.\d+|[+-]?\d+)
        # ([+-]?\d{1,})\)
        c = search("x_lim=\(([+-]?\d*\.\d+|[+-]?\d+),([+-]?\d*\.\d+|[+-]?\d+)",text)
        if (c):
            num1 = float(c.group(1))
            num2 = float(c.group(2))
            print(num1),print(type(num1))
            print(num2),print(type(num2))
            if ( num1 < num2 ):
                print("fees")
                from_x=num1
                to_x=num2
            else:
                from_x=num2
                to_x=num1

        c = search("y_lim=\(([+-]?\d*\.\d+|[+-]?\d+),([+-]?\d*\.\d+|[+-]?\d+)",text)
        if (c):
            num1 = float(c.group(1))
            num2 = float(c.group(2))
            if ( num1 < num2 ):
                y_lim.append(num1)
                y_lim.append(num2)
            else:
                y_lim.append(num2)
                y_lim.append(num1)
        
        self.x = []
        self.y = []
        self.x = arange(from_x,to_x,0.01)

        for x in self.x:
            try:
                string = text.split(",")[0].replace("x",str("(%f)"%(x)))
                tmp_number = solve_expresion(string)

                if ( type(tmp_number) != str ):
                    self.y.append(tmp_number)
                else:
                    self.y.append(None)
                    continue
            except TypeError:
                self.y.append(None)
            except Exception as e:
                print(e)
                self.builder.get_object("plot_1").set_text("Invalid synatax, use variable x")
        self.y = array(self.y)
        print(len(self.x))
        print(len(self.y))
        self.ax.plot(self.x,self.y)

        self.ax.set_xlim(from_x,to_x)
        if ( len(y_lim) == 2 ):
            print("ase")
            self.ax.set_ylim(y_lim[0],y_lim[1])
        self.ax.grid(True)
        self.canvas.draw_idle()
        # c == None podminka
        # c = re.match("(from=[-+]?(\d{1,}) to=[-+]?(\d{1,}))|(to=[-+]?(\d{1,}) from=[-+]?(\d{1,}))"
        #group(0) = founded string
        #group(1) = fouund first variante
        #group(2) = number from
        #group(3) = number to
        #group(4) = found second variante
        #group(5) = number to
        #group(6) = number from






    def back_to_future(self,w,data=None):
        if ( w.get_label() != "" ):
            self.text_buff.set_text(w.get_label())
            self.builder.get_object("notebook1").set_current_page(self.hisotry_page[self.history_ex.index(w.get_label())])  

    def add_to_history(self,expresion,page):
        if ( expresion in self.history_ex):
            return
        if ( len(self.history_ex) == 10 ):
            del(self.history_ex[0])
            del(self.hisotry_page[0])
            self.history_ex.append(expresion)
            self.hisotry_page.append(page)
        else:
            self.history_ex.append(expresion)
            self.hisotry_page.append(page)

        o = 9
        for i in self.history_ex:
            self.builder.get_object("history_"+str(o)).set_text(i)
            o-=1

    def solve_prog(self,base_string):
        try:
            base_dictionary = {"BIN":2,"OCT":8,"DEC":10,"HEX":16}

            base_string=base_string.replace("xor","^")

            for i in base_string:
                if i not in ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","~","^","&","|","+","-","/","*"," ",]:
                    return "Invalid input syntax"

            insert_base = base_dictionary[self.get_radio_state("in")]
            output_base = base_dictionary[self.get_radio_state("out")]

            base_string = prog_transfer(base_string,insert_base)
        
            output = eval(base_string, {"__builtins__":None})
        
            if output_base == insert_base:
                output = output
            elif output_base == 2:
                output = bin(int(str(output)))[2:]
            elif output_base == 8:
                output = oct(int(str(output)))[2:]
            elif output_base == 10:
                output = int(str(output))
            elif output_base == 16:
                output = hex(int(str(output)))[2:]

            return str(output).upper()

        except SyntaxError:
            return "Invalid input syntax"
        except ValueError:
            return "Invalid base"
        except:
            return "Some Error"

    def prog_calc(self,w,d=None):
        text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False)

        if ( text == "" ):
            return

        text = text.splitlines()

        self.text_buff.set_text( text[0] + '\n' + str( self.solve_prog(text[0]) ) )
        self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text[0])) )

    def enable_textview(self,w,e):
        page = self.builder.get_object("notebook1").get_current_page()
        textview = ("class_17", "science_30", "prog_28")

        if ( page in [0,1,2] ):
            if ( e.keyval == Gdk.KEY_Return or e.keyval == Gdk.KEY_KP_Enter):
                self.builder.get_object( textview[page] ).set_editable(True)
                if ( page == 2 ):
                    self.prog_calc(w)
                else:
                    self.calculate(w)

    def disable_textview(self,w,e):
        # tuple with name of all textview
        page = self.builder.get_object("notebook1").get_current_page()
        textview = ("class_17", "science_30", "prog_28")

        if ( page in [0,1,2] ):
            if ( e.keyval == Gdk.KEY_Return or e.keyval == Gdk.KEY_KP_Enter):
                self.builder.get_object(textview[page]).set_editable(False)

    def fix_cursor(self,widget,data=None):
        text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False)

        if ('\n' in text):
            if ( len(text.splitlines()) > 2):
                text = text.replace("\n","",1)
                self.text_buff.set_text(text)
            text = text.splitlines()[0]
        if ( self.text_buff.props.cursor_position > len(text) ):
            self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text)) )

    def calculate(self,widget,data=None):
        # except for plot
        text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False)

        if ( text == "" ):
            return

        text = text.splitlines()

        self.text_buff.set_text( text[0] + '\n' + str( solve_expresion(text[0]) ) )
        self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text[0])) )
        self.add_to_history(text[0],self.builder.get_object("notebook1").get_current_page())

    def num_base_change(self,widget,data=None):
        if ( widget.get_active() == True):
            if ( widget.get_label() == "BIN" ):
                base=2
            elif ( widget.get_label() == "OCT" ):
                base=8
            elif ( widget.get_label() == "DEC" ):
                base=10
            elif (widget.get_label() == "HEX" ):
                base=16

            for i in range(16):
                if ( i < base ):
                    self.builder.get_object("prog_"+str(i)).set_sensitive(True)
                else:
                    self.builder.get_object("prog_"+str(i)).set_sensitive(False)


    def get_radio_state(self,group):
        if ( group == "in" ):
            g = self.builder.get_object("radiobutton1").get_group()
        elif ( group == "out" ):
            g = self.builder.get_object("radiobutton5").get_group()

        for i in g:
            if ( i.get_active() ):
                return i.get_label()

    def clear_text(self,widget,data=None):
        self.text_buff.set_text("")

    def window_destroy(self,widget,data=None):
        Gtk.main_quit()

    def remove_last_char(self,widget,data=None):
        text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False)
        l_text = list(text)
        cursor_pos = self.text_buff.props.cursor_position-1
        del(l_text[cursor_pos])
        s = "".join(l_text)
        self.text_buff.set_text(s)
        self.text_buff.place_cursor(self.text_buff.get_iter_at_line_offset(0,cursor_pos))

    def show_tab(self,name,number_of_item):
        for i in range(number_of_item+1):
            self.builder.get_object(name+"_"+str(i)).show()
    def hide_tab(self,name,number_of_item):
        for i in range(number_of_item+1):
            self.builder.get_object(name+"_"+str(i)).hide()

    # @TODO focus switch textview
    def switch_page(self,widget,p1,p2):
        if ( widget.get_current_page() != 4):
            self.text_buff.set_text("")

        modes = { "class" : 0, "science" : 1, "prog" : 2, "plot" : 3, "history" : 4, "authors" : 5 }
        number_of_item = (19,30,29,2,19,0)
        size = ( (300,330),(450,320),(450,320),(800,600),(480,330),(302,330) )
        for i in modes:
            if ( p2 != modes[i] and  i!="authors"):
                self.hide_tab(i,number_of_item[modes[i]])
            else:
                self.show_tab(i,number_of_item[modes[i]])

        self.window.set_size_request(size[p2][0],size[p2][1])
        self.window.resize(size[p2][0],size[p2][1])

    def press_button(self,widget,data=None):
        char = widget.get_label()
        functions = {"√":"sqrt()","x!":"!","ln":"ln()","abs":"||","sin":"sin()","cos":"cos()","tg":"tan()","cotg":"cotg()"}
        if char in ["0","1","2","3","4","5","6","7","8","9","+","-","/","*",",","e","π","%","^","&","|","xor","~","A","B","C","D","E","F"]:
            self.text_buff.insert_at_cursor(char)
        elif char in functions:
            self.text_buff.insert_at_cursor(functions[char])
            if ( char != "x! "):
                text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False)
                text = text.splitlines()
                self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text[0])-1) )

    def main(self):
        self.window.show()
        Gtk.main()
示例#5
0
class AppCanvas():
    def __init__(self):
        self.curvesCounter = 0
        self.activeCurve = None
        self.pointOfRotation = None
        self.getScaleWidget = None
        self.getAngleWidget = None

        fig = plt.figure(
            figsize=[9, 6],
            dpi=100,
        )
        self.ax = fig.add_subplot()
        #plt.axis([0,300,0,200],'scaled')
        plt.axis([0, 300, 0, 200])

        self.canvas = FigureCanvas(fig)

    def get_canvas(self):
        return self.canvas

    def get_ax(self):
        return self.ax

    def set_activeCurve(self, activeCurve):
        self.activeCurve = activeCurve

    def set_getScaleWidget(self, getScaleWidget):
        self.getScaleWidget = getScaleWidget

    def set_getAngleWidget(self, getAngleWidget):
        self.getAngleWidget = getAngleWidget

    def pick_curve(self, event):
        lineName = event.artist.get_label()
        if lineName == self.activeCurve.linePlot.get_label():
            self.drag_curve_active = self.canvas.mpl_connect(
                'motion_notify_event', self.drag_curve)
            self.drop_curve_active = self.canvas.mpl_connect(
                'button_release_event', self.drop_curve)
            self.mouseX = event.mouseevent.xdata
            self.mouseY = event.mouseevent.ydata
            #self.activeCurve.set_working_accurancy()

    def drop_curve(self, event):
        if self.drag_curve_active != None:
            if event.inaxes != None:
                self.activeCurve.move_curve(event.xdata - self.mouseX,
                                            event.ydata - self.mouseY)
            self.canvas.mpl_disconnect(self.drag_curve_active)
            self.canvas.mpl_disconnect(self.drop_curve_active)
            #self.activeCurve.set_normal_accurancy()
            self.canvas.draw_idle()
            self.drag_curve_active = None
            self.drop_curve_active = None

    def drag_curve(self, event):
        if event.inaxes != None:
            self.activeCurve.move_curve(event.xdata - self.mouseX,
                                        event.ydata - self.mouseY)
            self.mouseX = event.xdata
            self.mouseY = event.ydata
            self.canvas.draw_idle()

    def resize_curve(self, event):
        scale = self.getScaleWidget.get_scale_value()
        self.getScaleWidget.reset_scale_value()
        if self.activeCurve != None:
            self.activeCurve.resize_curve(scale / 100.0)
        self.canvas.draw_idle()

    def change_line_width(self, event):
        scale = self.getScaleWidget.get_scale_value()
        self.getScaleWidget.reset_scale_value()
        if self.activeCurve != None:
            self.activeCurve.change_line_width(scale / 100.0)
        self.canvas.draw_idle()

    def change_curve(self, event):
        scale = self.getScaleWidget.get_scale_value()
        self.getScaleWidget.reset_scale_value()
        if self.activeCurve != None:
            self.activeCurve.resize_curve(scale / 100.0)
        self.canvas.draw_idle()

    def rotate_curve(self, event):
        angle = self.getAngleWidget.get_entry_text()
        if self.pointOfRotation != None:
            s = self.pointOfRotation[0].get_xdata()[0]
            t = self.pointOfRotation[0].get_ydata()[0]
        else:
            s, t = 0, 0
        try:
            if self.activeCurve != None and int(angle) < 360 and int(
                    angle) > -360:
                angle = int(angle)
                if angle < 0:
                    angle += 360
            self.activeCurve.rotate_curve(angle, s, t)
            self.canvas.draw_idle()
        except:
            pass

    def add_point_of_rotation(self, event):
        self.delete_point_of_rotation()
        self.pointOfRotation = self.ax.plot([event.xdata], [event.ydata], 'ko')
        self.canvas.draw_idle()

    def delete_point_of_rotation(self):
        if self.pointOfRotation != None:
            self.pointOfRotation[0].remove()
            del self.pointOfRotation
            self.pointOfRotation = None
            self.canvas.draw_idle()

    def select_point(self, event):
        lineName = event.artist.get_label()
        if lineName == self.activeCurve.pointsPlot.get_label():
            self.activeCurve.activate_point(event.mouseevent.xdata,
                                            event.mouseevent.ydata)
            self.canvas.draw_idle()

    def delete_point(self, event):
        lineName = event.artist.get_label()
        if lineName == self.activeCurve.pointsPlot.get_label():
            self.activeCurve.delete_point(event.mouseevent.xdata,
                                          event.mouseevent.ydata)
            self.canvas.draw_idle()

    def add_point(self, event):
        if self.activeCurve != None and event.inaxes != None:
            self.activeCurve.add_point(event.xdata, event.ydata)
            self.canvas.draw_idle()

    def pick_point(self, event):
        self.drag_point_active = self.canvas.mpl_connect(
            'motion_notify_event', self.drag_point)
        self.activeCurve.set_working_accurancy()

    def drop_point(self, event):
        self.canvas.mpl_disconnect(self.drag_point_active)
        if self.activeCurve != None:
            self.activeCurve.disactivate_point()
            self.activeCurve.set_normal_accurancy()
            self.canvas.draw_idle()

    def drag_point(self, event):
        if event.inaxes != None:
            self.activeCurve.move_point(event.xdata, event.ydata)
            self.canvas.draw_idle()
示例#6
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()