1
0
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:
Virgil Dupras
2016-05-29 22:37:38 -04:00
parent 8b878b7b13
commit 7d749779f2
18 changed files with 261 additions and 393 deletions

View File

@@ -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()

View File

@@ -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):

View File

@@ -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)

View File

@@ -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()