Various bugfixes to the new way of handling selections.
This commit is contained in:
parent
e0ca48d4b3
commit
ced0e9aa5d
|
@ -70,7 +70,8 @@ class DimListController:
|
||||||
seltree.drag_dest_set(gtk.DEST_DEFAULT_ALL,
|
seltree.drag_dest_set(gtk.DEST_DEFAULT_ALL,
|
||||||
[("GTK_TREE_MODEL_ROW", gtk.TARGET_SAME_APP, 7)],
|
[("GTK_TREE_MODEL_ROW", gtk.TARGET_SAME_APP, 7)],
|
||||||
gtk.gdk.ACTION_LINK)
|
gtk.gdk.ACTION_LINK)
|
||||||
seltree.connect('drag-data-received', self.drag_data_received)
|
|
||||||
|
seltree.connect('drag-data-received', self._drag_data_received)
|
||||||
|
|
||||||
## Set up identifier list
|
## Set up identifier list
|
||||||
idlist.set_model(self.idstore)
|
idlist.set_model(self.idstore)
|
||||||
|
@ -89,10 +90,19 @@ class DimListController:
|
||||||
return i
|
return i
|
||||||
i = self.selstore.iter_next(i)
|
i = self.selstore.iter_next(i)
|
||||||
return None
|
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):
|
def set_project(self, project):
|
||||||
"""Dependency injection."""
|
"""Dependency injection."""
|
||||||
print "setting project"
|
self.project = project
|
||||||
self.dim_names = project.dim_names
|
self.dim_names = project.dim_names
|
||||||
self.update_dims()
|
self.update_dims()
|
||||||
project.add_selection_observer(self)
|
project.add_selection_observer(self)
|
||||||
|
@ -120,11 +130,6 @@ class DimListController:
|
||||||
if not self.get_dimension(dim):
|
if not self.get_dimension(dim):
|
||||||
self.dimstore.insert_after(None, (dim,))
|
self.dimstore.insert_after(None, (dim,))
|
||||||
|
|
||||||
def add_selection(self, dataset, selection):
|
|
||||||
di = self.get_dataset_iter(dataset)
|
|
||||||
values = (selection.title, selection, selection.dims()[0])
|
|
||||||
self.selstore.insert_after(di, None, values)
|
|
||||||
|
|
||||||
def add_dataset(self, dataset):
|
def add_dataset(self, dataset):
|
||||||
di = self.get_dataset_iter(dataset)
|
di = self.get_dataset_iter(dataset)
|
||||||
if not di:
|
if not di:
|
||||||
|
@ -136,12 +141,16 @@ class DimListController:
|
||||||
|
|
||||||
def selection_changed(self, selection):
|
def selection_changed(self, selection):
|
||||||
"""Callback function from Project."""
|
"""Callback function from Project."""
|
||||||
print 'selection changed'
|
|
||||||
|
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()
|
self.update_dims()
|
||||||
|
|
||||||
def dataset_changed(self):
|
def dataset_changed(self):
|
||||||
"""Callback function from Project."""
|
"""Callback function from Project."""
|
||||||
print 'dataset changed'
|
|
||||||
self.update_dims()
|
self.update_dims()
|
||||||
|
|
||||||
## GTK Callbacks.
|
## GTK Callbacks.
|
||||||
|
@ -157,12 +166,13 @@ class DimListController:
|
||||||
def _sel_cursor_changed(self, widget):
|
def _sel_cursor_changed(self, widget):
|
||||||
"Show the list of identifier strings."
|
"Show the list of identifier strings."
|
||||||
p = self.seltree.get_cursor()[0]
|
p = self.seltree.get_cursor()[0]
|
||||||
|
p = self.selstore_filter.convert_path_to_child_path(p)
|
||||||
i = self.selstore.get_iter(p)
|
i = self.selstore.get_iter(p)
|
||||||
obj = self.selstore.get_value(i, 1)
|
obj = self.selstore.get_value(i, 1)
|
||||||
|
|
||||||
id_list = []
|
id_list = []
|
||||||
if isinstance(obj, dataset.Selection):
|
if isinstance(obj, dataset.Selection):
|
||||||
id_list = obj[self._current_dim]
|
id_list = list(obj[self._current_dim])
|
||||||
id_list.sort()
|
id_list.sort()
|
||||||
|
|
||||||
self.idstore.clear()
|
self.idstore.clear()
|
||||||
|
@ -170,7 +180,18 @@ class DimListController:
|
||||||
self.idstore.append((e,))
|
self.idstore.append((e,))
|
||||||
|
|
||||||
def _sel_row_activated(self, widget, path, column):
|
def _sel_row_activated(self, widget, path, column):
|
||||||
pass
|
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])
|
||||||
|
|
||||||
def set_dimension(self, dimname):
|
def set_dimension(self, dimname):
|
||||||
self._current_dim = dimname
|
self._current_dim = dimname
|
||||||
|
@ -182,7 +203,7 @@ class DimListController:
|
||||||
if self.dimlist.get_cursor()[0] != path:
|
if self.dimlist.get_cursor()[0] != path:
|
||||||
self.dimlist.set_cursor(self.dimstore.get_path(dim))
|
self.dimlist.set_cursor(self.dimstore.get_path(dim))
|
||||||
|
|
||||||
def drag_data_received(self, widget, drag_context, x, y,
|
def _drag_data_received(self, widget, drag_context, x, y,
|
||||||
selection, info, timestamp):
|
selection, info, timestamp):
|
||||||
|
|
||||||
treestore, path = selection.tree_get_row_drag_data()
|
treestore, path = selection.tree_get_row_drag_data()
|
||||||
|
@ -191,328 +212,4 @@ class DimListController:
|
||||||
if isinstance(obj, dataset.CategoryDataset):
|
if isinstance(obj, dataset.CategoryDataset):
|
||||||
self.add_dataset(obj)
|
self.add_dataset(obj)
|
||||||
self.set_dimension(obj.get_dim_name(0))
|
self.set_dimension(obj.get_dim_name(0))
|
||||||
|
widget.emit_stop_by_name('drag-data-received')
|
||||||
|
|
||||||
class SelectionTree(gtk.TreeView):
|
|
||||||
"""Shows a list of CategoryDatasets for the selected dimension."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.store = gtk.TreeStore(gobject.TYPE_STRING,
|
|
||||||
gobject.TYPE_PYOBJECT,
|
|
||||||
gobject.TYPE_STRING)
|
|
||||||
|
|
||||||
# Set up filter that shows only the selected dimension
|
|
||||||
self.filter = self.store.filter_new()
|
|
||||||
gtk.TreeView.__init__(self, self.filter)
|
|
||||||
self.filter.set_visible_func(self.dimension_filter)
|
|
||||||
|
|
||||||
renderer = gtk.CellRendererText()
|
|
||||||
sel_column = gtk.TreeViewColumn('Selection', renderer, text=0)
|
|
||||||
self.insert_column(sel_column, 0)
|
|
||||||
|
|
||||||
self.connect('row-activated', self._on_row_activated)
|
|
||||||
self.connect('cursor-changed', self._on_cursor_changed)
|
|
||||||
|
|
||||||
self.drag_dest_set(gtk.DEST_DEFAULT_ALL,
|
|
||||||
[("GTK_TREE_MODEL_ROW", gtk.TARGET_SAME_APP, 7)],
|
|
||||||
gtk.gdk.ACTION_LINK)
|
|
||||||
self.connect('drag-data-received', self.drag_data_received)
|
|
||||||
|
|
||||||
self._identifier_list = None
|
|
||||||
|
|
||||||
self.set_headers_visible(True)
|
|
||||||
self._dimension = None
|
|
||||||
|
|
||||||
|
|
||||||
def set_identifier_list(self, widget):
|
|
||||||
self._identifier_list = widget
|
|
||||||
|
|
||||||
def set_project(self, project):
|
|
||||||
"""Dependency injection. Set the current project."""
|
|
||||||
self.project = project
|
|
||||||
project.add_selection_observer(self)
|
|
||||||
project.add_dataset_observer(self)
|
|
||||||
self.add_selection(None, project.get_selection())
|
|
||||||
|
|
||||||
def get_dataset_iter(self, dataset):
|
|
||||||
i = self.store.get_iter_first()
|
|
||||||
while i:
|
|
||||||
if self.store.get_value(i, 1) == dataset:
|
|
||||||
return i
|
|
||||||
i = self.store.iter_next(i)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def add_selection(self, dataset, selection):
|
|
||||||
if dataset == None:
|
|
||||||
return
|
|
||||||
di = self.get_dataset_iter(dataset)
|
|
||||||
self.store.insert_after(di, None, (selection.title,
|
|
||||||
selection,
|
|
||||||
dataset.dims()[0]))
|
|
||||||
|
|
||||||
def add_dataset(self, dataset):
|
|
||||||
di = self.get_dataset_iter(dataset)
|
|
||||||
if not di:
|
|
||||||
i = self.store.insert_after(None, None, (dataset.get_name(),
|
|
||||||
dataset,
|
|
||||||
dataset.get_dim_name(0)))
|
|
||||||
for selection in dataset.as_selections():
|
|
||||||
self.store.insert_after(i, None, (selection.title,
|
|
||||||
selection,
|
|
||||||
dataset.get_dim_name(0)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def dimension_filter(self, store, i):
|
|
||||||
"""Filters out everything but the selected dimension."""
|
|
||||||
i_dim = store.get_value(i, 2)
|
|
||||||
return i_dim == self._dimension
|
|
||||||
|
|
||||||
def set_dimension(self, dimension):
|
|
||||||
self._dimension = dimension
|
|
||||||
self.filter.refilter()
|
|
||||||
|
|
||||||
def dataset_changed(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def selection_changed(self, selection):
|
|
||||||
pass
|
|
||||||
|
|
||||||
## GTK callbacks
|
|
||||||
def _on_row_activated(self, treeview, path, column):
|
|
||||||
i = self.store.get_iter(path)
|
|
||||||
obj = self.store.get_value(i, 1)
|
|
||||||
if isinstance(obj, dataset.Dataset):
|
|
||||||
self.expand_row(path, True)
|
|
||||||
|
|
||||||
def _on_cursor_changed(self, treeview):
|
|
||||||
p = self.get_cursor()[0]
|
|
||||||
self.scroll_to_cell(p)
|
|
||||||
i = self.store.get_iter(p)
|
|
||||||
obj = self.store.get_value(i, 1)
|
|
||||||
id_list = []
|
|
||||||
if isinstance(obj, dataset.Selection):
|
|
||||||
id_list = obj[self._dimension]
|
|
||||||
|
|
||||||
self._identifier_list.set_identifiers(id_list)
|
|
||||||
|
|
||||||
def drag_data_received(self, widget, drag_context, x, y,
|
|
||||||
selection, info, timestamp):
|
|
||||||
|
|
||||||
treestore, path = selection.tree_get_row_drag_data()
|
|
||||||
iter = treestore.get_iter(path)
|
|
||||||
obj = treestore.get_value(iter,2)
|
|
||||||
if isinstance(obj, dataset.CategoryDataset):
|
|
||||||
self.add_dataset(obj)
|
|
||||||
self.set_dimension(obj.get_dim_name(0))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IdentifierList(gtk.TreeView):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.store = gtk.ListStore(gobject.TYPE_STRING)
|
|
||||||
gtk.TreeView.__init__(self, self.store)
|
|
||||||
self.set_headers_visible(True)
|
|
||||||
|
|
||||||
# Set up identifier column to show active identifiers
|
|
||||||
renderer = gtk.CellRendererText()
|
|
||||||
ids_column = gtk.TreeViewColumn('Identifiers', renderer, text=0)
|
|
||||||
self.insert_column(ids_column, 0)
|
|
||||||
|
|
||||||
def set_identifiers(self, identifiers):
|
|
||||||
"Show the list of identifier strings."
|
|
||||||
self.store.clear()
|
|
||||||
id_list = identifiers
|
|
||||||
id_list.sort()
|
|
||||||
for e in id_list:
|
|
||||||
self.store.append((e,))
|
|
||||||
|
|
||||||
class OldSelectionTree(gtk.TreeView):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
|
|
||||||
gtk.TreeView.__init__(self, self.store)
|
|
||||||
|
|
||||||
renderer = gtk.CellRendererText()
|
|
||||||
sel_column = gtk.TreeViewColumn('Selection', renderer, text=0)
|
|
||||||
self.insert_column(sel_column, 0)
|
|
||||||
|
|
||||||
self.connect('row-activated', self._on_row_activated)
|
|
||||||
self.connect('cursor-changed', self._on_cursor_changed)
|
|
||||||
|
|
||||||
self._identifier_list = None
|
|
||||||
self._dim_list = {}
|
|
||||||
|
|
||||||
# A mapping of selection names to selection lines.
|
|
||||||
self._selections = []
|
|
||||||
|
|
||||||
self.set_headers_visible(True)
|
|
||||||
self._current_dim = None
|
|
||||||
self._current_selection = None
|
|
||||||
|
|
||||||
# Set up context menu
|
|
||||||
self._menu = SimpleMenu()
|
|
||||||
self._menu.add_simple_item('Select', self._on_set_selection)
|
|
||||||
|
|
||||||
self.connect('popup_menu', self._on_popup_menu)
|
|
||||||
self.connect('button_press_event', self._on_button_press_event)
|
|
||||||
|
|
||||||
def set_identifier_list(self, identifier_list):
|
|
||||||
"""Dependency injection. Sets the widget that should be used to
|
|
||||||
display the selected identifiers."""
|
|
||||||
self._identifier_list = identifier_list
|
|
||||||
|
|
||||||
def set_project(self, project):
|
|
||||||
"""Dependency injection. Set the current project."""
|
|
||||||
self.project = project
|
|
||||||
project.add_selection_observer(self)
|
|
||||||
project.add_dataset_observer(self)
|
|
||||||
self.update_dims(project.dim_names)
|
|
||||||
self.add_selection(project.get_selection())
|
|
||||||
|
|
||||||
def selection_changed(self, selection):
|
|
||||||
"""Callback on selection update"""
|
|
||||||
self.update_dims(selection.keys())
|
|
||||||
self.add_selection(selection)
|
|
||||||
self._update_current_dim()
|
|
||||||
|
|
||||||
def get_selection_iter(self, dimname, selection):
|
|
||||||
i = self._dim_list[dimname]
|
|
||||||
if not i:
|
|
||||||
return None
|
|
||||||
|
|
||||||
children = self.store.iter_children(i)
|
|
||||||
if not children:
|
|
||||||
return None
|
|
||||||
|
|
||||||
while children:
|
|
||||||
if self.store.get_value(children, 1) == selection:
|
|
||||||
return self.store.get_value(children, 1)
|
|
||||||
children = self.store.iter_next(children)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def dataset_changed(self):
|
|
||||||
"""Callback when new datasets are created"""
|
|
||||||
self.selection_changed(self.project.get_selection())
|
|
||||||
for sel in self.project.selections:
|
|
||||||
if not sel in self._selections:
|
|
||||||
self._selections.append(sel)
|
|
||||||
self.add_selection(sel)
|
|
||||||
|
|
||||||
def update_dims(self, dim_list):
|
|
||||||
"""Update the list of dimensions shown"""
|
|
||||||
for dim in dim_list:
|
|
||||||
if not self._dim_list.has_key(dim):
|
|
||||||
d = self.store.insert_after(None, None, (dim, None))
|
|
||||||
self._dim_list[dim] = d
|
|
||||||
|
|
||||||
def add_selection(self, selection):
|
|
||||||
for dim in selection.dims():
|
|
||||||
if not self.get_selection_iter(dim, selection):
|
|
||||||
d = self._dim_list[dim]
|
|
||||||
i = self.store.insert_after(d, None, (selection.title, selection))
|
|
||||||
|
|
||||||
def _update_current_dim(self):
|
|
||||||
id_list = []
|
|
||||||
if self._current_dim and self._current_selection:
|
|
||||||
id_list = self._current_selection[self._current_dim]
|
|
||||||
self._identifier_list.set_identifiers(id_list)
|
|
||||||
|
|
||||||
# Callbacks
|
|
||||||
def _on_row_activated(self, treeview, path, column):
|
|
||||||
self.project.set_selection(self._current_dim,
|
|
||||||
self._current_selection[self._current_dim])
|
|
||||||
|
|
||||||
def _on_cursor_changed(self, treeview):
|
|
||||||
cursor = self.get_cursor()[0]
|
|
||||||
i = self.store.get_iter(cursor)
|
|
||||||
p = self.store.iter_parent(i)
|
|
||||||
if p == None:
|
|
||||||
self._current_dim = None
|
|
||||||
else:
|
|
||||||
self._current_dim = self.store.get_value(p, 0)
|
|
||||||
self._current_selection = self.store.get_value(i, 1)
|
|
||||||
self._update_current_dim()
|
|
||||||
|
|
||||||
def _on_set_selection(self, *rest):
|
|
||||||
if not self._current_selection:
|
|
||||||
return
|
|
||||||
self.project.set_selection(self._current_dim,
|
|
||||||
self._current_selection[self._current_dim])
|
|
||||||
|
|
||||||
def _on_button_press_event(self, widget, event):
|
|
||||||
if event.button == 3:
|
|
||||||
self._menu.popup(None, None, None, event.button, event.time)
|
|
||||||
|
|
||||||
def _on_popup_menu(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class DimList(gtk.TreeView):
|
|
||||||
"""A widget that displays the list of dimensions in the program."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.store = gtk.ListStore(gobject.TYPE_STRING)
|
|
||||||
gtk.TreeView.__init__(self, self.store)
|
|
||||||
|
|
||||||
renderer = gtk.CellRendererText()
|
|
||||||
dim_column = gtk.TreeViewColumn('Dimension', renderer, text=0)
|
|
||||||
self.insert_column(dim_column, 0)
|
|
||||||
self._selection_list = None
|
|
||||||
|
|
||||||
self.connect('row-activated', self._on_row_activated)
|
|
||||||
self.connect('cursor-changed', self._on_cursor_changed)
|
|
||||||
|
|
||||||
def set_project(self, project):
|
|
||||||
"""Dependency injection. The widget list needs the project, but
|
|
||||||
there is not necessarily a current project when the application
|
|
||||||
is started."""
|
|
||||||
|
|
||||||
self.dim_names = project.dim_names
|
|
||||||
self.update_dims()
|
|
||||||
project.add_selection_observer(self)
|
|
||||||
project.add_dataset_observer(self)
|
|
||||||
|
|
||||||
def set_selection_tree(self, selection_tree):
|
|
||||||
"""Dependency injection. It is not known whether the selection tree
|
|
||||||
or the dimension list is created first."""
|
|
||||||
self.selection_tree = selection_tree
|
|
||||||
|
|
||||||
def get_dimension(self, dim):
|
|
||||||
"""Returns the iterator to the dimension with the given name, or
|
|
||||||
None if not found."""
|
|
||||||
|
|
||||||
i = self.store.get_iter_first()
|
|
||||||
while i:
|
|
||||||
if self.store.get(i, 0)[0] == dim:
|
|
||||||
return i
|
|
||||||
i = self.store.iter_next(i)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def update_dims(self):
|
|
||||||
"""Update the list of dimensions shown"""
|
|
||||||
for dim in self.dim_names:
|
|
||||||
if not self.get_dimension(dim):
|
|
||||||
self.store.insert_after(None, (dim,))
|
|
||||||
|
|
||||||
def selection_changed(self, selection):
|
|
||||||
"""Callback function from Project."""
|
|
||||||
self.update_dims()
|
|
||||||
|
|
||||||
def dataset_changed(self):
|
|
||||||
"""Callback function from Project."""
|
|
||||||
self.update_dims()
|
|
||||||
|
|
||||||
## GTK Callbacks.
|
|
||||||
def _on_cursor_changed(self, widget):
|
|
||||||
cursor = self.get_cursor()[0]
|
|
||||||
print cursor
|
|
||||||
i = self.store.get_iter(cursor)
|
|
||||||
row = self.store.get_value(i, 0)
|
|
||||||
self.selection_tree.set_dimension(row)
|
|
||||||
self.scroll_to_cell(cursor)
|
|
||||||
|
|
||||||
|
|
||||||
def _on_row_activated(self, widget, path, column):
|
|
||||||
self.selection_tree.grab_focus()
|
|
||||||
|
|
||||||
|
|
Reference in New Issue