mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-22 14:41:39 +00:00
Format files with black
- Format all files with black - Update tox.ini flake8 arguments to be compatible - Add black to requirements-extra.txt - Reduce ignored flake8 rules and fix a few violations
This commit is contained in:
147
qt/app.py
147
qt/app.py
@@ -36,11 +36,12 @@ from .me.preferences_dialog import PreferencesDialog as PreferencesDialogMusic
|
||||
from .pe.preferences_dialog import PreferencesDialog as PreferencesDialogPicture
|
||||
from .pe.photo import File as PlatSpecificPhoto
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class DupeGuru(QObject):
|
||||
LOGO_NAME = 'logo_se'
|
||||
NAME = 'dupeGuru'
|
||||
LOGO_NAME = "logo_se"
|
||||
NAME = "dupeGuru"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
@@ -49,20 +50,28 @@ class DupeGuru(QObject):
|
||||
self.model = DupeGuruModel(view=self)
|
||||
self._setup()
|
||||
|
||||
#--- Private
|
||||
# --- Private
|
||||
def _setup(self):
|
||||
core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = PlatSpecificPhoto
|
||||
self._setupActions()
|
||||
self._update_options()
|
||||
self.recentResults = Recent(self, 'recentResults')
|
||||
self.recentResults = Recent(self, "recentResults")
|
||||
self.recentResults.mustOpenItem.connect(self.model.load_from)
|
||||
self.resultWindow = None
|
||||
self.details_dialog = None
|
||||
self.directories_dialog = DirectoriesDialog(self)
|
||||
self.progress_window = ProgressWindow(self.directories_dialog, self.model.progress_window)
|
||||
self.problemDialog = ProblemDialog(parent=self.directories_dialog, model=self.model.problem_dialog)
|
||||
self.ignoreListDialog = IgnoreListDialog(parent=self.directories_dialog, model=self.model.ignore_list_dialog)
|
||||
self.deletionOptions = DeletionOptions(parent=self.directories_dialog, model=self.model.deletion_options)
|
||||
self.progress_window = ProgressWindow(
|
||||
self.directories_dialog, self.model.progress_window
|
||||
)
|
||||
self.problemDialog = ProblemDialog(
|
||||
parent=self.directories_dialog, model=self.model.problem_dialog
|
||||
)
|
||||
self.ignoreListDialog = IgnoreListDialog(
|
||||
parent=self.directories_dialog, model=self.model.ignore_list_dialog
|
||||
)
|
||||
self.deletionOptions = DeletionOptions(
|
||||
parent=self.directories_dialog, model=self.model.deletion_options
|
||||
)
|
||||
self.about_box = AboutBox(self.directories_dialog, self)
|
||||
|
||||
self.directories_dialog.show()
|
||||
@@ -80,46 +89,70 @@ class DupeGuru(QObject):
|
||||
# Setup actions that are common to both the directory dialog and the results window.
|
||||
# (name, shortcut, icon, desc, func)
|
||||
ACTIONS = [
|
||||
('actionQuit', 'Ctrl+Q', '', tr("Quit"), self.quitTriggered),
|
||||
('actionPreferences', 'Ctrl+P', '', tr("Options"), self.preferencesTriggered),
|
||||
('actionIgnoreList', '', '', tr("Ignore List"), self.ignoreListTriggered),
|
||||
('actionClearPictureCache', 'Ctrl+Shift+P', '', tr("Clear Picture Cache"), self.clearPictureCacheTriggered),
|
||||
('actionShowHelp', 'F1', '', tr("dupeGuru Help"), self.showHelpTriggered),
|
||||
('actionAbout', '', '', tr("About dupeGuru"), self.showAboutBoxTriggered),
|
||||
('actionOpenDebugLog', '', '', tr("Open Debug Log"), self.openDebugLogTriggered),
|
||||
("actionQuit", "Ctrl+Q", "", tr("Quit"), self.quitTriggered),
|
||||
(
|
||||
"actionPreferences",
|
||||
"Ctrl+P",
|
||||
"",
|
||||
tr("Options"),
|
||||
self.preferencesTriggered,
|
||||
),
|
||||
("actionIgnoreList", "", "", tr("Ignore List"), self.ignoreListTriggered),
|
||||
(
|
||||
"actionClearPictureCache",
|
||||
"Ctrl+Shift+P",
|
||||
"",
|
||||
tr("Clear Picture Cache"),
|
||||
self.clearPictureCacheTriggered,
|
||||
),
|
||||
("actionShowHelp", "F1", "", tr("dupeGuru Help"), self.showHelpTriggered),
|
||||
("actionAbout", "", "", tr("About dupeGuru"), self.showAboutBoxTriggered),
|
||||
(
|
||||
"actionOpenDebugLog",
|
||||
"",
|
||||
"",
|
||||
tr("Open Debug Log"),
|
||||
self.openDebugLogTriggered,
|
||||
),
|
||||
]
|
||||
createActions(ACTIONS, self)
|
||||
|
||||
def _update_options(self):
|
||||
self.model.options['mix_file_kind'] = self.prefs.mix_file_kind
|
||||
self.model.options['escape_filter_regexp'] = not self.prefs.use_regexp
|
||||
self.model.options['clean_empty_dirs'] = self.prefs.remove_empty_folders
|
||||
self.model.options['ignore_hardlink_matches'] = self.prefs.ignore_hardlink_matches
|
||||
self.model.options['copymove_dest_type'] = self.prefs.destination_type
|
||||
self.model.options['scan_type'] = self.prefs.get_scan_type(self.model.app_mode)
|
||||
self.model.options['min_match_percentage'] = self.prefs.filter_hardness
|
||||
self.model.options['word_weighting'] = self.prefs.word_weighting
|
||||
self.model.options['match_similar_words'] = self.prefs.match_similar
|
||||
threshold = self.prefs.small_file_threshold if self.prefs.ignore_small_files else 0
|
||||
self.model.options['size_threshold'] = threshold * 1024 # threshold is in KB. the scanner wants bytes
|
||||
self.model.options["mix_file_kind"] = self.prefs.mix_file_kind
|
||||
self.model.options["escape_filter_regexp"] = not self.prefs.use_regexp
|
||||
self.model.options["clean_empty_dirs"] = self.prefs.remove_empty_folders
|
||||
self.model.options[
|
||||
"ignore_hardlink_matches"
|
||||
] = self.prefs.ignore_hardlink_matches
|
||||
self.model.options["copymove_dest_type"] = self.prefs.destination_type
|
||||
self.model.options["scan_type"] = self.prefs.get_scan_type(self.model.app_mode)
|
||||
self.model.options["min_match_percentage"] = self.prefs.filter_hardness
|
||||
self.model.options["word_weighting"] = self.prefs.word_weighting
|
||||
self.model.options["match_similar_words"] = self.prefs.match_similar
|
||||
threshold = (
|
||||
self.prefs.small_file_threshold if self.prefs.ignore_small_files else 0
|
||||
)
|
||||
self.model.options["size_threshold"] = (
|
||||
threshold * 1024
|
||||
) # threshold is in KB. the scanner wants bytes
|
||||
scanned_tags = set()
|
||||
if self.prefs.scan_tag_track:
|
||||
scanned_tags.add('track')
|
||||
scanned_tags.add("track")
|
||||
if self.prefs.scan_tag_artist:
|
||||
scanned_tags.add('artist')
|
||||
scanned_tags.add("artist")
|
||||
if self.prefs.scan_tag_album:
|
||||
scanned_tags.add('album')
|
||||
scanned_tags.add("album")
|
||||
if self.prefs.scan_tag_title:
|
||||
scanned_tags.add('title')
|
||||
scanned_tags.add("title")
|
||||
if self.prefs.scan_tag_genre:
|
||||
scanned_tags.add('genre')
|
||||
scanned_tags.add("genre")
|
||||
if self.prefs.scan_tag_year:
|
||||
scanned_tags.add('year')
|
||||
self.model.options['scanned_tags'] = scanned_tags
|
||||
self.model.options['match_scaled'] = self.prefs.match_scaled
|
||||
self.model.options['picture_cache_type'] = self.prefs.picture_cache_type
|
||||
scanned_tags.add("year")
|
||||
self.model.options["scanned_tags"] = scanned_tags
|
||||
self.model.options["match_scaled"] = self.prefs.match_scaled
|
||||
self.model.options["picture_cache_type"] = self.prefs.picture_cache_type
|
||||
|
||||
#--- Private
|
||||
# --- Private
|
||||
def _get_details_dialog_class(self):
|
||||
if self.model.app_mode == AppMode.Picture:
|
||||
return DetailsDialogPicture
|
||||
@@ -136,7 +169,7 @@ class DupeGuru(QObject):
|
||||
else:
|
||||
return PreferencesDialogStandard
|
||||
|
||||
#--- Public
|
||||
# --- Public
|
||||
def add_selected_to_ignore_list(self):
|
||||
self.model.add_selected_to_ignore_list()
|
||||
|
||||
@@ -166,17 +199,19 @@ class DupeGuru(QObject):
|
||||
self.model.save()
|
||||
QApplication.quit()
|
||||
|
||||
#--- Signals
|
||||
# --- Signals
|
||||
willSavePrefs = pyqtSignal()
|
||||
SIGTERM = pyqtSignal()
|
||||
|
||||
#--- Events
|
||||
# --- Events
|
||||
def finishedLaunching(self):
|
||||
if sys.getfilesystemencoding() == 'ascii':
|
||||
if sys.getfilesystemencoding() == "ascii":
|
||||
# No need to localize this, it's a debugging message.
|
||||
msg = "Something is wrong with the way your system locale is set. If the files you're "\
|
||||
"scanning have accented letters, you'll probably get a crash. It is advised that "\
|
||||
msg = (
|
||||
"Something is wrong with the way your system locale is set. If the files you're "
|
||||
"scanning have accented letters, you'll probably get a crash. It is advised that "
|
||||
"you set your system locale properly."
|
||||
)
|
||||
QMessageBox.warning(self.directories_dialog, "Wrong Locale", msg)
|
||||
|
||||
def clearPictureCacheTriggered(self):
|
||||
@@ -191,11 +226,13 @@ class DupeGuru(QObject):
|
||||
self.model.ignore_list_dialog.show()
|
||||
|
||||
def openDebugLogTriggered(self):
|
||||
debugLogPath = op.join(self.model.appdata, 'debug.log')
|
||||
debugLogPath = op.join(self.model.appdata, "debug.log")
|
||||
desktop.open_path(debugLogPath)
|
||||
|
||||
def preferencesTriggered(self):
|
||||
preferences_dialog = self._get_preferences_dialog_class()(self.directories_dialog, self)
|
||||
preferences_dialog = self._get_preferences_dialog_class()(
|
||||
self.directories_dialog, self
|
||||
)
|
||||
preferences_dialog.load()
|
||||
result = preferences_dialog.exec()
|
||||
if result == QDialog.Accepted:
|
||||
@@ -212,17 +249,17 @@ class DupeGuru(QObject):
|
||||
|
||||
def showHelpTriggered(self):
|
||||
base_path = platform.HELP_PATH
|
||||
help_path = op.abspath(op.join(base_path, 'index.html'))
|
||||
help_path = op.abspath(op.join(base_path, "index.html"))
|
||||
if op.exists(help_path):
|
||||
url = QUrl.fromLocalFile(help_path)
|
||||
else:
|
||||
url = QUrl('https://www.hardcoded.net/dupeguru/help/en/')
|
||||
url = QUrl("https://www.hardcoded.net/dupeguru/help/en/")
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def handleSIGTERM(self):
|
||||
self.shutdown()
|
||||
|
||||
#--- model --> view
|
||||
# --- model --> view
|
||||
def get_default(self, key):
|
||||
return self.prefs.get_value(key)
|
||||
|
||||
@@ -231,10 +268,10 @@ class DupeGuru(QObject):
|
||||
|
||||
def show_message(self, msg):
|
||||
window = QApplication.activeWindow()
|
||||
QMessageBox.information(window, '', msg)
|
||||
QMessageBox.information(window, "", msg)
|
||||
|
||||
def ask_yes_no(self, prompt):
|
||||
return self.confirm('', prompt)
|
||||
return self.confirm("", prompt)
|
||||
|
||||
def create_results_window(self):
|
||||
"""Creates resultWindow and details_dialog depending on the selected ``app_mode``.
|
||||
@@ -256,11 +293,13 @@ class DupeGuru(QObject):
|
||||
|
||||
def select_dest_folder(self, prompt):
|
||||
flags = QFileDialog.ShowDirsOnly
|
||||
return QFileDialog.getExistingDirectory(self.resultWindow, prompt, '', flags)
|
||||
return QFileDialog.getExistingDirectory(self.resultWindow, prompt, "", flags)
|
||||
|
||||
def select_dest_file(self, prompt, extension):
|
||||
files = tr("{} file (*.{})").format(extension.upper(), extension)
|
||||
destination, chosen_filter = QFileDialog.getSaveFileName(self.resultWindow, prompt, '', files)
|
||||
if not destination.endswith('.{}'.format(extension)):
|
||||
destination = '{}.{}'.format(destination, extension)
|
||||
destination, chosen_filter = QFileDialog.getSaveFileName(
|
||||
self.resultWindow, prompt, "", files
|
||||
)
|
||||
if not destination.endswith(".{}".format(extension)):
|
||||
destination = "{}.{}".format(destination, extension)
|
||||
return destination
|
||||
|
||||
@@ -12,7 +12,8 @@ from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QCheckBox, QDialogButt
|
||||
from hscommon.trans import trget
|
||||
from qtlib.radio_box import RadioBox
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class DeletionOptions(QDialog):
|
||||
def __init__(self, parent, model, **kwargs):
|
||||
@@ -41,7 +42,9 @@ class DeletionOptions(QDialog):
|
||||
self.linkMessageLabel = QLabel(text)
|
||||
self.linkMessageLabel.setWordWrap(True)
|
||||
self.verticalLayout.addWidget(self.linkMessageLabel)
|
||||
self.linkTypeRadio = RadioBox(items=[tr("Symlink"), tr("Hardlink")], spread=False)
|
||||
self.linkTypeRadio = RadioBox(
|
||||
items=[tr("Symlink"), tr("Hardlink")], spread=False
|
||||
)
|
||||
self.verticalLayout.addWidget(self.linkTypeRadio)
|
||||
if not self.model.supports_links():
|
||||
self.linkCheckbox.setEnabled(False)
|
||||
@@ -60,11 +63,11 @@ class DeletionOptions(QDialog):
|
||||
self.buttonBox.addButton(tr("Cancel"), QDialogButtonBox.RejectRole)
|
||||
self.verticalLayout.addWidget(self.buttonBox)
|
||||
|
||||
#--- Signals
|
||||
# --- Signals
|
||||
def linkCheckboxChanged(self, changed: int):
|
||||
self.model.link_deleted = bool(changed)
|
||||
|
||||
#--- model --> view
|
||||
# --- model --> view
|
||||
def update_msg(self, msg: str):
|
||||
self.msgLabel.setText(msg)
|
||||
|
||||
@@ -80,4 +83,3 @@ class DeletionOptions(QDialog):
|
||||
|
||||
def set_hardlink_option_enabled(self, is_enabled: bool):
|
||||
self.linkTypeRadio.setEnabled(is_enabled)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2010-02-05
|
||||
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
@@ -11,6 +11,7 @@ from PyQt5.QtWidgets import QDialog
|
||||
|
||||
from .details_table import DetailsModel
|
||||
|
||||
|
||||
class DetailsDialog(QDialog):
|
||||
def __init__(self, parent, app, **kwargs):
|
||||
super().__init__(parent, Qt.Tool, **kwargs)
|
||||
@@ -20,28 +21,27 @@ class DetailsDialog(QDialog):
|
||||
# To avoid saving uninitialized geometry on appWillSavePrefs, we track whether our dialog
|
||||
# has been shown. If it has, we know that our geometry should be saved.
|
||||
self._shown_once = False
|
||||
self.app.prefs.restoreGeometry('DetailsWindowRect', self)
|
||||
self.app.prefs.restoreGeometry("DetailsWindowRect", self)
|
||||
self.tableModel = DetailsModel(self.model)
|
||||
# tableView is defined in subclasses
|
||||
self.tableView.setModel(self.tableModel)
|
||||
self.model.view = self
|
||||
|
||||
|
||||
self.app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||
|
||||
def _setupUi(self): # Virtual
|
||||
|
||||
def _setupUi(self): # Virtual
|
||||
pass
|
||||
|
||||
|
||||
def show(self):
|
||||
self._shown_once = True
|
||||
super().show()
|
||||
|
||||
#--- Events
|
||||
# --- Events
|
||||
def appWillSavePrefs(self):
|
||||
if self._shown_once:
|
||||
self.app.prefs.saveGeometry('DetailsWindowRect', self)
|
||||
|
||||
#--- model --> view
|
||||
self.app.prefs.saveGeometry("DetailsWindowRect", self)
|
||||
|
||||
# --- model --> view
|
||||
def refresh(self):
|
||||
self.tableModel.beginResetModel()
|
||||
self.tableModel.endResetModel()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-17
|
||||
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from PyQt5.QtCore import Qt, QAbstractTableModel
|
||||
@@ -11,18 +11,19 @@ from PyQt5.QtWidgets import QHeaderView, QTableView
|
||||
|
||||
from hscommon.trans import trget
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
HEADER = [tr("Attribute"), tr("Selected"), tr("Reference")]
|
||||
|
||||
|
||||
class DetailsModel(QAbstractTableModel):
|
||||
def __init__(self, model, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.model = model
|
||||
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(HEADER)
|
||||
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid():
|
||||
return None
|
||||
@@ -31,15 +32,19 @@ class DetailsModel(QAbstractTableModel):
|
||||
column = index.column()
|
||||
row = index.row()
|
||||
return self.model.row(row)[column]
|
||||
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if orientation == Qt.Horizontal and role == Qt.DisplayRole and section < len(HEADER):
|
||||
if (
|
||||
orientation == Qt.Horizontal
|
||||
and role == Qt.DisplayRole
|
||||
and section < len(HEADER)
|
||||
):
|
||||
return HEADER[section]
|
||||
return None
|
||||
|
||||
|
||||
def rowCount(self, parent):
|
||||
return self.model.row_count()
|
||||
|
||||
|
||||
|
||||
class DetailsTable(QTableView):
|
||||
def __init__(self, *args):
|
||||
@@ -47,7 +52,7 @@ class DetailsTable(QTableView):
|
||||
self.setAlternatingRowColors(True)
|
||||
self.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.setShowGrid(False)
|
||||
|
||||
|
||||
def setModel(self, model):
|
||||
QTableView.setModel(self, model)
|
||||
# The model needs to be set to set header stuff
|
||||
@@ -61,4 +66,3 @@ class DetailsTable(QTableView):
|
||||
vheader = self.verticalHeader()
|
||||
vheader.setVisible(False)
|
||||
vheader.setDefaultSectionSize(18)
|
||||
|
||||
|
||||
@@ -6,9 +6,21 @@
|
||||
|
||||
from PyQt5.QtCore import QRect, Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QFileDialog, QHeaderView, QVBoxLayout, QHBoxLayout, QTreeView,
|
||||
QAbstractItemView, QSpacerItem, QSizePolicy, QPushButton, QMainWindow, QMenuBar, QMenu, QLabel,
|
||||
QComboBox
|
||||
QWidget,
|
||||
QFileDialog,
|
||||
QHeaderView,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QTreeView,
|
||||
QAbstractItemView,
|
||||
QSpacerItem,
|
||||
QSizePolicy,
|
||||
QPushButton,
|
||||
QMainWindow,
|
||||
QMenuBar,
|
||||
QMenu,
|
||||
QLabel,
|
||||
QComboBox,
|
||||
)
|
||||
from PyQt5.QtGui import QPixmap, QIcon
|
||||
|
||||
@@ -21,17 +33,20 @@ from qtlib.util import moveToScreenCenter, createActions
|
||||
from . import platform
|
||||
from .directories_model import DirectoriesModel, DirectoriesDelegate
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class DirectoriesDialog(QMainWindow):
|
||||
def __init__(self, app, **kwargs):
|
||||
super().__init__(None, **kwargs)
|
||||
self.app = app
|
||||
self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS
|
||||
self.recentFolders = Recent(self.app, 'recentFolders')
|
||||
self.recentFolders = Recent(self.app, "recentFolders")
|
||||
self._setupUi()
|
||||
self._updateScanTypeList()
|
||||
self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView)
|
||||
self.directoriesModel = DirectoriesModel(
|
||||
self.app.model.directory_tree, view=self.treeView
|
||||
)
|
||||
self.directoriesDelegate = DirectoriesDelegate()
|
||||
self.treeView.setItemDelegate(self.directoriesDelegate)
|
||||
self._setupColumns()
|
||||
@@ -61,9 +76,21 @@ class DirectoriesDialog(QMainWindow):
|
||||
def _setupActions(self):
|
||||
# (name, shortcut, icon, desc, func)
|
||||
ACTIONS = [
|
||||
('actionLoadResults', 'Ctrl+L', '', tr("Load Results..."), self.loadResultsTriggered),
|
||||
('actionShowResultsWindow', '', '', tr("Results Window"), self.app.showResultsWindow),
|
||||
('actionAddFolder', '', '', tr("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)
|
||||
|
||||
@@ -117,9 +144,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(label)
|
||||
self.appModeRadioBox = RadioBox(
|
||||
self,
|
||||
items=[tr("Standard"), tr("Music"), tr("Picture")],
|
||||
spread=False
|
||||
self, items=[tr("Standard"), tr("Music"), tr("Picture")], spread=False
|
||||
)
|
||||
hl.addWidget(self.appModeRadioBox)
|
||||
self.verticalLayout.addLayout(hl)
|
||||
@@ -129,21 +154,28 @@ class DirectoriesDialog(QMainWindow):
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(label)
|
||||
self.scanTypeComboBox = QComboBox(self)
|
||||
self.scanTypeComboBox.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
|
||||
self.scanTypeComboBox.setSizePolicy(
|
||||
QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
)
|
||||
self.scanTypeComboBox.setMaximumWidth(400)
|
||||
hl.addWidget(self.scanTypeComboBox)
|
||||
self.showPreferencesButton = QPushButton(tr("More Options"), self.centralwidget)
|
||||
self.showPreferencesButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(self.showPreferencesButton)
|
||||
self.verticalLayout.addLayout(hl)
|
||||
self.promptLabel = QLabel(tr("Select folders to scan and press \"Scan\"."), self.centralwidget)
|
||||
self.promptLabel = QLabel(
|
||||
tr('Select folders to scan and press "Scan".'), self.centralwidget
|
||||
)
|
||||
self.verticalLayout.addWidget(self.promptLabel)
|
||||
self.treeView = QTreeView(self.centralwidget)
|
||||
self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.treeView.setAcceptDrops(True)
|
||||
triggers = QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed\
|
||||
triggers = (
|
||||
QAbstractItemView.DoubleClicked
|
||||
| QAbstractItemView.EditKeyPressed
|
||||
| QAbstractItemView.SelectedClicked
|
||||
)
|
||||
self.treeView.setEditTriggers(triggers)
|
||||
self.treeView.setDragDropOverwriteMode(True)
|
||||
self.treeView.setDragDropMode(QAbstractItemView.DropOnly)
|
||||
@@ -208,7 +240,9 @@ class DirectoriesDialog(QMainWindow):
|
||||
|
||||
def _updateScanTypeList(self):
|
||||
try:
|
||||
self.scanTypeComboBox.currentIndexChanged[int].disconnect(self.scanTypeChanged)
|
||||
self.scanTypeComboBox.currentIndexChanged[int].disconnect(
|
||||
self.scanTypeChanged
|
||||
)
|
||||
except TypeError:
|
||||
# Not connected, ignore
|
||||
pass
|
||||
@@ -223,7 +257,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged)
|
||||
self.app._update_options()
|
||||
|
||||
#--- QWidget overrides
|
||||
# --- QWidget overrides
|
||||
def closeEvent(self, event):
|
||||
event.accept()
|
||||
if self.app.model.results.is_modified:
|
||||
@@ -234,11 +268,13 @@ class DirectoriesDialog(QMainWindow):
|
||||
if event.isAccepted():
|
||||
self.app.shutdown()
|
||||
|
||||
#--- Events
|
||||
# --- Events
|
||||
def addFolderTriggered(self):
|
||||
title = tr("Select a folder to add to the scanning list")
|
||||
flags = QFileDialog.ShowDirsOnly
|
||||
dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags))
|
||||
dirpath = str(
|
||||
QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags)
|
||||
)
|
||||
if not dirpath:
|
||||
return
|
||||
self.lastAddedFolder = dirpath
|
||||
@@ -264,8 +300,8 @@ class DirectoriesDialog(QMainWindow):
|
||||
|
||||
def loadResultsTriggered(self):
|
||||
title = tr("Select a results file to load")
|
||||
files = ';;'.join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")])
|
||||
destination = QFileDialog.getOpenFileName(self, title, '', files)[0]
|
||||
files = ";;".join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")])
|
||||
destination = QFileDialog.getOpenFileName(self, title, "", files)[0]
|
||||
if destination:
|
||||
self.app.model.load_from(destination)
|
||||
self.app.recentResults.insertItem(destination)
|
||||
@@ -283,9 +319,10 @@ class DirectoriesDialog(QMainWindow):
|
||||
|
||||
def scanTypeChanged(self, index):
|
||||
scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
|
||||
self.app.prefs.set_scan_type(self.app.model.app_mode, scan_options[index].scan_type)
|
||||
self.app.prefs.set_scan_type(
|
||||
self.app.model.app_mode, scan_options[index].scan_type
|
||||
)
|
||||
self.app._update_options()
|
||||
|
||||
def selectionChanged(self, selected, deselected):
|
||||
self._updateRemoveButton()
|
||||
|
||||
|
||||
@@ -10,19 +10,24 @@ import urllib.parse
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, Qt, QRect, QUrl, QModelIndex, QItemSelection
|
||||
from PyQt5.QtWidgets import (
|
||||
QComboBox, QStyledItemDelegate, QStyle, QStyleOptionComboBox,
|
||||
QStyleOptionViewItem, QApplication
|
||||
QComboBox,
|
||||
QStyledItemDelegate,
|
||||
QStyle,
|
||||
QStyleOptionComboBox,
|
||||
QStyleOptionViewItem,
|
||||
QApplication,
|
||||
)
|
||||
from PyQt5.QtGui import QBrush
|
||||
|
||||
from hscommon.trans import trget
|
||||
from qtlib.tree_model import RefNode, TreeModel
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
HEADERS = [tr("Name"), tr("State")]
|
||||
STATES = [tr("Normal"), tr("Reference"), tr("Excluded")]
|
||||
|
||||
|
||||
class DirectoriesDelegate(QStyledItemDelegate):
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = QComboBox(parent)
|
||||
@@ -39,10 +44,12 @@ class DirectoriesDelegate(QStyledItemDelegate):
|
||||
# On OS X (with Qt4.6.0), adding State_Enabled to the flags causes the whole drawing to
|
||||
# fail (draw nothing), but it's an OS X only glitch. On Windows, it works alright.
|
||||
cboption.state |= QStyle.State_Enabled
|
||||
QApplication.style().drawComplexControl(QStyle.CC_ComboBox, cboption, painter)
|
||||
QApplication.style().drawComplexControl(
|
||||
QStyle.CC_ComboBox, cboption, painter
|
||||
)
|
||||
painter.setBrush(option.palette.text())
|
||||
rect = QRect(option.rect)
|
||||
rect.setLeft(rect.left()+4)
|
||||
rect.setLeft(rect.left() + 4)
|
||||
painter.drawText(rect, Qt.AlignLeft, option.text)
|
||||
else:
|
||||
super().paint(painter, option, index)
|
||||
@@ -68,7 +75,9 @@ class DirectoriesModel(TreeModel):
|
||||
self.view = view
|
||||
self.view.setModel(self)
|
||||
|
||||
self.view.selectionModel().selectionChanged[(QItemSelection, QItemSelection)].connect(self.selectionChanged)
|
||||
self.view.selectionModel().selectionChanged[
|
||||
(QItemSelection, QItemSelection)
|
||||
].connect(self.selectionChanged)
|
||||
|
||||
def _createNode(self, ref, row):
|
||||
return RefNode(self, None, ref, row)
|
||||
@@ -102,11 +111,11 @@ class DirectoriesModel(TreeModel):
|
||||
def dropMimeData(self, mimeData, action, row, column, parentIndex):
|
||||
# the data in mimeData is urlencoded **in utf-8**!!! What we do is to decode, the mime data
|
||||
# with 'ascii', which works since it's urlencoded. Then, we pass that to urllib.
|
||||
if not mimeData.hasFormat('text/uri-list'):
|
||||
if not mimeData.hasFormat("text/uri-list"):
|
||||
return False
|
||||
data = bytes(mimeData.data('text/uri-list')).decode('ascii')
|
||||
data = bytes(mimeData.data("text/uri-list")).decode("ascii")
|
||||
unquoted = urllib.parse.unquote(data)
|
||||
urls = unquoted.split('\r\n')
|
||||
urls = unquoted.split("\r\n")
|
||||
paths = [QUrl(url).toLocalFile() for url in urls if url]
|
||||
for path in paths:
|
||||
self.model.add_directory(path)
|
||||
@@ -129,7 +138,7 @@ class DirectoriesModel(TreeModel):
|
||||
return None
|
||||
|
||||
def mimeTypes(self):
|
||||
return ['text/uri-list']
|
||||
return ["text/uri-list"]
|
||||
|
||||
def setData(self, index, value, role):
|
||||
if not index.isValid() or role != Qt.EditRole or index.column() != 1:
|
||||
@@ -144,18 +153,20 @@ class DirectoriesModel(TreeModel):
|
||||
# work with ActionMove either. So screw that, and accept anything.
|
||||
return Qt.ActionMask
|
||||
|
||||
#--- Events
|
||||
# --- Events
|
||||
def selectionChanged(self, selected, deselected):
|
||||
newNodes = [modelIndex.internalPointer().ref for modelIndex in self.view.selectionModel().selectedRows()]
|
||||
newNodes = [
|
||||
modelIndex.internalPointer().ref
|
||||
for modelIndex in self.view.selectionModel().selectedRows()
|
||||
]
|
||||
self.model.selected_nodes = newNodes
|
||||
|
||||
#--- Signals
|
||||
# --- Signals
|
||||
foldersAdded = pyqtSignal(list)
|
||||
|
||||
#--- model --> view
|
||||
# --- model --> view
|
||||
def refresh(self):
|
||||
self.reset()
|
||||
|
||||
def refresh_states(self):
|
||||
self.refreshData()
|
||||
|
||||
|
||||
@@ -7,13 +7,20 @@
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QPushButton, QTableView, QAbstractItemView
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog,
|
||||
QVBoxLayout,
|
||||
QPushButton,
|
||||
QTableView,
|
||||
QAbstractItemView,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
from qtlib.util import horizontalWrap
|
||||
from .ignore_list_table import IgnoreListTable
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class IgnoreListDialog(QDialog):
|
||||
def __init__(self, parent, model, **kwargs):
|
||||
@@ -46,13 +53,11 @@ class IgnoreListDialog(QDialog):
|
||||
self.clearButton = QPushButton(tr("Clear"))
|
||||
self.closeButton = QPushButton(tr("Close"))
|
||||
self.verticalLayout.addLayout(
|
||||
horizontalWrap([
|
||||
self.removeSelectedButton, self.clearButton,
|
||||
None, self.closeButton
|
||||
])
|
||||
horizontalWrap(
|
||||
[self.removeSelectedButton, self.clearButton, None, self.closeButton]
|
||||
)
|
||||
)
|
||||
|
||||
#--- model --> view
|
||||
# --- model --> view
|
||||
def show(self):
|
||||
super().show()
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
# Created On: 2012-03-13
|
||||
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from qtlib.column import Column
|
||||
from qtlib.table import Table
|
||||
|
||||
|
||||
class IgnoreListTable(Table):
|
||||
COLUMNS = [
|
||||
Column('path1', defaultWidth=230),
|
||||
Column('path2', defaultWidth=230),
|
||||
Column("path1", defaultWidth=230),
|
||||
Column("path2", defaultWidth=230),
|
||||
]
|
||||
|
||||
@@ -11,7 +11,8 @@ from hscommon.trans import trget
|
||||
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
||||
from ..details_table import DetailsTable
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class DetailsDialog(DetailsDialogBase):
|
||||
def _setupUi(self):
|
||||
@@ -26,4 +27,3 @@ class DetailsDialog(DetailsDialogBase):
|
||||
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.tableView.setShowGrid(False)
|
||||
self.verticalLayout.addWidget(self.tableView)
|
||||
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
|
||||
from PyQt5.QtCore import QSize
|
||||
from PyQt5.QtWidgets import (
|
||||
QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QSizePolicy,
|
||||
QSpacerItem,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
@@ -15,7 +20,8 @@ from core.scanner import ScanType
|
||||
|
||||
from ..preferences_dialog import PreferencesDialogBase
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class PreferencesDialog(PreferencesDialogBase):
|
||||
def _setupPreferenceWidgets(self):
|
||||
@@ -33,33 +39,40 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self.horizontalLayout_2.setSpacing(0)
|
||||
spacerItem1 = QSpacerItem(15, 20, QSizePolicy.Fixed, QSizePolicy.Minimum)
|
||||
self.horizontalLayout_2.addItem(spacerItem1)
|
||||
self._setupAddCheckbox('tagTrackBox', tr("Track"), self.widget)
|
||||
self._setupAddCheckbox("tagTrackBox", tr("Track"), self.widget)
|
||||
self.horizontalLayout_2.addWidget(self.tagTrackBox)
|
||||
self._setupAddCheckbox('tagArtistBox', tr("Artist"), self.widget)
|
||||
self._setupAddCheckbox("tagArtistBox", tr("Artist"), self.widget)
|
||||
self.horizontalLayout_2.addWidget(self.tagArtistBox)
|
||||
self._setupAddCheckbox('tagAlbumBox', tr("Album"), self.widget)
|
||||
self._setupAddCheckbox("tagAlbumBox", tr("Album"), self.widget)
|
||||
self.horizontalLayout_2.addWidget(self.tagAlbumBox)
|
||||
self._setupAddCheckbox('tagTitleBox', tr("Title"), self.widget)
|
||||
self._setupAddCheckbox("tagTitleBox", tr("Title"), self.widget)
|
||||
self.horizontalLayout_2.addWidget(self.tagTitleBox)
|
||||
self._setupAddCheckbox('tagGenreBox', tr("Genre"), self.widget)
|
||||
self._setupAddCheckbox("tagGenreBox", tr("Genre"), self.widget)
|
||||
self.horizontalLayout_2.addWidget(self.tagGenreBox)
|
||||
self._setupAddCheckbox('tagYearBox', tr("Year"), self.widget)
|
||||
self._setupAddCheckbox("tagYearBox", tr("Year"), self.widget)
|
||||
self.horizontalLayout_2.addWidget(self.tagYearBox)
|
||||
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
|
||||
self.widgetsVLayout.addWidget(self.widget)
|
||||
self._setupAddCheckbox('wordWeightingBox', tr("Word weighting"))
|
||||
self._setupAddCheckbox("wordWeightingBox", tr("Word weighting"))
|
||||
self.widgetsVLayout.addWidget(self.wordWeightingBox)
|
||||
self._setupAddCheckbox('matchSimilarBox', tr("Match similar words"))
|
||||
self._setupAddCheckbox("matchSimilarBox", tr("Match similar words"))
|
||||
self.widgetsVLayout.addWidget(self.matchSimilarBox)
|
||||
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"))
|
||||
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"))
|
||||
self.widgetsVLayout.addWidget(self.mixFileKindBox)
|
||||
self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering"))
|
||||
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._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._setupAddCheckbox(
|
||||
"ignoreHardlinkMatches",
|
||||
tr("Ignore duplicates hardlinking to the same file"),
|
||||
)
|
||||
self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
|
||||
self._setupAddCheckbox('debugModeBox', tr("Debug mode (restart required)"))
|
||||
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
|
||||
self.widgetsVLayout.addWidget(self.debugModeBox)
|
||||
self._setupBottomPart()
|
||||
|
||||
@@ -76,8 +89,10 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
# Update UI state based on selected scan type
|
||||
scan_type = prefs.get_scan_type(AppMode.Music)
|
||||
word_based = scan_type in (
|
||||
ScanType.Filename, ScanType.Fields, ScanType.FieldsNoOrder,
|
||||
ScanType.Tag
|
||||
ScanType.Filename,
|
||||
ScanType.Fields,
|
||||
ScanType.FieldsNoOrder,
|
||||
ScanType.Tag,
|
||||
)
|
||||
tag_based = scan_type == ScanType.Tag
|
||||
self.filterHardnessSlider.setEnabled(word_based)
|
||||
@@ -99,4 +114,3 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
prefs.scan_tag_year = ischecked(self.tagYearBox)
|
||||
prefs.match_similar = ischecked(self.matchSimilarBox)
|
||||
prefs.word_weighting = ischecked(self.wordWeightingBox)
|
||||
|
||||
|
||||
@@ -7,26 +7,26 @@
|
||||
from qtlib.column import Column
|
||||
from ..results_model import ResultsModel as ResultsModelBase
|
||||
|
||||
|
||||
class ResultsModel(ResultsModelBase):
|
||||
COLUMNS = [
|
||||
Column('marked', defaultWidth=30),
|
||||
Column('name', defaultWidth=200),
|
||||
Column('folder_path', defaultWidth=180),
|
||||
Column('size', defaultWidth=60),
|
||||
Column('duration', defaultWidth=60),
|
||||
Column('bitrate', defaultWidth=50),
|
||||
Column('samplerate', defaultWidth=60),
|
||||
Column('extension', defaultWidth=40),
|
||||
Column('mtime', defaultWidth=120),
|
||||
Column('title', defaultWidth=120),
|
||||
Column('artist', defaultWidth=120),
|
||||
Column('album', defaultWidth=120),
|
||||
Column('genre', defaultWidth=80),
|
||||
Column('year', defaultWidth=40),
|
||||
Column('track', defaultWidth=40),
|
||||
Column('comment', defaultWidth=120),
|
||||
Column('percentage', defaultWidth=60),
|
||||
Column('words', defaultWidth=120),
|
||||
Column('dupe_count', defaultWidth=80),
|
||||
Column("marked", defaultWidth=30),
|
||||
Column("name", defaultWidth=200),
|
||||
Column("folder_path", defaultWidth=180),
|
||||
Column("size", defaultWidth=60),
|
||||
Column("duration", defaultWidth=60),
|
||||
Column("bitrate", defaultWidth=50),
|
||||
Column("samplerate", defaultWidth=60),
|
||||
Column("extension", defaultWidth=40),
|
||||
Column("mtime", defaultWidth=120),
|
||||
Column("title", defaultWidth=120),
|
||||
Column("artist", defaultWidth=120),
|
||||
Column("album", defaultWidth=120),
|
||||
Column("genre", defaultWidth=80),
|
||||
Column("year", defaultWidth=40),
|
||||
Column("track", defaultWidth=40),
|
||||
Column("comment", defaultWidth=120),
|
||||
Column("percentage", defaultWidth=60),
|
||||
Column("words", defaultWidth=120),
|
||||
Column("dupe_count", defaultWidth=80),
|
||||
]
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from ._block_qt import getblocks # NOQA
|
||||
from ._block_qt import getblocks # NOQA
|
||||
|
||||
# Converted to C
|
||||
# def getblock(image):
|
||||
|
||||
@@ -6,13 +6,20 @@
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QAbstractItemView, QHBoxLayout, QLabel, QSizePolicy
|
||||
from PyQt5.QtWidgets import (
|
||||
QVBoxLayout,
|
||||
QAbstractItemView,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QSizePolicy,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
||||
from ..details_table import DetailsTable
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class DetailsDialog(DetailsDialogBase):
|
||||
def __init__(self, parent, app):
|
||||
@@ -33,7 +40,9 @@ class DetailsDialog(DetailsDialogBase):
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.selectedImage.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.selectedImage.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.selectedImage.setSizePolicy(sizePolicy)
|
||||
self.selectedImage.setScaledContents(False)
|
||||
self.selectedImage.setAlignment(Qt.AlignCenter)
|
||||
@@ -42,7 +51,9 @@ class DetailsDialog(DetailsDialogBase):
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.referenceImage.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.referenceImage.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.referenceImage.setSizePolicy(sizePolicy)
|
||||
self.referenceImage.setAlignment(Qt.AlignCenter)
|
||||
self.horizontalLayout.addWidget(self.referenceImage)
|
||||
@@ -77,18 +88,22 @@ class DetailsDialog(DetailsDialogBase):
|
||||
def _updateImages(self):
|
||||
if self.selectedPixmap is not None:
|
||||
target_size = self.selectedImage.size()
|
||||
scaledPixmap = self.selectedPixmap.scaled(target_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
scaledPixmap = self.selectedPixmap.scaled(
|
||||
target_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
|
||||
)
|
||||
self.selectedImage.setPixmap(scaledPixmap)
|
||||
else:
|
||||
self.selectedImage.setPixmap(QPixmap())
|
||||
if self.referencePixmap is not None:
|
||||
target_size = self.referenceImage.size()
|
||||
scaledPixmap = self.referencePixmap.scaled(target_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
scaledPixmap = self.referencePixmap.scaled(
|
||||
target_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
|
||||
)
|
||||
self.referenceImage.setPixmap(scaledPixmap)
|
||||
else:
|
||||
self.referenceImage.setPixmap(QPixmap())
|
||||
|
||||
#--- Override
|
||||
# --- Override
|
||||
def resizeEvent(self, event):
|
||||
self._updateImages()
|
||||
|
||||
@@ -101,4 +116,3 @@ class DetailsDialog(DetailsDialogBase):
|
||||
DetailsDialogBase.refresh(self)
|
||||
if self.isVisible():
|
||||
self._update()
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from core.pe.photo import Photo as PhotoBase
|
||||
|
||||
from .block import getblocks
|
||||
|
||||
|
||||
class File(PhotoBase):
|
||||
def _plat_get_dimensions(self):
|
||||
try:
|
||||
@@ -53,4 +54,3 @@ class File(PhotoBase):
|
||||
t.rotate(270)
|
||||
image = image.transformed(t)
|
||||
return getblocks(image, block_count_per_side)
|
||||
|
||||
|
||||
@@ -12,23 +12,33 @@ from core.app import AppMode
|
||||
|
||||
from ..preferences_dialog import PreferencesDialogBase
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class PreferencesDialog(PreferencesDialogBase):
|
||||
def _setupPreferenceWidgets(self):
|
||||
self._setupFilterHardnessBox()
|
||||
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
|
||||
self._setupAddCheckbox('matchScaledBox', tr("Match pictures of different dimensions"))
|
||||
self._setupAddCheckbox(
|
||||
"matchScaledBox", tr("Match pictures of different dimensions")
|
||||
)
|
||||
self.widgetsVLayout.addWidget(self.matchScaledBox)
|
||||
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"))
|
||||
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"))
|
||||
self.widgetsVLayout.addWidget(self.mixFileKindBox)
|
||||
self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering"))
|
||||
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._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._setupAddCheckbox(
|
||||
"ignoreHardlinkMatches",
|
||||
tr("Ignore duplicates hardlinking to the same file"),
|
||||
)
|
||||
self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
|
||||
self._setupAddCheckbox('debugModeBox', tr("Debug mode (restart required)"))
|
||||
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
|
||||
self.widgetsVLayout.addWidget(self.debugModeBox)
|
||||
self.widgetsVLayout.addWidget(QLabel(tr("Picture cache mode:")))
|
||||
self.cacheTypeRadio = RadioBox(self, items=["Sqlite", "Shelve"], spread=False)
|
||||
@@ -37,7 +47,9 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
|
||||
def _load(self, prefs, setchecked):
|
||||
setchecked(self.matchScaledBox, prefs.match_scaled)
|
||||
self.cacheTypeRadio.selected_index = 1 if prefs.picture_cache_type == 'shelve' else 0
|
||||
self.cacheTypeRadio.selected_index = (
|
||||
1 if prefs.picture_cache_type == "shelve" else 0
|
||||
)
|
||||
|
||||
# Update UI state based on selected scan type
|
||||
scan_type = prefs.get_scan_type(AppMode.Picture)
|
||||
@@ -46,5 +58,6 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
|
||||
def _save(self, prefs, ischecked):
|
||||
prefs.match_scaled = ischecked(self.matchScaledBox)
|
||||
prefs.picture_cache_type = 'shelve' if self.cacheTypeRadio.selected_index == 1 else 'sqlite'
|
||||
|
||||
prefs.picture_cache_type = (
|
||||
"shelve" if self.cacheTypeRadio.selected_index == 1 else "sqlite"
|
||||
)
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
from qtlib.column import Column
|
||||
from ..results_model import ResultsModel as ResultsModelBase
|
||||
|
||||
|
||||
class ResultsModel(ResultsModelBase):
|
||||
COLUMNS = [
|
||||
Column('marked', defaultWidth=30),
|
||||
Column('name', defaultWidth=200),
|
||||
Column('folder_path', defaultWidth=180),
|
||||
Column('size', defaultWidth=60),
|
||||
Column('extension', defaultWidth=40),
|
||||
Column('dimensions', defaultWidth=100),
|
||||
Column('exif_timestamp', defaultWidth=120),
|
||||
Column('mtime', defaultWidth=120),
|
||||
Column('percentage', defaultWidth=60),
|
||||
Column('dupe_count', defaultWidth=80),
|
||||
Column("marked", defaultWidth=30),
|
||||
Column("name", defaultWidth=200),
|
||||
Column("folder_path", defaultWidth=180),
|
||||
Column("size", defaultWidth=60),
|
||||
Column("extension", defaultWidth=40),
|
||||
Column("dimensions", defaultWidth=100),
|
||||
Column("exif_timestamp", defaultWidth=120),
|
||||
Column("mtime", defaultWidth=120),
|
||||
Column("percentage", defaultWidth=60),
|
||||
Column("dupe_count", defaultWidth=80),
|
||||
]
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@ from hscommon.plat import ISWINDOWS, ISOSX, ISLINUX
|
||||
if op.exists(__file__):
|
||||
# We want to get the absolute path or our root folder. We know that in that folder we're
|
||||
# inside qt/, so we just go back one level.
|
||||
BASE_PATH = op.abspath(op.join(op.dirname(__file__), '..'))
|
||||
BASE_PATH = op.abspath(op.join(op.dirname(__file__), ".."))
|
||||
else:
|
||||
# We're under a freezed environment. Our base path is ''.
|
||||
BASE_PATH = ''
|
||||
HELP_PATH = op.join(BASE_PATH, 'help')
|
||||
BASE_PATH = ""
|
||||
HELP_PATH = op.join(BASE_PATH, "help")
|
||||
|
||||
if ISWINDOWS:
|
||||
INITIAL_FOLDER_IN_DIALOGS = 'C:\\'
|
||||
INITIAL_FOLDER_IN_DIALOGS = "C:\\"
|
||||
elif ISOSX:
|
||||
INITIAL_FOLDER_IN_DIALOGS = '/'
|
||||
INITIAL_FOLDER_IN_DIALOGS = "/"
|
||||
elif ISLINUX:
|
||||
INITIAL_FOLDER_IN_DIALOGS = '/'
|
||||
INITIAL_FOLDER_IN_DIALOGS = "/"
|
||||
else:
|
||||
# unsupported platform, however '/' is a good guess for a path which is available
|
||||
INITIAL_FOLDER_IN_DIALOGS = '/'
|
||||
INITIAL_FOLDER_IN_DIALOGS = "/"
|
||||
|
||||
@@ -11,40 +11,47 @@ from core.app import AppMode
|
||||
from core.scanner import ScanType
|
||||
from qtlib.preferences import Preferences as PreferencesBase
|
||||
|
||||
|
||||
class Preferences(PreferencesBase):
|
||||
def _load_values(self, settings):
|
||||
get = self.get_value
|
||||
self.filter_hardness = get('FilterHardness', self.filter_hardness)
|
||||
self.mix_file_kind = get('MixFileKind', self.mix_file_kind)
|
||||
self.ignore_hardlink_matches = get('IgnoreHardlinkMatches', self.ignore_hardlink_matches)
|
||||
self.use_regexp = get('UseRegexp', self.use_regexp)
|
||||
self.remove_empty_folders = get('RemoveEmptyFolders', self.remove_empty_folders)
|
||||
self.debug_mode = get('DebugMode', self.debug_mode)
|
||||
self.destination_type = get('DestinationType', self.destination_type)
|
||||
self.custom_command = get('CustomCommand', self.custom_command)
|
||||
self.language = get('Language', self.language)
|
||||
self.filter_hardness = get("FilterHardness", self.filter_hardness)
|
||||
self.mix_file_kind = get("MixFileKind", self.mix_file_kind)
|
||||
self.ignore_hardlink_matches = get(
|
||||
"IgnoreHardlinkMatches", self.ignore_hardlink_matches
|
||||
)
|
||||
self.use_regexp = get("UseRegexp", self.use_regexp)
|
||||
self.remove_empty_folders = get("RemoveEmptyFolders", self.remove_empty_folders)
|
||||
self.debug_mode = get("DebugMode", self.debug_mode)
|
||||
self.destination_type = get("DestinationType", self.destination_type)
|
||||
self.custom_command = get("CustomCommand", self.custom_command)
|
||||
self.language = get("Language", self.language)
|
||||
if not self.language and trans.installed_lang:
|
||||
self.language = trans.installed_lang
|
||||
|
||||
self.tableFontSize = get('TableFontSize', self.tableFontSize)
|
||||
self.resultWindowIsMaximized = get('ResultWindowIsMaximized', self.resultWindowIsMaximized)
|
||||
self.resultWindowRect = self.get_rect('ResultWindowRect', self.resultWindowRect)
|
||||
self.directoriesWindowRect = self.get_rect('DirectoriesWindowRect', self.directoriesWindowRect)
|
||||
self.recentResults = get('RecentResults', self.recentResults)
|
||||
self.recentFolders = get('RecentFolders', self.recentFolders)
|
||||
self.tableFontSize = get("TableFontSize", self.tableFontSize)
|
||||
self.resultWindowIsMaximized = get(
|
||||
"ResultWindowIsMaximized", self.resultWindowIsMaximized
|
||||
)
|
||||
self.resultWindowRect = self.get_rect("ResultWindowRect", self.resultWindowRect)
|
||||
self.directoriesWindowRect = self.get_rect(
|
||||
"DirectoriesWindowRect", self.directoriesWindowRect
|
||||
)
|
||||
self.recentResults = get("RecentResults", self.recentResults)
|
||||
self.recentFolders = get("RecentFolders", self.recentFolders)
|
||||
|
||||
self.word_weighting = get('WordWeighting', self.word_weighting)
|
||||
self.match_similar = get('MatchSimilar', self.match_similar)
|
||||
self.ignore_small_files = get('IgnoreSmallFiles', self.ignore_small_files)
|
||||
self.small_file_threshold = get('SmallFileThreshold', self.small_file_threshold)
|
||||
self.scan_tag_track = get('ScanTagTrack', self.scan_tag_track)
|
||||
self.scan_tag_artist = get('ScanTagArtist', self.scan_tag_artist)
|
||||
self.scan_tag_album = get('ScanTagAlbum', self.scan_tag_album)
|
||||
self.scan_tag_title = get('ScanTagTitle', self.scan_tag_title)
|
||||
self.scan_tag_genre = get('ScanTagGenre', self.scan_tag_genre)
|
||||
self.scan_tag_year = get('ScanTagYear', self.scan_tag_year)
|
||||
self.match_scaled = get('MatchScaled', self.match_scaled)
|
||||
self.picture_cache_type = get('PictureCacheType', self.picture_cache_type)
|
||||
self.word_weighting = get("WordWeighting", self.word_weighting)
|
||||
self.match_similar = get("MatchSimilar", self.match_similar)
|
||||
self.ignore_small_files = get("IgnoreSmallFiles", self.ignore_small_files)
|
||||
self.small_file_threshold = get("SmallFileThreshold", self.small_file_threshold)
|
||||
self.scan_tag_track = get("ScanTagTrack", self.scan_tag_track)
|
||||
self.scan_tag_artist = get("ScanTagArtist", self.scan_tag_artist)
|
||||
self.scan_tag_album = get("ScanTagAlbum", self.scan_tag_album)
|
||||
self.scan_tag_title = get("ScanTagTitle", self.scan_tag_title)
|
||||
self.scan_tag_genre = get("ScanTagGenre", self.scan_tag_genre)
|
||||
self.scan_tag_year = get("ScanTagYear", self.scan_tag_year)
|
||||
self.match_scaled = get("MatchScaled", self.match_scaled)
|
||||
self.picture_cache_type = get("PictureCacheType", self.picture_cache_type)
|
||||
|
||||
def reset(self):
|
||||
self.filter_hardness = 95
|
||||
@@ -54,8 +61,8 @@ class Preferences(PreferencesBase):
|
||||
self.remove_empty_folders = False
|
||||
self.debug_mode = False
|
||||
self.destination_type = 1
|
||||
self.custom_command = ''
|
||||
self.language = trans.installed_lang if trans.installed_lang else ''
|
||||
self.custom_command = ""
|
||||
self.language = trans.installed_lang if trans.installed_lang else ""
|
||||
|
||||
self.tableFontSize = QApplication.font().pointSize()
|
||||
self.resultWindowIsMaximized = False
|
||||
@@ -67,7 +74,7 @@ class Preferences(PreferencesBase):
|
||||
self.word_weighting = True
|
||||
self.match_similar = False
|
||||
self.ignore_small_files = True
|
||||
self.small_file_threshold = 10 # KB
|
||||
self.small_file_threshold = 10 # KB
|
||||
self.scan_tag_track = False
|
||||
self.scan_tag_artist = True
|
||||
self.scan_tag_album = True
|
||||
@@ -75,53 +82,53 @@ class Preferences(PreferencesBase):
|
||||
self.scan_tag_genre = False
|
||||
self.scan_tag_year = False
|
||||
self.match_scaled = False
|
||||
self.picture_cache_type = 'sqlite'
|
||||
self.picture_cache_type = "sqlite"
|
||||
|
||||
def _save_values(self, settings):
|
||||
set_ = self.set_value
|
||||
set_('FilterHardness', self.filter_hardness)
|
||||
set_('MixFileKind', self.mix_file_kind)
|
||||
set_('IgnoreHardlinkMatches', self.ignore_hardlink_matches)
|
||||
set_('UseRegexp', self.use_regexp)
|
||||
set_('RemoveEmptyFolders', self.remove_empty_folders)
|
||||
set_('DebugMode', self.debug_mode)
|
||||
set_('DestinationType', self.destination_type)
|
||||
set_('CustomCommand', self.custom_command)
|
||||
set_('Language', self.language)
|
||||
set_("FilterHardness", self.filter_hardness)
|
||||
set_("MixFileKind", self.mix_file_kind)
|
||||
set_("IgnoreHardlinkMatches", self.ignore_hardlink_matches)
|
||||
set_("UseRegexp", self.use_regexp)
|
||||
set_("RemoveEmptyFolders", self.remove_empty_folders)
|
||||
set_("DebugMode", self.debug_mode)
|
||||
set_("DestinationType", self.destination_type)
|
||||
set_("CustomCommand", self.custom_command)
|
||||
set_("Language", self.language)
|
||||
|
||||
set_('TableFontSize', self.tableFontSize)
|
||||
set_('ResultWindowIsMaximized', self.resultWindowIsMaximized)
|
||||
self.set_rect('ResultWindowRect', self.resultWindowRect)
|
||||
self.set_rect('DirectoriesWindowRect', self.directoriesWindowRect)
|
||||
set_('RecentResults', self.recentResults)
|
||||
set_('RecentFolders', self.recentFolders)
|
||||
set_("TableFontSize", self.tableFontSize)
|
||||
set_("ResultWindowIsMaximized", self.resultWindowIsMaximized)
|
||||
self.set_rect("ResultWindowRect", self.resultWindowRect)
|
||||
self.set_rect("DirectoriesWindowRect", self.directoriesWindowRect)
|
||||
set_("RecentResults", self.recentResults)
|
||||
set_("RecentFolders", self.recentFolders)
|
||||
|
||||
set_('WordWeighting', self.word_weighting)
|
||||
set_('MatchSimilar', self.match_similar)
|
||||
set_('IgnoreSmallFiles', self.ignore_small_files)
|
||||
set_('SmallFileThreshold', self.small_file_threshold)
|
||||
set_('ScanTagTrack', self.scan_tag_track)
|
||||
set_('ScanTagArtist', self.scan_tag_artist)
|
||||
set_('ScanTagAlbum', self.scan_tag_album)
|
||||
set_('ScanTagTitle', self.scan_tag_title)
|
||||
set_('ScanTagGenre', self.scan_tag_genre)
|
||||
set_('ScanTagYear', self.scan_tag_year)
|
||||
set_('MatchScaled', self.match_scaled)
|
||||
set_('PictureCacheType', self.picture_cache_type)
|
||||
set_("WordWeighting", self.word_weighting)
|
||||
set_("MatchSimilar", self.match_similar)
|
||||
set_("IgnoreSmallFiles", self.ignore_small_files)
|
||||
set_("SmallFileThreshold", self.small_file_threshold)
|
||||
set_("ScanTagTrack", self.scan_tag_track)
|
||||
set_("ScanTagArtist", self.scan_tag_artist)
|
||||
set_("ScanTagAlbum", self.scan_tag_album)
|
||||
set_("ScanTagTitle", self.scan_tag_title)
|
||||
set_("ScanTagGenre", self.scan_tag_genre)
|
||||
set_("ScanTagYear", self.scan_tag_year)
|
||||
set_("MatchScaled", self.match_scaled)
|
||||
set_("PictureCacheType", self.picture_cache_type)
|
||||
|
||||
# scan_type is special because we save it immediately when we set it.
|
||||
def get_scan_type(self, app_mode):
|
||||
if app_mode == AppMode.Picture:
|
||||
return self.get_value('ScanTypePicture', ScanType.FuzzyBlock)
|
||||
return self.get_value("ScanTypePicture", ScanType.FuzzyBlock)
|
||||
elif app_mode == AppMode.Music:
|
||||
return self.get_value('ScanTypeMusic', ScanType.Tag)
|
||||
return self.get_value("ScanTypeMusic", ScanType.Tag)
|
||||
else:
|
||||
return self.get_value('ScanTypeStandard', ScanType.Contents)
|
||||
return self.get_value("ScanTypeStandard", ScanType.Contents)
|
||||
|
||||
def set_scan_type(self, app_mode, value):
|
||||
if app_mode == AppMode.Picture:
|
||||
self.set_value('ScanTypePicture', value)
|
||||
self.set_value("ScanTypePicture", value)
|
||||
elif app_mode == AppMode.Music:
|
||||
self.set_value('ScanTypeMusic', value)
|
||||
self.set_value("ScanTypeMusic", value)
|
||||
else:
|
||||
self.set_value('ScanTypeStandard', value)
|
||||
self.set_value("ScanTypeStandard", value)
|
||||
|
||||
@@ -6,8 +6,20 @@
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox,
|
||||
QSlider, QSizePolicy, QSpacerItem, QCheckBox, QLineEdit, QMessageBox, QSpinBox, QLayout
|
||||
QDialog,
|
||||
QDialogButtonBox,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QComboBox,
|
||||
QSlider,
|
||||
QSizePolicy,
|
||||
QSpacerItem,
|
||||
QCheckBox,
|
||||
QLineEdit,
|
||||
QMessageBox,
|
||||
QSpinBox,
|
||||
QLayout,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
@@ -16,23 +28,42 @@ from qtlib.preferences import get_langnames
|
||||
|
||||
from .preferences import Preferences
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
SUPPORTED_LANGUAGES = [
|
||||
'en', 'fr', 'de', 'el', 'zh_CN', 'cs', 'it', 'hy', 'ru', 'uk', 'pt_BR', 'vi', 'pl_PL', 'ko', 'es',
|
||||
'nl',
|
||||
"en",
|
||||
"fr",
|
||||
"de",
|
||||
"el",
|
||||
"zh_CN",
|
||||
"cs",
|
||||
"it",
|
||||
"hy",
|
||||
"ru",
|
||||
"uk",
|
||||
"pt_BR",
|
||||
"vi",
|
||||
"pl_PL",
|
||||
"ko",
|
||||
"es",
|
||||
"nl",
|
||||
]
|
||||
|
||||
|
||||
class PreferencesDialogBase(QDialog):
|
||||
def __init__(self, parent, app, **kwargs):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
super().__init__(parent, flags, **kwargs)
|
||||
self.app = app
|
||||
all_languages = get_langnames()
|
||||
self.supportedLanguages = sorted(SUPPORTED_LANGUAGES, key=lambda lang: all_languages[lang])
|
||||
self.supportedLanguages = sorted(
|
||||
SUPPORTED_LANGUAGES, key=lambda lang: all_languages[lang]
|
||||
)
|
||||
self._setupUi()
|
||||
|
||||
self.filterHardnessSlider.valueChanged['int'].connect(self.filterHardnessLabel.setNum)
|
||||
self.filterHardnessSlider.valueChanged["int"].connect(
|
||||
self.filterHardnessLabel.setNum
|
||||
)
|
||||
self.buttonBox.clicked.connect(self.buttonClicked)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.buttonBox.rejected.connect(self.reject)
|
||||
@@ -51,7 +82,9 @@ class PreferencesDialogBase(QDialog):
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.filterHardnessSlider.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.filterHardnessSlider.setSizePolicy(sizePolicy)
|
||||
self.filterHardnessSlider.setMinimum(1)
|
||||
self.filterHardnessSlider.setMaximum(100)
|
||||
@@ -81,12 +114,16 @@ class PreferencesDialogBase(QDialog):
|
||||
self.fontSizeLabel = QLabel(tr("Font size:"))
|
||||
self.fontSizeSpinBox = QSpinBox()
|
||||
self.fontSizeSpinBox.setMinimum(5)
|
||||
self.widgetsVLayout.addLayout(horizontalWrap([self.fontSizeLabel, self.fontSizeSpinBox, None]))
|
||||
self.widgetsVLayout.addLayout(
|
||||
horizontalWrap([self.fontSizeLabel, self.fontSizeSpinBox, None])
|
||||
)
|
||||
self.languageLabel = QLabel(tr("Language:"), self)
|
||||
self.languageComboBox = QComboBox(self)
|
||||
for lang in self.supportedLanguages:
|
||||
self.languageComboBox.addItem(get_langnames()[lang])
|
||||
self.widgetsVLayout.addLayout(horizontalWrap([self.languageLabel, self.languageComboBox, None]))
|
||||
self.widgetsVLayout.addLayout(
|
||||
horizontalWrap([self.languageLabel, self.languageComboBox, None])
|
||||
)
|
||||
self.copyMoveLabel = QLabel(self)
|
||||
self.copyMoveLabel.setText(tr("Copy and Move:"))
|
||||
self.widgetsVLayout.addWidget(self.copyMoveLabel)
|
||||
@@ -96,7 +133,9 @@ class PreferencesDialogBase(QDialog):
|
||||
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.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)
|
||||
@@ -121,7 +160,11 @@ class PreferencesDialogBase(QDialog):
|
||||
self._setupPreferenceWidgets()
|
||||
self.mainVLayout.addLayout(self.widgetsVLayout)
|
||||
self.buttonBox = QDialogButtonBox(self)
|
||||
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults)
|
||||
self.buttonBox.setStandardButtons(
|
||||
QDialogButtonBox.Cancel
|
||||
| QDialogButtonBox.Ok
|
||||
| QDialogButtonBox.RestoreDefaults
|
||||
)
|
||||
self.mainVLayout.addWidget(self.buttonBox)
|
||||
self.layout().setSizeConstraint(QLayout.SetFixedSize)
|
||||
|
||||
@@ -169,18 +212,21 @@ class PreferencesDialogBase(QDialog):
|
||||
lang = self.supportedLanguages[self.languageComboBox.currentIndex()]
|
||||
oldlang = self.app.prefs.language
|
||||
if oldlang not in self.supportedLanguages:
|
||||
oldlang = 'en'
|
||||
oldlang = "en"
|
||||
if lang != oldlang:
|
||||
QMessageBox.information(self, "", tr("dupeGuru has to restart for language changes to take effect."))
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"",
|
||||
tr("dupeGuru has to restart for language changes to take effect."),
|
||||
)
|
||||
self.app.prefs.language = lang
|
||||
self._save(prefs, ischecked)
|
||||
|
||||
def resetToDefaults(self):
|
||||
self.load(Preferences())
|
||||
|
||||
#--- Events
|
||||
# --- Events
|
||||
def buttonClicked(self, button):
|
||||
role = self.buttonBox.buttonRole(button)
|
||||
if role == QDialogButtonBox.ResetRole:
|
||||
self.resetToDefaults()
|
||||
|
||||
|
||||
@@ -8,8 +8,19 @@
|
||||
|
||||
from PyQt5.QtCore import Qt, QMimeData, QByteArray
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QComboBox, QListView,
|
||||
QDialogButtonBox, QAbstractItemView, QLabel, QStyle, QSplitter, QWidget, QSizePolicy
|
||||
QDialog,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QPushButton,
|
||||
QComboBox,
|
||||
QListView,
|
||||
QDialogButtonBox,
|
||||
QAbstractItemView,
|
||||
QLabel,
|
||||
QStyle,
|
||||
QSplitter,
|
||||
QWidget,
|
||||
QSizePolicy,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
@@ -17,9 +28,10 @@ from qtlib.selectable_list import ComboboxModel, ListviewModel
|
||||
from qtlib.util import verticalSpacer
|
||||
from core.gui.prioritize_dialog import PrioritizeDialog as PrioritizeDialogModel
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
MIME_INDEXES = "application/dupeguru.rowindexes"
|
||||
|
||||
MIME_INDEXES = 'application/dupeguru.rowindexes'
|
||||
|
||||
class PrioritizationList(ListviewModel):
|
||||
def flags(self, index):
|
||||
@@ -27,7 +39,7 @@ class PrioritizationList(ListviewModel):
|
||||
return Qt.ItemIsEnabled | Qt.ItemIsDropEnabled
|
||||
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled
|
||||
|
||||
#--- Drag & Drop
|
||||
# --- Drag & Drop
|
||||
def dropMimeData(self, mimeData, action, row, column, parentIndex):
|
||||
if not mimeData.hasFormat(MIME_INDEXES):
|
||||
return False
|
||||
@@ -36,13 +48,13 @@ class PrioritizationList(ListviewModel):
|
||||
if parentIndex.isValid():
|
||||
return False
|
||||
strMimeData = bytes(mimeData.data(MIME_INDEXES)).decode()
|
||||
indexes = list(map(int, strMimeData.split(',')))
|
||||
indexes = list(map(int, strMimeData.split(",")))
|
||||
self.model.move_indexes(indexes, row)
|
||||
return True
|
||||
|
||||
def mimeData(self, indexes):
|
||||
rows = {str(index.row()) for index in indexes}
|
||||
data = ','.join(rows)
|
||||
data = ",".join(rows)
|
||||
mimeData = QMimeData()
|
||||
mimeData.setData(MIME_INDEXES, QByteArray(data.encode()))
|
||||
return mimeData
|
||||
@@ -53,14 +65,19 @@ class PrioritizationList(ListviewModel):
|
||||
def supportedDropActions(self):
|
||||
return Qt.MoveAction
|
||||
|
||||
|
||||
class PrioritizeDialog(QDialog):
|
||||
def __init__(self, parent, app, **kwargs):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
super().__init__(parent, flags, **kwargs)
|
||||
self._setupUi()
|
||||
self.model = PrioritizeDialogModel(app=app.model)
|
||||
self.categoryList = ComboboxModel(model=self.model.category_list, view=self.categoryCombobox)
|
||||
self.criteriaList = ListviewModel(model=self.model.criteria_list, view=self.criteriaListView)
|
||||
self.categoryList = ComboboxModel(
|
||||
model=self.model.category_list, view=self.categoryCombobox
|
||||
)
|
||||
self.criteriaList = ListviewModel(
|
||||
model=self.model.criteria_list, view=self.criteriaListView
|
||||
)
|
||||
self.prioritizationList = PrioritizationList(
|
||||
model=self.model.prioritization_list, view=self.prioritizationListView
|
||||
)
|
||||
@@ -75,7 +92,7 @@ class PrioritizeDialog(QDialog):
|
||||
self.setWindowTitle(tr("Re-Prioritize duplicates"))
|
||||
self.resize(700, 400)
|
||||
|
||||
#widgets
|
||||
# widgets
|
||||
msg = tr(
|
||||
"Add criteria to the right box and click OK to send the dupes that correspond the "
|
||||
"best to these criteria to their respective group's "
|
||||
@@ -85,15 +102,19 @@ class PrioritizeDialog(QDialog):
|
||||
self.promptLabel.setWordWrap(True)
|
||||
self.categoryCombobox = QComboBox()
|
||||
self.criteriaListView = QListView()
|
||||
self.addCriteriaButton = QPushButton(self.style().standardIcon(QStyle.SP_ArrowRight), "")
|
||||
self.removeCriteriaButton = QPushButton(self.style().standardIcon(QStyle.SP_ArrowLeft), "")
|
||||
self.addCriteriaButton = QPushButton(
|
||||
self.style().standardIcon(QStyle.SP_ArrowRight), ""
|
||||
)
|
||||
self.removeCriteriaButton = QPushButton(
|
||||
self.style().standardIcon(QStyle.SP_ArrowLeft), ""
|
||||
)
|
||||
self.prioritizationListView = QListView()
|
||||
self.prioritizationListView.setAcceptDrops(True)
|
||||
self.prioritizationListView.setDragEnabled(True)
|
||||
self.prioritizationListView.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
self.prioritizationListView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.buttonBox = QDialogButtonBox()
|
||||
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
|
||||
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
|
||||
|
||||
# layout
|
||||
self.mainLayout = QVBoxLayout(self)
|
||||
|
||||
@@ -8,14 +8,22 @@
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QSpacerItem, QSizePolicy,
|
||||
QLabel, QTableView, QAbstractItemView
|
||||
QDialog,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QPushButton,
|
||||
QSpacerItem,
|
||||
QSizePolicy,
|
||||
QLabel,
|
||||
QTableView,
|
||||
QAbstractItemView,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
from .problem_table import ProblemTable
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class ProblemDialog(QDialog):
|
||||
def __init__(self, parent, model, **kwargs):
|
||||
@@ -62,4 +70,3 @@ class ProblemDialog(QDialog):
|
||||
self.closeButton.setDefault(True)
|
||||
self.horizontalLayout.addWidget(self.closeButton)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2010-04-12
|
||||
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from qtlib.column import Column
|
||||
from qtlib.table import Table
|
||||
|
||||
|
||||
class ProblemTable(Table):
|
||||
COLUMNS = [
|
||||
Column('path', defaultWidth=150),
|
||||
Column('msg', defaultWidth=150),
|
||||
Column("path", defaultWidth=150),
|
||||
Column("msg", defaultWidth=150),
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, model, view, **kwargs):
|
||||
super().__init__(model, view, **kwargs)
|
||||
# we have to prevent Return from initiating editing.
|
||||
# self.view.editSelected = lambda: None
|
||||
|
||||
@@ -8,9 +8,19 @@
|
||||
|
||||
from PyQt5.QtCore import Qt, QRect
|
||||
from PyQt5.QtWidgets import (
|
||||
QMainWindow, QMenu, QLabel, QFileDialog, QMenuBar, QWidget,
|
||||
QVBoxLayout, QAbstractItemView, QStatusBar, QDialog, QPushButton, QCheckBox,
|
||||
QDesktopWidget
|
||||
QMainWindow,
|
||||
QMenu,
|
||||
QLabel,
|
||||
QFileDialog,
|
||||
QMenuBar,
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QAbstractItemView,
|
||||
QStatusBar,
|
||||
QDialog,
|
||||
QPushButton,
|
||||
QCheckBox,
|
||||
QDesktopWidget,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
@@ -25,7 +35,8 @@ from .se.results_model import ResultsModel as ResultsModelStandard
|
||||
from .me.results_model import ResultsModel as ResultsModelMusic
|
||||
from .pe.results_model import ResultsModel as ResultsModelPicture
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class ResultWindow(QMainWindow):
|
||||
def __init__(self, parent, app, **kwargs):
|
||||
@@ -54,41 +65,143 @@ class ResultWindow(QMainWindow):
|
||||
def _setupActions(self):
|
||||
# (name, shortcut, icon, desc, func)
|
||||
ACTIONS = [
|
||||
('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),
|
||||
('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),
|
||||
('actionReprioritize', '', '', tr("Re-Prioritize Results..."), self.reprioritizeTriggered),
|
||||
("actionDetails", "Ctrl+I", "", tr("Details"), self.detailsTriggered),
|
||||
("actionActions", "", "", tr("Actions"), self.actionsTriggered),
|
||||
(
|
||||
'actionRemoveSelected', 'Ctrl+Del', '',
|
||||
tr("Remove Selected from Results"), self.removeSelectedTriggered
|
||||
"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,
|
||||
),
|
||||
(
|
||||
'actionIgnoreSelected', 'Ctrl+Shift+Del', '',
|
||||
tr("Add Selected to Ignore List"), self.addToIgnoreListTriggered
|
||||
"actionMoveMarked",
|
||||
"Ctrl+M",
|
||||
"",
|
||||
tr("Move Marked to..."),
|
||||
self.moveTriggered,
|
||||
),
|
||||
(
|
||||
'actionMakeSelectedReference', 'Ctrl+Space', '',
|
||||
tr("Make Selected into Reference"), self.app.model.make_selected_reference
|
||||
"actionCopyMarked",
|
||||
"Ctrl+Shift+M",
|
||||
"",
|
||||
tr("Copy Marked to..."),
|
||||
self.copyTriggered,
|
||||
),
|
||||
('actionOpenSelected', 'Ctrl+O', '', tr("Open Selected with Default Application"), self.openTriggered),
|
||||
(
|
||||
'actionRevealSelected', 'Ctrl+Shift+O', '',
|
||||
tr("Open Containing Folder of Selected"), self.revealTriggered
|
||||
"actionRemoveMarked",
|
||||
"Ctrl+R",
|
||||
"",
|
||||
tr("Remove Marked from Results"),
|
||||
self.removeMarkedTriggered,
|
||||
),
|
||||
(
|
||||
"actionReprioritize",
|
||||
"",
|
||||
"",
|
||||
tr("Re-Prioritize Results..."),
|
||||
self.reprioritizeTriggered,
|
||||
),
|
||||
(
|
||||
"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 into Reference"),
|
||||
self.app.model.make_selected_reference,
|
||||
),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
(
|
||||
"actionExportToHTML",
|
||||
"",
|
||||
"",
|
||||
tr("Export To HTML"),
|
||||
self.app.model.export_to_xhtml,
|
||||
),
|
||||
(
|
||||
"actionExportToCSV",
|
||||
"",
|
||||
"",
|
||||
tr("Export To CSV"),
|
||||
self.app.model.export_to_csv,
|
||||
),
|
||||
(
|
||||
"actionSaveResults",
|
||||
"Ctrl+S",
|
||||
"",
|
||||
tr("Save Results..."),
|
||||
self.saveResultsTriggered,
|
||||
),
|
||||
(
|
||||
"actionInvokeCustomCommand",
|
||||
"Ctrl+Alt+I",
|
||||
"",
|
||||
tr("Invoke Custom Command"),
|
||||
self.app.invokeCustomCommand,
|
||||
),
|
||||
('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),
|
||||
('actionExportToHTML', '', '', tr("Export To HTML"), self.app.model.export_to_xhtml),
|
||||
('actionExportToCSV', '', '', tr("Export To CSV"), self.app.model.export_to_csv),
|
||||
('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)
|
||||
@@ -154,7 +267,9 @@ class ResultWindow(QMainWindow):
|
||||
# Columns menu
|
||||
menu = self.menuColumns
|
||||
self._column_actions = []
|
||||
for index, (display, visible) in enumerate(self.app.model.result_table.columns.menu_items()):
|
||||
for index, (display, visible) in enumerate(
|
||||
self.app.model.result_table.columns.menu_items()
|
||||
):
|
||||
action = menu.addAction(display)
|
||||
action.setCheckable(True)
|
||||
action.setChecked(visible)
|
||||
@@ -195,10 +310,17 @@ class ResultWindow(QMainWindow):
|
||||
self.deltaValuesCheckBox = QCheckBox(tr("Delta Values"))
|
||||
self.searchEdit = SearchEdit()
|
||||
self.searchEdit.setMaximumWidth(300)
|
||||
self.horizontalLayout = horizontalWrap([
|
||||
self.actionsButton, self.detailsButton,
|
||||
self.dupesOnlyCheckBox, self.deltaValuesCheckBox, None, self.searchEdit, 8
|
||||
])
|
||||
self.horizontalLayout = horizontalWrap(
|
||||
[
|
||||
self.actionsButton,
|
||||
self.detailsButton,
|
||||
self.dupesOnlyCheckBox,
|
||||
self.deltaValuesCheckBox,
|
||||
None,
|
||||
self.searchEdit,
|
||||
8,
|
||||
]
|
||||
)
|
||||
self.horizontalLayout.setSpacing(8)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.resultsView = ResultsView(self.centralwidget)
|
||||
@@ -237,14 +359,14 @@ class ResultWindow(QMainWindow):
|
||||
else:
|
||||
moveToScreenCenter(self)
|
||||
|
||||
#--- Private
|
||||
# --- Private
|
||||
def _update_column_actions_status(self):
|
||||
# Update menu checked state
|
||||
menu_items = self.app.model.result_table.columns.menu_items()
|
||||
for action, (display, visible) in zip(self._column_actions, menu_items):
|
||||
action.setChecked(visible)
|
||||
|
||||
#--- Actions
|
||||
# --- Actions
|
||||
def actionsTriggered(self):
|
||||
self.actionsButton.showMenu()
|
||||
|
||||
@@ -318,14 +440,14 @@ class ResultWindow(QMainWindow):
|
||||
def saveResultsTriggered(self):
|
||||
title = tr("Select a file to save your results to")
|
||||
files = tr("dupeGuru Results (*.dupeguru)")
|
||||
destination, chosen_filter = QFileDialog.getSaveFileName(self, title, '', files)
|
||||
destination, chosen_filter = QFileDialog.getSaveFileName(self, title, "", files)
|
||||
if destination:
|
||||
if not destination.endswith('.dupeguru'):
|
||||
destination = '{}.dupeguru'.format(destination)
|
||||
if not destination.endswith(".dupeguru"):
|
||||
destination = "{}.dupeguru".format(destination)
|
||||
self.app.model.save_as(destination)
|
||||
self.app.recentResults.insertItem(destination)
|
||||
|
||||
#--- Events
|
||||
# --- Events
|
||||
def appWillSavePrefs(self):
|
||||
prefs = self.app.prefs
|
||||
prefs.resultWindowIsMaximized = self.isMaximized()
|
||||
|
||||
@@ -12,6 +12,7 @@ from PyQt5.QtWidgets import QTableView
|
||||
|
||||
from qtlib.table import Table
|
||||
|
||||
|
||||
class ResultsModel(Table):
|
||||
def __init__(self, app, view, **kwargs):
|
||||
model = app.model.result_table
|
||||
@@ -21,12 +22,12 @@ class ResultsModel(Table):
|
||||
font.setPointSize(app.prefs.tableFontSize)
|
||||
self.view.setFont(font)
|
||||
fm = QFontMetrics(font)
|
||||
view.verticalHeader().setDefaultSectionSize(fm.height()+2)
|
||||
view.verticalHeader().setDefaultSectionSize(fm.height() + 2)
|
||||
|
||||
app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||
|
||||
def _getData(self, row, column, role):
|
||||
if column.name == 'marked':
|
||||
if column.name == "marked":
|
||||
if role == Qt.CheckStateRole and row.markable:
|
||||
return Qt.Checked if row.marked else Qt.Unchecked
|
||||
return None
|
||||
@@ -37,33 +38,33 @@ class ResultsModel(Table):
|
||||
if row.isref:
|
||||
return QBrush(Qt.blue)
|
||||
elif row.is_cell_delta(column.name):
|
||||
return QBrush(QColor(255, 142, 40)) # orange
|
||||
return QBrush(QColor(255, 142, 40)) # orange
|
||||
elif role == Qt.FontRole:
|
||||
isBold = row.isref
|
||||
font = QFont(self.view.font())
|
||||
font.setBold(isBold)
|
||||
return font
|
||||
elif role == Qt.EditRole:
|
||||
if column.name == 'name':
|
||||
if column.name == "name":
|
||||
return row.data[column.name]
|
||||
return None
|
||||
|
||||
def _getFlags(self, row, column):
|
||||
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
if column.name == 'marked':
|
||||
if column.name == "marked":
|
||||
if row.markable:
|
||||
flags |= Qt.ItemIsUserCheckable
|
||||
elif column.name == 'name':
|
||||
elif column.name == "name":
|
||||
flags |= Qt.ItemIsEditable
|
||||
return flags
|
||||
|
||||
def _setData(self, row, column, value, role):
|
||||
if role == Qt.CheckStateRole:
|
||||
if column.name == 'marked':
|
||||
if column.name == "marked":
|
||||
row.marked = bool(value)
|
||||
return True
|
||||
elif role == Qt.EditRole:
|
||||
if column.name == 'name':
|
||||
if column.name == "name":
|
||||
return self.model.rename_selected(value)
|
||||
return False
|
||||
|
||||
@@ -71,7 +72,7 @@ class ResultsModel(Table):
|
||||
column = self.model.COLUMNS[column]
|
||||
self.model.sort(column.name, order == Qt.AscendingOrder)
|
||||
|
||||
#--- Properties
|
||||
# --- Properties
|
||||
@property
|
||||
def power_marker(self):
|
||||
return self.model.power_marker
|
||||
@@ -88,11 +89,11 @@ class ResultsModel(Table):
|
||||
def delta_values(self, value):
|
||||
self.model.delta_values = value
|
||||
|
||||
#--- Events
|
||||
# --- Events
|
||||
def appWillSavePrefs(self):
|
||||
self.model.columns.save_columns()
|
||||
|
||||
#--- model --> view
|
||||
# --- model --> view
|
||||
def invalidate_markings(self):
|
||||
# redraw view
|
||||
# HACK. this is the only way I found to update the widget without reseting everything
|
||||
@@ -101,9 +102,9 @@ class ResultsModel(Table):
|
||||
|
||||
|
||||
class ResultsView(QTableView):
|
||||
#--- Override
|
||||
# --- Override
|
||||
def keyPressEvent(self, event):
|
||||
if event.text() == ' ':
|
||||
if event.text() == " ":
|
||||
self.spacePressed.emit()
|
||||
return
|
||||
super().keyPressEvent(event)
|
||||
@@ -112,5 +113,5 @@ class ResultsView(QTableView):
|
||||
self.doubleClicked.emit(QModelIndex())
|
||||
# We don't call the superclass' method because the default behavior is to rename the cell.
|
||||
|
||||
#--- Signals
|
||||
# --- Signals
|
||||
spacePressed = pyqtSignal()
|
||||
|
||||
@@ -11,7 +11,8 @@ from hscommon.trans import trget
|
||||
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
||||
from ..details_table import DetailsTable
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class DetailsDialog(DetailsDialogBase):
|
||||
def _setupUi(self):
|
||||
@@ -26,4 +27,3 @@ class DetailsDialog(DetailsDialogBase):
|
||||
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.tableView.setShowGrid(False)
|
||||
self.verticalLayout.addWidget(self.tableView)
|
||||
|
||||
|
||||
@@ -6,7 +6,13 @@
|
||||
|
||||
from PyQt5.QtCore import QSize
|
||||
from PyQt5.QtWidgets import (
|
||||
QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget, QLineEdit
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QSizePolicy,
|
||||
QSpacerItem,
|
||||
QWidget,
|
||||
QLineEdit,
|
||||
)
|
||||
|
||||
from hscommon.trans import trget
|
||||
@@ -17,7 +23,8 @@ from core.scanner import ScanType
|
||||
|
||||
from ..preferences_dialog import PreferencesDialogBase
|
||||
|
||||
tr = trget('ui')
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class PreferencesDialog(PreferencesDialogBase):
|
||||
def _setupPreferenceWidgets(self):
|
||||
@@ -26,24 +33,36 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self.widget = QWidget(self)
|
||||
self.widget.setMinimumSize(QSize(0, 136))
|
||||
self.verticalLayout_4 = QVBoxLayout(self.widget)
|
||||
self._setupAddCheckbox('wordWeightingBox', tr("Word weighting"), self.widget)
|
||||
self._setupAddCheckbox("wordWeightingBox", tr("Word weighting"), self.widget)
|
||||
self.verticalLayout_4.addWidget(self.wordWeightingBox)
|
||||
self._setupAddCheckbox('matchSimilarBox', tr("Match similar words"), self.widget)
|
||||
self._setupAddCheckbox(
|
||||
"matchSimilarBox", tr("Match similar words"), self.widget
|
||||
)
|
||||
self.verticalLayout_4.addWidget(self.matchSimilarBox)
|
||||
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"), self.widget)
|
||||
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"), self.widget)
|
||||
self.verticalLayout_4.addWidget(self.mixFileKindBox)
|
||||
self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering"), self.widget)
|
||||
self._setupAddCheckbox(
|
||||
"useRegexpBox", tr("Use regular expressions when filtering"), self.widget
|
||||
)
|
||||
self.verticalLayout_4.addWidget(self.useRegexpBox)
|
||||
self._setupAddCheckbox('removeEmptyFoldersBox', tr("Remove empty folders on delete or move"), self.widget)
|
||||
self._setupAddCheckbox(
|
||||
"removeEmptyFoldersBox",
|
||||
tr("Remove empty folders on delete or move"),
|
||||
self.widget,
|
||||
)
|
||||
self.verticalLayout_4.addWidget(self.removeEmptyFoldersBox)
|
||||
self.horizontalLayout_2 = QHBoxLayout()
|
||||
self._setupAddCheckbox('ignoreSmallFilesBox', tr("Ignore files smaller than"), self.widget)
|
||||
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)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.sizeThresholdEdit.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.sizeThresholdEdit.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.sizeThresholdEdit.setSizePolicy(sizePolicy)
|
||||
self.sizeThresholdEdit.setMaximumSize(QSize(50, 16777215))
|
||||
self.horizontalLayout_2.addWidget(self.sizeThresholdEdit)
|
||||
@@ -54,11 +73,14 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self.horizontalLayout_2.addItem(spacerItem1)
|
||||
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
|
||||
self._setupAddCheckbox(
|
||||
'ignoreHardlinkMatches',
|
||||
tr("Ignore duplicates hardlinking to the same file"), self.widget
|
||||
"ignoreHardlinkMatches",
|
||||
tr("Ignore duplicates hardlinking to the same file"),
|
||||
self.widget,
|
||||
)
|
||||
self.verticalLayout_4.addWidget(self.ignoreHardlinkMatches)
|
||||
self._setupAddCheckbox('debugModeBox', tr("Debug mode (restart required)"), self.widget)
|
||||
self._setupAddCheckbox(
|
||||
"debugModeBox", tr("Debug mode (restart required)"), self.widget
|
||||
)
|
||||
self.verticalLayout_4.addWidget(self.debugModeBox)
|
||||
self.widgetsVLayout.addWidget(self.widget)
|
||||
self._setupBottomPart()
|
||||
@@ -81,4 +103,3 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
prefs.word_weighting = ischecked(self.wordWeightingBox)
|
||||
prefs.ignore_small_files = ischecked(self.ignoreSmallFilesBox)
|
||||
prefs.small_file_threshold = tryint(self.sizeThresholdEdit.text())
|
||||
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
from qtlib.column import Column
|
||||
from ..results_model import ResultsModel as ResultsModelBase
|
||||
|
||||
|
||||
class ResultsModel(ResultsModelBase):
|
||||
COLUMNS = [
|
||||
Column('marked', defaultWidth=30),
|
||||
Column('name', defaultWidth=200),
|
||||
Column('folder_path', defaultWidth=180),
|
||||
Column('size', defaultWidth=60),
|
||||
Column('extension', defaultWidth=40),
|
||||
Column('mtime', defaultWidth=120),
|
||||
Column('percentage', defaultWidth=60),
|
||||
Column('words', defaultWidth=120),
|
||||
Column('dupe_count', defaultWidth=80),
|
||||
Column("marked", defaultWidth=30),
|
||||
Column("name", defaultWidth=200),
|
||||
Column("folder_path", defaultWidth=180),
|
||||
Column("size", defaultWidth=60),
|
||||
Column("extension", defaultWidth=40),
|
||||
Column("mtime", defaultWidth=120),
|
||||
Column("percentage", defaultWidth=60),
|
||||
Column("words", defaultWidth=120),
|
||||
Column("dupe_count", defaultWidth=80),
|
||||
]
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2010-02-12
|
||||
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
#
|
||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
|
||||
class StatsLabel:
|
||||
def __init__(self, model, view):
|
||||
self.view = view
|
||||
self.model = model
|
||||
self.model.view = self
|
||||
|
||||
|
||||
def refresh(self):
|
||||
self.view.setText(self.model.display)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user