From 49a7043b4dedbb5c76e6fa21c85f8926e0bb1285 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 13 Mar 2012 14:27:08 -0400 Subject: [PATCH] Updated hscommon to its tip. Because the latest changes in hscommon include the introduction of a base GUIObject which significantly changes view setting mechanisms, significant adjustments had to be made in dupeGuru. --- cocoa/base/AppDelegate.m | 4 +-- core/gui/base.py | 3 +- core/gui/details_panel.py | 11 +++---- core/gui/directory_tree.py | 9 +++--- core/gui/prioritize_dialog.py | 7 ++++- core/gui/problem_table.py | 1 - core/gui/result_table.py | 10 +++---- core/gui/stats_label.py | 7 ++--- core/tests/app_test.py | 55 +++++++++++++++-------------------- core/tests/base.py | 7 +++++ qt/base/details_dialog.py | 2 +- 11 files changed, 56 insertions(+), 60 deletions(-) diff --git a/cocoa/base/AppDelegate.m b/cocoa/base/AppDelegate.m index 3fa5d257..6bc58ed3 100644 --- a/cocoa/base/AppDelegate.m +++ b/cocoa/base/AppDelegate.m @@ -37,7 +37,7 @@ http://www.hardcoded.net/licenses/bsd_license [_recentResults setDelegate:self]; _resultWindow = [self createResultWindow]; _directoryPanel = [self createDirectoryPanel]; - _detailsPanel = nil; // Lazily loaded + _detailsPanel = [self createDetailsPanel]; _aboutBox = nil; // Lazily loaded _preferencesPanel = nil; // Lazily loaded [[[self directoryPanel] window] makeKeyAndOrderFront:self]; @@ -83,8 +83,6 @@ http://www.hardcoded.net/licenses/bsd_license - (DetailsPanel *)detailsPanel { - if (!_detailsPanel) - _detailsPanel = [self createDetailsPanel]; return _detailsPanel; } diff --git a/core/gui/base.py b/core/gui/base.py index 2e69ecf9..168a99ac 100644 --- a/core/gui/base.py +++ b/core/gui/base.py @@ -9,10 +9,9 @@ from hscommon.notify import Listener from hscommon.gui.base import NoopGUI -class GUIObject(Listener): +class DupeGuruGUIObject(Listener): def __init__(self, app): Listener.__init__(self, app) - self.view = NoopGUI() self.app = app def directories_changed(self): diff --git a/core/gui/details_panel.py b/core/gui/details_panel.py index e1e76d0c..db7930fe 100644 --- a/core/gui/details_panel.py +++ b/core/gui/details_panel.py @@ -6,15 +6,16 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license -from .base import GUIObject +from hscommon.gui.base import GUIObject +from .base import DupeGuruGUIObject -class DetailsPanel(GUIObject): +class DetailsPanel(GUIObject, DupeGuruGUIObject): def __init__(self, app): - GUIObject.__init__(self, app) + GUIObject.__init__(self) + DupeGuruGUIObject.__init__(self, app) self._table = [] - def connect(self): - GUIObject.connect(self) + def _view_updated(self): self._refresh() self.view.refresh() diff --git a/core/gui/directory_tree.py b/core/gui/directory_tree.py index 7c323feb..5d6ac3f0 100644 --- a/core/gui/directory_tree.py +++ b/core/gui/directory_tree.py @@ -9,7 +9,7 @@ from hscommon.gui.tree import Tree, Node from ..directories import DirectoryState -from .base import GUIObject +from .base import DupeGuruGUIObject STATE_ORDER = [DirectoryState.Normal, DirectoryState.Reference, DirectoryState.Excluded] @@ -54,17 +54,16 @@ class DirectoryNode(Node): self._tree.update_all_states() -class DirectoryTree(GUIObject, Tree): +class DirectoryTree(Tree, DupeGuruGUIObject): #--- model -> view calls: # refresh() # refresh_states() # when only states label need to be refreshed # def __init__(self, app): - GUIObject.__init__(self, app) Tree.__init__(self) + DupeGuruGUIObject.__init__(self, app) - def connect(self): - GUIObject.connect(self) + def _view_updated(self): self._refresh() self.view.refresh() diff --git a/core/gui/prioritize_dialog.py b/core/gui/prioritize_dialog.py index b7a9b3a1..804afdd5 100644 --- a/core/gui/prioritize_dialog.py +++ b/core/gui/prioritize_dialog.py @@ -6,6 +6,7 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license +from hscommon.gui.base import GUIObject from hscommon.gui.selectable_list import GUISelectableList class CriterionCategoryList(GUISelectableList): @@ -39,8 +40,9 @@ class PrioritizationList(GUISelectableList): del prilist[i] self._refresh_contents() -class PrioritizeDialog: +class PrioritizeDialog(GUIObject): def __init__(self, app): + GUIObject.__init__(self) self.app = app self.categories = [cat(app.results) for cat in app._prioritization_categories()] self.category_list = CriterionCategoryList(self) @@ -48,6 +50,9 @@ class PrioritizeDialog: self.criteria_list = GUISelectableList() self.prioritizations = [] self.prioritization_list = PrioritizationList(self) + + #--- Override + def _view_updated(self): self.category_list.select(0) #--- Private diff --git a/core/gui/problem_table.py b/core/gui/problem_table.py index e7fa9aaa..a15cebff 100644 --- a/core/gui/problem_table.py +++ b/core/gui/problem_table.py @@ -21,7 +21,6 @@ class ProblemTable(GUITable): def __init__(self, problem_dialog): GUITable.__init__(self) self.columns = Columns(self) - self.view = None self.dialog = problem_dialog #--- Override diff --git a/core/gui/result_table.py b/core/gui/result_table.py index 7cc7ad62..2fe9d3c1 100644 --- a/core/gui/result_table.py +++ b/core/gui/result_table.py @@ -11,7 +11,7 @@ from operator import attrgetter from hscommon.gui.table import GUITable, Row from hscommon.gui.column import Columns -from .base import GUIObject +from .base import DupeGuruGUIObject class DupeRow(Row): def __init__(self, table, group, dupe): @@ -51,20 +51,18 @@ class DupeRow(Row): self._app.mark_dupe(self._dupe, value) -class ResultTable(GUIObject, GUITable): +class ResultTable(GUITable, DupeGuruGUIObject): def __init__(self, app): - GUIObject.__init__(self, app) GUITable.__init__(self) + DupeGuruGUIObject.__init__(self, app) self.columns = Columns(self, prefaccess=app, savename='ResultTable') self._power_marker = False self._delta_values = False self._sort_descriptors = ('name', True) #--- Override - def connect(self): - GUIObject.connect(self) + def _view_updated(self): self._refresh_with_view() - self.columns.restore_columns() def _restore_selection(self, previous_selection): if self.app.selected_dupes: diff --git a/core/gui/stats_label.py b/core/gui/stats_label.py index 54c22ac1..6ec79470 100644 --- a/core/gui/stats_label.py +++ b/core/gui/stats_label.py @@ -6,11 +6,10 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license -from .base import GUIObject +from .base import DupeGuruGUIObject -class StatsLabel(GUIObject): - def connect(self): - GUIObject.connect(self) +class StatsLabel(DupeGuruGUIObject): + def _view_updated(self): self.view.refresh() @property diff --git a/core/tests/app_test.py b/core/tests/app_test.py index a96802ce..fe350366 100644 --- a/core/tests/app_test.py +++ b/core/tests/app_test.py @@ -18,7 +18,7 @@ import hscommon.util from hscommon.testutil import CallLogger, eq_, log_calls from jobprogress.job import Job -from .base import DupeGuru +from .base import DupeGuru, TestApp from .results_test import GetTestGroups from .. import app, fs, engine from ..gui.details_panel import DetailsPanel @@ -32,7 +32,7 @@ def add_fake_files_to_directories(directories, files): class TestCaseDupeGuru: def test_apply_filter_calls_results_apply_filter(self, monkeypatch): - dgapp = DupeGuru() + dgapp = TestApp().app monkeypatch.setattr(dgapp.results, 'apply_filter', log_calls(dgapp.results.apply_filter)) dgapp.apply_filter('foo') eq_(2, len(dgapp.results.apply_filter.calls)) @@ -42,7 +42,7 @@ class TestCaseDupeGuru: eq_('foo', call['filter_str']) def test_apply_filter_escapes_regexp(self, monkeypatch): - dgapp = DupeGuru() + dgapp = TestApp().app monkeypatch.setattr(dgapp.results, 'apply_filter', log_calls(dgapp.results.apply_filter)) dgapp.apply_filter('()[]\\.|+?^abc') call = dgapp.results.apply_filter.calls[1] @@ -65,7 +65,7 @@ class TestCaseDupeGuru: # XXX This monkeypatch is temporary. will be fixed in a better monkeypatcher. monkeypatch.setattr(app, 'smart_copy', hscommon.conflict.smart_copy) monkeypatch.setattr(os, 'makedirs', lambda path: None) # We don't want the test to create that fake directory - dgapp = DupeGuru() + dgapp = TestApp().app dgapp.directories.add_path(p) [f] = dgapp.directories.get_files() dgapp.copy_or_move(f, True, 'some_destination', 0) @@ -79,7 +79,7 @@ class TestCaseDupeGuru: sourcepath = tmppath + 'source' io.mkdir(sourcepath) io.open(sourcepath + 'myfile', 'w') - app = DupeGuru() + app = TestApp().app app.directories.add_path(tmppath) [myfile] = app.directories.get_files() monkeypatch.setattr(app, 'clean_empty_dirs', log_calls(lambda path: None)) @@ -95,7 +95,7 @@ class TestCaseDupeGuru: # At some point, any() was used in a wrong way that made Scan() wrongly return 1 - app = DupeGuru() + app = TestApp().app f1, f2 = [FakeFile('foo') for i in range(2)] f1.is_ref, f2.is_ref = (False, False) assert not (bool(f1) and bool(f2)) @@ -109,7 +109,7 @@ class TestCaseDupeGuru: tmppath = Path(str(tmpdir)) io.open(tmppath + 'myfile', 'w').write('foo') os.link(str(tmppath + 'myfile'), str(tmppath + 'hardlink')) - app = DupeGuru() + app = TestApp().app app.directories.add_path(tmppath) app.scanner.scan_type = ScanType.Contents app.options['ignore_hardlink_matches'] = True @@ -120,7 +120,7 @@ class TestCaseDupeGuru: # Issue #140 # It's possible that rename operation has its selected row swept off from under it, thus # making the selected row None. Don't crash when it happens. - dgapp = DupeGuru() + dgapp = TestApp().app # selected_row is None because there's no result. assert not dgapp.result_table.rename_selected('foo') # no crash @@ -130,7 +130,7 @@ class TestCaseDupeGuru_clean_empty_dirs: monkeypatch.setattr(hscommon.util, 'delete_if_empty', log_calls(lambda path, files_to_delete=[]: None)) # XXX This monkeypatch is temporary. will be fixed in a better monkeypatcher. monkeypatch.setattr(app, 'delete_if_empty', hscommon.util.delete_if_empty) - self.app = DupeGuru() + self.app = TestApp().app def test_option_off(self, do_setup): self.app.clean_empty_dirs(Path('/foo/bar')) @@ -164,20 +164,14 @@ class TestCaseDupeGuru_clean_empty_dirs: class TestCaseDupeGuruWithResults: def pytest_funcarg__do_setup(self, request): - # XXX eventually, convert this to TestApp-based tests - self.app = DupeGuru() + app = TestApp() + self.app = app.app self.objects,self.matches,self.groups = GetTestGroups() self.app.results.groups = self.groups - self.dpanel = self.app.details_panel - self.dpanel.view = CallLogger() - self.dtree = self.app.directory_tree - self.dtree.view = CallLogger() - self.rtable_gui = CallLogger() - self.rtable = self.app.result_table - self.rtable.view = self.rtable_gui - self.dpanel.connect() - self.dtree.connect() - self.rtable.connect() + self.dpanel = app.dpanel + self.dtree = app.dtree + self.rtable = app.rtable + self.rtable.refresh() tmpdir = request.getfuncargvalue('tmpdir') tmppath = Path(str(tmpdir)) io.mkdir(tmppath + 'foo') @@ -422,16 +416,14 @@ class TestCaseDupeGuru_renameSelected: groups = engine.get_groups(matches) g = groups[0] g.prioritize(lambda x:x.name) - app = DupeGuru() - app.results.groups = groups - self.app = app + app = TestApp() + app.app.results.groups = groups + self.app = app.app + self.rtable = app.rtable + self.rtable.refresh() self.groups = groups self.p = p self.files = files - self.rtable_gui = CallLogger() - self.rtable = self.app.result_table - self.rtable.view = self.rtable_gui - self.rtable.connect() def test_simple(self, do_setup): app = self.app @@ -477,10 +469,9 @@ class TestAppWithDirectoriesInTree: io.mkdir(p + 'sub1') io.mkdir(p + 'sub2') io.mkdir(p + 'sub3') - self.app = DupeGuru() - self.dtree = self.app.directory_tree - self.dtree.view = CallLogger() - self.dtree.connect() + app = TestApp() + self.app = app.app + self.dtree = app.dtree self.dtree.add_directory(p) self.dtree.view.clear_calls() diff --git a/core/tests/base.py b/core/tests/base.py index 0d489b78..e5f04416 100644 --- a/core/tests/base.py +++ b/core/tests/base.py @@ -36,6 +36,9 @@ class DupeGuruView: def set_default(self, key_name, value): pass + def show_message(self, msg): + pass + def ask_yes_no(self, prompt): return True # always answer yes @@ -144,7 +147,11 @@ class TestApp(TestAppBase): self.rtable = link_gui(self.app.result_table) self.dtree = link_gui(self.app.directory_tree) self.dpanel = link_gui(self.app.details_panel) + self.slabel = link_gui(self.app.stats_label) self.pdialog = PrioritizeDialog(self.app) + link_gui(self.pdialog.category_list) + link_gui(self.pdialog.criteria_list) + link_gui(self.pdialog.prioritization_list) #--- Helpers def select_pri_criterion(self, name): diff --git a/qt/base/details_dialog.py b/qt/base/details_dialog.py index 5f6cef74..a820257d 100644 --- a/qt/base/details_dialog.py +++ b/qt/base/details_dialog.py @@ -16,13 +16,13 @@ class DetailsDialog(QDialog): QDialog.__init__(self, parent, Qt.Tool) self.app = app self.model = app.model.details_panel - self.model.view = self self._setupUi() if self.app.prefs.detailsWindowRect is not None: self.setGeometry(self.app.prefs.detailsWindowRect) self.tableModel = DetailsModel(self.model) # tableView is defined in subclasses self.tableView.setModel(self.tableModel) + self.model.view = self self.app.willSavePrefs.connect(self.appWillSavePrefs)