Skip to content

Commit eecbaec

Browse files
committed
Update changelog, efX-SDK, and logging path
1 parent 731ee1a commit eecbaec

File tree

9 files changed

+132
-21
lines changed

9 files changed

+132
-21
lines changed

HISTORY.rst

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22
History
33
=======
44

5+
0.2.0 (2021-01-04)
6+
------------------
7+
8+
* Added efX-SDK library
9+
* Added module to export/convert `nsihdr` files to uint `raw` files using the efX-SDK (Windows only)
10+
* Tentatively implemented a GUI for the nsihdr2raw export tool
11+
* Tweaked logging path for log files generated relative to input data for `nsihdr2raw`
12+
13+
0.1.4 (2020-10-27)
14+
------------------
15+
16+
* Code clean up, remove unnecessary transposing of data, improved debug statements and logging
17+
18+
0.1.3 (2020-10-26)
19+
------------------
20+
21+
* Fixed font file missing when installing via pip
22+
23+
524
0.1.2 (2020-09-25)
625
------------------
726

@@ -10,4 +29,4 @@ History
1029
0.1.0 (2020-06-19)
1130
------------------
1231

13-
* First release on PyPI.
32+
* Initial version

rawtools/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ def raw_generate():
6262
generate.main(args)
6363

6464
def raw_nsihdr():
65-
description = "This tool converts a NSI project from 32-bit float to 16-bit unsigned integer format, and it extracts the midslice and generates a side-view projection of the volume."
65+
description = "This tool converts a NSI project from 32-bit float to 16-bit unsigned integer format."
6666

6767
parser = argparse.ArgumentParser(description=description, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
6868
parser.add_argument("-V", "--version", action="version", version=f'%(prog)s {__version__}')
6969
parser.add_argument("-v", "--verbose", action="store_true", help="Increase output verbosity")
7070
parser.add_argument("-f", "--force", action="store_true", default=False, help="Force file creation. Overwrite any existing files.")
71-
parser.add_argument("--gui", action="store_true", default=False, help="Enable GUI")
71+
parser.add_argument("--gui", action="store_true", default=False, help="(Experimental) Enable GUI")
7272
parser.add_argument('path', metavar='PATH', type=str, nargs="+", help='List of .nsihdr files')
7373
args = parser.parse_args()
7474

rawtools/gui/nsihdr.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313

1414
__version__ = version('rawtools')
1515

16-
class GuiState(Enum):
17-
IDLE = 1
18-
PROCESSING = 2
19-
ERROR = 3
20-
2116
def center(root, toplevel):
2217
toplevel.update_idletasks()
2318

@@ -37,13 +32,12 @@ def center(root, toplevel):
3732

3833
class App():
3934
def __init__(self, args):
40-
self.source = 'C:/Users/Tim Parker/Datasets/topp/xrt/development/batch-export'
35+
self.source = 'C:/Users/Tim Parker/Datasets/topp/xrt/development/batch2'
4136
self.args = args
4237
# Source: https://www.elegantthemes.com/blog/freebie-of-the-week/beautiful-flat-icons-for-free
4338
self.icon_fp = "rawtools\\assets\\tools.ico"
4439
self.icon_caution_fp = "rawtools\\assets\\caution.ico"
45-
self.state = GuiState.IDLE
46-
40+
self.state = 'idle'
4741

4842
self.root = ThemedTk(theme='arc')
4943
root = self.root
@@ -209,11 +203,11 @@ def export(self):
209203
self.prompt.title(prompt_title)
210204
self.prompt.iconbitmap(self.icon_fp)
211205
self.prompt.resizable(False, False)
212-
self.prompt_frame = ttk.Frame(self.prompt, padding="16 16")
213-
self.prompt_frame.grid(column=0, row=0, sticky=(N, S, E, W))
214-
self.prompt_message = ttk.Label(self.prompt_frame, text=prompt_message).grid(row = 0, column = 0, columnspan=4, pady="0 32")
215-
self.prompt_button = ttk.Button(self.prompt_frame, text="Ok", command=self.dismiss).grid(row = 1, column = 1, columnspan=1)
216-
self.prompt_button = ttk.Button(self.prompt_frame, text="Cancel", command=self.cancel_export).grid(row = 1, column = 2, columnspan=1)
206+
prompt_frame = ttk.Frame(self.prompt, padding="16 16")
207+
prompt_frame.grid(column=0, row=0, sticky=(N, S, E, W))
208+
self.prompt_message = ttk.Label(prompt_frame, text=prompt_message).grid(row = 0, column = 0, columnspan=4, pady="0 32")
209+
self.prompt_button = ttk.Button(prompt_frame, text="Ok", command=self.dismiss).grid(row = 1, column = 1, columnspan=1)
210+
self.prompt_button = ttk.Button(prompt_frame, text="Cancel", command=self.cancel_export).grid(row = 1, column = 2, columnspan=1)
217211

218212
# Orient window on screen
219213
center(self.root, self.prompt)
@@ -224,11 +218,14 @@ def export(self):
224218
self.prompt.grab_set()
225219
self.prompt.wait_window()
226220

221+
self.prompt_frame.grid_forget()
222+
self.prompt = None
223+
227224
# Process data
228225
if not self.cancelled:
229226
# Do processing
230227
logging.debug(self.args)
231-
self.args.gui_window = self.root
228+
self.args.app = self
232229
nsihdr.main(self.args)
233230
else:
234231
logging.debug(f"Cancelled export")
@@ -254,3 +251,8 @@ def cancel_export(self):
254251
def dismiss(self):
255252
self.prompt.grab_release()
256253
self.prompt.destroy()
254+
255+
def dismiss_progress_prompt(self):
256+
self.progress_bar_prompt.grab_release()
257+
self.progress_bar_prompt.destroy()
258+
1.57 KB
Binary file not shown.

rawtools/lib/win32/efX-SDK.dll

1 KB
Binary file not shown.
1.57 KB
Binary file not shown.

rawtools/lib/win64/efX-SDK.dll

1 KB
Binary file not shown.

rawtools/log.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ def configure(args):
2222
# Set project-level logging
2323
if args.module_name is not None:
2424
logfile_basename = f"{dt.today().strftime('%Y-%m-%d_%H-%M-%S')}_{args.module_name}.log"
25-
lfp = os.path.join(os.path.realpath(os.path.dirname(args.path[0])), logfile_basename) # base log file path
25+
rpath = os.path.realpath(args.path[0])
26+
logging.info(f"{rpath=}")
27+
if os.path.isdir(rpath):
28+
dname = rpath
29+
else:
30+
dname = os.path.dirname(rpath)
31+
logging.info(f"{dname=}")
32+
lfp = os.path.join(dname, logfile_basename) # base log file path
33+
logging.info(f"{lfp=}")
2634
fileHandler = logging.FileHandler(lfp)
2735
fileHandler.setFormatter(logFormatter)
2836
fileHandler.setLevel(logging.DEBUG) # always show debug statements in log file

rawtools/nsihdr.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
"""NSIHDR to RAW Batch Converter"""
44
import logging
55
import os
6-
import re
76
import sys
7+
import tkinter as tk
88
from multiprocessing import Pool, cpu_count
99
from pprint import pformat
1010
from time import time
11-
11+
from tkinter import (E, N, S, StringVar, Toplevel, W, filedialog, messagebox,
12+
ttk)
13+
import threading
1214
import numpy as np
1315
from tqdm import tqdm
1416

1517
from rawtools import dat
1618
from rawtools.convert import scale
19+
from rawtools.gui import nsihdr
1720

1821
# Load in NSI SDK
1922
currentdir = os.path.dirname(os.path.realpath(__file__))
@@ -22,6 +25,34 @@
2225
sys.path.append(includesdir)
2326
from rawtools import nsiefx
2427

28+
# def check_progress(amount):
29+
# logging.info(amount)
30+
# # progress_text.set(str(round(total_slices_processed / args.total_slice_count * 100.0, 1)))
31+
# progress_text.set(str(total_slices_processed))
32+
# progress['value'] = total_slices_processed
33+
# logging.info(f"{progress_text.get()=}")
34+
35+
36+
def update_progress(increment):
37+
logging.debug(f"{increment=}")
38+
39+
def start_progress_thread(event, pbar, root):
40+
global progress_thread
41+
progress_thread = threading.Thread(target=update_progress, args=(1,))
42+
progress_thread.daemon = True
43+
# pbar.start()
44+
progress_thread.start()
45+
root.after(20, check_progress_thread(pbar, root))
46+
47+
def check_progress_thread(pbar, root):
48+
if progress_thread.is_alive():
49+
logging.info("Pbar is alive")
50+
root.after(20, check_progress_thread)
51+
else:
52+
logging.info("Pbar is done")
53+
pbar.stop()
54+
55+
2556
def process(args, fp, export_path):
2657
"""Converts NSIHDR files to a single .RAW + .DAT
2758
@@ -32,6 +63,9 @@ def process(args, fp, export_path):
3263
export_path (str): filepath to output .RAW file
3364
"""
3465
logging.debug(f'{fp=}')
66+
total_slices_processed = 0
67+
progress = None
68+
check_progress = None
3569

3670
with nsiefx.open(fp) as volume:
3771
v = volume # for shorthand laziness
@@ -52,7 +86,6 @@ def process(args, fp, export_path):
5286
bname = os.path.basename(os.path.splitext(fp)[0])
5387
dat_path = os.path.join(dname, f'{bname}.dat')
5488

55-
5689
if os.path.exists(export_path) and args.force == True:
5790
os.remove(export_path)
5891
logging.warning(f"Removed old '{export_path}'")
@@ -63,6 +96,7 @@ def process(args, fp, export_path):
6396
dat.write(dat_path, dimensions = (width, height, depth), thickness = voxel_size)
6497
logging.debug(f"Generated '{dat_path}'")
6598

99+
pbar = None
66100
with open(export_path, 'ab') as raw_ofp:
67101
if not args.verbose:
68102
pbar = tqdm(total= depth, desc=f"Exporting {bname}")
@@ -72,8 +106,11 @@ def process(args, fp, export_path):
72106
cross_section = scale(cross_section, data_min, data_max, 0, 65535).astype(np.uint16)
73107
cross_section.tofile(raw_ofp)
74108

109+
total_slices_processed += 1
110+
75111
if not args.verbose:
76112
pbar.update()
113+
# logging.debug(f"Processed {total_slices_processed}")
77114
if not args.verbose:
78115
pbar.close()
79116

@@ -141,9 +178,54 @@ def main(args):
141178
logging.error(err)
142179
raise err
143180
else:
181+
182+
# GUI Implementation
183+
if args.gui:
184+
# Determine the number of slices in advance
185+
args.total_slice_count = 0
186+
for fp in args.files:
187+
with nsiefx.open(fp) as volume:
188+
args.total_slice_count += volume.num_slices()
189+
190+
# Initialize progress bar
191+
progress_bar_prompt_title = "Placeholder Progress Bar"
192+
app = args.app
193+
root = app.root
194+
icon_fp = app.icon_fp
195+
progress_bar_prompt = Toplevel(root)
196+
progress_bar_prompt.title(progress_bar_prompt_title)
197+
progress_bar_prompt.iconbitmap(icon_fp)
198+
progress_bar_prompt.resizable(False, False)
199+
progress_bar_prompt_frame = ttk.Frame(progress_bar_prompt, padding="16 16")
200+
progress_bar_prompt_frame.grid(column=0, row=0, sticky=(N, S, E, W))
201+
202+
progress = ttk.Progressbar(progress_bar_prompt_frame, orient='horizontal', mode='indeterminate', length=200, max=args.total_slice_count)
203+
progress.grid(row=0, column=0, columnspan=2, pady="0 16", sticky=(E,W))
204+
progress_text = tk.StringVar()
205+
progress_text_label = ttk.Label(progress_bar_prompt_frame, textvariable=progress_text, width=7)
206+
progress_text_label.grid(row=0, column=3, columnspan=1, pady="0 16", sticky=E, padx="8 0")
207+
progress_text.set('0%')
208+
209+
def dismiss_progress_prompt():
210+
progress_bar_prompt.grab_release()
211+
progress_bar_prompt.destroy()
212+
213+
# Orient window on screen
214+
nsihdr.center(root, progress_bar_prompt)
215+
# Disable interaction with parent window
216+
progress_bar_prompt.protocol("WM_DELETE_WINDOW", dismiss_progress_prompt)
217+
progress_bar_prompt.transient(root)
218+
progress_bar_prompt.wait_visibility()
219+
progress_bar_prompt.grab_set()
220+
progress_bar_prompt.wait_window()
221+
start_progress_thread(None, progress, root)
222+
223+
# CLI Implementation
144224
# For each provided volume...
225+
pbar = None
145226
if not args.verbose:
146227
pbar = tqdm(total = len(args.files), desc=f"Overall progress")
228+
147229
for fp in args.files:
148230
logging.debug(f"Processing '{fp}'")
149231
dname = os.path.dirname(fp)

0 commit comments

Comments
 (0)