mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-03-10 05:34:36 +00:00
The directories dialog is now the main window. There's probably many glitches left to fix due to that change, but the basic functionalities are there.
--HG-- rename : qt/base/main_window.py => qt/base/result_window.py rename : qt/pe/main_window.py => qt/pe/result_window.py
This commit is contained in:
parent
30eb26af7d
commit
d51f5184d7
133
qt/base/app.py
133
qt/base/app.py
@ -6,14 +6,12 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
|
||||
|
||||
import logging
|
||||
import os
|
||||
import os.path as op
|
||||
|
||||
from PyQt4.QtCore import QTimer, QObject, QCoreApplication, QUrl, SIGNAL, pyqtSignal
|
||||
from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox
|
||||
from PyQt4.QtCore import QTimer, QObject, QCoreApplication, QUrl, QProcess, SIGNAL, pyqtSignal
|
||||
from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox, QApplication
|
||||
|
||||
from jobprogress import job
|
||||
from jobprogress.qt import Progress
|
||||
@ -21,13 +19,14 @@ from jobprogress.qt import Progress
|
||||
from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE
|
||||
|
||||
from qtlib.about_box import AboutBox
|
||||
from qtlib.recent import Recent
|
||||
from qtlib.reg import Registration
|
||||
|
||||
from . import platform
|
||||
|
||||
from .main_window import MainWindow
|
||||
from .result_window import ResultWindow
|
||||
from .directories_dialog import DirectoriesDialog
|
||||
from .problem_dialog import ProblemDialog
|
||||
from .util import createActions
|
||||
|
||||
JOBID2TITLE = {
|
||||
JOB_SCAN: "Scanning for duplicates",
|
||||
@ -54,16 +53,20 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
|
||||
#--- Private
|
||||
def _setup(self):
|
||||
self._setupActions()
|
||||
self.prefs = self._create_preferences()
|
||||
self.prefs.load()
|
||||
self._update_options()
|
||||
self.main_window = self._create_main_window()
|
||||
self._progress = Progress(self.main_window)
|
||||
self.directories_dialog = DirectoriesDialog(self.main_window, self)
|
||||
self.details_dialog = self._create_details_dialog(self.main_window)
|
||||
self.problemDialog = ProblemDialog(parent=self.main_window, app=self)
|
||||
self.preferences_dialog = self._create_preferences_dialog(self.main_window)
|
||||
self.about_box = AboutBox(self.main_window, self)
|
||||
self.resultWindow = self._create_result_window()
|
||||
self._progress = Progress(self.resultWindow)
|
||||
self.directories_dialog = DirectoriesDialog(self.resultWindow, self)
|
||||
self.details_dialog = self._create_details_dialog(self.resultWindow)
|
||||
self.problemDialog = ProblemDialog(parent=self.resultWindow, app=self)
|
||||
self.preferences_dialog = self._create_preferences_dialog(self.resultWindow)
|
||||
self.about_box = AboutBox(self.resultWindow, self)
|
||||
|
||||
self.recentResults = Recent(self, self.directories_dialog.menuLoadRecent, 'recentResults')
|
||||
self.recentResults.mustOpenItem.connect(self.load_from)
|
||||
|
||||
self.reg = Registration(self)
|
||||
self.set_registration(self.prefs.registration_code, self.prefs.registration_email)
|
||||
@ -73,19 +76,30 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
# In some circumstances, the nag is hidden by other window, which may make the user think
|
||||
# that the application haven't launched.
|
||||
QTimer.singleShot(0, self.reg.show_nag)
|
||||
if self.prefs.mainWindowIsMaximized:
|
||||
self.main_window.showMaximized()
|
||||
else:
|
||||
self.main_window.show()
|
||||
self.directories_dialog.show()
|
||||
self.load()
|
||||
|
||||
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
||||
self.connect(self._progress, SIGNAL('finished(QString)'), self.job_finished)
|
||||
|
||||
def _setupActions(self):
|
||||
# Setup actions that are common to both the directory dialog and the results window.
|
||||
# (name, shortcut, icon, desc, func)
|
||||
ACTIONS = [
|
||||
('actionQuit', 'Ctrl+Q', '', "Quit", self.quitTriggered),
|
||||
('actionPreferences', 'Ctrl+5', 'preferences', "Preferences", self.preferencesTriggered),
|
||||
('actionShowHelp', 'F1', '', "dupeGuru Help", self.showHelpTriggered),
|
||||
('actionAbout', '', '', "About dupeGuru", self.showAboutBoxTriggered),
|
||||
('actionRegister', '', '', "Register dupeGuru", self.registerTriggered),
|
||||
('actionCheckForUpdate', '', '', "Check for Update", self.checkForUpdateTriggered),
|
||||
('actionOpenDebugLog', '', '', "Open Debug Log", self.openDebugLogTriggered),
|
||||
]
|
||||
createActions(ACTIONS, self)
|
||||
|
||||
def _setup_as_registered(self):
|
||||
self.prefs.registration_code = self.registration_code
|
||||
self.prefs.registration_email = self.registration_email
|
||||
self.main_window.actionRegister.setVisible(False)
|
||||
self.actionRegister.setVisible(False)
|
||||
self.about_box.registerButton.hide()
|
||||
self.about_box.registeredEmailLabel.setText(self.prefs.registration_email)
|
||||
|
||||
@ -99,8 +113,8 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
def _create_details_dialog(self, parent):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _create_main_window(self):
|
||||
return MainWindow(app=self)
|
||||
def _create_result_window(self):
|
||||
return ResultWindow(app=self)
|
||||
|
||||
def _create_preferences(self):
|
||||
raise NotImplementedError()
|
||||
@ -126,7 +140,7 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
self._progress.run(jobid, title, func, args=args)
|
||||
except job.JobInProgressError:
|
||||
msg = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
|
||||
QMessageBox.information(self.main_window, 'Action in progress', msg)
|
||||
QMessageBox.information(self.resultWindow, 'Action in progress', msg)
|
||||
|
||||
def add_selected_to_ignore_list(self):
|
||||
dupes = self.without_ref(self.selected_dupes)
|
||||
@ -134,14 +148,14 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
return
|
||||
title = "Add to Ignore List"
|
||||
msg = "All selected {0} matches are going to be ignored in all subsequent scans. Continue?".format(len(dupes))
|
||||
if self.main_window._confirm(title, msg):
|
||||
if self.confirm(title, msg):
|
||||
DupeGuruBase.add_selected_to_ignore_list(self)
|
||||
|
||||
def copy_or_move_marked(self, copy):
|
||||
opname = 'copy' if copy else 'move'
|
||||
title = "Select a directory to {0} marked files to".format(opname)
|
||||
flags = QFileDialog.ShowDirsOnly
|
||||
destination = str(QFileDialog.getExistingDirectory(self.main_window, title, '', flags))
|
||||
destination = str(QFileDialog.getExistingDirectory(self.resultWindow, title, '', flags))
|
||||
if not destination:
|
||||
return
|
||||
recreate_path = self.prefs.destination_type
|
||||
@ -153,46 +167,32 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
return
|
||||
title = "Remove duplicates"
|
||||
msg = "You are about to remove {0} files from results. Continue?".format(len(dupes))
|
||||
if self.main_window._confirm(title, msg):
|
||||
if self.confirm(title, msg):
|
||||
DupeGuruBase.remove_selected(self)
|
||||
|
||||
#--- Public
|
||||
def askForRegCode(self):
|
||||
self.reg.ask_for_code()
|
||||
|
||||
def confirm(self, title, msg, default_button=QMessageBox.Yes):
|
||||
active = QApplication.activeWindow()
|
||||
buttons = QMessageBox.Yes | QMessageBox.No
|
||||
answer = QMessageBox.question(active, title, msg, buttons, default_button)
|
||||
return answer == QMessageBox.Yes
|
||||
|
||||
def invokeCustomCommand(self):
|
||||
cmd = self.prefs.custom_command
|
||||
if cmd:
|
||||
self.invoke_command(cmd)
|
||||
else:
|
||||
msg = "You have no custom command set up. Please, set it up in your preferences."
|
||||
QMessageBox.warning(self.main_window, 'Custom Command', msg)
|
||||
|
||||
def openDebugLog(self):
|
||||
debugLogPath = op.join(self.appdata, 'debug.log')
|
||||
self._open_path(debugLogPath)
|
||||
|
||||
def show_about_box(self):
|
||||
self.about_box.show()
|
||||
QMessageBox.warning(self.resultWindow, 'Custom Command', msg)
|
||||
|
||||
def show_details(self):
|
||||
self.details_dialog.show()
|
||||
|
||||
def show_directories(self):
|
||||
self.directories_dialog.show()
|
||||
|
||||
def show_help(self):
|
||||
base_path = platform.HELP_PATH.format(self.EDITION)
|
||||
url = QUrl.fromLocalFile(op.abspath(op.join(base_path, 'index.html')))
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def show_preferences(self):
|
||||
self.preferences_dialog.load()
|
||||
result = self.preferences_dialog.exec_()
|
||||
if result == QDialog.Accepted:
|
||||
self.preferences_dialog.save()
|
||||
self.prefs.save()
|
||||
self._update_options()
|
||||
def showResultsWindow(self):
|
||||
self.resultWindow.show()
|
||||
|
||||
#--- Signals
|
||||
willSavePrefs = pyqtSignal()
|
||||
@ -203,6 +203,9 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
self.prefs.save()
|
||||
self.save()
|
||||
|
||||
def checkForUpdateTriggered(self):
|
||||
QProcess.execute('updater.exe', ['/checknow'])
|
||||
|
||||
def job_finished(self, jobid):
|
||||
self._job_completed(jobid)
|
||||
if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE):
|
||||
@ -210,10 +213,40 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
self.problemDialog.show()
|
||||
else:
|
||||
msg = "All files were processed successfully."
|
||||
QMessageBox.information(self.main_window, 'Operation Complete', msg)
|
||||
QMessageBox.information(self.resultWindow, 'Operation Complete', msg)
|
||||
elif jobid == JOB_SCAN:
|
||||
if not self.results.groups:
|
||||
title = "Scanning complete"
|
||||
title = "Scan complete"
|
||||
msg = "No duplicates found."
|
||||
QMessageBox.information(self.main_window, title, msg)
|
||||
QMessageBox.information(self.resultWindow, title, msg)
|
||||
else:
|
||||
self.showResultsWindow()
|
||||
elif jobid == JOB_LOAD:
|
||||
self.showResultsWindow()
|
||||
|
||||
def openDebugLogTriggered(self):
|
||||
debugLogPath = op.join(self.appdata, 'debug.log')
|
||||
self._open_path(debugLogPath)
|
||||
|
||||
def preferencesTriggered(self):
|
||||
self.preferences_dialog.load()
|
||||
result = self.preferences_dialog.exec_()
|
||||
if result == QDialog.Accepted:
|
||||
self.preferences_dialog.save()
|
||||
self.prefs.save()
|
||||
self._update_options()
|
||||
|
||||
def quitTriggered(self):
|
||||
self.directories_dialog.close()
|
||||
|
||||
def registerTriggered(self):
|
||||
self.reg.ask_for_code()
|
||||
|
||||
def showAboutBoxTriggered(self):
|
||||
self.about_box.show()
|
||||
|
||||
def showHelpTriggered(self):
|
||||
base_path = platform.HELP_PATH.format(self.EDITION)
|
||||
url = QUrl.fromLocalFile(op.abspath(op.join(base_path, 'index.html')))
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
|
@ -6,17 +6,20 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt, QSize
|
||||
from PyQt4.QtGui import (QDialog, QFileDialog, QHeaderView, QVBoxLayout, QHBoxLayout, QTreeView,
|
||||
QAbstractItemView, QSpacerItem, QSizePolicy, QPushButton, QApplication)
|
||||
from PyQt4.QtCore import QSize, QRect
|
||||
from PyQt4.QtGui import (QWidget, QFileDialog, QHeaderView, QVBoxLayout, QHBoxLayout, QTreeView,
|
||||
QAbstractItemView, QSpacerItem, QSizePolicy, QPushButton, QApplication, QMessageBox, QMainWindow,
|
||||
QMenuBar, QMenu)
|
||||
|
||||
from core.app import NoScannableFileError
|
||||
|
||||
from . import platform
|
||||
from .directories_model import DirectoriesModel, DirectoriesDelegate
|
||||
from .util import createActions
|
||||
|
||||
class DirectoriesDialog(QDialog):
|
||||
class DirectoriesDialog(QMainWindow):
|
||||
def __init__(self, parent, app):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
QDialog.__init__(self, parent, flags)
|
||||
QMainWindow.__init__(self, None)
|
||||
self.app = app
|
||||
self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS
|
||||
self.directoriesModel = DirectoriesModel(self.app)
|
||||
@ -24,21 +27,61 @@ class DirectoriesDialog(QDialog):
|
||||
self._setupUi()
|
||||
self._updateRemoveButton()
|
||||
|
||||
self.connect(self.doneButton, SIGNAL('clicked()'), self.doneButtonClicked)
|
||||
self.connect(self.addButton, SIGNAL('clicked()'), self.addButtonClicked)
|
||||
self.connect(self.removeButton, SIGNAL('clicked()'), self.removeButtonClicked)
|
||||
self.connect(self.treeView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
||||
self.scanButton.clicked.connect(self.scanButtonClicked)
|
||||
self.addButton.clicked.connect(self.addButtonClicked)
|
||||
self.removeButton.clicked.connect(self.removeButtonClicked)
|
||||
self.treeView.selectionModel().selectionChanged.connect(self.selectionChanged)
|
||||
self.app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||
|
||||
def _setupActions(self):
|
||||
# (name, shortcut, icon, desc, func)
|
||||
ACTIONS = [
|
||||
('actionLoadResults', 'Ctrl+L', '', "Load Results...", self.loadResultsTriggered),
|
||||
('actionShowResultsWindow', '', '', "Results Window", self.app.showResultsWindow),
|
||||
]
|
||||
createActions(ACTIONS, self)
|
||||
|
||||
def _setupMenu(self):
|
||||
self.menubar = QMenuBar(self)
|
||||
self.menubar.setGeometry(QRect(0, 0, 42, 22))
|
||||
self.menuFile = QMenu(self.menubar)
|
||||
self.menuFile.setTitle("File")
|
||||
self.menuView = QMenu(self.menubar)
|
||||
self.menuView.setTitle("View")
|
||||
self.menuHelp = QMenu(self.menubar)
|
||||
self.menuHelp.setTitle("Help")
|
||||
self.menuLoadRecent = QMenu(self.menuFile)
|
||||
self.menuLoadRecent.setTitle("Load Recent Results")
|
||||
self.setMenuBar(self.menubar)
|
||||
|
||||
self.menuFile.addAction(self.actionLoadResults)
|
||||
self.menuFile.addAction(self.menuLoadRecent.menuAction())
|
||||
self.menuFile.addSeparator()
|
||||
self.menuFile.addAction(self.app.actionQuit)
|
||||
self.menuView.addAction(self.app.actionPreferences)
|
||||
self.menuView.addAction(self.actionShowResultsWindow)
|
||||
self.menuHelp.addAction(self.app.actionShowHelp)
|
||||
self.menuHelp.addAction(self.app.actionRegister)
|
||||
self.menuHelp.addAction(self.app.actionCheckForUpdate)
|
||||
self.menuHelp.addAction(self.app.actionOpenDebugLog)
|
||||
self.menuHelp.addAction(self.app.actionAbout)
|
||||
|
||||
self.menubar.addAction(self.menuFile.menuAction())
|
||||
self.menubar.addAction(self.menuView.menuAction())
|
||||
self.menubar.addAction(self.menuHelp.menuAction())
|
||||
|
||||
def _setupUi(self):
|
||||
self.setWindowTitle("Directories")
|
||||
self.setWindowTitle(self.app.NAME)
|
||||
self.resize(420, 338)
|
||||
self.verticalLayout = QVBoxLayout(self)
|
||||
self.treeView = QTreeView(self)
|
||||
self.centralwidget = QWidget(self)
|
||||
self.verticalLayout = QVBoxLayout(self.centralwidget)
|
||||
self.treeView = QTreeView(self.centralwidget)
|
||||
self.treeView.setItemDelegate(self.directoriesDelegate)
|
||||
self.treeView.setModel(self.directoriesModel)
|
||||
self.treeView.setAcceptDrops(True)
|
||||
self.treeView.setEditTriggers(QAbstractItemView.DoubleClicked|QAbstractItemView.EditKeyPressed|QAbstractItemView.SelectedClicked)
|
||||
triggers = QAbstractItemView.DoubleClicked|QAbstractItemView.EditKeyPressed\
|
||||
|QAbstractItemView.SelectedClicked
|
||||
self.treeView.setEditTriggers(triggers)
|
||||
self.treeView.setDragDropOverwriteMode(True)
|
||||
self.treeView.setDragDropMode(QAbstractItemView.DropOnly)
|
||||
self.treeView.setUniformRowHeights(True)
|
||||
@ -51,31 +94,35 @@ class DirectoriesDialog(QDialog):
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.removeButton = QPushButton(self)
|
||||
self.removeButton = QPushButton(self.centralwidget)
|
||||
self.removeButton.setText("Remove")
|
||||
self.removeButton.setShortcut("Del")
|
||||
self.removeButton.setMinimumSize(QSize(91, 0))
|
||||
self.removeButton.setMaximumSize(QSize(16777215, 32))
|
||||
self.horizontalLayout.addWidget(self.removeButton)
|
||||
self.addButton = QPushButton(self)
|
||||
self.addButton = QPushButton(self.centralwidget)
|
||||
self.addButton.setText("Add")
|
||||
self.addButton.setMinimumSize(QSize(91, 0))
|
||||
self.addButton.setMaximumSize(QSize(16777215, 32))
|
||||
self.horizontalLayout.addWidget(self.addButton)
|
||||
spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Fixed, QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem1)
|
||||
self.doneButton = QPushButton(self)
|
||||
self.doneButton.setText("Done")
|
||||
self.scanButton = QPushButton(self.centralwidget)
|
||||
self.scanButton.setText("Scan")
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.doneButton.sizePolicy().hasHeightForWidth())
|
||||
self.doneButton.setSizePolicy(sizePolicy)
|
||||
self.doneButton.setMinimumSize(QSize(91, 0))
|
||||
self.doneButton.setMaximumSize(QSize(16777215, 32))
|
||||
self.doneButton.setDefault(True)
|
||||
self.horizontalLayout.addWidget(self.doneButton)
|
||||
sizePolicy.setHeightForWidth(self.scanButton.sizePolicy().hasHeightForWidth())
|
||||
self.scanButton.setSizePolicy(sizePolicy)
|
||||
self.scanButton.setMinimumSize(QSize(91, 0))
|
||||
self.scanButton.setMaximumSize(QSize(16777215, 32))
|
||||
self.scanButton.setDefault(True)
|
||||
self.horizontalLayout.addWidget(self.scanButton)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.setCentralWidget(self.centralwidget)
|
||||
|
||||
self._setupActions()
|
||||
self._setupMenu()
|
||||
|
||||
if self.app.prefs.directoriesWindowRect is not None:
|
||||
self.setGeometry(self.app.prefs.directoriesWindowRect)
|
||||
@ -90,6 +137,17 @@ class DirectoriesDialog(QDialog):
|
||||
node = index.internalPointer()
|
||||
# label = 'Remove' if node.parent is None else 'Exclude'
|
||||
|
||||
#--- QWidget overrides
|
||||
def closeEvent(self, event):
|
||||
event.accept()
|
||||
if self.app.results.is_modified:
|
||||
title = "Unsaved results"
|
||||
msg = "You have unsaved results, do you really want to quit?"
|
||||
if not self.app.confirm(title, msg):
|
||||
event.ignore()
|
||||
if event.isAccepted():
|
||||
QApplication.quit()
|
||||
|
||||
#--- Events
|
||||
def addButtonClicked(self):
|
||||
title = "Select a directory to add to the scanning list"
|
||||
@ -103,8 +161,13 @@ class DirectoriesDialog(QDialog):
|
||||
def appWillSavePrefs(self):
|
||||
self.app.prefs.directoriesWindowRect = self.geometry()
|
||||
|
||||
def doneButtonClicked(self):
|
||||
self.hide()
|
||||
def loadResultsTriggered(self):
|
||||
title = "Select a results file to load"
|
||||
files = "dupeGuru Results (*.dupeguru)"
|
||||
destination = QFileDialog.getOpenFileName(self, title, '', files)
|
||||
if destination:
|
||||
self.app.load_from(destination)
|
||||
self.app.recentResults.insertItem(destination)
|
||||
|
||||
def removeButtonClicked(self):
|
||||
indexes = self.treeView.selectedIndexes()
|
||||
@ -116,6 +179,18 @@ class DirectoriesDialog(QDialog):
|
||||
row = index.row()
|
||||
self.app.remove_directory(row)
|
||||
|
||||
def scanButtonClicked(self):
|
||||
title = "Start a new scan"
|
||||
if len(self.app.results.groups) > 0:
|
||||
msg = "Are you sure you want to start a new duplicate scan?"
|
||||
if not self.app.confirm(title, msg):
|
||||
return
|
||||
try:
|
||||
self.app.start_scanning()
|
||||
except NoScannableFileError:
|
||||
msg = "The selected directories contain no scannable file."
|
||||
QMessageBox.warning(self, title, msg)
|
||||
|
||||
def selectionChanged(self, selected, deselected):
|
||||
self._updateRemoveButton()
|
||||
|
||||
|
@ -36,8 +36,8 @@ class Preferences(PreferencesBase):
|
||||
self.columns_width[index] = width
|
||||
self.columns_visible = get('ColumnsVisible', self.columns_visible)
|
||||
|
||||
self.mainWindowIsMaximized = get('MainWindowIsMaximized', self.mainWindowIsMaximized)
|
||||
self.mainWindowRect = self.get_rect('MainWindowRect', self.mainWindowRect)
|
||||
self.resultWindowIsMaximized = get('ResultWindowIsMaximized', self.resultWindowIsMaximized)
|
||||
self.resultWindowRect = self.get_rect('ResultWindowRect', self.resultWindowRect)
|
||||
self.detailsWindowRect = self.get_rect('DetailsWindowRect', self.detailsWindowRect)
|
||||
self.directoriesWindowRect = self.get_rect('DirectoriesWindowRect', self.directoriesWindowRect)
|
||||
self.recentResults = get('RecentResults', self.recentResults)
|
||||
@ -59,8 +59,8 @@ class Preferences(PreferencesBase):
|
||||
self.destination_type = 1
|
||||
self.custom_command = ''
|
||||
|
||||
self.mainWindowIsMaximized = False
|
||||
self.mainWindowRect = None
|
||||
self.resultWindowIsMaximized = False
|
||||
self.resultWindowRect = None
|
||||
self.detailsWindowRect = None
|
||||
self.directoriesWindowRect = None
|
||||
self.recentResults = []
|
||||
@ -89,8 +89,8 @@ class Preferences(PreferencesBase):
|
||||
set_('ColumnsWidth', self.columns_width)
|
||||
set_('ColumnsVisible', self.columns_visible)
|
||||
|
||||
set_('MainWindowIsMaximized', self.mainWindowIsMaximized)
|
||||
self.set_rect('MainWindowRect', self.mainWindowRect)
|
||||
set_('ResultWindowIsMaximized', self.resultWindowIsMaximized)
|
||||
self.set_rect('ResultWindowRect', self.resultWindowRect)
|
||||
self.set_rect('DetailsWindowRect', self.detailsWindowRect)
|
||||
self.set_rect('DirectoriesWindowRect', self.directoriesWindowRect)
|
||||
set_('RecentResults', self.recentResults)
|
||||
|
@ -8,21 +8,18 @@
|
||||
|
||||
import sys
|
||||
|
||||
from PyQt4.QtCore import Qt, QCoreApplication, QProcess, SIGNAL, QUrl, QRect
|
||||
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QToolButton, QLabel, QHeaderView,
|
||||
QMessageBox, QInputDialog, QLineEdit, QDesktopServices, QFileDialog, QAction, QMenuBar,
|
||||
QToolBar, QWidget, QVBoxLayout, QAbstractItemView, QStatusBar)
|
||||
from PyQt4.QtCore import Qt, SIGNAL, QUrl, QRect
|
||||
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QLabel, QHeaderView, QMessageBox,
|
||||
QInputDialog, QLineEdit, QDesktopServices, QFileDialog, QMenuBar, QWidget, QVBoxLayout,
|
||||
QAbstractItemView, QStatusBar)
|
||||
|
||||
from hscommon.util import nonone
|
||||
from qtlib.recent import Recent
|
||||
|
||||
from core.app import NoScannableFileError
|
||||
|
||||
from . import dg_rc
|
||||
from .results_model import ResultsModel, ResultsView
|
||||
from .stats_label import StatsLabel
|
||||
from .util import createActions
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
class ResultWindow(QMainWindow):
|
||||
def __init__(self, app):
|
||||
QMainWindow.__init__(self, None)
|
||||
self.app = app
|
||||
@ -41,11 +38,8 @@ class MainWindow(QMainWindow):
|
||||
def _setupActions(self):
|
||||
# (name, shortcut, icon, desc, func)
|
||||
ACTIONS = [
|
||||
('actionScan', 'Ctrl+T', self.app.LOGO_NAME, "Start Scan", self.scanTriggered),
|
||||
('actionDirectories', 'Ctrl+4', 'folder', "Directories", self.directoriesTriggered),
|
||||
('actionDetails', 'Ctrl+3', 'details', "Details", self.detailsTriggered),
|
||||
('actionActions', '', 'actions', "Actions", self.actionsTriggered),
|
||||
('actionPreferences', 'Ctrl+5', 'preferences', "Preferences", self.preferencesTriggered),
|
||||
('actionDelta', 'Ctrl+2', 'delta', "Delta Values", self.deltaTriggered),
|
||||
('actionPowerMarker', 'Ctrl+1', 'power_marker', "Power Marker", self.powerMarkerTriggered),
|
||||
('actionDeleteMarked', 'Ctrl+D', '', "Send Marked to Recycle Bin", self.deleteTriggered),
|
||||
@ -64,28 +58,13 @@ class MainWindow(QMainWindow):
|
||||
('actionInvertMarking', 'Ctrl+Alt+A', '', "Invert Marking", self.markInvertTriggered),
|
||||
('actionMarkSelected', '', '', "Mark Selected", self.markSelectedTriggered),
|
||||
('actionClearIgnoreList', '', '', "Clear Ignore List", self.clearIgnoreListTriggered),
|
||||
('actionQuit', 'Ctrl+Q', '', "Quit", self.close),
|
||||
('actionApplyFilter', 'Ctrl+F', '', "Apply Filter", self.applyFilterTriggered),
|
||||
('actionCancelFilter', 'Ctrl+Shift+F', '', "Cancel Filter", self.cancelFilterTriggered),
|
||||
('actionShowHelp', 'F1', '', "dupeGuru Help", self.showHelpTriggered),
|
||||
('actionAbout', '', '', "About dupeGuru", self.aboutTriggered),
|
||||
('actionRegister', '', '', "Register dupeGuru", self.registerTrigerred),
|
||||
('actionCheckForUpdate', '', '', "Check for Update", self.checkForUpdateTriggered),
|
||||
('actionExport', '', '', "Export To HTML", self.exportTriggered),
|
||||
('actionLoadResults', 'Ctrl+L', '', "Load Results...", self.loadResultsTriggered),
|
||||
('actionSaveResults', 'Ctrl+S', '', "Save Results...", self.saveResultsTriggered),
|
||||
('actionOpenDebugLog', '', '', "Open Debug Log", self.openDebugLogTriggered),
|
||||
('actionInvokeCustomCommand', 'Ctrl+I', '', "Invoke Custom Command", self.app.invokeCustomCommand),
|
||||
]
|
||||
for name, shortcut, icon, desc, func in ACTIONS:
|
||||
action = QAction(self)
|
||||
if icon:
|
||||
action.setIcon(QIcon(QPixmap(':/' + icon)))
|
||||
if shortcut:
|
||||
action.setShortcut(shortcut)
|
||||
action.setText(desc)
|
||||
action.triggered.connect(func)
|
||||
setattr(self, name, action)
|
||||
createActions(ACTIONS, self)
|
||||
self.actionDelta.setCheckable(True)
|
||||
self.actionPowerMarker.setCheckable(True)
|
||||
|
||||
@ -100,14 +79,10 @@ class MainWindow(QMainWindow):
|
||||
self.menuActions.setTitle("Actions")
|
||||
self.menuColumns = QMenu(self.menubar)
|
||||
self.menuColumns.setTitle("Columns")
|
||||
self.menuModes = QMenu(self.menubar)
|
||||
self.menuModes.setTitle("Modes")
|
||||
self.menuWindow = QMenu(self.menubar)
|
||||
self.menuWindow.setTitle("Windows")
|
||||
self.menuView = QMenu(self.menubar)
|
||||
self.menuView.setTitle("View")
|
||||
self.menuHelp = QMenu(self.menubar)
|
||||
self.menuHelp.setTitle("Help")
|
||||
self.menuLoadRecent = QMenu(self.menuFile)
|
||||
self.menuLoadRecent.setTitle("Load Recent Results")
|
||||
self.setMenuBar(self.menubar)
|
||||
|
||||
self.menuActions.addAction(self.actionDeleteMarked)
|
||||
@ -131,32 +106,27 @@ class MainWindow(QMainWindow):
|
||||
self.menuMark.addAction(self.actionMarkNone)
|
||||
self.menuMark.addAction(self.actionInvertMarking)
|
||||
self.menuMark.addAction(self.actionMarkSelected)
|
||||
self.menuModes.addAction(self.actionPowerMarker)
|
||||
self.menuModes.addAction(self.actionDelta)
|
||||
self.menuWindow.addAction(self.actionDetails)
|
||||
self.menuWindow.addAction(self.actionDirectories)
|
||||
self.menuWindow.addAction(self.actionPreferences)
|
||||
self.menuHelp.addAction(self.actionShowHelp)
|
||||
self.menuHelp.addAction(self.actionRegister)
|
||||
self.menuHelp.addAction(self.actionCheckForUpdate)
|
||||
self.menuHelp.addAction(self.actionOpenDebugLog)
|
||||
self.menuHelp.addAction(self.actionAbout)
|
||||
self.menuFile.addAction(self.actionScan)
|
||||
self.menuFile.addSeparator()
|
||||
self.menuFile.addAction(self.actionLoadResults)
|
||||
self.menuFile.addAction(self.menuLoadRecent.menuAction())
|
||||
self.menuView.addAction(self.actionPowerMarker)
|
||||
self.menuView.addAction(self.actionDelta)
|
||||
self.menuView.addSeparator()
|
||||
self.menuView.addAction(self.actionDetails)
|
||||
self.menuView.addAction(self.app.actionPreferences)
|
||||
self.menuHelp.addAction(self.app.actionShowHelp)
|
||||
self.menuHelp.addAction(self.app.actionRegister)
|
||||
self.menuHelp.addAction(self.app.actionCheckForUpdate)
|
||||
self.menuHelp.addAction(self.app.actionOpenDebugLog)
|
||||
self.menuHelp.addAction(self.app.actionAbout)
|
||||
self.menuFile.addAction(self.actionSaveResults)
|
||||
self.menuFile.addAction(self.actionExport)
|
||||
self.menuFile.addAction(self.actionClearIgnoreList)
|
||||
self.menuFile.addSeparator()
|
||||
self.menuFile.addAction(self.actionQuit)
|
||||
self.menuFile.addAction(self.app.actionQuit)
|
||||
|
||||
self.menubar.addAction(self.menuFile.menuAction())
|
||||
self.menubar.addAction(self.menuMark.menuAction())
|
||||
self.menubar.addAction(self.menuActions.menuAction())
|
||||
self.menubar.addAction(self.menuColumns.menuAction())
|
||||
self.menubar.addAction(self.menuModes.menuAction())
|
||||
self.menubar.addAction(self.menuWindow.menuAction())
|
||||
self.menubar.addAction(self.menuView.menuAction())
|
||||
self.menubar.addAction(self.menuHelp.menuAction())
|
||||
|
||||
# Columns menu
|
||||
@ -190,27 +160,8 @@ class MainWindow(QMainWindow):
|
||||
actionMenu.addAction(self.actionRenameSelected)
|
||||
self.actionActions.setMenu(actionMenu)
|
||||
|
||||
def _setupToolbar(self):
|
||||
self.toolBar = QToolBar(self)
|
||||
self.toolBar.setMovable(False)
|
||||
self.toolBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||
self.toolBar.setFloatable(False)
|
||||
self.addToolBar(Qt.ToolBarArea(Qt.TopToolBarArea), self.toolBar)
|
||||
|
||||
self.toolBar.addAction(self.actionScan)
|
||||
button = QToolButton(self.toolBar)
|
||||
button.setDefaultAction(self.actionActions.menu().menuAction())
|
||||
button.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||
self.actionsButton = button
|
||||
self.toolBar.addWidget(button)
|
||||
self.toolBar.addAction(self.actionDirectories)
|
||||
self.toolBar.addAction(self.actionDetails)
|
||||
self.toolBar.addAction(self.actionPreferences)
|
||||
self.toolBar.addAction(self.actionDelta)
|
||||
self.toolBar.addAction(self.actionPowerMarker)
|
||||
|
||||
def _setupUi(self):
|
||||
self.setWindowTitle(QCoreApplication.instance().applicationName())
|
||||
self.setWindowTitle("dupeGuru Results")
|
||||
self.resize(630, 514)
|
||||
self.centralwidget = QWidget(self)
|
||||
self.verticalLayout_2 = QVBoxLayout(self.centralwidget)
|
||||
@ -230,17 +181,16 @@ class MainWindow(QMainWindow):
|
||||
self.setCentralWidget(self.centralwidget)
|
||||
self._setupActions()
|
||||
self._setupMenu()
|
||||
self._setupToolbar()
|
||||
self.recentResults = Recent(self.app, self.menuLoadRecent, 'recentResults')
|
||||
self.recentResults.mustOpenItem.connect(self.app.load_from)
|
||||
self.statusbar = QStatusBar(self)
|
||||
self.statusbar.setSizeGripEnabled(True)
|
||||
self.setStatusBar(self.statusbar)
|
||||
self.statusLabel = QLabel(self)
|
||||
self.statusbar.addPermanentWidget(self.statusLabel, 1)
|
||||
|
||||
if self.app.prefs.mainWindowRect is not None and not self.app.prefs.mainWindowIsMaximized:
|
||||
self.setGeometry(self.app.prefs.mainWindowRect)
|
||||
if self.app.prefs.resultWindowIsMaximized:
|
||||
self.setWindowState(self.windowState() | Qt.WindowMaximized)
|
||||
if self.app.prefs.resultWindowRect is not None and not self.app.prefs.resultWindowIsMaximized:
|
||||
self.setGeometry(self.app.prefs.resultWindowRect)
|
||||
|
||||
# Platform-specific setup
|
||||
if sys.platform == 'linux2':
|
||||
@ -249,11 +199,6 @@ class MainWindow(QMainWindow):
|
||||
self.actionHardlinkMarked.setVisible(False)
|
||||
|
||||
#--- Private
|
||||
def _confirm(self, title, msg, default_button=QMessageBox.Yes):
|
||||
buttons = QMessageBox.Yes | QMessageBox.No
|
||||
answer = QMessageBox.question(self, title, msg, buttons, default_button)
|
||||
return answer == QMessageBox.Yes
|
||||
|
||||
def _load_columns(self):
|
||||
h = self.resultsView.horizontalHeader()
|
||||
h.setResizeMode(QHeaderView.Interactive)
|
||||
@ -270,19 +215,7 @@ class MainWindow(QMainWindow):
|
||||
colid = action.column_index
|
||||
action.setChecked(not h.isSectionHidden(colid))
|
||||
|
||||
#--- QWidget overrides
|
||||
def closeEvent(self, event):
|
||||
event.accept()
|
||||
if self.app.results.is_modified:
|
||||
title = "Unsaved results"
|
||||
msg = "You have unsaved results, do you really want to quit?"
|
||||
if not self._confirm(title, msg):
|
||||
event.ignore()
|
||||
|
||||
#--- Actions
|
||||
def aboutTriggered(self):
|
||||
self.app.show_about_box()
|
||||
|
||||
def actionsTriggered(self):
|
||||
self.actionsButton.showMenu()
|
||||
|
||||
@ -303,9 +236,6 @@ class MainWindow(QMainWindow):
|
||||
def cancelFilterTriggered(self):
|
||||
self.app.apply_filter('')
|
||||
|
||||
def checkForUpdateTriggered(self):
|
||||
QProcess.execute('updater.exe', ['/checknow'])
|
||||
|
||||
def clearIgnoreListTriggered(self):
|
||||
title = "Clear Ignore List"
|
||||
count = len(self.app.scanner.ignore_list)
|
||||
@ -313,7 +243,7 @@ class MainWindow(QMainWindow):
|
||||
QMessageBox.information(self, title, "Nothing to clear.")
|
||||
return
|
||||
msg = "Do you really want to remove all {0} items from the ignore list?".format(count)
|
||||
if self._confirm(title, msg, QMessageBox.No):
|
||||
if self.app.confirm(title, msg, QMessageBox.No):
|
||||
self.app.scanner.ignore_list.Clear()
|
||||
QMessageBox.information(self, title, "Ignore list cleared.")
|
||||
|
||||
@ -326,7 +256,7 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
title = "Delete duplicates"
|
||||
msg = "You are about to send {0} files to the recycle bin. Continue?".format(count)
|
||||
if self._confirm(title, msg):
|
||||
if self.app.confirm(title, msg):
|
||||
self.app.delete_marked()
|
||||
|
||||
def deltaTriggered(self):
|
||||
@ -335,9 +265,6 @@ class MainWindow(QMainWindow):
|
||||
def detailsTriggered(self):
|
||||
self.app.show_details()
|
||||
|
||||
def directoriesTriggered(self):
|
||||
self.app.show_directories()
|
||||
|
||||
def exportTriggered(self):
|
||||
h = self.resultsView.horizontalHeader()
|
||||
column_ids = []
|
||||
@ -354,17 +281,9 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
title = "Delete and hardlink duplicates"
|
||||
msg = "You are about to send {0} files to the trash and hardlink them afterwards. Continue?".format(count)
|
||||
if self._confirm(title, msg):
|
||||
if self.app.confirm(title, msg):
|
||||
self.app.delete_marked(replace_with_hardlinks=True)
|
||||
|
||||
def loadResultsTriggered(self):
|
||||
title = "Select a results file to load"
|
||||
files = "dupeGuru Results (*.dupeguru)"
|
||||
destination = QFileDialog.getOpenFileName(self, title, '', files)
|
||||
if destination:
|
||||
self.app.load_from(destination)
|
||||
self.recentResults.insertItem(destination)
|
||||
|
||||
def makeReferenceTriggered(self):
|
||||
self.app.make_selected_reference()
|
||||
|
||||
@ -383,9 +302,6 @@ class MainWindow(QMainWindow):
|
||||
def moveTriggered(self):
|
||||
self.app.copy_or_move_marked(False)
|
||||
|
||||
def openDebugLogTriggered(self):
|
||||
self.app.openDebugLog()
|
||||
|
||||
def openTriggered(self):
|
||||
self.app.open_selected()
|
||||
|
||||
@ -395,16 +311,13 @@ class MainWindow(QMainWindow):
|
||||
def preferencesTriggered(self):
|
||||
self.app.show_preferences()
|
||||
|
||||
def registerTrigerred(self):
|
||||
self.app.reg.ask_for_code()
|
||||
|
||||
def removeMarkedTriggered(self):
|
||||
count = self.app.results.mark_count
|
||||
if not count:
|
||||
return
|
||||
title = "Remove duplicates"
|
||||
msg = "You are about to remove {0} files from results. Continue?".format(count)
|
||||
if self._confirm(title, msg):
|
||||
if self.app.confirm(title, msg):
|
||||
self.app.remove_marked()
|
||||
|
||||
def removeSelectedTriggered(self):
|
||||
@ -422,23 +335,7 @@ class MainWindow(QMainWindow):
|
||||
destination = QFileDialog.getSaveFileName(self, title, '', files)
|
||||
if destination:
|
||||
self.app.save_as(destination)
|
||||
self.recentResults.insertItem(destination)
|
||||
|
||||
def scanTriggered(self):
|
||||
title = "Start a new scan"
|
||||
if len(self.app.results.groups) > 0:
|
||||
msg = "Are you sure you want to start a new duplicate scan?"
|
||||
if not self._confirm(title, msg):
|
||||
return
|
||||
try:
|
||||
self.app.start_scanning()
|
||||
except NoScannableFileError:
|
||||
msg = "The selected directories contain no scannable file."
|
||||
QMessageBox.warning(self, title, msg)
|
||||
self.app.show_directories()
|
||||
|
||||
def showHelpTriggered(self):
|
||||
self.app.show_help()
|
||||
self.app.recentResults.insertItem(destination)
|
||||
|
||||
#--- Events
|
||||
def appWillSavePrefs(self):
|
||||
@ -451,8 +348,8 @@ class MainWindow(QMainWindow):
|
||||
visible.append(not h.isSectionHidden(i))
|
||||
prefs.columns_width = widths
|
||||
prefs.columns_visible = visible
|
||||
prefs.mainWindowIsMaximized = self.isMaximized()
|
||||
prefs.mainWindowRect = self.geometry()
|
||||
prefs.resultWindowIsMaximized = self.isMaximized()
|
||||
prefs.resultWindowRect = self.geometry()
|
||||
|
||||
def columnToggled(self, action):
|
||||
colid = action.column_index
|
21
qt/base/util.py
Normal file
21
qt/base/util.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2011-01-15
|
||||
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
from PyQt4.QtGui import QPixmap, QIcon, QAction
|
||||
|
||||
def createActions(actions, target):
|
||||
# actions = [(name, shortcut, icon, desc, func)]
|
||||
for name, shortcut, icon, desc, func in actions:
|
||||
action = QAction(target)
|
||||
if icon:
|
||||
action.setIcon(QIcon(QPixmap(':/' + icon)))
|
||||
if shortcut:
|
||||
action.setShortcut(shortcut)
|
||||
action.setText(desc)
|
||||
action.triggered.connect(func)
|
||||
setattr(target, name, action)
|
@ -20,7 +20,7 @@ from core_pe.scanner import ScannerPE
|
||||
from ..base.app import DupeGuru as DupeGuruBase
|
||||
from .block import getblocks
|
||||
from .details_dialog import DetailsDialog
|
||||
from .main_window import MainWindow
|
||||
from .result_window import ResultWindow
|
||||
from .preferences import Preferences
|
||||
from .preferences_dialog import PreferencesDialog
|
||||
|
||||
@ -79,8 +79,8 @@ class DupeGuru(DupeGuruBase):
|
||||
def _create_details_dialog(self, parent):
|
||||
return DetailsDialog(parent, self)
|
||||
|
||||
def _create_main_window(self):
|
||||
return MainWindow(app=self)
|
||||
def _create_result_window(self):
|
||||
return ResultWindow(app=self)
|
||||
|
||||
def _create_preferences(self):
|
||||
return Preferences()
|
||||
|
@ -9,11 +9,11 @@
|
||||
from PyQt4.QtCore import SIGNAL
|
||||
from PyQt4.QtGui import QMessageBox, QAction
|
||||
|
||||
from ..base.main_window import MainWindow as MainWindowBase
|
||||
from ..base.result_window import ResultWindow as ResultWindowBase
|
||||
|
||||
class MainWindow(MainWindowBase):
|
||||
class ResultWindow(ResultWindowBase):
|
||||
def _setupUi(self):
|
||||
MainWindowBase._setupUi(self)
|
||||
ResultWindowBase._setupUi(self)
|
||||
self.actionClearPictureCache = QAction("Clear Picture Cache", self)
|
||||
self.menuFile.insertAction(self.actionClearIgnoreList, self.actionClearPictureCache)
|
||||
self.connect(self.actionClearPictureCache, SIGNAL("triggered()"), self.clearPictureCacheTriggered)
|
||||
@ -21,7 +21,7 @@ class MainWindow(MainWindowBase):
|
||||
def clearPictureCacheTriggered(self):
|
||||
title = "Clear Picture Cache"
|
||||
msg = "Do you really want to remove all your cached picture analysis?"
|
||||
if self._confirm(title, msg, QMessageBox.No):
|
||||
if self.app.confirm(title, msg, QMessageBox.No):
|
||||
self.app.scanner.clear_picture_cache()
|
||||
QMessageBox.information(self, title, "Picture cache cleared.")
|
||||
|
Loading…
x
Reference in New Issue
Block a user