From a68d836beb1a6f0c1c1ab97ad174fd65a593169c Mon Sep 17 00:00:00 2001 From: bendikro Date: Thu, 9 Oct 2014 16:59:45 +0200 Subject: [PATCH] [#2256] [GTKUI] Indexes aren't updated properly when removing columns --- deluge/tests/test_torrentview.py | 64 +++++++++++++++++++ deluge/ui/gtkui/gtkui.py | 8 ++- deluge/ui/gtkui/listview.py | 105 +++++++++++++++++++++---------- tox.ini | 1 + 4 files changed, 145 insertions(+), 33 deletions(-) create mode 100644 deluge/tests/test_torrentview.py diff --git a/deluge/tests/test_torrentview.py b/deluge/tests/test_torrentview.py new file mode 100644 index 000000000..0a4bbdeb5 --- /dev/null +++ b/deluge/tests/test_torrentview.py @@ -0,0 +1,64 @@ +import gobject +from twisted.trial import unittest + +import deluge.common +import deluge.component as component +from deluge.ui.gtkui.mainwindow import MainWindow +from deluge.ui.gtkui.menubar import MenuBar +from deluge.ui.gtkui.torrentdetails import TorrentDetails +from deluge.ui.gtkui.torrentview import TorrentView + +deluge.common.setup_translations() + + +class TorrentviewTestCase(unittest.TestCase): + + def setUp(self): # NOQA + self.mainwindow = MainWindow() + self.torrentview = TorrentView() + self.torrentdetails = TorrentDetails() + self.menubar = MenuBar() + + def tearDown(self): # NOQA + def on_shutdown(result): + component._ComponentRegistry.components = {} + return component.shutdown().addCallback(on_shutdown) + + def test_torrentview_columns(self): + + default_column_index = ['filter', 'torrent_id', 'dirty', '#', u'Name', u'Size', + u'Downloaded', u'Uploaded', u'Remaining', u'Progress', + u'Seeds', u'Peers', u'Seeds:Peers', u'Down Speed', + u'Up Speed', u'Down Limit', u'Up Limit', u'ETA', u'Ratio', + u'Avail', u'Added', u'Completed', u'Complete Seen', + u'Tracker', u'Download Folder', u'Owner', u'Shared'] + default_liststore_columns = [bool, str, bool, int, str, str, gobject.TYPE_UINT64, + gobject.TYPE_UINT64, gobject.TYPE_UINT64, gobject.TYPE_UINT64, + float, str, int, int, int, int, float, float, float, + float, float, int, float, float, float, float, + float, str, str, str, str, bool] + self.assertEquals(self.torrentview.column_index, default_column_index) + self.assertEquals(self.torrentview.liststore_columns, default_liststore_columns) + + def test_addcolumn_verify_index(self): + + self.assertEquals(self.torrentview.columns["Download Folder"].column_indices, [29]) + test_col = "Test column" + self.torrentview.add_text_column(test_col, status_field=["label"]) + self.assertEquals(len(self.torrentview.liststore_columns), 33) + self.assertEquals(len(self.torrentview.column_index), 28) + self.assertEquals(self.torrentview.column_index[-1], test_col) + self.assertEquals(self.torrentview.columns[test_col].column_indices, [32]) + + test_col2 = "Test column2" + self.torrentview.add_text_column(test_col2, status_field=["label2"]) + self.assertEquals(len(self.torrentview.liststore_columns), 34) + self.assertEquals(len(self.torrentview.column_index), 29) + self.assertEquals(self.torrentview.column_index[-1], test_col2) + self.assertEquals(self.torrentview.columns[test_col2].column_indices, [33]) + + self.torrentview.remove_column(test_col) + self.assertEquals(len(self.torrentview.liststore_columns), 33) + self.assertEquals(len(self.torrentview.column_index), 28) + self.assertEquals(self.torrentview.column_index[-1], test_col2) + self.assertEquals(self.torrentview.columns[test_col2].column_indices, [32]) diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index a766bc2df..d867b6c4e 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -15,7 +15,13 @@ import warnings import gobject import gtk from twisted.internet import gtk2reactor -reactor = gtk2reactor.install() # Install twisted reactor, before any other modules import reactor. +from twisted.internet.error import ReactorAlreadyInstalledError + +try: + reactor = gtk2reactor.install() # Install twisted reactor, before any other modules import reactor. +except ReactorAlreadyInstalledError: + # Running unit tests so trial already installed a rector + pass import deluge.common import deluge.component as component diff --git a/deluge/ui/gtkui/listview.py b/deluge/ui/gtkui/listview.py index 7c95a8f1b..92f93cec0 100644 --- a/deluge/ui/gtkui/listview.py +++ b/deluge/ui/gtkui/listview.py @@ -59,6 +59,13 @@ class ListView: # If this is set, it is used to sort the model self.sort_func = None self.sort_id = None + # Values needed to update TreeViewColumns + self.column_type = None + self.renderer = None + self.text_index = 0 + self.value_index = 0 + self.pixbuf_index = 0 + self.data_func = None class TreeviewColumn(gtk.TreeViewColumn, object): """ @@ -105,6 +112,13 @@ class ListView: else: self.set_cell_data_func(self.cell_renderer, self.data_func, self.data_func_data) + def set_col_attributes(self, renderer, add=True, **kw): + if add is True: + for attr, value in kw.iteritems(): + self.add_attribute(renderer, attr, value) + else: + self.set_attributes(renderer, **kw) + def __init__(self, treeview_widget=None, state_file=None): log.debug("ListView initialized..") @@ -365,6 +379,49 @@ class ListView: self.liststore = new_list + def update_treeview_column(self, header, add=True): + """Update TreeViewColumn based on ListView column mappings""" + column = self.columns[header] + tree_column = self.columns[header].column + + if column.column_type == "text": + if add: + tree_column.pack_start(column.renderer) + tree_column.set_col_attributes(column.renderer, add=add, + text=column.column_indices[column.text_index]) + elif column.column_type == "bool": + if add: + tree_column.pack_start(column.renderer) + tree_column.set_col_attributes(column.renderer, active=column.column_indices[0]) + elif column.column_type == "func": + if add: + tree_column.pack_start(column.renderer, True) + indice_arg = column.column_indices[0] + if len(column.column_indices) > 1: + indice_arg = tuple(column.column_indices) + tree_column.set_cell_data_func(column.renderer, column.data_func, indice_arg) + elif column.column_type == "progress": + if add: + tree_column.pack_start(column.renderer) + if column.data_func is None: + tree_column.set_col_attributes(column.renderer, add=add, + text=column.column_indices[column.text_index], + value=column.column_indices[column.value_index]) + else: + tree_column.set_cell_data_func(column.renderer, column.data_func, + tuple(column.column_indices)) + elif column.column_type == "texticon": + if add: + tree_column.pack_start(column.renderer[column.pixbuf_index], False) + tree_column.pack_start(column.renderer[column.text_index], True) + tree_column.set_col_attributes(column.renderer[column.text_index], add=add, + text=column.column_indices[column.text_index]) + if column.data_func is not None: + tree_column.set_cell_data_func( + column.renderer[column.pixbuf_index], column.data_func, + column.column_indices[column.pixbuf_index]) + return True + def remove_column(self, header): """Removes the column with the name 'header' from the listview""" # Store a copy of this columns state in case it's re-added @@ -383,8 +440,10 @@ class ListView: for column in self.columns.values(): if column.column_indices[0] > column_indices[0]: # We need to shift this column_indices - for index in column.column_indices: - index = index - len(column_indices) + for i, index in enumerate(column.column_indices): + column.column_indices[i] = index - len(column_indices) + # Update the associated TreeViewColumn + self.update_treeview_column(column.name, add=False) # Remove from the liststore columns list for index in column_indices: @@ -421,11 +480,19 @@ class ListView: self.column_index.append(header) # Create a new column object and add it to the list + column = self.TreeviewColumn(header) self.columns[header] = self.ListViewColumn(header, column_indices) - + self.columns[header].column = column self.columns[header].status_field = status_field self.columns[header].sort_func = sort_func self.columns[header].sort_id = column_indices[sortid] + # Store creation details + self.columns[header].column_type = column_type + self.columns[header].renderer = render + self.columns[header].text_index = text + self.columns[header].value_index = value + self.columns[header].pixbuf_index = pixbuf + self.columns[header].data_func = function if unique: self.unique_column_id = column_indices[sortid] @@ -439,37 +506,11 @@ class ListView: if self.model_filter: self.create_model_filter() - column = self.TreeviewColumn(header) - - if column_type == "text": - column.pack_start(render) - column.add_attribute(render, "text", self.columns[header].column_indices[text]) - elif column_type == "bool": - column.pack_start(render) - column.add_attribute(render, "active", self.columns[header].column_indices[0]) - elif column_type == "func": - column.pack_start(render, True) - if len(self.columns[header].column_indices) > 1: - column.set_cell_data_func_attributes(render, function, tuple(self.columns[header].column_indices)) - else: - column.set_cell_data_func_attributes(render, function, self.columns[header].column_indices[0]) - elif column_type == "progress": - column.pack_start(render) - if function is None: - column.add_attribute(render, "text", self.columns[header].column_indices[text]) - column.add_attribute(render, "value", self.columns[header].column_indices[value]) - else: - column.set_cell_data_func_attributes(render, function, tuple(self.columns[header].column_indices)) - elif column_type == "texticon": - column.pack_start(render[pixbuf], False) - if function is not None: - column.set_cell_data_func_attributes(render[pixbuf], function, - self.columns[header].column_indices[pixbuf]) - column.pack_start(render[text], True) - column.add_attribute(render[text], "text", self.columns[header].column_indices[text]) - elif column_type is None: + if column_type is None: return + self.update_treeview_column(header) + column.set_sort_column_id(self.columns[header].column_indices[sortid]) column.set_clickable(True) column.set_resizable(True) diff --git a/tox.ini b/tox.ini index 12bdf675c..e0f90e3fb 100644 --- a/tox.ini +++ b/tox.ini @@ -11,6 +11,7 @@ exclude = .tox,.git,dist,build [tox] envlist = py27, py26, flake8, isort, docs +minversion=1.8 [testenv] setenv = PYTHONPATH = {env:PYTHONPATH}:{env:PWD}