1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2026-01-22 14:41:39 +00:00

Created gui.details_panel and moved all details panel related logic in there (cocoa only, for now).

This commit is contained in:
Virgil Dupras
2010-02-05 20:10:54 +01:00
parent cd9b7f2f11
commit 7ffefe6259
22 changed files with 273 additions and 182 deletions

View File

@@ -15,6 +15,7 @@ from hsutil.cocoa.objcmin import (NSNotificationCenter, NSUserDefaults,
NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask,
NSWorkspace, NSWorkspaceRecycleOperation)
from hsutil.misc import stripnone
from hsutil.notify import Broadcaster
from hsutil.reg import RegistrationRequired
from . import app, fs
@@ -36,8 +37,9 @@ def demo_method(method):
return wrapper
class DupeGuru(app.DupeGuru):
class DupeGuru(app.DupeGuru, Broadcaster):
def __init__(self, data_module, appdata_subdir, appid):
Broadcaster.__init__(self)
LOGGING_LEVEL = logging.DEBUG if NSUserDefaults.standardUserDefaults().boolForKey_('debug') else logging.WARNING
logging.basicConfig(level=LOGGING_LEVEL, format='%(levelname)s %(message)s')
logging.debug('started in debug mode')
@@ -48,7 +50,7 @@ class DupeGuru(app.DupeGuru):
self.progress = cocoa.ThreadedJobPerformer()
self.display_delta_values = False
self.selected_dupes = []
self.RefreshDetailsTable(None,None)
self.RefreshDetailsWithSelected()
#--- Override
@staticmethod
@@ -91,14 +93,6 @@ class DupeGuru(app.DupeGuru):
curr_path = self.directories.get_subfolders(curr_path)[current_index]
return self.get_folder_path(node_path[1:], curr_path)
def RefreshDetailsTable(self,dupe,group):
l1 = self._get_display_info(dupe, group, False)
# we don't want the two sides of the table to display the stats for the same file
ref = group.ref if group is not None and group.ref is not dupe else None
l2 = self._get_display_info(ref, group, False)
names = [c['display'] for c in self.data.COLUMNS]
self.details_table = zip(names,l1,l2)
#---Public
def AddSelectedToIgnoreList(self):
for dupe in self.selected_dupes:
@@ -120,13 +114,7 @@ class DupeGuru(app.DupeGuru):
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
def RefreshDetailsWithSelected(self):
if self.selected_dupes:
self.RefreshDetailsTable(
self.selected_dupes[0],
self.results.get_group_of_duplicate(self.selected_dupes[0])
)
else:
self.RefreshDetailsTable(None,None)
self.notify('details_table_changed')
def RemoveDirectory(self,index):
try:
@@ -153,7 +141,8 @@ class DupeGuru(app.DupeGuru):
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(path,'')
def start_scanning(self):
self.RefreshDetailsTable(None, None)
self.selected_dupes = []
self.RefreshDetailsWithSelected()
try:
app.DupeGuru.start_scanning(self)
return 0
@@ -291,15 +280,3 @@ class DupeGuru(app.DupeGuru):
else:
return 0
def GetTableViewCount(self, tag):
if self.progress._job_running:
return 0
return len(self.details_table)
def GetTableViewMarkedIndexes(self,tag):
return []
def GetTableViewValues(self,tag,row):
return self.details_table[row]

View File

@@ -14,6 +14,8 @@ from hsutil.cocoa.objcmin import NSObject
from hsutil.cocoa import signature
from hsutil.reg import InvalidCodeError
from .gui.details_panel import DetailsPanel
# Fix py2app's problems on relative imports
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner
from hsutil import conflict
@@ -200,3 +202,23 @@ class PyDupeGuruBase(PyApp):
def setRegisteredCode_andEmail_(self, code, email):
self.app.set_registration(code, email)
class PyDetailsPanel(NSObject):
def initWithCocoa_pyParent_(self, cocoa, pyparent):
super(PyDetailsPanel, self).init()
self.cocoa = cocoa
self.py = DetailsPanel(self, pyparent.app)
return self
@signature('i@:')
def numberOfRows(self):
return self.py.row_count()
@signature('@@:@i')
def valueForColumn_row_(self, column, row):
return self.py.row(row)[int(column)]
# python --> cocoa
def refresh(self):
self.cocoa.refresh()

0
core/gui/__init__.py Normal file
View File

47
core/gui/details_panel.py Normal file
View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-02-05
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from hsutil.notify import Listener
class DetailsPanel(Listener):
def __init__(self, view, app):
Listener.__init__(self, app)
self.app = app
self.view = view
self._table = []
self._refresh()
self.connect()
#--- Private
def _refresh(self):
if self.app.selected_dupes:
dupe = self.app.selected_dupes[0]
group = self.app.results.get_group_of_duplicate(dupe)
else:
dupe = None
group = None
l1 = self.app._get_display_info(dupe, group, False)
# we don't want the two sides of the table to display the stats for the same file
ref = group.ref if group is not None and group.ref is not dupe else None
l2 = self.app._get_display_info(ref, group, False)
names = [c['display'] for c in self.app.data.COLUMNS]
self._table = zip(names, l1, l2)
#--- Public
def row_count(self):
return len(self._table)
def row(self, row_index):
return self._table[row_index]
#--- Event Handlers
def details_table_changed(self):
self._refresh()
self.view.refresh()

View File

@@ -26,6 +26,7 @@ try:
except ImportError:
from nose.plugins.skip import SkipTest
raise SkipTest("These tests can only be run on OS X")
from ..gui.details_panel import DetailsPanel
class DupeGuru(DupeGuruBase):
def __init__(self):
@@ -38,9 +39,28 @@ def r2np(rows):
#Transforms a list of rows [1,2,3] into a list of node paths [[1],[2],[3]]
return [[i] for i in rows]
class CallLogger(object):
"""This is a dummy object that logs all calls made to it.
It is used to simulate the GUI layer.
"""
def __init__(self):
self.calls = []
def __getattr__(self, func_name):
def func(*args, **kw):
self.calls.append(func_name)
return func
def clear_calls(self):
del self.calls[:]
class TCDupeGuru(TestCase):
def setUp(self):
self.app = DupeGuru()
self.dpanel_gui = CallLogger()
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
self.objects,self.matches,self.groups = GetTestGroups()
self.app.results.groups = self.groups
tmppath = self.tmppath()
@@ -48,6 +68,44 @@ class TCDupeGuru(TestCase):
io.mkdir(tmppath + 'bar')
self.app.directories.add_path(tmppath)
def check_gui_calls(self, gui, expected, verify_order=False):
"""Checks that the expected calls have been made to 'gui', then clears the log.
`expected` is an iterable of strings representing method names.
If `verify_order` is True, the order of the calls matters.
"""
if verify_order:
eq_(gui.calls, expected)
else:
eq_(set(gui.calls), set(expected))
gui.clear_calls()
def check_gui_calls_partial(self, gui, expected=None, not_expected=None):
"""Checks that the expected calls have been made to 'gui', then clears the log.
`expected` is an iterable of strings representing method names. Order doesn't matter.
Moreover, if calls have been made that are not in expected, no failure occur.
`not_expected` can be used for a more explicit check (rather than calling `check_gui_calls`
with an empty `expected`) to assert that calls have *not* been made.
"""
calls = set(gui.calls)
if expected is not None:
expected = set(expected)
not_called = expected - calls
assert not not_called, u"These calls haven't been made: {0}".format(not_called)
if not_expected is not None:
not_expected = set(not_expected)
called = not_expected & calls
assert not called, u"These calls shouldn't have been made: {0}".format(called)
gui.clear_calls()
def clear_gui_calls(self):
for attr in dir(self):
if attr.endswith('_gui'):
gui = getattr(self, attr)
if hasattr(gui, 'calls'): # We might have test methods ending with '_gui'
gui.clear_calls()
def test_GetObjects(self):
app = self.app
objects = self.objects
@@ -194,24 +252,14 @@ class TCDupeGuru(TestCase):
self.assert_(app.results.is_marked(objects[4]))
def test_refreshDetailsWithSelected(self):
def mock_refresh(dupe,group):
self.called = True
if self.app.selected_dupes:
self.assert_(dupe is self.app.selected_dupes[0])
self.assert_(group is self.app.results.get_group_of_duplicate(dupe))
else:
self.assert_(dupe is None)
self.assert_(group is None)
self.app.RefreshDetailsTable = mock_refresh
self.called = False
self.app.SelectPowerMarkerNodePaths(r2np([0,2]))
self.app.RefreshDetailsWithSelected()
self.assert_(self.called)
self.called = False
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
self.app.SelectPowerMarkerNodePaths([])
self.app.RefreshDetailsWithSelected()
self.assert_(self.called)
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
def test_makeSelectedReference(self):
app = self.app

View File

@@ -31,7 +31,7 @@ def GetDisplayInfo(dupe, group, delta):
dupe.name,
format_path(dupe.path),
format_size(size, 0, 1, False),
dupe.extension,
dupe.extension if hasattr(dupe, 'extension') else '---',
]
def GetDupeSortKey(dupe, get_group, key, delta):