flimkit 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flimkit/FLIM/__init__.py +0 -0
- flimkit/FLIM/assemble.py +254 -0
- flimkit/FLIM/batch.py +681 -0
- flimkit/FLIM/bg_tools.py +51 -0
- flimkit/FLIM/fit_tools.py +244 -0
- flimkit/FLIM/fitters.py +1471 -0
- flimkit/FLIM/irf_tools.py +617 -0
- flimkit/FLIM/models.py +391 -0
- flimkit/GPU/__init__.py +85 -0
- flimkit/GPU/_base.py +391 -0
- flimkit/GPU/cuda.py +10 -0
- flimkit/GPU/mlx_backend.py +381 -0
- flimkit/GPU/mps.py +10 -0
- flimkit/GPU/rocm.py +10 -0
- flimkit/GPU/torch_backend.py +385 -0
- flimkit/UI/app_state.py +10 -0
- flimkit/UI/controller.py +139 -0
- flimkit/UI/expert_settings.py +248 -0
- flimkit/UI/fit_help.py +206 -0
- flimkit/UI/fov_preview.py +1085 -0
- flimkit/UI/gui.py +3919 -0
- flimkit/UI/icon.icns +0 -0
- flimkit/UI/icon.ico +0 -0
- flimkit/UI/icon.png +0 -0
- flimkit/UI/irf_widget.py +103 -0
- flimkit/UI/mode_controller.py +118 -0
- flimkit/UI/modes/__init__.py +0 -0
- flimkit/UI/modes/base.py +3 -0
- flimkit/UI/modes/batch_mode.py +312 -0
- flimkit/UI/modes/fov_mode.py +164 -0
- flimkit/UI/modes/irf_mode.py +80 -0
- flimkit/UI/modes/phasor_mode.py +131 -0
- flimkit/UI/modes/stitch_mode.py +254 -0
- flimkit/UI/phasor_panel.py +1087 -0
- flimkit/UI/progress_window.py +113 -0
- flimkit/UI/project_panel.py +262 -0
- flimkit/UI/results_panel.py +332 -0
- flimkit/UI/roi_tools.py +794 -0
- flimkit/UI/utils.py +217 -0
- flimkit/__init__.py +0 -0
- flimkit/_version.py +41 -0
- flimkit/cli.py +120 -0
- flimkit/configs.py +148 -0
- flimkit/dialogs.py +46 -0
- flimkit/formats/BH/__init__.py +0 -0
- flimkit/formats/BH/reader.py +296 -0
- flimkit/formats/BH/writer.py +86 -0
- flimkit/formats/ISS/__init__.py +0 -0
- flimkit/formats/ISS/fdflim.py +86 -0
- flimkit/formats/ISS/image.py +114 -0
- flimkit/formats/ISS/reader.py +223 -0
- flimkit/formats/PS/__init__.py +0 -0
- flimkit/formats/PS/reader.py +202 -0
- flimkit/formats/PTU/__init__.py +0 -0
- flimkit/formats/PTU/decode.py +27 -0
- flimkit/formats/PTU/phu.py +85 -0
- flimkit/formats/PTU/reader.py +235 -0
- flimkit/formats/PTU/series.py +258 -0
- flimkit/formats/PTU/stitch.py +1182 -0
- flimkit/formats/PTU/tools.py +94 -0
- flimkit/formats/__init__.py +2 -0
- flimkit/formats/flim_file.py +232 -0
- flimkit/formats/phasor.py +132 -0
- flimkit/formats/signal.py +170 -0
- flimkit/image/tools.py +124 -0
- flimkit/interactive.py +1857 -0
- flimkit/mpl_backend.py +22 -0
- flimkit/phasor/__init__.py +40 -0
- flimkit/phasor/filters.py +127 -0
- flimkit/phasor/fret.py +654 -0
- flimkit/phasor/interactive.py +556 -0
- flimkit/phasor/peaks.py +186 -0
- flimkit/phasor/signal.py +90 -0
- flimkit/phasor_launcher.py +314 -0
- flimkit/plugins/__init__.py +137 -0
- flimkit/plugins/bindings.py +116 -0
- flimkit/plugins/builtin/__init__.py +3 -0
- flimkit/plugins/builtin/core_tools.py +28 -0
- flimkit/plugins/loader.py +371 -0
- flimkit/plugins/registry.py +406 -0
- flimkit/project.py +197 -0
- flimkit/synth.py +145 -0
- flimkit/utils/__init__.py +0 -0
- flimkit/utils/batch_fit.py +301 -0
- flimkit/utils/config_manager.py +119 -0
- flimkit/utils/config_snapshot.py +30 -0
- flimkit/utils/crash_handler.py +183 -0
- flimkit/utils/display.py +197 -0
- flimkit/utils/enhanced_outputs.py +345 -0
- flimkit/utils/fancy.py +103 -0
- flimkit/utils/lifetime_image.py +243 -0
- flimkit/utils/misc.py +111 -0
- flimkit/utils/plotting.py +190 -0
- flimkit/utils/roi.py +370 -0
- flimkit/utils/session.py +51 -0
- flimkit/utils/update_check.py +198 -0
- flimkit/utils/xlsx_tools.py +97 -0
- flimkit/utils/xml_utils.py +219 -0
- flimkit-0.12.0.dist-info/METADATA +356 -0
- flimkit-0.12.0.dist-info/RECORD +104 -0
- flimkit-0.12.0.dist-info/WHEEL +5 -0
- flimkit-0.12.0.dist-info/entry_points.txt +2 -0
- flimkit-0.12.0.dist-info/licenses/LICENSE.md +11 -0
- flimkit-0.12.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
class ProgressWindow:
|
|
6
|
+
def __init__(self, parent, task_name='Working...'):
|
|
7
|
+
self.parent = parent
|
|
8
|
+
self.cancelled = threading.Event()
|
|
9
|
+
self.top = tk.Toplevel(parent)
|
|
10
|
+
self.top.title(task_name)
|
|
11
|
+
self.top.geometry('400x120')
|
|
12
|
+
self.top.resizable(True, True)
|
|
13
|
+
self.top.transient(parent)
|
|
14
|
+
self.top.grab_set()
|
|
15
|
+
self.top.protocol('WM_DELETE_WINDOW', self.cancel)
|
|
16
|
+
|
|
17
|
+
# Make the window scalable with columns and rows
|
|
18
|
+
self.top.columnconfigure(0, weight=1)
|
|
19
|
+
self.top.rowconfigure(1, weight=1)
|
|
20
|
+
|
|
21
|
+
ttk.Label(self.top, text=task_name, font=('Arial', 12, 'bold')).grid(
|
|
22
|
+
row=0, column=0, pady=(16, 8), sticky='ew', padx=16)
|
|
23
|
+
self.progress = ttk.Progressbar(self.top, orient='horizontal', mode='determinate')
|
|
24
|
+
self.progress.grid(row=1, column=0, pady=6, padx=16, sticky='ew')
|
|
25
|
+
self.status = tk.StringVar(value='Starting...')
|
|
26
|
+
ttk.Label(self.top, textvariable=self.status).grid(
|
|
27
|
+
row=2, column=0, pady=(2, 8), padx=16, sticky='ew')
|
|
28
|
+
self.btn_cancel = ttk.Button(self.top, text='Cancel', command=self.cancel)
|
|
29
|
+
self.btn_cancel.grid(row=3, column=0, pady=(0, 8), sticky='ew', padx=16)
|
|
30
|
+
|
|
31
|
+
def set_progress(self, value, maximum=None):
|
|
32
|
+
if maximum is not None:
|
|
33
|
+
self.progress['maximum'] = maximum
|
|
34
|
+
self.progress['value'] = value
|
|
35
|
+
self.status.set(f"{int(value)}/{int(self.progress['maximum'])} completed")
|
|
36
|
+
self.top.update_idletasks()
|
|
37
|
+
|
|
38
|
+
def set_status(self, msg):
|
|
39
|
+
self.status.set(msg)
|
|
40
|
+
self.top.update_idletasks()
|
|
41
|
+
|
|
42
|
+
def cancel(self):
|
|
43
|
+
self.cancelled.set()
|
|
44
|
+
self.set_status('Cancelling...')
|
|
45
|
+
self.btn_cancel.config(state='disabled')
|
|
46
|
+
|
|
47
|
+
def close(self):
|
|
48
|
+
self.top.grab_release()
|
|
49
|
+
self.top.destroy()
|
|
50
|
+
|
|
51
|
+
def was_cancelled(self):
|
|
52
|
+
return self.cancelled.is_set()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ProgressWindowManager:
|
|
56
|
+
|
|
57
|
+
def __init__(self, root):
|
|
58
|
+
self.root = root
|
|
59
|
+
self.windows = {}
|
|
60
|
+
self.counter = 0
|
|
61
|
+
self.lock = threading.Lock()
|
|
62
|
+
|
|
63
|
+
def create_progress_window(self, task_name='Processing...'):
|
|
64
|
+
with self.lock:
|
|
65
|
+
window_id = self.counter
|
|
66
|
+
self.counter += 1
|
|
67
|
+
|
|
68
|
+
def create_window():
|
|
69
|
+
window_ref = ProgressWindow(self.root, task_name=task_name)
|
|
70
|
+
with self.lock:
|
|
71
|
+
self.windows[window_id] = window_ref
|
|
72
|
+
|
|
73
|
+
self.root.after(0, create_window)
|
|
74
|
+
return window_id
|
|
75
|
+
|
|
76
|
+
def update_progress(self, window_id, current, total):
|
|
77
|
+
def update():
|
|
78
|
+
if window_id in self.windows:
|
|
79
|
+
try:
|
|
80
|
+
self.windows[window_id].set_progress(current, maximum=total)
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
self.root.after(0, update)
|
|
85
|
+
|
|
86
|
+
def set_status(self, window_id, msg):
|
|
87
|
+
def update():
|
|
88
|
+
if window_id in self.windows:
|
|
89
|
+
try:
|
|
90
|
+
self.windows[window_id].set_status(msg)
|
|
91
|
+
except Exception:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
self.root.after(0, update)
|
|
95
|
+
|
|
96
|
+
def close_window(self, window_id):
|
|
97
|
+
def close():
|
|
98
|
+
if window_id in self.windows:
|
|
99
|
+
try:
|
|
100
|
+
self.windows[window_id].close()
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
finally:
|
|
104
|
+
with self.lock:
|
|
105
|
+
self.windows.pop(window_id, None)
|
|
106
|
+
|
|
107
|
+
self.root.after(0, close)
|
|
108
|
+
|
|
109
|
+
def close_all(self):
|
|
110
|
+
with self.lock:
|
|
111
|
+
ids = list(self.windows.keys())
|
|
112
|
+
for wid in ids:
|
|
113
|
+
self.close_window(wid)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk, filedialog
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
_ICON_FIT_ONLY = '●'
|
|
6
|
+
_ICON_PHASOR_ONLY = '◐'
|
|
7
|
+
_ICON_BOTH = '◉'
|
|
8
|
+
_ICON_NOSESSION = '○'
|
|
9
|
+
_ICON_FOV = 'F'
|
|
10
|
+
_ICON_XLIF = 'T'
|
|
11
|
+
_ICON_ZSTACK = 'Z'
|
|
12
|
+
|
|
13
|
+
class ProjectBrowserPanel:
|
|
14
|
+
|
|
15
|
+
def __init__(self, parent, app, width=170):
|
|
16
|
+
self._app = app
|
|
17
|
+
self._width = width
|
|
18
|
+
self._project = None
|
|
19
|
+
self._stems = []
|
|
20
|
+
self.frame = ttk.Frame(parent, width=width)
|
|
21
|
+
self.frame.grid_propagate(False)
|
|
22
|
+
self.frame.columnconfigure(0, weight=1)
|
|
23
|
+
self.frame.rowconfigure(1, weight=1)
|
|
24
|
+
hdr = ttk.Frame(self.frame)
|
|
25
|
+
hdr.grid(row=0, column=0, sticky='ew', padx=4, pady=(6, 2))
|
|
26
|
+
ttk.Label(hdr, text='Project',
|
|
27
|
+
font=('TkDefaultFont', 9, 'bold')).pack(side='left')
|
|
28
|
+
lb_outer = ttk.Frame(self.frame, relief='sunken', borderwidth=1)
|
|
29
|
+
lb_outer.grid(row=1, column=0, sticky='nsew', padx=4, pady=2)
|
|
30
|
+
lb_outer.columnconfigure(0, weight=1)
|
|
31
|
+
lb_outer.rowconfigure(0, weight=1)
|
|
32
|
+
sb = ttk.Scrollbar(lb_outer, orient='vertical')
|
|
33
|
+
sb.grid(row=0, column=1, sticky='ns')
|
|
34
|
+
self._lb = tk.Listbox(
|
|
35
|
+
lb_outer,
|
|
36
|
+
yscrollcommand = sb.set,
|
|
37
|
+
selectmode = 'single',
|
|
38
|
+
activestyle = 'none',
|
|
39
|
+
font = ('Courier', 9),
|
|
40
|
+
relief = 'flat',
|
|
41
|
+
borderwidth = 0,
|
|
42
|
+
highlightthickness = 0,
|
|
43
|
+
exportselection = False,
|
|
44
|
+
)
|
|
45
|
+
self._lb.grid(row=0, column=0, sticky='nsew')
|
|
46
|
+
sb.config(command=self._lb.yview)
|
|
47
|
+
self._lb.bind('<<ListboxSelect>>', self._on_select)
|
|
48
|
+
self._sv_status = tk.StringVar(value='No project open')
|
|
49
|
+
ttk.Label(
|
|
50
|
+
self.frame,
|
|
51
|
+
textvariable = self._sv_status,
|
|
52
|
+
foreground = 'grey',
|
|
53
|
+
font = ('TkDefaultFont', 7),
|
|
54
|
+
wraplength = width - 12,
|
|
55
|
+
anchor = 'w',
|
|
56
|
+
).grid(row=2, column=0, sticky='ew', padx=6, pady=(2, 6))
|
|
57
|
+
self._setup_dnd()
|
|
58
|
+
|
|
59
|
+
def grid(self, **kw):
|
|
60
|
+
self.frame.grid(**kw)
|
|
61
|
+
|
|
62
|
+
def on_fit_done(
|
|
63
|
+
self,
|
|
64
|
+
stem,
|
|
65
|
+
out_st=None,
|
|
66
|
+
output_prefix=None,
|
|
67
|
+
ptu_dir=None,
|
|
68
|
+
):
|
|
69
|
+
if self._project is None:
|
|
70
|
+
return
|
|
71
|
+
self._project.update_after_fit(
|
|
72
|
+
stem,
|
|
73
|
+
out_st = out_st,
|
|
74
|
+
output_prefix = output_prefix,
|
|
75
|
+
ptu_dir = ptu_dir,
|
|
76
|
+
)
|
|
77
|
+
self._project.save()
|
|
78
|
+
self._refresh()
|
|
79
|
+
|
|
80
|
+
def on_phasor_done(self, stem):
|
|
81
|
+
if self._project is None:
|
|
82
|
+
return
|
|
83
|
+
self._project.update_after_phasor(stem)
|
|
84
|
+
self._project.save()
|
|
85
|
+
self._refresh()
|
|
86
|
+
|
|
87
|
+
def _setup_dnd(self):
|
|
88
|
+
try:
|
|
89
|
+
from tkinterdnd2 import DND_FILES, DND_TEXT
|
|
90
|
+
except ImportError:
|
|
91
|
+
return
|
|
92
|
+
try:
|
|
93
|
+
self._lb.drop_target_register(DND_FILES, DND_TEXT)
|
|
94
|
+
self._lb.dnd_bind('<<Drop>>', self._on_drop)
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
def _on_drop(self, event):
|
|
99
|
+
try:
|
|
100
|
+
paths = self._lb.tk.splitlist(event.data)
|
|
101
|
+
except Exception:
|
|
102
|
+
paths = [event.data]
|
|
103
|
+
for p in paths:
|
|
104
|
+
p = p.strip()
|
|
105
|
+
if not p:
|
|
106
|
+
continue
|
|
107
|
+
path = Path(p)
|
|
108
|
+
if path.is_dir():
|
|
109
|
+
self.load_folder(str(path))
|
|
110
|
+
if hasattr(self._app, '_add_to_recent'):
|
|
111
|
+
self._app._add_to_recent(str(path), 'project')
|
|
112
|
+
return
|
|
113
|
+
if path.is_file():
|
|
114
|
+
if hasattr(self._app, '_switch_form'):
|
|
115
|
+
self._app._switch_form('fov')
|
|
116
|
+
if hasattr(self._app, 'sv_ptu'):
|
|
117
|
+
self._app.sv_ptu.set(str(path))
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
def load_folder(self, folder):
|
|
121
|
+
if not folder:
|
|
122
|
+
return
|
|
123
|
+
from flimkit.project import ProjectFile
|
|
124
|
+
self._project = ProjectFile.load_or_create(Path(folder))
|
|
125
|
+
self._project.save()
|
|
126
|
+
if self._project.config:
|
|
127
|
+
from flimkit.utils.config_manager import cfg
|
|
128
|
+
cfg.load_project_overrides(self._project.config)
|
|
129
|
+
self._refresh()
|
|
130
|
+
|
|
131
|
+
def _open_project(self):
|
|
132
|
+
folder = filedialog.askdirectory(title='Open project folder')
|
|
133
|
+
if not folder:
|
|
134
|
+
return
|
|
135
|
+
self.load_folder(folder)
|
|
136
|
+
if hasattr(self._app, '_add_to_recent'):
|
|
137
|
+
self._app._add_to_recent(folder, 'project')
|
|
138
|
+
|
|
139
|
+
def _refresh(self):
|
|
140
|
+
if self._project is None:
|
|
141
|
+
return
|
|
142
|
+
self._lb.delete(0, tk.END)
|
|
143
|
+
self._stems.clear()
|
|
144
|
+
for stem, rec in self._project.sorted_scans():
|
|
145
|
+
has_fit = rec.has_session
|
|
146
|
+
has_ph = rec.has_phasor_session
|
|
147
|
+
if has_fit and has_ph:
|
|
148
|
+
session_dot = _ICON_BOTH
|
|
149
|
+
elif has_fit:
|
|
150
|
+
session_dot = _ICON_FIT_ONLY
|
|
151
|
+
elif has_ph:
|
|
152
|
+
session_dot = _ICON_PHASOR_ONLY
|
|
153
|
+
else:
|
|
154
|
+
session_dot = _ICON_NOSESSION
|
|
155
|
+
if rec.scan_type == 'xlif':
|
|
156
|
+
type_tag = _ICON_XLIF
|
|
157
|
+
elif rec.scan_type == 'zstack':
|
|
158
|
+
type_tag = _ICON_ZSTACK
|
|
159
|
+
else:
|
|
160
|
+
type_tag = _ICON_FOV
|
|
161
|
+
label = f'{session_dot} {type_tag} {stem}'
|
|
162
|
+
self._lb.insert(tk.END, label)
|
|
163
|
+
self._stems.append(stem)
|
|
164
|
+
n = len(self._stems)
|
|
165
|
+
n_sess = sum(1 for r in self._project.scans.values()
|
|
166
|
+
if r.has_session or r.has_phasor_session)
|
|
167
|
+
folder_name = self._project.project_dir.name
|
|
168
|
+
self._sv_status.set(
|
|
169
|
+
f"{folder_name} | {n} scan{'s' if n != 1 else ''} | {n_sess} saved"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def _on_select(self, _event=None):
|
|
173
|
+
if getattr(self, '_selecting', False):
|
|
174
|
+
return
|
|
175
|
+
sel = self._lb.curselection()
|
|
176
|
+
if not sel or self._project is None:
|
|
177
|
+
return
|
|
178
|
+
stem = self._stems[sel[0]]
|
|
179
|
+
rec = self._project.scans.get(stem)
|
|
180
|
+
if rec is None:
|
|
181
|
+
return
|
|
182
|
+
self._selecting = True
|
|
183
|
+
try:
|
|
184
|
+
self._load_scan(rec)
|
|
185
|
+
finally:
|
|
186
|
+
self._selecting = False
|
|
187
|
+
|
|
188
|
+
def _load_scan(self, rec):
|
|
189
|
+
app = self._app
|
|
190
|
+
if hasattr(app, '_cancel_pending_scan_loads'):
|
|
191
|
+
app._cancel_pending_scan_loads()
|
|
192
|
+
if hasattr(app, '_fov_preview'):
|
|
193
|
+
app._fov_preview._roi_manager.clear_all()
|
|
194
|
+
app._fov_preview._roi_patches.clear()
|
|
195
|
+
app._fov_preview._redraw_region_overlays()
|
|
196
|
+
for panel in (getattr(app, '_roi_analysis_panel', None),
|
|
197
|
+
getattr(app, '_stitch_roi_panel', None)):
|
|
198
|
+
if panel is not None:
|
|
199
|
+
panel._refresh_region_list()
|
|
200
|
+
if rec.scan_type == 'fov':
|
|
201
|
+
current_form = getattr(app, '_current_form', 'fov')
|
|
202
|
+
want_phasor = (current_form == 'phasor')
|
|
203
|
+
if want_phasor:
|
|
204
|
+
app._switch_form('phasor')
|
|
205
|
+
else:
|
|
206
|
+
app._switch_form('fov')
|
|
207
|
+
if hasattr(app, '_last_loaded_ptu'):
|
|
208
|
+
app._last_loaded_ptu = rec.source_path
|
|
209
|
+
app.sv_ptu.set(rec.source_path)
|
|
210
|
+
if hasattr(app, '_fov_preview'):
|
|
211
|
+
app._fov_preview.load_fov(rec.source_path)
|
|
212
|
+
if rec.xlsx_path and hasattr(app, 'sv_xlsx'):
|
|
213
|
+
app.sv_xlsx.set(rec.xlsx_path)
|
|
214
|
+
if hasattr(app, '_irf_fov'):
|
|
215
|
+
if rec.xlsx_path:
|
|
216
|
+
app._irf_fov.sv_method.set('irf_xlsx')
|
|
217
|
+
else:
|
|
218
|
+
app._irf_fov.sv_method.set('machine_irf')
|
|
219
|
+
if hasattr(app, 'sv_out_fov'):
|
|
220
|
+
app.sv_out_fov.set(rec.stem)
|
|
221
|
+
if want_phasor:
|
|
222
|
+
if hasattr(app, 'sv_ph_ptu'):
|
|
223
|
+
app.sv_ph_ptu.set(rec.source_path)
|
|
224
|
+
if rec.phasor_session_path and hasattr(app, '_restore_phasor_session'):
|
|
225
|
+
app._restore_phasor_session(str(rec.phasor_session_path))
|
|
226
|
+
else:
|
|
227
|
+
if rec.session_path and hasattr(app, '_load_fitted_data_from_file'):
|
|
228
|
+
app._load_fitted_data_from_file(str(rec.session_path), suppress_popups=True)
|
|
229
|
+
elif rec.scan_type == 'zstack':
|
|
230
|
+
app._switch_form('fov')
|
|
231
|
+
if hasattr(app, 'sv_fov_analysis'):
|
|
232
|
+
app.sv_fov_analysis.set('zstack')
|
|
233
|
+
if hasattr(app, '_on_fov_analysis_changed'):
|
|
234
|
+
app._on_fov_analysis_changed()
|
|
235
|
+
if hasattr(app, 'sv_zstack_dir'):
|
|
236
|
+
app.sv_zstack_dir.set(rec.ptu_dir or rec.source_path)
|
|
237
|
+
if hasattr(app, 'sv_out_fov'):
|
|
238
|
+
app.sv_out_fov.set(rec.stem)
|
|
239
|
+
if rec.session_path and hasattr(app, '_fov_preview'):
|
|
240
|
+
try:
|
|
241
|
+
from flimkit.utils.display import load_zstack_display_slices
|
|
242
|
+
group_dir = Path(rec.session_path).parent
|
|
243
|
+
slices = load_zstack_display_slices(
|
|
244
|
+
group_dir, ptu_dir=rec.ptu_dir, region=rec.stem)
|
|
245
|
+
if slices:
|
|
246
|
+
app._fov_preview.display_zstack(slices)
|
|
247
|
+
if hasattr(app, '_populate_zstack_summary_from_dir'):
|
|
248
|
+
app._populate_zstack_summary_from_dir(group_dir)
|
|
249
|
+
except Exception as exc:
|
|
250
|
+
print(f'[Project] Could not reload z-stack {rec.stem}: {exc}')
|
|
251
|
+
else:
|
|
252
|
+
app._switch_form('stitch')
|
|
253
|
+
if hasattr(app, '_last_loaded_xlif'):
|
|
254
|
+
app._last_loaded_xlif = rec.source_path
|
|
255
|
+
app.sv_xlif.set(rec.source_path)
|
|
256
|
+
if rec.ptu_dir and hasattr(app, 'sv_ptu_dir'):
|
|
257
|
+
app.sv_ptu_dir.set(rec.ptu_dir)
|
|
258
|
+
out_st = self._project.default_out_st(rec.stem) if self._project else ''
|
|
259
|
+
if hasattr(app, 'sv_out_st'):
|
|
260
|
+
app.sv_out_st.set(out_st)
|
|
261
|
+
if rec.session_path and hasattr(app, '_load_fitted_data_from_file'):
|
|
262
|
+
app._load_fitted_data_from_file(str(rec.session_path), suppress_popups=True)
|