Skip to content

Commit

Permalink
file size criteria, new year, pyinstaller optimisation
Browse files Browse the repository at this point in the history
  • Loading branch information
PJDude committed Dec 16, 2024
1 parent 7ebcbfe commit 6b0601c
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 28 deletions.
4 changes: 2 additions & 2 deletions scripts/pyinstaller.run.bat
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@

@echo.
@echo running-pyinstaller-stage_dude
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
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

@echo.
@echo running-pyinstaller-dudecmd
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
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

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

Expand Down
2 changes: 1 addition & 1 deletion scripts/pyinstaller.run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ echo pyinstaller `pyinstaller --version` >> distro.info.txt

echo ''
echo running-pyinstaller-stage_dude
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
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

echo ''
echo packing
Expand Down
7 changes: 5 additions & 2 deletions src/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

####################################################################################
#
# Copyright (c) 2022-2024 Piotr Jochymek
# Copyright (c) 2022-2025 Piotr Jochymek
#
# MIT License
#
Expand Down Expand Up @@ -48,7 +48,7 @@ def parse_args(ver):
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
prog = 'dude.exe' if (os.name=='nt') else 'dude',
description = f"dude version {ver}\nCopyright (c) 2022-2024 Piotr Jochymek\n\nhttps://github.com/PJDude/dude",
description = f"dude version {ver}\nCopyright (c) 2022-2025 Piotr Jochymek\n\nhttps://github.com/PJDude/dude",
)

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

parser.add_argument('-sizemin' ,nargs=1,help='minimum file size')
parser.add_argument('-sizemax' ,nargs=1,help='maximum file size')

parser_help=parser.format_help().split('\n')
help_parts=[parser_help[0]] + parser_help[7::]

Expand Down
41 changes: 38 additions & 3 deletions src/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

####################################################################################
#
# Copyright (c) 2022-2024 Piotr Jochymek
# Copyright (c) 2022-2025 Piotr Jochymek
#
# MIT License
#
Expand Down Expand Up @@ -90,6 +90,18 @@ def bytes_to_str(num):

return "BIG"

def str_to_bytes(string):
try:
string = string.replace(' ','').lower().rstrip('b')
string_endswith = string.endswith
for suffix,weight in ( ('k',1024),('m',1024*1024),('g',1024*1024*1024),('t',1024*1024*1024*1024) ):
if string_endswith(suffix):
return int(string[0:-1]) * weight #no decimal point

return int(string)
except:
return -1

def fnumber(num):
return str(format(num,',d').replace(',',' '))

Expand Down Expand Up @@ -321,7 +333,7 @@ def convert_to_degrees(value):
return None

scan_update_info_path_nr=None
def scan(self,operation_mode):
def scan(self,operation_mode,file_min_size_int=0,file_max_size_int=0):
from PIL.Image import open as image_open

self.log.info('')
Expand Down Expand Up @@ -352,10 +364,13 @@ def scan(self,operation_mode):
self_scan_results_images = self.scan_results_images = set()
self_scan_results_image_to_gps = self.scan_results_image_to_gps = {}

use_min_size = bool(file_min_size_int!=0)
use_max_size = bool(file_max_size_int!=0)
use_size = use_min_size or use_max_size

#############################################################################################
if operation_mode in (MODE_SIMILARITY,MODE_GPS):


supported_extensions = IMAGES_EXTENSIONS

self_log_skipped = self.log_skipped
Expand Down Expand Up @@ -430,6 +445,17 @@ def scan(self,operation_mode):
folder_size+=size
#https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
sum_size+=size

if use_size:
if use_min_size:
if size<file_min_size_int:
skipping_action(f'size<min {size},{file_min_size_int},{path},{entry.name}' )
continue
if use_max_size:
if size>file_max_size_int:
skipping_action(f'size>max {size},{file_max_size_int},{path},{entry.name}' )
continue

if extension.lower() in supported_extensions:
#sum_size_images+=size
folder_counter_images+=1
Expand Down Expand Up @@ -531,6 +557,15 @@ def scan(self,operation_mode):
if size:=stat_res.st_size:
folder_size+=size

if use_size:
if use_min_size:
if size<file_min_size_int:
skipping_action(f'size<min {size},{file_min_size_int},{path},{entry.name}' )
continue
if use_max_size:
if size>file_max_size_int:
skipping_action(f'size>max {size},{file_max_size_int},{path},{entry.name}' )
continue
subpath=path.replace(path_to_scan,'')
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) )

Expand Down
2 changes: 1 addition & 1 deletion src/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

####################################################################################
#
# Copyright (c) 2022-2024 Piotr Jochymek
# Copyright (c) 2022-2025 Piotr Jochymek
#
# MIT License
#
Expand Down
131 changes: 116 additions & 15 deletions src/dude.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

####################################################################################
#
# Copyright (c) 2022-2024 Piotr Jochymek
# Copyright (c) 2022-2025 Piotr Jochymek
#
# MIT License
#
Expand Down Expand Up @@ -247,8 +247,6 @@ def __init__(self,tkwindow):
from threading import Thread
from queue import Queue

print('Image_Cache init')

self.paths_queue = Queue()
self.scaled_images_data_queue = Queue()

Expand Down Expand Up @@ -421,7 +419,6 @@ def eternal_photo_imagination(self,tkwindow):
###############################################################

def end(self):
print('Image_Cache END')
self.init_all()
self.threads_keeep_looping=False

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

def __init__(self,cwd,paths_to_add=None,exclude=None,exclude_regexp=None,norun=None,images_mode_tuple=None):
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):
images,ihash,idivergence,rotations,imin,imax,igps = images_mode_tuple if images_mode_tuple else (False,0,0,False,0,0,False)

gc_disable()
Expand Down Expand Up @@ -1375,6 +1372,18 @@ def self_folder_tree_yview(*args):
self.exclude_frame.grid_rowconfigure(99, weight=1)
##############

self.file_min_size_check_var = BooleanVar()
self.file_max_size_check_var = BooleanVar()

self.file_min_size_var = StringVar()
self.file_max_size_var = StringVar()

self.file_min_size_var.set(size_min_str)
self.file_max_size_var.set(size_max_str)

self.file_min_size_check_var.set(bool(size_min_str))
self.file_max_size_check_var.set(bool(size_max_str))

operation_mode_frame = LabelFrame(self_scan_dialog_area_main,text='Operation mode',borderwidth=2,bg=bg_color,takefocus=False)
operation_mode_frame.grid(row=3,column=0,sticky='news',padx=4,pady=4,columnspan=4)

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

gps_button.columnconfigure( (0,1,2), weight=1, uniform=2)

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

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

temp_frame3a = LabelFrame(self_scan_dialog_area_main,text='File size range',borderwidth=2,bg=bg_color,takefocus=False)
temp_frame3a.grid(row=4,column=0,sticky='news',padx=4,pady=4,columnspan=4)

self.file_min_check = Checkbutton(temp_frame3a, text = 'Min:' , variable=self.file_min_size_check_var,command=self.use_size_min_change_file)
self.file_min_check.grid(row=0,column=0,padx=4,pady=4, sticky='wens')

self.file_min_Entry = Entry(temp_frame3a, textvariable=self.file_min_size_var,width=10)
self.file_min_Entry.grid(row=0,column=1,sticky='news',padx=2,pady=2)

self.file_min_Space = Label(temp_frame3a)
self.file_min_Space.grid(row=0,column=2,sticky='news',padx=2,pady=2)

self.file_max_check = Checkbutton(temp_frame3a, text = 'Max:' , variable=self.file_max_size_check_var,command=self.use_size_max_change_file)
self.file_max_check.grid(row=0,column=3,padx=4,pady=4, sticky='wens')

self.file_max_Entry = Entry(temp_frame3a, textvariable=self.file_max_size_var,width=10)
self.file_max_Entry.grid(row=0,column=4,sticky='news',padx=2,pady=2)

self.file_max_Space = Label(temp_frame3a)
self.file_max_Space.grid(row=0,column=5,sticky='news',padx=2,pady=2)

temp_frame3a.grid_columnconfigure((2,5), weight=1)

size_min_tooltip = "Limit the search pool to files with\nsize equal or greater to the specified\n (e.g. 112kB, 1MB ...)"
size_max_tooltip = "Limit the search pool to files with\nsize smaller or equal to the specified\n (e.g. 10MB, 1GB ...)"
self.widget_tooltip(self.file_min_check,size_min_tooltip)
self.widget_tooltip(self.file_min_Entry,size_min_tooltip)

self.widget_tooltip(self.file_max_check,size_max_tooltip)
self.widget_tooltip(self.file_max_Entry,size_max_tooltip)


temp_frame3 = LabelFrame(self_scan_dialog_area_main,text='Similarity mode options',borderwidth=2,bg=bg_color,takefocus=False)
temp_frame3.grid(row=4,column=0,sticky='news',padx=4,pady=4,columnspan=4)
temp_frame3.grid(row=5,column=0,sticky='news',padx=4,pady=4,columnspan=4)

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

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

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

self.image_max_check = Checkbutton(size_range_frame, text = 'Max size:' , variable=self.image_max_size_check_var,command=self.use_size_max_change)
self.image_max_check.grid(row=0,column=2,padx=4,pady=4, sticky='wens')
self.image_min_Space = Label(size_range_frame)
self.image_min_Space.grid(row=0,column=2,sticky='news',padx=2,pady=2)


self.image_max_check = Checkbutton(size_range_frame, text = 'Max:' , variable=self.image_max_size_check_var,command=self.use_size_max_change)
self.image_max_check.grid(row=0,column=3,padx=4,pady=4, sticky='wens')

self.image_max_Entry = Entry(size_range_frame, textvariable=self.image_max_size_var,width=10)
self.image_max_Entry.grid(row=0,column=3,sticky='news',padx=2,pady=2)
self.image_max_Entry.grid(row=0,column=4,sticky='news',padx=2,pady=2)

self.image_min_Space = Label(size_range_frame)
self.image_min_Space.grid(row=0,column=5,sticky='news',padx=2,pady=2)

size_range_frame.grid_columnconfigure((2,5), weight=1)


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)"
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)"
Expand Down Expand Up @@ -1706,6 +1759,12 @@ def use_size_min_change(self):
def use_size_max_change(self):
self.image_max_Entry.configure(state='normal' if self.image_max_size_check_var.get() else 'disabled')

def use_size_min_change_file(self):
self.file_min_Entry.configure(state='normal' if self.file_min_size_check_var.get() else 'disabled')

def use_size_max_change_file(self):
self.file_max_Entry.configure(state='normal' if self.file_max_size_check_var.get() else 'disabled')

def operation_mode_change(self):
operation_mode = self.operation_mode_var.get()
if operation_mode==MODE_CRC:
Expand Down Expand Up @@ -1752,6 +1811,9 @@ def operation_mode_change(self):
else:
print('unknown operation_mode:',operation_mode)

self.use_size_min_change_file()
self.use_size_max_change_file()

def distance_val_set(self):
self.similarity_distance_var_lab.set(str(self.similarity_distance_var.get())[:4])

Expand Down Expand Up @@ -3102,7 +3164,7 @@ def show_preview(self,user_action=True):
else:
self.preview_shown=True

if cfg_geometry:=self.cfg_get('preview','200x200',section='geometry'):
if cfg_geometry:=self.cfg_get('preview','800x600',section='geometry'):
self_preview.geometry(cfg_geometry)

self_preview.deiconify()
Expand Down Expand Up @@ -3898,7 +3960,7 @@ def scan(self):
try:
image_min_size_int = int(image_min_size)
except Exception as e:
self.get_info_dialog_on_scan().show('Min size value error',f'fix: "{image_min_size}"')
self.get_info_dialog_on_scan().show('Image Min size value error',f'fix: "{image_min_size}"')
return

image_max_size_int = 0
Expand All @@ -3907,10 +3969,30 @@ def scan(self):
try:
image_max_size_int = int(image_max_size)
except Exception as e:
self.get_info_dialog_on_scan().show('Max size value error',f'fix: "{image_max_size}"')
self.get_info_dialog_on_scan().show('Image Max size value error',f'fix: "{image_max_size}"')
return

scan_thread=Thread(target=lambda : dude_core.scan(operation_mode),daemon=True)
##################
file_min_size_int = 0
if self.file_min_size_check_var.get():
if file_min_size := self.file_min_size_var.get():
file_min_size_int = str_to_bytes(file_min_size)

if file_min_size_int==-1:
self.get_info_dialog_on_scan().show('File Min size value error',f'fix: "{file_min_size}"')
return

file_max_size_int = 0
if self.file_max_size_check_var.get():
if file_max_size := self.file_max_size_var.get():
file_max_size_int = str_to_bytes(file_max_size)

if file_max_size_int==-1:
self.get_info_dialog_on_scan().show('File Max size value error',f'fix: "{file_max_size}"')
return
#################

scan_thread=Thread(target=lambda : dude_core.scan(operation_mode,file_min_size_int,file_max_size_int),daemon=True)
scan_thread.start()

self_progress_dialog_on_scan.lab_l1.configure(text='Total space:')
Expand Down Expand Up @@ -6764,7 +6846,26 @@ def show_homepage(self):
else:
images_mode_tuple.append(False)

Gui( getcwd(),p_args.paths,p_args.exclude,p_args.exclude_regexp,p_args.norun,images_mode_tuple )

size_min=0
if p_args.sizemin:
size_min_cand = str_to_bytes(p_args.sizemin[0])
if size_min_cand == -1:
print(f"cannot parse sizemin value:'{p_args.sizemin[0]}'")
sys.exit(2)
else:
size_min=p_args.sizemin[0]

size_max=0
if p_args.sizemax:
size_max_cand = str_to_bytes(p_args.sizemax[0])
if size_max_cand == -1:
print(f"cannot parse sizemax value:'{p_args.sizemax[0]}'")
sys.exit(2)
else:
size_max=p_args.sizemax[0]

Gui( getcwd(),p_args.paths,p_args.exclude,p_args.exclude_regexp,p_args.norun,images_mode_tuple,size_min,size_max )

except Exception as e_main:
print(e_main)
Expand Down
2 changes: 1 addition & 1 deletion src/png.2.py.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

####################################################################################
#
# Copyright (c) 2022-2024 Piotr Jochymek
# Copyright (c) 2022-2025 Piotr Jochymek
#
# MIT License
#
Expand Down
Loading

0 comments on commit 6b0601c

Please sign in to comment.