import logger, dataset import annotations import pygtk import gtk import gtk.gdk import gtk.glade import gnome import gnome.ui import gobject class SimpleMenu(gtk.Menu): def __init__(self): gtk.Menu.__init__(self) def add_simple_item(self, title, function): item = gtk.MenuItem(title) item.connect('activate', function) self.append(item) item.show() class IdListController: """Controller class for the identifier list.""" def __init__(self, idlist): self._idlist = idlist # dimname: current_annotation_name self._annotation = {} # current dimension self._dimension = None # id, annotation self._idstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,) ## Set up identifier list idlist.set_model(self._idstore) renderer = gtk.CellRendererText() dim_column = gtk.TreeViewColumn('Identifiers', renderer, text=0) idlist.insert_column(dim_column, 0) idlist.connect('button-press-event', self._button_pressed) ## Set up identifier list context menu menu = self._menu = SimpleMenu() menu.add_simple_item('Import...', self._on_import_list) menu.add_simple_item('Export...', self._on_export_list) item = gtk.MenuItem('Show annotations') menu.append(item) item.show() self._menu_ann = item ## ## Public interface ## def set_dimension(self, dimname): """Set dimension""" if dimname == self._dimension: return if not self._annotation.has_key(dimname): self._annotation[dimname] = None self._dimension = dimname def set_annotation(self, annotation): """Set the displayed annotation to annotation. If annotation is None, the annotation column is hidden. Otherwise the annotation column is shown and filled with values from the given annotation field.""" if annotation == self._annotation: return if self.annotation == None: self._idlist.remove_column(self._annotation_colum) else: for x in self._idstore: print "set annotation:", x def set_selection(self, selection): """Set the selection to be displayed. The selection is not stored, the values are copied into the TreeStore""" self._idstore.clear() id_list = list(selection[self._dimension]) idlist = list(selection[self._dimension]) if self._annotation[self._dimension] != None: annlist = annotations.get_dim_annotations(self._dimension, self._annotation[self._dimension], idlist) for id, ann in zip(idlist, annlist): self._idstore.append((id, ann)) else: for e in idlist: self._idstore.append((e, None)) ## ## Private interface ## def _update_annotations_menu(self): """Updates the annotations menu with the available annotations for the current dim.""" dim_h = annotations.get_dim_handler(self._dimension) if not dim_h: print "No annotations etc" self._menu_ann.set_sensitive(False) else: annotations_menu = gtk.Menu() self._menu_ann.set_sensitive(True) dh = annotations.get_dim_handler(self._dimension) ann_names = dh.get_annotation_names() for ann in ann_names: item = gtk.MenuItem(ann) item.connect('activate', self._on_annotation_activated, ann) annotations_menu.append(item) item.show() self._menu_ann.set_submenu(annotations_menu) ## ## GTK Callbacks ## def _popup_menu(self, *rest): self._update_annotations_menu() self._menu.popup(None, None, None, 0, 0) def _on_annotation_activated(self, menuitem, annotation): self.set_annotation(annotation) def _button_pressed(self, widget, event): if event.button == 3: self._update_annotations_menu() self._menu.popup(None, None, None, event.button, event.time) def _on_export_list(self, menuitem): print "export stuff" def _on_import_list(self, menuitem): dialog = gtk.FileChooserDialog('Load annotations') dialog.set_action(gtk.FILE_CHOOSER_ACTION_OPEN) dialog.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK) dialog.set_select_multiple(True) retval = dialog.run() if retval in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: pass elif retval == gtk.RESPONSE_OK: for filename in dialog.get_filenames(): annotations.read_annotations_file(filename) else: print "unknown; ", retval dialog.destroy() class SelectionListController: def __init__(self, seltree, idlist_controller): self._seltree = seltree self._sel_stores = {} self._dimension = None self._idlist_controller = idlist_controller renderer = gtk.CellRendererText() sel_column = gtk.TreeViewColumn('Selection', renderer, text=0) seltree.insert_column(sel_column, 0) seltree.connect('row-activated', self._on_row_activated) seltree.connect('cursor-changed', self._on_cursor_changed) seltree.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("GTK_TREE_MODEL_ROW", gtk.TARGET_SAME_APP, 7)], gtk.gdk.ACTION_LINK) seltree.connect('drag-data-received', self._drag_data_received) ## ## Public interface ## def set_dimlist_controller(self, dimlist_controller): self._dimlist_controller = dimlist_controller def set_dimension(self, dim): self._ensure_selection_store(dim) self._seltree.set_model(self._sel_stores[dim]) self._idlist_controller.set_dimension(dim) self._dimension = dim def selection_changed(self, dimname, selection): """Callback function from Project.""" print "selection changed" for dim in selection.dims(): self._ensure_selection_store(dim) store = self._sel_stores[dim] if not self._get_current_selection_iter(selection, dim): values = (selection.title, selection, dim) store.insert_after(None, None, values) def add_dataset(self, dataset): self._ensure_selection_store(dataset.get_dim_name(0)) store = self._sel_stores[dataset.get_dim_name(0)] di = self._get_dataset_iter(dataset) if not di: values = (dataset.get_name(), dataset, dataset.get_dim_name(0)) i = store.insert_after(None, None, values) for selection in dataset.as_selections(): values = (selection.title, selection, dataset.get_dim_name(0)) store.insert_after(i, None, values) ## ## Private interface ## def _add_selection_store(self, dim): """Add a new gtk.TreeStore for the selections on a dimension.""" # Create new store ## Two types of lines, one for CategoryDatasets and one for ## Selections. The elements are title, link to dataset or selection ## and the name of the dimension. store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT, gobject.TYPE_STRING) # Set selection store for this dimension self._sel_stores[dim] = store def _ensure_selection_store(self, dim): """Ensure that the object has a gtk.TreeStore for the given dimension""" # Do not overwrite existing stores if self._sel_stores.has_key(dim): return self._add_selection_store(dim) def _get_dataset_iter(self, ds): """Returns the iterator to the selection tree row containing a given dataset.""" store = self._sel_stores[ds.get_dim_name(0)] i = store.get_iter_first() while i: if store.get_value(i, 1) == ds: return i i = store.iter_next(i) return None def _get_current_selection_iter(self, selection, dimension): if not self._sel_stores.has_key(dimension): return None store = self._sel_stores[dimension] i = store.get_iter_first() while i: if store.get_value(i, 1) == selection: if store.get_value(i, 2) == dimension: return i i = store.iter_next(i) return None ## ## GTK callbacks ## def _drag_data_received(self, widget, drag_context, x, y, selection, info, timestamp): treestore, path = selection.tree_get_row_drag_data() i = treestore.get_iter(path) obj = treestore.get_value(i, 2) if isinstance(obj, dataset.CategoryDataset): self.add_dataset(obj) self._dimlist_controller.set_dimension(obj.get_dim_name(0)) widget.emit_stop_by_name('drag-data-received') def _on_cursor_changed(self, widget): "Show the list of identifier strings." store = self._sel_stores[self._dimension] p = self._seltree.get_cursor()[0] i = store.get_iter(p) obj = store.get_value(i, 1) if isinstance(obj, dataset.Selection): self._idlist_controller.set_selection(obj) def _on_row_activated(self, widget, path, column): store = self._sel_stores[self._dimension] i = store.get_iter(path) obj = store.get_value(i, 1) if isinstance(obj, dataset.Dataset): seltree = self._seltree if seltree.row_expanded(path): seltree.collapse_row(path) else: seltree.expand_row(path, True) elif isinstance(obj, dataset.Selection): self.project.set_selection(self._dimension, obj[self._dimension]) class DimListController: def __init__(self, dimlist, seltree_controller): self._current_dim = None self._seltree_controller = seltree_controller ## dimstore is a list of all dimensions in the application self.dimstore = gtk.ListStore(gobject.TYPE_STRING) ## The widgets we are controlling self.dimlist = dimlist ## Set up dimensions list dimlist.set_model(self.dimstore) renderer = gtk.CellRendererText() dim_column = gtk.TreeViewColumn('Dimension', renderer, text=0) dimlist.insert_column(dim_column, 0) dimlist.connect('row-activated', self._dim_row_activated) dimlist.connect('cursor-changed', self._dim_cursor_changed) ## ## Public interface ## def set_project(self, project): """Dependency injection.""" self.project = project self.dim_names = project.dim_names self.update_dims() project.add_selection_observer(self._seltree_controller) project.add_dataset_observer(self) def get_dimension(self, dim): """Returns the iterator to the dimension with the given name, or None if not found.""" i = self.dimstore.get_iter_first() while i: if self.dimstore.get_value(i, 0) == dim: return i i = self.dimstore.iter_next(i) return None def set_dimension(self, dimname): self._current_dim = dimname dim = self.get_dimension(self._current_dim) path = self.dimstore.get_path(dim) if self.dimlist.get_cursor()[0] != path: self.dimlist.set_cursor(self.dimstore.get_path(dim)) self._seltree_controller.set_dimension(dimname) def dataset_changed(self): """Callback function from Project.""" self.update_dims() ## ## Private interface ## def update_dims(self): """Update the list of dimensions shown""" for dim in self.dim_names: if not self.get_dimension(dim): self.dimstore.insert_after(None, (dim,)) def _dimension_filter(self, store, row): """Filters out everything but the selected dimension.""" row_dim = store.get_value(row, 2) return row_dim == self._current_dim ## ## GTK Callbacks. ## def _dim_cursor_changed(self, widget): cursor = self.dimlist.get_cursor()[0] i = self.dimstore.get_iter(cursor) row = self.dimstore.get_value(i, 0) self.set_dimension(row) def _dim_row_activated(self, widget, path, column): self._seltree_controller.set_dimension(dim)