From e8861abbdc4cb36bfc7d570efe5452a3c821165b Mon Sep 17 00:00:00 2001 From: Kameron Kenny <1267885+kkenny@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:47:20 -0400 Subject: [PATCH] It works. --- BitMover_ui.py | 176 ++++++++++++++++++++++++++++--------------------- dedup.py | 2 +- file_stuff.py | 15 ----- raw_photo.py | 10 +-- rename.py | 2 +- 5 files changed, 108 insertions(+), 97 deletions(-) diff --git a/BitMover_ui.py b/BitMover_ui.py index f08bdeb..3243b36 100755 --- a/BitMover_ui.py +++ b/BitMover_ui.py @@ -1,36 +1,32 @@ #!/usr/bin/env python import os import sys -# import time - -from PyQt6.QtCore import * -from PyQt6.QtGui import * -from PyQt6.QtWidgets import * - import traceback +from PyQt6.QtCore import pyqtSignal, pyqtSlot, QRunnable, QObject, QThreadPool +from PyQt6.QtWidgets import QMainWindow, QApplication +from PyQt6.QtGui import QIcon,QPixmap + +from PIL import Image + from configure import CONFIG_FILE, Configure -from file_stuff import is_file, get_t_files +from file_stuff import is_file from BitMover_MainWindow import Ui_MainWindow - from get_image_tag import get_exif_tag - from media import Media - - from lumberjack import timber -from raw_photo import extract_jpg_thumb +from raw_photo import extract_jpg_thumb, get_raw_image_dimensions log = timber(__name__) class WorkerSignals(QObject): """ - Defines the signals available from a running worker thread. + Defines the signals avail from a running worker thread. Supported signals are: finished - No data + No Data error tuple (exctype, value, traceback.format_exc() ) @@ -40,17 +36,16 @@ class WorkerSignals(QObject): progress int indicating % progress - """ + started = pyqtSignal() finished = pyqtSignal() error = pyqtSignal(tuple) result = pyqtSignal(object) progress = pyqtSignal(int) - class Worker(QRunnable): """ - Worker thread + Worker Thread Inherits from QRunnable to handler worker thread setup, signals and wrap-up. @@ -61,10 +56,10 @@ class Worker(QRunnable): :param kwargs: Keywords to pass to the callback function """ - def __init__(self, fn, *args, **kwargs): - super(Worker, self).__init__() + def __init__(self,fn,*args,**kwargs): + super(Worker,self).__init__() - # Store constructor arguments (re-used for processing) + # Store constructor args (re-used for processing) self.fn = fn self.args = args self.kwargs = kwargs @@ -78,18 +73,18 @@ class Worker(QRunnable): """ Initialise the runner function with passed args, kwargs. """ - # Retrieve args/kwargs here; and fire processing using them try: + self.signals.started.emit() result = self.fn(*self.args, **self.kwargs) except: traceback.print_exc() exctype, value = sys.exc_info()[:2] - self.signals.error.emit((exctype, value, traceback.format_exc())) + self.signals.error.emit((exctype,value,traceback.format_exc())) else: - self.signals.result.emit(result) # Return the result of the processing + self.signals.result.emit(result) finally: - self.signals.finished.emit() # Done + self.signals.finished.emit() # Subclass QMainWindow to customize your application's main window class MainWindow(QMainWindow, Ui_MainWindow): @@ -100,12 +95,17 @@ class MainWindow(QMainWindow, Ui_MainWindow): c = Configure(CONFIG_FILE) self.config = c.load_config() self.total_files = 0 - - # self.t_find_files = threading.Thread(target=self.find_files) + self.file_total = 0 + self.threads = {} + self.lcd_files_found.display(int(0)) + self.set_progress(0, 0) self.setWindowTitle("BitMover") self.setWindowIcon(QIcon('assets/forklift.png')) + self.threadpool = QThreadPool() + print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) + self.src_dir = self.config['folders']['source']['base'] self.dst_dir = self.config['folders']['destination']['base'] @@ -117,20 +117,24 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.pushButton_src_browse.clicked.connect(self.select_src_directory) self.pushButton_dst_browse.clicked.connect(self.select_dst_directory) self.pushButton_3_scan_dir.clicked.connect(self.find_files) + + self.toggle_scan_button(True) # self.pushButton_3_scan_dir.clicked.connect(self.t_find_files.start) - self.lcd_files_found.display(int(0)) - self.set_progress(0,0) + self.img_preview.setPixmap(QPixmap('assets/preview_placeholder.jpg')) self.img_preview.setScaledContents(True) - # self.img_preview.setFixedWidth(preview_width) - # self.img_preview.setFixedHeight(preview_height) self.file_list.currentItemChanged.connect(self.index_changed) - self.threadpool = QThreadPool() self.files = {} + def toggle_scan_button(self,enable=True): + if enable: + self.pushButton_3_scan_dir.setEnabled(True) + else: + self.pushButton_3_scan_dir.setDisabled(True) + def index_changed(self,i): f = i.text() @@ -143,19 +147,17 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.label_data_date_time_created.setText(dtc) if m.file_type == 'image': - # img = Image.open(f) dpi = get_exif_tag(f,"xresolution") - width = get_exif_tag(f,"image width") - height = get_exif_tag(f,"image height") - if width is None: - get_exif_tag(f,"exifimagewidth") - if height is None: - get_exif_tag(f,"exifimagelength") - # width = img.width - # height = img.height + if f.lower().endswith("jpg") or f.lower().endswith("jpeg"): + img = Image.open(f) + width = img.width + height = img.height + else: + width = get_raw_image_dimensions(f)[1] + height = get_raw_image_dimensions(f)[0] size = f'{width}x{height}' if width is not None and height is not None: - mpixels = (width * height) / 1000000 + mpixels = round((width * height) / 1000000, 1) else: mpixels = '' iso = get_exif_tag(f,"iso") @@ -191,7 +193,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): jpg = extract_jpg_thumb(f) self.img_preview.setPixmap(QPixmap(jpg)) - def select_src_directory(self): directory = QFileDialog.getExistingDirectory(self, "Select Directory", @@ -212,74 +213,99 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.dst_dir = directory self.lineEdit_dst_dir.setText(self.dst_dir) + def set_total_files(self,t): + self.total_files = t + self.lcd_files_found.display(self.total_files) + + def progress_fn(self,n): + # print("%d%% done" % n) + self.progressBar_overall.setValue(int(n)) + def set_progress(self,p,t): """ set progress for bar, p = progress counter t = target total - o = progress bar object """ if int(t) == 0: t += 1 - # - # while QPainter.isActive(Ui_MainWindow.QPainter()): - # print('painter active') - # time.sleep(0.02) percent_complete = (int(p) / int(t)) * 100 self.progressBar_overall.setValue(int(percent_complete)) - def print_output(self, s): - print(s) - - def thread_complete(self): - print("THREAD COMPLETE!") - - def thread_find_files(self): - print('in thread_find_files') - print(self.src_dir) - worker = Worker(self.find_files()) - worker.signals.result.connect(self.print_output) - worker.signals.finished.connect(self.thread_complete) - worker.signals.progress.connect(self.progress_fn) - self.threadpool.start(worker) - - - - def find_files(self): - """ find files to build a dictionary out of """ - log.info('In find_files') - print(self.src_dir) + def get_t_files(self): + for folder, subfolders, filename in os.walk(self.src_dir): + for f_type in self.file_types: + for ext in self.file_types[f_type]: + for file in filename: + if file.lower().endswith(ext): + current_file = os.path.join(folder, file) + if is_file(current_file): + self.file_total += int(1) + else: + print(f"Skipping {current_file} as it does not look like a real file.") + def t_find_files(self,progress_callback): file_count = int(0) - file_total = get_t_files(self.src_dir,self.file_types) + self.get_t_files() + self.set_total_files(self.file_total) for folder, subfolders, filename in os.walk(self.src_dir): for f_type in self.file_types: for ext in self.file_types[f_type]: - # for file in tqdm(filename, - # desc='Finding ' + ext + ' Files in ' + folder): for file in filename: if file.lower().endswith(ext): current_file = os.path.join(folder, file) if is_file(current_file): file_count += int(1) - self.process_file(folder, file) - self.set_progress(file_count,file_total) + self.process_file(current_file) + # self.set_progress(file_count,file_total) # time.sleep(.02) else: print(f"Skipping {current_file} as it does not look like a real file.") - progress_callback.emit((file_count / file_total) * 100) + progress_callback.emit((file_count / self.file_total) * 100) + return "Done." + + def print_output(self,s): + print(s) + + def scan_thread_started(self): + print('scan thread started') + self.toggle_scan_button(False) + + def scan_thread_done(self): + print('scan thread complete.') + self.toggle_scan_button(True) + + def thread_complete(self): + print("THREAD COMPLETE.") + + def find_files(self): + """ find files to build a dictionary out of """ + worker = Worker(self.t_find_files) + # worker.signals.started.connect(self.toggle_scan_button(enable=False)) + worker.signals.started.connect(self.scan_thread_started) + worker.signals.result.connect(self.print_output) + worker.signals.finished.connect(self.thread_complete) + worker.signals.finished.connect(self.scan_thread_done) + # worker.signals.finished.connect(self.toggle_scan_button(enable=True)) + worker.signals.progress.connect(self.progress_fn) + + # Execute. + self.threadpool.start(worker) def get_event(self): event_name = self.eventName.text() return event_name - def process_file(self,path_name, f_name): + def process_file(self,p): """ gather information and add to dictionary """ + path_name = os.path.dirname(p) + f_name = os.path.basename(p) + event = self.get_event() c = self.config diff --git a/dedup.py b/dedup.py index 1ee3935..abd02c0 100755 --- a/dedup.py +++ b/dedup.py @@ -19,7 +19,7 @@ args = parser.parse_args() if args.folder: FOLDER = args.folder else: - print("you need to specify a folder.") + print("you need to specify a folder. Dedup") sys.exit() if args.dryrun: dry_run = True diff --git a/file_stuff.py b/file_stuff.py index c0a6f69..6369e3a 100644 --- a/file_stuff.py +++ b/file_stuff.py @@ -126,18 +126,3 @@ def validate_config_dir_access(config): accessible = True return accessible -def get_t_files(p,t): - total = int(0) - for folder, subfolders, filename in os.walk(p): - for f_type in t: - for ext in t[f_type]: - # for file in tqdm(filename, - # desc='Finding ' + ext + ' Files in ' + folder): - for file in filename: - if file.lower().endswith(ext): - current_file = os.path.join(folder, file) - if is_file(current_file): - total += int(1) - else: - print(f"Skipping {current_file} as it does not look like a real file.") - return total \ No newline at end of file diff --git a/raw_photo.py b/raw_photo.py index eed76e4..5e29b9a 100644 --- a/raw_photo.py +++ b/raw_photo.py @@ -21,8 +21,8 @@ def extract_jpg_thumb(raw_file_path): return 'thumbnail.jpg' -# thumbnail_path = extract_jpg_thumb('your_raw_image.nef') -# if thumbnail_path: -# print("Thumbnail extracted to:", thumbnail_path) -# else: -# print("No thumbnail found or unsupported format.") \ No newline at end of file +def get_raw_image_dimensions(raw_file_path): + i = imread(raw_file_path) + height, width = i.raw_image.shape[:2] + + return height,width \ No newline at end of file diff --git a/rename.py b/rename.py index 47333c8..8c7162a 100755 --- a/rename.py +++ b/rename.py @@ -19,7 +19,7 @@ args = parser.parse_args() if args.folder: FOLDER = args.folder else: - print("you need to specify a folder.") + print("you need to specify a folder. Duh.") sys.exit() if args.dryrun: dry_run = True