mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-03-12 03:31:37 +00:00
Compare commits
16 Commits
4.3.0
...
091cae0cc6
| Author | SHA1 | Date | |
|---|---|---|---|
|
091cae0cc6
|
|||
|
e30a135451
|
|||
| 1db93fd142 | |||
| 48862b6414 | |||
|
|
c920412856 | ||
|
4448b999ab
|
|||
| af1ae33598 | |||
| 265d10b261 | |||
|
|
1eee3fd7e4 | ||
|
|
1827827fdf | ||
|
|
db174d4e63 | ||
|
1f1dfa88dc
|
|||
|
916c5204cf
|
|||
|
71af825b37
|
|||
|
97f490b8b7
|
|||
|
d369bcddd7
|
@@ -1,2 +1,2 @@
|
|||||||
__version__ = "4.3.0"
|
__version__ = "4.3.1"
|
||||||
__appname__ = "dupeGuru"
|
__appname__ = "dupeGuru"
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ class DupeGuru(Broadcaster):
|
|||||||
"ignore_hardlink_matches": False,
|
"ignore_hardlink_matches": False,
|
||||||
"copymove_dest_type": DestType.RELATIVE,
|
"copymove_dest_type": DestType.RELATIVE,
|
||||||
"picture_cache_type": self.PICTURE_CACHE_TYPE,
|
"picture_cache_type": self.PICTURE_CACHE_TYPE,
|
||||||
|
"include_exists_check": True,
|
||||||
|
"rehash_ignore_mtime": False,
|
||||||
}
|
}
|
||||||
self.selected_dupes = []
|
self.selected_dupes = []
|
||||||
self.details_panel = DetailsPanel(self)
|
self.details_panel = DetailsPanel(self)
|
||||||
@@ -555,7 +557,9 @@ class DupeGuru(Broadcaster):
|
|||||||
# a workaround to make the damn thing work.
|
# a workaround to make the damn thing work.
|
||||||
exepath, args = match.groups()
|
exepath, args = match.groups()
|
||||||
path, exename = op.split(exepath)
|
path, exename = op.split(exepath)
|
||||||
p = subprocess.Popen(exename + args, shell=True, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
p = subprocess.Popen(
|
||||||
|
exename + args, shell=True, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||||
|
)
|
||||||
output = p.stdout.read()
|
output = p.stdout.read()
|
||||||
logging.info("Custom command %s %s: %s", exename, args, output)
|
logging.info("Custom command %s %s: %s", exename, args, output)
|
||||||
else:
|
else:
|
||||||
@@ -792,6 +796,7 @@ class DupeGuru(Broadcaster):
|
|||||||
Scans folders selected in :attr:`directories` and put the results in :attr:`results`
|
Scans folders selected in :attr:`directories` and put the results in :attr:`results`
|
||||||
"""
|
"""
|
||||||
scanner = self.SCANNER_CLASS()
|
scanner = self.SCANNER_CLASS()
|
||||||
|
fs.filesdb.ignore_mtime = self.options["rehash_ignore_mtime"] is True
|
||||||
if not self.directories.has_any_file():
|
if not self.directories.has_any_file():
|
||||||
self.view.show_message(tr("The selected directories contain no scannable file."))
|
self.view.show_message(tr("The selected directories contain no scannable file."))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -303,12 +303,13 @@ def getmatches_by_contents(files, bigsize=0, j=job.nulljob):
|
|||||||
# skip hashing for zero length files
|
# skip hashing for zero length files
|
||||||
result.append(Match(first, second, 100))
|
result.append(Match(first, second, 100))
|
||||||
continue
|
continue
|
||||||
if first.digest_partial == second.digest_partial:
|
# if digests are the same (and not None) then files match
|
||||||
|
if first.digest_partial == second.digest_partial and first.digest_partial is not None:
|
||||||
if bigsize > 0 and first.size > bigsize:
|
if bigsize > 0 and first.size > bigsize:
|
||||||
if first.digest_samples == second.digest_samples:
|
if first.digest_samples == second.digest_samples and first.digest_samples is not None:
|
||||||
result.append(Match(first, second, 100))
|
result.append(Match(first, second, 100))
|
||||||
else:
|
else:
|
||||||
if first.digest == second.digest:
|
if first.digest == second.digest and first.digest is not None:
|
||||||
result.append(Match(first, second, 100))
|
result.append(Match(first, second, 100))
|
||||||
group_count += 1
|
group_count += 1
|
||||||
j.add_progress(desc=PROGRESS_MESSAGE % (len(result), group_count))
|
j.add_progress(desc=PROGRESS_MESSAGE % (len(result), group_count))
|
||||||
|
|||||||
27
core/fs.py
27
core/fs.py
@@ -100,11 +100,14 @@ class FilesDB:
|
|||||||
create_table_query = "CREATE TABLE IF NOT EXISTS files (path TEXT PRIMARY KEY, size INTEGER, mtime_ns INTEGER, entry_dt DATETIME, digest BLOB, digest_partial BLOB, digest_samples BLOB)"
|
create_table_query = "CREATE TABLE IF NOT EXISTS files (path TEXT PRIMARY KEY, size INTEGER, mtime_ns INTEGER, entry_dt DATETIME, digest BLOB, digest_partial BLOB, digest_samples BLOB)"
|
||||||
drop_table_query = "DROP TABLE IF EXISTS files;"
|
drop_table_query = "DROP TABLE IF EXISTS files;"
|
||||||
select_query = "SELECT {key} FROM files WHERE path=:path AND size=:size and mtime_ns=:mtime_ns"
|
select_query = "SELECT {key} FROM files WHERE path=:path AND size=:size and mtime_ns=:mtime_ns"
|
||||||
|
select_query_ignore_mtime = "SELECT {key} FROM files WHERE path=:path AND size=:size"
|
||||||
insert_query = """
|
insert_query = """
|
||||||
INSERT INTO files (path, size, mtime_ns, entry_dt, {key}) VALUES (:path, :size, :mtime_ns, datetime('now'), :value)
|
INSERT INTO files (path, size, mtime_ns, entry_dt, {key}) VALUES (:path, :size, :mtime_ns, datetime('now'), :value)
|
||||||
ON CONFLICT(path) DO UPDATE SET size=:size, mtime_ns=:mtime_ns, entry_dt=datetime('now'), {key}=:value;
|
ON CONFLICT(path) DO UPDATE SET size=:size, mtime_ns=:mtime_ns, entry_dt=datetime('now'), {key}=:value;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ignore_mtime = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.conn = None
|
self.conn = None
|
||||||
self.cur = None
|
self.cur = None
|
||||||
@@ -144,13 +147,20 @@ class FilesDB:
|
|||||||
stat = path.stat()
|
stat = path.stat()
|
||||||
size = stat.st_size
|
size = stat.st_size
|
||||||
mtime_ns = stat.st_mtime_ns
|
mtime_ns = stat.st_mtime_ns
|
||||||
|
try:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.cur.execute(self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns})
|
if self.ignore_mtime:
|
||||||
|
self.cur.execute(self.select_query_ignore_mtime.format(key=key), {"path": str(path), "size": size})
|
||||||
|
else:
|
||||||
|
self.cur.execute(
|
||||||
|
self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns}
|
||||||
|
)
|
||||||
result = self.cur.fetchone()
|
result = self.cur.fetchone()
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
return result[0]
|
return result[0]
|
||||||
|
except Exception as ex:
|
||||||
|
logging.warning(f"Couldn't get {key} for {path} w/{size}, {mtime_ns}: {ex}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -158,12 +168,14 @@ class FilesDB:
|
|||||||
stat = path.stat()
|
stat = path.stat()
|
||||||
size = stat.st_size
|
size = stat.st_size
|
||||||
mtime_ns = stat.st_mtime_ns
|
mtime_ns = stat.st_mtime_ns
|
||||||
|
try:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.cur.execute(
|
self.cur.execute(
|
||||||
self.insert_query.format(key=key),
|
self.insert_query.format(key=key),
|
||||||
{"path": str(path), "size": size, "mtime_ns": mtime_ns, "value": value},
|
{"path": str(path), "size": size, "mtime_ns": mtime_ns, "value": value},
|
||||||
)
|
)
|
||||||
|
except Exception as ex:
|
||||||
|
logging.warning(f"Couldn't put {key} for {path} w/{size}, {mtime_ns}: {ex}")
|
||||||
|
|
||||||
def commit(self) -> None:
|
def commit(self) -> None:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
@@ -265,34 +277,25 @@ class File:
|
|||||||
self.size = nonone(stats.st_size, 0)
|
self.size = nonone(stats.st_size, 0)
|
||||||
self.mtime = nonone(stats.st_mtime, 0)
|
self.mtime = nonone(stats.st_mtime, 0)
|
||||||
elif field == "digest_partial":
|
elif field == "digest_partial":
|
||||||
try:
|
|
||||||
self.digest_partial = filesdb.get(self.path, "digest_partial")
|
self.digest_partial = filesdb.get(self.path, "digest_partial")
|
||||||
if self.digest_partial is None:
|
if self.digest_partial is None:
|
||||||
self.digest_partial = self._calc_digest_partial()
|
self.digest_partial = self._calc_digest_partial()
|
||||||
filesdb.put(self.path, "digest_partial", self.digest_partial)
|
filesdb.put(self.path, "digest_partial", self.digest_partial)
|
||||||
except Exception as e:
|
|
||||||
logging.warning("Couldn't get digest_partial for %s: %s", self.path, e)
|
|
||||||
elif field == "digest":
|
elif field == "digest":
|
||||||
try:
|
|
||||||
self.digest = filesdb.get(self.path, "digest")
|
self.digest = filesdb.get(self.path, "digest")
|
||||||
if self.digest is None:
|
if self.digest is None:
|
||||||
self.digest = self._calc_digest()
|
self.digest = self._calc_digest()
|
||||||
filesdb.put(self.path, "digest", self.digest)
|
filesdb.put(self.path, "digest", self.digest)
|
||||||
except Exception as e:
|
|
||||||
logging.warning("Couldn't get digest for %s: %s", self.path, e)
|
|
||||||
elif field == "digest_samples":
|
elif field == "digest_samples":
|
||||||
size = self.size
|
size = self.size
|
||||||
# Might as well hash such small files entirely.
|
# Might as well hash such small files entirely.
|
||||||
if size <= MIN_FILE_SIZE:
|
if size <= MIN_FILE_SIZE:
|
||||||
setattr(self, field, self.digest)
|
setattr(self, field, self.digest)
|
||||||
return
|
return
|
||||||
try:
|
|
||||||
self.digest_samples = filesdb.get(self.path, "digest_samples")
|
self.digest_samples = filesdb.get(self.path, "digest_samples")
|
||||||
if self.digest_samples is None:
|
if self.digest_samples is None:
|
||||||
self.digest_samples = self._calc_digest_samples()
|
self.digest_samples = self._calc_digest_samples()
|
||||||
filesdb.put(self.path, "digest_samples", self.digest_samples)
|
filesdb.put(self.path, "digest_samples", self.digest_samples)
|
||||||
except Exception as e:
|
|
||||||
logging.warning(f"Couldn't get digest_samples for {self.path}: {e}")
|
|
||||||
|
|
||||||
def _read_all_info(self, attrnames=None):
|
def _read_all_info(self, attrnames=None):
|
||||||
"""Cache all possible info.
|
"""Cache all possible info.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class Photo(fs.File):
|
|||||||
__slots__ = fs.File.__slots__ + tuple(INITIAL_INFO.keys())
|
__slots__ = fs.File.__slots__ + tuple(INITIAL_INFO.keys())
|
||||||
|
|
||||||
# These extensions are supported on all platforms
|
# These extensions are supported on all platforms
|
||||||
HANDLED_EXTS = {"png", "jpg", "jpeg", "gif", "bmp", "tiff", "tif"}
|
HANDLED_EXTS = {"png", "jpg", "jpeg", "gif", "bmp", "tiff", "tif", "webp"}
|
||||||
|
|
||||||
def _plat_get_dimensions(self):
|
def _plat_get_dimensions(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ class Scanner:
|
|||||||
matches = [m for m in matches if m.first.path not in toremove or m.second.path not in toremove]
|
matches = [m for m in matches if m.first.path not in toremove or m.second.path not in toremove]
|
||||||
if not self.mix_file_kind:
|
if not self.mix_file_kind:
|
||||||
matches = [m for m in matches if get_file_ext(m.first.name) == get_file_ext(m.second.name)]
|
matches = [m for m in matches if get_file_ext(m.first.name) == get_file_ext(m.second.name)]
|
||||||
|
if self.include_exists_check:
|
||||||
matches = [m for m in matches if m.first.path.exists() and m.second.path.exists()]
|
matches = [m for m in matches if m.first.path.exists() and m.second.path.exists()]
|
||||||
matches = [m for m in matches if not (m.first.is_ref and m.second.is_ref)]
|
matches = [m for m in matches if not (m.first.is_ref and m.second.is_ref)]
|
||||||
if ignore_list:
|
if ignore_list:
|
||||||
@@ -212,3 +213,4 @@ class Scanner:
|
|||||||
large_size_threshold = 0
|
large_size_threshold = 0
|
||||||
big_file_size_threshold = 0
|
big_file_size_threshold = 0
|
||||||
word_weighting = False
|
word_weighting = False
|
||||||
|
include_exists_check = True
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
=== 4.3.1 (2022-07-08)
|
||||||
|
* Fix issue where cache db exceptions could prevent files being hashed (#1015)
|
||||||
|
* Add extra guard for non-zero length files without digests to prevent false duplicates
|
||||||
|
* Update Italian translations
|
||||||
|
|
||||||
=== 4.3.0 (2022-07-01)
|
=== 4.3.0 (2022-07-01)
|
||||||
* Redirect stdout from custom command to the log files (#1008)
|
* Redirect stdout from custom command to the log files (#1008)
|
||||||
* Update translations
|
* Update translations
|
||||||
|
|||||||
@@ -10,110 +10,110 @@ msgstr ""
|
|||||||
#: core\gui\ignore_list_table.py:19 core\gui\ignore_list_table.py:20
|
#: core\gui\ignore_list_table.py:19 core\gui\ignore_list_table.py:20
|
||||||
#: core\gui\problem_table.py:18
|
#: core\gui\problem_table.py:18
|
||||||
msgid "File Path"
|
msgid "File Path"
|
||||||
msgstr ""
|
msgstr "مسار الملف"
|
||||||
|
|
||||||
#: core\gui\problem_table.py:19
|
#: core\gui\problem_table.py:19
|
||||||
msgid "Error Message"
|
msgid "Error Message"
|
||||||
msgstr ""
|
msgstr "رسالة خطأ"
|
||||||
|
|
||||||
#: core\me\prioritize.py:23
|
#: core\me\prioritize.py:23
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr ""
|
msgstr "مدة"
|
||||||
|
|
||||||
#: core\me\prioritize.py:30 core\me\result_table.py:23
|
#: core\me\prioritize.py:30 core\me\result_table.py:23
|
||||||
msgid "Bitrate"
|
msgid "Bitrate"
|
||||||
msgstr ""
|
msgstr "معدل البت"
|
||||||
|
|
||||||
#: core\me\prioritize.py:37
|
#: core\me\prioritize.py:37
|
||||||
msgid "Samplerate"
|
msgid "Samplerate"
|
||||||
msgstr ""
|
msgstr "معدل العينة"
|
||||||
|
|
||||||
#: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:92
|
#: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:92
|
||||||
#: core\se\result_table.py:19
|
#: core\se\result_table.py:19
|
||||||
msgid "Filename"
|
msgid "Filename"
|
||||||
msgstr ""
|
msgstr "اسم الملف"
|
||||||
|
|
||||||
#: core\me\result_table.py:20 core\pe\result_table.py:20 core\prioritize.py:75
|
#: core\me\result_table.py:20 core\pe\result_table.py:20 core\prioritize.py:75
|
||||||
#: core\se\result_table.py:20
|
#: core\se\result_table.py:20
|
||||||
msgid "Folder"
|
msgid "Folder"
|
||||||
msgstr ""
|
msgstr "مجلد"
|
||||||
|
|
||||||
#: core\me\result_table.py:21
|
#: core\me\result_table.py:21
|
||||||
msgid "Size (MB)"
|
msgid "Size (MB)"
|
||||||
msgstr ""
|
msgstr "الحجم (ميغا بايت)"
|
||||||
|
|
||||||
#: core\me\result_table.py:22
|
#: core\me\result_table.py:22
|
||||||
msgid "Time"
|
msgid "Time"
|
||||||
msgstr ""
|
msgstr "زمن"
|
||||||
|
|
||||||
#: core\me\result_table.py:24
|
#: core\me\result_table.py:24
|
||||||
msgid "Sample Rate"
|
msgid "Sample Rate"
|
||||||
msgstr ""
|
msgstr "معدل العينة"
|
||||||
|
|
||||||
#: core\me\result_table.py:25 core\pe\result_table.py:22 core\prioritize.py:65
|
#: core\me\result_table.py:25 core\pe\result_table.py:22 core\prioritize.py:65
|
||||||
#: core\se\result_table.py:22
|
#: core\se\result_table.py:22
|
||||||
msgid "Kind"
|
msgid "Kind"
|
||||||
msgstr ""
|
msgstr "طيب القلب"
|
||||||
|
|
||||||
#: core\me\result_table.py:26 core\pe\result_table.py:25
|
#: core\me\result_table.py:26 core\pe\result_table.py:25
|
||||||
#: core\prioritize.py:163 core\se\result_table.py:23
|
#: core\prioritize.py:163 core\se\result_table.py:23
|
||||||
msgid "Modification"
|
msgid "Modification"
|
||||||
msgstr ""
|
msgstr "تعديل"
|
||||||
|
|
||||||
#: core\me\result_table.py:27
|
#: core\me\result_table.py:27
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr "عنوان"
|
||||||
|
|
||||||
#: core\me\result_table.py:28
|
#: core\me\result_table.py:28
|
||||||
msgid "Artist"
|
msgid "Artist"
|
||||||
msgstr ""
|
msgstr "فنان"
|
||||||
|
|
||||||
#: core\me\result_table.py:29
|
#: core\me\result_table.py:29
|
||||||
msgid "Album"
|
msgid "Album"
|
||||||
msgstr ""
|
msgstr "البوم"
|
||||||
|
|
||||||
#: core\me\result_table.py:30
|
#: core\me\result_table.py:30
|
||||||
msgid "Genre"
|
msgid "Genre"
|
||||||
msgstr ""
|
msgstr "النوع"
|
||||||
|
|
||||||
#: core\me\result_table.py:31
|
#: core\me\result_table.py:31
|
||||||
msgid "Year"
|
msgid "Year"
|
||||||
msgstr ""
|
msgstr "سنة"
|
||||||
|
|
||||||
#: core\me\result_table.py:32
|
#: core\me\result_table.py:32
|
||||||
msgid "Track Number"
|
msgid "Track Number"
|
||||||
msgstr ""
|
msgstr "رقم الشاحنة"
|
||||||
|
|
||||||
#: core\me\result_table.py:33
|
#: core\me\result_table.py:33
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr ""
|
msgstr "تعليق"
|
||||||
|
|
||||||
#: core\me\result_table.py:34 core\pe\result_table.py:26
|
#: core\me\result_table.py:34 core\pe\result_table.py:26
|
||||||
#: core\se\result_table.py:24
|
#: core\se\result_table.py:24
|
||||||
msgid "Match %"
|
msgid "Match %"
|
||||||
msgstr ""
|
msgstr "مباراة ٪"
|
||||||
|
|
||||||
#: core\me\result_table.py:35 core\se\result_table.py:25
|
#: core\me\result_table.py:35 core\se\result_table.py:25
|
||||||
msgid "Words Used"
|
msgid "Words Used"
|
||||||
msgstr ""
|
msgstr "الكلمات المستخدمة"
|
||||||
|
|
||||||
#: core\me\result_table.py:36 core\pe\result_table.py:27
|
#: core\me\result_table.py:36 core\pe\result_table.py:27
|
||||||
#: core\se\result_table.py:26
|
#: core\se\result_table.py:26
|
||||||
msgid "Dupe Count"
|
msgid "Dupe Count"
|
||||||
msgstr ""
|
msgstr "عدد المخادعين"
|
||||||
|
|
||||||
#: core\pe\prioritize.py:23 core\pe\result_table.py:23
|
#: core\pe\prioritize.py:23 core\pe\result_table.py:23
|
||||||
msgid "Dimensions"
|
msgid "Dimensions"
|
||||||
msgstr ""
|
msgstr "أبعاد"
|
||||||
|
|
||||||
#: core\pe\result_table.py:21 core\se\result_table.py:21
|
#: core\pe\result_table.py:21 core\se\result_table.py:21
|
||||||
msgid "Size (KB)"
|
msgid "Size (KB)"
|
||||||
msgstr ""
|
msgstr "الحجم (كيلو بايت)"
|
||||||
|
|
||||||
#: core\pe\result_table.py:24
|
#: core\pe\result_table.py:24
|
||||||
msgid "EXIF Timestamp"
|
msgid "EXIF Timestamp"
|
||||||
msgstr ""
|
msgstr "الطابع الزمني EXIF"
|
||||||
|
|
||||||
#: core\prioritize.py:156
|
#: core\prioritize.py:156
|
||||||
msgid "Size"
|
msgid "Size"
|
||||||
msgstr ""
|
msgstr "بحجم"
|
||||||
|
|||||||
@@ -36,83 +36,83 @@ msgstr ""
|
|||||||
msgid "Sending to Trash"
|
msgid "Sending to Trash"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:291
|
#: core\app.py:293
|
||||||
msgid "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
|
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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:302
|
#: core\app.py:304
|
||||||
msgid "No duplicates found."
|
msgid "No duplicates found."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:317
|
#: core\app.py:319
|
||||||
msgid "All marked files were copied successfully."
|
msgid "All marked files were copied successfully."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:319
|
#: core\app.py:321
|
||||||
msgid "All marked files were moved successfully."
|
msgid "All marked files were moved successfully."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:321
|
#: core\app.py:323
|
||||||
msgid "All marked files were deleted successfully."
|
msgid "All marked files were deleted successfully."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:323
|
#: core\app.py:325
|
||||||
msgid "All marked files were successfully sent to Trash."
|
msgid "All marked files were successfully sent to Trash."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:328
|
#: core\app.py:330
|
||||||
msgid "Could not load file: {}"
|
msgid "Could not load file: {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:384
|
#: core\app.py:386
|
||||||
msgid "'{}' already is in the list."
|
msgid "'{}' already is in the list."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:386
|
#: core\app.py:388
|
||||||
msgid "'{}' does not exist."
|
msgid "'{}' does not exist."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:394
|
#: core\app.py:396
|
||||||
msgid "All selected %d matches are going to be ignored in all subsequent scans. Continue?"
|
msgid "All selected %d matches are going to be ignored in all subsequent scans. Continue?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:471
|
#: core\app.py:473
|
||||||
msgid "Select a directory to copy marked files to"
|
msgid "Select a directory to copy marked files to"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:473
|
#: core\app.py:475
|
||||||
msgid "Select a directory to move marked files to"
|
msgid "Select a directory to move marked files to"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:512
|
#: core\app.py:514
|
||||||
msgid "Select a destination for your exported CSV"
|
msgid "Select a destination for your exported CSV"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:518 core\app.py:773 core\app.py:783
|
#: core\app.py:520 core\app.py:781 core\app.py:791
|
||||||
msgid "Couldn't write to file: {}"
|
msgid "Couldn't write to file: {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:541
|
#: core\app.py:543
|
||||||
msgid "You have no custom command set up. Set it up in your preferences."
|
msgid "You have no custom command set up. Set it up in your preferences."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:697 core\app.py:709
|
#: core\app.py:705 core\app.py:717
|
||||||
msgid "You are about to remove %d files from results. Continue?"
|
msgid "You are about to remove %d files from results. Continue?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:745
|
#: core\app.py:753
|
||||||
msgid "{} duplicate groups were changed by the re-prioritization."
|
msgid "{} duplicate groups were changed by the re-prioritization."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:792
|
#: core\app.py:801
|
||||||
msgid "The selected directories contain no scannable file."
|
msgid "The selected directories contain no scannable file."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:808
|
#: core\app.py:817
|
||||||
msgid "Collecting files to scan"
|
msgid "Collecting files to scan"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core\app.py:858
|
#: core\app.py:867
|
||||||
msgid "%s (%d discarded)"
|
msgid "%s (%d discarded)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ msgstr "Raccolte {} cartelle da scansionare"
|
|||||||
|
|
||||||
#: core\engine.py:27
|
#: core\engine.py:27
|
||||||
msgid "%d matches found from %d groups"
|
msgid "%d matches found from %d groups"
|
||||||
msgstr "%d corrispondeze trovate da %d gruppi"
|
msgstr "%d corrispondenze trovate da %d gruppi"
|
||||||
|
|
||||||
#: core\gui\deletion_options.py:71
|
#: core\gui\deletion_options.py:71
|
||||||
msgid "You are sending {} file(s) to the Trash."
|
msgid "You are sending {} file(s) to the Trash."
|
||||||
|
|||||||
@@ -2,15 +2,16 @@
|
|||||||
# Andrew Senetar <arsenetar@gmail.com>, 2022
|
# Andrew Senetar <arsenetar@gmail.com>, 2022
|
||||||
# Emanuele, 2022
|
# Emanuele, 2022
|
||||||
# Fuan <jcfrt@posteo.net>, 2022
|
# Fuan <jcfrt@posteo.net>, 2022
|
||||||
|
# Giovanni, 2022
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Last-Translator: Fuan <jcfrt@posteo.net>, 2022\n"
|
"Last-Translator: Giovanni, 2022\n"
|
||||||
"Language-Team: Italian (https://www.transifex.com/voltaicideas/teams/116153/it/)\n"
|
"Language-Team: Italian (https://www.transifex.com/voltaicideas/teams/116153/it/)\n"
|
||||||
"Language: it\n"
|
"Language: it\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: utf-8\n"
|
"Content-Transfer-Encoding: utf-8\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||||
|
|
||||||
#: qt/app.py:81
|
#: qt/app.py:81
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
@@ -979,37 +980,40 @@ msgstr "Ignora file più grandi di"
|
|||||||
|
|
||||||
#: qt\app.py:135 qt\app.py:293
|
#: qt\app.py:135 qt\app.py:293
|
||||||
msgid "Clear Cache"
|
msgid "Clear Cache"
|
||||||
msgstr ""
|
msgstr "Svuota cache"
|
||||||
|
|
||||||
#: qt\app.py:294
|
#: qt\app.py:294
|
||||||
msgid ""
|
msgid ""
|
||||||
"Do you really want to clear the cache? This will remove all cached file "
|
"Do you really want to clear the cache? This will remove all cached file "
|
||||||
"hashes and picture analysis."
|
"hashes and picture analysis."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Vuoi davvero svuotare la cache? Ciò rimuoverà tutti gli hash dei file "
|
||||||
|
"memorizzati nella cache e le analisi delle immagini."
|
||||||
|
|
||||||
#: qt\app.py:299
|
#: qt\app.py:299
|
||||||
msgid "Cache cleared."
|
msgid "Cache cleared."
|
||||||
msgstr ""
|
msgstr "Cache svuotata"
|
||||||
|
|
||||||
#: qt\preferences_dialog.py:173
|
#: qt\preferences_dialog.py:173
|
||||||
msgid "Use dark style"
|
msgid "Use dark style"
|
||||||
msgstr ""
|
msgstr "Usa stile scuro"
|
||||||
|
|
||||||
#: qt\preferences_dialog.py:241
|
#: qt\preferences_dialog.py:241
|
||||||
msgid "Profile scan operation"
|
msgid "Profile scan operation"
|
||||||
msgstr ""
|
msgstr "Profila l'operazione di scansione"
|
||||||
|
|
||||||
#: qt\preferences_dialog.py:242
|
#: qt\preferences_dialog.py:242
|
||||||
msgid "Profile the scan operation and save logs for optimization."
|
msgid "Profile the scan operation and save logs for optimization."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Profila l'operazione di scansione e salva i registri per l'ottimizzazione."
|
||||||
|
|
||||||
#: qt\preferences_dialog.py:246
|
#: qt\preferences_dialog.py:246
|
||||||
msgid "Logs located in: <a href=\"{}\">{}</a>"
|
msgid "Logs located in: <a href=\"{}\">{}</a>"
|
||||||
msgstr ""
|
msgstr "I log si trovano in: <a href=\"{}\">{}</a>"
|
||||||
|
|
||||||
#: qt\preferences_dialog.py:291
|
#: qt\preferences_dialog.py:291
|
||||||
msgid "Debug"
|
msgid "Debug"
|
||||||
msgstr ""
|
msgstr "Debug"
|
||||||
|
|
||||||
#: qt\about_box.py:31
|
#: qt\about_box.py:31
|
||||||
msgid "About {}"
|
msgid "About {}"
|
||||||
@@ -1021,7 +1025,7 @@ msgstr "Versione {}"
|
|||||||
|
|
||||||
#: qt\about_box.py:49 qt\about_box.py:75
|
#: qt\about_box.py:49 qt\about_box.py:75
|
||||||
msgid "Checking for updates..."
|
msgid "Checking for updates..."
|
||||||
msgstr ""
|
msgstr "Controllo degli aggiornamenti..."
|
||||||
|
|
||||||
#: qt\about_box.py:54
|
#: qt\about_box.py:54
|
||||||
msgid "Licensed under GPLv3"
|
msgid "Licensed under GPLv3"
|
||||||
@@ -1029,11 +1033,11 @@ msgstr "Distribuito sotto licenza GPLv3"
|
|||||||
|
|
||||||
#: qt\about_box.py:68
|
#: qt\about_box.py:68
|
||||||
msgid "No update available."
|
msgid "No update available."
|
||||||
msgstr ""
|
msgstr "Nessun aggiornamento disponibile."
|
||||||
|
|
||||||
#: qt\about_box.py:71
|
#: qt\about_box.py:71
|
||||||
msgid "New version {} available, download <a href=\"{}\">here</a>."
|
msgid "New version {} available, download <a href=\"{}\">here</a>."
|
||||||
msgstr ""
|
msgstr "È disponibile la nuova versione {}, scaricabile <a href=\"{}\">qui</a>."
|
||||||
|
|
||||||
#: qt\error_report_dialog.py:50
|
#: qt\error_report_dialog.py:50
|
||||||
msgid "Error Report"
|
msgid "Error Report"
|
||||||
|
|||||||
@@ -1092,3 +1092,25 @@ msgstr ""
|
|||||||
#: qt\search_edit.py:78
|
#: qt\search_edit.py:78
|
||||||
msgid "Search..."
|
msgid "Search..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: qt\preferences_dialog.py:219
|
||||||
|
msgid ""
|
||||||
|
"These options are for advanced users or for very specific situations, most "
|
||||||
|
"users should not have to modify these."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: qt\preferences_dialog.py:225
|
||||||
|
msgid "Include existence check after scan completion"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: qt\preferences_dialog.py:227
|
||||||
|
msgid "Ignore difference in mtime when loading cached digests"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: qt\progress_window.py:64
|
||||||
|
msgid "Cancel?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: qt\progress_window.py:65
|
||||||
|
msgid "Are you sure you want to cancel? All progress will be lost."
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ Icon=dupeguru
|
|||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Utility;
|
Categories=Utility;
|
||||||
|
Keywords=file manager;gui;
|
||||||
|
|||||||
@@ -193,6 +193,8 @@ class DupeGuru(QObject):
|
|||||||
self.model.options["scanned_tags"] = scanned_tags
|
self.model.options["scanned_tags"] = scanned_tags
|
||||||
self.model.options["match_scaled"] = self.prefs.match_scaled
|
self.model.options["match_scaled"] = self.prefs.match_scaled
|
||||||
self.model.options["picture_cache_type"] = self.prefs.picture_cache_type
|
self.model.options["picture_cache_type"] = self.prefs.picture_cache_type
|
||||||
|
self.model.options["include_exists_check"] = self.prefs.include_exists_check
|
||||||
|
self.model.options["rehash_ignore_mtime"] = self.prefs.rehash_ignore_mtime
|
||||||
|
|
||||||
if self.details_dialog:
|
if self.details_dialog:
|
||||||
self.details_dialog.update_options()
|
self.details_dialog.update_options()
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ class Preferences(PreferencesBase):
|
|||||||
self.ignore_hardlink_matches = get("IgnoreHardlinkMatches", self.ignore_hardlink_matches)
|
self.ignore_hardlink_matches = get("IgnoreHardlinkMatches", self.ignore_hardlink_matches)
|
||||||
self.use_regexp = get("UseRegexp", self.use_regexp)
|
self.use_regexp = get("UseRegexp", self.use_regexp)
|
||||||
self.remove_empty_folders = get("RemoveEmptyFolders", self.remove_empty_folders)
|
self.remove_empty_folders = get("RemoveEmptyFolders", self.remove_empty_folders)
|
||||||
|
self.rehash_ignore_mtime = get("RehashIgnoreMTime", self.rehash_ignore_mtime)
|
||||||
|
self.include_exists_check = get("IncludeExistsCheck", self.include_exists_check)
|
||||||
self.debug_mode = get("DebugMode", self.debug_mode)
|
self.debug_mode = get("DebugMode", self.debug_mode)
|
||||||
self.profile_scan = get("ProfileScan", self.profile_scan)
|
self.profile_scan = get("ProfileScan", self.profile_scan)
|
||||||
self.destination_type = get("DestinationType", self.destination_type)
|
self.destination_type = get("DestinationType", self.destination_type)
|
||||||
@@ -231,6 +233,8 @@ class Preferences(PreferencesBase):
|
|||||||
self.use_regexp = False
|
self.use_regexp = False
|
||||||
self.ignore_hardlink_matches = False
|
self.ignore_hardlink_matches = False
|
||||||
self.remove_empty_folders = False
|
self.remove_empty_folders = False
|
||||||
|
self.rehash_ignore_mtime = False
|
||||||
|
self.include_exists_check = True
|
||||||
self.debug_mode = False
|
self.debug_mode = False
|
||||||
self.profile_scan = False
|
self.profile_scan = False
|
||||||
self.destination_type = 1
|
self.destination_type = 1
|
||||||
@@ -283,6 +287,8 @@ class Preferences(PreferencesBase):
|
|||||||
set_("IgnoreHardlinkMatches", self.ignore_hardlink_matches)
|
set_("IgnoreHardlinkMatches", self.ignore_hardlink_matches)
|
||||||
set_("UseRegexp", self.use_regexp)
|
set_("UseRegexp", self.use_regexp)
|
||||||
set_("RemoveEmptyFolders", self.remove_empty_folders)
|
set_("RemoveEmptyFolders", self.remove_empty_folders)
|
||||||
|
set_("RehashIgnoreMTime", self.rehash_ignore_mtime)
|
||||||
|
set_("IncludeExistsCheck", self.include_exists_check)
|
||||||
set_("DebugMode", self.debug_mode)
|
set_("DebugMode", self.debug_mode)
|
||||||
set_("ProfileScan", self.profile_scan)
|
set_("ProfileScan", self.profile_scan)
|
||||||
set_("DestinationType", self.destination_type)
|
set_("DestinationType", self.destination_type)
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ class Sections(Flag):
|
|||||||
|
|
||||||
GENERAL = auto()
|
GENERAL = auto()
|
||||||
DISPLAY = auto()
|
DISPLAY = auto()
|
||||||
|
ADVANCED = auto()
|
||||||
DEBUG = auto()
|
DEBUG = auto()
|
||||||
ALL = GENERAL | DISPLAY | DEBUG
|
ALL = GENERAL | DISPLAY | ADVANCED | DEBUG
|
||||||
|
|
||||||
|
|
||||||
class PreferencesDialogBase(QDialog):
|
class PreferencesDialogBase(QDialog):
|
||||||
@@ -213,6 +214,19 @@ use the modifier key to drag the floating window around"
|
|||||||
details_groupbox.setLayout(self.details_groupbox_layout)
|
details_groupbox.setLayout(self.details_groupbox_layout)
|
||||||
self.displayVLayout.addWidget(details_groupbox)
|
self.displayVLayout.addWidget(details_groupbox)
|
||||||
|
|
||||||
|
def _setup_advanced_page(self):
|
||||||
|
tab_label = QLabel(
|
||||||
|
tr(
|
||||||
|
"These options are for advanced users or for very specific situations, most users should not have to modify these."
|
||||||
|
),
|
||||||
|
wordWrap=True,
|
||||||
|
)
|
||||||
|
self.advanced_vlayout.addWidget(tab_label)
|
||||||
|
self._setupAddCheckbox("include_exists_check_box", tr("Include existence check after scan completion"))
|
||||||
|
self.advanced_vlayout.addWidget(self.include_exists_check_box)
|
||||||
|
self._setupAddCheckbox("rehash_ignore_mtime_box", tr("Ignore difference in mtime when loading cached digests"))
|
||||||
|
self.advanced_vlayout.addWidget(self.rehash_ignore_mtime_box)
|
||||||
|
|
||||||
def _setupDebugPage(self):
|
def _setupDebugPage(self):
|
||||||
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
|
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
|
||||||
self._setupAddCheckbox("profile_scan_box", tr("Profile scan operation"))
|
self._setupAddCheckbox("profile_scan_box", tr("Profile scan operation"))
|
||||||
@@ -244,16 +258,20 @@ use the modifier key to drag the floating window around"
|
|||||||
self.tabwidget = QTabWidget()
|
self.tabwidget = QTabWidget()
|
||||||
self.page_general = QWidget()
|
self.page_general = QWidget()
|
||||||
self.page_display = QWidget()
|
self.page_display = QWidget()
|
||||||
|
self.page_advanced = QWidget()
|
||||||
self.page_debug = QWidget()
|
self.page_debug = QWidget()
|
||||||
self.widgetsVLayout = QVBoxLayout()
|
self.widgetsVLayout = QVBoxLayout()
|
||||||
self.page_general.setLayout(self.widgetsVLayout)
|
self.page_general.setLayout(self.widgetsVLayout)
|
||||||
self.displayVLayout = QVBoxLayout()
|
self.displayVLayout = QVBoxLayout()
|
||||||
self.displayVLayout.setSpacing(5) # arbitrary value, might conflict with style
|
self.displayVLayout.setSpacing(5) # arbitrary value, might conflict with style
|
||||||
self.page_display.setLayout(self.displayVLayout)
|
self.page_display.setLayout(self.displayVLayout)
|
||||||
|
self.advanced_vlayout = QVBoxLayout()
|
||||||
|
self.page_advanced.setLayout(self.advanced_vlayout)
|
||||||
self.debugVLayout = QVBoxLayout()
|
self.debugVLayout = QVBoxLayout()
|
||||||
self.page_debug.setLayout(self.debugVLayout)
|
self.page_debug.setLayout(self.debugVLayout)
|
||||||
self._setupPreferenceWidgets()
|
self._setupPreferenceWidgets()
|
||||||
self._setupDisplayPage()
|
self._setupDisplayPage()
|
||||||
|
self._setup_advanced_page()
|
||||||
self._setupDebugPage()
|
self._setupDebugPage()
|
||||||
# self.mainVLayout.addLayout(self.widgetsVLayout)
|
# self.mainVLayout.addLayout(self.widgetsVLayout)
|
||||||
self.buttonBox = QDialogButtonBox(self)
|
self.buttonBox = QDialogButtonBox(self)
|
||||||
@@ -265,9 +283,11 @@ use the modifier key to drag the floating window around"
|
|||||||
self.layout().setSizeConstraint(QLayout.SetFixedSize)
|
self.layout().setSizeConstraint(QLayout.SetFixedSize)
|
||||||
self.tabwidget.addTab(self.page_general, tr("General"))
|
self.tabwidget.addTab(self.page_general, tr("General"))
|
||||||
self.tabwidget.addTab(self.page_display, tr("Display"))
|
self.tabwidget.addTab(self.page_display, tr("Display"))
|
||||||
|
self.tabwidget.addTab(self.page_advanced, tr("Advanced"))
|
||||||
self.tabwidget.addTab(self.page_debug, tr("Debug"))
|
self.tabwidget.addTab(self.page_debug, tr("Debug"))
|
||||||
self.displayVLayout.addStretch(0)
|
self.displayVLayout.addStretch(0)
|
||||||
self.widgetsVLayout.addStretch(0)
|
self.widgetsVLayout.addStretch(0)
|
||||||
|
self.advanced_vlayout.addStretch(0)
|
||||||
self.debugVLayout.addStretch(0)
|
self.debugVLayout.addStretch(0)
|
||||||
|
|
||||||
def _load(self, prefs, setchecked, section):
|
def _load(self, prefs, setchecked, section):
|
||||||
@@ -318,6 +338,9 @@ use the modifier key to drag the floating window around"
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
selected_lang = self.supportedLanguages["en"]
|
selected_lang = self.supportedLanguages["en"]
|
||||||
self.languageComboBox.setCurrentText(selected_lang)
|
self.languageComboBox.setCurrentText(selected_lang)
|
||||||
|
if section & Sections.ADVANCED:
|
||||||
|
setchecked(self.rehash_ignore_mtime_box, prefs.rehash_ignore_mtime)
|
||||||
|
setchecked(self.include_exists_check_box, prefs.include_exists_check)
|
||||||
if section & Sections.DEBUG:
|
if section & Sections.DEBUG:
|
||||||
setchecked(self.debugModeBox, prefs.debug_mode)
|
setchecked(self.debugModeBox, prefs.debug_mode)
|
||||||
setchecked(self.profile_scan_box, prefs.profile_scan)
|
setchecked(self.profile_scan_box, prefs.profile_scan)
|
||||||
@@ -334,6 +357,8 @@ use the modifier key to drag the floating window around"
|
|||||||
prefs.use_regexp = ischecked(self.useRegexpBox)
|
prefs.use_regexp = ischecked(self.useRegexpBox)
|
||||||
prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox)
|
prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox)
|
||||||
prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches)
|
prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches)
|
||||||
|
prefs.rehash_ignore_mtime = ischecked(self.rehash_ignore_mtime_box)
|
||||||
|
prefs.include_exists_check = ischecked(self.include_exists_check_box)
|
||||||
prefs.debug_mode = ischecked(self.debugModeBox)
|
prefs.debug_mode = ischecked(self.debugModeBox)
|
||||||
prefs.profile_scan = ischecked(self.profile_scan_box)
|
prefs.profile_scan = ischecked(self.profile_scan_box)
|
||||||
prefs.reference_bold_font = ischecked(self.reference_bold_font)
|
prefs.reference_bold_font = ischecked(self.reference_bold_font)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QTimer
|
from PyQt5.QtCore import Qt, QTimer
|
||||||
from PyQt5.QtWidgets import QProgressDialog
|
from PyQt5.QtWidgets import QDialog, QMessageBox, QVBoxLayout, QLabel, QProgressBar, QPushButton
|
||||||
|
|
||||||
from hscommon.trans import tr
|
from hscommon.trans import tr
|
||||||
|
|
||||||
@@ -25,37 +25,60 @@ class ProgressWindow:
|
|||||||
def refresh(self): # Labels
|
def refresh(self): # Labels
|
||||||
if self._window is not None:
|
if self._window is not None:
|
||||||
self._window.setWindowTitle(self.model.jobdesc_textfield.text)
|
self._window.setWindowTitle(self.model.jobdesc_textfield.text)
|
||||||
self._window.setLabelText(self.model.progressdesc_textfield.text)
|
self._label.setText(self.model.progressdesc_textfield.text)
|
||||||
|
|
||||||
def set_progress(self, last_progress):
|
def set_progress(self, last_progress):
|
||||||
if self._window is not None:
|
if self._window is not None:
|
||||||
if last_progress < 0:
|
if last_progress < 0:
|
||||||
self._window.setRange(0, 0)
|
self._progress_bar.setRange(0, 0)
|
||||||
else:
|
else:
|
||||||
self._window.setRange(0, 100)
|
self._progress_bar.setRange(0, 100)
|
||||||
self._window.setValue(last_progress)
|
self._progress_bar.setValue(last_progress)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||||
self._window = QProgressDialog("", tr("Cancel"), 0, 100, self.parent, flags)
|
self._window = QDialog(self.parent, flags)
|
||||||
|
self._setup_ui()
|
||||||
self._window.setModal(True)
|
self._window.setModal(True)
|
||||||
self._window.setAutoReset(False)
|
|
||||||
self._window.setAutoClose(False)
|
|
||||||
self._timer = QTimer(self._window)
|
self._timer = QTimer(self._window)
|
||||||
self._timer.timeout.connect(self.model.pulse)
|
self._timer.timeout.connect(self.model.pulse)
|
||||||
self._window.show()
|
self._window.show()
|
||||||
self._window.canceled.connect(self.model.cancel)
|
|
||||||
self._timer.start(500)
|
self._timer.start(500)
|
||||||
|
|
||||||
|
def _setup_ui(self):
|
||||||
|
self._window.setWindowTitle(tr("Cancel"))
|
||||||
|
vertical_layout = QVBoxLayout(self._window)
|
||||||
|
self._label = QLabel("", self._window)
|
||||||
|
vertical_layout.addWidget(self._label)
|
||||||
|
self._progress_bar = QProgressBar(self._window)
|
||||||
|
self._progress_bar.setRange(0, 100)
|
||||||
|
vertical_layout.addWidget(self._progress_bar)
|
||||||
|
self._cancel_button = QPushButton(tr("Cancel"), self._window)
|
||||||
|
self._cancel_button.clicked.connect(self.cancel)
|
||||||
|
vertical_layout.addWidget(self._cancel_button)
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
if self._window is not None:
|
||||||
|
confirm_dialog = QMessageBox(
|
||||||
|
QMessageBox.Icon.Question,
|
||||||
|
tr("Cancel?"),
|
||||||
|
tr("Are you sure you want to cancel? All progress will be lost."),
|
||||||
|
QMessageBox.StandardButton.No | QMessageBox.StandardButton.Yes,
|
||||||
|
self._window,
|
||||||
|
)
|
||||||
|
confirm_dialog.setDefaultButton(QMessageBox.StandardButton.No)
|
||||||
|
result = confirm_dialog.exec_()
|
||||||
|
if result != QMessageBox.StandardButton.Yes:
|
||||||
|
return
|
||||||
|
self.close()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
# it seems it is possible for close to be called without a corresponding
|
# it seems it is possible for close to be called without a corresponding
|
||||||
# show, only perform a close if there is a window to close
|
# show, only perform a close if there is a window to close
|
||||||
if self._window is not None:
|
if self._window is not None:
|
||||||
self._timer.stop()
|
self._timer.stop()
|
||||||
del self._timer
|
del self._timer
|
||||||
# For some weird reason, canceled() signal is sent upon close, whether the user canceled
|
|
||||||
# or not. If we don't want a false cancellation, we have to disconnect it.
|
|
||||||
self._window.canceled.disconnect()
|
|
||||||
self._window.close()
|
self._window.close()
|
||||||
self._window.setParent(None)
|
self._window.setParent(None)
|
||||||
self._window = None
|
self._window = None
|
||||||
|
self.model.cancel()
|
||||||
|
|||||||
2
tox.ini
2
tox.ini
@@ -19,4 +19,4 @@ deps =
|
|||||||
exclude = .tox,env,build,cocoalib,cocoa,help,./qt/dg_rc.py,cocoa/run_template.py,./pkg
|
exclude = .tox,env,build,cocoalib,cocoa,help,./qt/dg_rc.py,cocoa/run_template.py,./pkg
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
select = C,E,F,W,B,B950
|
select = C,E,F,W,B,B950
|
||||||
extend-ignore = E203, E501
|
extend-ignore = E203, E501, W503
|
||||||
|
|||||||
Reference in New Issue
Block a user