17 Commits

Author SHA1 Message Date
  Andrew Senetar 6a03e1e399
Update URLs 3 months ago
  Andrew Senetar ae51842007
Update README.md 3 months ago
  Andrew Senetar ab6acd9e88
Merge pull request #733 from glubsy/dev 3 months ago
  glubsy 5553414205 Fix updating QTableView on input 3 months ago
  glubsy b138dfad33 Fix exception when testing invalid regex 3 months ago
  glubsy 348ce95f83 Remove comment 3 months ago
  glubsy 288023d03e Update changelog 3 months ago
  glubsy 7740dfca0e Update Readme 3 months ago
  glubsy c1d94d6771 Merge branch 'master' into dev 3 months ago
  glubsy 6bc619055e Change version to 4.1.0 4 months ago
  glubsy 32d66cd19b Move up to 4.0.5 5 months ago
  glubsy 735ba2fd0e Update error dialog traceback message for users 5 months ago
  glubsy b16b6ecf4d Fix error after merging branches 5 months ago
  glubsy 2875448c71 Merge branch 'save_directories' into dev 5 months ago
  glubsy 51b76385c0 Merge branch 'exclude_list' into dev 5 months ago
  glubsy b9f8dd6ea0 Merge branch 'PR_ref_row_background_color' into dev 5 months ago
  glubsy 6623b04403 Merge branch 'details_dialog_improvements' into dev 5 months ago
9 changed files with 127 additions and 57 deletions
Split View
  1. +2
    -2
      .github/ISSUE_TEMPLATE/bug_report.md
  2. +36
    -16
      README.md
  3. +1
    -1
      core/__init__.py
  4. +2
    -1
      core/gui/exclude_list_dialog.py
  5. +26
    -0
      help/changelog
  6. +51
    -27
      qt/app.py
  7. +2
    -4
      qt/exclude_list_dialog.py
  8. +4
    -3
      qtlib/error_report_dialog.py
  9. +3
    -3
      setup.nsi

+ 2
- 2
.github/ISSUE_TEMPLATE/bug_report.md View File

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

+ 36
- 16
README.md View File

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

$ make && make run

### Building without Make

$ 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

dupeGuru is built with "make":
### Generating Debian/Ubuntu package
To generate packages the extra requirements in requirements-extra.txt must be installed, the
steps are as follows:

$ make
$ make run
$ 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

### Generate Debian/Ubuntu package
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 && python3 build.py --clean && python3 package.py"
$ 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
## 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
- 1
core/__init__.py View File

@@ -1,2 +1,2 @@
__version__ = "4.0.4"
__version__ = "4.1.0"
__appname__ = "dupeGuru"

+ 2
- 1
core/gui/exclude_list_dialog.py View File

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


+ 26
- 0
help/changelog View File

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


+ 51
- 27
qt/app.py View File

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


+ 2
- 4
qt/exclude_list_dialog.py View File

@@ -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("""\


+ 4
- 3
qtlib/error_report_dialog.py View File

@@ -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."


+ 3
- 3
setup.nsi View File

@@ -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…
Cancel
Save