start threading
This commit is contained in:
+109
-19
@@ -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()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user