mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-02-05 12:51:39 +00:00
qt: merge ME edition into SE
(breaks PE temporarily) Adds a Standard/Music Application Mode button to SE and thus adds the ability to run ME scan types in SE. When in Music mode, the Music-specific results window, details panel and preferences panel will show up. All preferences except scan_type become shared between app modes (changing the pref in a mode changes it in the other mode). Results Window and Details Panel are now re-created at each scan operation because they could change their type between two runs. Preferences panel is instantiated on the fly and discarded after close. This is a very big merge operation and I'm trying to touch as little code as possible, sometimes at the cost of elegance. I try to minimize the breakage that this change brings.
This commit is contained in:
@@ -47,7 +47,6 @@ class DupeGuru(QObject):
|
||||
self.prefs.load()
|
||||
self.model = self.MODELCLASS(view=self)
|
||||
self._setup()
|
||||
self.prefsChanged.emit(self.prefs)
|
||||
|
||||
#--- Private
|
||||
def _setup(self):
|
||||
@@ -55,15 +54,14 @@ class DupeGuru(QObject):
|
||||
self._update_options()
|
||||
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.resultWindow = self.RESULT_WINDOW_CLASS(self.directories_dialog, self)
|
||||
self.progress_window = ProgressWindow(self.resultWindow, self.model.progress_window)
|
||||
self.details_dialog = self.DETAILS_DIALOG_CLASS(self.resultWindow, self)
|
||||
self.problemDialog = ProblemDialog(parent=self.resultWindow, model=self.model.problem_dialog)
|
||||
self.ignoreListDialog = IgnoreListDialog(parent=self.resultWindow, model=self.model.ignore_list_dialog)
|
||||
self.deletionOptions = DeletionOptions(parent=self.resultWindow, model=self.model.deletion_options)
|
||||
self.preferences_dialog = self.PREFERENCES_DIALOG_CLASS(self.resultWindow, self)
|
||||
self.about_box = AboutBox(self.resultWindow, 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.about_box = AboutBox(self.directories_dialog, self)
|
||||
|
||||
self.directories_dialog.show()
|
||||
self.model.load()
|
||||
@@ -94,6 +92,7 @@ class DupeGuru(QObject):
|
||||
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)
|
||||
|
||||
#--- Public
|
||||
def add_selected_to_ignore_list(self):
|
||||
@@ -112,14 +111,15 @@ class DupeGuru(QObject):
|
||||
self.model.invoke_custom_command()
|
||||
|
||||
def show_details(self):
|
||||
self.details_dialog.show()
|
||||
if self.details_dialog is not None:
|
||||
self.details_dialog.show()
|
||||
|
||||
def showResultsWindow(self):
|
||||
self.resultWindow.show()
|
||||
if self.resultWindow is not None:
|
||||
self.resultWindow.show()
|
||||
|
||||
#--- Signals
|
||||
willSavePrefs = pyqtSignal()
|
||||
prefsChanged = pyqtSignal(object)
|
||||
|
||||
#--- Events
|
||||
def finishedLaunching(self):
|
||||
@@ -143,13 +143,14 @@ class DupeGuru(QObject):
|
||||
desktop.open_path(debugLogPath)
|
||||
|
||||
def preferencesTriggered(self):
|
||||
self.preferences_dialog.load()
|
||||
result = self.preferences_dialog.exec()
|
||||
preferences_dialog = self.PREFERENCES_DIALOG_CLASS(self.directories_dialog, self)
|
||||
preferences_dialog.load()
|
||||
result = preferences_dialog.exec()
|
||||
if result == QDialog.Accepted:
|
||||
self.preferences_dialog.save()
|
||||
preferences_dialog.save()
|
||||
self.prefs.save()
|
||||
self._update_options()
|
||||
self.prefsChanged.emit(self.prefs)
|
||||
preferences_dialog.setParent(None)
|
||||
|
||||
def quitTriggered(self):
|
||||
self.directories_dialog.close()
|
||||
@@ -176,6 +177,18 @@ class DupeGuru(QObject):
|
||||
def ask_yes_no(self, prompt):
|
||||
return self.confirm('', prompt)
|
||||
|
||||
def create_results_window(self):
|
||||
"""Creates resultWindow and details_dialog depending on the selected ``app_mode``.
|
||||
"""
|
||||
if self.details_dialog is not None:
|
||||
self.details_dialog.close()
|
||||
self.details_dialog.setParent(None)
|
||||
if self.resultWindow is not None:
|
||||
self.resultWindow.close()
|
||||
self.resultWindow.setParent(None)
|
||||
self.resultWindow = self.RESULT_WINDOW_CLASS(self.directories_dialog, self)
|
||||
self.details_dialog = self.DETAILS_DIALOG_CLASS(self.resultWindow, self)
|
||||
|
||||
def show_results_window(self):
|
||||
self.showResultsWindow()
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ from PyQt5.QtWidgets import (
|
||||
from PyQt5.QtGui import QPixmap, QIcon
|
||||
|
||||
from hscommon.trans import trget
|
||||
from core.app import AppMode
|
||||
from qtlib.radio_box import RadioBox
|
||||
from qtlib.recent import Recent
|
||||
from qtlib.util import moveToScreenCenter, createActions
|
||||
|
||||
@@ -28,9 +30,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS
|
||||
self.recentFolders = Recent(self.app, 'recentFolders')
|
||||
self._setupUi()
|
||||
SCAN_TYPE_ORDER = [so.scan_type for so in self.app.model.SCANNER_CLASS.get_scan_options()]
|
||||
scan_type_index = SCAN_TYPE_ORDER.index(self.app.prefs.scan_type)
|
||||
self.scanTypeComboBox.setCurrentIndex(scan_type_index)
|
||||
self._updateScanTypeList()
|
||||
self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView)
|
||||
self.directoriesDelegate = DirectoriesDelegate()
|
||||
self.treeView.setItemDelegate(self.directoriesDelegate)
|
||||
@@ -41,11 +41,12 @@ class DirectoriesDialog(QMainWindow):
|
||||
self._updateAddButton()
|
||||
self._updateRemoveButton()
|
||||
self._updateLoadResultsButton()
|
||||
self._updateActionsState()
|
||||
self._setupBindings()
|
||||
|
||||
def _setupBindings(self):
|
||||
self.appModeRadioBox.itemSelected.connect(self.appModeButtonSelected)
|
||||
self.showPreferencesButton.clicked.connect(self.app.actionPreferences.trigger)
|
||||
self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged)
|
||||
self.scanButton.clicked.connect(self.scanButtonClicked)
|
||||
self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger)
|
||||
self.addFolderButton.clicked.connect(self.actionAddFolder.trigger)
|
||||
@@ -123,6 +124,13 @@ class DirectoriesDialog(QMainWindow):
|
||||
self.treeView.setUniformRowHeights(True)
|
||||
self.verticalLayout.addWidget(self.treeView)
|
||||
hl = QHBoxLayout()
|
||||
label = QLabel(tr("Application Mode:"), self)
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(label)
|
||||
self.appModeRadioBox = RadioBox(self, items=[tr("Standard"), tr("Music")], spread=False)
|
||||
hl.addWidget(self.appModeRadioBox)
|
||||
self.verticalLayout.addLayout(hl)
|
||||
hl = QHBoxLayout()
|
||||
hl.setAlignment(Qt.AlignLeft)
|
||||
label = QLabel(tr("Scan Type:"), self)
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
@@ -130,10 +138,8 @@ class DirectoriesDialog(QMainWindow):
|
||||
self.scanTypeComboBox = QComboBox(self)
|
||||
self.scanTypeComboBox.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
|
||||
self.scanTypeComboBox.setMaximumWidth(400)
|
||||
for scan_option in self.app.model.SCANNER_CLASS.get_scan_options():
|
||||
self.scanTypeComboBox.addItem(scan_option.label)
|
||||
hl.addWidget(self.scanTypeComboBox)
|
||||
self.showPreferencesButton = QPushButton(tr("Options"), self.centralwidget)
|
||||
self.showPreferencesButton = QPushButton(tr("More Options"), self.centralwidget)
|
||||
self.showPreferencesButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(self.showPreferencesButton)
|
||||
self.verticalLayout.addLayout(hl)
|
||||
@@ -172,6 +178,9 @@ class DirectoriesDialog(QMainWindow):
|
||||
header.setSectionResizeMode(1, QHeaderView.Fixed)
|
||||
header.resizeSection(1, 100)
|
||||
|
||||
def _updateActionsState(self):
|
||||
self.actionShowResultsWindow.setEnabled(self.app.resultWindow is not None)
|
||||
|
||||
def _updateAddButton(self):
|
||||
if self.recentFolders.isEmpty():
|
||||
self.addFolderButton.setMenu(None)
|
||||
@@ -191,6 +200,23 @@ class DirectoriesDialog(QMainWindow):
|
||||
else:
|
||||
self.loadResultsButton.setMenu(self.menuRecentResults)
|
||||
|
||||
def _updateScanTypeList(self):
|
||||
try:
|
||||
self.scanTypeComboBox.currentIndexChanged[int].disconnect(self.scanTypeChanged)
|
||||
except TypeError:
|
||||
# Not connected, ignore
|
||||
pass
|
||||
self.scanTypeComboBox.clear()
|
||||
scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
|
||||
for scan_option in scan_options:
|
||||
self.scanTypeComboBox.addItem(scan_option.label)
|
||||
SCAN_TYPE_ORDER = [so.scan_type for so in scan_options]
|
||||
selected_scan_type = self.app.prefs.get_scan_type(self.app.model.app_mode)
|
||||
scan_type_index = SCAN_TYPE_ORDER.index(selected_scan_type)
|
||||
self.scanTypeComboBox.setCurrentIndex(scan_type_index)
|
||||
self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged)
|
||||
self.app._update_options()
|
||||
|
||||
#--- QWidget overrides
|
||||
def closeEvent(self, event):
|
||||
event.accept()
|
||||
@@ -213,6 +239,14 @@ class DirectoriesDialog(QMainWindow):
|
||||
self.app.model.add_directory(dirpath)
|
||||
self.recentFolders.insertItem(dirpath)
|
||||
|
||||
def appModeButtonSelected(self, index):
|
||||
if index == 1:
|
||||
mode = AppMode.Music
|
||||
else:
|
||||
mode = AppMode.Standard
|
||||
self.app.model.app_mode = mode
|
||||
self._updateScanTypeList()
|
||||
|
||||
def appWillSavePrefs(self):
|
||||
self.app.prefs.directoriesWindowRect = self.geometry()
|
||||
|
||||
@@ -241,7 +275,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
|
||||
def scanTypeChanged(self, index):
|
||||
scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
|
||||
self.app.prefs.scan_type = 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):
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from hscommon import trans
|
||||
from core.app import AppMode
|
||||
from core.scanner import ScanType
|
||||
from qtlib.preferences import Preferences as PreferencesBase
|
||||
|
||||
class Preferences(PreferencesBase):
|
||||
@@ -89,10 +91,14 @@ class Preferences(PreferencesBase):
|
||||
self._save_specific(settings)
|
||||
|
||||
# scan_type is special because we save it immediately when we set it.
|
||||
@property
|
||||
def scan_type(self):
|
||||
return self.get_value('ScanType', self.DEFAULT_SCAN_TYPE)
|
||||
def get_scan_type(self, app_mode):
|
||||
if app_mode == AppMode.Music:
|
||||
return self.get_value('ScanTypeMusic', ScanType.Tag)
|
||||
else:
|
||||
return self.get_value('ScanTypeStandard', ScanType.Contents)
|
||||
|
||||
@scan_type.setter
|
||||
def scan_type(self, value):
|
||||
self.set_value('ScanType', value)
|
||||
def set_scan_type(self, app_mode, value):
|
||||
if app_mode == AppMode.Music:
|
||||
self.set_value('ScanTypeMusic', value)
|
||||
else:
|
||||
self.set_value('ScanTypeStandard', value)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-23
|
||||
# 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, pyqtSignal, QModelIndex
|
||||
@@ -17,13 +17,17 @@ class ResultsModel(Table):
|
||||
model = app.model.result_table
|
||||
super().__init__(model, view, **kwargs)
|
||||
view.horizontalHeader().setSortIndicator(1, Qt.AscendingOrder)
|
||||
|
||||
app.prefsChanged.connect(self.appPrefsChanged)
|
||||
font = view.font()
|
||||
font.setPointSize(app.prefs.tableFontSize)
|
||||
self.view.setFont(font)
|
||||
fm = QFontMetrics(font)
|
||||
view.verticalHeader().setDefaultSectionSize(fm.height()+2)
|
||||
|
||||
app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||
|
||||
|
||||
def _getData(self, row, column, role):
|
||||
if column.name == 'marked':
|
||||
if role == Qt.CheckStateRole and row.markable:
|
||||
if role == Qt.CheckStateRole and row.markable:
|
||||
return Qt.Checked if row.marked else Qt.Unchecked
|
||||
return None
|
||||
if role == Qt.DisplayRole:
|
||||
@@ -43,7 +47,7 @@ class ResultsModel(Table):
|
||||
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':
|
||||
@@ -52,7 +56,7 @@ class ResultsModel(Table):
|
||||
elif column.name == 'name':
|
||||
flags |= Qt.ItemIsEditable
|
||||
return flags
|
||||
|
||||
|
||||
def _setData(self, row, column, value, role):
|
||||
if role == Qt.CheckStateRole:
|
||||
if column.name == 'marked':
|
||||
@@ -62,46 +66,39 @@ class ResultsModel(Table):
|
||||
if column.name == 'name':
|
||||
return self.model.rename_selected(value)
|
||||
return False
|
||||
|
||||
|
||||
def sort(self, column, order):
|
||||
column = self.model.COLUMNS[column]
|
||||
self.model.sort(column.name, order == Qt.AscendingOrder)
|
||||
|
||||
|
||||
#--- Properties
|
||||
@property
|
||||
def power_marker(self):
|
||||
return self.model.power_marker
|
||||
|
||||
|
||||
@power_marker.setter
|
||||
def power_marker(self, value):
|
||||
self.model.power_marker = value
|
||||
|
||||
|
||||
@property
|
||||
def delta_values(self):
|
||||
return self.model.delta_values
|
||||
|
||||
|
||||
@delta_values.setter
|
||||
def delta_values(self, value):
|
||||
self.model.delta_values = value
|
||||
|
||||
|
||||
#--- Events
|
||||
def appPrefsChanged(self, prefs):
|
||||
font = self.view.font()
|
||||
font.setPointSize(prefs.tableFontSize)
|
||||
self.view.setFont(font)
|
||||
fm = QFontMetrics(font)
|
||||
self.view.verticalHeader().setDefaultSectionSize(fm.height()+2)
|
||||
|
||||
def appWillSavePrefs(self):
|
||||
self.model.columns.save_columns()
|
||||
|
||||
|
||||
#--- model --> view
|
||||
def invalidate_markings(self):
|
||||
# redraw view
|
||||
# HACK. this is the only way I found to update the widget without reseting everything
|
||||
self.view.scroll(0, 1)
|
||||
self.view.scroll(0, -1)
|
||||
|
||||
|
||||
|
||||
class ResultsView(QTableView):
|
||||
#--- Override
|
||||
@@ -110,10 +107,10 @@ class ResultsView(QTableView):
|
||||
self.spacePressed.emit()
|
||||
return
|
||||
super().keyPressEvent(event)
|
||||
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
self.doubleClicked.emit(QModelIndex())
|
||||
# We don't call the superclass' method because the default behavior is to rename the cell.
|
||||
|
||||
|
||||
#--- Signals
|
||||
spacePressed = pyqtSignal()
|
||||
|
||||
Reference in New Issue
Block a user