From 2cc4e86b0a93b1aec5fd44442da33823bad5eb9d Mon Sep 17 00:00:00 2001 From: einarr Date: Wed, 21 Feb 2007 00:00:26 +0000 Subject: [PATCH] Split selections.py into three different controller classes, one for each list. There is still some work to be done, and bugs to be fixed. --- fluents/fluents.py | 11 +- fluents/selections.py | 529 +++++++++++++++++++++++++----------------- 2 files changed, 329 insertions(+), 211 deletions(-) diff --git a/fluents/fluents.py b/fluents/fluents.py index 64fe361..2c959d8 100644 --- a/fluents/fluents.py +++ b/fluents/fluents.py @@ -152,9 +152,12 @@ class FluentApp: gtk.glade.set_custom_handler(self.custom_object_factory) self.widget_tree = gtk.glade.XML(GLADEFILENAME, 'appwindow') self.workflow = wf(self) - self.dimlistcontroller = selections.DimListController(self['dim_list'], - self['selection_tree'], - self['identifier_list']) + + self.idlist_crt = selections.IdListController(self['identifier_list']) + self.sellist_crt = selections.SelectionListController(self['selection_tree'], + self.idlist_crt) + self.dimlist_crt = selections.DimListController(self['dim_list'], + self.sellist_crt) def init_gui(self): self['appwindow'].set_size_request(800, 600) @@ -203,7 +206,7 @@ class FluentApp: project.project = proj self.workflow.add_project(proj) self.navigator_view.add_project(proj) - self.dimlistcontroller.set_project(proj) + self.dimlist_crt.set_project(proj) def set_workflow(self, workflow): self.workflow = workflow diff --git a/fluents/selections.py b/fluents/selections.py index 609646a..4df9b60 100644 --- a/fluents/selections.py +++ b/fluents/selections.py @@ -21,244 +21,126 @@ class SimpleMenu(gtk.Menu): item.show() -class DimListController: - def __init__(self, dimlist, seltree, idlist): +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,) - self._current_dim = None - - ## dimstore is a list of all dimensions in the application - self.dimstore = gtk.ListStore(gobject.TYPE_STRING) - - ## 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. - self.selstore = gtk.TreeStore(gobject.TYPE_STRING, - gobject.TYPE_PYOBJECT, - gobject.TYPE_STRING) - self.selstore_filter = self.selstore.filter_new() - self.selstore_filter.set_visible_func(self.dimension_filter) - - ## idstore is a list of currently selected identifiers - ## the list is cleared and rebuilt each time a new selection is - ## focused. - self.idstore = gtk.ListStore(gobject.TYPE_STRING) - - ## The widgets we are controlling - self.dimlist = dimlist - self.seltree = seltree - self.idlist = idlist - - ## 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) - - ## Set up selection tree - seltree.set_model(self.selstore_filter) - - renderer = gtk.CellRendererText() - sel_column = gtk.TreeViewColumn('Selection', renderer, text=0) - seltree.insert_column(sel_column, 0) - - seltree.connect('row-activated', self._sel_row_activated) - seltree.connect('cursor-changed', self._sel_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) - ## Set up identifier list - idlist.set_model(self.idstore) + 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._idlist_button_pressed) + idlist.connect('button-press-event', self._button_pressed) ## Set up identifier list context menu - self._idlist_menu = SimpleMenu() - self._idlist_menu.add_simple_item('Import...', self._on_idlist_import) - self._idlist_menu.add_simple_item('Export...', self._on_idlist_export) + menu = self._menu = SimpleMenu() + menu.add_simple_item('Import...', self._on_import_list) + menu.add_simple_item('Export...', self._on_export_list) - i = gtk.MenuItem('Show annotations') - self._idlist_menu.append(i) - i.show() - self._idlist_menu_ann = i - - - def get_dataset_iter(self, dataset): - """Returns the iterator to the selection tree row containing a - given dataset.""" - i = self.selstore.get_iter_first() - while i: - if self.selstore.get_value(i, 1) == dataset: - return i - i = self.selstore.iter_next(i) - return None - - def get_current_selection_iter(self, selection, dimension): - i = self.selstore.get_iter_first() - while i: - if self.selstore.get_value(i, 1) == selection: - if self.selstore.get_value(i, 2) == dimension: - return i - i = self.selstore.iter_next(i) - return None - - def set_project(self, project): - """Dependency injection.""" - self.project = project - self.dim_names = project.dim_names - self.update_dims() - project.add_selection_observer(self) - 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 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 - - 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 add_dataset(self, dataset): - di = self.get_dataset_iter(dataset) - if not di: - values = (dataset.get_name(), dataset, dataset.get_dim_name(0)) - i = self.selstore.insert_after(None, None, values) - for selection in dataset.as_selections(): - values = (selection.title, selection, dataset.get_dim_name(0)) - self.selstore.insert_after(i, None, values) - - def selection_changed(self, dim_name, selection): - """Callback function from Project.""" - - for dim in selection.dims(): - if not self.get_current_selection_iter(selection, dim): - values = (selection.title, selection, dim) - self.selstore.insert_after(None, None, values) - - self.update_dims() - - def dataset_changed(self): - """Callback function from Project.""" - self.update_dims() - - ## 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.grab_focus() - - def _sel_cursor_changed(self, widget): - "Show the list of identifier strings." - p = self.seltree.get_cursor()[0] - p = self.selstore_filter.convert_path_to_child_path(p) - i = self.selstore.get_iter(p) - obj = self.selstore.get_value(i, 1) - - id_list = [] - if isinstance(obj, dataset.Selection): - id_list = list(obj[self._current_dim]) - id_list.sort() - - self.idstore.clear() - for e in id_list: - self.idstore.append((e,)) - - def _sel_row_activated(self, widget, path, column): - path = self.selstore_filter.convert_path_to_child_path(path) - i = self.selstore.get_iter(path) - obj = self.selstore.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._current_dim, - obj[self._current_dim]) - + item = gtk.MenuItem('Show annotations') + menu.append(item) + item.show() + self._menu_ann = item + + ## + ## Public interface + ## def set_dimension(self, dimname): - self._current_dim = dimname - self.selstore_filter.refilter() + """Set dimension""" + if dimname == self._dimension: + return - dim = self.get_dimension(self._current_dim) - path = self.dimstore.get_path(dim) + 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.dimlist.get_cursor()[0] != path: - self.dimlist.set_cursor(self.dimstore.get_path(dim)) + if self.annotation == None: + self._idlist.remove_column(self._annotation_colum) + else: + for x in self._idstore: + print "set annotation:", x - 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.set_dimension(obj.get_dim_name(0)) - widget.emit_stop_by_name('drag-data-received') + 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): - dim_h = annotations.get_dim_handler(self._current_dim) + """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._idlist_menu_ann.set_sensitive(False) + self._menu_ann.set_sensitive(False) else: annotations_menu = gtk.Menu() - self._idlist_menu_ann.set_sensitive(True) - ann_names = annotations.get_dim_handler(self._current_dim).get_annotation_names() + self._menu_ann.set_sensitive(True) + ann_names = annotations.get_dim_handler(self._dimension).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._idlist_menu_ann.set_submenu(annotations_menu) + self._menu_ann.set_submenu(annotations_menu) - def _idlist_button_pressed(self, widget, event): - if event.button == 3: - self._update_annotations_menu() - self._idlist_menu.popup(None, None, None, event.button, event.time) - - def _popup_idlist_menu(self, *rest): + ## + ## GTK Callbacks + ## + def _popup_menu(self, *rest): self._update_annotations_menu() - self._idlist_menu.popup(None, None, None, 0, 0) + self._menu.popup(None, None, None, 0, 0) def _on_annotation_activated(self, menuitem, annotation): - print "Congratulations. You chouse %s" % annotation + self.set_annotation(annotation) - def _on_idlist_export(self, menuitem): + 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_idlist_import(self, menuitem): + 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, @@ -274,5 +156,238 @@ class DimListController: print "unknown; ", retval dialog.destroy() - pass + +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_dimension(self, dim): + print "SelTreeController.set_dimension(%s)" % 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): + print " inserting into store" + values = (selection.title, selection, dim) + store.insert_after(None, None, values) + + def add_dataset(self, dataset): + 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.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) + print "DimListController.set_dimension()" + + 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)