Skip to content

Commit 6b0601c

Browse files
committed
file size criteria, new year, pyinstaller optimisation
1 parent 7ebcbfe commit 6b0601c

File tree

10 files changed

+167
-28
lines changed

10 files changed

+167
-28
lines changed

scripts/pyinstaller.run.bat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121

2222
@echo.
2323
@echo running-pyinstaller-stage_dude
24-
pyinstaller --version-file=version.pi.dude.txt --noconfirm --clean --add-data="distro.info.txt:." --add-data="version.txt;." --add-data="../LICENSE;." --icon=icon.ico --distpath=%OUTDIR% --windowed --contents-directory=internal --name dude --additional-hooks-dir=. --collect-binaries tkinterdnd2 --hidden-import="PIL._tkinter_finder" dude.py || exit /b 2
24+
pyinstaller --version-file=version.pi.dude.txt --noconfirm --clean --add-data="distro.info.txt:." --add-data="version.txt;." --add-data="../LICENSE;." --icon=icon.ico --distpath=%OUTDIR% --windowed --contents-directory=internal --name dude --additional-hooks-dir=. --collect-binaries tkinterdnd2 --hidden-import="PIL._tkinter_finder" --optimize 2 dude.py || exit /b 2
2525

2626
@echo.
2727
@echo running-pyinstaller-dudecmd
28-
pyinstaller --version-file=version.pi.dudecmd.txt --noconfirm --clean --add-data="distro.info.txt:." --add-data="version.txt;." --add-data="../LICENSE;." --icon=icon.ico --distpath=%OUTDIR% --console --contents-directory=internal --name dudecmd console.py || exit /b 1
28+
pyinstaller --version-file=version.pi.dudecmd.txt --noconfirm --clean --add-data="distro.info.txt:." --add-data="version.txt;." --add-data="../LICENSE;." --icon=icon.ico --distpath=%OUTDIR% --console --contents-directory=internal --name dudecmd --optimize 2 console.py || exit /b 1
2929

3030
move %OUTDIR%\dudecmd\dudecmd.exe %OUTDIR%\dude
3131

scripts/pyinstaller.run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ echo pyinstaller `pyinstaller --version` >> distro.info.txt
2121

2222
echo ''
2323
echo running-pyinstaller-stage_dude
24-
pyinstaller --strip --noconfirm --noconsole --clean --add-data="distro.info.txt:." --add-data="version.txt:." --add-data="../LICENSE:." --contents-directory=internal --distpath=$outdir --additional-hooks-dir=. --collect-binaries tkinterdnd2 --hidden-import='PIL._tkinter_finder' ./dude.py
24+
pyinstaller --strip --noconfirm --noconsole --clean --add-data="distro.info.txt:." --add-data="version.txt:." --add-data="../LICENSE:." --contents-directory=internal --distpath=$outdir --additional-hooks-dir=. --collect-binaries tkinterdnd2 --hidden-import='PIL._tkinter_finder' --optimize 2 ./dude.py
2525

2626
echo ''
2727
echo packing

src/console.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
####################################################################################
44
#
5-
# Copyright (c) 2022-2024 Piotr Jochymek
5+
# Copyright (c) 2022-2025 Piotr Jochymek
66
#
77
# MIT License
88
#
@@ -48,7 +48,7 @@ def parse_args(ver):
4848
parser = argparse.ArgumentParser(
4949
formatter_class=argparse.RawTextHelpFormatter,
5050
prog = 'dude.exe' if (os.name=='nt') else 'dude',
51-
description = f"dude version {ver}\nCopyright (c) 2022-2024 Piotr Jochymek\n\nhttps://github.com/PJDude/dude",
51+
description = f"dude version {ver}\nCopyright (c) 2022-2025 Piotr Jochymek\n\nhttps://github.com/PJDude/dude",
5252
)
5353

5454
parser.add_argument('paths',nargs='*',help='path to scan')
@@ -74,6 +74,9 @@ def parse_args(ver):
7474
parser.add_argument('-imax' ,nargs=1,help='Images similarity mode maximum image size (pixels)',type=int)
7575
parser.add_argument('-igps' ,action='store_true',help='Images GPS proximity mode')
7676

77+
parser.add_argument('-sizemin' ,nargs=1,help='minimum file size')
78+
parser.add_argument('-sizemax' ,nargs=1,help='maximum file size')
79+
7780
parser_help=parser.format_help().split('\n')
7881
help_parts=[parser_help[0]] + parser_help[7::]
7982

src/core.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
####################################################################################
44
#
5-
# Copyright (c) 2022-2024 Piotr Jochymek
5+
# Copyright (c) 2022-2025 Piotr Jochymek
66
#
77
# MIT License
88
#
@@ -90,6 +90,18 @@ def bytes_to_str(num):
9090

9191
return "BIG"
9292

93+
def str_to_bytes(string):
94+
try:
95+
string = string.replace(' ','').lower().rstrip('b')
96+
string_endswith = string.endswith
97+
for suffix,weight in ( ('k',1024),('m',1024*1024),('g',1024*1024*1024),('t',1024*1024*1024*1024) ):
98+
if string_endswith(suffix):
99+
return int(string[0:-1]) * weight #no decimal point
100+
101+
return int(string)
102+
except:
103+
return -1
104+
93105
def fnumber(num):
94106
return str(format(num,',d').replace(',',' '))
95107

@@ -321,7 +333,7 @@ def convert_to_degrees(value):
321333
return None
322334

323335
scan_update_info_path_nr=None
324-
def scan(self,operation_mode):
336+
def scan(self,operation_mode,file_min_size_int=0,file_max_size_int=0):
325337
from PIL.Image import open as image_open
326338

327339
self.log.info('')
@@ -352,10 +364,13 @@ def scan(self,operation_mode):
352364
self_scan_results_images = self.scan_results_images = set()
353365
self_scan_results_image_to_gps = self.scan_results_image_to_gps = {}
354366

367+
use_min_size = bool(file_min_size_int!=0)
368+
use_max_size = bool(file_max_size_int!=0)
369+
use_size = use_min_size or use_max_size
370+
355371
#############################################################################################
356372
if operation_mode in (MODE_SIMILARITY,MODE_GPS):
357373

358-
359374
supported_extensions = IMAGES_EXTENSIONS
360375

361376
self_log_skipped = self.log_skipped
@@ -430,6 +445,17 @@ def scan(self,operation_mode):
430445
folder_size+=size
431446
#https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
432447
sum_size+=size
448+
449+
if use_size:
450+
if use_min_size:
451+
if size<file_min_size_int:
452+
skipping_action(f'size<min {size},{file_min_size_int},{path},{entry.name}' )
453+
continue
454+
if use_max_size:
455+
if size>file_max_size_int:
456+
skipping_action(f'size>max {size},{file_max_size_int},{path},{entry.name}' )
457+
continue
458+
433459
if extension.lower() in supported_extensions:
434460
#sum_size_images+=size
435461
folder_counter_images+=1
@@ -531,6 +557,15 @@ def scan(self,operation_mode):
531557
if size:=stat_res.st_size:
532558
folder_size+=size
533559

560+
if use_size:
561+
if use_min_size:
562+
if size<file_min_size_int:
563+
skipping_action(f'size<min {size},{file_min_size_int},{path},{entry.name}' )
564+
continue
565+
if use_max_size:
566+
if size>file_max_size_int:
567+
skipping_action(f'size>max {size},{file_max_size_int},{path},{entry.name}' )
568+
continue
534569
subpath=path.replace(path_to_scan,'')
535570
self_scan_results_by_size[size].add( (path_nr,subpath,entry.name,stat_res.st_mtime_ns,stat_res.st_ctime_ns,stat_res.st_dev,stat_res.st_ino) )
536571

src/dialogs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
####################################################################################
44
#
5-
# Copyright (c) 2022-2024 Piotr Jochymek
5+
# Copyright (c) 2022-2025 Piotr Jochymek
66
#
77
# MIT License
88
#

src/dude.py

Lines changed: 116 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
####################################################################################
44
#
5-
# Copyright (c) 2022-2024 Piotr Jochymek
5+
# Copyright (c) 2022-2025 Piotr Jochymek
66
#
77
# MIT License
88
#
@@ -247,8 +247,6 @@ def __init__(self,tkwindow):
247247
from threading import Thread
248248
from queue import Queue
249249

250-
print('Image_Cache init')
251-
252250
self.paths_queue = Queue()
253251
self.scaled_images_data_queue = Queue()
254252

@@ -421,7 +419,6 @@ def eternal_photo_imagination(self,tkwindow):
421419
###############################################################
422420

423421
def end(self):
424-
print('Image_Cache END')
425422
self.init_all()
426423
self.threads_keeep_looping=False
427424

@@ -737,7 +734,7 @@ def preview_xscrollcommand(self,v1,v2):
737734
self.preview_text_hbar.set(v1,v2)
738735
self.preview_text_hbar.grid(row=1,column=0,sticky='we')
739736

740-
def __init__(self,cwd,paths_to_add=None,exclude=None,exclude_regexp=None,norun=None,images_mode_tuple=None):
737+
def __init__(self,cwd,paths_to_add=None,exclude=None,exclude_regexp=None,norun=None,images_mode_tuple=None,size_min_str=0,size_max_str=0):
741738
images,ihash,idivergence,rotations,imin,imax,igps = images_mode_tuple if images_mode_tuple else (False,0,0,False,0,0,False)
742739

743740
gc_disable()
@@ -1375,6 +1372,18 @@ def self_folder_tree_yview(*args):
13751372
self.exclude_frame.grid_rowconfigure(99, weight=1)
13761373
##############
13771374

1375+
self.file_min_size_check_var = BooleanVar()
1376+
self.file_max_size_check_var = BooleanVar()
1377+
1378+
self.file_min_size_var = StringVar()
1379+
self.file_max_size_var = StringVar()
1380+
1381+
self.file_min_size_var.set(size_min_str)
1382+
self.file_max_size_var.set(size_max_str)
1383+
1384+
self.file_min_size_check_var.set(bool(size_min_str))
1385+
self.file_max_size_check_var.set(bool(size_max_str))
1386+
13781387
operation_mode_frame = LabelFrame(self_scan_dialog_area_main,text='Operation mode',borderwidth=2,bg=bg_color,takefocus=False)
13791388
operation_mode_frame.grid(row=3,column=0,sticky='news',padx=4,pady=4,columnspan=4)
13801389

@@ -1391,12 +1400,46 @@ def self_folder_tree_yview(*args):
13911400
gps_button = Radiobutton(operation_mode_frame,text='Images GPS data proximity',variable=self.operation_mode_var,value=MODE_GPS,command=self.operation_mode_change )
13921401
gps_button.grid(row=0,column=2,sticky='news',padx=8,pady=3)
13931402

1403+
gps_button.columnconfigure( (0,1,2), weight=1, uniform=2)
1404+
13941405
self.widget_tooltip(gps_button,"Only image files with EXIF GPS\ndata are processed. Identified groups\ncontain images with GPS coordinates\nwith close proximity to each other")
13951406

13961407
operation_mode_frame.grid_columnconfigure( (0,1,2), weight=1)
13971408

1409+
temp_frame3a = LabelFrame(self_scan_dialog_area_main,text='File size range',borderwidth=2,bg=bg_color,takefocus=False)
1410+
temp_frame3a.grid(row=4,column=0,sticky='news',padx=4,pady=4,columnspan=4)
1411+
1412+
self.file_min_check = Checkbutton(temp_frame3a, text = 'Min:' , variable=self.file_min_size_check_var,command=self.use_size_min_change_file)
1413+
self.file_min_check.grid(row=0,column=0,padx=4,pady=4, sticky='wens')
1414+
1415+
self.file_min_Entry = Entry(temp_frame3a, textvariable=self.file_min_size_var,width=10)
1416+
self.file_min_Entry.grid(row=0,column=1,sticky='news',padx=2,pady=2)
1417+
1418+
self.file_min_Space = Label(temp_frame3a)
1419+
self.file_min_Space.grid(row=0,column=2,sticky='news',padx=2,pady=2)
1420+
1421+
self.file_max_check = Checkbutton(temp_frame3a, text = 'Max:' , variable=self.file_max_size_check_var,command=self.use_size_max_change_file)
1422+
self.file_max_check.grid(row=0,column=3,padx=4,pady=4, sticky='wens')
1423+
1424+
self.file_max_Entry = Entry(temp_frame3a, textvariable=self.file_max_size_var,width=10)
1425+
self.file_max_Entry.grid(row=0,column=4,sticky='news',padx=2,pady=2)
1426+
1427+
self.file_max_Space = Label(temp_frame3a)
1428+
self.file_max_Space.grid(row=0,column=5,sticky='news',padx=2,pady=2)
1429+
1430+
temp_frame3a.grid_columnconfigure((2,5), weight=1)
1431+
1432+
size_min_tooltip = "Limit the search pool to files with\nsize equal or greater to the specified\n (e.g. 112kB, 1MB ...)"
1433+
size_max_tooltip = "Limit the search pool to files with\nsize smaller or equal to the specified\n (e.g. 10MB, 1GB ...)"
1434+
self.widget_tooltip(self.file_min_check,size_min_tooltip)
1435+
self.widget_tooltip(self.file_min_Entry,size_min_tooltip)
1436+
1437+
self.widget_tooltip(self.file_max_check,size_max_tooltip)
1438+
self.widget_tooltip(self.file_max_Entry,size_max_tooltip)
1439+
1440+
13981441
temp_frame3 = LabelFrame(self_scan_dialog_area_main,text='Similarity mode options',borderwidth=2,bg=bg_color,takefocus=False)
1399-
temp_frame3.grid(row=4,column=0,sticky='news',padx=4,pady=4,columnspan=4)
1442+
temp_frame3.grid(row=5,column=0,sticky='news',padx=4,pady=4,columnspan=4)
14001443

14011444
sf_par3=Frame(temp_frame3,bg=bg_color)
14021445
sf_par3.pack(fill='both',expand=True,side='top')
@@ -1460,17 +1503,27 @@ def self_folder_tree_yview(*args):
14601503
size_range_frame = LabelFrame(sf_par3,text='Image size range (pixels)',borderwidth=2,bg=bg_color,takefocus=False)
14611504
size_range_frame.grid(row=2,column=0,padx=2,sticky='news',columnspan=2)
14621505

1463-
self.image_min_check = Checkbutton(size_range_frame, text = 'Min size:' , variable=self.image_min_size_check_var,command=self.use_size_min_change)
1506+
self.image_min_check = Checkbutton(size_range_frame, text = 'Min:' , variable=self.image_min_size_check_var,command=self.use_size_min_change)
14641507
self.image_min_check.grid(row=0,column=0,padx=4,pady=4, sticky='wens')
14651508

14661509
self.image_min_Entry = Entry(size_range_frame, textvariable=self.image_min_size_var,width=10)
14671510
self.image_min_Entry.grid(row=0,column=1,sticky='news',padx=2,pady=2)
14681511

1469-
self.image_max_check = Checkbutton(size_range_frame, text = 'Max size:' , variable=self.image_max_size_check_var,command=self.use_size_max_change)
1470-
self.image_max_check.grid(row=0,column=2,padx=4,pady=4, sticky='wens')
1512+
self.image_min_Space = Label(size_range_frame)
1513+
self.image_min_Space.grid(row=0,column=2,sticky='news',padx=2,pady=2)
1514+
1515+
1516+
self.image_max_check = Checkbutton(size_range_frame, text = 'Max:' , variable=self.image_max_size_check_var,command=self.use_size_max_change)
1517+
self.image_max_check.grid(row=0,column=3,padx=4,pady=4, sticky='wens')
14711518

14721519
self.image_max_Entry = Entry(size_range_frame, textvariable=self.image_max_size_var,width=10)
1473-
self.image_max_Entry.grid(row=0,column=3,sticky='news',padx=2,pady=2)
1520+
self.image_max_Entry.grid(row=0,column=4,sticky='news',padx=2,pady=2)
1521+
1522+
self.image_min_Space = Label(size_range_frame)
1523+
self.image_min_Space.grid(row=0,column=5,sticky='news',padx=2,pady=2)
1524+
1525+
size_range_frame.grid_columnconfigure((2,5), weight=1)
1526+
14741527

14751528
min_tooltip = "Limit the search pool to images with\nboth dimensions (width and height)\nequal or greater to the specified value\nin pixels (e.g. 512)"
14761529
max_tooltip = "Limit the search pool to images with\nboth dimensions (width and height)\nsmaller or equal to the specified value\nin pixels (e.g. 4096)"
@@ -1706,6 +1759,12 @@ def use_size_min_change(self):
17061759
def use_size_max_change(self):
17071760
self.image_max_Entry.configure(state='normal' if self.image_max_size_check_var.get() else 'disabled')
17081761

1762+
def use_size_min_change_file(self):
1763+
self.file_min_Entry.configure(state='normal' if self.file_min_size_check_var.get() else 'disabled')
1764+
1765+
def use_size_max_change_file(self):
1766+
self.file_max_Entry.configure(state='normal' if self.file_max_size_check_var.get() else 'disabled')
1767+
17091768
def operation_mode_change(self):
17101769
operation_mode = self.operation_mode_var.get()
17111770
if operation_mode==MODE_CRC:
@@ -1752,6 +1811,9 @@ def operation_mode_change(self):
17521811
else:
17531812
print('unknown operation_mode:',operation_mode)
17541813

1814+
self.use_size_min_change_file()
1815+
self.use_size_max_change_file()
1816+
17551817
def distance_val_set(self):
17561818
self.similarity_distance_var_lab.set(str(self.similarity_distance_var.get())[:4])
17571819

@@ -3102,7 +3164,7 @@ def show_preview(self,user_action=True):
31023164
else:
31033165
self.preview_shown=True
31043166

3105-
if cfg_geometry:=self.cfg_get('preview','200x200',section='geometry'):
3167+
if cfg_geometry:=self.cfg_get('preview','800x600',section='geometry'):
31063168
self_preview.geometry(cfg_geometry)
31073169

31083170
self_preview.deiconify()
@@ -3898,7 +3960,7 @@ def scan(self):
38983960
try:
38993961
image_min_size_int = int(image_min_size)
39003962
except Exception as e:
3901-
self.get_info_dialog_on_scan().show('Min size value error',f'fix: "{image_min_size}"')
3963+
self.get_info_dialog_on_scan().show('Image Min size value error',f'fix: "{image_min_size}"')
39023964
return
39033965

39043966
image_max_size_int = 0
@@ -3907,10 +3969,30 @@ def scan(self):
39073969
try:
39083970
image_max_size_int = int(image_max_size)
39093971
except Exception as e:
3910-
self.get_info_dialog_on_scan().show('Max size value error',f'fix: "{image_max_size}"')
3972+
self.get_info_dialog_on_scan().show('Image Max size value error',f'fix: "{image_max_size}"')
39113973
return
39123974

3913-
scan_thread=Thread(target=lambda : dude_core.scan(operation_mode),daemon=True)
3975+
##################
3976+
file_min_size_int = 0
3977+
if self.file_min_size_check_var.get():
3978+
if file_min_size := self.file_min_size_var.get():
3979+
file_min_size_int = str_to_bytes(file_min_size)
3980+
3981+
if file_min_size_int==-1:
3982+
self.get_info_dialog_on_scan().show('File Min size value error',f'fix: "{file_min_size}"')
3983+
return
3984+
3985+
file_max_size_int = 0
3986+
if self.file_max_size_check_var.get():
3987+
if file_max_size := self.file_max_size_var.get():
3988+
file_max_size_int = str_to_bytes(file_max_size)
3989+
3990+
if file_max_size_int==-1:
3991+
self.get_info_dialog_on_scan().show('File Max size value error',f'fix: "{file_max_size}"')
3992+
return
3993+
#################
3994+
3995+
scan_thread=Thread(target=lambda : dude_core.scan(operation_mode,file_min_size_int,file_max_size_int),daemon=True)
39143996
scan_thread.start()
39153997

39163998
self_progress_dialog_on_scan.lab_l1.configure(text='Total space:')
@@ -6764,7 +6846,26 @@ def show_homepage(self):
67646846
else:
67656847
images_mode_tuple.append(False)
67666848

6767-
Gui( getcwd(),p_args.paths,p_args.exclude,p_args.exclude_regexp,p_args.norun,images_mode_tuple )
6849+
6850+
size_min=0
6851+
if p_args.sizemin:
6852+
size_min_cand = str_to_bytes(p_args.sizemin[0])
6853+
if size_min_cand == -1:
6854+
print(f"cannot parse sizemin value:'{p_args.sizemin[0]}'")
6855+
sys.exit(2)
6856+
else:
6857+
size_min=p_args.sizemin[0]
6858+
6859+
size_max=0
6860+
if p_args.sizemax:
6861+
size_max_cand = str_to_bytes(p_args.sizemax[0])
6862+
if size_max_cand == -1:
6863+
print(f"cannot parse sizemax value:'{p_args.sizemax[0]}'")
6864+
sys.exit(2)
6865+
else:
6866+
size_max=p_args.sizemax[0]
6867+
6868+
Gui( getcwd(),p_args.paths,p_args.exclude,p_args.exclude_regexp,p_args.norun,images_mode_tuple,size_min,size_max )
67686869

67696870
except Exception as e_main:
67706871
print(e_main)

src/png.2.py.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
####################################################################################
44
#
5-
# Copyright (c) 2022-2024 Piotr Jochymek
5+
# Copyright (c) 2022-2025 Piotr Jochymek
66
#
77
# MIT License
88
#

0 commit comments

Comments
 (0)