def __init__(self, sim_manager, model_file_name=None):
        """ Initializes simulator manager, and lists for plots.
        Instantiates main frame as well.
        Then if a model file name was provided,
        load that model file name. """
        self.sim_manager = sim_manager
        self.network_view = NetworkView(self)
        self.dt = settings.SIMULATOR_DEFAULT_DELTA_TIME

        self.registered = []
        self.plots = []
        self.load_plots()

        self._has_network = False
        self._loaded_model_file = None

        # instantiates main visualizer frame
        self.main_frame = MainFrame(self.sim_manager, self)

        if (model_file_name):
            self.load_model_from_filename(model_file_name, load_layout=True)
    def __init__(self, sim_manager, model_file_name=None):
        """ Initializes simulator manager, and lists for plots.
        Instantiates main frame as well.
        Then if a model file name was provided,
        load that model file name. """
        self.sim_manager = sim_manager
        self.network_view = NetworkView(self)
        self.dt = settings.SIMULATOR_DEFAULT_DELTA_TIME

        self.registered = []
        self.plots = []
        self.load_plots()

        self._has_network = False
        self._loaded_model_file = None

        # instantiates main visualizer frame
        self.main_frame = MainFrame(self.sim_manager, self)

        if (model_file_name):
            self.load_model_from_filename(model_file_name, load_layout=True)
class VisualizerController(object):
    """ Controller class for visualizer.
    Handles adding, removing plots, loading, saving of models and layouts.
    """

    def __init__(self, sim_manager, model_file_name=None):
        """ Initializes simulator manager, and lists for plots.
        Instantiates main frame as well.
        Then if a model file name was provided,
        load that model file name. """
        self.sim_manager = sim_manager
        self.network_view = NetworkView(self)
        self.dt = settings.SIMULATOR_DEFAULT_DELTA_TIME

        self.registered = []
        self.plots = []
        self.load_plots()

        self._has_network = False
        self._loaded_model_file = None

        # instantiates main visualizer frame
        self.main_frame = MainFrame(self.sim_manager, self)

        if (model_file_name):
            self.load_model_from_filename(model_file_name, load_layout=True)

    def plots_for_object(self, obj):
        """ Gets list of capabilities of nodes in plot object.
        For each capability and for each registered plot,
        if the plot supports that type of capability,
        append to list of available plots.

        Returns the list of plots available for this object.
        """
        supported_plots = []
        node_caps = self.sim_manager.get_caps_for_obj(obj)
        for cap in node_caps:
            for vz in self.registered:
                if vz.supports_cap(cap):
                    supported_plots.append((vz, obj, cap))
        return supported_plots

    def add_plot_for_obj(self, plt, obj, cap, config=None, position=None,
                         size=None):
        """ Instantiates plot object, set its config values if
        config is provided, then appends to list of plots.
        Calls the connect_to_obj method to set up simulator manager
        and plot object. Then finally calls show_plot to set up
        plot in visualizer frame.
        """
        plot = plt(self, obj, cap)
        plot.set_config_values(config)
        self.plots.append(plot)
        self.sim_manager.connect_to_obj(obj, cap, plot.update)
        self.main_frame.show_plot(plot, False, position, size)

    def remove_plot_for_obj(self, plot, obj, cap):
        """ Disconnects object from simulator manager,
        calls remove method on plot to remove from visualizer
        and list of plots.
        """
        self.sim_manager.disconnect_from_obj(obj, cap, plot.update)
        self.plots.remove(plot)
        self.main_frame.remove_plot(plot)

    def on_quit(self):
        """ Event handler for when program quits
        saves a layout file in json format of data from
        get_layout_dict(), which contains layout data.
        """
        if self._loaded_model_file:
            filename = os.path.splitext(self._loaded_model_file)[0] + \
                settings.LAYOUT_FILE_EXTENSION
            with open(filename, 'wb') as f:
                json.dump(self.get_layout_dict(), f)

    def on_open_model(self, widget):
        """ Event handler for when open model menu item is clicked.
        """
        filename = self.file_open(ext=settings.PYTHON_FILE_EXTENSION,
                                  ext_name=settings.PYTHON_FILE_EXTENSION_NAME)
        if not filename:
            return
        self.load_model_from_filename(filename, load_layout=True)

    def restore_layout_dict(self, dct, load_model=False):
        """ Given data from layout dictionary, load model file.
        Loop through all plot objects in layout dictionary,
        add the plot objects, then calls the restore method
        on network view.

        Throws:
            ValueError - dct could not be loaded.
        """
        layout_dict = dct['layout']

        # Restore model file
        if load_model:
            self.load_model_from_filename(layout_dict['model'],
                                          load_layout=False)

        # Restore plots
        for plot_dict in layout_dict['plots']:
            target_obj = self.get_nengo_for_uid(plot_dict['target_obj'])
            target_cap_name = plot_dict['target_cap']
            target_cap = None
            for cap in self.sim_manager.get_caps_for_obj(target_obj):
                if cap.name == target_cap_name:
                    target_cap = cap
            if not target_cap:
                raise ValueError("No capability for nengo object: " +
                                 target_obj + " with name: " + target_cap_name)
            plot_type = plot_dict['plot_type']
            for plot_cls in self.registered:
                if plot_type == plot_cls.__name__:
                    self.add_plot_for_obj(plot_cls, target_obj,
                                          target_cap, plot_dict['config'],
                                          plot_dict['position'],
                                          plot_dict['size'])
                    break
            else:
                # loop exited without break
                raise ValueError("No plot:" + plot_type + "for nengo object: "
                                 + target_obj + " with name: " +
                                 target_cap_name)

        # Restore network
        net_dict = layout_dict['network']
        self.main_frame.set_item_position(self.network_view,
                                          net_dict['position'])
        self.main_frame.set_item_size(self.network_view, net_dict['size'])
        self.network_view.restore_layout(net_dict['network_layout'])

    def get_layout_dict(self):
        """ Saves plot data, layout data and network layout data into a json
        data.

        Returns json data object.
        """
        layout_dict = {}
        # Save model file
        layout_dict['model'] = self._loaded_model_file
        # Save plots
        layout_dict['plots'] = []
        for plot in self.plots:
            plot_dict = {}
            plot_dict['plot_type'] = plot.__class__.__name__
            plot_dict['target_obj'] = self.get_uid_for_nengo(plot.nengo_obj)
            plot_dict['target_cap'] = plot.capability.name
            plot_dict['position'] = self.main_frame.get_item_position(plot)
            plot_dict['size'] = self.main_frame.get_item_size(plot)
            plot_dict['config'] = plot.get_config_values()
            layout_dict['plots'].append(plot_dict)
        # Save network
        net_dict = {}
        net_dict['position'] = \
            self.main_frame.get_item_position(self.network_view)
        net_dict['size'] = self.main_frame.get_item_size(self.network_view)
        net_dict['network_layout'] = self.network_view.store_layout()
        layout_dict['network'] = net_dict
        return {'layout': layout_dict}

    def get_uid_for_nengo(self, nengo_obj):
        """ Returns a consistent unique id for the given nengo object.
        """
        # Let's just use the network view's method right now,
        # since that seems to be working great
        return self.network_view.get_name_from_obj(nengo_obj)

    def get_nengo_for_uid(self, uid):
        """ Returns a nengo object for a given uid.
        """
        return self.network_view.get_obj_from_name(uid)

    def load_model_from_filename(self, filename, load_layout=False):
        """ Tries to load model from a filename.
        If successful, load a layout file with the associated model
        file.
        """
        mod_name, file_ext = os.path.splitext(os.path.basename(filename))
        try:
            module = imp.load_source(mod_name, filename)
            self.load_model(module.model)
            self._loaded_model_file = filename
            if (not self._has_network):
                self.main_frame.show_plot(self.network_view, True)
                self._has_network = True
                self.main_frame.controller_panel.enable_controls()
        except (AttributeError, ImportError, IOError, SyntaxError) as e:
            print e
            self.show_err_dialog("Error loading model",
                                 "Could not load model from " + str(filename))

        if load_layout:
            try:
                layout_file = \
                    os.path.splitext(self._loaded_model_file)[0] + '.bpwn'
                if not layout_file:
                    return
                with open(layout_file, 'rb') as f:
                    self.restore_layout_dict(json.load(f))
            except Exception as e:
                # If the layout file isn't there, don't bug user...
                if type(e) is not IOError:
                    traceback.print_exc()
                    self.show_err_dialog("Error loading layout for model",
                                         "Could not load layout from " +
                                         str(layout_file))
                    self.load_model_from_filename(filename, load_layout=False)
                    self.network_view.init_default_config()

    def show_err_dialog(self, message, secondary):
        """ Show an error message in a dialog.
        """
        dialog = Gtk.MessageDialog(self.main_frame.window, 0,
                                   Gtk.MessageType.INFO,
                                   Gtk.ButtonsType.OK, message)
        dialog.format_secondary_text(secondary)
        dialog.run()
        dialog.destroy()

    def set_nengo_obj_id(self, nengo_obj):
        """ Creates an id for a given nengo obj.

        We did notice that NengoObject has a key property,
        however we required an id would give us reasonable behavior
        given a model with minor changes (ie somewhat resistant to
        reordering, addition, removal of nodes). Also we use md5
        because of its collision resistance.

        NOTE: We identify a nengo object given its label, class,
        and output dimensions. These are not necessarily unique,
        and in the case where they are not (ie objects are poorly
        labeled), we have undefined behavior.
        """

        obj_id = hashlib.md5()
        obj_id.update(nengo_obj.label)
        obj_id.update(nengo_obj.__class__.__name__)

        obj_caps = self.sim_manager.get_caps_for_obj(nengo_obj)
        for cap in obj_caps:
            obj_id.update(cap.get_out_dimensions(nengo_obj))
        nengo_obj.id = obj_id.hexdigest()

    def load_model(self, model):
        """ Removes current models, and resets current visualizer state.
        Loads new model in network view and simulator manager.
        """
        copy_plots = self.plots[:]
        for plt in copy_plots:
            plt.remove_plot(None, None)
        self.plots = []
        if self.sim_manager.current_step > 0:
            self.main_frame.reset_button(None)  # a little hacky, but hey
        self.model = model

        for nengo_obj in model.objs:
            self.set_nengo_obj_id(nengo_obj)

        self.main_frame.window.set_title("Nengo Visualizer - " + model.label)
        self.network_view.load_model(model)
        self.sim_manager.load_new_model(model, self.dt)

    def load_plots(self):
        """ Loads all plot files by looking for python file extension
        in plot directory.
        """
        plots_dir = os.path.join(os.path.dirname(__file__), "../plots/")
        for name in os.listdir(plots_dir):
            if name.endswith(".py") and not name.startswith("_"):
                self.load_module(os.path.join(plots_dir, name))
        self.registered = REGISTERED_PLOTS.values()

    def load_module(self, filename):
        """ Loads a module by file name and returns module object.
        """
        mod_name, ext = os.path.splitext(os.path.basename(filename))
        return imp.load_source(mod_name, filename)

    def on_layout_button_release(self, widget, event):
        """ Event handler for when export to pdf menu item is
        activated in right click context menu for plots.

        Returns true if event is handled here, false otherwise.
        """
        if event.button == settings.EVENT_BUTTON_RIGHT_CLICK:
            export_pdf_item = gtk.MenuItem("Export to PDF...")
            export_pdf_item.connect("activate", self.on_export_pdf, widget)
            export_pdf_item.show()
            self.layout_context_menu = gtk.Menu()
            self.layout_context_menu.append(export_pdf_item)
            self.layout_context_menu.popup(None, None, None, None,
                                           event.button, event.time)
            return True
        return False

    def on_export_pdf(self, event_widget, widget, name=None):
        """ Opens file to write pdf.
        Creates a pdf surface with file handler, then draws to pdf surface and
        close file.
        """
        if not name:
            name = self.main_frame.window.get_title()
        filename = self.file_save(name + ".pdf")
        if not filename:
            return
        with open(filename, "wb") as f:
            allocation = widget.get_allocation()
            cr = cairo.Context(cairo.PDFSurface(f, allocation.width,
                                                allocation.height))
            try:
                widget.on_draw_event(None, cr)
            except AttributeError:
                widget.draw(cr)
            cr.show_page()
            cr.get_target().finish()

    def file_open(self, ext="", ext_name=""):
        """ Calls _file_browse which opens a file browser with open button.

        Returns result.
        """
        buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                   gtk.STOCK_OPEN, gtk.RESPONSE_OK)
        return self._file_browse(gtk.FILE_CHOOSER_ACTION_OPEN,
                                 buttons, "", ext, ext_name)

    def file_save(self, name="", ext="", ext_name=""):
        """ Calls _file_browse which opens a file browser with save button.

        Returns result.
        """
        buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                   gtk.STOCK_SAVE, gtk.RESPONSE_OK)
        return self._file_browse(gtk.FILE_CHOOSER_ACTION_SAVE,
                                 buttons, name, ext, ext_name)

    def _file_browse(self, action, buttons, name="", ext="", ext_name=""):
        """ Creates a file chooser dialog.
        Use a * for filter.

        Returns result of instantiating dialog.
        """
        dialog = gtk.FileChooserDialog(title="Select File",
                                       action=action, buttons=buttons)
        dialog.set_current_folder(os.getcwd())
        dialog.set_do_overwrite_confirmation(True)
        if action == gtk.FILE_CHOOSER_ACTION_SAVE:
            dialog.set_current_name(name)

        if ext:
            filt = gtk.FileFilter()
            filt.set_name(ext_name if ext_name else ext)
            filt.add_pattern("*." + ext)
            dialog.add_filter(filt)

        filt = gtk.FileFilter()
        filt.set_name("All files")
        filt.add_pattern("*")
        dialog.add_filter(filt)

        result = ""
        if dialog.run() == gtk.RESPONSE_OK:
            result = dialog.get_filename()
        dialog.destroy()
        return result
class VisualizerController(object):
    """ Controller class for visualizer.
    Handles adding, removing plots, loading, saving of models and layouts.
    """
    def __init__(self, sim_manager, model_file_name=None):
        """ Initializes simulator manager, and lists for plots.
        Instantiates main frame as well.
        Then if a model file name was provided,
        load that model file name. """
        self.sim_manager = sim_manager
        self.network_view = NetworkView(self)
        self.dt = settings.SIMULATOR_DEFAULT_DELTA_TIME

        self.registered = []
        self.plots = []
        self.load_plots()

        self._has_network = False
        self._loaded_model_file = None

        # instantiates main visualizer frame
        self.main_frame = MainFrame(self.sim_manager, self)

        if (model_file_name):
            self.load_model_from_filename(model_file_name, load_layout=True)

    def plots_for_object(self, obj):
        """ Gets list of capabilities of nodes in plot object.
        For each capability and for each registered plot,
        if the plot supports that type of capability,
        append to list of available plots.

        Returns the list of plots available for this object.
        """
        supported_plots = []
        node_caps = self.sim_manager.get_caps_for_obj(obj)
        for cap in node_caps:
            for vz in self.registered:
                if vz.supports_cap(cap):
                    supported_plots.append((vz, obj, cap))
        return supported_plots

    def add_plot_for_obj(self,
                         plt,
                         obj,
                         cap,
                         config=None,
                         position=None,
                         size=None):
        """ Instantiates plot object, set its config values if
        config is provided, then appends to list of plots.
        Calls the connect_to_obj method to set up simulator manager
        and plot object. Then finally calls show_plot to set up
        plot in visualizer frame.
        """
        plot = plt(self, obj, cap)
        plot.set_config_values(config)
        self.plots.append(plot)
        self.sim_manager.connect_to_obj(obj, cap, plot.update)
        self.main_frame.show_plot(plot, False, position, size)

    def remove_plot_for_obj(self, plot, obj, cap):
        """ Disconnects object from simulator manager,
        calls remove method on plot to remove from visualizer
        and list of plots.
        """
        self.sim_manager.disconnect_from_obj(obj, cap, plot.update)
        self.plots.remove(plot)
        self.main_frame.remove_plot(plot)

    def on_quit(self):
        """ Event handler for when program quits
        saves a layout file in json format of data from
        get_layout_dict(), which contains layout data.
        """
        if self._loaded_model_file:
            filename = os.path.splitext(self._loaded_model_file)[0] + \
                settings.LAYOUT_FILE_EXTENSION
            with open(filename, 'wb') as f:
                json.dump(self.get_layout_dict(), f)

    def on_open_model(self, widget):
        """ Event handler for when open model menu item is clicked.
        """
        filename = self.file_open(ext=settings.PYTHON_FILE_EXTENSION,
                                  ext_name=settings.PYTHON_FILE_EXTENSION_NAME)
        if not filename:
            return
        self.load_model_from_filename(filename, load_layout=True)

    def restore_layout_dict(self, dct, load_model=False):
        """ Given data from layout dictionary, load model file.
        Loop through all plot objects in layout dictionary,
        add the plot objects, then calls the restore method
        on network view.

        Throws:
            ValueError - dct could not be loaded.
        """
        layout_dict = dct['layout']

        # Restore model file
        if load_model:
            self.load_model_from_filename(layout_dict['model'],
                                          load_layout=False)

        # Restore plots
        for plot_dict in layout_dict['plots']:
            target_obj = self.get_nengo_for_uid(plot_dict['target_obj'])
            target_cap_name = plot_dict['target_cap']
            target_cap = None
            for cap in self.sim_manager.get_caps_for_obj(target_obj):
                if cap.name == target_cap_name:
                    target_cap = cap
            if not target_cap:
                raise ValueError("No capability for nengo object: " +
                                 target_obj + " with name: " + target_cap_name)
            plot_type = plot_dict['plot_type']
            for plot_cls in self.registered:
                if plot_type == plot_cls.__name__:
                    self.add_plot_for_obj(plot_cls, target_obj, target_cap,
                                          plot_dict['config'],
                                          plot_dict['position'],
                                          plot_dict['size'])
                    break
            else:
                # loop exited without break
                raise ValueError("No plot:" + plot_type +
                                 "for nengo object: " + target_obj +
                                 " with name: " + target_cap_name)

        # Restore network
        net_dict = layout_dict['network']
        self.main_frame.set_item_position(self.network_view,
                                          net_dict['position'])
        self.main_frame.set_item_size(self.network_view, net_dict['size'])
        self.network_view.restore_layout(net_dict['network_layout'])

    def get_layout_dict(self):
        """ Saves plot data, layout data and network layout data into a json
        data.

        Returns json data object.
        """
        layout_dict = {}
        # Save model file
        layout_dict['model'] = self._loaded_model_file
        # Save plots
        layout_dict['plots'] = []
        for plot in self.plots:
            plot_dict = {}
            plot_dict['plot_type'] = plot.__class__.__name__
            plot_dict['target_obj'] = self.get_uid_for_nengo(plot.nengo_obj)
            plot_dict['target_cap'] = plot.capability.name
            plot_dict['position'] = self.main_frame.get_item_position(plot)
            plot_dict['size'] = self.main_frame.get_item_size(plot)
            plot_dict['config'] = plot.get_config_values()
            layout_dict['plots'].append(plot_dict)
        # Save network
        net_dict = {}
        net_dict['position'] = \
            self.main_frame.get_item_position(self.network_view)
        net_dict['size'] = self.main_frame.get_item_size(self.network_view)
        net_dict['network_layout'] = self.network_view.store_layout()
        layout_dict['network'] = net_dict
        return {'layout': layout_dict}

    def get_uid_for_nengo(self, nengo_obj):
        """ Returns a consistent unique id for the given nengo object.
        """
        # Let's just use the network view's method right now,
        # since that seems to be working great
        return self.network_view.get_name_from_obj(nengo_obj)

    def get_nengo_for_uid(self, uid):
        """ Returns a nengo object for a given uid.
        """
        return self.network_view.get_obj_from_name(uid)

    def load_model_from_filename(self, filename, load_layout=False):
        """ Tries to load model from a filename.
        If successful, load a layout file with the associated model
        file.
        """
        mod_name, file_ext = os.path.splitext(os.path.basename(filename))
        try:
            module = imp.load_source(mod_name, filename)
            self.load_model(module.model)
            self._loaded_model_file = filename
            if (not self._has_network):
                self.main_frame.show_plot(self.network_view, True)
                self._has_network = True
                self.main_frame.controller_panel.enable_controls()
        except (AttributeError, ImportError, IOError, SyntaxError) as e:
            print e
            self.show_err_dialog("Error loading model",
                                 "Could not load model from " + str(filename))

        if load_layout:
            try:
                layout_file = \
                    os.path.splitext(self._loaded_model_file)[0] + '.bpwn'
                if not layout_file:
                    return
                with open(layout_file, 'rb') as f:
                    self.restore_layout_dict(json.load(f))
            except Exception as e:
                # If the layout file isn't there, don't bug user...
                if type(e) is not IOError:
                    traceback.print_exc()
                    self.show_err_dialog(
                        "Error loading layout for model",
                        "Could not load layout from " + str(layout_file))
                    self.load_model_from_filename(filename, load_layout=False)
                    self.network_view.init_default_config()

    def show_err_dialog(self, message, secondary):
        """ Show an error message in a dialog.
        """
        dialog = Gtk.MessageDialog(self.main_frame.window, 0,
                                   Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
                                   message)
        dialog.format_secondary_text(secondary)
        dialog.run()
        dialog.destroy()

    def set_nengo_obj_id(self, nengo_obj):
        """ Creates an id for a given nengo obj.

        We did notice that NengoObject has a key property,
        however we required an id would give us reasonable behavior
        given a model with minor changes (ie somewhat resistant to
        reordering, addition, removal of nodes). Also we use md5
        because of its collision resistance.

        NOTE: We identify a nengo object given its label, class,
        and output dimensions. These are not necessarily unique,
        and in the case where they are not (ie objects are poorly
        labeled), we have undefined behavior.
        """

        obj_id = hashlib.md5()
        obj_id.update(nengo_obj.label)
        obj_id.update(nengo_obj.__class__.__name__)

        obj_caps = self.sim_manager.get_caps_for_obj(nengo_obj)
        for cap in obj_caps:
            obj_id.update(cap.get_out_dimensions(nengo_obj))
        nengo_obj.id = obj_id.hexdigest()

    def load_model(self, model):
        """ Removes current models, and resets current visualizer state.
        Loads new model in network view and simulator manager.
        """
        copy_plots = self.plots[:]
        for plt in copy_plots:
            plt.remove_plot(None, None)
        self.plots = []
        if self.sim_manager.current_step > 0:
            self.main_frame.reset_button(None)  # a little hacky, but hey
        self.model = model

        for nengo_obj in model.objs:
            self.set_nengo_obj_id(nengo_obj)

        self.main_frame.window.set_title("Nengo Visualizer - " + model.label)
        self.network_view.load_model(model)
        self.sim_manager.load_new_model(model, self.dt)

    def load_plots(self):
        """ Loads all plot files by looking for python file extension
        in plot directory.
        """
        plots_dir = os.path.join(os.path.dirname(__file__), "../plots/")
        for name in os.listdir(plots_dir):
            if name.endswith(".py") and not name.startswith("_"):
                self.load_module(os.path.join(plots_dir, name))
        self.registered = REGISTERED_PLOTS.values()

    def load_module(self, filename):
        """ Loads a module by file name and returns module object.
        """
        mod_name, ext = os.path.splitext(os.path.basename(filename))
        return imp.load_source(mod_name, filename)

    def on_layout_button_release(self, widget, event):
        """ Event handler for when export to pdf menu item is
        activated in right click context menu for plots.

        Returns true if event is handled here, false otherwise.
        """
        if event.button == settings.EVENT_BUTTON_RIGHT_CLICK:
            export_pdf_item = gtk.MenuItem("Export to PDF...")
            export_pdf_item.connect("activate", self.on_export_pdf, widget)
            export_pdf_item.show()
            self.layout_context_menu = gtk.Menu()
            self.layout_context_menu.append(export_pdf_item)
            self.layout_context_menu.popup(None, None, None, None,
                                           event.button, event.time)
            return True
        return False

    def on_export_pdf(self, event_widget, widget, name=None):
        """ Opens file to write pdf.
        Creates a pdf surface with file handler, then draws to pdf surface and
        close file.
        """
        if not name:
            name = self.main_frame.window.get_title()
        filename = self.file_save(name + ".pdf")
        if not filename:
            return
        with open(filename, "wb") as f:
            allocation = widget.get_allocation()
            cr = cairo.Context(
                cairo.PDFSurface(f, allocation.width, allocation.height))
            try:
                widget.on_draw_event(None, cr)
            except AttributeError:
                widget.draw(cr)
            cr.show_page()
            cr.get_target().finish()

    def file_open(self, ext="", ext_name=""):
        """ Calls _file_browse which opens a file browser with open button.

        Returns result.
        """
        buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
                   gtk.RESPONSE_OK)
        return self._file_browse(gtk.FILE_CHOOSER_ACTION_OPEN, buttons, "",
                                 ext, ext_name)

    def file_save(self, name="", ext="", ext_name=""):
        """ Calls _file_browse which opens a file browser with save button.

        Returns result.
        """
        buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE,
                   gtk.RESPONSE_OK)
        return self._file_browse(gtk.FILE_CHOOSER_ACTION_SAVE, buttons, name,
                                 ext, ext_name)

    def _file_browse(self, action, buttons, name="", ext="", ext_name=""):
        """ Creates a file chooser dialog.
        Use a * for filter.

        Returns result of instantiating dialog.
        """
        dialog = gtk.FileChooserDialog(title="Select File",
                                       action=action,
                                       buttons=buttons)
        dialog.set_current_folder(os.getcwd())
        dialog.set_do_overwrite_confirmation(True)
        if action == gtk.FILE_CHOOSER_ACTION_SAVE:
            dialog.set_current_name(name)

        if ext:
            filt = gtk.FileFilter()
            filt.set_name(ext_name if ext_name else ext)
            filt.add_pattern("*." + ext)
            dialog.add_filter(filt)

        filt = gtk.FileFilter()
        filt.set_name("All files")
        filt.add_pattern("*")
        dialog.add_filter(filt)

        result = ""
        if dialog.run() == gtk.RESPONSE_OK:
            result = dialog.get_filename()
        dialog.destroy()
        return result