class GtkApplication(Application): """ Application is a wrapper window for the ProjectTreeView which holds the information on the current project. It adds a menu bar and a toolbar as described in the two attributes window_actions and ui_string of this module. Furthermore, it provides basic functions to work with the project. """ def init(self): #self.path.bset('icon_dir', 'system_prefix_dir', os.path.join('share', 'pixmaps', 'sloppyplot')) self.window = AppWindow(self) self._clipboard = gtk.Clipboard() # not implemented yet self._current_plot = None self.path.bset('icon_dir', 'base_dir', os.path.join('Gtk','Icons')) self.register_stock() def register_stock(self): uihelper.register_stock_icons(self.path.get('icon_dir'), prefix='sloppy-') # register stock items items = [('sloppy-rename', '_Rename', 0, 0, None)] aliases = [('sloppy-rename', 'gtk-edit')] gtk.stock_add(items) factory = gtk.IconFactory() factory.add_default() style = self.window.get_style() for new_stock, alias in aliases: icon_set = style.lookup_icon_set(alias) factory.add(new_stock, icon_set) def init_plugins(self): Application.init_plugins(self) for plugin in self.plugins.itervalues(): if hasattr(plugin, 'gtk_popup_actions'): action_wrappers = plugin.gtk_popup_actions() # create action group ag = gtk.ActionGroup("Plugin") for item in action_wrappers: ag.add_action(item.action) self.window.uimanager.insert_action_group(ag, -1) # construct plugin ui plugin_ui = '<popup name="popup_dataset">' for item in action_wrappers: plugin_ui += '<menuitem action="%s"/>' % item.name plugin_ui += '</popup>' # merge plugin ui merge_id = self.window.uimanager.add_ui_from_string(plugin_ui) # ---------------------------------------------------------------------- # Project def set_project(self, project, confirm=True): """ Assign the given project to the Application. @param confirm: Ask user for permission to close the project (unless there were no changes). """ if self._project is not None: if self._project.journal.can_undo() and confirm is True: msg = \ """ You are about to close the Project. Do you want to save your changes ? """ dialog = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION, message_format = msg) dialog.add_button("_Don't Save", gtk.RESPONSE_NO) btn_default = dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) dialog.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_YES) btn_default.grab_focus() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: # yes = yes, save the file before closing self.save_project() elif response == gtk.RESPONSE_NO: # no = no, proceed with closing pass else: # everything else -> abort action raise error.UserCancel # set new project Application.set_project(self, project) self.window.treeview.set_project(project) # assign project label to window title if project: title = project.filename or "<unnamed project>" else: title = "(no project)" self.window.set_title(basename(title)) if project is not None: project.journal.on_change = self.window._refresh_undo_redo self.window._refresh_undo_redo() self.window._refresh_recentfiles() def load_project(self, filename=None): """ Open a FileChooserDialog and let the user pick a new project to be loaded. The old project is replaced. """ if filename is None: # TODO # maybe we could have application.load_project # just request the file name and we simply # create a method for this dialog. # create chooser object chooser = gtk.FileChooserDialog( title="Open project", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_default_response(gtk.RESPONSE_OK) chooser.set_current_folder( self.path.get('current_dir') ) chooser.set_select_multiple(False) filter = gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") chooser.add_filter(filter) filter = gtk.FileFilter() filter.set_name("Sloppyplot Project files") filter.add_pattern("*.spj") filter.add_pattern("*.SPJ") chooser.add_filter(filter) chooser.set_filter(filter) # default filter shortcut_folder = self.path.get('example_dir') if os.path.exists(shortcut_folder): chooser.add_shortcut_folder( shortcut_folder ) response = chooser.run() if response == gtk.RESPONSE_OK: filename = chooser.get_filename() else: filename = None chooser.destroy() if filename is not None: Application.load_project(self, filename) 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( self.path.get('example_dir') ) 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 = self.path.get('example_dir') 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)) self.sig_emit('update-recent-files') def quit(self): """ Quit Application and gtk main loop. """ try: Application.quit(self) gtk.main_quit() except error.UserCancel: return # ---------------------------------------------------------------------- # Callbacks # # delete-event/destroy/quit application def _cb_quit_application(self, action): self.quit() def _cb_project_close(self,widget=None): self.close_project() def _cb_project_open(self,widget): self.load_project() def _cb_project_save(self,widget): self.save_project() def _cb_project_save_as(self,widget): self.save_project_as() def _cb_project_new(self,widget): self.new_project() #---------------------------------------------------------------------- def _cb_edit(self, action): plots, datasets = self.window.treeview.get_selected_plds() if len(plots) > 0: self.edit_layer(plots[0]) else: for dataset in datasets: self.edit_dataset(dataset) # --- VIEW --------------------------------------------------------------------- def edit_dataset(self, ds, undolist=[]): assert( isinstance(ds, Dataset) ) # reuse old DatasetWindow or create new one window = self.window.subwindow_match( (lambda win: isinstance(win, DatasetWindow) and (win.dataset == ds))) \ or \ self.window.subwindow_add( DatasetWindow(self, self._project, ds) ) window.present() def edit_layer(self, plot, layer=None, current_page=None): """ Edit the given layer of the given plot. If no layer is given, the method tries to edit the first Layer. If there is no Layer in the plot, an error will logged. TODO: current_page. """ if layer is None: if len(plot.layers) > 0: layer = plot.layers[0] else: logger.error("The plot to be edited has not even a single layer!") return win = LayerWindow(self, plot, layer, current_page=current_page) win.set_modal(True) win.present() #---------------------------------------------------------------------- def _cb_new_plot(self,widget): pj = self._check_project() plot = new_lineplot2d(key='empty plot') pj.add_plots([plot]) # --- PLOT --------------------------------------------------------------------- def _cb_plot(self,widget): self.plot_current_objects() def _cb_plot_gnuplot(self,widget): self.plot_current_objects('gnuplot/x11') def _cb_plot_matplotlib(self,widget): self.plot_current_objects('matplotlib') def plot(self,plot,backend_name='matplotlib'): logger.debug("Backend name is %s" % backend_name) if backend_name == 'gnuplot/x11': backend = self.project.request_backend('gnuplot/x11', plot=plot) backend.draw() return # TODO: open Gnuplot window # add_subwindow # match_subwindow # request_subwindow # evtl. schon als Toolbox ??? # window = self.window.subwindow_match( \ # (lambda win: isinstance(win, GnuplotWindow) and \ # win.project == self.project and \ # win.plot == plot) ) # if window is None: # window = GnuplotWindow(self, project=self.project, plot=plot) # self.window.subwindow_add( window ) elif backend_name == 'matplotlib': # # as widget # widget = self.window.find_plotwidget(project=self.project,plot=plot) # if widget is None: # widget = MatplotlibWidget(self, project=self.project, plot=plot) # self.window.add_plotwidget(widget) # widget.show() # as window window = self.window.subwindow_match( \ (lambda win: isinstance(win, MatplotlibWindow) \ and win.get_project() == self.project \ and win.get_plot() == plot) ) if window is None: window = MatplotlibWindow(self, project=self.project, plot=plot) ##window.set_transient_for(self.window) self.window.subwindow_add(window) # TESTING # Of course, the toolbox might decide to not use the backend, # e.g. if there is an 'auto' button and it is not pressed. So I will # need to change that then. window.connect("focus-in-event", (lambda a,b: self.window.toolbox.set_backend(window.get_backend()))) window.show() window.present() else: raise RuntimeError("Unknown backend %s" % backend_name) def plot_current_objects(self, backend_name='matplotlib', undolist=[]): (plots, datasets) = self.window.treeview.get_selected_plds() for plot in plots: self.plot(plot, backend_name) def on_action_export_via_gnuplot(self, action): plots = self.window.treeview.get_selected_plots() if len(plots) > 0: self.plot_postscript(self.project, plots[0]) def plot_postscript(app, project, plot): # # request filename # filename = PostscriptTerminal.build_filename('ps', project, plot) chooser = gtk.FileChooserDialog( title="PostScript Export", 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_select_multiple(False) chooser.set_current_folder(os.path.dirname(filename)) chooser.set_current_name(os.path.basename(filename)) filter = gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") chooser.add_filter(filter) filter = gtk.FileFilter() filter.set_name("Postscript (.ps; .eps)") filter.add_pattern("*.ps") filter.add_pattern("*.eps") chooser.add_filter(filter) chooser.set_filter(filter) # default filter response = chooser.run() try: if response == gtk.RESPONSE_OK: filename = chooser.get_filename() else: raise error.UserCancel finally: chooser.destroy() # # request export options # ##props = ['mode', 'enhanced', 'color', 'blacktext', 'solid', ## 'dashlength', 'linewidth', 'duplexing', 'rounded', 'fontname', ## 'fontsize', 'timestamp'] dialog = OptionsDialog(PostscriptTerminal(), title="Options Postscript Export", parent=app.window) #dialog.set_size_request(320,520) # determine requested postscript mode (ps or eps) from extension path, ext = os.path.splitext(filename) ext = ext.lower() if ext == '.eps': dialog.owner.mode = 'eps' elif ext == '.ps': dialog.owner.mode = 'landscape' try: result = dialog.run() if result == gtk.RESPONSE_ACCEPT: dialog.check_out() else: return terminal = dialog.owner finally: dialog.destroy() # # now check if mode and filename extension match # def fix_filename(filename, mode): msg = "The postscript mode you selected (%s) does not match the given filename extension (%s). Do you want to adjust the filename to match the mode? " % (mode, os.path.splitext(filename)[1]) dialog = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION, message_format = msg) dialog.add_button("Keep Filename", gtk.RESPONSE_NO) btn_default = dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) dialog.add_button("Adjust Filename", gtk.RESPONSE_YES) btn_default.grab_focus() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: # yes = yes, adjust filename if mode == '.eps': new_ext = '.eps' else: new_ext = '.ps' path, ext = os.path.splitext(filename) return path + new_ext elif response == gtk.RESPONSE_NO: # no = no, keep filename return filename else: # everything else -> abort action raise error.UserCancel if (terminal.mode == 'eps' and ext != '.eps') or \ (terminal.mode != 'eps' and ext != '.ps'): filename = fix_filename(filename, terminal.mode) # # construct backend for output # backend = BackendRegistry['gnuplot'](project=project, plot=plot, filename=filename, terminal=terminal) try: backend.draw() finally: backend.disconnect() # --- DATASET HANDLING ------------------------------------------------- def _cb_import_dataset(self, action): pj = self._check_project() # allow user to choose files for import chooser = gtk.FileChooserDialog( title="Import Dataset from file", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_default_response(gtk.RESPONSE_OK) chooser.set_current_folder(self.path.get('current_dir')) chooser.set_select_multiple(True) filter_keys = {} # used for reference later on # add 'All Files' filter blurb_all_files = "All Files" filter = gtk.FileFilter() filter.set_name(blurb_all_files) filter.add_pattern("*") chooser.add_filter(filter) chooser.set_filter(filter) filter_keys[blurb_all_files] = 'auto' # default if nothing else specified # # create file filters # # Each item in importer_registry is a class derived from # dataio.Importer. By using IOTemplate objects we can # customize the default values for these templates. for (key, template) in dataio.import_templates.iteritems(): ext_list = template.extensions.split(',') if len(ext_list) == 0: continue extensions = ';'.join(map(lambda ext: '*.'+ext, ext_list)) blurb = "%s (%s)" % (template.blurb, extensions) filter = gtk.FileFilter() filter.set_name(blurb) for ext in ext_list: filter.add_pattern("*."+ext.lower()) filter.add_pattern("*."+ext.upper()) chooser.add_filter(filter) filter_keys[blurb] = key # add shortcut folder to example path, if such exists shortcut_folder = self.path.get('data_dir') if os.path.exists(shortcut_folder): chooser.add_shortcut_folder(shortcut_folder) # # prepare extra widget # # The custom widget `combobox` lets the user choose, # which ImporterTemplate is to be used. # model: key, blurb model = gtk.ListStore(str, str) # add 'Same as Filter' as first choice, then add all importers model.append( (None, "Auto") ) for key, template in dataio.import_templates.iteritems(): model.append( (key, template.blurb) ) combobox = gtk.ComboBox(model) cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_active(0) combobox.show() label = gtk.Label("Use Template: ") label.show() hbox = gtk.HBox() hbox.pack_end(combobox,False) hbox.pack_end(label,False) hbox.show() vbox = gtk.VBox() vbox.pack_start(hbox,False) #vbox.pack_start(pbar,False) vbox.show() chooser.set_extra_widget(vbox) # # run dialog # try: response = chooser.run() if response == gtk.RESPONSE_OK: filenames = chooser.get_filenames() if len(filenames) == 0: return template_key = model[combobox.get_active()][0] if template_key is None: # auto f = chooser.get_filter() template_key = filter_keys[f.get_name()] else: return finally: chooser.destroy() self.do_import(pj, filenames, template_key) def do_import(self, project, filenames, template_key=None): # try to determine template key if it is not given if template_key is None or template_key=='auto': matches = dataio.importer_template_from_filename(filenames[0]) if len(matches) > 0: template_key = matches[0] else: template_key = 'ASCII' # # Request import options # # Note that if 'skip_option' is set in the template, then # there will be no user options dialog. if dataio.import_templates[template_key].skip_options is False: dialog = import_dialog.ImportOptions(template_key, previewfile=filenames[0]) try: result = dialog.run() if result == gtk.RESPONSE_ACCEPT: # save template as 'recently used' template = dataio.IOTemplate() template.defaults = dialog.importer.get_values(include=dialog.importer.public_props) template.blurb = "Recently used Template" template.importer_key = dialog.template.importer_key template.write_config = True template.is_internal = True dataio.import_templates['recently used'] = template else: return finally: dialog.destroy() else: template = template_key # The progress bar displays which file is currently being imported. Application.import_datasets(self, project, filenames, template) def _cb_new_dataset(self,widget): """ Create a new dataset and switch to its editing window. """ pj = self._check_project() ds = pj.new_dataset() self.edit_dataset(ds) def _cb_create_plot_from_datasets(self, widget): pj = self._check_project() datasets = self.window.treeview.get_selected_datasets() pj.create_plot_from_datasets(datasets) def _cb_add_datasets_to_plot(self, action): pj = self._check_project() (plots, datasets) = self.window.treeview.get_selected_plds() if len(plots) == 1 and len(datasets) > 0: pj.add_datasets_to_plot(datasets, plots[0]) def _cb_delete(self, widget): pj = self._check_project() objects = self.window.treeview.get_selected_objects() pj.remove_objects(objects) def _cb_experimental_plot(self, action): pj = self._check_project() plugin = self.plugins['Default'] plugin.add_experimental_plot(pj) # --- EDIT ------------------------------------------------------------- ### ### TODO: implement cut/copy/paste ### def _cb_edit_cut(self, widget): pass def _cb_edit_copy(self, widget): pass def _cb_edit_paste(self, widget): pass # --- UNDO/REDO -------------------------------------------------------- def _cb_undo(self, widget): pj = self._check_project() pj.undo() def _cb_redo(self, widget): pj = self._check_project() pj.redo() #---------------------------------------------------------------------- # MISC CALLBACKS def _cb_recent_files_clear(self, action): self.clear_recent_files() def on_action_Preferences(self, action): dlg = config.ConfigurationDialog() try: dlg.run() finally: dlg.destroy() return #---------------------------------------------------------------------- # Simple user I/O, inherited from Base.Application # def ask_yes_no(self, msg): dialog = gtk.MessageDialog(parent=self.window, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_YES_NO, message_format=msg) result = dialog.run() dialog.destroy() return result == gtk.RESPONSE_YES def error_msg(self, msg): dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK, message_format=unicode(msg)) dialog.run() dialog.destroy() def status_msg(self, msg): sb = self.window.statusbar context = sb.get_context_id("main") id = sb.push(context, msg) def remove_msg(statusbar, a_context, a_id): statusbar.remove(a_context, a_id) return False gobject.timeout_add(3000, remove_msg, sb, context, id) def progress(self, fraction): pb = self.window.progressbar if fraction == -1: pb.hide() else: pb.show() pb.set_fraction(fraction) # Calling main_iteration is definitely not the only way to # update the progressbar; see FAQ 23.20 for details. But # I refrain from using generators for the import function. while gtk.events_pending(): gtk.main_iteration()
class GtkApplication(Application): """ Application is a wrapper window for the ProjectTreeView which holds the information on the current project. It adds a menu bar and a toolbar as described in the two attributes window_actions and ui_string of this module. Furthermore, it provides basic functions to work with the project. """ def init(self): register_all_png_icons(const.internal_path(const.PATH_ICONS), 'sloppy-') self.window = AppWindow(self) self._clipboard = gtk.Clipboard() # not implemented yet self._current_plot = None def init_plugins(self): Application.init_plugins(self) for plugin in self.plugins.itervalues(): if hasattr(plugin, 'gtk_popup_actions'): action_wrappers = plugin.gtk_popup_actions() # create action group ag = gtk.ActionGroup("Plugin") for item in action_wrappers: ag.add_action(item.action) self.window.uimanager.insert_action_group(ag, -1) # construct plugin ui plugin_ui = '<popup name="popup_dataset">' for item in action_wrappers: plugin_ui += '<menuitem action="%s"/>' % item.name plugin_ui += '</popup>' # merge plugin ui merge_id = self.window.uimanager.add_ui_from_string(plugin_ui) # ---------------------------------------------------------------------- # Project def set_project(self, project, confirm=True): """ Assign the given project to the Application. @param confirm: Ask user for permission to close the project (unless there were no changes). """ if self._project is not None: if self._project.journal.can_undo() and confirm is True: msg = \ """ You are about to close the Project. Do you want to save your changes ? """ dialog = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION, message_format = msg) dialog.add_button("_Don't Save", gtk.RESPONSE_NO) btn_default = dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) dialog.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_YES) btn_default.grab_focus() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: # yes = yes, save the file before closing self.save_project() elif response == gtk.RESPONSE_NO: # no = no, proceed with closing pass else: # everything else -> abort action raise error.UserCancel # set new project Application.set_project(self, project) self.window.treeview.set_project(project) # assign project label to window title if project: title = project.filename or "<unnamed project>" else: title = "(no project)" self.window.set_title(basename(title)) if project is not None: project.journal.on_change = self.window._refresh_undo_redo self.window._refresh_undo_redo() self.window._refresh_recentfiles() def load_project(self, filename=None): """ Open a FileChooserDialog and let the user pick a new project to be loaded. The old project is replaced. """ if filename is None: # TODO # maybe we could have application.load_project # just request the file name and we simply # create a method for this dialog. # create chooser object chooser = gtk.FileChooserDialog( title="Open project", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, 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) filter = gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") chooser.add_filter(filter) filter = gtk.FileFilter() filter.set_name("Sloppyplot Project files") filter.add_pattern("*.spj") filter.add_pattern("*.SPJ") 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() if response == gtk.RESPONSE_OK: filename = chooser.get_filename() else: filename = None chooser.destroy() if filename is not None: Application.load_project(self, filename) 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 quit(self): """ Quit Application and gtk main loop. """ try: Application.quit(self) gtk.main_quit() except error.UserCancel: return #------------------------------------------------------------------------------ # Current plot # def set_current_plot(self, plot): print "APP: set_current_plto to ", plot self._current_plot = plot Signals.emit(self, "notify::current_plot", plot) def get_current_plot(self): return self._current_plot current_plot = property(get_current_plot, set_current_plot) # ---------------------------------------------------------------------- # Callbacks # # delete-event/destroy/quit application def _cb_quit_application(self, action): self.quit() def _cb_project_close(self,widget=None): self.close_project() def _cb_project_open(self,widget): self.load_project() def _cb_project_save(self,widget): self.save_project() def _cb_project_save_as(self,widget): self.save_project_as() def _cb_project_new(self,widget): self.new_project() #---------------------------------------------------------------------- def _cb_edit(self, action): plots, datasets = self.window.treeview.get_selected_plds() if len(plots) > 0: self.edit_layer(plots[0]) else: for dataset in datasets: self.edit_dataset(dataset) # --- VIEW --------------------------------------------------------------------- def edit_dataset(self, ds, undolist=[]): assert( isinstance(ds, Dataset) ) # reuse old DatasetWindow or create new one window = self.window.subwindow_match( (lambda win: isinstance(win, DatasetWindow) and (win.dataset == ds))) \ or \ self.window.subwindow_add( DatasetWindow(self, self._project, ds) ) window.present() def edit_layer(self, plot, layer=None, current_page=None): """ Edit the given layer of the given plot. If no layer is given, the method tries to edit the first Layer. If there is no Layer in the plot, an error will logged. TODO: current_page. """ if layer is None: if len(plot.layers) > 0: layer = plot.layers[0] else: logger.error("The plot to be edited has not even a single layer!") return win = LayerWindow(self, plot, layer, current_page=current_page) win.set_modal(True) win.present() #---------------------------------------------------------------------- def _cb_new_plot(self,widget): pj = self._check_project() plot = new_lineplot2d(key='empty plot') pj.add_plots([plot]) # --- PLOT --------------------------------------------------------------------- def _cb_plot(self,widget): self.plot_current_objects() def _cb_plot_gnuplot(self,widget): self.plot_current_objects('gnuplot/x11') def _cb_plot_matplotlib(self,widget): self.plot_current_objects('matplotlib') def plot(self,plot,backend_name=const.DEFAULT_BACKEND): logger.debug("Backend name is %s" % backend_name) if backend_name == 'gnuplot/x11': backend = self.project.request_backend('gnuplot/x11', plot=plot) backend.draw() return # TODO: open Gnuplot window # add_subwindow # match_subwindow # request_subwindow # evtl. schon als ToolWindow ??? # window = self.window.subwindow_match( \ # (lambda win: isinstance(win, GnuplotWindow) and \ # win.project == self.project and \ # win.plot == plot) ) # if window is None: # window = GnuplotWindow(self, project=self.project, plot=plot) # self.window.subwindow_add( window ) elif backend_name == 'matplotlib': # # as widget # widget = self.window.find_plotwidget(project=self.project,plot=plot) # if widget is None: # widget = MatplotlibWidget(self, project=self.project, plot=plot) # self.window.add_plotwidget(widget) # widget.show() # as window window = self.window.subwindow_match( \ (lambda win: isinstance(win, MatplotlibWindow) \ and win.get_project() == self.project \ and win.get_plot() == plot) ) if window is None: window = MatplotlibWindow(self, project=self.project, plot=plot) ##window.set_transient_for(self.window) self.window.subwindow_add(window) window.show() window.present() else: raise RuntimeError("Unknown backend %s" % backend_name) def plot_current_objects(self, backend_name=const.DEFAULT_BACKEND, undolist=[]): (plots, datasets) = self.window.treeview.get_selected_plds() for plot in plots: self.plot(plot, backend_name) def on_action_export_via_gnuplot(self, action): plots = self.window.treeview.get_selected_plots() if len(plots) > 0: self.plot_postscript(self.project, plots[0]) def plot_postscript(app, project, plot): # # request filename # filename = PostscriptTerminal.build_filename('ps', project, plot) chooser = gtk.FileChooserDialog( title="PostScript Export", 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_select_multiple(False) chooser.set_current_folder(os.path.dirname(filename)) chooser.set_current_name(os.path.basename(filename)) filter = gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") chooser.add_filter(filter) filter = gtk.FileFilter() filter.set_name("Postscript (.ps; .eps)") filter.add_pattern("*.ps") filter.add_pattern("*.eps") chooser.add_filter(filter) chooser.set_filter(filter) # default filter response = chooser.run() try: if response == gtk.RESPONSE_OK: filename = chooser.get_filename() else: raise error.UserCancel finally: chooser.destroy() # # request export options # ##props = ['mode', 'enhanced', 'color', 'blacktext', 'solid', ## 'dashlength', 'linewidth', 'duplexing', 'rounded', 'fontname', ## 'fontsize', 'timestamp'] dialog = OptionsDialog(PostscriptTerminal(), title="Options Postscript Export", parent=app.window) #dialog.set_size_request(320,520) # determine requested postscript mode (ps or eps) from extension path, ext = os.path.splitext(filename) ext = ext.lower() if ext == '.eps': dialog.owner.mode = 'eps' elif ext == '.ps': dialog.owner.mode = 'landscape' try: result = dialog.run() if response == gtk.RESPONSE_ACCEPT: dialog.check_out() else: return terminal = dialog.container finally: dialog.destroy() if result != gtk.RESPONSE_ACCEPT: return # # now check if mode and filename extension match # def fix_filename(filename, mode): msg = "The postscript mode you selected (%s) does not match the given filename extension (%s). Do you want to adjust the filename to match the mode? " % (mode, os.path.splitext(filename)[1]) dialog = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION, message_format = msg) dialog.add_button("Keep Filename", gtk.RESPONSE_NO) btn_default = dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) dialog.add_button("Adjust Filename", gtk.RESPONSE_YES) btn_default.grab_focus() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: # yes = yes, adjust filename if mode == '.eps': new_ext = '.eps' else: new_ext = '.ps' path, ext = os.path.splitext(filename) return path + new_ext elif response == gtk.RESPONSE_NO: # no = no, keep filename return filename else: # everything else -> abort action raise error.UserCancel if (terminal.mode == 'eps' and ext != '.eps') or \ (terminal.mode != 'eps' and ext != '.ps'): filename = fix_filename(filename, terminal.mode) # # construct backend for output # backend = BackendRegistry['gnuplot'](project=project, plot=plot, filename=filename, terminal=terminal) try: backend.draw() finally: backend.disconnect() # --- DATASET HANDLING ------------------------------------------------- def _cb_import_dataset(self, action): pj = self._check_project() # allow user to choose files for import chooser = gtk.FileChooserDialog( title="Import Dataset from file", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_default_response(gtk.RESPONSE_OK) chooser.set_current_folder(const.internal_path(const.PATH_DATA)) chooser.set_select_multiple(True) filter_keys = {} # used for reference later on # add 'All Files' filter blurb_all_files = "All Files" filter = gtk.FileFilter() filter.set_name(blurb_all_files) filter.add_pattern("*") chooser.add_filter(filter) chooser.set_filter(filter) filter_keys[blurb_all_files] = 'auto' # default if nothing else specified # create file filters for (key, importer) in ImporterRegistry.iteritems(): extensions = ';'.join(map(lambda ext: '*.'+ext, importer.extensions)) blurb = "%s (%s)" % (importer.blurb, extensions) filter = gtk.FileFilter() filter.set_name(blurb) for ext in importer.extensions: filter.add_pattern("*."+ext.lower()) filter.add_pattern("*."+ext.upper()) chooser.add_filter(filter) filter_keys[blurb] = key # add shortcut folder to example path, if such exists shortcut_folder = const.internal_path(const.PATH_EXAMPLE) if os.path.exists(shortcut_folder): chooser.add_shortcut_folder(shortcut_folder) # # prepare extra widget # # The custom widget `combobox` lets the user choose, # which Importer is to be used. # model: key, blurb model = gtk.ListStore(str, str) # add 'Same as Filter' as first choice, then add all importers model.append( (None, "Auto") ) for key, importer in ImporterRegistry.iteritems(): model.append( (key, importer.blurb) ) combobox = gtk.ComboBox(model) cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_active(0) combobox.show() label = gtk.Label("Importer") label.show() hbox = gtk.HBox() hbox.pack_end(combobox,False) hbox.pack_end(label,False) hbox.show() # The progress bar display which file is currently being imported. pbar = gtk.ProgressBar() vbox = gtk.VBox() vbox.pack_start(hbox,False) vbox.pack_start(pbar,False) vbox.show() chooser.set_extra_widget(vbox) # # run dialog # try: response = chooser.run() if response == gtk.RESPONSE_OK: filenames = chooser.get_filenames() if len(filenames) == 0: return importer_key = model[combobox.get_active()][0] if importer_key is None: # auto f = chooser.get_filter() importer_key = filter_keys[f.get_name()] if importer_key is 'auto': matches = importer_from_filename(filenames[0]) if len(matches) > 0: importer_key = matches[0] else: importer_key = 'ASCII' else: return # TODO #chooser.set_active(False) pbar.show() # request import options importer = ImporterRegistry[importer_key]() try: dialog = OptionsDialog(importer, parent=self.window) except NoOptionsError: pass else: # If there are any options, construct a # preview widget to help the user. view = gtk.TextView() buffer = view.get_buffer() view.set_editable(False) view.show() tag_main = buffer.create_tag(family="Courier") tag_linenr = buffer.create_tag(family="Courier", weight=pango.WEIGHT_HEAVY) # fill preview buffer with at most 100 lines preview_file = filenames[0] try: fd = open(preview_file, 'r') except IOError: raise RuntimeError("Could not open file %s for preview!" % preview_file) iter = buffer.get_start_iter() try: for j in range(100): line = fd.readline() if len(line) == 0: break buffer.insert_with_tags(iter, u"%3d\t" % j, tag_linenr) try: buffer.insert_with_tags(iter, unicode(line), tag_main) except UnicodeDecodeError: buffer.insert_with_tags(iter, u"<unreadable line>\n", tag_main) finally: fd.close() preview_widget = uihelper.add_scrollbars(view) preview_widget.show() dialog.vbox.add(preview_widget) dialog.set_size_request(480,320) try: result = dialog.run() if result == gtk.RESPONSE_ACCEPT: dialog.check_out else: return finally: dialog.destroy() def set_text(queue): while True: try: text, fraction = queue.get() if text == -1: pbar.hide() elif text is not None: pbar.set_text(text) if fraction is not None: pbar.set_fraction(fraction) except QueueEmpty: pass yield None queue = Queue() thread_progress = GIdleThread(set_text(queue)) thread_progress.start() thread_import = GIdleThread(self.import_datasets(pj, filenames, importer), queue) thread_import.start() thread_import.wait() finally: chooser.destroy() def _cb_new_dataset(self,widget): """ Create a new dataset and switch to its editing window. """ pj = self._check_project() ds = pj.new_dataset() self.edit_dataset(ds) def _cb_create_plot_from_datasets(self, widget): pj = self._check_project() datasets = self.window.treeview.get_selected_datasets() pj.create_plot_from_datasets(datasets) def _cb_add_datasets_to_plot(self, action): pj = self._check_project() (plots, datasets) = self.window.treeview.get_selected_plds() if len(plots) == 1 and len(datasets) > 0: pj.add_datasets_to_plot(datasets, plots[0]) def _cb_delete(self, widget): pj = self._check_project() objects = self.window.treeview.get_selected_objects() pj.remove_objects(objects) def _cb_experimental_plot(self, action): pj = self._check_project() plugin = self.plugins['Default'] plugin.add_experimental_plot(pj) # --- EDIT ------------------------------------------------------------- ### ### TODO: implement cut/copy/paste ### def _cb_edit_cut(self, widget): pass def _cb_edit_copy(self, widget): pass def _cb_edit_paste(self, widget): pass # --- UNDO/REDO -------------------------------------------------------- def _cb_undo(self, widget): pj = self._check_project() pj.undo() def _cb_redo(self, widget): pj = self._check_project() pj.redo() #---------------------------------------------------------------------- # MISC CALLBACKS def _cb_recent_files_clear(self, action): self.clear_recent_files() #---------------------------------------------------------------------- # Simple user I/O # def ask_yes_no(self, msg): dialog = gtk.MessageDialog(parent=self.window, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_YES_NO, message_format=msg) result = dialog.run() dialog.destroy() return result == gtk.RESPONSE_YES def error_message(self, msg): dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK, message_format=unicode(msg)) dialog.run() dialog.destroy()