mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-05-07 09:19:50 +00:00
Compare commits
17 Commits
6a2c1eb293
...
6a03e1e399
Author | SHA1 | Date | |
---|---|---|---|
6a03e1e399 | |||
ae51842007 | |||
ab6acd9e88 | |||
|
5553414205 | ||
|
b138dfad33 | ||
|
348ce95f83 | ||
|
288023d03e | ||
|
7740dfca0e | ||
|
c1d94d6771 | ||
|
6bc619055e | ||
|
32d66cd19b | ||
|
735ba2fd0e | ||
|
b16b6ecf4d | ||
|
2875448c71 | ||
|
51b76385c0 | ||
|
b9f8dd6ea0 | ||
|
6623b04403 |
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -24,8 +24,8 @@ A clear and concise description of what you expected to happen.
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Windows 10 / OSX 10.15 / Ubuntu 20.04]
|
||||
- Version [e.g. 4.0.4]
|
||||
- OS: [e.g. Windows 10 / OSX 10.15 / Ubuntu 20.04 / Arch Linux]
|
||||
- Version [e.g. 4.1.0]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here. You may include the debug log although it is normally best to attach it as a file.
|
||||
|
52
README.md
52
README.md
@ -1,19 +1,21 @@
|
||||
# dupeGuru
|
||||
|
||||
[dupeGuru][dupeguru] is a cross-platform (Linux, OS X, Windows) GUI tool to find duplicate files in
|
||||
a system. It's written mostly in Python 3 and has the peculiarity of using
|
||||
a system. It is written mostly in Python 3 and has the peculiarity of using
|
||||
[multiple GUI toolkits][cross-toolkit], all using the same core Python code. On OS X, the UI layer
|
||||
is written in Objective-C and uses Cocoa. On Linux, it's written in Python and uses Qt5.
|
||||
is written in Objective-C and uses Cocoa. On Linux, it is written in Python and uses Qt5.
|
||||
|
||||
The Cocoa UI of dupeGuru is hosted in a separate repo: https://github.com/hsoft/dupeguru-cocoa
|
||||
The Cocoa UI of dupeGuru is hosted in a separate repo: https://github.com/arsenetar/dupeguru-cocoa
|
||||
|
||||
## Current status
|
||||
|
||||
Development has been slow this past year, however very close to getting all the different 4.0.4 releases posted. Most of the work this past year (2019) has been towards packaging the application and issues related to that.
|
||||
2020: various bug fixes and small UI improvements have been added. Packaging for MacOS is still a problem.
|
||||
|
||||
Still looking for additional help especially with regards to:
|
||||
- OSX maintenance (reproducing bugs & cocoa version)
|
||||
- Linux maintenance (reproducing bugs)
|
||||
* OSX maintenance: reproducing bugs & cocoa version, building package with Cocoa UI.
|
||||
* Linux maintenance: reproducing bugs, maintaining PPA repository, Debian package.
|
||||
* Translations: updating missing strings.
|
||||
* Documentation: keeping it up-to-date.
|
||||
|
||||
## Contents of this folder
|
||||
|
||||
@ -31,26 +33,44 @@ This folder contains the source for dupeGuru. Its documentation is in `help`, bu
|
||||
|
||||
## How to build dupeGuru from source
|
||||
|
||||
### Windows
|
||||
### Windows & macOS specific additional instructions
|
||||
For windows instructions see the [Windows Instructions](Windows.md).
|
||||
For macos instructions (qt version) see the [macOS Instructions](macos.md).
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* [Python 3.5+][python]
|
||||
* [Python 3.6+][python]
|
||||
* PyQt5
|
||||
|
||||
### make
|
||||
### Building with Make
|
||||
dupeGuru comes with a makefile that can be used to build and run:
|
||||
|
||||
dupeGuru is built with "make":
|
||||
$ make && make run
|
||||
|
||||
$ make
|
||||
$ make run
|
||||
### Building without Make
|
||||
|
||||
### Generate Debian/Ubuntu package
|
||||
$ cd <dupeGuru directory>
|
||||
$ python3 -m venv --system-site-packages .\env
|
||||
$ source .\env\bin\activate
|
||||
$ pip install -r requirements.txt
|
||||
$ python build.py
|
||||
$ python run.py
|
||||
|
||||
$ bash -c "python3 -m venv --system-site-packages env && source env/bin/activate && pip install -r requirements.txt && python3 build.py --clean && python3 package.py"
|
||||
### Generating Debian/Ubuntu package
|
||||
To generate packages the extra requirements in requirements-extra.txt must be installed, the
|
||||
steps are as follows:
|
||||
|
||||
### Running tests
|
||||
$ cd <dupeGuru directory>
|
||||
$ python3 -m venv --system-site-packages .\env
|
||||
$ source .\env\bin\activate
|
||||
$ pip install -r requirements.txt -r requirements-extra.txt
|
||||
$ python build.py --clean
|
||||
$ python package.py
|
||||
|
||||
This can be made a one-liner (once in the directory) as:
|
||||
|
||||
$ bash -c "python3 -m venv --system-site-packages env && source env/bin/activate && pip install -r requirements.txt -r requirements-extra.txt && python build.py --clean && python package.py"
|
||||
|
||||
## Running tests
|
||||
|
||||
The complete test suite is run with [Tox 1.7+][tox]. If you have it installed system-wide, you
|
||||
don't even need to set up a virtualenv. Just `cd` into the root project folder and run `tox`.
|
||||
|
@ -1,2 +1,2 @@
|
||||
__version__ = "4.0.4"
|
||||
__version__ = "4.1.0"
|
||||
__appname__ = "dupeGuru"
|
||||
|
@ -55,7 +55,8 @@ class ExcludeListDialogCore:
|
||||
"""Sets property on row to highlight if its regex matches test_string supplied."""
|
||||
matched = False
|
||||
for row in self.exclude_list_table.rows:
|
||||
if self.exclude_list.get_compiled(row.regex).match(test_string):
|
||||
compiled_regex = self.exclude_list.get_compiled(row.regex)
|
||||
if compiled_regex and compiled_regex.match(test_string):
|
||||
matched = True
|
||||
row.highlight = True
|
||||
else:
|
||||
|
@ -1,3 +1,29 @@
|
||||
=== 4.1.0 (2020-12-29)
|
||||
|
||||
* Use tabs instead of separate windows (#688)
|
||||
* Show the shortcut for "mark selected" in results dialog (#656, #641)
|
||||
* Add image comparison features to details dialog (#683)
|
||||
* Add the ability to use regex based exclusion filters (#705)
|
||||
* Change reference row background color, and allow user to adjust the color (#701)
|
||||
* Save / Load directories as XML (#706)
|
||||
* Workaround for EXIF IFD type mismatch in parsing function (#630, #698)
|
||||
* Progress dialog stuck at "Verified X/X matches" (#693, #694)
|
||||
* Fix word wrap in ignore list dialog (#687)
|
||||
* Fix issue with result window action on creation (#685)
|
||||
* Colorize details table differences, allow moving rows (#682)
|
||||
* Fix loading Result of 'Scan Type: Folders' shows only '---' in every table cell (#677, #676)
|
||||
* Fix issue with details and results dialog row trimming (#655, #654)
|
||||
* Add option to enable/disable bold font (#646, #314)
|
||||
* Use relative icon path for themes to override more easily (#746)
|
||||
* Fix issues with Python 3.8 compatibility (#665)
|
||||
* Fix flake8 issues (#672)
|
||||
* Update to use newer pytest and expand flake8 checking, cleanup various Deprecation Warnings
|
||||
* Add warnings to packaging script when files are not built (#691)
|
||||
* Use relative icon path for themes to override more easily (#746)
|
||||
* Update Packaging for Ubuntu (#593)
|
||||
* Minor Build Updates (#627, #575, #628, #614)
|
||||
* Update CI builds and add windows CI (#572, #669)
|
||||
|
||||
=== 4.0.4 (2019-05-13)
|
||||
|
||||
* Update qt/platform.py to support other Unix style OSes (#444)
|
||||
|
78
qt/app.py
78
qt/app.py
@ -65,20 +65,25 @@ class DupeGuru(QObject):
|
||||
self.recentResults.mustOpenItem.connect(self.model.load_from)
|
||||
self.resultWindow = None
|
||||
if self.use_tabs:
|
||||
self.main_window = TabBarWindow(self) if not self.prefs.tabs_default_pos else TabWindow(self)
|
||||
self.main_window = (
|
||||
TabBarWindow(self)
|
||||
if not self.prefs.tabs_default_pos
|
||||
else TabWindow(self)
|
||||
)
|
||||
parent_window = self.main_window
|
||||
self.directories_dialog = self.main_window.createPage("DirectoriesDialog", app=self)
|
||||
self.directories_dialog = self.main_window.createPage(
|
||||
"DirectoriesDialog", app=self
|
||||
)
|
||||
self.main_window.addTab(
|
||||
self.directories_dialog, "Directories", switch=False)
|
||||
self.directories_dialog, "Directories", switch=False
|
||||
)
|
||||
self.actionDirectoriesWindow.setEnabled(False)
|
||||
else: # floating windows only
|
||||
self.main_window = None
|
||||
self.directories_dialog = DirectoriesDialog(self)
|
||||
parent_window = self.directories_dialog
|
||||
|
||||
self.progress_window = ProgressWindow(
|
||||
parent_window, self.model.progress_window
|
||||
)
|
||||
self.progress_window = ProgressWindow(parent_window, self.model.progress_window)
|
||||
self.problemDialog = ProblemDialog(
|
||||
parent=parent_window, model=self.model.problem_dialog
|
||||
)
|
||||
@ -86,22 +91,25 @@ class DupeGuru(QObject):
|
||||
self.ignoreListDialog = self.main_window.createPage(
|
||||
"IgnoreListDialog",
|
||||
parent=self.main_window,
|
||||
model=self.model.ignore_list_dialog)
|
||||
model=self.model.ignore_list_dialog,
|
||||
)
|
||||
|
||||
self.excludeListDialog = self.main_window.createPage(
|
||||
"ExcludeListDialog",
|
||||
app=self,
|
||||
parent=self.main_window,
|
||||
model=self.model.exclude_list_dialog)
|
||||
model=self.model.exclude_list_dialog,
|
||||
)
|
||||
else:
|
||||
self.ignoreListDialog = IgnoreListDialog(
|
||||
parent=parent_window, model=self.model.ignore_list_dialog)
|
||||
parent=parent_window, model=self.model.ignore_list_dialog
|
||||
)
|
||||
self.excludeDialog = ExcludeListDialog(
|
||||
app=self, parent=parent_window, model=self.model.exclude_list_dialog)
|
||||
app=self, parent=parent_window, model=self.model.exclude_list_dialog
|
||||
)
|
||||
|
||||
self.deletionOptions = DeletionOptions(
|
||||
parent=parent_window,
|
||||
model=self.model.deletion_options
|
||||
parent=parent_window, model=self.model.deletion_options
|
||||
)
|
||||
self.about_box = AboutBox(parent_window, self)
|
||||
|
||||
@ -129,7 +137,13 @@ class DupeGuru(QObject):
|
||||
self.preferencesTriggered,
|
||||
),
|
||||
("actionIgnoreList", "", "", tr("Ignore List"), self.ignoreListTriggered),
|
||||
("actionDirectoriesWindow", "", "", tr("Directories"), self.showDirectoriesWindow),
|
||||
(
|
||||
"actionDirectoriesWindow",
|
||||
"",
|
||||
"",
|
||||
tr("Directories"),
|
||||
self.showDirectoriesWindow,
|
||||
),
|
||||
(
|
||||
"actionClearPictureCache",
|
||||
"Ctrl+Shift+P",
|
||||
@ -137,7 +151,13 @@ class DupeGuru(QObject):
|
||||
tr("Clear Picture Cache"),
|
||||
self.clearPictureCacheTriggered,
|
||||
),
|
||||
("actionExcludeList", "", "", tr("Exclusion Filters"), self.excludeListTriggered),
|
||||
(
|
||||
"actionExcludeList",
|
||||
"",
|
||||
"",
|
||||
tr("Exclusion Filters"),
|
||||
self.excludeListTriggered,
|
||||
),
|
||||
("actionShowHelp", "F1", "", tr("dupeGuru Help"), self.showHelpTriggered),
|
||||
("actionAbout", "", "", tr("About dupeGuru"), self.showAboutBoxTriggered),
|
||||
(
|
||||
@ -232,8 +252,7 @@ class DupeGuru(QObject):
|
||||
if self.resultWindow is not None:
|
||||
if self.use_tabs:
|
||||
if self.main_window.indexOfWidget(self.resultWindow) < 0:
|
||||
self.main_window.addTab(
|
||||
self.resultWindow, "Results", switch=True)
|
||||
self.main_window.addTab(self.resultWindow, "Results", switch=True)
|
||||
return
|
||||
self.main_window.showTab(self.resultWindow)
|
||||
else:
|
||||
@ -265,9 +284,11 @@ class DupeGuru(QObject):
|
||||
"scanning have accented letters, you'll probably get a crash. It is advised that "
|
||||
"you set your system locale properly."
|
||||
)
|
||||
QMessageBox.warning(self.main_window if self.main_window
|
||||
else self.directories_dialog,
|
||||
"Wrong Locale", msg)
|
||||
QMessageBox.warning(
|
||||
self.main_window if self.main_window else self.directories_dialog,
|
||||
"Wrong Locale",
|
||||
msg,
|
||||
)
|
||||
|
||||
def clearPictureCacheTriggered(self):
|
||||
title = tr("Clear Picture Cache")
|
||||
@ -293,7 +314,9 @@ class DupeGuru(QObject):
|
||||
"""Add tab for dialog, name the tab with desc_string, then show it."""
|
||||
index = self.main_window.indexOfWidget(dialog)
|
||||
# Create the tab if it doesn't exist already
|
||||
if index < 0: # or (not dialog.isVisible() and not self.main_window.isTabVisible(index)):
|
||||
if (
|
||||
index < 0
|
||||
): # or (not dialog.isVisible() and not self.main_window.isTabVisible(index)):
|
||||
index = self.main_window.addTab(dialog, desc_string, switch=True)
|
||||
# Show the tab for that widget
|
||||
self.main_window.setCurrentIndex(index)
|
||||
@ -304,8 +327,7 @@ class DupeGuru(QObject):
|
||||
|
||||
def preferencesTriggered(self):
|
||||
preferences_dialog = self._get_preferences_dialog_class()(
|
||||
self.main_window if self.main_window else self.directories_dialog,
|
||||
self
|
||||
self.main_window if self.main_window else self.directories_dialog, self
|
||||
)
|
||||
preferences_dialog.load()
|
||||
result = preferences_dialog.exec()
|
||||
@ -333,7 +355,7 @@ class DupeGuru(QObject):
|
||||
if op.exists(help_path):
|
||||
url = QUrl.fromLocalFile(help_path)
|
||||
else:
|
||||
url = QUrl("https://www.hardcoded.net/dupeguru/help/en/")
|
||||
url = QUrl("https://dupeguru.voltaicideas.net/help/en/")
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def handleSIGTERM(self):
|
||||
@ -354,8 +376,7 @@ class DupeGuru(QObject):
|
||||
return self.confirm("", prompt)
|
||||
|
||||
def create_results_window(self):
|
||||
"""Creates resultWindow and details_dialog depending on the selected ``app_mode``.
|
||||
"""
|
||||
"""Creates resultWindow and details_dialog depending on the selected ``app_mode``."""
|
||||
if self.details_dialog is not None:
|
||||
# The object is not deleted entirely, avoid saving its geometry in the future
|
||||
# self.willSavePrefs.disconnect(self.details_dialog.appWillSavePrefs)
|
||||
@ -367,10 +388,13 @@ class DupeGuru(QObject):
|
||||
if self.resultWindow is not None:
|
||||
self.resultWindow.close()
|
||||
# This is better for tabs, as it takes care of duplicate items in menu bar
|
||||
self.resultWindow.deleteLater() if self.use_tabs else self.resultWindow.setParent(None)
|
||||
self.resultWindow.deleteLater() if self.use_tabs else self.resultWindow.setParent(
|
||||
None
|
||||
)
|
||||
if self.use_tabs:
|
||||
self.resultWindow = self.main_window.createPage(
|
||||
"ResultWindow", parent=self.main_window, app=self)
|
||||
"ResultWindow", parent=self.main_window, app=self
|
||||
)
|
||||
else: # We don't use a tab widget, regular floating QMainWindow
|
||||
self.resultWindow = ResultWindow(self.directories_dialog, self)
|
||||
self.directories_dialog._updateActionsState()
|
||||
|
@ -118,9 +118,7 @@ class ExcludeListDialog(QDialog):
|
||||
return
|
||||
# if at least one row matched, we know whether table is highlighted or not
|
||||
self._row_matched = self.model.test_string(input_text)
|
||||
# FIXME There is a bug on Windows (7) where the table rows don't get
|
||||
# repainted until the table receives a mouse click event.
|
||||
self.tableView.update()
|
||||
self.table.refresh()
|
||||
|
||||
input_regex = self.inputLine.text()
|
||||
if not input_regex:
|
||||
@ -148,7 +146,7 @@ class ExcludeListDialog(QDialog):
|
||||
if self._row_matched:
|
||||
self._row_matched = False
|
||||
self.model.reset_rows_highlight()
|
||||
self.tableView.update()
|
||||
self.table.refresh()
|
||||
|
||||
def display_help_message(self):
|
||||
self.app.show_message(tr("""\
|
||||
|
@ -58,9 +58,10 @@ class ErrorReportDialog(QDialog):
|
||||
self.verticalLayout.addWidget(self.errorTextEdit)
|
||||
msg = tr(
|
||||
"Error reports should be reported as Github issues. You can copy the error traceback "
|
||||
"above and paste it in a new issue (bonus point if you run a search to make sure the "
|
||||
"issue doesn't already exist). What usually really helps is if you add a description "
|
||||
"of how you got the error. Thanks!"
|
||||
"above and paste it in a new issue.\n\nPlease make sure to run a search for any already "
|
||||
"existing issues beforehand. Also make sure to test the very latest version available from the repository, "
|
||||
"since the bug you are experiencing might have already been patched.\n\n"
|
||||
"What usually really helps is if you add a description of how you got the error. Thanks!"
|
||||
"\n\n"
|
||||
"Although the application should continue to run after this error, it may be in an "
|
||||
"unstable state, so it is recommended that you restart the application."
|
||||
|
@ -48,9 +48,9 @@ SetCompressor /SOLID lzma
|
||||
!define APPLICENSE "LICENSE" ; License is not in build directory
|
||||
!define APPICON "images\dgse_logo.ico" ; nor is the icon
|
||||
!define DISTDIR "dist"
|
||||
!define HELPURL "http://www.hardcoded.net/support/"
|
||||
!define UPDATEURL "http://www.hardcoded.net/dupeguru/"
|
||||
!define ABOUTURL "http://www.hardcoded.net/dupeguru/"
|
||||
!define HELPURL "https://github.com/arsenetar/dupeguru/issues"
|
||||
!define UPDATEURL "https://dupeguru.voltaicideas.net/"
|
||||
!define ABOUTURL "https://dupeguru.voltaicideas.net/"
|
||||
|
||||
; Static Defines
|
||||
!define UNINSTALLREGBASE "Software\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||
|
Loading…
x
Reference in New Issue
Block a user