[#32] Internationalized the qt layer and localized it to French.

In the process of doing so, I also added a new preferences_dialog base class to reduce code duplication in the three pref dialogs (I didn't want to copy/paste the language combobox addition three times).
This commit is contained in:
Virgil Dupras 2011-01-21 13:57:54 +01:00
parent 7f8a357019
commit 2c127adf59
28 changed files with 1023 additions and 544 deletions

View File

@ -17,3 +17,5 @@ cocoa/*/Info.plist
cocoa/*/build
cocoa/*/dg_cocoa.plugin
qt/base/*_rc.py
qt/lang/fr.qm
qt/lang/en.qm

View File

@ -1 +1,2 @@
__version__ = '5.10.4'
__version__ = '5.10.4'
__appname__ = 'dupeGuru Music Edition'

View File

@ -1 +1,2 @@
__version__ = '1.11.3'
__version__ = '1.11.3'
__appname__ = 'dupeGuru Picture Edition'

View File

@ -1 +1,2 @@
__version__ = '2.12.3'
__appname__ = 'dupeGuru'

View File

@ -15,9 +15,10 @@ from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox, QAp
from jobprogress import job
from jobprogress.qt import Progress
from hscommon.trans import tr, trmsg
from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE
from qtlib.about_box import AboutBox
from qtlib.recent import Recent
from qtlib.reg import Registration
@ -29,11 +30,11 @@ from .problem_dialog import ProblemDialog
from .util import createActions
JOBID2TITLE = {
JOB_SCAN: "Scanning for duplicates",
JOB_LOAD: "Loading",
JOB_MOVE: "Moving",
JOB_COPY: "Copying",
JOB_DELETE: "Sending files to the recycle bin",
JOB_SCAN: tr("Scanning for duplicates"),
JOB_LOAD: tr("Loading"),
JOB_MOVE: tr("Moving"),
JOB_COPY: tr("Copying"),
JOB_DELETE: tr("Sending files to the recycle bin"),
}
class DupeGuru(DupeGuruBase, QObject):
@ -86,13 +87,13 @@ class DupeGuru(DupeGuruBase, QObject):
# Setup actions that are common to both the directory dialog and the results window.
# (name, shortcut, icon, desc, func)
ACTIONS = [
('actionQuit', 'Ctrl+Q', '', "Quit", self.quitTriggered),
('actionPreferences', 'Ctrl+P', '', "Preferences", self.preferencesTriggered),
('actionShowHelp', 'F1', '', "dupeGuru Help", self.showHelpTriggered),
('actionAbout', '', '', "About dupeGuru", self.showAboutBoxTriggered),
('actionRegister', '', '', "Register dupeGuru", self.registerTriggered),
('actionCheckForUpdate', '', '', "Check for Update", self.checkForUpdateTriggered),
('actionOpenDebugLog', '', '', "Open Debug Log", self.openDebugLogTriggered),
('actionQuit', 'Ctrl+Q', '', tr("Quit"), self.quitTriggered),
('actionPreferences', 'Ctrl+P', '', tr("Preferences"), self.preferencesTriggered),
('actionShowHelp', 'F1', '', tr("dupeGuru Help"), self.showHelpTriggered),
('actionAbout', '', '', tr("About dupeGuru"), self.showAboutBoxTriggered),
('actionRegister', '', '', tr("Register dupeGuru"), self.registerTriggered),
('actionCheckForUpdate', '', '', tr("Check for Update"), self.checkForUpdateTriggered),
('actionOpenDebugLog', '', '', tr("Open Debug Log"), self.openDebugLogTriggered),
]
createActions(ACTIONS, self)
@ -139,21 +140,21 @@ class DupeGuru(DupeGuruBase, QObject):
args = tuple([j] + list(args))
self._progress.run(jobid, title, func, args=args)
except job.JobInProgressError:
msg = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
msg = trmsg("TaskHangingMsg")
QMessageBox.information(self.resultWindow, 'Action in progress', msg)
def add_selected_to_ignore_list(self):
dupes = self.without_ref(self.selected_dupes)
if not dupes:
return
title = "Add to Ignore List"
msg = "All selected {0} matches are going to be ignored in all subsequent scans. Continue?".format(len(dupes))
title = tr("Add to Ignore List")
msg = trmsg("IgnoreConfirmMsg").format(len(dupes))
if self.confirm(title, msg):
DupeGuruBase.add_selected_to_ignore_list(self)
def copy_or_move_marked(self, copy):
opname = 'copy' if copy else 'move'
title = "Select a directory to {0} marked files to".format(opname)
opname = tr("copy") if copy else tr("move")
title = trmsg("SelectCopyOrMoveDestinationMsg").format(opname)
flags = QFileDialog.ShowDirsOnly
destination = str(QFileDialog.getExistingDirectory(self.resultWindow, title, '', flags))
if not destination:
@ -165,8 +166,8 @@ class DupeGuru(DupeGuruBase, QObject):
dupes = self.without_ref(self.selected_dupes)
if not dupes:
return
title = "Remove duplicates"
msg = "You are about to remove {0} files from results. Continue?".format(len(dupes))
title = tr("Remove duplicates")
msg = trmsg("FileRemovalConfirmMsg").format(len(dupes))
if self.confirm(title, msg):
DupeGuruBase.remove_selected(self)
@ -185,8 +186,8 @@ class DupeGuru(DupeGuruBase, QObject):
if cmd:
self.invoke_command(cmd)
else:
msg = "You have no custom command set up. Please, set it up in your preferences."
QMessageBox.warning(self.resultWindow, 'Custom Command', msg)
msg = trmsg("NoCustomCommandMsg")
QMessageBox.warning(self.resultWindow, tr("Custom Command"), msg)
def show_details(self):
self.details_dialog.show()
@ -212,12 +213,12 @@ class DupeGuru(DupeGuruBase, QObject):
if self.results.problems:
self.problemDialog.show()
else:
msg = "All files were processed successfully."
QMessageBox.information(self.resultWindow, 'Operation Complete', msg)
msg = trmsg("OperationSuccessMsg")
QMessageBox.information(self.resultWindow, tr("Operation Complete"), msg)
elif jobid == JOB_SCAN:
if not self.results.groups:
title = "Scan complete"
msg = "No duplicates found."
title = tr("Scan complete")
msg = trmsg("NoDuplicateFoundMsg")
QMessageBox.information(self.resultWindow, title, msg)
else:
self.showResultsWindow()

View File

@ -9,7 +9,9 @@
from PyQt4.QtCore import Qt, SIGNAL, QAbstractTableModel
from PyQt4.QtGui import QHeaderView, QTableView
HEADER = ['Attribute', 'Selected', 'Reference']
from hscommon.trans import tr
HEADER = [tr("Attribute"), tr("Selected"), tr("Reference")]
class DetailsModel(QAbstractTableModel):
def __init__(self, model):

View File

@ -1,5 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="en.qm">../lang/en.qm</file>
<file alias="fr.qm">../lang/fr.qm</file>
<file alias="qt_fr.qm">../lang/qt_fr.qm</file>
<file alias="logo_pe">../../images/dgpe_logo_32.png</file>

View File

@ -11,6 +11,7 @@ from PyQt4.QtGui import (QWidget, QFileDialog, QHeaderView, QVBoxLayout, QHBoxLa
QAbstractItemView, QSpacerItem, QSizePolicy, QPushButton, QApplication, QMessageBox, QMainWindow,
QMenuBar, QMenu, QIcon, QPixmap)
from hscommon.trans import tr, trmsg
from qtlib.recent import Recent
from core.app import NoScannableFileError
@ -49,9 +50,9 @@ class DirectoriesDialog(QMainWindow):
def _setupActions(self):
# (name, shortcut, icon, desc, func)
ACTIONS = [
('actionLoadResults', 'Ctrl+L', '', "Load Results...", self.loadResultsTriggered),
('actionShowResultsWindow', '', '', "Results Window", self.app.showResultsWindow),
('actionAddFolder', '', '', "Add Folder...", self.addFolderTriggered),
('actionLoadResults', 'Ctrl+L', '', tr("Load Results..."), self.loadResultsTriggered),
('actionShowResultsWindow', '', '', tr("Results Window"), self.app.showResultsWindow),
('actionAddFolder', '', '', tr("Add Folder..."), self.addFolderTriggered),
]
createActions(ACTIONS, self)
@ -59,13 +60,13 @@ class DirectoriesDialog(QMainWindow):
self.menubar = QMenuBar(self)
self.menubar.setGeometry(QRect(0, 0, 42, 22))
self.menuFile = QMenu(self.menubar)
self.menuFile.setTitle("File")
self.menuFile.setTitle(tr("File"))
self.menuView = QMenu(self.menubar)
self.menuView.setTitle("View")
self.menuView.setTitle(tr("View"))
self.menuHelp = QMenu(self.menubar)
self.menuHelp.setTitle("Help")
self.menuHelp.setTitle(tr("Help"))
self.menuLoadRecent = QMenu(self.menuFile)
self.menuLoadRecent.setTitle("Load Recent Results")
self.menuLoadRecent.setTitle(tr("Load Recent Results"))
self.setMenuBar(self.menubar)
self.menuFile.addAction(self.actionLoadResults)
@ -126,10 +127,10 @@ class DirectoriesDialog(QMainWindow):
spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.loadResultsButton = QPushButton(self.centralwidget)
self.loadResultsButton.setText("Load Results")
self.loadResultsButton.setText(tr("Load Results"))
self.horizontalLayout.addWidget(self.loadResultsButton)
self.scanButton = QPushButton(self.centralwidget)
self.scanButton.setText("Scan")
self.scanButton.setText(tr("Scan"))
self.scanButton.setDefault(True)
self.horizontalLayout.addWidget(self.scanButton)
self.verticalLayout.addLayout(self.horizontalLayout)
@ -167,8 +168,8 @@ class DirectoriesDialog(QMainWindow):
def closeEvent(self, event):
event.accept()
if self.app.results.is_modified:
title = "Unsaved results"
msg = "You have unsaved results, do you really want to quit?"
title = tr("Unsaved results")
msg = trmsg("ReallyWantToQuitMsg")
if not self.app.confirm(title, msg):
event.ignore()
if event.isAccepted():
@ -176,7 +177,7 @@ class DirectoriesDialog(QMainWindow):
#--- Events
def addFolderTriggered(self):
title = "Select a directory to add to the scanning list"
title = trmsg("SelectFolderToAddMsg")
flags = QFileDialog.ShowDirsOnly
dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags))
if not dirpath:
@ -189,8 +190,8 @@ class DirectoriesDialog(QMainWindow):
self.app.prefs.directoriesWindowRect = self.geometry()
def loadResultsTriggered(self):
title = "Select a results file to load"
files = "dupeGuru Results (*.dupeguru)"
title = trmsg("SelectResultToLoadMsg")
files = tr("dupeGuru Results (*.dupeguru)")
destination = QFileDialog.getOpenFileName(self, title, '', files)
if destination:
self.app.load_from(destination)
@ -207,15 +208,16 @@ class DirectoriesDialog(QMainWindow):
self.app.remove_directory(row)
def scanButtonClicked(self):
title = "Start a new scan"
title = tr("Start a new scan")
# XXX must be triggered on unsaved results
if len(self.app.results.groups) > 0:
msg = "Are you sure you want to start a new duplicate scan?"
msg = trmsg("ReallyWantToContinueMsg")
if not self.app.confirm(title, msg):
return
try:
self.app.start_scanning()
except NoScannableFileError:
msg = "The selected directories contain no scannable file."
msg = trmsg("NoScannableFileMsg")
QMessageBox.warning(self, title, msg)
def selectionChanged(self, selected, deselected):

View File

@ -12,12 +12,13 @@ from PyQt4.QtCore import QModelIndex, Qt, QRect, QEvent, QPoint, QUrl
from PyQt4.QtGui import (QComboBox, QStyledItemDelegate, QMouseEvent, QApplication, QBrush, QStyle,
QStyleOptionComboBox, QStyleOptionViewItemV4)
from hscommon.trans import tr
from qtlib.tree_model import RefNode, TreeModel
from core.gui.directory_tree import DirectoryTree
HEADERS = ['Name', 'State']
STATES = ['Normal', 'Reference', 'Excluded']
HEADERS = [tr("Name"), tr("State")]
STATES = [tr("Normal"), tr("Reference"), tr("Excluded")]
class DirectoriesDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):

View File

@ -29,6 +29,8 @@ class Preferences(PreferencesBase):
self.remove_empty_folders = get('RemoveEmptyFolders', self.remove_empty_folders)
self.destination_type = get('DestinationType', self.destination_type)
self.custom_command = get('CustomCommand', self.custom_command)
self.language = get('Language', self.language)
widths = get('ColumnsWidth', self.columns_width)
# only set nonzero values
for index, width in enumerate(widths[:len(self.columns_width)]):
@ -59,6 +61,7 @@ class Preferences(PreferencesBase):
self.remove_empty_folders = False
self.destination_type = 1
self.custom_command = ''
self.language = ''
self.resultWindowIsMaximized = False
self.resultWindowRect = None
@ -90,6 +93,7 @@ class Preferences(PreferencesBase):
set_('CustomCommand', self.custom_command)
set_('ColumnsWidth', self.columns_width)
set_('ColumnsVisible', self.columns_visible)
set_('Language', self.language)
set_('ResultWindowIsMaximized', self.resultWindowIsMaximized)
self.set_rect('ResultWindowRect', self.resultWindowRect)

View File

@ -0,0 +1,164 @@
# Created By: Virgil Dupras
# Created On: 2011-01-21
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "BSD" 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/bsd_license
import sys
from PyQt4.QtCore import SIGNAL, Qt, QSize
from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox,
QSlider, QSizePolicy, QSpacerItem, QCheckBox, QLineEdit, QMessageBox)
from hscommon.trans import tr, trmsg
class PreferencesDialogBase(QDialog):
def __init__(self, parent, app):
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
QDialog.__init__(self, parent, flags)
self.app = app
self._setupUi()
self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum)
self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
def _setupFilterHardnessBox(self):
self.filterHardnessHLayout = QHBoxLayout()
self.filterHardnessLabel = QLabel(self)
self.filterHardnessLabel.setText(tr("Filter Hardness:"))
self.filterHardnessLabel.setMinimumSize(QSize(0, 0))
self.filterHardnessHLayout.addWidget(self.filterHardnessLabel)
self.filterHardnessVLayout = QVBoxLayout()
self.filterHardnessVLayout.setSpacing(0)
self.filterHardnessHLayoutSub1 = QHBoxLayout()
self.filterHardnessHLayoutSub1.setSpacing(12)
self.filterHardnessSlider = QSlider(self)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth())
self.filterHardnessSlider.setSizePolicy(sizePolicy)
self.filterHardnessSlider.setMinimum(1)
self.filterHardnessSlider.setMaximum(100)
self.filterHardnessSlider.setTracking(True)
self.filterHardnessSlider.setOrientation(Qt.Horizontal)
self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessSlider)
self.filterHardnessLabel = QLabel(self)
self.filterHardnessLabel.setText("100")
self.filterHardnessLabel.setMinimumSize(QSize(21, 0))
self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessLabel)
self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub1)
self.filterHardnessHLayoutSub2 = QHBoxLayout()
self.filterHardnessHLayoutSub2.setContentsMargins(-1, 0, -1, -1)
self.moreResultsLabel = QLabel(self)
self.moreResultsLabel.setText(tr("More Results"))
self.filterHardnessHLayoutSub2.addWidget(self.moreResultsLabel)
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.filterHardnessHLayoutSub2.addItem(spacerItem)
self.fewerResultsLabel = QLabel(self)
self.fewerResultsLabel.setText(tr("Fewer Results"))
self.filterHardnessHLayoutSub2.addWidget(self.fewerResultsLabel)
self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub2)
self.filterHardnessHLayout.addLayout(self.filterHardnessVLayout)
def _setupBottomPart(self):
# The bottom part of the pref panel is always the same in all editions.
self.languageLabel = QLabel(tr("Language:"), self)
self.widgetsVLayout.addWidget(self.languageLabel)
self.languageComboBox = QComboBox(self)
self.languageComboBox.addItem(tr("English"))
self.languageComboBox.addItem(tr("French"))
self.widgetsVLayout.addWidget(self.languageComboBox)
self.copyMoveLabel = QLabel(self)
self.copyMoveLabel.setText(tr("Copy and Move:"))
self.widgetsVLayout.addWidget(self.copyMoveLabel)
self.copyMoveDestinationComboBox = QComboBox(self)
self.copyMoveDestinationComboBox.addItem(tr("Right in destination"))
self.copyMoveDestinationComboBox.addItem(tr("Recreate relative path"))
self.copyMoveDestinationComboBox.addItem(tr("Recreate absolute path"))
self.widgetsVLayout.addWidget(self.copyMoveDestinationComboBox)
self.customCommandLabel = QLabel(self)
self.customCommandLabel.setText(tr("Custom Command (arguments: %d for dupe, %r for ref):"))
self.widgetsVLayout.addWidget(self.customCommandLabel)
self.customCommandEdit = QLineEdit(self)
self.widgetsVLayout.addWidget(self.customCommandEdit)
def _setupAddCheckbox(self, name, label, parent=None):
if parent is None:
parent = self
cb = QCheckBox(parent)
cb.setText(label)
setattr(self, name, cb)
def _setupPreferenceWidgets(self):
# Edition-specific
pass
def _setupUi(self):
self.setWindowTitle(tr("Preferences"))
self.resize(304, 263)
self.setSizeGripEnabled(False)
self.setModal(True)
self.mainVLayout = QVBoxLayout(self)
self.widgetsVLayout = QVBoxLayout()
self._setupPreferenceWidgets()
self.mainVLayout.addLayout(self.widgetsVLayout)
self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults)
self.mainVLayout.addWidget(self.buttonBox)
if sys.platform not in {'darwin', 'linux2'}:
self.verticalLayout.removeWidget(self.ignoreHardlinkMatches)
self.ignoreHardlinkMatches.setHidden(True)
def _load(self, prefs, setchecked):
# Edition-specific
pass
def _save(self, prefs, ischecked):
# Edition-specific
pass
def load(self, prefs=None):
if prefs is None:
prefs = self.app.prefs
self.filterHardnessSlider.setValue(prefs.filter_hardness)
self.filterHardnessLabel.setNum(prefs.filter_hardness)
setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
setchecked(self.mixFileKindBox, prefs.mix_file_kind)
setchecked(self.useRegexpBox, prefs.use_regexp)
setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders)
setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches)
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
self.customCommandEdit.setText(prefs.custom_command)
langindex = {'fr': 1}.get(self.app.prefs.language, 0)
self.languageComboBox.setCurrentIndex(langindex)
self._load(prefs, setchecked)
def save(self):
prefs = self.app.prefs
prefs.filter_hardness = self.filterHardnessSlider.value()
ischecked = lambda cb: cb.checkState() == Qt.Checked
prefs.mix_file_kind = ischecked(self.mixFileKindBox)
prefs.use_regexp = ischecked(self.useRegexpBox)
prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox)
prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches)
prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex()
prefs.custom_command = str(self.customCommandEdit.text())
langs = ['en', 'fr']
lang = langs[self.languageComboBox.currentIndex()]
oldlang = self.app.prefs.language
if oldlang not in langs:
oldlang = 'en'
if lang != oldlang:
QMessageBox.information(self, "", trmsg("NeedsToRestartToApplyLangMsg"))
self.app.prefs.language = lang
self._save(prefs, ischecked)
#--- Events
def buttonClicked(self, button):
role = self.buttonBox.buttonRole(button)
if role == QDialogButtonBox.ResetRole:
self.resetToDefaults()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-04-12
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
@ -11,6 +10,7 @@ from PyQt4.QtCore import Qt
from PyQt4.QtGui import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QSpacerItem, QSizePolicy,
QLabel, QTableView, QAbstractItemView, QApplication)
from hscommon.trans import tr, trmsg
from core.gui.problem_dialog import ProblemDialog as ProblemDialogModel
from .problem_table import ProblemTable
@ -29,11 +29,11 @@ class ProblemDialog(QDialog):
self.closeButton.clicked.connect(self.accept)
def _setupUi(self):
self.setWindowTitle("Problems!")
self.setWindowTitle(tr("Problems!"))
self.resize(413, 323)
self.verticalLayout = QVBoxLayout(self)
self.label = QLabel(self)
self.label.setText("There were problems processing some (or all) of the files. The cause of these problems are described in the table below. Those files were not removed from your results.")
self.label.setText(trmsg("ProblemsDuringProcessingMsg"))
self.label.setWordWrap(True)
self.verticalLayout.addWidget(self.label)
self.tableView = QTableView(self)
@ -47,12 +47,12 @@ class ProblemDialog(QDialog):
self.verticalLayout.addWidget(self.tableView)
self.horizontalLayout = QHBoxLayout()
self.revealButton = QPushButton(self)
self.revealButton.setText("Reveal Selected")
self.revealButton.setText(tr("Reveal Selected"))
self.horizontalLayout.addWidget(self.revealButton)
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.closeButton = QPushButton(self)
self.closeButton.setText("Close")
self.closeButton.setText(tr("Close"))
self.closeButton.setDefault(True)
self.horizontalLayout.addWidget(self.closeButton)
self.verticalLayout.addLayout(self.horizontalLayout)

View File

@ -7,14 +7,15 @@
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.trans import tr
from qtlib.column import Column
from qtlib.table import Table
from core.gui.problem_table import ProblemTable as ProblemTableModel
class ProblemTable(Table):
COLUMNS = [
Column('path', 'File Path', 150),
Column('msg', 'Error Message', 150),
Column('path', tr("File Path"), 150),
Column('msg', tr("Error Message"), 150),
]
def __init__(self, problem_dialog, view):

View File

@ -9,10 +9,11 @@
import sys
from PyQt4.QtCore import Qt, SIGNAL, QUrl, QRect
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QLabel, QHeaderView, QMessageBox,
QInputDialog, QLineEdit, QDesktopServices, QFileDialog, QMenuBar, QWidget, QVBoxLayout,
QAbstractItemView, QStatusBar)
from PyQt4.QtGui import (QMainWindow, QMenu, QLabel, QHeaderView, QMessageBox, QInputDialog,
QLineEdit, QDesktopServices, QFileDialog, QMenuBar, QWidget, QVBoxLayout, QAbstractItemView,
QStatusBar)
from hscommon.trans import tr, trmsg
from hscommon.util import nonone
from .results_model import ResultsModel, ResultsView
@ -38,31 +39,31 @@ class ResultWindow(QMainWindow):
def _setupActions(self):
# (name, shortcut, icon, desc, func)
ACTIONS = [
('actionDetails', 'Ctrl+I', '', "Details", self.detailsTriggered),
('actionActions', '', '', "Actions", self.actionsTriggered),
('actionPowerMarker', 'Ctrl+1', '', "Show Dupes Only", self.powerMarkerTriggered),
('actionDelta', 'Ctrl+2', '', "Show Delta Values", self.deltaTriggered),
('actionDeleteMarked', 'Ctrl+D', '', "Send Marked to Recycle Bin", self.deleteTriggered),
('actionHardlinkMarked', 'Ctrl+Shift+D', '', "Delete Marked and Replace with Hardlinks", self.hardlinkTriggered),
('actionMoveMarked', 'Ctrl+M', '', "Move Marked to...", self.moveTriggered),
('actionCopyMarked', 'Ctrl+Shift+M', '', "Copy Marked to...", self.copyTriggered),
('actionRemoveMarked', 'Ctrl+R', '', "Remove Marked from Results", self.removeMarkedTriggered),
('actionRemoveSelected', 'Ctrl+Del', '', "Remove Selected from Results", self.removeSelectedTriggered),
('actionIgnoreSelected', 'Ctrl+Shift+Del', '', "Add Selected to Ignore List", self.addToIgnoreListTriggered),
('actionMakeSelectedReference', 'Ctrl+Space', '', "Make Selected Reference", self.makeReferenceTriggered),
('actionOpenSelected', 'Ctrl+O', '', "Open Selected with Default Application", self.openTriggered),
('actionRevealSelected', 'Ctrl+Shift+O', '', "Open Containing Folder of Selected", self.revealTriggered),
('actionRenameSelected', 'F2', '', "Rename Selected", self.renameTriggered),
('actionMarkAll', 'Ctrl+A', '', "Mark All", self.markAllTriggered),
('actionMarkNone', 'Ctrl+Shift+A', '', "Mark None", self.markNoneTriggered),
('actionInvertMarking', 'Ctrl+Alt+A', '', "Invert Marking", self.markInvertTriggered),
('actionMarkSelected', '', '', "Mark Selected", self.markSelectedTriggered),
('actionClearIgnoreList', '', '', "Clear Ignore List", self.clearIgnoreListTriggered),
('actionApplyFilter', 'Ctrl+F', '', "Apply Filter", self.applyFilterTriggered),
('actionCancelFilter', 'Ctrl+Shift+F', '', "Cancel Filter", self.cancelFilterTriggered),
('actionExport', '', '', "Export To HTML", self.exportTriggered),
('actionSaveResults', 'Ctrl+S', '', "Save Results...", self.saveResultsTriggered),
('actionInvokeCustomCommand', 'Ctrl+Alt+I', '', "Invoke Custom Command", self.app.invokeCustomCommand),
('actionDetails', 'Ctrl+I', '', tr("Details"), self.detailsTriggered),
('actionActions', '', '', tr("Actions"), self.actionsTriggered),
('actionPowerMarker', 'Ctrl+1', '', tr("Show Dupes Only"), self.powerMarkerTriggered),
('actionDelta', 'Ctrl+2', '', tr("Show Delta Values"), self.deltaTriggered),
('actionDeleteMarked', 'Ctrl+D', '', tr("Send Marked to Recycle Bin"), self.deleteTriggered),
('actionHardlinkMarked', 'Ctrl+Shift+D', '', tr("Delete Marked and Replace with Hardlinks"), self.hardlinkTriggered),
('actionMoveMarked', 'Ctrl+M', '', tr("Move Marked to..."), self.moveTriggered),
('actionCopyMarked', 'Ctrl+Shift+M', '', tr("Copy Marked to..."), self.copyTriggered),
('actionRemoveMarked', 'Ctrl+R', '', tr("Remove Marked from Results"), self.removeMarkedTriggered),
('actionRemoveSelected', 'Ctrl+Del', '', tr("Remove Selected from Results"), self.removeSelectedTriggered),
('actionIgnoreSelected', 'Ctrl+Shift+Del', '', tr("Add Selected to Ignore List"), self.addToIgnoreListTriggered),
('actionMakeSelectedReference', 'Ctrl+Space', '', tr("Make Selected Reference"), self.makeReferenceTriggered),
('actionOpenSelected', 'Ctrl+O', '', tr("Open Selected with Default Application"), self.openTriggered),
('actionRevealSelected', 'Ctrl+Shift+O', '', tr("Open Containing Folder of Selected"), self.revealTriggered),
('actionRenameSelected', 'F2', '', tr("Rename Selected"), self.renameTriggered),
('actionMarkAll', 'Ctrl+A', '', tr("Mark All"), self.markAllTriggered),
('actionMarkNone', 'Ctrl+Shift+A', '', tr("Mark None"), self.markNoneTriggered),
('actionInvertMarking', 'Ctrl+Alt+A', '', tr("Invert Marking"), self.markInvertTriggered),
('actionMarkSelected', '', '', tr("Mark Selected"), self.markSelectedTriggered),
('actionClearIgnoreList', '', '', tr("Clear Ignore List"), self.clearIgnoreListTriggered),
('actionApplyFilter', 'Ctrl+F', '', tr("Apply Filter"), self.applyFilterTriggered),
('actionCancelFilter', 'Ctrl+Shift+F', '', tr("Cancel Filter"), self.cancelFilterTriggered),
('actionExport', '', '', tr("Export To HTML"), self.exportTriggered),
('actionSaveResults', 'Ctrl+S', '', tr("Save Results..."), self.saveResultsTriggered),
('actionInvokeCustomCommand', 'Ctrl+Alt+I', '', tr("Invoke Custom Command"), self.app.invokeCustomCommand),
]
createActions(ACTIONS, self)
self.actionDelta.setCheckable(True)
@ -72,17 +73,17 @@ class ResultWindow(QMainWindow):
self.menubar = QMenuBar(self)
self.menubar.setGeometry(QRect(0, 0, 630, 22))
self.menuFile = QMenu(self.menubar)
self.menuFile.setTitle("File")
self.menuFile.setTitle(tr("File"))
self.menuMark = QMenu(self.menubar)
self.menuMark.setTitle("Mark")
self.menuMark.setTitle(tr("Mark"))
self.menuActions = QMenu(self.menubar)
self.menuActions.setTitle("Actions")
self.menuActions.setTitle(tr("Actions"))
self.menuColumns = QMenu(self.menubar)
self.menuColumns.setTitle("Columns")
self.menuColumns.setTitle(tr("Columns"))
self.menuView = QMenu(self.menubar)
self.menuView.setTitle("View")
self.menuView.setTitle(tr("View"))
self.menuHelp = QMenu(self.menubar)
self.menuHelp.setTitle("Help")
self.menuHelp.setTitle(tr("Help"))
self.setMenuBar(self.menubar)
self.menuActions.addAction(self.actionDeleteMarked)
@ -138,11 +139,11 @@ class ResultWindow(QMainWindow):
action.column_index = index
self._column_actions.append(action)
menu.addSeparator()
action = menu.addAction("Reset to Defaults")
action = menu.addAction(tr("Reset to Defaults"))
action.column_index = -1
# Action menu
actionMenu = QMenu('Actions', self.menubar)
actionMenu = QMenu(tr("Actions"), self.menubar)
actionMenu.addAction(self.actionDeleteMarked)
actionMenu.addAction(self.actionHardlinkMarked)
actionMenu.addAction(self.actionMoveMarked)
@ -160,7 +161,7 @@ class ResultWindow(QMainWindow):
self.actionActions.setMenu(actionMenu)
def _setupUi(self):
self.setWindowTitle("dupeGuru Results")
self.setWindowTitle(tr("dupeGuru Results"))
self.resize(630, 514)
self.centralwidget = QWidget(self)
self.verticalLayout_2 = QVBoxLayout(self.centralwidget)
@ -222,8 +223,8 @@ class ResultWindow(QMainWindow):
self.app.add_selected_to_ignore_list()
def applyFilterTriggered(self):
title = "Apply Filter"
msg = "Type the filter you want to apply on your results. See help for details."
title = tr("Apply Filter")
msg = trmsg("TypeFilterMsg")
text = nonone(self._last_filter, '[*]')
answer, ok = QInputDialog.getText(self, title, msg, QLineEdit.Normal, text)
if not ok:
@ -236,15 +237,15 @@ class ResultWindow(QMainWindow):
self.app.apply_filter('')
def clearIgnoreListTriggered(self):
title = "Clear Ignore List"
title = tr("Clear Ignore List")
count = len(self.app.scanner.ignore_list)
if not count:
QMessageBox.information(self, title, "Nothing to clear.")
QMessageBox.information(self, title, trmsg("NothingToClearMsg"))
return
msg = "Do you really want to remove all {0} items from the ignore list?".format(count)
msg = trmsg("ClearIgnoreListConfirmMsg").format(count)
if self.app.confirm(title, msg, QMessageBox.No):
self.app.scanner.ignore_list.Clear()
QMessageBox.information(self, title, "Ignore list cleared.")
QMessageBox.information(self, title, trmsg("IgnoreListClearedMsg"))
def copyTriggered(self):
self.app.copy_or_move_marked(True)
@ -253,8 +254,8 @@ class ResultWindow(QMainWindow):
count = self.app.results.mark_count
if not count:
return
title = "Delete duplicates"
msg = "You are about to send {0} files to the recycle bin. Continue?".format(count)
title = tr("Delete duplicates")
msg = trmsg("SendToTrashConfirmMsg").format(count)
if self.app.confirm(title, msg):
self.app.delete_marked()
@ -278,8 +279,8 @@ class ResultWindow(QMainWindow):
count = self.app.results.mark_count
if not count:
return
title = "Delete and hardlink duplicates"
msg = "You are about to send {0} files to the trash and hardlink them afterwards. Continue?".format(count)
title = tr("Delete and hardlink duplicates")
msg = trmsg("HardlinkConfirmMsg").format(count)
if self.app.confirm(title, msg):
self.app.delete_marked(replace_with_hardlinks=True)
@ -314,8 +315,8 @@ class ResultWindow(QMainWindow):
count = self.app.results.mark_count
if not count:
return
title = "Remove duplicates"
msg = "You are about to remove {0} files from results. Continue?".format(count)
title = tr("Remove duplicates")
msg = trmsg("FileRemovalConfirmMsg").format(count)
if self.app.confirm(title, msg):
self.app.remove_marked()
@ -329,8 +330,8 @@ class ResultWindow(QMainWindow):
self.app.reveal_selected()
def saveResultsTriggered(self):
title = "Select a file to save your results to"
files = "dupeGuru Results (*.dupeguru)"
title = trmsg("SelectResultToSaveMsg")
files = tr("dupeGuru Results (*.dupeguru)")
destination = QFileDialog.getSaveFileName(self, title, '', files)
if destination:
self.app.save_as(destination)

99
qt/lang/en.ts Normal file
View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="en_US">
<context>
<name>message</name>
<message>
<source>TaskHangingMsg</source>
<translation>A previous action is still hanging in there. You can&apos;t start a new one yet. Wait a few seconds, then try again.</translation>
</message>
<message>
<source>IgnoreConfirmMsg</source>
<translation>All selected {} matches are going to be ignored in all subsequent scans. Continue?</translation>
</message>
<message>
<source>SelectCopyOrMoveDestinationMsg</source>
<translation>Select a directory to {} marked files to</translation>
</message>
<message>
<source>FileRemovalConfirmMsg</source>
<translation>You are about to remove {} files from results. Continue?</translation>
</message>
<message>
<source>NoCustomCommandMsg</source>
<translation>You have no custom command set up. Please, set it up in your preferences.</translation>
</message>
<message>
<source>OperationSuccessMsg</source>
<translation>All files were processed successfully.</translation>
</message>
<message>
<source>NoDuplicateFoundMsg</source>
<translation>No duplicates found.</translation>
</message>
<message>
<source>ReallyWantToQuitMsg</source>
<translation>You have unsaved results, do you really want to quit?</translation>
</message>
<message>
<source>SelectFolderToAddMsg</source>
<translation>Select a folder to add to the scanning list</translation>
</message>
<message>
<source>SelectResultToLoadMsg</source>
<translation>Select a results file to load</translation>
</message>
<message>
<source>ReallyWantToContinueMsg</source>
<translation>You have unsaved results, do you really want to continue?</translation>
</message>
<message>
<source>NoScannableFileMsg</source>
<translation>The selected folders contain no scannable file.</translation>
</message>
<message>
<source>ProblemsDuringProcessingMsg</source>
<translation>There were problems processing some (or all) of the files. The cause of these problems are described in the table below. Those files were not removed from your results.</translation>
</message>
<message>
<source>TypeFilterMsg</source>
<translation>Type the filter you want to apply on your results. See help for details.</translation>
</message>
<message>
<source>NothingToClearMsg</source>
<translation>Nothing to clear.</translation>
</message>
<message>
<source>ClearIgnoreListConfirmMsg</source>
<translation>Do you really want to remove all {} items from the ignore list?</translation>
</message>
<message>
<source>IgnoreListClearedMsg</source>
<translation>Ignore list cleared.</translation>
</message>
<message>
<source>SendToTrashConfirmMsg</source>
<translation>You are about to send {} files to the recycle bin. Continue?</translation>
</message>
<message>
<source>HardlinkConfirmMsg</source>
<translation>You are about to send {} files to the trash and hardlink them afterwards. Continue?</translation>
</message>
<message>
<source>SelectResultToSaveMsg</source>
<translation>Select a file to save your results to</translation>
</message>
<message>
<source>NeedsToRestartToApplyLangMsg</source>
<translation>dupeGuru has to restart for language changes to take effect.</translation>
</message>
<message>
<source>ClearPictureCacheConfirmMsg</source>
<translation>Do you really want to remove all your cached picture analysis?</translation>
</message>
<message>
<source>PictureCacheClearedMsg</source>
<translation>Picture cache cleared.</translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@ -4,6 +4,7 @@
<!-- ******** Core ******** -->
<context>
<name>core</name>
<!-- core.* -->
<message>
<source>Collecting files to scan</source>
<translation>Collecte des fichiers à scanner</translation>
@ -107,5 +108,528 @@
<source>Sending dupes to the Trash</source>
<translation>Envoi de doublons à la corbeille en cours</translation>
</message>
<!-- qt.* -->
<message>
<source>Sending files to the recycle bin</source>
<translation>Envoi de fichiers à la corbeille</translation>
</message>
<message>
<source>Quit</source>
<translation>Quitter</translation>
</message>
<message>
<source>Preferences</source>
<translation>Préférences</translation>
</message>
<message>
<source>dupeGuru Help</source>
<translation>Aide dupeGuru</translation>
</message>
<message>
<source>About dupeGuru</source>
<translation>À propos de dupeGuru</translation>
</message>
<message>
<source>Register dupeGuru</source>
<translation>Enregistrer dupeGuru</translation>
</message>
<message>
<source>Check for Update</source>
<translation>Vérifier les mises à jour</translation>
</message>
<message>
<source>Open Debug Log</source>
<translation>Ouvrir logs de déboguage</translation>
</message>
<message>
<source>Add to Ignore List</source>
<translation>Ignorer ces doublons à l'avenir</translation>
</message>
<message>
<source>copy</source>
<translation>copier</translation>
</message>
<message>
<source>move</source>
<translation>déplacer</translation>
</message>
<message>
<source>Remove duplicates</source>
<translation>Retirer des doublons</translation>
</message>
<message>
<source>Custom Command</source>
<translation>Commande personnalisée</translation>
</message>
<message>
<source>Operation Complete</source>
<translation>Opération complétée</translation>
</message>
<message>
<source>Scan complete</source>
<translation>Scan complété</translation>
</message>
<message>
<source>Attribute</source>
<translation>Attribut</translation>
</message>
<message>
<source>Selected</source>
<translation>Sélectionné</translation>
</message>
<message>
<source>Reference</source>
<translation>Référence</translation>
</message>
<message>
<source>Load Results...</source>
<translation>Charger résultats...</translation>
</message>
<message>
<source>Results Window</source>
<translation>Fenêtre de résultats</translation>
</message>
<message>
<source>Add Folder...</source>
<translation>Ajouter dossier...</translation>
</message>
<message>
<source>File</source>
<translation>Fichier</translation>
</message>
<message>
<source>View</source>
<translation>Voir</translation>
</message>
<message>
<source>Help</source>
<translation>Aide</translation>
</message>
<message>
<source>Load Recent Results</source>
<translation>Charger résultats récents</translation>
</message>
<message>
<source>Load Results</source>
<translation>Charger</translation>
</message>
<message>
<source>Scan</source>
<translation>Scan</translation>
</message>
<message>
<source>Unsaved results</source>
<translation>Résultats non sauvegardés</translation>
</message>
<message>
<source>dupeGuru Results (*.dupeguru)</source>
<translation>Résultats dupeGuru (*.dupeguru)</translation>
</message>
<message>
<source>Start a new scan</source>
<translation>Commencer un nouveau scan</translation>
</message>
<message>
<source>Name</source>
<translation>Nom</translation>
</message>
<message>
<source>State</source>
<translation>Type</translation>
</message>
<message>
<source>Normal</source>
<translation>Normal</translation>
</message>
<message>
<source>Excluded</source>
<translation>Exclus</translation>
</message>
<message>
<source>Problems!</source>
<translation>Problèmes!</translation>
</message>
<message>
<source>Reveal Selected</source>
<translation>Révéler Fichier</translation>
</message>
<message>
<source>Close</source>
<translation>Fermer</translation>
</message>
<message>
<source>File Path</source>
<translation>Chemin du fichier</translation>
</message>
<message>
<source>Error Message</source>
<translation>Message d'erreur</translation>
</message>
<message>
<source>Details</source>
<translation>Détails</translation>
</message>
<message>
<source>Actions</source>
<translation>Actions</translation>
</message>
<message>
<source>Show Dupes Only</source>
<translation>Ne pas montrer les références</translation>
</message>
<message>
<source>Show Delta Values</source>
<translation>Montrer les valeurs en tant que delta</translation>
</message>
<message>
<source>Send Marked to Recycle Bin</source>
<translation>Envoyer marqués à la corbeille</translation>
</message>
<message>
<source>Delete Marked and Replace with Hardlinks</source>
<translation>Remplacer marqués par des hardlinks</translation>
</message>
<message>
<source>Move Marked to...</source>
<translation>Déplacer marqués vers...</translation>
</message>
<message>
<source>Copy Marked to...</source>
<translation>Copier marqués vers...</translation>
</message>
<message>
<source>Remove Marked from Results</source>
<translation>Retirer marqués des résultats</translation>
</message>
<message>
<source>Remove Selected from Results</source>
<translation>Retirer sélectionnés des résultats</translation>
</message>
<message>
<source>Add Selected to Ignore List</source>
<translation>Ajouter sélectionnés à la liste de fichiers ignorés</translation>
</message>
<message>
<source>Make Selected Reference</source>
<translation>Transformer sélectionnés en références</translation>
</message>
<message>
<source>Open Selected with Default Application</source>
<translation>Ouvrir sélectionné avec l'application par défaut</translation>
</message>
<message>
<source>Open Containing Folder of Selected</source>
<translation>Ouvrir le dossier contenant le fichier sélectionné</translation>
</message>
<message>
<source>Rename Selected</source>
<translation>Renommer sélectionné</translation>
</message>
<message>
<source>Mark All</source>
<translation>Tout marquer</translation>
</message>
<message>
<source>Mark None</source>
<translation>Tout démarquer</translation>
</message>
<message>
<source>Invert Marking</source>
<translation>Inverser le marquage</translation>
</message>
<message>
<source>Mark Selected</source>
<translation>Marquer sélectionnés</translation>
</message>
<message>
<source>Clear Ignore List</source>
<translation>Vider la liste de fichiers ignorés</translation>
</message>
<message>
<source>Apply Filter</source>
<translation>Appliquer filtre</translation>
</message>
<message>
<source>Cancel Filter</source>
<translation>Annuler filtre</translation>
</message>
<message>
<source>Export To HTML</source>
<translation>Exporter vers HTML</translation>
</message>
<message>
<source>Save Results...</source>
<translation>Sauvegarder résultats...</translation>
</message>
<message>
<source>Invoke Custom Command</source>
<translation>Invoquer commande personnalisée</translation>
</message>
<message>
<source>Mark</source>
<translation>Marquer</translation>
</message>
<message>
<source>Columns</source>
<translation>Colonnes</translation>
</message>
<message>
<source>Reset to Defaults</source>
<translation>Réinitialiser</translation>
</message>
<message>
<source>dupeGuru Results</source>
<translation>dupeGuru (Résultats)</translation>
</message>
<message>
<source>Delete duplicates</source>
<translation>Effacement de doublons</translation>
</message>
<message>
<source>Delete and hardlink duplicates</source>
<translation>Hardlinking de doublons</translation>
</message>
<message>
<source>Scan Type:</source>
<translation>Type de scan:</translation>
</message>
<message>
<source>Filename</source>
<translation>Nom de fichier</translation>
</message>
<message>
<source>Contents</source>
<translation>Contenu</translation>
</message>
<message>
<source>Filter Hardness:</source>
<translation>Seuil du filtre:</translation>
</message>
<message>
<source>More Results</source>
<translation>+ de doublons</translation>
</message>
<message>
<source>Fewer Results</source>
<translation>- de doublons</translation>
</message>
<message>
<source>Word weighting</source>
<translation>Proportionalité des mots</translation>
</message>
<message>
<source>Match similar words</source>
<translation>Comparer les mots similaires</translation>
</message>
<message>
<source>Can mix file kind</source>
<translation>Comparer les fichiers de différents types</translation>
</message>
<message>
<source>Use regular expressions when filtering</source>
<translation>Utiliser les expressions régulières pour les filtres</translation>
</message>
<message>
<source>Remove empty folders on delete or move</source>
<translation>Effacer les dossiers vides après un déplacement</translation>
</message>
<message>
<source>Ignore files smaller than</source>
<translation>Ignorer les fichiers plus petits que:</translation>
</message>
<message>
<source>KB</source>
<translation>KB</translation>
</message>
<message>
<source>Ignore duplicates hardlinking to the same file</source>
<translation>Ignorer doublons avec hardlink vers le même fichier</translation>
</message>
<message>
<source>Copy and Move:</source>
<translation>Déplacements de fichiers:</translation>
</message>
<message>
<source>Right in destination</source>
<translation>Directement à la destination</translation>
</message>
<message>
<source>Recreate relative path</source>
<translation>Re-créer chemins relatifs</translation>
</message>
<message>
<source>Recreate absolute path</source>
<translation>Re-créer chemins absolus</translation>
</message>
<message>
<source>Custom Command (arguments: %d for dupe, %r for ref):</source>
<translation>Commande perso. (arguments: %d pour doublon, %r pour réf):</translation>
</message>
<message>
<source>Filename - Fields</source>
<translation>Nom de fichier (Champs)</translation>
</message>
<message>
<source>Filename - Fields (No Order)</source>
<translation>Nom de fichier (Champs sans ordre)</translation>
</message>
<message>
<source>Tags</source>
<translation>Tags</translation>
</message>
<message>
<source>Audio Contents</source>
<translation>Contenu Audio</translation>
</message>
<message>
<source>Tags to scan:</source>
<translation>Tags à scanner:</translation>
</message>
<message>
<source>Track</source>
<translation>Track</translation>
</message>
<message>
<source>Artist</source>
<translation>Artiste</translation>
</message>
<message>
<source>Album</source>
<translation>Album</translation>
</message>
<message>
<source>Title</source>
<translation>Titre</translation>
</message>
<message>
<source>Genre</source>
<translation>Genre</translation>
</message>
<message>
<source>Year</source>
<translation>Année</translation>
</message>
<message>
<source>Match scaled pictures together</source>
<translation>Comparer les images de taille différente</translation>
</message>
<message>
<source>Clear Picture Cache</source>
<translation>Vider la cache d'images</translation>
</message>
<message>
<source>Clear List</source>
<translation>Vider la liste</translation>
</message>
<message>
<source>Language:</source>
<translation>Langue:</translation>
</message>
<message>
<source>English</source>
<translation>Anglais</translation>
</message>
<message>
<source>French</source>
<translation>Français</translation>
</message>
</context>
<!-- ******** Message ******** -->
<context>
<name>message</name>
<message>
<source>TaskHangingMsg</source>
<translation>Une action précédente est encore en cours. Attendez quelques secondes avant d'en repartir une nouvelle.</translation>
</message>
<message>
<source>IgnoreConfirmMsg</source>
<translation>%d fichiers seront ignorés des prochains scans. Continuer?</translation>
</message>
<message>
<source>SelectCopyOrMoveDestinationMsg</source>
<translation>Sélectionnez un dossier vers lequel {} les fichiers marqués.</translation>
</message>
<message>
<source>FileRemovalConfirmMsg</source>
<translation>{} fichiers seront retirés des résultats. Continuer?</translation>
</message>
<message>
<source>NoCustomCommandMsg</source>
<translation>Vous n'avez pas de commande personnalisée. Ajoutez-la dans vos préférences.</translation>
</message>
<message>
<source>OperationSuccessMsg</source>
<translation>Tous les fichiers ont é traités avec succès.</translation>
</message>
<message>
<source>NoDuplicateFoundMsg</source>
<translation>Aucun doublon trouvé.</translation>
</message>
<message>
<source>ReallyWantToQuitMsg</source>
<translation>Vos résultats ne sont pas sauvegardés. Voulez-vous vraiment quitter?</translation>
</message>
<message>
<source>SelectFolderToAddMsg</source>
<translation>Sélectionnez un dossier à ajouter à la liste</translation>
</message>
<message>
<source>SelectResultToLoadMsg</source>
<translation>Sélectionnez un fichier résultats à charger</translation>
</message>
<message>
<source>ReallyWantToContinueMsg</source>
<translation>Vos résultats ne sont pas sauvegardés. Voulez-vous vraiment continuer?</translation>
</message>
<message>
<source>NoScannableFileMsg</source>
<translation>Les dossiers sélectionnés ne continnent pas de fichiers valides.</translation>
</message>
<message>
<source>ProblemsDuringProcessingMsg</source>
<translation>Des problèmes ont é rencontrés lors du traitement de certains fichiers. La nature de ces problèmes est décrite dans la liste ci-dessous. Ces fichiers n'ont pas é retirés des résultats.</translation>
</message>
<message>
<source>TypeFilterMsg</source>
<translation>Entrer le filtre que vous voulez appliquer sur vos résultats.</translation>
</message>
<message>
<source>NothingToClearMsg</source>
<translation>Il n'y a rien à vider.</translation>
</message>
<message>
<source>ClearIgnoreListConfirmMsg</source>
<translation>Voulez-vous vider la liste de fichiers ignorés des {} items qu'elle contient?</translation>
</message>
<message>
<source>IgnoreListClearedMsg</source>
<translation>La liste de doublons ignorés a é vidée.</translation>
</message>
<message>
<source>SendToTrashConfirmMsg</source>
<translation>{} fichiers seront envoyés à la corbeille. Continuer?</translation>
</message>
<message>
<source>HardlinkConfirmMsg</source>
<translation>{} fichiers seront envoyés à la corbeille (puis 'hardlinkés'). Continuer?</translation>
</message>
<message>
<source>SelectResultToSaveMsg</source>
<translation>Sélectionnez un fichier résultats dans lequel sauvegarder</translation>
</message>
<message>
<source>NeedsToRestartToApplyLangMsg</source>
<translation>dupeGuru doit redémarrer pour appliquer le changement de langue.</translation>
</message>
<message>
<source>ClearPictureCacheConfirmMsg</source>
<translation>Voulez-vous vraiment vider la cache de vos analyses précédentes?</translation>
</message>
<message>
<source>PictureCacheClearedMsg</source>
<translation>La cache des analyses précédentes a é vidée.</translation>
</message>
</context>
</TS>

View File

@ -6,7 +6,7 @@
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from core_me import data, scanner, fs, __version__
from core_me import data, scanner, fs, __appname__
from ..base.app import DupeGuru as DupeGuruBase
from .details_dialog import DetailsDialog
@ -16,8 +16,7 @@ from .preferences_dialog import PreferencesDialog
class DupeGuru(DupeGuruBase):
EDITION = 'me'
LOGO_NAME = 'logo_me'
NAME = 'dupeGuru Music Edition'
VERSION = __version__
NAME = __appname__
DELTA_COLUMNS = frozenset([2, 3, 4, 5, 7])
def __init__(self):

View File

@ -9,12 +9,13 @@
from PyQt4.QtCore import QSize
from PyQt4.QtGui import QVBoxLayout, QAbstractItemView
from hscommon.trans import tr
from ..base.details_dialog import DetailsDialog as DetailsDialogBase
from ..base.details_table import DetailsTable
class DetailsDialog(DetailsDialogBase):
def _setupUi(self):
self.setWindowTitle("Details")
self.setWindowTitle(tr("Details"))
self.resize(502, 295)
self.setMinimumSize(QSize(250, 250))
self.verticalLayout = QVBoxLayout(self)

View File

@ -7,12 +7,14 @@
# http://www.hardcoded.net/licenses/bsd_license
import sys
from PyQt4.QtCore import SIGNAL, Qt, QSize
from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox,
QSlider, QSizePolicy, QSpacerItem, QWidget, QCheckBox, QLineEdit, QDialogButtonBox, QApplication)
from PyQt4.QtCore import SIGNAL, QSize
from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QSizePolicy, QSpacerItem,
QWidget, QApplication)
from hscommon.trans import tr
from core.scanner import ScanType
from ..base.preferences_dialog import PreferencesDialogBase
from . import preferences
SCAN_TYPE_ORDER = [
@ -24,169 +26,73 @@ SCAN_TYPE_ORDER = [
ScanType.ContentsAudio,
]
class PreferencesDialog(QDialog):
class PreferencesDialog(PreferencesDialogBase):
def __init__(self, parent, app):
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
QDialog.__init__(self, parent, flags)
self.app = app
self._setupUi()
PreferencesDialogBase.__init__(self, parent, app)
self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked)
self.connect(self.scanTypeComboBox, SIGNAL('currentIndexChanged(int)'), self.scanTypeChanged)
self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
def _setupUi(self):
self.setWindowTitle("Preferences")
self.resize(325, 360)
self.setSizeGripEnabled(False)
self.setModal(True)
self.verticalLayout_2 = QVBoxLayout(self)
self.verticalLayout = QVBoxLayout()
def _setupPreferenceWidgets(self):
self.horizontalLayout = QHBoxLayout()
self.label_2 = QLabel(self)
self.label_2.setText("Scan Type:")
self.label_2.setText(tr("Scan Type:"))
self.label_2.setMinimumSize(QSize(100, 0))
self.label_2.setMaximumSize(QSize(100, 16777215))
self.horizontalLayout.addWidget(self.label_2)
self.scanTypeComboBox = QComboBox(self)
self.scanTypeComboBox.addItem("Filename")
self.scanTypeComboBox.addItem("Filename - Fields")
self.scanTypeComboBox.addItem("Filename - Fields (No Order)")
self.scanTypeComboBox.addItem("Tags")
self.scanTypeComboBox.addItem("Contents")
self.scanTypeComboBox.addItem("Audio Contents")
self.scanTypeComboBox.addItem(tr("Filename"))
self.scanTypeComboBox.addItem(tr("Filename - Fields"))
self.scanTypeComboBox.addItem(tr("Filename - Fields (No Order)"))
self.scanTypeComboBox.addItem(tr("Tags"))
self.scanTypeComboBox.addItem(tr("Contents"))
self.scanTypeComboBox.addItem(tr("Audio Contents"))
self.horizontalLayout.addWidget(self.scanTypeComboBox)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_3 = QHBoxLayout()
self.label = QLabel(self)
self.label.setText("Filter Hardness:")
self.label.setMinimumSize(QSize(100, 0))
self.label.setMaximumSize(QSize(100, 16777215))
self.horizontalLayout_3.addWidget(self.label)
self.verticalLayout_3 = QVBoxLayout()
self.verticalLayout_3.setSpacing(0)
self.horizontalLayout_6 = QHBoxLayout()
self.horizontalLayout_6.setSpacing(12)
self.filterHardnessSlider = QSlider(self)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth())
self.filterHardnessSlider.setSizePolicy(sizePolicy)
self.filterHardnessSlider.setMinimum(1)
self.filterHardnessSlider.setMaximum(100)
self.filterHardnessSlider.setTracking(True)
self.filterHardnessSlider.setOrientation(Qt.Horizontal)
self.horizontalLayout_6.addWidget(self.filterHardnessSlider)
self.filterHardnessLabel = QLabel(self)
self.filterHardnessLabel.setText("100")
self.filterHardnessLabel.setMinimumSize(QSize(21, 0))
self.horizontalLayout_6.addWidget(self.filterHardnessLabel)
self.verticalLayout_3.addLayout(self.horizontalLayout_6)
self.horizontalLayout_5 = QHBoxLayout()
self.horizontalLayout_5.setContentsMargins(-1, 0, -1, -1)
self.label_4 = QLabel(self)
self.label_4.setText("More Results")
self.horizontalLayout_5.addWidget(self.label_4)
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(spacerItem)
self.label_3 = QLabel(self)
self.label_3.setText("Fewer Results")
self.horizontalLayout_5.addWidget(self.label_3)
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.widgetsVLayout.addLayout(self.horizontalLayout)
self._setupFilterHardnessBox()
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
self.widget = QWidget(self)
self.widget.setMinimumSize(QSize(0, 40))
self.verticalLayout_4 = QVBoxLayout(self.widget)
self.verticalLayout_4.setSpacing(0)
self.verticalLayout_4.setMargin(0)
self.label_6 = QLabel(self.widget)
self.label_6.setText("Tags to scan:")
self.label_6.setText(tr("Tags to scan:"))
self.verticalLayout_4.addWidget(self.label_6)
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setSpacing(0)
spacerItem1 = QSpacerItem(15, 20, QSizePolicy.Fixed, QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem1)
self.tagTrackBox = QCheckBox(self.widget)
self.tagTrackBox.setText("Track")
self._setupAddCheckbox('tagTrackBox', tr("Track"), self.widget)
self.horizontalLayout_2.addWidget(self.tagTrackBox)
self.tagArtistBox = QCheckBox(self.widget)
self.tagArtistBox.setText("Artist")
self._setupAddCheckbox('tagArtistBox', tr("Artist"), self.widget)
self.horizontalLayout_2.addWidget(self.tagArtistBox)
self.tagAlbumBox = QCheckBox(self.widget)
self.tagAlbumBox.setText("Album")
self._setupAddCheckbox('tagAlbumBox', tr("Album"), self.widget)
self.horizontalLayout_2.addWidget(self.tagAlbumBox)
self.tagTitleBox = QCheckBox(self.widget)
self.tagTitleBox.setText("Title")
self._setupAddCheckbox('tagTitleBox', tr("Title"), self.widget)
self.horizontalLayout_2.addWidget(self.tagTitleBox)
self.tagGenreBox = QCheckBox(self.widget)
self.tagGenreBox.setText("Genre")
self._setupAddCheckbox('tagGenreBox', tr("Genre"), self.widget)
self.horizontalLayout_2.addWidget(self.tagGenreBox)
self.tagYearBox = QCheckBox(self.widget)
self.tagYearBox.setText("Year")
self._setupAddCheckbox('tagYearBox', tr("Year"), self.widget)
self.horizontalLayout_2.addWidget(self.tagYearBox)
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.verticalLayout.addWidget(self.widget)
self.wordWeightingBox = QCheckBox(self)
self.wordWeightingBox.setText("Word weighting")
self.verticalLayout.addWidget(self.wordWeightingBox)
self.matchSimilarBox = QCheckBox(self)
self.matchSimilarBox.setText("Match similar words")
self.verticalLayout.addWidget(self.matchSimilarBox)
self.mixFileKindBox = QCheckBox(self)
self.mixFileKindBox.setText("Can mix file kind")
self.verticalLayout.addWidget(self.mixFileKindBox)
self.useRegexpBox = QCheckBox(self)
self.useRegexpBox.setText("Use regular expressions when filtering")
self.verticalLayout.addWidget(self.useRegexpBox)
self.removeEmptyFoldersBox = QCheckBox(self)
self.removeEmptyFoldersBox.setText("Remove empty folders on delete or move")
self.verticalLayout.addWidget(self.removeEmptyFoldersBox)
self.ignoreHardlinkMatches = QCheckBox(self)
self.ignoreHardlinkMatches.setText("Ignore duplicates hardlinking to the same file")
self.verticalLayout.addWidget(self.ignoreHardlinkMatches)
self.horizontalLayout_4 = QHBoxLayout()
self.label_5 = QLabel(self)
self.label_5.setText("Copy and Move:")
self.label_5.setMinimumSize(QSize(100, 0))
self.label_5.setMaximumSize(QSize(100, 16777215))
self.horizontalLayout_4.addWidget(self.label_5)
self.copyMoveDestinationComboBox = QComboBox(self)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.copyMoveDestinationComboBox.sizePolicy().hasHeightForWidth())
self.copyMoveDestinationComboBox.setSizePolicy(sizePolicy)
self.copyMoveDestinationComboBox.addItem("Right in destination")
self.copyMoveDestinationComboBox.addItem("Recreate relative path")
self.copyMoveDestinationComboBox.addItem("Recreate absolute path")
self.horizontalLayout_4.addWidget(self.copyMoveDestinationComboBox)
self.verticalLayout.addLayout(self.horizontalLayout_4)
self.label_7 = QLabel(self)
self.label_7.setText("Custom Command (arguments: %d for dupe, %r for ref):")
self.verticalLayout.addWidget(self.label_7)
self.customCommandEdit = QLineEdit(self)
self.verticalLayout.addWidget(self.customCommandEdit)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults)
self.verticalLayout_2.addWidget(self.buttonBox)
if sys.platform not in {'darwin', 'linux2'}:
self.verticalLayout.removeWidget(self.ignoreHardlinkMatches)
self.ignoreHardlinkMatches.setHidden(True)
self.widgetsVLayout.addWidget(self.widget)
self._setupAddCheckbox('wordWeightingBox', tr("Word weighting"))
self.widgetsVLayout.addWidget(self.wordWeightingBox)
self._setupAddCheckbox('matchSimilarBox', tr("Match similar words"))
self.widgetsVLayout.addWidget(self.matchSimilarBox)
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"))
self.widgetsVLayout.addWidget(self.mixFileKindBox)
self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering"))
self.widgetsVLayout.addWidget(self.useRegexpBox)
self._setupAddCheckbox('removeEmptyFoldersBox', tr("Remove empty folders on delete or move"))
self.widgetsVLayout.addWidget(self.removeEmptyFoldersBox)
self._setupAddCheckbox('ignoreHardlinkMatches', tr("Ignore duplicates hardlinking to the same file"))
self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
self._setupBottomPart()
def load(self, prefs=None):
if prefs is None:
prefs = self.app.prefs
self.filterHardnessSlider.setValue(prefs.filter_hardness)
self.filterHardnessLabel.setNum(prefs.filter_hardness)
def _load(self, prefs, setchecked):
scan_type_index = SCAN_TYPE_ORDER.index(prefs.scan_type)
self.scanTypeComboBox.setCurrentIndex(scan_type_index)
setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
setchecked(self.tagTrackBox, prefs.scan_tag_track)
setchecked(self.tagArtistBox, prefs.scan_tag_artist)
setchecked(self.tagAlbumBox, prefs.scan_tag_album)
@ -195,18 +101,9 @@ class PreferencesDialog(QDialog):
setchecked(self.tagYearBox, prefs.scan_tag_year)
setchecked(self.matchSimilarBox, prefs.match_similar)
setchecked(self.wordWeightingBox, prefs.word_weighting)
setchecked(self.mixFileKindBox, prefs.mix_file_kind)
setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches)
setchecked(self.useRegexpBox, prefs.use_regexp)
setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders)
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
self.customCommandEdit.setText(prefs.custom_command)
def save(self):
prefs = self.app.prefs
prefs.filter_hardness = self.filterHardnessSlider.value()
def _save(self, prefs, ischecked):
prefs.scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()]
ischecked = lambda cb: cb.checkState() == Qt.Checked
prefs.scan_tag_track = ischecked(self.tagTrackBox)
prefs.scan_tag_artist = ischecked(self.tagArtistBox)
prefs.scan_tag_album = ischecked(self.tagAlbumBox)
@ -215,22 +112,11 @@ class PreferencesDialog(QDialog):
prefs.scan_tag_year = ischecked(self.tagYearBox)
prefs.match_similar = ischecked(self.matchSimilarBox)
prefs.word_weighting = ischecked(self.wordWeightingBox)
prefs.mix_file_kind = ischecked(self.mixFileKindBox)
prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches)
prefs.use_regexp = ischecked(self.useRegexpBox)
prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox)
prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex()
prefs.custom_command = str(self.customCommandEdit.text())
def resetToDefaults(self):
self.load(preferences.Preferences())
#--- Events
def buttonClicked(self, button):
role = self.buttonBox.buttonRole(button)
if role == QDialogButtonBox.ResetRole:
self.resetToDefaults()
def scanTypeChanged(self, index):
scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()]
word_based = scan_type in (ScanType.Filename, ScanType.Fields, ScanType.FieldsNoOrder,
@ -248,7 +134,6 @@ class PreferencesDialog(QDialog):
if __name__ == '__main__':
import sys
from ..testapp import TestApp
app = QApplication([])
dgapp = TestApp()

View File

@ -14,7 +14,7 @@ from PyQt4.QtGui import QImage, QImageReader
from hscommon.util import get_file_ext
from core import fs
from core_pe import data as data_pe, __version__
from core_pe import data as data_pe, __appname__
from core_pe.scanner import ScannerPE
from ..base.app import DupeGuru as DupeGuruBase
@ -58,8 +58,7 @@ class File(fs.File):
class DupeGuru(DupeGuruBase):
EDITION = 'pe'
LOGO_NAME = 'logo_pe'
NAME = 'dupeGuru Picture Edition'
VERSION = __version__
NAME = __appname__
DELTA_COLUMNS = frozenset([2, 5])
def __init__(self):

View File

@ -9,6 +9,7 @@
from PyQt4.QtCore import Qt, QSize
from PyQt4.QtGui import QVBoxLayout, QAbstractItemView, QHBoxLayout, QLabel, QSizePolicy, QPixmap
from hscommon.trans import tr
from ..base.details_dialog import DetailsDialog as DetailsDialogBase
from ..base.details_table import DetailsTable
@ -19,7 +20,7 @@ class DetailsDialog(DetailsDialogBase):
self.referencePixmap = None
def _setupUi(self):
self.setWindowTitle("Details")
self.setWindowTitle(tr("Details"))
self.resize(502, 295)
self.setMinimumSize(QSize(250, 250))
self.verticalLayout = QVBoxLayout(self)

View File

@ -7,148 +7,40 @@
# http://www.hardcoded.net/licenses/bsd_license
import sys
from PyQt4.QtCore import SIGNAL, Qt, QSize
from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox,
QSlider, QSizePolicy, QSpacerItem, QWidget, QCheckBox, QLineEdit, QDialogButtonBox, QApplication)
from PyQt4.QtGui import QLabel, QApplication
from hscommon.trans import tr
from ..base.preferences_dialog import PreferencesDialogBase
from . import preferences
class PreferencesDialog(QDialog):
def __init__(self, parent, app):
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
QDialog.__init__(self, parent, flags)
self.app = app
self._setupUi()
self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked)
self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
class PreferencesDialog(PreferencesDialogBase):
def _setupPreferenceWidgets(self):
self._setupFilterHardnessBox()
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
self._setupAddCheckbox('matchScaledBox', tr("Match scaled pictures together"))
self.widgetsVLayout.addWidget(self.matchScaledBox)
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"))
self.widgetsVLayout.addWidget(self.mixFileKindBox)
self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering"))
self.widgetsVLayout.addWidget(self.useRegexpBox)
self._setupAddCheckbox('removeEmptyFoldersBox', tr("Remove empty folders on delete or move"))
self.widgetsVLayout.addWidget(self.removeEmptyFoldersBox)
self._setupAddCheckbox('ignoreHardlinkMatches', tr("Ignore duplicates hardlinking to the same file"))
self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
self._setupBottomPart()
def _setupUi(self):
self.setWindowTitle("Preferences")
self.resize(304, 263)
self.setSizeGripEnabled(False)
self.setModal(True)
self.verticalLayout_2 = QVBoxLayout(self)
self.verticalLayout = QVBoxLayout()
self.horizontalLayout_3 = QHBoxLayout()
self.label = QLabel(self)
self.label.setText("Filter Hardness:")
self.label.setMinimumSize(QSize(0, 0))
self.horizontalLayout_3.addWidget(self.label)
self.verticalLayout_3 = QVBoxLayout()
self.verticalLayout_3.setSpacing(0)
self.horizontalLayout_6 = QHBoxLayout()
self.horizontalLayout_6.setSpacing(12)
self.filterHardnessSlider = QSlider(self)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth())
self.filterHardnessSlider.setSizePolicy(sizePolicy)
self.filterHardnessSlider.setMinimum(1)
self.filterHardnessSlider.setMaximum(100)
self.filterHardnessSlider.setTracking(True)
self.filterHardnessSlider.setOrientation(Qt.Horizontal)
self.horizontalLayout_6.addWidget(self.filterHardnessSlider)
self.filterHardnessLabel = QLabel(self)
self.filterHardnessLabel.setText("100")
self.filterHardnessLabel.setMinimumSize(QSize(21, 0))
self.horizontalLayout_6.addWidget(self.filterHardnessLabel)
self.verticalLayout_3.addLayout(self.horizontalLayout_6)
self.horizontalLayout_5 = QHBoxLayout()
self.horizontalLayout_5.setContentsMargins(-1, 0, -1, -1)
self.label_4 = QLabel(self)
self.label_4.setText("More Results")
self.horizontalLayout_5.addWidget(self.label_4)
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(spacerItem)
self.label_3 = QLabel(self)
self.label_3.setText("Fewer Results")
self.horizontalLayout_5.addWidget(self.label_3)
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.matchScaledBox = QCheckBox(self)
self.matchScaledBox.setText("Match scaled pictures together")
self.verticalLayout.addWidget(self.matchScaledBox)
self.mixFileKindBox = QCheckBox(self)
self.mixFileKindBox.setText("Can mix file kind")
self.verticalLayout.addWidget(self.mixFileKindBox)
self.useRegexpBox = QCheckBox(self)
self.useRegexpBox.setText("Use regular expressions when filtering")
self.verticalLayout.addWidget(self.useRegexpBox)
self.removeEmptyFoldersBox = QCheckBox(self)
self.removeEmptyFoldersBox.setText("Remove empty folders on delete or move")
self.verticalLayout.addWidget(self.removeEmptyFoldersBox)
self.ignoreHardlinkMatches = QCheckBox(self)
self.ignoreHardlinkMatches.setText("Ignore duplicates hardlinking to the same file")
self.verticalLayout.addWidget(self.ignoreHardlinkMatches)
self.horizontalLayout_4 = QHBoxLayout()
self.label_5 = QLabel(self)
self.label_5.setText("Copy and Move:")
self.label_5.setMinimumSize(QSize(0, 0))
self.horizontalLayout_4.addWidget(self.label_5)
self.copyMoveDestinationComboBox = QComboBox(self)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.copyMoveDestinationComboBox.sizePolicy().hasHeightForWidth())
self.copyMoveDestinationComboBox.addItem("Right in destination")
self.copyMoveDestinationComboBox.addItem("Recreate relative path")
self.copyMoveDestinationComboBox.addItem("Recreate absolute path")
self.horizontalLayout_4.addWidget(self.copyMoveDestinationComboBox)
self.verticalLayout.addLayout(self.horizontalLayout_4)
self.label_2 = QLabel(self)
self.label_2.setText("Custom Command (arguments: %d for dupe %r for ref):")
self.verticalLayout.addWidget(self.label_2)
self.customCommandEdit = QLineEdit(self)
self.verticalLayout.addWidget(self.customCommandEdit)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults)
self.verticalLayout_2.addWidget(self.buttonBox)
if sys.platform not in {'darwin', 'linux2'}:
self.verticalLayout.removeWidget(self.ignoreHardlinkMatches)
self.ignoreHardlinkMatches.setHidden(True)
def load(self, prefs=None):
if prefs is None:
prefs = self.app.prefs
self.filterHardnessSlider.setValue(prefs.filter_hardness)
self.filterHardnessLabel.setNum(prefs.filter_hardness)
setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
def _load(self, prefs, setchecked):
setchecked(self.matchScaledBox, prefs.match_scaled)
setchecked(self.mixFileKindBox, prefs.mix_file_kind)
setchecked(self.useRegexpBox, prefs.use_regexp)
setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders)
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
self.customCommandEdit.setText(prefs.custom_command)
def save(self):
prefs = self.app.prefs
prefs.filter_hardness = self.filterHardnessSlider.value()
ischecked = lambda cb: cb.checkState() == Qt.Checked
def _save(self, prefs, ischecked):
prefs.match_scaled = ischecked(self.matchScaledBox)
prefs.mix_file_kind = ischecked(self.mixFileKindBox)
prefs.use_regexp = ischecked(self.useRegexpBox)
prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox)
prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex()
prefs.custom_command = str(self.customCommandEdit.text())
def resetToDefaults(self):
self.load(preferences.Preferences())
#--- Events
def buttonClicked(self, button):
role = self.buttonBox.buttonRole(button)
if role == QDialogButtonBox.ResetRole:
self.resetToDefaults()
if __name__ == '__main__':
import sys
from ..testapp import TestApp
app = QApplication([])
dgapp = TestApp()

View File

@ -9,19 +9,20 @@
from PyQt4.QtCore import SIGNAL
from PyQt4.QtGui import QMessageBox, QAction
from hscommon.trans import tr, trmsg
from ..base.result_window import ResultWindow as ResultWindowBase
class ResultWindow(ResultWindowBase):
def _setupUi(self):
ResultWindowBase._setupUi(self)
self.actionClearPictureCache = QAction("Clear Picture Cache", self)
self.actionClearPictureCache = QAction(tr("Clear Picture Cache"), self)
self.menuFile.insertAction(self.actionClearIgnoreList, self.actionClearPictureCache)
self.connect(self.actionClearPictureCache, SIGNAL("triggered()"), self.clearPictureCacheTriggered)
def clearPictureCacheTriggered(self):
title = "Clear Picture Cache"
msg = "Do you really want to remove all your cached picture analysis?"
title = tr("Clear Picture Cache")
msg = trmsg("ClearPictureCacheConfirmMsg")
if self.app.confirm(title, msg, QMessageBox.No):
self.app.scanner.clear_picture_cache()
QMessageBox.information(self, title, "Picture cache cleared.")
QMessageBox.information(self, title, trmsg("PictureCacheClearedMsg"))

View File

@ -6,7 +6,7 @@
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from core_se import data, __version__
from core_se import data, __appname__
from core.directories import Directories as DirectoriesBase, STATE_EXCLUDED
from ..base.app import DupeGuru as DupeGuruBase
@ -26,8 +26,7 @@ class Directories(DirectoriesBase):
class DupeGuru(DupeGuruBase):
EDITION = 'se'
LOGO_NAME = 'logo_se'
NAME = 'dupeGuru'
VERSION = __version__
NAME = __appname__
DELTA_COLUMNS = frozenset([2, 4])
def __init__(self):

View File

@ -9,12 +9,13 @@
from PyQt4.QtCore import QSize
from PyQt4.QtGui import QVBoxLayout, QAbstractItemView
from hscommon.trans import tr
from ..base.details_dialog import DetailsDialog as DetailsDialogBase
from ..base.details_table import DetailsTable
class DetailsDialog(DetailsDialogBase):
def _setupUi(self):
self.setWindowTitle("Details")
self.setWindowTitle(tr("Details"))
self.resize(502, 186)
self.setMinimumSize(QSize(200, 0))
self.verticalLayout = QVBoxLayout(self)

View File

@ -7,14 +7,16 @@
# http://www.hardcoded.net/licenses/bsd_license
import sys
from PyQt4.QtCore import SIGNAL, Qt, QSize
from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox,
QSlider, QSizePolicy, QSpacerItem, QWidget, QCheckBox, QLineEdit, QDialogButtonBox, QApplication)
from PyQt4.QtCore import SIGNAL, QSize
from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QSizePolicy, QSpacerItem,
QWidget, QLineEdit, QApplication)
from hscommon.trans import tr
from hscommon.util import tryint
from core.scanner import ScanType
from ..base.preferences_dialog import PreferencesDialogBase
from . import preferences
SCAN_TYPE_ORDER = [
@ -22,97 +24,41 @@ SCAN_TYPE_ORDER = [
ScanType.Contents,
]
class PreferencesDialog(QDialog):
class PreferencesDialog(PreferencesDialogBase):
def __init__(self, parent, app):
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
QDialog.__init__(self, parent, flags)
self.app = app
self._setupUi()
PreferencesDialogBase.__init__(self, parent, app)
self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked)
self.connect(self.scanTypeComboBox, SIGNAL('currentIndexChanged(int)'), self.scanTypeChanged)
self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
def _setupUi(self):
self.setWindowTitle("Preferences")
self.resize(308, 361)
self.setSizeGripEnabled(False)
self.setModal(True)
self.verticalLayout_2 = QVBoxLayout(self)
self.verticalLayout = QVBoxLayout()
def _setupPreferenceWidgets(self):
self.horizontalLayout = QHBoxLayout()
self.label_2 = QLabel(self)
self.label_2.setText("Scan Type:")
self.label_2.setText(tr("Scan Type:"))
self.label_2.setMinimumSize(QSize(100, 0))
self.label_2.setMaximumSize(QSize(100, 16777215))
self.horizontalLayout.addWidget(self.label_2)
self.scanTypeComboBox = QComboBox(self)
self.scanTypeComboBox.addItem("Filename")
self.scanTypeComboBox.addItem("Contents")
self.scanTypeComboBox.addItem(tr("Filename"))
self.scanTypeComboBox.addItem(tr("Contents"))
self.horizontalLayout.addWidget(self.scanTypeComboBox)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_3 = QHBoxLayout()
self.label = QLabel(self)
self.label.setText("Filter Hardness:")
self.label.setMinimumSize(QSize(100, 0))
self.label.setMaximumSize(QSize(100, 16777215))
self.horizontalLayout_3.addWidget(self.label)
self.verticalLayout_3 = QVBoxLayout()
self.verticalLayout_3.setSpacing(0)
self.horizontalLayout_6 = QHBoxLayout()
self.horizontalLayout_6.setSpacing(12)
self.filterHardnessSlider = QSlider(self)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth())
self.filterHardnessSlider.setSizePolicy(sizePolicy)
self.filterHardnessSlider.setMinimum(1)
self.filterHardnessSlider.setMaximum(100)
self.filterHardnessSlider.setTracking(True)
self.filterHardnessSlider.setOrientation(Qt.Horizontal)
self.horizontalLayout_6.addWidget(self.filterHardnessSlider)
self.filterHardnessLabel = QLabel(self)
self.filterHardnessLabel.setText("100")
self.filterHardnessLabel.setMinimumSize(QSize(21, 0))
self.horizontalLayout_6.addWidget(self.filterHardnessLabel)
self.verticalLayout_3.addLayout(self.horizontalLayout_6)
self.horizontalLayout_5 = QHBoxLayout()
self.horizontalLayout_5.setContentsMargins(-1, 0, -1, -1)
self.label_4 = QLabel(self)
self.label_4.setText("More Results")
self.horizontalLayout_5.addWidget(self.label_4)
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(spacerItem)
self.label_3 = QLabel(self)
self.label_3.setText("Fewer Results")
self.horizontalLayout_5.addWidget(self.label_3)
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.widgetsVLayout.addLayout(self.horizontalLayout)
self._setupFilterHardnessBox()
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
self.widget = QWidget(self)
self.widget.setMinimumSize(QSize(0, 136))
self.verticalLayout_4 = QVBoxLayout(self.widget)
self.wordWeightingBox = QCheckBox(self.widget)
self.wordWeightingBox.setText("Word weighting")
self._setupAddCheckbox('wordWeightingBox', tr("Word weighting"), self.widget)
self.verticalLayout_4.addWidget(self.wordWeightingBox)
self.matchSimilarBox = QCheckBox(self.widget)
self.matchSimilarBox.setText("Match similar words")
self._setupAddCheckbox('matchSimilarBox', tr("Match similar words"), self.widget)
self.verticalLayout_4.addWidget(self.matchSimilarBox)
self.mixFileKindBox = QCheckBox(self.widget)
self.mixFileKindBox.setText("Can mix file kind")
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"), self.widget)
self.verticalLayout_4.addWidget(self.mixFileKindBox)
self.useRegexpBox = QCheckBox(self.widget)
self.useRegexpBox.setText("Use regular expressions when filtering")
self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering"), self.widget)
self.verticalLayout_4.addWidget(self.useRegexpBox)
self.removeEmptyFoldersBox = QCheckBox(self.widget)
self.removeEmptyFoldersBox.setText("Remove empty folders on delete or move")
self._setupAddCheckbox('removeEmptyFoldersBox', tr("Remove empty folders on delete or move"), self.widget)
self.verticalLayout_4.addWidget(self.removeEmptyFoldersBox)
self.horizontalLayout_2 = QHBoxLayout()
self.ignoreSmallFilesBox = QCheckBox(self.widget)
self.ignoreSmallFilesBox.setText("Ignore files smaller than")
self._setupAddCheckbox('ignoreSmallFilesBox', tr("Ignore files smaller than"), self.widget)
self.horizontalLayout_2.addWidget(self.ignoreSmallFilesBox)
self.sizeThresholdEdit = QLineEdit(self.widget)
sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
@ -123,94 +69,43 @@ class PreferencesDialog(QDialog):
self.sizeThresholdEdit.setMaximumSize(QSize(50, 16777215))
self.horizontalLayout_2.addWidget(self.sizeThresholdEdit)
self.label_6 = QLabel(self.widget)
self.label_6.setText("KB")
self.label_6.setText(tr("KB"))
self.horizontalLayout_2.addWidget(self.label_6)
spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem1)
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.ignoreHardlinkMatches = QCheckBox(self.widget)
self.ignoreHardlinkMatches.setText("Ignore duplicates hardlinking to the same file")
self._setupAddCheckbox('ignoreHardlinkMatches', tr("Ignore duplicates hardlinking to the same file"), self.widget)
self.verticalLayout_4.addWidget(self.ignoreHardlinkMatches)
self.verticalLayout.addWidget(self.widget)
self.horizontalLayout_4 = QHBoxLayout()
self.label_5 = QLabel(self)
self.label_5.setText("Copy and Move:")
self.label_5.setMinimumSize(QSize(100, 0))
self.label_5.setMaximumSize(QSize(100, 16777215))
self.horizontalLayout_4.addWidget(self.label_5)
self.copyMoveDestinationComboBox = QComboBox(self)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.copyMoveDestinationComboBox.sizePolicy().hasHeightForWidth())
self.copyMoveDestinationComboBox.setSizePolicy(sizePolicy)
self.copyMoveDestinationComboBox.addItem("Right in destination")
self.copyMoveDestinationComboBox.addItem("Recreate relative path")
self.copyMoveDestinationComboBox.addItem("Recreate absolute path")
self.horizontalLayout_4.addWidget(self.copyMoveDestinationComboBox)
self.verticalLayout.addLayout(self.horizontalLayout_4)
self.label_7 = QLabel(self)
self.label_7.setText("Custom Command (arguments: %d for dupe, %r for ref):")
self.verticalLayout.addWidget(self.label_7)
self.customCommandEdit = QLineEdit(self)
self.verticalLayout.addWidget(self.customCommandEdit)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults)
self.verticalLayout_2.addWidget(self.buttonBox)
self.widgetsVLayout.addWidget(self.widget)
self._setupBottomPart()
def _setupUi(self):
PreferencesDialogBase._setupUi(self)
if sys.platform not in {'darwin', 'linux2'}:
self.verticalLayout_4.removeWidget(self.ignoreHardlinkMatches)
self.ignoreHardlinkMatches.setHidden(True)
if sys.platform == 'linux2':
# Under linux, whether it's a Qt layout bug or something else, the size threshold text edit
# doesn't have enough space, so we make the pref pane higher to compensate.
self.resize(self.width(), 400)
def load(self, prefs=None):
if prefs is None:
prefs = self.app.prefs
self.filterHardnessSlider.setValue(prefs.filter_hardness)
self.filterHardnessLabel.setNum(prefs.filter_hardness)
def _load(self, prefs, setchecked):
scan_type_index = SCAN_TYPE_ORDER.index(prefs.scan_type)
self.scanTypeComboBox.setCurrentIndex(scan_type_index)
setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
setchecked(self.matchSimilarBox, prefs.match_similar)
setchecked(self.wordWeightingBox, prefs.word_weighting)
setchecked(self.mixFileKindBox, prefs.mix_file_kind)
setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches)
setchecked(self.useRegexpBox, prefs.use_regexp)
setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders)
setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files)
self.sizeThresholdEdit.setText(str(prefs.small_file_threshold))
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
self.customCommandEdit.setText(prefs.custom_command)
def save(self):
prefs = self.app.prefs
prefs.filter_hardness = self.filterHardnessSlider.value()
def _save(self, prefs, ischecked):
prefs.scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()]
ischecked = lambda cb: cb.checkState() == Qt.Checked
prefs.match_similar = ischecked(self.matchSimilarBox)
prefs.word_weighting = ischecked(self.wordWeightingBox)
prefs.mix_file_kind = ischecked(self.mixFileKindBox)
prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches)
prefs.use_regexp = ischecked(self.useRegexpBox)
prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox)
prefs.ignore_small_files = ischecked(self.ignoreSmallFilesBox)
prefs.small_file_threshold = tryint(self.sizeThresholdEdit.text())
prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex()
prefs.custom_command = str(self.customCommandEdit.text())
def resetToDefaults(self):
self.load(preferences.Preferences())
#--- Events
def buttonClicked(self, button):
role = self.buttonBox.buttonRole(button)
if role == QDialogButtonBox.ResetRole:
self.resetToDefaults()
def scanTypeChanged(self, index):
scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()]
word_based = scan_type == ScanType.Filename

View File

@ -15,12 +15,16 @@ from PyQt4.QtGui import QApplication, QIcon, QPixmap
from hscommon.trans import install_qt_trans
from qtlib.error_report_dialog import install_excepthook
from qt.base import dg_rc
from core_{{edition}} import __version__, __appname__
if sys.platform == 'win32':
import qt.base.cxfreeze_fix
if __name__ == "__main__":
app = QApplication(sys.argv)
QCoreApplication.setOrganizationName('Hardcoded Software')
QCoreApplication.setApplicationName(__appname__)
QCoreApplication.setApplicationVersion(__version__)
settings = QSettings()
lang = settings.value('Language').toString()
install_qt_trans(lang)
@ -28,9 +32,6 @@ if __name__ == "__main__":
# has been installed
from qt.{{edition}}.app import DupeGuru
app.setWindowIcon(QIcon(QPixmap(":/{0}".format(DupeGuru.LOGO_NAME))))
QCoreApplication.setOrganizationName('Hardcoded Software')
QCoreApplication.setApplicationName(DupeGuru.NAME)
QCoreApplication.setApplicationVersion(DupeGuru.VERSION)
dgapp = DupeGuru()
install_excepthook()
sys.exit(app.exec_())