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.
This commit is contained in:
Virgil Dupras 2012-03-13 14:27:08 -04:00
parent 878c744c21
commit 49a7043b4d
11 changed files with 56 additions and 60 deletions

View File

@ -37,7 +37,7 @@ http://www.hardcoded.net/licenses/bsd_license
[_recentResults setDelegate:self]; [_recentResults setDelegate:self];
_resultWindow = [self createResultWindow]; _resultWindow = [self createResultWindow];
_directoryPanel = [self createDirectoryPanel]; _directoryPanel = [self createDirectoryPanel];
_detailsPanel = nil; // Lazily loaded _detailsPanel = [self createDetailsPanel];
_aboutBox = nil; // Lazily loaded _aboutBox = nil; // Lazily loaded
_preferencesPanel = nil; // Lazily loaded _preferencesPanel = nil; // Lazily loaded
[[[self directoryPanel] window] makeKeyAndOrderFront:self]; [[[self directoryPanel] window] makeKeyAndOrderFront:self];
@ -83,8 +83,6 @@ http://www.hardcoded.net/licenses/bsd_license
- (DetailsPanel *)detailsPanel - (DetailsPanel *)detailsPanel
{ {
if (!_detailsPanel)
_detailsPanel = [self createDetailsPanel];
return _detailsPanel; return _detailsPanel;
} }

View File

@ -9,10 +9,9 @@
from hscommon.notify import Listener from hscommon.notify import Listener
from hscommon.gui.base import NoopGUI from hscommon.gui.base import NoopGUI
class GUIObject(Listener): class DupeGuruGUIObject(Listener):
def __init__(self, app): def __init__(self, app):
Listener.__init__(self, app) Listener.__init__(self, app)
self.view = NoopGUI()
self.app = app self.app = app
def directories_changed(self): def directories_changed(self):

View File

@ -6,15 +6,16 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license # 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): def __init__(self, app):
GUIObject.__init__(self, app) GUIObject.__init__(self)
DupeGuruGUIObject.__init__(self, app)
self._table = [] self._table = []
def connect(self): def _view_updated(self):
GUIObject.connect(self)
self._refresh() self._refresh()
self.view.refresh() self.view.refresh()

View File

@ -9,7 +9,7 @@
from hscommon.gui.tree import Tree, Node from hscommon.gui.tree import Tree, Node
from ..directories import DirectoryState from ..directories import DirectoryState
from .base import GUIObject from .base import DupeGuruGUIObject
STATE_ORDER = [DirectoryState.Normal, DirectoryState.Reference, DirectoryState.Excluded] STATE_ORDER = [DirectoryState.Normal, DirectoryState.Reference, DirectoryState.Excluded]
@ -54,17 +54,16 @@ class DirectoryNode(Node):
self._tree.update_all_states() self._tree.update_all_states()
class DirectoryTree(GUIObject, Tree): class DirectoryTree(Tree, DupeGuruGUIObject):
#--- model -> view calls: #--- model -> view calls:
# refresh() # refresh()
# refresh_states() # when only states label need to be refreshed # refresh_states() # when only states label need to be refreshed
# #
def __init__(self, app): def __init__(self, app):
GUIObject.__init__(self, app)
Tree.__init__(self) Tree.__init__(self)
DupeGuruGUIObject.__init__(self, app)
def connect(self): def _view_updated(self):
GUIObject.connect(self)
self._refresh() self._refresh()
self.view.refresh() self.view.refresh()

View File

@ -6,6 +6,7 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license # http://www.hardcoded.net/licenses/bsd_license
from hscommon.gui.base import GUIObject
from hscommon.gui.selectable_list import GUISelectableList from hscommon.gui.selectable_list import GUISelectableList
class CriterionCategoryList(GUISelectableList): class CriterionCategoryList(GUISelectableList):
@ -39,8 +40,9 @@ class PrioritizationList(GUISelectableList):
del prilist[i] del prilist[i]
self._refresh_contents() self._refresh_contents()
class PrioritizeDialog: class PrioritizeDialog(GUIObject):
def __init__(self, app): def __init__(self, app):
GUIObject.__init__(self)
self.app = app self.app = app
self.categories = [cat(app.results) for cat in app._prioritization_categories()] self.categories = [cat(app.results) for cat in app._prioritization_categories()]
self.category_list = CriterionCategoryList(self) self.category_list = CriterionCategoryList(self)
@ -48,6 +50,9 @@ class PrioritizeDialog:
self.criteria_list = GUISelectableList() self.criteria_list = GUISelectableList()
self.prioritizations = [] self.prioritizations = []
self.prioritization_list = PrioritizationList(self) self.prioritization_list = PrioritizationList(self)
#--- Override
def _view_updated(self):
self.category_list.select(0) self.category_list.select(0)
#--- Private #--- Private

View File

@ -21,7 +21,6 @@ class ProblemTable(GUITable):
def __init__(self, problem_dialog): def __init__(self, problem_dialog):
GUITable.__init__(self) GUITable.__init__(self)
self.columns = Columns(self) self.columns = Columns(self)
self.view = None
self.dialog = problem_dialog self.dialog = problem_dialog
#--- Override #--- Override

View File

@ -11,7 +11,7 @@ from operator import attrgetter
from hscommon.gui.table import GUITable, Row from hscommon.gui.table import GUITable, Row
from hscommon.gui.column import Columns from hscommon.gui.column import Columns
from .base import GUIObject from .base import DupeGuruGUIObject
class DupeRow(Row): class DupeRow(Row):
def __init__(self, table, group, dupe): def __init__(self, table, group, dupe):
@ -51,20 +51,18 @@ class DupeRow(Row):
self._app.mark_dupe(self._dupe, value) self._app.mark_dupe(self._dupe, value)
class ResultTable(GUIObject, GUITable): class ResultTable(GUITable, DupeGuruGUIObject):
def __init__(self, app): def __init__(self, app):
GUIObject.__init__(self, app)
GUITable.__init__(self) GUITable.__init__(self)
DupeGuruGUIObject.__init__(self, app)
self.columns = Columns(self, prefaccess=app, savename='ResultTable') self.columns = Columns(self, prefaccess=app, savename='ResultTable')
self._power_marker = False self._power_marker = False
self._delta_values = False self._delta_values = False
self._sort_descriptors = ('name', True) self._sort_descriptors = ('name', True)
#--- Override #--- Override
def connect(self): def _view_updated(self):
GUIObject.connect(self)
self._refresh_with_view() self._refresh_with_view()
self.columns.restore_columns()
def _restore_selection(self, previous_selection): def _restore_selection(self, previous_selection):
if self.app.selected_dupes: if self.app.selected_dupes:

View File

@ -6,11 +6,10 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license # http://www.hardcoded.net/licenses/bsd_license
from .base import GUIObject from .base import DupeGuruGUIObject
class StatsLabel(GUIObject): class StatsLabel(DupeGuruGUIObject):
def connect(self): def _view_updated(self):
GUIObject.connect(self)
self.view.refresh() self.view.refresh()
@property @property

View File

@ -18,7 +18,7 @@ import hscommon.util
from hscommon.testutil import CallLogger, eq_, log_calls from hscommon.testutil import CallLogger, eq_, log_calls
from jobprogress.job import Job from jobprogress.job import Job
from .base import DupeGuru from .base import DupeGuru, TestApp
from .results_test import GetTestGroups from .results_test import GetTestGroups
from .. import app, fs, engine from .. import app, fs, engine
from ..gui.details_panel import DetailsPanel from ..gui.details_panel import DetailsPanel
@ -32,7 +32,7 @@ def add_fake_files_to_directories(directories, files):
class TestCaseDupeGuru: class TestCaseDupeGuru:
def test_apply_filter_calls_results_apply_filter(self, monkeypatch): 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)) monkeypatch.setattr(dgapp.results, 'apply_filter', log_calls(dgapp.results.apply_filter))
dgapp.apply_filter('foo') dgapp.apply_filter('foo')
eq_(2, len(dgapp.results.apply_filter.calls)) eq_(2, len(dgapp.results.apply_filter.calls))
@ -42,7 +42,7 @@ class TestCaseDupeGuru:
eq_('foo', call['filter_str']) eq_('foo', call['filter_str'])
def test_apply_filter_escapes_regexp(self, monkeypatch): 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)) monkeypatch.setattr(dgapp.results, 'apply_filter', log_calls(dgapp.results.apply_filter))
dgapp.apply_filter('()[]\\.|+?^abc') dgapp.apply_filter('()[]\\.|+?^abc')
call = dgapp.results.apply_filter.calls[1] 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. # XXX This monkeypatch is temporary. will be fixed in a better monkeypatcher.
monkeypatch.setattr(app, 'smart_copy', hscommon.conflict.smart_copy) 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 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) dgapp.directories.add_path(p)
[f] = dgapp.directories.get_files() [f] = dgapp.directories.get_files()
dgapp.copy_or_move(f, True, 'some_destination', 0) dgapp.copy_or_move(f, True, 'some_destination', 0)
@ -79,7 +79,7 @@ class TestCaseDupeGuru:
sourcepath = tmppath + 'source' sourcepath = tmppath + 'source'
io.mkdir(sourcepath) io.mkdir(sourcepath)
io.open(sourcepath + 'myfile', 'w') io.open(sourcepath + 'myfile', 'w')
app = DupeGuru() app = TestApp().app
app.directories.add_path(tmppath) app.directories.add_path(tmppath)
[myfile] = app.directories.get_files() [myfile] = app.directories.get_files()
monkeypatch.setattr(app, 'clean_empty_dirs', log_calls(lambda path: None)) 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 # 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, f2 = [FakeFile('foo') for i in range(2)]
f1.is_ref, f2.is_ref = (False, False) f1.is_ref, f2.is_ref = (False, False)
assert not (bool(f1) and bool(f2)) assert not (bool(f1) and bool(f2))
@ -109,7 +109,7 @@ class TestCaseDupeGuru:
tmppath = Path(str(tmpdir)) tmppath = Path(str(tmpdir))
io.open(tmppath + 'myfile', 'w').write('foo') io.open(tmppath + 'myfile', 'w').write('foo')
os.link(str(tmppath + 'myfile'), str(tmppath + 'hardlink')) os.link(str(tmppath + 'myfile'), str(tmppath + 'hardlink'))
app = DupeGuru() app = TestApp().app
app.directories.add_path(tmppath) app.directories.add_path(tmppath)
app.scanner.scan_type = ScanType.Contents app.scanner.scan_type = ScanType.Contents
app.options['ignore_hardlink_matches'] = True app.options['ignore_hardlink_matches'] = True
@ -120,7 +120,7 @@ class TestCaseDupeGuru:
# Issue #140 # Issue #140
# It's possible that rename operation has its selected row swept off from under it, thus # 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. # 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. # selected_row is None because there's no result.
assert not dgapp.result_table.rename_selected('foo') # no crash 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)) 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. # XXX This monkeypatch is temporary. will be fixed in a better monkeypatcher.
monkeypatch.setattr(app, 'delete_if_empty', hscommon.util.delete_if_empty) 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): def test_option_off(self, do_setup):
self.app.clean_empty_dirs(Path('/foo/bar')) self.app.clean_empty_dirs(Path('/foo/bar'))
@ -164,20 +164,14 @@ class TestCaseDupeGuru_clean_empty_dirs:
class TestCaseDupeGuruWithResults: class TestCaseDupeGuruWithResults:
def pytest_funcarg__do_setup(self, request): def pytest_funcarg__do_setup(self, request):
# XXX eventually, convert this to TestApp-based tests app = TestApp()
self.app = DupeGuru() self.app = app.app
self.objects,self.matches,self.groups = GetTestGroups() self.objects,self.matches,self.groups = GetTestGroups()
self.app.results.groups = self.groups self.app.results.groups = self.groups
self.dpanel = self.app.details_panel self.dpanel = app.dpanel
self.dpanel.view = CallLogger() self.dtree = app.dtree
self.dtree = self.app.directory_tree self.rtable = app.rtable
self.dtree.view = CallLogger() self.rtable.refresh()
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()
tmpdir = request.getfuncargvalue('tmpdir') tmpdir = request.getfuncargvalue('tmpdir')
tmppath = Path(str(tmpdir)) tmppath = Path(str(tmpdir))
io.mkdir(tmppath + 'foo') io.mkdir(tmppath + 'foo')
@ -422,16 +416,14 @@ class TestCaseDupeGuru_renameSelected:
groups = engine.get_groups(matches) groups = engine.get_groups(matches)
g = groups[0] g = groups[0]
g.prioritize(lambda x:x.name) g.prioritize(lambda x:x.name)
app = DupeGuru() app = TestApp()
app.results.groups = groups app.app.results.groups = groups
self.app = app self.app = app.app
self.rtable = app.rtable
self.rtable.refresh()
self.groups = groups self.groups = groups
self.p = p self.p = p
self.files = files 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): def test_simple(self, do_setup):
app = self.app app = self.app
@ -477,10 +469,9 @@ class TestAppWithDirectoriesInTree:
io.mkdir(p + 'sub1') io.mkdir(p + 'sub1')
io.mkdir(p + 'sub2') io.mkdir(p + 'sub2')
io.mkdir(p + 'sub3') io.mkdir(p + 'sub3')
self.app = DupeGuru() app = TestApp()
self.dtree = self.app.directory_tree self.app = app.app
self.dtree.view = CallLogger() self.dtree = app.dtree
self.dtree.connect()
self.dtree.add_directory(p) self.dtree.add_directory(p)
self.dtree.view.clear_calls() self.dtree.view.clear_calls()

View File

@ -36,6 +36,9 @@ class DupeGuruView:
def set_default(self, key_name, value): def set_default(self, key_name, value):
pass pass
def show_message(self, msg):
pass
def ask_yes_no(self, prompt): def ask_yes_no(self, prompt):
return True # always answer yes return True # always answer yes
@ -144,7 +147,11 @@ class TestApp(TestAppBase):
self.rtable = link_gui(self.app.result_table) self.rtable = link_gui(self.app.result_table)
self.dtree = link_gui(self.app.directory_tree) self.dtree = link_gui(self.app.directory_tree)
self.dpanel = link_gui(self.app.details_panel) self.dpanel = link_gui(self.app.details_panel)
self.slabel = link_gui(self.app.stats_label)
self.pdialog = PrioritizeDialog(self.app) self.pdialog = PrioritizeDialog(self.app)
link_gui(self.pdialog.category_list)
link_gui(self.pdialog.criteria_list)
link_gui(self.pdialog.prioritization_list)
#--- Helpers #--- Helpers
def select_pri_criterion(self, name): def select_pri_criterion(self, name):

View File

@ -16,13 +16,13 @@ class DetailsDialog(QDialog):
QDialog.__init__(self, parent, Qt.Tool) QDialog.__init__(self, parent, Qt.Tool)
self.app = app self.app = app
self.model = app.model.details_panel self.model = app.model.details_panel
self.model.view = self
self._setupUi() self._setupUi()
if self.app.prefs.detailsWindowRect is not None: if self.app.prefs.detailsWindowRect is not None:
self.setGeometry(self.app.prefs.detailsWindowRect) self.setGeometry(self.app.prefs.detailsWindowRect)
self.tableModel = DetailsModel(self.model) self.tableModel = DetailsModel(self.model)
# tableView is defined in subclasses # tableView is defined in subclasses
self.tableView.setModel(self.tableModel) self.tableView.setModel(self.tableModel)
self.model.view = self
self.app.willSavePrefs.connect(self.appWillSavePrefs) self.app.willSavePrefs.connect(self.appWillSavePrefs)