def __call__(self, cmd):
        """Send string to gnuplot"""

        encoded_cmd = cmd.encode( self.encoding )
        #encoded_cmd = cmd
        self.gpout.flush()
        Signals.emit(self, 'gnuplot-send-cmd', cmd=cmd)
        self.gpwrite.write(encoded_cmd + "\n")
        self.gpwrite.write("print '<--END-->'\n")
        self.gpwrite.flush()

        result = []
        while result[-1:] != ["<--END-->\n"]:
            if self.process.poll() is not None:                
                break
            result.append( self.gpout.readline() )

        if len(result) == 0:
            result = None
        elif result[-1:] == ["<--END-->\n"]:
            result = result[:-1]
            
        self.history.append( (encoded_cmd, result) )
        Signals.emit(self, 'gnuplot-finish-cmd', cmd=cmd, result=result)
        return result
def set(container, *args, **kwargs):
    undolist = kwargs.pop('undolist', UndoList())
    olditems = dict()
    changeset = []

    arglist = list(args)
    while len(arglist) > 1:
        key = arglist.pop(0)
        value = arglist.pop(0)
        olditems[key] = container.get_value(key, None)
        setattr(container, key, value)
        changeset.append(key)

    for (key, value) in kwargs.iteritems():
        olditems[key] = container.get_value(key, None)            
        setattr(container, key, value)
        changeset.append(key)

    undolist.append( UndoInfo(set, container, **olditems) )      

    if len(changeset) > 0:
        Signals.emit(container, "notify", changeset)

        # TODO: remove, it's deprecated
        Signals.emit(container, "prop-changed", changeset) 
    def new_dataset(self, key='dataset', undolist=None):
        """
        Add a new Dataset object to the Project.

        The `data` field contains a nearly empty numarray (1 row, 2
        columns, all zero).

        If no key is given, then one is created.  If the key already
        exists, then the method assures that it is unique within the
        Project.

        Returns newly created Dataset.
        """
        if undolist is None:
            undolist = self.journal
        
        key = pdict.unique_key(self.datasets, key)
        ds = Dataset()
        pdict.setitem(self.datasets, key, ds)
        ds.data = Table(nrows=1,ncols=2)
        ds.data.column(0).designation = 'X'
        ds.data.column(1).designation = 'Y'        
        Signals.emit(self, "notify::datasets")

        ui = UndoInfo(self.remove_objects, [ds], False)
        ui.describe("Create new Dataset '%s'" % key)
        undolist.append(ui)
        
        return ds
def smart_set(container, *args, **kwargs):

    """ Like 'set', but only add undo information, if the values have
    actually changed. """
    
    undolist = kwargs.pop('undolist', UndoList())
    olditems = dict()
    changed_props = []

    def do_set(key, value):
        old_value = container.get_value(key, None)
        setattr(container, key, value)
        if old_value != container.get_value(key, None):
            olditems[key] = old_value
            changed_props.append(key)
            print "Prop '%s' has changed from '%s' to '%s'." % (key, container.get_value(key, None), old_value)

    arglist = list(args)
    while len(arglist) > 1:
        do_set(arglist.pop(0), arglist.pop(0))
        
    for (key, value) in kwargs.iteritems():
        do_set(key, value)


    if len(changed_props) > 0:
        undolist.append( UndoInfo(smart_set, container, **olditems) )      
        Signals.emit(container, "prop-changed", changed_props)
    else:
        undolist.append( NullUndo() )
    def cb_interpolate(self, action):
        plugin = self.app.get_plugin('pygsl')
        pygsl = plugin.pygsl
        
        table = self.dataset.get_data()
        x, y = table[0], table[1]
        
        steps = table.nrows * 3
        start, end = x[0], x[-1]
        stepwidth = (end - start) / steps
        new_x = Numeric.arange(start=start, stop=end+stepwidth, step=stepwidth)

        new_table = Table(nrows=steps, ncols=2,
                          typecodes=[table.get_typecode(0),
                                     table.get_typecode(1)])

        sp = pygsl.spline.cspline(table.nrows)
        sp.init(x, y)

        iter = new_table.row(0)
        for xi in new_x:
            iter.set( (xi, sp.eval(xi)) )
            try:
                iter = iter.next()
            except StopIteration:
                print "Iteration stopped"
            
        # set new Dataset
        self.project.datasets.append( Dataset(key="Niklas", data=new_table) )
        Signals.emit(self.project.datasets, "changed")        
    def mouse_move(self, event):
        if not event.inaxes:
            return
        ax = event.inaxes
        minx, maxx = ax.get_xlim()
        miny, maxy = ax.get_ylim()

        x, y = event.xdata, event.ydata
        Signals.emit(self, "move", x, y)
 def request_backend(self, key, plot=None):
     matches = self.find_backends(key=key, plot=plot)
     if len(matches) > 0:
         return matches[0]
     else:
         backend = BackendRegistry[key](project=self, plot=plot)
         self.backends.append(backend)
         Signals.emit(self, "notify::backends")
         return backend
 def disconnect(self):
     """ Close the connection to the backend. """
     if self.project is not None:
         self.project.remove_backend(self)
     self.set(None,None)        
     self.connected = False
     Signals.emit(self, 'backend-closed')
     Signals.disconnect(sender=self)
     Signals.disconnect(receiver=self)
 def load_project(self, filename):
     # load new project and if it is sucessfully loaded,
     # detach the old project
     new_project = load_project(filename)
     if new_project:
         self.set_project(new_project)
         self.recent_files.insert(0, os.path.abspath(filename))
         if len(self.recent_files) > 10:
             self.recent_files.pop(-1)
         Signals.emit(self, "update-recent-files")
 def set_current_layer(self, layer):
     """
     Set the current layer.
     The layer must be either None or a Layer instance that is
     contained in self.layers.
     """
     if layer is None or layer in self.layers:
         self._current_layer = layer
         Signals.emit(self, "notify::current_layer", layer)
     else:
         raise ValueError("Layer %s can't be set as current, because it is not part of the Plot!" % layer)
    def mouse_move(self, event):
        if not event.inaxes:
            return
        ax = event.inaxes
        minx, maxx = ax.get_xlim()
        miny, maxy = ax.get_ylim()

        x, y = event.xdata, event.ydata
        Signals.emit("newcoords", x, y)

        # return value
        self.point = (x, y)
    def button_press(self, event):
        ax = event.inaxes

        if event.button == 3:
            Signals.emit(self, "picked-axes", ax)

        elif event.button == 1:
            if ax is not None:
                obj = ax.pick(event.x, event.y)
            else:
                obj = None
            print "RMB, you picked", obj
    def update_cols(self):
        """
        Call this whenever you add/remove a column or when you change
        the type of a column.
        """
        self._colcount = len(self._columns)
        self._typecodes = map(lambda x: x.typecode(), self._columns)

        type_map = {'d': float, 'f': float, 'O': str}
        self._converters = map(lambda tc: type_map[tc], self._typecodes)

        Signals.emit(self, 'update-columns') # FIXME
    def update_cols(self):
        """
        Call this whenever you add/remove a column or when you change
        the type of a column.
        """
        self._ncols = len(self._columns)
        self._typecodes = map(lambda x: x.typecode(), self._columns)

        # TODO: move to types.h
        type_map = {'d': float, 'f': float, 'O': str, 'l': long, 'i': int}
        self._converters = map(lambda tc: type_map[tc], self._typecodes)

        Signals.emit(self, 'update-columns') # FIXME
    def update_cols(self):
        """
        Call this whenever you add/remove a column or when you change
        the type of a column.
        """
        self._ncols = len(self._columns)
        self._typecodes = map(lambda x: x.typecode(), self._columns)

        # TODO: move to types.h
        type_map = {"d": float, "f": float, "O": str, "l": long}
        self._converters = map(lambda tc: type_map[tc], self._typecodes)

        Signals.emit(self, "update-columns")  # FIXME
    def set_new_index(self, index):
        " Sets the new index (but keeps the line). "

        if index >= self.bounds[0] and index <= self.bounds[1]:
            self.index = index

            xdata = self.line.get_xdata()[index]
            ydata = self.line.get_ydata()[index]
            self.point = (xdata, ydata)

            self.coords = self.axes.transData.xy_tup((xdata, ydata))

            Signals.emit(self, "update-position", self.line, self.index, self.point)
            self.draw()
    def new_plot(self, undolist=None):
        " Returns a new Plot. "
        if undolist is None:
            undolist = self.journal
        
        new_plot = new_lineplot2d()
        new_plot.key = pdict.unique_key(self.plots, "new lineplot2d")
        self.add_plot(new_plot)
        ui = UndoInfo(self.remove_plot, new_plot).describe("New Plot")
        undolist.append(ui)

        Signals.emit(self, "notify::plots")
        
        return new_plot    
    def save_project_as(self, filename = None):
        """ Save project under another filename. """
        pj = self._check_project()

        if not filename:
            # allow user to choose a filename
            chooser = gtk.FileChooserDialog(
                title="Save Project As",
                action=gtk.FILE_CHOOSER_ACTION_SAVE,
                buttons=(gtk.STOCK_CANCEL,
                         gtk.RESPONSE_CANCEL,
                         gtk.STOCK_SAVE,
                         gtk.RESPONSE_OK))
            chooser.set_default_response(gtk.RESPONSE_OK)
            chooser.set_current_folder( const.internal_path(const.PATH_EXAMPLE) )
            chooser.set_select_multiple(False)
            chooser.set_filename(pj.filename or "unnamed.spj")

            filter = gtk.FileFilter()
            filter.set_name("All files")
            filter.add_pattern("*")
            chooser.add_filter(filter)
            chooser.set_filter(filter) # default filter

            shortcut_folder = const.internal_path(const.PATH_EXAMPLE)
            if os.path.exists(shortcut_folder):
                chooser.add_shortcut_folder(shortcut_folder)

            response = chooser.run()
            try:
                if response == gtk.RESPONSE_OK:
                    filename = chooser.get_filename()                    
                else:
                    raise error.UserCancel
            finally:
                chooser.destroy()

            # add extension if not yet there
            if filename.lower().endswith('.spj') is False:
                filename = filename + ".spj"

        self._project.filename = filename
        self.window.set_title(basename(self._project.filename))
        save_project(self._project)
        self._project.journal.clear()

        self.recent_files.append(os.path.abspath(filename))
        Signals.emit(self, 'update-recent-files')
    def load_project(self, filename):
        # load new project and if it is sucessfully loaded,
        # detach the old project
        new_project = load_project(filename)
        if new_project:
            self.set_project(new_project)

            # Add filename to list of recent_files __unless__
            # it is identical with the most recent file.
            new_filename = os.path.abspath(filename)
            if len(self.recent_files) == 0 or \
                   self.recent_files[0] != new_filename:                               
                self.recent_files.insert(0, new_filename)
                if len(self.recent_files) > 10:
                    self.recent_files.pop(-1)
                Signals.emit(self, "update-recent-files")
 def close(self):
     " Close project properly. "
     
     for dataset in self.datasets:
         dataset.close()
     for plot in self.plots:
         plot.close()
     if self._archive is not None:
         self._archive.close()
     
     # disconnect all opened backends
     for backend in self.backends:
         backend.disconnect()
    
     Signals.emit(self, 'close')
     self.app = None # TODO: this should be unnecessary if the app catches the signal
    def rename_plot(self, xn_plot, new_key, undolist=None):
        " Analogon to `rename_dataset`. "
        if undolist is None:
            undolist = self.journal

        plotlist = [plot for plot in self.plots]
        plot = self.get_plot(xn_plot)
        plotlist.remove(plot)
        new_key = pdict.unique_key(plotlist, new_key)

        ui = UndoInfo(self.rename_plot, plot, plot.key)
        ui.describe("Rename Plot")

        plot.key = new_key
        undolist.append(ui)
        Signals.emit(self, "notify::plots")

        return plot
    def set_project(self, project, confirm=True):

        has_changed = (id(self._project) != id(project))        
        if self._project is not None and has_changed:
            self._project.close()

        self._project = project
        if project is not None:
            project.app = self
            def detach_project(project):
                if id(self._project) == id(project):
                    self._project.app = None
                    self._project = None
                    
            # TODO: connect_once would be nice.
            Signals.connect(project, 'close', detach_project)

        if has_changed is True:
            Signals.emit(self, 'notify::project', self._project)
    def redraw(self, rebuild_cache=True):

        # All commands for gnuplot are appended to the cmd_list,
        # so that they can be executed at the very end.
        cmd_list = []
        cmd_list.append('cd "%s"' % self.tmpdir)
	cmd_list.append( "set encoding %s" % self.encoding )

        cmd_list += self.terminal.build(self)     

        # multiplot ?
        if len(self.plot.layers) > 1:
            cmd_list.append( "set multiplot" )
            for layer in self.plot.layers:
                group_info = {}
                x, y = uwrap.get(layer, 'x'), uwrap.get(layer, 'y')
                width, height = uwrap.get(layer, 'width'), uwrap.get(layer, 'height')
                cmd_list.append("set origin %.2f,%.2f" % (x,y))
                cmd_list.append("set size %.2f,%.2f" % (width, height))
                cmd_list += self.build_layer(layer, group_info)
            cmd_list.append( "unset multiplot" )
        else:
            # Single plot!
            # create plotting commands from the Layer information
            group_info = {}
            cmd_list += self.build_layer(self.plot.layers[0], group_info)

        self.export_datasets()
       
        # Now execute all collected commands.
        print "cmd list is: "
        for cmd in cmd_list:
            print "   ", cmd
        print
        
        Signals.emit(self, 'gnuplot-start-plotting')
        logger.info("Gnuplot command list:\n\n%s" % "\n".join(cmd_list))
        for cmd in cmd_list:
            self(cmd)

        Signals.emit(self,'gnuplot-after-plot', window_title=self.window_title)
    def close_project(self, confirm=True):
        pj = self._check_project()

        print "Closing Project"

        for dataset in pj.datasets:
            dataset.close()
        for plot in pj.plots:
            plot.close()
        if pj._archive is not None:
            pj._archive.close()

        pj.app = None

        # disconnect all opened backends
        for backend in pj.backends:
            backend.disconnect()

        print "Emitting Signal"

        Signals.emit(pj, "close")

        self._project = None
    def rename_dataset(self, xn_dataset, new_key, undolist=None):
        """
        Rename a Dataset and make sure that its key is unique.
        The name might be modified so if the key is important to you,
        you might want to check it afterwards.
        Returns the Dataset.
        """
        if undolist is None:
            undolist = self.journal

        dataset = self.get_dataset(xn_dataset)

        dslist = [ds for ds in self.datasets]
        dslist.remove(dataset)
        new_key = pdict.unique_key(dslist, new_key)

        ui = UndoInfo(self.rename_dataset, dataset, dataset.key)
        ui.describe("Rename Dataset")

        dataset.key = new_key
        undolist.append(ui)
        Signals.emit(self, "notify::datasets")

        return dataset
    def set_plot(self, plot):
        # TODO: remove old plot
        # TODO: connect to plot's title    

        if plot is not None:
            backend = self.project.request_backend('matplotlib', plot=plot)

            #backend.canvas.set_size_request(800, 600)
            sw = uihelper.add_scrollbars(backend.canvas, viewport=True)
            sw.show()
            self.vbox.pack_start(sw)
        else:
            backend = None
           
        # disconnect old stuff
        if self.backend is not None and self.backend != backend:
            self.backend.disconnect()

        for signal in self._signals:
            Signals.disconnect(signal)
        self._signals = []
        
        if self.cursor is not None:
            self.cursor.finish()

        # connect new backend
        self.plot = plot
        self.backend = backend


        if backend is not None:
            self._signals.extend(
                [Signals.connect(plot, "plot-changed", (lambda sender: backend.draw())),
                 Signals.connect(plot, "closed", (lambda sender: Signals.emit(self, 'closed')))]
                )
            try:
                backend.draw()
            except:
                #gtkutils.info_msg("Nothing to plot.")
                raise            

            # Cursor
            self.cursor = mpl_selector.Cursor(self.backend.figure)
            Signals.connect(self.cursor, "move",
                            (lambda sender,x,y: self.set_coords(x,y)))
            self.cursor.init()
def emit_last(sender, name, *args, **kwargs):
    ul = kwargs.pop('undolist', [])
    Signals.emit(sender, name, *args, **kwargs)
    ul.insert(0, UndoInfo(emit_last, sender, name, *args, **kwargs))
def emit(sender, name, *args, **kwargs):
    " undo wrapper around emit. "
    undolist = kwargs.pop('undolist', [])
    Signals.emit(sender, name, *args, **kwargs)
    undolist.append(UndoInfo(emit, sender, name, *args, **kwargs))
 def detach(self):
     Signals.emit(self, 'closed')
 def close(self):
     Signals.emit(self, 'closed')