1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2025-05-08 09:49:51 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
5a4958cff9
Update translation .pot files 2021-08-17 21:18:47 -05:00
be10b462fc
Add portable mode
If settings.ini is present next to the executable, will run in portable mode.
This results in settings, data, and cache all being in same folder as dupeGuru.
2021-08-17 21:12:32 -05:00
d62b13bcdb
Removing travis
- All CI is now covered by Github Actions
- Remove .travis.yml
- Remove tox-travis in requirements-extra.txt
2021-08-17 18:16:20 -05:00
13 changed files with 113 additions and 107 deletions

View File

@ -1,27 +0,0 @@
sudo: false
language: python
install:
- pip3 install -r requirements.txt -r requirements-extra.txt
script: tox
matrix:
include:
- os: "linux"
dist: "xenial"
python: "3.6"
- os: "linux"
dist: "xenial"
python: "3.7"
- os: "linux"
dist: "focal"
python: "3.8"
- os: "linux"
dist: "focal"
python: "3.9"
- os: "windows"
language: shell
python: "3.9"
env: "PATH=/c/python39:/c/python39/Scripts:$PATH"
before_install:
- choco install python --version=3.9.6
- cp /c/python39/python.exe /c/python39/python3.exe
script: tox -e py39

View File

@ -126,13 +126,13 @@ class DupeGuru(Broadcaster):
PICTURE_CACHE_TYPE = "sqlite" # set to 'shelve' for a ShelveCache
def __init__(self, view):
def __init__(self, view, portable=False):
if view.get_default(DEBUG_MODE_PREFERENCE):
logging.getLogger().setLevel(logging.DEBUG)
logging.debug("Debug mode enabled")
Broadcaster.__init__(self)
self.view = view
self.appdata = desktop.special_folder_path(desktop.SpecialFolder.AppData, appname=self.NAME)
self.appdata = desktop.special_folder_path(desktop.SpecialFolder.AppData, appname=self.NAME, portable=portable)
if not op.exists(self.appdata):
os.makedirs(self.appdata)
self.app_mode = AppMode.Standard

View File

@ -5,6 +5,8 @@
# http://www.gnu.org/licenses/gpl-3.0.html
import time
import sys
import os
from hscommon.util import format_time_decimal
@ -58,3 +60,7 @@ def fix_surrogate_encoding(s, encoding="utf-8"):
return s.encode(encoding, "replace").decode(encoding)
else:
return s
def executable_folder():
return os.path.dirname(os.path.abspath(sys.argv[0]))

View File

@ -30,7 +30,7 @@ def reveal_path(path):
_reveal_path(str(path))
def special_folder_path(special_folder, appname=None):
def special_folder_path(special_folder, appname=None, portable=False):
"""Returns the path of ``special_folder``.
``special_folder`` is a SpecialFolder.* const. The result is the special folder for the current
@ -38,7 +38,7 @@ def special_folder_path(special_folder, appname=None):
You can override the application name with ``appname``. This argument is ingored under Qt.
"""
return _special_folder_path(special_folder, appname)
return _special_folder_path(special_folder, appname, portable=portable)
try:
@ -54,7 +54,7 @@ try:
_open_path = proxy.openPath_
_reveal_path = proxy.revealPath_
def _special_folder_path(special_folder, appname=None):
def _special_folder_path(special_folder, appname=None, portable=False):
if special_folder == SpecialFolder.Cache:
base = proxy.getCachePath()
else:
@ -68,6 +68,9 @@ except ImportError:
try:
from PyQt5.QtCore import QUrl, QStandardPaths
from PyQt5.QtGui import QDesktopServices
from qtlib.util import getAppData
from core.util import executable_folder
from hscommon.plat import ISWINDOWS
def _open_url(url):
QDesktopServices.openUrl(QUrl(url))
@ -79,12 +82,15 @@ except ImportError:
def _reveal_path(path):
_open_path(op.dirname(str(path)))
def _special_folder_path(special_folder, appname=None):
def _special_folder_path(special_folder, appname=None, portable=False):
if special_folder == SpecialFolder.Cache:
qtfolder = QStandardPaths.CacheLocation
if ISWINDOWS and portable:
folder = op.join(executable_folder(), "cache")
else:
folder = QStandardPaths.standardLocations(QStandardPaths.CacheLocation)[0]
else:
qtfolder = QStandardPaths.DataLocation
return QStandardPaths.standardLocations(qtfolder)[0]
folder = getAppData(portable)
return folder
except ImportError:
# We're either running tests, and these functions don't matter much or we're in a really
@ -97,5 +103,5 @@ except ImportError:
def _reveal_path(path):
pass
def _special_folder_path(special_folder, appname=None):
def _special_folder_path(special_folder, appname=None, portable=False):
return "/tmp"

View File

@ -36,95 +36,95 @@ msgstr ""
msgid "Sending to Trash"
msgstr ""
#: core\app.py:308
#: core\app.py:290
msgid "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
msgstr ""
#: core\app.py:318
#: core\app.py:300
msgid "No duplicates found."
msgstr ""
#: core\app.py:333
#: core\app.py:315
msgid "All marked files were copied successfully."
msgstr ""
#: core\app.py:334
#: core\app.py:316
msgid "All marked files were moved successfully."
msgstr ""
#: core\app.py:335
#: core\app.py:317
msgid "All marked files were successfully sent to Trash."
msgstr ""
#: core\app.py:343
#: core\app.py:323
msgid "Could not load file: {}"
msgstr ""
#: core\app.py:399
#: core\app.py:379
msgid "'{}' already is in the list."
msgstr ""
#: core\app.py:401
#: core\app.py:381
msgid "'{}' does not exist."
msgstr ""
#: core\app.py:410
#: core\app.py:389
msgid "All selected %d matches are going to be ignored in all subsequent scans. Continue?"
msgstr ""
#: core\app.py:486
#: core\app.py:463
msgid "Select a directory to copy marked files to"
msgstr ""
#: core\app.py:487
#: core\app.py:465
msgid "Select a directory to move marked files to"
msgstr ""
#: core\app.py:527
#: core\app.py:504
msgid "Select a destination for your exported CSV"
msgstr ""
#: core\app.py:534 core\app.py:803 core\app.py:813
#: core\app.py:510 core\app.py:764 core\app.py:774
msgid "Couldn't write to file: {}"
msgstr ""
#: core\app.py:559
#: core\app.py:533
msgid "You have no custom command set up. Set it up in your preferences."
msgstr ""
#: core\app.py:727 core\app.py:740
#: core\app.py:691 core\app.py:703
msgid "You are about to remove %d files from results. Continue?"
msgstr ""
#: core\app.py:776
#: core\app.py:739
msgid "{} duplicate groups were changed by the re-prioritization."
msgstr ""
#: core\app.py:823
#: core\app.py:783
msgid "The selected directories contain no scannable file."
msgstr ""
#: core\app.py:837
#: core\app.py:796
msgid "Collecting files to scan"
msgstr ""
#: core\app.py:893
#: core\app.py:843
msgid "%s (%d discarded)"
msgstr ""
#: core\engine.py:255 core\engine.py:299
#: core\engine.py:251 core\engine.py:294
msgid "0 matches found"
msgstr ""
#: core\engine.py:273 core\engine.py:307
#: core\engine.py:269 core\engine.py:306
msgid "%d matches found"
msgstr ""
#: core\gui\deletion_options.py:73
#: core\gui\deletion_options.py:71
msgid "You are sending {} file(s) to the Trash."
msgstr ""
#: core\gui\exclude_list_table.py:15
#: core\gui\exclude_list_table.py:14
msgid "Regular Expressions"
msgstr ""
@ -156,15 +156,15 @@ msgstr ""
msgid "Analyzed %d/%d pictures"
msgstr ""
#: core\pe\matchblock.py:181
#: core\pe\matchblock.py:177
msgid "Performed %d/%d chunk matches"
msgstr ""
#: core\pe\matchblock.py:191
#: core\pe\matchblock.py:185
msgid "Preparing for matching"
msgstr ""
#: core\pe\matchblock.py:244
#: core\pe\matchblock.py:234
msgid "Verified %d/%d matches"
msgstr ""
@ -212,11 +212,11 @@ msgstr ""
msgid "Oldest"
msgstr ""
#: core\results.py:144
#: core\results.py:134
msgid "%d / %d (%s / %s) duplicates marked."
msgstr ""
#: core\results.py:151
#: core\results.py:141
msgid " filter: %s"
msgstr ""

View File

@ -905,3 +905,11 @@ msgstr ""
#: qt\preferences_dialog.py:286
msgid "Display"
msgstr ""
#: qt\se\preferences_dialog.py:70
msgid "Partially hash files bigger than"
msgstr ""
#: qt\se\preferences_dialog.py:80
msgid "MB"
msgstr ""

View File

@ -52,7 +52,7 @@ class DupeGuru(QObject):
# Enable tabs instead of separate floating windows for each dialog
# Could be passed as an argument to this class if we wanted
self.use_tabs = True
self.model = DupeGuruModel(view=self)
self.model = DupeGuruModel(view=self, portable=self.prefs.portable)
self._setup()
# --- Private

View File

@ -29,6 +29,7 @@ class Preferences(PreferencesBase):
self.language = get("Language", self.language)
if not self.language and trans.installed_lang:
self.language = trans.installed_lang
self.portable = get("Portable", False)
self.tableFontSize = get("TableFontSize", self.tableFontSize)
self.reference_bold_font = get("ReferenceBoldFont", self.reference_bold_font)
@ -138,6 +139,7 @@ class Preferences(PreferencesBase):
set_("DestinationType", self.destination_type)
set_("CustomCommand", self.custom_command)
set_("Language", self.language)
set_("Portable", self.portable)
set_("TableFontSize", self.tableFontSize)
set_("ReferenceBoldFont", self.reference_bold_font)

View File

@ -4,27 +4,27 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: utf-8\n"
#: qtlib\about_box.py:43
#: qtlib\about_box.py:37
msgid "About {}"
msgstr ""
#: qtlib\about_box.py:65
#: qtlib\about_box.py:57
msgid "Version {}"
msgstr ""
#: qtlib\about_box.py:70
#: qtlib\about_box.py:61
msgid "Licensed under GPLv3"
msgstr ""
#: qtlib\error_report_dialog.py:49
#: qtlib\error_report_dialog.py:47
msgid "Error Report"
msgstr ""
#: qtlib\error_report_dialog.py:53
#: qtlib\error_report_dialog.py:51
msgid "Something went wrong. How about reporting the error?"
msgstr ""
#: qtlib\error_report_dialog.py:59
#: qtlib\error_report_dialog.py:57
msgid ""
"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n"
"\n"
@ -35,83 +35,83 @@ msgid ""
"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."
msgstr ""
#: qtlib\error_report_dialog.py:75
#: qtlib\error_report_dialog.py:73
msgid "Close"
msgstr ""
#: qtlib\error_report_dialog.py:79
#: qtlib\error_report_dialog.py:77
msgid "Go to Github"
msgstr ""
#: qtlib\preferences.py:23
#: qtlib\preferences.py:24
msgid "Czech"
msgstr ""
#: qtlib\preferences.py:24
#: qtlib\preferences.py:25
msgid "German"
msgstr ""
#: qtlib\preferences.py:25
#: qtlib\preferences.py:26
msgid "Greek"
msgstr ""
#: qtlib\preferences.py:26
#: qtlib\preferences.py:27
msgid "English"
msgstr ""
#: qtlib\preferences.py:27
#: qtlib\preferences.py:28
msgid "Spanish"
msgstr ""
#: qtlib\preferences.py:28
#: qtlib\preferences.py:29
msgid "French"
msgstr ""
#: qtlib\preferences.py:29
#: qtlib\preferences.py:30
msgid "Armenian"
msgstr ""
#: qtlib\preferences.py:30
#: qtlib\preferences.py:31
msgid "Italian"
msgstr ""
#: qtlib\preferences.py:31
#: qtlib\preferences.py:32
msgid "Japanese"
msgstr ""
#: qtlib\preferences.py:32
#: qtlib\preferences.py:33
msgid "Korean"
msgstr ""
#: qtlib\preferences.py:33
#: qtlib\preferences.py:34
msgid "Dutch"
msgstr ""
#: qtlib\preferences.py:34
#: qtlib\preferences.py:35
msgid "Polish"
msgstr ""
#: qtlib\preferences.py:35
#: qtlib\preferences.py:36
msgid "Brazilian"
msgstr ""
#: qtlib\preferences.py:36
#: qtlib\preferences.py:37
msgid "Russian"
msgstr ""
#: qtlib\preferences.py:37
#: qtlib\preferences.py:38
msgid "Turkish"
msgstr ""
#: qtlib\preferences.py:38
#: qtlib\preferences.py:39
msgid "Ukrainian"
msgstr ""
#: qtlib\preferences.py:39
#: qtlib\preferences.py:40
msgid "Vietnamese"
msgstr ""
#: qtlib\preferences.py:40
#: qtlib\preferences.py:41
msgid "Chinese (Simplified)"
msgstr ""

View File

@ -12,6 +12,7 @@ from PyQt5.QtWidgets import QDockWidget
from hscommon.trans import trget
from hscommon.util import tryint
from hscommon.plat import ISWINDOWS
from core.util import executable_folder
from os import path as op
@ -68,18 +69,25 @@ def adjust_after_deserialization(v):
return v
def createQSettings():
def create_qsettings():
# Create a QSettings instance with the correct arguments.
# On windows use an ini file in the AppDataLocation instead of registry if possible as it
# makes it easier for a user to clear it out when there are issues.
if ISWINDOWS:
Locations = QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)
if Locations:
return QSettings(op.join(Locations[0], "settings.ini"), QSettings.IniFormat)
config_location = op.join(executable_folder(), "settings.ini")
if op.isfile(config_location):
settings = QSettings(config_location, QSettings.IniFormat)
settings.setValue("Portable", True)
elif ISWINDOWS:
# On windows use an ini file in the AppDataLocation instead of registry if possible as it
# makes it easier for a user to clear it out when there are issues.
locations = QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)
if locations:
settings = QSettings(op.join(locations[0], "settings.ini"), QSettings.IniFormat)
else:
return QSettings()
settings = QSettings()
settings.setValue("Portable", False)
else:
return QSettings()
settings = QSettings()
settings.setValue("Portable", False)
return settings
# About QRect conversion:
@ -93,7 +101,7 @@ class Preferences(QObject):
def __init__(self):
QObject.__init__(self)
self.reset()
self._settings = createQSettings()
self._settings = create_qsettings()
def _load_values(self, settings, get):
pass

View File

@ -12,6 +12,7 @@ import os.path as op
import os
import logging
from core.util import executable_folder
from hscommon.util import first
from PyQt5.QtCore import QStandardPaths
@ -88,8 +89,11 @@ def setAccelKeys(menu):
action.setText(newtext)
def getAppData():
return QStandardPaths.standardLocations(QStandardPaths.DataLocation)[0]
def getAppData(portable=False):
if portable:
return op.join(executable_folder(), "data")
else:
return QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)[0]
class SysWrapper(io.IOBase):

View File

@ -1,5 +1,4 @@
pytest>=6,<7
flake8
tox-travis
black
pyinstaller>=4.5,<5.0; sys_platform != 'linux'

4
run.py
View File

@ -16,7 +16,7 @@ from PyQt5.QtWidgets import QApplication
from hscommon.trans import install_gettext_trans_under_qt
from qtlib.error_report_dialog import install_excepthook
from qtlib.util import setupQtLogging
from qtlib.preferences import createQSettings
from qtlib.preferences import create_qsettings
from qt import dg_rc # noqa: F401
from qt.platform import BASE_PATH
from core import __version__, __appname__
@ -53,7 +53,7 @@ def main():
QCoreApplication.setApplicationName(__appname__)
QCoreApplication.setApplicationVersion(__version__)
setupQtLogging()
settings = createQSettings()
settings = create_qsettings()
lang = settings.value("Language")
locale_folder = op.join(BASE_PATH, "locale")
install_gettext_trans_under_qt(locale_folder, lang)