Decoupled app in core.app from apps in qt.app and core.app_cocoa. Instead of subclassing it, they now hold a reference to it while fulfilling the role of core.app's "view".

This commit is contained in:
Virgil Dupras 2011-09-20 15:06:29 -04:00
parent 841b249b67
commit f730f4f55a
15 changed files with 162 additions and 170 deletions

View File

@ -41,7 +41,16 @@ class DestType:
Absolute = 2 Absolute = 2
class DupeGuru(RegistrableApplication, Broadcaster): class DupeGuru(RegistrableApplication, Broadcaster):
def __init__(self, data_module, appdata): #--- View interface
# open_path(path)
# reveal_path(path)
# start_job(jobid, func, *args) ( func(j, *args) )
# get_default(key_name, fallback_value=None)
# set_default(key_name, value)
# show_extra_fairware_reminder()
def __init__(self, view, data_module, appdata):
self.view = view
if self.get_default(DEBUG_MODE_PREFERENCE, False): if self.get_default(DEBUG_MODE_PREFERENCE, False):
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
logging.debug("Debug mode enabled") logging.debug("Debug mode enabled")
@ -122,14 +131,6 @@ class DupeGuru(RegistrableApplication, Broadcaster):
if jobid in {JOB_COPY, JOB_MOVE, JOB_DELETE}: if jobid in {JOB_COPY, JOB_MOVE, JOB_DELETE}:
self.notify('problems_changed') self.notify('problems_changed')
@staticmethod
def _open_path(path):
raise NotImplementedError()
@staticmethod
def _reveal_path(path):
raise NotImplementedError()
@staticmethod @staticmethod
def _remove_hardlink_dupes(files): def _remove_hardlink_dupes(files):
seen_inodes = set() seen_inodes = set()
@ -151,19 +152,6 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.selected_dupes = dupes self.selected_dupes = dupes
self.notify('dupes_selected') self.notify('dupes_selected')
def _start_job(self, jobid, func, *args):
# func(j, *args)
raise NotImplementedError()
def _get_default(self, key_name, fallback_value=None):
raise NotImplementedError()
def _set_default(self, key_name, value):
raise NotImplementedError()
def _show_extra_fairware_reminder(self):
raise NotImplementedError()
#--- Public #--- Public
def add_directory(self, d): def add_directory(self, d):
try: try:
@ -194,7 +182,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def show_extra_fairware_reminder_if_needed(self): def show_extra_fairware_reminder_if_needed(self):
if self.results.mark_count > 100 and self.should_show_fairware_reminder: if self.results.mark_count > 100 and self.should_show_fairware_reminder:
self._show_extra_fairware_reminder() self.view.show_extra_fairware_reminder()
def clean_empty_dirs(self, path): def clean_empty_dirs(self, path):
if self.options['clean_empty_dirs']: if self.options['clean_empty_dirs']:
@ -234,11 +222,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.show_extra_fairware_reminder_if_needed() self.show_extra_fairware_reminder_if_needed()
jobid = JOB_COPY if copy else JOB_MOVE jobid = JOB_COPY if copy else JOB_MOVE
self._start_job(jobid, do) self.view.start_job(jobid, do)
def delete_marked(self, replace_with_hardlinks=False): def delete_marked(self, replace_with_hardlinks=False):
self.show_extra_fairware_reminder_if_needed() self.show_extra_fairware_reminder_if_needed()
self._start_job(JOB_DELETE, self._do_delete, replace_with_hardlinks) self.view.start_job(JOB_DELETE, self._do_delete, replace_with_hardlinks)
def export_to_xhtml(self, column_ids): def export_to_xhtml(self, column_ids):
column_ids = [colid for colid in column_ids if colid.isdigit()] column_ids = [colid for colid in column_ids if colid.isdigit()]
@ -288,7 +276,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def load_from(self, filename): def load_from(self, filename):
def do(j): def do(j):
self.results.load_from_xml(filename, self._get_file, j) self.results.load_from_xml(filename, self._get_file, j)
self._start_job(JOB_LOAD, do) self.view.start_job(JOB_LOAD, do)
def make_selected_reference(self): def make_selected_reference(self):
dupes = self.without_ref(self.selected_dupes) dupes = self.without_ref(self.selected_dupes)
@ -321,7 +309,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def open_selected(self): def open_selected(self):
if self.selected_dupes: if self.selected_dupes:
self._open_path(self.selected_dupes[0].path) self.view.open_path(self.selected_dupes[0].path)
def purge_ignore_list(self): def purge_ignore_list(self):
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s)) self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
@ -360,7 +348,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def reveal_selected(self): def reveal_selected(self):
if self.selected_dupes: if self.selected_dupes:
self._reveal_path(self.selected_dupes[0].path) self.view.reveal_path(self.selected_dupes[0].path)
def save(self): def save(self):
if not op.exists(self.appdata): if not op.exists(self.appdata):
@ -388,7 +376,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
raise NoScannableFileError() raise NoScannableFileError()
self.results.groups = [] self.results.groups = []
self._results_changed() self._results_changed()
self._start_job(JOB_SCAN, do) self.view.start_job(JOB_SCAN, do)
def toggle_selected_mark_state(self): def toggle_selected_mark_state(self):
for dupe in self.selected_dupes: for dupe in self.selected_dupes:
@ -399,7 +387,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
return [dupe for dupe in dupes if self.results.get_group_of_duplicate(dupe).ref is not dupe] return [dupe for dupe in dupes if self.results.get_group_of_duplicate(dupe).ref is not dupe]
def get_default(self, key, fallback_value=None): def get_default(self, key, fallback_value=None):
result = nonone(self._get_default(key), fallback_value) result = nonone(self.view.get_default(key), fallback_value)
if fallback_value is not None and not isinstance(result, type(fallback_value)): if fallback_value is not None and not isinstance(result, type(fallback_value)):
# we don't want to end up with garbage values from the prefs # we don't want to end up with garbage values from the prefs
try: try:
@ -409,7 +397,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
return result return result
def set_default(self, key, value): def set_default(self, key, value):
self._set_default(key, value) self.view.set_default(key, value)
#--- Properties #--- Properties
@property @property

View File

@ -27,46 +27,51 @@ JOBID2TITLE = {
app.JOB_DELETE: tr("Sending to Trash"), app.JOB_DELETE: tr("Sending to Trash"),
} }
class DupeGuru(app.DupeGuru): class DupeGuruView:
def __init__(self, data_module, appdata_subdir): def __init__(self, app):
logging.basicConfig(level=logging.WARNING, format='%(levelname)s %(message)s') self.app = app
install_exception_hook()
appsupport = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0]
appdata = op.join(appsupport, appdata_subdir)
app.DupeGuru.__init__(self, data_module, appdata)
self.progress = cocoa.ThreadedJobPerformer()
#--- Override
@staticmethod @staticmethod
def _open_path(path): def open_path(path):
NSWorkspace.sharedWorkspace().openFile_(str(path)) NSWorkspace.sharedWorkspace().openFile_(str(path))
@staticmethod @staticmethod
def _reveal_path(path): def reveal_path(path):
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(str(path), '') NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(str(path), '')
def _start_job(self, jobid, func, *args): def start_job(self, jobid, func, *args):
try: try:
j = self.progress.create_job() j = self.app.progress.create_job()
args = tuple([j] + list(args)) args = tuple([j] + list(args))
self.progress.run_threaded(func, args=args) self.app.progress.run_threaded(func, args=args)
except job.JobInProgressError: except job.JobInProgressError:
NSNotificationCenter.defaultCenter().postNotificationName_object_('JobInProgress', self) NSNotificationCenter.defaultCenter().postNotificationName_object_('JobInProgress', self)
else: else:
ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid} ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid}
NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('JobStarted', self, ud) NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('JobStarted', self, ud)
def _get_default(self, key_name): def get_default(self, key_name):
raw = NSUserDefaults.standardUserDefaults().objectForKey_(key_name) raw = NSUserDefaults.standardUserDefaults().objectForKey_(key_name)
result = pythonify(raw) result = pythonify(raw)
return result return result
def _set_default(self, key_name, value): def set_default(self, key_name, value):
NSUserDefaults.standardUserDefaults().setObject_forKey_(value, key_name) NSUserDefaults.standardUserDefaults().setObject_forKey_(value, key_name)
def _show_extra_fairware_reminder(self): def show_extra_fairware_reminder(self):
NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('ShowExtraFairwareReminder', self, None) NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('ShowExtraFairwareReminder', self, None)
class DupeGuru(app.DupeGuru):
def __init__(self, data_module, appdata_subdir):
logging.basicConfig(level=logging.WARNING, format='%(levelname)s %(message)s')
install_exception_hook()
view = DupeGuruView(self)
appsupport = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0]
appdata = op.join(appsupport, appdata_subdir)
app.DupeGuru.__init__(self, view, data_module, appdata)
self.progress = cocoa.ThreadedJobPerformer()
#--- Public #--- Public
def start_scanning(self): def start_scanning(self):
self._select_dupes([]) self._select_dupes([])

View File

@ -19,7 +19,7 @@ from jobprogress import job
from jobprogress.qt import Progress from jobprogress.qt import Progress
from hscommon.trans import tr, trmsg from hscommon.trans import tr, trmsg
from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE from core.app import DupeGuru as DupeGuruModel, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE
from qtlib.about_box import AboutBox from qtlib.about_box import AboutBox
from qtlib.recent import Recent from qtlib.recent import Recent
@ -45,11 +45,12 @@ class SysWrapper(io.IOBase):
if s.strip(): # don't log empty stuff if s.strip(): # don't log empty stuff
logging.warning(s) logging.warning(s)
class DupeGuru(DupeGuruBase, QObject): class DupeGuru(QObject):
LOGO_NAME = '<replace this>' LOGO_NAME = '<replace this>'
NAME = '<replace this>' NAME = '<replace this>'
def __init__(self, data_module): def __init__(self, data_module):
QObject.__init__(self)
appdata = str(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) appdata = str(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
if not op.exists(appdata): if not op.exists(appdata):
os.makedirs(appdata) os.makedirs(appdata)
@ -62,8 +63,7 @@ class DupeGuru(DupeGuruBase, QObject):
sys.stdout = SysWrapper() sys.stdout = SysWrapper()
self.prefs = self._create_preferences() self.prefs = self._create_preferences()
self.prefs.load() self.prefs.load()
DupeGuruBase.__init__(self, data_module, appdata) self.model = DupeGuruModel(view=self, data_module=data_module, appdata=appdata)
QObject.__init__(self)
self._setup() self._setup()
#--- Private #--- Private
@ -71,7 +71,7 @@ class DupeGuru(DupeGuruBase, QObject):
self._setupActions() self._setupActions()
self._update_options() self._update_options()
self.recentResults = Recent(self, 'recentResults') self.recentResults = Recent(self, 'recentResults')
self.recentResults.mustOpenItem.connect(self.load_from) self.recentResults.mustOpenItem.connect(self.model.load_from)
self.resultWindow = self._create_result_window() self.resultWindow = self._create_result_window()
self._progress = Progress(self.resultWindow) self._progress = Progress(self.resultWindow)
self.directories_dialog = DirectoriesDialog(self.resultWindow, self) self.directories_dialog = DirectoriesDialog(self.resultWindow, self)
@ -81,16 +81,16 @@ class DupeGuru(DupeGuruBase, QObject):
self.about_box = AboutBox(self.resultWindow, self) self.about_box = AboutBox(self.resultWindow, self)
self.reg = Registration(self) self.reg = Registration(self.model)
self.set_registration(self.prefs.registration_code, self.prefs.registration_email) self.model.set_registration(self.prefs.registration_code, self.prefs.registration_email)
if self.should_show_fairware_reminder: if self.model.should_show_fairware_reminder:
# The timer scheme is because if the nag is not shown before the application is # The timer scheme is because if the nag is not shown before the application is
# completely initialized, the nag will be shown before the app shows up in the task bar # completely initialized, the nag will be shown before the app shows up in the task bar
# In some circumstances, the nag is hidden by other window, which may make the user think # In some circumstances, the nag is hidden by other window, which may make the user think
# that the application haven't launched. # that the application haven't launched.
QTimer.singleShot(0, self.reg.show_nag) QTimer.singleShot(0, self.reg.show_nag)
self.directories_dialog.show() self.directories_dialog.show()
self.load() self.model.load()
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate) self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
self.connect(self._progress, SIGNAL('finished(QString)'), self.job_finished) self.connect(self._progress, SIGNAL('finished(QString)'), self.job_finished)
@ -120,10 +120,10 @@ class DupeGuru(DupeGuruBase, QObject):
self.about_box.registeredEmailLabel.setText(self.prefs.registration_email) self.about_box.registeredEmailLabel.setText(self.prefs.registration_email)
def _update_options(self): def _update_options(self):
self.scanner.mix_file_kind = self.prefs.mix_file_kind self.model.scanner.mix_file_kind = self.prefs.mix_file_kind
self.options['escape_filter_regexp'] = self.prefs.use_regexp self.model.options['escape_filter_regexp'] = self.prefs.use_regexp
self.options['clean_empty_dirs'] = self.prefs.remove_empty_folders self.model.options['clean_empty_dirs'] = self.prefs.remove_empty_folders
self.options['ignore_hardlink_matches'] = self.prefs.ignore_hardlink_matches self.model.options['ignore_hardlink_matches'] = self.prefs.ignore_hardlink_matches
#--- Virtual #--- Virtual
def _create_details_dialog(self, parent): def _create_details_dialog(self, parent):
@ -138,44 +138,15 @@ class DupeGuru(DupeGuruBase, QObject):
def _create_preferences_dialog(self, parent): def _create_preferences_dialog(self, parent):
raise NotImplementedError() raise NotImplementedError()
#--- Override #--- Public
@staticmethod
def _open_path(path):
url = QUrl.fromLocalFile(str(path))
QDesktopServices.openUrl(url)
@staticmethod
def _reveal_path(path):
DupeGuru._open_path(path[:-1])
def _start_job(self, jobid, func, *args):
title = JOBID2TITLE[jobid]
try:
j = self._progress.create_job()
args = tuple([j] + list(args))
self._progress.run(jobid, title, func, args=args)
except job.JobInProgressError:
msg = trmsg("TaskHangingMsg")
QMessageBox.information(self.resultWindow, 'Action in progress', msg)
def _get_default(self, key):
return self.prefs.get_value(key)
def _set_default(self, key, value):
self.prefs.set_value(key, value)
def _show_extra_fairware_reminder(self):
dialog = ExtraFairwareReminder(self.directories_dialog, self)
dialog.exec_()
def add_selected_to_ignore_list(self): def add_selected_to_ignore_list(self):
dupes = self.without_ref(self.selected_dupes) dupes = self.model.without_ref(self.model.selected_dupes)
if not dupes: if not dupes:
return return
title = tr("Add to Ignore List") title = tr("Add to Ignore List")
msg = trmsg("IgnoreConfirmMsg").format(len(dupes)) msg = trmsg("IgnoreConfirmMsg").format(len(dupes))
if self.confirm(title, msg): if self.confirm(title, msg):
DupeGuruBase.add_selected_to_ignore_list(self) self.model.add_selected_to_ignore_list(self)
def copy_or_move_marked(self, copy): def copy_or_move_marked(self, copy):
opname = tr("copy") if copy else tr("move") opname = tr("copy") if copy else tr("move")
@ -185,18 +156,17 @@ class DupeGuru(DupeGuruBase, QObject):
if not destination: if not destination:
return return
recreate_path = self.prefs.destination_type recreate_path = self.prefs.destination_type
DupeGuruBase.copy_or_move_marked(self, copy, destination, recreate_path) self.model.copy_or_move_marked(self, copy, destination, recreate_path)
def remove_selected(self): def remove_selected(self):
dupes = self.without_ref(self.selected_dupes) dupes = self.model.without_ref(self.model.selected_dupes)
if not dupes: if not dupes:
return return
title = tr("Remove duplicates") title = tr("Remove duplicates")
msg = trmsg("FileRemovalConfirmMsg").format(len(dupes)) msg = trmsg("FileRemovalConfirmMsg").format(len(dupes))
if self.confirm(title, msg): if self.confirm(title, msg):
DupeGuruBase.remove_selected(self) self.model.remove_selected(self)
#--- Public
def askForRegCode(self): def askForRegCode(self):
self.reg.ask_for_code() self.reg.ask_for_code()
@ -209,7 +179,7 @@ class DupeGuru(DupeGuruBase, QObject):
def invokeCustomCommand(self): def invokeCustomCommand(self):
cmd = self.prefs.custom_command cmd = self.prefs.custom_command
if cmd: if cmd:
self.invoke_command(cmd) self.model.invoke_command(cmd)
else: else:
msg = trmsg("NoCustomCommandMsg") msg = trmsg("NoCustomCommandMsg")
QMessageBox.warning(self.resultWindow, tr("Custom Command"), msg) QMessageBox.warning(self.resultWindow, tr("Custom Command"), msg)
@ -227,21 +197,21 @@ class DupeGuru(DupeGuruBase, QObject):
def application_will_terminate(self): def application_will_terminate(self):
self.willSavePrefs.emit() self.willSavePrefs.emit()
self.prefs.save() self.prefs.save()
self.save() self.model.save()
def checkForUpdateTriggered(self): def checkForUpdateTriggered(self):
QProcess.execute('updater.exe', ['/checknow']) QProcess.execute('updater.exe', ['/checknow'])
def job_finished(self, jobid): def job_finished(self, jobid):
self._job_completed(jobid) self.model._job_completed(jobid)
if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE): if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE):
if self.results.problems: if self.model.results.problems:
self.problemDialog.show() self.problemDialog.show()
else: else:
msg = trmsg("OperationSuccessMsg") msg = trmsg("OperationSuccessMsg")
QMessageBox.information(self.resultWindow, tr("Operation Complete"), msg) QMessageBox.information(self.resultWindow, tr("Operation Complete"), msg)
elif jobid == JOB_SCAN: elif jobid == JOB_SCAN:
if not self.results.groups: if not self.model.results.groups:
title = tr("Scan complete") title = tr("Scan complete")
msg = trmsg("NoDuplicateFoundMsg") msg = trmsg("NoDuplicateFoundMsg")
QMessageBox.information(self.resultWindow, title, msg) QMessageBox.information(self.resultWindow, title, msg)
@ -251,12 +221,12 @@ class DupeGuru(DupeGuruBase, QObject):
self.showResultsWindow() self.showResultsWindow()
def openDebugLogTriggered(self): def openDebugLogTriggered(self):
debugLogPath = op.join(self.appdata, 'debug.log') debugLogPath = op.join(self.model.appdata, 'debug.log')
self._open_path(debugLogPath) self.open_path(debugLogPath)
def preferencesTriggered(self): def preferencesTriggered(self):
self.preferences_dialog.load() self.preferences_dialog.load()
result = self.preferences_dialog.exec_() result = self.preferences_dialog.exec()
if result == QDialog.Accepted: if result == QDialog.Accepted:
self.preferences_dialog.save() self.preferences_dialog.save()
self.prefs.save() self.prefs.save()
@ -276,3 +246,33 @@ class DupeGuru(DupeGuruBase, QObject):
url = QUrl.fromLocalFile(op.abspath(op.join(base_path, 'index.html'))) url = QUrl.fromLocalFile(op.abspath(op.join(base_path, 'index.html')))
QDesktopServices.openUrl(url) QDesktopServices.openUrl(url)
#--- model --> view
@staticmethod
def open_path(path):
url = QUrl.fromLocalFile(str(path))
QDesktopServices.openUrl(url)
@staticmethod
def reveal_path(path):
DupeGuru.open_path(path[:-1])
def start_job(self, jobid, func, *args):
title = JOBID2TITLE[jobid]
try:
j = self._progress.create_job()
args = tuple([j] + list(args))
self._progress.run(jobid, title, func, args=args)
except job.JobInProgressError:
msg = trmsg("TaskHangingMsg")
QMessageBox.information(self.resultWindow, 'Action in progress', msg)
def get_default(self, key):
return self.prefs.get_value(key)
def set_default(self, key, value):
self.prefs.set_value(key, value)
def show_extra_fairware_reminder(self):
dialog = ExtraFairwareReminder(self.directories_dialog, self)
dialog.exec()

View File

@ -18,7 +18,7 @@ class DetailsDialog(QDialog):
def __init__(self, parent, app): def __init__(self, parent, app):
QDialog.__init__(self, parent, Qt.Tool) QDialog.__init__(self, parent, Qt.Tool)
self.app = app self.app = app
self.model = DetailsPanel(self, app) self.model = DetailsPanel(self, app.model)
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)

View File

@ -45,7 +45,7 @@ class DirectoriesDialog(QMainWindow):
self.treeView.selectionModel().selectionChanged.connect(self.selectionChanged) self.treeView.selectionModel().selectionChanged.connect(self.selectionChanged)
self.app.recentResults.itemsChanged.connect(self._updateLoadResultsButton) self.app.recentResults.itemsChanged.connect(self._updateLoadResultsButton)
self.recentFolders.itemsChanged.connect(self._updateAddButton) self.recentFolders.itemsChanged.connect(self._updateAddButton)
self.recentFolders.mustOpenItem.connect(self.app.add_directory) self.recentFolders.mustOpenItem.connect(self.app.model.add_directory)
self.directoriesModel.foldersAdded.connect(self.directoriesModelAddedFolders) self.directoriesModel.foldersAdded.connect(self.directoriesModelAddedFolders)
self.app.willSavePrefs.connect(self.appWillSavePrefs) self.app.willSavePrefs.connect(self.appWillSavePrefs)
@ -170,7 +170,7 @@ class DirectoriesDialog(QMainWindow):
#--- QWidget overrides #--- QWidget overrides
def closeEvent(self, event): def closeEvent(self, event):
event.accept() event.accept()
if self.app.results.is_modified: if self.app.model.results.is_modified:
title = tr("Unsaved results") title = tr("Unsaved results")
msg = trmsg("ReallyWantToQuitMsg") msg = trmsg("ReallyWantToQuitMsg")
if not self.app.confirm(title, msg): if not self.app.confirm(title, msg):
@ -186,7 +186,7 @@ class DirectoriesDialog(QMainWindow):
if not dirpath: if not dirpath:
return return
self.lastAddedFolder = dirpath self.lastAddedFolder = dirpath
self.app.add_directory(dirpath) self.app.model.add_directory(dirpath)
self.recentFolders.insertItem(dirpath) self.recentFolders.insertItem(dirpath)
def appWillSavePrefs(self): def appWillSavePrefs(self):
@ -201,7 +201,7 @@ class DirectoriesDialog(QMainWindow):
files = ';;'.join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")]) files = ';;'.join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")])
destination = QFileDialog.getOpenFileName(self, title, '', files) destination = QFileDialog.getOpenFileName(self, title, '', files)
if destination: if destination:
self.app.load_from(destination) self.app.model.load_from(destination)
self.app.recentResults.insertItem(destination) self.app.recentResults.insertItem(destination)
def removeFolderButtonClicked(self): def removeFolderButtonClicked(self):
@ -212,16 +212,16 @@ class DirectoriesDialog(QMainWindow):
node = index.internalPointer() node = index.internalPointer()
if node.parent is None: if node.parent is None:
row = index.row() row = index.row()
self.app.remove_directory(row) self.app.model.remove_directory(row)
def scanButtonClicked(self): def scanButtonClicked(self):
title = tr("Start a new scan") title = tr("Start a new scan")
if self.app.results.is_modified: if self.app.model.results.is_modified:
msg = trmsg("ReallyWantToContinueMsg") msg = trmsg("ReallyWantToContinueMsg")
if not self.app.confirm(title, msg): if not self.app.confirm(title, msg):
return return
try: try:
self.app.start_scanning() self.app.model.start_scanning()
except NoScannableFileError: except NoScannableFileError:
msg = trmsg("NoScannableFileMsg") msg = trmsg("NoScannableFileMsg")
QMessageBox.warning(self, title, msg) QMessageBox.warning(self, title, msg)

View File

@ -60,7 +60,7 @@ class DirectoriesDelegate(QStyledItemDelegate):
class DirectoriesModel(TreeModel): class DirectoriesModel(TreeModel):
def __init__(self, app): def __init__(self, app):
TreeModel.__init__(self) TreeModel.__init__(self)
self.model = DirectoryTree(self, app) self.model = DirectoryTree(self, app.model)
self.model.connect() self.model.connect()
def _createNode(self, ref, row): def _createNode(self, ref, row):

View File

@ -55,7 +55,7 @@ class PrioritizeDialog(QDialog):
QDialog.__init__(self, parent, flags) QDialog.__init__(self, parent, flags)
self.app = app self.app = app
self._setupUi() self._setupUi()
self.model = PrioritizeDialogModel(view=self, app=app) self.model = PrioritizeDialogModel(view=self, app=app.model)
self.categoryList = ComboboxModel(model=self.model.category_list, view=self.categoryCombobox) self.categoryList = ComboboxModel(model=self.model.category_list, view=self.categoryCombobox)
self.criteriaList = ListviewModel(model=self.model.criteria_list, view=self.criteriaListView) self.criteriaList = ListviewModel(model=self.model.criteria_list, view=self.criteriaListView)
self.prioritizationList = PrioritizationList(model=self.model.prioritization_list, view=self.prioritizationListView) self.prioritizationList = PrioritizationList(model=self.model.prioritization_list, view=self.prioritizationListView)

View File

@ -20,7 +20,7 @@ class ProblemDialog(QDialog):
QDialog.__init__(self, parent, flags) QDialog.__init__(self, parent, flags)
self.app = app self.app = app
self._setupUi() self._setupUi()
self.model = ProblemDialogModel(view=self, app=app) self.model = ProblemDialogModel(view=self, app=app.model)
self.table = ProblemTable(problem_dialog=self, view=self.tableView) self.table = ProblemTable(problem_dialog=self, view=self.tableView)
self.model.connect() self.model.connect()
self.table.model.connect() self.table.model.connect()

View File

@ -140,7 +140,7 @@ class ResultWindow(QMainWindow):
# Columns menu # Columns menu
menu = self.menuColumns menu = self.menuColumns
self._column_actions = [] self._column_actions = []
for index, column in enumerate(self.app.data.COLUMNS): for index, column in enumerate(self.app.model.data.COLUMNS):
action = menu.addAction(column.display) action = menu.addAction(column.display)
action.setCheckable(True) action.setCheckable(True)
action.column_index = index action.column_index = index
@ -224,7 +224,7 @@ class ResultWindow(QMainWindow):
self.actionsButton.showMenu() self.actionsButton.showMenu()
def addToIgnoreListTriggered(self): def addToIgnoreListTriggered(self):
self.app.add_selected_to_ignore_list() self.app.model.add_selected_to_ignore_list()
def applyFilterTriggered(self): def applyFilterTriggered(self):
title = tr("Apply Filter") title = tr("Apply Filter")
@ -234,34 +234,34 @@ class ResultWindow(QMainWindow):
if not ok: if not ok:
return return
answer = str(answer) answer = str(answer)
self.app.apply_filter(answer) self.app.model.apply_filter(answer)
self._last_filter = answer self._last_filter = answer
def cancelFilterTriggered(self): def cancelFilterTriggered(self):
self.app.apply_filter('') self.app.model.apply_filter('')
def clearIgnoreListTriggered(self): def clearIgnoreListTriggered(self):
title = tr("Clear Ignore List") title = tr("Clear Ignore List")
count = len(self.app.scanner.ignore_list) count = len(self.app.model.scanner.ignore_list)
if not count: if not count:
QMessageBox.information(self, title, trmsg("NothingToClearMsg")) QMessageBox.information(self, title, trmsg("NothingToClearMsg"))
return return
msg = trmsg("ClearIgnoreListConfirmMsg").format(count) msg = trmsg("ClearIgnoreListConfirmMsg").format(count)
if self.app.confirm(title, msg, QMessageBox.No): if self.app.confirm(title, msg, QMessageBox.No):
self.app.scanner.ignore_list.Clear() self.app.model.scanner.ignore_list.Clear()
QMessageBox.information(self, title, trmsg("IgnoreListClearedMsg")) QMessageBox.information(self, title, trmsg("IgnoreListClearedMsg"))
def copyTriggered(self): def copyTriggered(self):
self.app.copy_or_move_marked(True) self.app.model.copy_or_move_marked(True)
def deleteTriggered(self): def deleteTriggered(self):
count = self.app.results.mark_count count = self.app.model.results.mark_count
if not count: if not count:
return return
title = tr("Delete duplicates") title = tr("Delete duplicates")
msg = trmsg("SendToTrashConfirmMsg").format(count) msg = trmsg("SendToTrashConfirmMsg").format(count)
if self.app.confirm(title, msg): if self.app.confirm(title, msg):
self.app.delete_marked() self.app.model.delete_marked()
def deltaTriggered(self): def deltaTriggered(self):
self.resultsModel.delta_values = self.actionDelta.isChecked() self.resultsModel.delta_values = self.actionDelta.isChecked()
@ -272,42 +272,42 @@ class ResultWindow(QMainWindow):
def exportTriggered(self): def exportTriggered(self):
h = self.resultsView.horizontalHeader() h = self.resultsView.horizontalHeader()
column_ids = [] column_ids = []
for i in range(len(self.app.data.COLUMNS)): for i in range(len(self.app.model.data.COLUMNS)):
if not h.isSectionHidden(i): if not h.isSectionHidden(i):
column_ids.append(str(i)) column_ids.append(str(i))
exported_path = self.app.export_to_xhtml(column_ids) exported_path = self.app.model.export_to_xhtml(column_ids)
url = QUrl.fromLocalFile(exported_path) url = QUrl.fromLocalFile(exported_path)
QDesktopServices.openUrl(url) QDesktopServices.openUrl(url)
def hardlinkTriggered(self): def hardlinkTriggered(self):
count = self.app.results.mark_count count = self.app.model.results.mark_count
if not count: if not count:
return return
title = tr("Delete and hardlink duplicates") title = tr("Delete and hardlink duplicates")
msg = trmsg("HardlinkConfirmMsg").format(count) msg = trmsg("HardlinkConfirmMsg").format(count)
if self.app.confirm(title, msg): if self.app.confirm(title, msg):
self.app.delete_marked(replace_with_hardlinks=True) self.app.model.delete_marked(replace_with_hardlinks=True)
def makeReferenceTriggered(self): def makeReferenceTriggered(self):
self.app.make_selected_reference() self.app.model.make_selected_reference()
def markAllTriggered(self): def markAllTriggered(self):
self.app.mark_all() self.app.model.mark_all()
def markInvertTriggered(self): def markInvertTriggered(self):
self.app.mark_invert() self.app.model.mark_invert()
def markNoneTriggered(self): def markNoneTriggered(self):
self.app.mark_none() self.app.model.mark_none()
def markSelectedTriggered(self): def markSelectedTriggered(self):
self.app.toggle_selected_mark_state() self.app.model.toggle_selected_mark_state()
def moveTriggered(self): def moveTriggered(self):
self.app.copy_or_move_marked(False) self.app.model.copy_or_move_marked(False)
def openTriggered(self): def openTriggered(self):
self.app.open_selected() self.app.model.open_selected()
def powerMarkerTriggered(self): def powerMarkerTriggered(self):
self.resultsModel.power_marker = self.actionPowerMarker.isChecked() self.resultsModel.power_marker = self.actionPowerMarker.isChecked()
@ -316,16 +316,16 @@ class ResultWindow(QMainWindow):
self.app.show_preferences() self.app.show_preferences()
def removeMarkedTriggered(self): def removeMarkedTriggered(self):
count = self.app.results.mark_count count = self.app.model.results.mark_count
if not count: if not count:
return return
title = tr("Remove duplicates") title = tr("Remove duplicates")
msg = trmsg("FileRemovalConfirmMsg").format(count) msg = trmsg("FileRemovalConfirmMsg").format(count)
if self.app.confirm(title, msg): if self.app.confirm(title, msg):
self.app.remove_marked() self.app.model.remove_marked()
def removeSelectedTriggered(self): def removeSelectedTriggered(self):
self.app.remove_selected() self.app.model.remove_selected()
def renameTriggered(self): def renameTriggered(self):
self.resultsView.edit(self.resultsView.selectionModel().currentIndex()) self.resultsView.edit(self.resultsView.selectionModel().currentIndex())
@ -337,7 +337,7 @@ class ResultWindow(QMainWindow):
dlg.model.perform_reprioritization() dlg.model.perform_reprioritization()
def revealTriggered(self): def revealTriggered(self):
self.app.reveal_selected() self.app.model.reveal_selected()
def saveResultsTriggered(self): def saveResultsTriggered(self):
title = trmsg("SelectResultToSaveMsg") title = trmsg("SelectResultToSaveMsg")
@ -346,7 +346,7 @@ class ResultWindow(QMainWindow):
if destination: if destination:
if not destination.endswith('.dupeguru'): if not destination.endswith('.dupeguru'):
destination = '{}.dupeguru'.format(destination) destination = '{}.dupeguru'.format(destination)
self.app.save_as(destination) self.app.model.save_as(destination)
self.app.recentResults.insertItem(destination) self.app.recentResults.insertItem(destination)
#--- Events #--- Events
@ -355,7 +355,7 @@ class ResultWindow(QMainWindow):
h = self.resultsView.horizontalHeader() h = self.resultsView.horizontalHeader()
widths = [] widths = []
visible = [] visible = []
for i in range(len(self.app.data.COLUMNS)): for i in range(len(self.app.model.data.COLUMNS)):
widths.append(h.sectionSize(i)) widths.append(h.sectionSize(i))
visible.append(not h.isSectionHidden(i)) visible.append(not h.isSectionHidden(i))
prefs.columns_width = widths prefs.columns_width = widths
@ -377,8 +377,8 @@ class ResultWindow(QMainWindow):
self.actionActions.menu().exec_(event.globalPos()) self.actionActions.menu().exec_(event.globalPos())
def resultsDoubleClicked(self): def resultsDoubleClicked(self):
self.app.open_selected() self.app.model.open_selected()
def resultsSpacePressed(self): def resultsSpacePressed(self):
self.app.toggle_selected_mark_state() self.app.model.toggle_selected_mark_state()

View File

@ -15,10 +15,10 @@ from core.gui.result_table import ResultTable as ResultTableModel
class ResultsModel(Table): class ResultsModel(Table):
def __init__(self, app, view): def __init__(self, app, view):
model = ResultTableModel(self, app) model = ResultTableModel(self, app.model)
self._app = app self._app = app
self._data = app.data self._data = app.model.data
self._delta_columns = app.data.DELTA_COLUMNS self._delta_columns = app.model.data.DELTA_COLUMNS
Table.__init__(self, model, view) Table.__init__(self, model, view)
self.model.connect() self.model.connect()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras # Created By: Virgil Dupras
# Created On: 2010-02-12 # Created On: 2010-02-12
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net) # Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
@ -12,7 +11,7 @@ from core.gui.stats_label import StatsLabel as StatsLabelModel
class StatsLabel(object): class StatsLabel(object):
def __init__(self, app, view): def __init__(self, app, view):
self.view = view self.view = view
self.model = StatsLabelModel(self, app) self.model = StatsLabelModel(self, app.model)
self.model.connect() self.model.connect()
def refresh(self): def refresh(self):

View File

@ -22,16 +22,16 @@ class DupeGuru(DupeGuruBase):
DupeGuruBase.__init__(self, data) DupeGuruBase.__init__(self, data)
def _setup(self): def _setup(self):
self.scanner = scanner.ScannerME() self.model.scanner = scanner.ScannerME()
self.directories.fileclasses = [fs.MusicFile] self.model.directories.fileclasses = [fs.MusicFile]
DupeGuruBase._setup(self) DupeGuruBase._setup(self)
def _update_options(self): def _update_options(self):
DupeGuruBase._update_options(self) DupeGuruBase._update_options(self)
self.scanner.min_match_percentage = self.prefs.filter_hardness self.model.scanner.min_match_percentage = self.prefs.filter_hardness
self.scanner.scan_type = self.prefs.scan_type self.model.scanner.scan_type = self.prefs.scan_type
self.scanner.word_weighting = self.prefs.word_weighting self.model.scanner.word_weighting = self.prefs.word_weighting
self.scanner.match_similar_words = self.prefs.match_similar self.model.scanner.match_similar_words = self.prefs.match_similar
scanned_tags = set() scanned_tags = set()
if self.prefs.scan_tag_track: if self.prefs.scan_tag_track:
scanned_tags.add('track') scanned_tags.add('track')
@ -45,7 +45,7 @@ class DupeGuru(DupeGuruBase):
scanned_tags.add('genre') scanned_tags.add('genre')
if self.prefs.scan_tag_year: if self.prefs.scan_tag_year:
scanned_tags.add('year') scanned_tags.add('year')
self.scanner.scanned_tags = scanned_tags self.model.scanner.scanned_tags = scanned_tags
def _create_details_dialog(self, parent): def _create_details_dialog(self, parent):
return DetailsDialog(parent, self) return DetailsDialog(parent, self)

View File

@ -74,16 +74,16 @@ class DupeGuru(DupeGuruBase):
DupeGuruBase.__init__(self, data_pe) DupeGuruBase.__init__(self, data_pe)
def _setup(self): def _setup(self):
self.scanner = ScannerPE() self.model.scanner = ScannerPE()
self.directories.fileclasses = [File] self.model.directories.fileclasses = [File]
self.scanner.cache_path = op.join(self.appdata, 'cached_pictures.db') self.model.scanner.cache_path = op.join(self.model.appdata, 'cached_pictures.db')
DupeGuruBase._setup(self) DupeGuruBase._setup(self)
def _update_options(self): def _update_options(self):
DupeGuruBase._update_options(self) DupeGuruBase._update_options(self)
self.scanner.scan_type = self.prefs.scan_type self.model.scanner.scan_type = self.prefs.scan_type
self.scanner.match_scaled = self.prefs.match_scaled self.model.scanner.match_scaled = self.prefs.match_scaled
self.scanner.threshold = self.prefs.filter_hardness self.model.scanner.threshold = self.prefs.filter_hardness
def _create_details_dialog(self, parent): def _create_details_dialog(self, parent):
return DetailsDialog(parent, self) return DetailsDialog(parent, self)

View File

@ -60,10 +60,10 @@ class DetailsDialog(DetailsDialogBase):
self.verticalLayout.addWidget(self.tableView) self.verticalLayout.addWidget(self.tableView)
def _update(self): def _update(self):
if not self.app.selected_dupes: if not self.app.model.selected_dupes:
return return
dupe = self.app.selected_dupes[0] dupe = self.app.model.selected_dupes[0]
group = self.app.results.get_group_of_duplicate(dupe) group = self.app.model.results.get_group_of_duplicate(dupe)
ref = group.ref ref = group.ref
self.selectedPixmap = QPixmap(str(dupe.path)) self.selectedPixmap = QPixmap(str(dupe.path))

View File

@ -37,12 +37,12 @@ class DupeGuru(DupeGuruBase):
def _update_options(self): def _update_options(self):
DupeGuruBase._update_options(self) DupeGuruBase._update_options(self)
self.scanner.min_match_percentage = self.prefs.filter_hardness self.model.scanner.min_match_percentage = self.prefs.filter_hardness
self.scanner.scan_type = self.prefs.scan_type self.model.scanner.scan_type = self.prefs.scan_type
self.scanner.word_weighting = self.prefs.word_weighting self.model.scanner.word_weighting = self.prefs.word_weighting
self.scanner.match_similar_words = self.prefs.match_similar self.model.scanner.match_similar_words = self.prefs.match_similar
threshold = self.prefs.small_file_threshold if self.prefs.ignore_small_files else 0 threshold = self.prefs.small_file_threshold if self.prefs.ignore_small_files else 0
self.scanner.size_threshold = threshold * 1024 # threshold is in KB. the scanner wants bytes self.model.scanner.size_threshold = threshold * 1024 # threshold is in KB. the scanner wants bytes
def _create_details_dialog(self, parent): def _create_details_dialog(self, parent):
return DetailsDialog(parent, self) return DetailsDialog(parent, self)