diff options
author | Jesús Eduardo <heckyel@hyperbola.info> | 2018-01-14 23:08:27 -0500 |
---|---|---|
committer | Jesús Eduardo <heckyel@hyperbola.info> | 2018-01-14 23:08:27 -0500 |
commit | 8fdbc96269d6e98d332affcb66c3e8f142fb33b6 (patch) | |
tree | a2adcd1d52fc78678636354bbdb3b5bcdcbd5272 /lvc/widgets | |
parent | 14ef3bac9141de514205db5c35abd634151a7dbd (diff) | |
download | librevideoconverter-8fdbc96269d6e98d332affcb66c3e8f142fb33b6.tar.lz librevideoconverter-8fdbc96269d6e98d332affcb66c3e8f142fb33b6.tar.xz librevideoconverter-8fdbc96269d6e98d332affcb66c3e8f142fb33b6.zip |
pep8 en lvc/widgets/gtk/tableview.py
Diffstat (limited to 'lvc/widgets')
-rw-r--r-- | lvc/widgets/gtk/tableview.py | 187 |
1 files changed, 108 insertions, 79 deletions
diff --git a/lvc/widgets/gtk/tableview.py b/lvc/widgets/gtk/tableview.py index df66990..252e7bc 100644 --- a/lvc/widgets/gtk/tableview.py +++ b/lvc/widgets/gtk/tableview.py @@ -38,14 +38,9 @@ import gobject import gtk from collections import namedtuple -# These are probably wrong, and are placeholders for now, until custom headers -# are also implemented for GTK. -CUSTOM_HEADER_HEIGHT = 25 -HEADER_HEIGHT = 25 - from lvc import signals from lvc.errors import (WidgetActionError, WidgetDomainError, - WidgetRangeError, WidgetNotReadyError) + WidgetRangeError, WidgetNotReadyError) from lvc.widgets.tableselection import SelectionOwnerMixin from lvc.widgets.tablescroll import ScrollbarOwnerMixin import drawing @@ -56,11 +51,16 @@ from .layoutmanager import LayoutManager from .weakconnect import weak_connect from .tableviewcells import GTKCustomCellRenderer +# These are probably wrong, and are placeholders for now, until custom headers +# are also implemented for GTK. +CUSTOM_HEADER_HEIGHT = 25 +HEADER_HEIGHT = 25 PathInfo = namedtuple('PathInfo', 'path column x y') Rect = namedtuple('Rect', 'x y width height') _album_view_gtkrc_installed = False + def _install_album_view_gtkrc(): """Hack for styling GTKTreeView for the album view widget. @@ -84,10 +84,12 @@ def _install_album_view_gtkrc(): gtk.rc_parse_string(rc_string) _album_view_gtkrc_installed = True + def rect_contains_point(rect, x, y): return ((rect.x <= x < rect.x + rect.width) and (rect.y <= y < rect.y + rect.height)) + class TreeViewScrolling(object): def __init__(self): self.scrollbars = [] @@ -95,9 +97,9 @@ class TreeViewScrolling(object): self.restoring_scroll = None self.connect('parent-set', self.on_parent_set) self.scroller = None - # hack necessary because of our weird widget hierarchy (GTK doesn't deal - # well with the Scroller's widget not being the direct parent of the - # TableView's widget.) + # hack necessary because of our weird widget hierarchy + # (GTK doesn't deal well with the Scroller's widget + # not being the direct parent of the TableView's widget.) self._coords_working = False def scroll_range_changed(self): @@ -116,7 +118,7 @@ class TreeViewScrolling(object): # scrolled return False real_pos = self.scrollbars[1].get_value() - return abs(auto_pos - real_pos) > 5 # allowing some fuzziness + return abs(auto_pos - real_pos) > 5 # allowing some fuzziness @property def position_set(self): @@ -133,7 +135,8 @@ class TreeViewScrolling(object): return self.scroller = window scrollbars = tuple(bar.get_adjustment() - for bar in (window.get_hscrollbar(), window.get_vscrollbar())) + for bar in (window.get_hscrollbar(), + window.get_vscrollbar())) self.scrollbars = scrollbars for i, bar in enumerate(scrollbars): weak_connect(bar, 'changed', self.on_scroll_range_changed, i) @@ -154,7 +157,8 @@ class TreeViewScrolling(object): """Restore the scrollbars to a remembered state.""" try: self.scroll_positions = tuple(self._clip_pos(adj, x) - for adj, x in zip(self.scrollbars, scroll_position)) + for adj, x in zip(self.scrollbars, + scroll_position)) except WidgetActionError, error: logging.debug("can't scroll yet: %s", error.reason) # try again later @@ -215,12 +219,14 @@ class TreeViewScrolling(object): if top < vadjustment.value: vadjustment.set_value(max(vadjustment.lower, top)) + class MiroTreeView(gtk.TreeView, TreeViewScrolling): """Extends the GTK TreeView widget to help implement TableView https://develop.participatoryculture.org/index.php/WidgetAPITableView""" # Add a tiny bit of padding so that the user can drag feeds below # the table, i.e. to the bottom row, as a top-level PAD_BOTTOM = 3 + def __init__(self): gtk.TreeView.__init__(self) TreeViewScrolling.__init__(self) @@ -275,6 +281,7 @@ class MiroTreeView(gtk.TreeView, TreeViewScrolling): gobject.type_register(MiroTreeView) + class HotspotTracker(object): """Handles tracking hotspots. https://develop.participatoryculture.org/index.php/WidgetAPITableView""" @@ -333,20 +340,21 @@ class HotspotTracker(object): if rect_contains_point(cell_area, self.x, self.y): model = self.treeview.get_model() self.renderer.cell_data_func(self.column, self.renderer._renderer, - model, self.iter, self.attr_map) + model, self.iter, self.attr_map) style = drawing.DrawingStyle(self.treeview_wrapper, - use_base_color=True, state=self.calc_cell_state()) + use_base_color=True, + state=self.calc_cell_state()) x = self.x - cell_area.x y = self.y - cell_area.y - return self.renderer.hotspot_test(style, - self.treeview_wrapper.layout_manager, - x, y, cell_area.width, cell_area.height) + return self.renderer.hotspot_test( + style, self.treeview_wrapper.layout_manager, + x, y, cell_area.width, cell_area.height) else: return None def update_hit(self): if self.is_for_context_menu(): - return # we always keep hit = True for this one + return # we always keep hit = True for this one old_hit = self.hit self.hit = (self.calc_hotspot() == self.name) if self.hit != old_hit: @@ -360,7 +368,9 @@ class HotspotTracker(object): x, y = self.treeview.tree_to_widget_coords(cell_area.x, cell_area.y) self.treeview.queue_draw_area(x, y, - cell_area.width, cell_area.height) + cell_area.width, + cell_area.height) + class TableColumn(signals.SignalEmitter): """A single column of a TableView. @@ -371,6 +381,7 @@ class TableColumn(signals.SignalEmitter): """ # GTK hard-codes 4px of padding for each column FIXED_PADDING = 4 + def __init__(self, title, renderer, header=None, **attrs): # header widget not used yet in GTK (#15800) signals.SignalEmitter.__init__(self) @@ -435,6 +446,7 @@ class TableColumn(signals.SignalEmitter): """ return self._column.get_sort_order() == gtk.SORT_ASCENDING + class GTKSelectionOwnerMixin(SelectionOwnerMixin): """GTK-specific methods for selection management. @@ -458,6 +470,7 @@ class GTKSelectionOwnerMixin(SelectionOwnerMixin): def _get_selected_iters(self): iters = [] + def collect(treemodel, path, iter_): iters.append(iter_) self.selection.selected_foreach(collect) @@ -491,7 +504,7 @@ class GTKSelectionOwnerMixin(SelectionOwnerMixin): return self._model.get_iter_from_string(string) except ValueError: raise WidgetDomainError( - "model iters", string, "%s other iters" % len(self.model)) + "model iters", string, "%s other iters" % len(self.model)) def select_path(self, path): self.selection.select_path(path) @@ -499,7 +512,7 @@ class GTKSelectionOwnerMixin(SelectionOwnerMixin): def _validate_iter(self, iter_): if self.get_path(iter_) is None: raise WidgetDomainError( - "model iters", iter_, "%s other iters" % len(self.model)) + "model iters", iter_, "%s other iters" % len(self.model)) real_model = self._widget.get_model() if not real_model: raise WidgetActionError("no model") @@ -517,9 +530,10 @@ class GTKSelectionOwnerMixin(SelectionOwnerMixin): # XXX: is there a way to clear the cursor? return path_as_string = ':'.join(str(component) for component in path) - with self.preserving_selection(): # set_cursor() messes up the selection + with self.preserving_selection(): # set_cursor() messes up the selection self._widget.set_cursor(path_as_string) + class DNDHandlerMixin(object): """TableView row DnD. @@ -539,7 +553,7 @@ class DNDHandlerMixin(object): self.wrapped_widget_connect('drag-leave', self.on_drag_leave) self.wrapped_widget_connect('drag-drop', self.on_drag_drop) self.wrapped_widget_connect('drag-data-received', - self.on_drag_data_received) + self.on_drag_data_received) self.wrapped_widget_connect('unrealize', self.on_drag_unrealize) def set_drag_source(self, drag_source): @@ -553,10 +567,10 @@ class DNDHandlerMixin(object): self.drag_dest = drag_dest if drag_dest is not None: targets = self._gtk_target_list(drag_dest.allowed_types()) - self._widget.enable_model_drag_dest(targets, - drag_dest.allowed_actions()) - self._widget.drag_dest_set(0, targets, - drag_dest.allowed_actions()) + self._widget.enable_model_drag_dest( + targets, drag_dest.allowed_actions()) + self._widget.drag_dest_set( + 0, targets, drag_dest.allowed_actions()) else: self._widget.unset_rows_drag_dest() self._widget.drag_dest_unset() @@ -589,8 +603,8 @@ class DNDHandlerMixin(object): # This makes it more natural for the user to drag a block of # selected items. renderer = path_info.column.get_cell_renderers()[0] - if (not self._x_coord_in_expander(treeview, path_info) - and not isinstance(renderer, GTKCheckboxCellRenderer)): + if (not self._x_coord_in_expander(treeview, path_info) and not + isinstance(renderer, GTKCheckboxCellRenderer)): self.delaying_press = True # grab keyboard focus since we handled the event self.focus() @@ -604,8 +618,8 @@ class DNDHandlerMixin(object): self.drag_data = {} def find_type(self, drag_context): - return self._widget.drag_dest_find_target(drag_context, - self._widget.drag_dest_get_target_list()) + return self._widget.drag_dest_find_target( + drag_context, self._widget.drag_dest_get_target_list()) def calc_positions(self, x, y): """Given x and y coordinates, generate a list of drop positions to @@ -625,7 +639,7 @@ class DNDHandlerMixin(object): iter_ = model.get_iter(gtk_path) if gtk_position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE, - gtk.TREE_VIEW_DROP_INTO_OR_AFTER): + gtk.TREE_VIEW_DROP_INTO_OR_AFTER): yield (iter_, -1, gtk_path, gtk_position) if hasattr(model, 'iter_is_valid'): @@ -634,7 +648,7 @@ class DNDHandlerMixin(object): parent_iter = model.iter_parent(iter_) position = gtk_path[-1] if gtk_position in (gtk.TREE_VIEW_DROP_BEFORE, - gtk.TREE_VIEW_DROP_INTO_OR_BEFORE): + gtk.TREE_VIEW_DROP_INTO_OR_BEFORE): # gtk gave us a "before" position, no need to change it yield (parent_iter, position, gtk_path, gtk.TREE_VIEW_DROP_BEFORE) else: @@ -646,7 +660,7 @@ class DNDHandlerMixin(object): yield (iter_, 0, child_path, gtk.TREE_VIEW_DROP_BEFORE) else: yield (parent_iter, position+1, gtk_path, - gtk.TREE_VIEW_DROP_AFTER) + gtk.TREE_VIEW_DROP_AFTER) def on_drag_motion(self, treeview, drag_context, x, y, timestamp): if not self.drag_dest: @@ -658,7 +672,9 @@ class DNDHandlerMixin(object): drop_action = 0 for pos_info in self.calc_positions(x, y): drop_action = self.drag_dest.validate_drop(self, self.model, type, - drag_context.actions, pos_info[0], pos_info[1]) + drag_context.actions, + pos_info[0], + pos_info[1]) if isinstance(drop_action, (list, tuple)): drop_action, iter = drop_action path = self.model.get_path(iter) @@ -692,8 +708,8 @@ class DNDHandlerMixin(object): treeview.drag_get_data(drag_context, target, timestamp) treeview.unset_drag_dest_row() - def on_drag_data_received(self, - treeview, drag_context, x, y, selection, info, timestamp): + def on_drag_data_received( + self, treeview, drag_context, x, y, selection, info, timestamp): # prevent the default handler treeview.emit_stop_by_name('drag-data-received') if not self.drag_dest: @@ -705,12 +721,14 @@ class DNDHandlerMixin(object): return drop_action = 0 for pos_info in self.calc_positions(x, y): - drop_action = self.drag_dest.validate_drop(self, self.model, type, - drag_context.actions, pos_info[0], pos_info[1]) + drop_action = self.drag_dest.validate_drop( + self, self.model, type, drag_context.actions, + pos_info[0], pos_info[1]) if drop_action: self.drag_dest.accept_drop(self, self.model, type, - drag_context.actions, pos_info[0], pos_info[1], - eval(selection.data)) + drag_context.actions, + pos_info[0], pos_info[1], + eval(selection.data)) return True return False @@ -723,16 +741,19 @@ class DNDHandlerMixin(object): """ if (self.drag_data and self.drag_button_down and treeview.drag_check_threshold(self.drag_start_x, - self.drag_start_y, int(event.x), int(event.y))): + self.drag_start_y, + int(event.x), + int(event.y))): self.delaying_press = False treeview.drag_begin(self._gtk_target_list(self.drag_data.keys()), - self.drag_source.allowed_actions(), 1, event) + self.drag_source.allowed_actions(), 1, event) @staticmethod def _gtk_target_list(types): count = itertools.count() return [(type, gtk.TARGET_SAME_APP, count.next()) for type in types] + class HotspotTrackingMixin(object): def __init__(self): self.hotspot_tracker = None @@ -748,8 +769,8 @@ class HotspotTrackingMixin(object): 'row-changed': self.on_row_changed, } self._hotspot_callback_handles.extend( - weak_connect(self._model, signal, handler) - for signal, handler in SIGNALS.iteritems()) + weak_connect(self._model, signal, handler) + for signal, handler in SIGNALS.iteritems()) def _disconnect_hotspot_signals(self): for handle in self._hotspot_callback_handles: @@ -781,10 +802,11 @@ class HotspotTrackingMixin(object): self.hotspot_tracker = hotspot_tracker hotspot_tracker.redraw_cell() if hotspot_tracker.is_for_context_menu(): - menu = self._popup_context_menu(self.hotspot_tracker.path, event) + menu = self._popup_context_menu(self.hotspot_tracker.path, + event) if menu: menu.connect('selection-done', - self._on_hotspot_context_menu_selection_done) + self._on_hotspot_context_menu_selection_done) # grab keyboard focus since we handled the event self.focus() return True @@ -810,7 +832,7 @@ class HotspotTrackingMixin(object): if (hotspot_tracker.hit and not hotspot_tracker.is_for_context_menu()): self.emit('hotspot-clicked', hotspot_tracker.name, - hotspot_tracker.iter) + hotspot_tracker.iter) hotspot_tracker.redraw_cell() self.hotspot_tracker = None return True @@ -822,6 +844,7 @@ class HotspotTrackingMixin(object): self.hotspot_tracker.redraw_cell() self.hotspot_tracker.update_hit() + class ColumnOwnerMixin(object): """Keeps track of the table's columns - including the list of columns, and properties that we set for a table but need to apply to each column. @@ -838,7 +861,7 @@ class ColumnOwnerMixin(object): self.columns = [] self.attr_map_for_column = {} self.gtk_column_to_wrapper = {} - self.create_signal('reallocate-columns') # not emitted on GTK + self.create_signal('reallocate-columns') # not emitted on GTK def remove_column(self, index): """Remove a column from the display and forget it from the column lists. @@ -853,7 +876,7 @@ class ColumnOwnerMixin(object): # FIXME: this should probably return column objects, and really should # not be keeping track of columns by title at all titles = [column.get_title().decode('utf-8') - for column in self._widget.get_columns()] + for column in self._widget.get_columns()] return titles def add_column(self, column): @@ -873,7 +896,7 @@ class ColumnOwnerMixin(object): """ if self.background_color: column.renderer._renderer.set_property('cell-background-gdk', - self.background_color) + self.background_color) column._column.set_reorderable(self._columns_draggable) if column.do_horizontal_padding: column.renderer._renderer.set_property('xpad', self._renderer_xpad) @@ -905,7 +928,7 @@ class ColumnOwnerMixin(object): """ for column in self.columns: column.renderer._renderer.set_property('cell-background-gdk', - self.background_color) + self.background_color) def set_auto_resizes(self, setting): # FIXME: to be implemented. @@ -917,6 +940,7 @@ class ColumnOwnerMixin(object): # column. pass + class HoverTrackingMixin(object): """Handle mouse hover events - tooltips for some cells and hover events for renderers which support them. @@ -969,23 +993,25 @@ class HoverTrackingMixin(object): self.hover_info = None self.hover_pos = None if (old_hover_info != self.hover_info or - old_hover_pos != self.hover_pos): + old_hover_pos != self.hover_pos): if (old_hover_info != self.hover_info and - old_hover_info is not None): + old_hover_info is not None): self._redraw_cell(treeview, *old_hover_info) if self.hover_info is not None: self._redraw_cell(treeview, *self.hover_info) + class GTKScrollbarOwnerMixin(ScrollbarOwnerMixin): # XXX this is half a wrapper for TreeViewScrolling. A lot of things will # become much simpler when we integrate TVS into this def __init__(self): ScrollbarOwnerMixin.__init__(self) # super uses this for postponed scroll_to_iter - # it's a faux-signal from our _widget; this hack is only necessary until + # it's a faux-signal from our _widget; + # this hack is only necessary until # we integrate TVS - self._widget.scroll_range_changed = (lambda *a: - self.emit('scroll-range-changed')) + self._widget.scroll_range_changed = ( + lambda *a: self.emit('scroll-range-changed')) def set_scroller(self, scroller): """Set the Scroller object for this widget, if its ScrolledWindow is @@ -1029,9 +1055,10 @@ class GTKScrollbarOwnerMixin(ScrollbarOwnerMixin): except WidgetNotReadyError: return None + class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, - HotspotTrackingMixin, ColumnOwnerMixin, HoverTrackingMixin, - GTKScrollbarOwnerMixin): + HotspotTrackingMixin, ColumnOwnerMixin, HoverTrackingMixin, + GTKScrollbarOwnerMixin): """https://develop.participatoryculture.org/index.php/WidgetAPITableView""" draws_selection = True @@ -1050,7 +1077,7 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, self.delaying_press = False self._use_custom_headers = False self.layout_manager = LayoutManager(self._widget) - self.height_changed = None # 17178 hack + self.height_changed = None # 17178 hack self._connect_signals() # setting up mixins after general TableView init GTKSelectionOwnerMixin.__init__(self) @@ -1082,9 +1109,9 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, self.wrapped_widget_connect('row-collapsed', self.on_row_collapsed) self.wrapped_widget_connect('button-press-event', self.on_button_press) self.wrapped_widget_connect('button-release-event', - self.on_button_release) + self.on_button_release) self.wrapped_widget_connect('motion-notify-event', - self.on_motion_notify) + self.on_motion_notify) def set_gradient_highlight(self, gradient): # This is just an OS X thing. @@ -1124,7 +1151,7 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, else: for column in self.columns: column.renderer._renderer.set_property( - 'cell-background-set', False) + 'cell-background-set', False) def set_alternate_row_backgrounds(self, setting): self._widget.set_rules_hint(setting) @@ -1142,8 +1169,8 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, def width_for_columns(self, total_width): """Given the width allocated for the TableView, return how much of that - is available to column contents. Note that this depends on the number of - columns. + is available to column contents. Note that this depends on the number + of columns. """ column_spacing = TableColumn.FIXED_PADDING * len(self.columns) return total_width - column_spacing @@ -1170,9 +1197,9 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, # if we don't want to draw selection, make the selected/active # colors the same as the normal ones self.modify_style('base', gtk.STATE_SELECTED, - style.base[gtk.STATE_NORMAL]) + style.base[gtk.STATE_NORMAL]) self.modify_style('base', gtk.STATE_ACTIVE, - style.base[gtk.STATE_NORMAL]) + style.base[gtk.STATE_NORMAL]) def set_search_column(self, model_index): self._widget.set_search_column(model_index) @@ -1192,7 +1219,7 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, self._widget.collapse_row(path) if bool(self._widget.row_expanded(path)) != bool(expanded): raise WidgetActionError("cannot expand the given item - it " - "probably has no children.") + "probably has no children.") def is_row_expanded(self, iter_): path = self.get_path(iter_) @@ -1236,8 +1263,8 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, # click is outside the content area, don't try to handle this. # In particular, our DnD code messes up resizing table columns. return False - if (event.button == 1 and self.drag_source and - not self._x_coord_in_expander(treeview, path_info)): + if (event.button == 1 and self.drag_source and not + self._x_coord_in_expander(treeview, path_info)): return self.start_drag(treeview, event, path_info) elif event.button == 3 and self.context_menu_callback: self.show_context_menu(treeview, event, path_info) @@ -1252,7 +1279,7 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, """ # hack for album view if (treeview.group_lines_enabled and - path_info.column == treeview.get_columns()[0]): + path_info.column == treeview.get_columns()[0]): self._select_all_rows_in_group(treeview, path_info.path) self._popup_context_menu(path_info.path, event) # grab keyboard focus since we handled the event @@ -1391,7 +1418,7 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, return True self.potential_drag_motion(treeview, event) - return None # XXX: used to fall through; not sure what retval does here + return None def start_bulk_change(self): self._widget.freeze_child_notify() @@ -1415,6 +1442,7 @@ class TableView(Widget, GTKSelectionOwnerMixin, DNDHandlerMixin, assert self.model.iter_is_valid(iter_) return self._model.get_path(iter_) + class TableModel(object): """https://develop.participatoryculture.org/index.php/WidgetAPITableView""" MODEL_CLASS = gtk.ListStore @@ -1434,13 +1462,13 @@ class TableModel(object): def map_types(self, miro_column_types): type_map = { - 'boolean': bool, - 'numeric': float, - 'integer': int, - 'text': str, - 'image': gtk.gdk.Pixbuf, - 'datetime': object, - 'object': object, + 'boolean': bool, + 'numeric': float, + 'integer': int, + 'text': str, + 'image': gtk.gdk.Pixbuf, + 'datetime': object, + 'object': object, } try: return [type_map[type] for type in miro_column_types] @@ -1522,6 +1550,7 @@ class TableModel(object): def iter_is_valid(self, iter_): return self._model.iter_is_valid(iter_) + class TreeTableModel(TableModel): """https://develop.participatoryculture.org/index.php/WidgetAPITableView""" MODEL_CLASS = gtk.TreeStore |