start threading

This commit is contained in:
Ross Kruger
2024-06-21 11:27:40 +01:00
parent e52349ab46
commit 30f8d040c8
+109 -19
View File
@@ -77,11 +77,74 @@ import time
import datetime import datetime
import contextlib import contextlib
import threading
# import watchdog.observers # import watchdog.observers
__VERSION__ = "0.1" __VERSION__ = "0.1"
__APP_NAME__ = "Uploader" __APP_NAME__ = "Uploader"
# Define notification event for thread completion
EVT_RESULT_ID = wx.NewId()
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""
def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
# Thread class that executes processing
class WorkerThread(threading.Thread):
"""Worker Thread Class."""
def __init__(self, notify_window):
"""Init Worker Thread Class."""
threading.Thread.__init__(self)
self._notify_window = notify_window
self._want_abort = 0
# This starts the thread running on creation, but you could
# also make the GUI thread responsible for calling this
self.start()
def run(self):
"""Run Worker Thread."""
# This is the code executing in the new thread. Simulation of
# a long process (well, 10s here) as a simple loop - you will
# need to structure your processing so that you periodically
# peek at the abort variable
for i in range(10):
time.sleep(1)
if self._want_abort:
# Use a result of None to acknowledge the abort (of
# course you can use whatever you'd like or even
# a separate event type)
wx.PostEvent(self._notify_window, ResultEvent(None))
return
# Here's where the result would be returned (this is an
# example fixed result of the number 10, but it could be
# any Python object)
wx.PostEvent(self._notify_window, ResultEvent(10))
def abort(self):
"""abort worker thread."""
# Method for use by main thread to signal an abort
self._want_abort = 1
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
anonymizer = dicognito.anonymizer.Anonymizer() anonymizer = dicognito.anonymizer.Anonymizer()
# determine if application is a script file or frozen exe # determine if application is a script file or frozen exe
@@ -194,6 +257,7 @@ def annonymise(
i += 1 i += 1
# new_filepath = Path(output_dir).joinpath(f"IMG_{i:03}.dcm") # new_filepath = Path(output_dir).joinpath(f"IMG_{i:03}.dcm")
logger.debug(f"anon: {n}")
anonymize_file(file, new_filepath) anonymize_file(file, new_filepath)
# progress_dialog.Destroy() # progress_dialog.Destroy()
@@ -201,6 +265,7 @@ def annonymise(
return new_filepath return new_filepath
@logger.catch()
def anonymize_file(file: Path, new_filepath: Path, remove_original: bool = False): def anonymize_file(file: Path, new_filepath: Path, remove_original: bool = False):
logger.debug(f"Annonymise file, in={file}, out={new_filepath}") logger.debug(f"Annonymise file, in={file}, out={new_filepath}")
# Ensure the output folder exists # Ensure the output folder exists
@@ -517,6 +582,8 @@ class AnonGui(wx.App):
self.rqst = None self.rqst = None
self.worker = None
wx.App.__init__(self, redirect, filename) wx.App.__init__(self, redirect, filename)
def my_listener(self, message, arg2=None): def my_listener(self, message, arg2=None):
@@ -571,7 +638,7 @@ class AnonGui(wx.App):
size=(300, 400), size=(300, 400),
) )
self.frame.SetIcon(wx.Icon("icon/icon1.ico")) self.frame.SetIcon(wx.Icon(resource_path("icon\icon1.ico")))
self.panel = wx.Panel(self.frame, wx.ID_ANY) self.panel = wx.Panel(self.frame, wx.ID_ANY)
@@ -834,6 +901,16 @@ class AnonGui(wx.App):
if input_dir.is_dir(): if input_dir.is_dir():
logger.debug("is dir") logger.debug("is dir")
files = list(Path(input_dir).rglob("*.dcm")) files = list(Path(input_dir).rglob("*.dcm"))
if not files:
# TODO load from DICOMDIR
to_exclude = ("DICOMDIR")
files = [i for i in Path(input_dir).rglob("*") if i.is_file() and i.name not in to_exclude]
else: else:
return return
@@ -859,20 +936,25 @@ class AnonGui(wx.App):
with self.progress(len(files), "Annonymise files") as p: with self.progress(len(files), "Annonymise files") as p:
# p.SetRange(len(files)) # p.SetRange(len(files))
i: int = 0
for n, file in enumerate(files): for n, file in enumerate(files):
# progress_dialog.Update(n) # progress_dialog.Update(n)
p.update(n) p.update(n)
new_filepath: Path new_filepath: Path
# Get next available filename # Get next available filename
i: int = 0
while os.path.exists( while os.path.exists(
new_filepath := output_dir.joinpath(f"IMG_{i:03}.dcm") new_filepath := output_dir.joinpath(f"IMG_{i:03}.dcm")
): ):
i += 1 i += 1
# new_filepath = Path(output_dir).joinpath(f"IMG_{i:03}.dcm") # new_filepath = Path(output_dir).joinpath(f"IMG_{i:03}.dcm")
dataset, filepath = anonymize_file(file, new_filepath, remove_original=remove_original) logger.debug(f"anon: {n}")
self.add_datails_to_file_detail_map(dataset, filepath) try:
dataset, filepath = anonymize_file(file, new_filepath, remove_original=remove_original)
except pydicom.errors.InvalidDicomError:
continue
self.add_datails_to_file_detail_map(dataset, filepath)
wx.GetApp().Yield()
# progress_dialog.Destroy() # progress_dialog.Destroy()
self.LoadDir(self.dir) self.LoadDir(self.dir)
@@ -942,7 +1024,7 @@ base site url: {self.base_site_url}
info.SetName(__APP_NAME__) info.SetName(__APP_NAME__)
info.SetVersion(__VERSION__) info.SetVersion(__VERSION__)
info.SetDescription(description) info.SetDescription(description)
icon_path = "icon/icon1.png" icon_path = resource_path("icon\icon1.png")
desired_width = 200 desired_width = 200
desired_height = 200 desired_height = 200
@@ -1136,7 +1218,12 @@ base site url: {self.base_site_url}
files = [Path(self.dir, f) for f, file_hash in files] files = [Path(self.dir, f) for f, file_hash in files]
for f in files: for f in files:
os.remove(f) try:
os.remove(f)
except OSError:
logger.debug(f"File to delete not found: {f}")
pass
if load_dir: if load_dir:
self.LoadDir(clear_file_detail_map=True) self.LoadDir(clear_file_detail_map=True)
@@ -1252,20 +1339,22 @@ base site url: {self.base_site_url}
hashes = [] hashes = []
for n, f in enumerate(self.files): for n, f in enumerate(self.files):
logger.debug(f"update {n}")
# progress_dialog.Update(n) # progress_dialog.Update(n)
p.update(n) #p.update(n)
# dataset = pydicom.read_file(f) # dataset = pydicom.read_file(f)
if f in self.file_detail_map: if f in self.file_detail_map:
d, hash, d2 = self.file_detail_map[f] d, hash, d2 = self.file_detail_map[f]
print(f"{n}: in map") #print(f"{n}: in map")
else: else:
logger.debug("read: {f}")
try: try:
dataset = pydicom.dcmread(f) dataset = pydicom.dcmread(f)
except FileNotFoundError: except FileNotFoundError:
return return
print("recalc") #print("recalc")
d, hash, d2 = self.add_datails_to_file_detail_map(dataset, f) d, hash, d2 = self.add_datails_to_file_detail_map(dataset, f)
hashes.append(hash) hashes.append(hash)
@@ -1283,18 +1372,18 @@ base site url: {self.base_site_url}
except: except:
logger.debug(f"Unable to get Series Instance UID: {f.name}") logger.debug(f"Unable to get Series Instance UID: {f.name}")
logger.debug(d) #logger.debug(d)
DATA[n] = d #DATA[n] = d
self.list_ctrl.Append(d) #self.list_ctrl.Append(d)
self.list_ctrl.SetItemPyData(n, {"file": f, "hash": hash}) #self.list_ctrl.SetItemPyData(n, {"file": f, "hash": hash})
self.list_ctrl.SetItemData(n, n) ##self.list_ctrl.SetItemData(n, n)
if f in self.file_duplicate_check: if f in self.file_duplicate_check:
if self.file_duplicate_check[f]["id"]: if self.file_duplicate_check[f]["id"]:
self.list_ctrl.SetItemBackgroundColour(n, wx.RED) self.list_ctrl.SetItemBackgroundColour(n, wx.RED)
n = n + 1 n = n + 1
self.list_ctrl.UpdateData(DATA) #self.list_ctrl.UpdateData(DATA)
self.populate_series_grid() self.populate_series_grid()
self.frame.Refresh() self.frame.Refresh()
@@ -1347,9 +1436,9 @@ base site url: {self.base_site_url}
duplicate_files_not_uploaded = [] duplicate_files_not_uploaded = []
logger.debug("Upload files") logger.debug("Upload files")
for f in self.files: for f in self.files:
if f in self.file_duplicate_check: if f in self.file_duplicate_check and self.file_duplicate_check[f]["id"]:
# duplicate the api return format so ClearFiles works # duplicate the api return format so ClearFiles works
duplicate_files_not_uploaded.append((f.name, "...")) duplicate_files_not_uploaded.append((f.name, "..."))
else: else:
files_to_upload.append(("files", open(str(f), "rb"))) files_to_upload.append(("files", open(str(f), "rb")))
@@ -1420,7 +1509,7 @@ base site url: {self.base_site_url}
if self.clear_files_on_upload: if self.clear_files_on_upload:
all_files = upload_file_list + duplicate_file_list + duplicate_files_not_uploaded all_files = upload_file_list + duplicate_file_list + duplicate_files_not_uploaded
print(all_files) print(all_files)
self.ClearFiles(event=None, load_dir=True, files=all_files) wx.CallAfter(self.ClearFiles,event=None, load_dir=True, files=all_files)
def upload_complete_message(self, upload_file_list, duplicate_file_list, failed, duplicate_files_not_uploaded): def upload_complete_message(self, upload_file_list, duplicate_file_list, failed, duplicate_files_not_uploaded):
wx.MessageBox( wx.MessageBox(
@@ -1501,3 +1590,4 @@ def launchGUI(
if __name__ == "__main__": if __name__ == "__main__":
app() app()