spacr 0.2.46__py3-none-any.whl → 0.2.56__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.
- spacr/core.py +306 -21
- spacr/deep_spacr.py +101 -41
- spacr/gui.py +1 -3
- spacr/gui_core.py +78 -65
- spacr/gui_elements.py +437 -152
- spacr/gui_utils.py +84 -73
- spacr/io.py +14 -7
- spacr/measure.py +196 -145
- spacr/plot.py +2 -42
- spacr/resources/font/open_sans/OFL.txt +93 -0
- spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/README.txt +100 -0
- spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
- spacr/sequencing.py +481 -587
- spacr/settings.py +197 -122
- spacr/utils.py +21 -13
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/METADATA +7 -4
- spacr-0.2.56.dist-info/RECORD +100 -0
- spacr-0.2.46.dist-info/RECORD +0 -60
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/LICENSE +0 -0
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/WHEEL +0 -0
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/top_level.txt +0 -0
spacr/deep_spacr.py
CHANGED
@@ -196,7 +196,7 @@ def test_model_performance(loaders, model, loader_name_list, epoch, train_mode,
|
|
196
196
|
test_time = end_time - start_time
|
197
197
|
return result, results_df
|
198
198
|
|
199
|
-
def train_test_model(
|
199
|
+
def train_test_model(settings):
|
200
200
|
|
201
201
|
from .io import _save_settings, _copy_missclassified
|
202
202
|
from .utils import pick_best_model
|
@@ -208,7 +208,10 @@ def train_test_model(src, settings, custom_model=False, custom_model_path=None):
|
|
208
208
|
gc.collect()
|
209
209
|
|
210
210
|
settings = set_default_train_test_model(settings)
|
211
|
-
|
211
|
+
|
212
|
+
src = settings['src']
|
213
|
+
|
214
|
+
channels_str = ''.join(settings['train_channels'])
|
212
215
|
dst = os.path.join(src,'model', settings['model_type'], channels_str, str(f"epochs_{settings['epochs']}"))
|
213
216
|
os.makedirs(dst, exist_ok=True)
|
214
217
|
settings['src'] = src
|
@@ -217,8 +220,8 @@ def train_test_model(src, settings, custom_model=False, custom_model_path=None):
|
|
217
220
|
settings_csv = os.path.join(dst,'train_test_model_settings.csv')
|
218
221
|
settings_df.to_csv(settings_csv, index=False)
|
219
222
|
|
220
|
-
if custom_model:
|
221
|
-
model = torch.load(custom_model_path)
|
223
|
+
if settings['custom_model']:
|
224
|
+
model = torch.load(settings['custom_model_path'])
|
222
225
|
|
223
226
|
if settings['train']:
|
224
227
|
_save_settings(settings, src)
|
@@ -234,7 +237,7 @@ def train_test_model(src, settings, custom_model=False, custom_model_path=None):
|
|
234
237
|
validation_split=settings['val_split'],
|
235
238
|
pin_memory=settings['pin_memory'],
|
236
239
|
normalize=settings['normalize'],
|
237
|
-
channels=settings['
|
240
|
+
channels=settings['train_channels'],
|
238
241
|
augment=settings['augment'],
|
239
242
|
verbose=settings['verbose'])
|
240
243
|
|
@@ -242,28 +245,28 @@ def train_test_model(src, settings, custom_model=False, custom_model_path=None):
|
|
242
245
|
train_fig.savefig(train_batch_1_figure, format='pdf', dpi=600)
|
243
246
|
|
244
247
|
if settings['train']:
|
245
|
-
model = train_model(dst = settings['dst'],
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
248
|
+
model, model_path = train_model(dst = settings['dst'],
|
249
|
+
model_type=settings['model_type'],
|
250
|
+
train_loaders = train,
|
251
|
+
train_loader_names = plate_names,
|
252
|
+
train_mode = settings['train_mode'],
|
253
|
+
epochs = settings['epochs'],
|
254
|
+
learning_rate = settings['learning_rate'],
|
255
|
+
init_weights = settings['init_weights'],
|
256
|
+
weight_decay = settings['weight_decay'],
|
257
|
+
amsgrad = settings['amsgrad'],
|
258
|
+
optimizer_type = settings['optimizer_type'],
|
259
|
+
use_checkpoint = settings['use_checkpoint'],
|
260
|
+
dropout_rate = settings['dropout_rate'],
|
261
|
+
n_jobs = settings['n_jobs'],
|
262
|
+
val_loaders = val,
|
263
|
+
test_loaders = None,
|
264
|
+
intermedeate_save = settings['intermedeate_save'],
|
265
|
+
schedule = settings['schedule'],
|
266
|
+
loss_type=settings['loss_type'],
|
267
|
+
gradient_accumulation=settings['gradient_accumulation'],
|
268
|
+
gradient_accumulation_steps=settings['gradient_accumulation_steps'],
|
269
|
+
channels=settings['train_channels'])
|
267
270
|
|
268
271
|
torch.cuda.empty_cache()
|
269
272
|
torch.cuda.memory.empty_cache()
|
@@ -280,7 +283,7 @@ def train_test_model(src, settings, custom_model=False, custom_model_path=None):
|
|
280
283
|
validation_split=0.0,
|
281
284
|
pin_memory=settings['pin_memory'],
|
282
285
|
normalize=settings['normalize'],
|
283
|
-
channels=settings['
|
286
|
+
channels=settings['train_channels'],
|
284
287
|
augment=False,
|
285
288
|
verbose=settings['verbose'])
|
286
289
|
if model == None:
|
@@ -314,6 +317,8 @@ def train_test_model(src, settings, custom_model=False, custom_model_path=None):
|
|
314
317
|
torch.cuda.empty_cache()
|
315
318
|
torch.cuda.memory.empty_cache()
|
316
319
|
gc.collect()
|
320
|
+
|
321
|
+
return model_path
|
317
322
|
|
318
323
|
def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='erm', epochs=100, learning_rate=0.0001, weight_decay=0.05, amsgrad=False, optimizer_type='adamw', use_checkpoint=False, dropout_rate=0, n_jobs=20, val_loaders=None, test_loaders=None, init_weights='imagenet', intermedeate_save=None, chan_dict=None, schedule = None, loss_type='binary_cross_entropy_with_logits', gradient_accumulation=False, gradient_accumulation_steps=4, channels=['r','g','b']):
|
319
324
|
"""
|
@@ -348,7 +353,7 @@ def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='
|
|
348
353
|
"""
|
349
354
|
|
350
355
|
from .io import _save_model, _save_progress
|
351
|
-
from .utils import compute_irm_penalty, calculate_loss, choose_model
|
356
|
+
from .utils import compute_irm_penalty, calculate_loss, choose_model, print_progress
|
352
357
|
|
353
358
|
print(f'Train batches:{len(train_loaders)}, Validation batches:{len(val_loaders)}')
|
354
359
|
|
@@ -386,6 +391,7 @@ def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='
|
|
386
391
|
else:
|
387
392
|
scheduler = None
|
388
393
|
|
394
|
+
time_ls = []
|
389
395
|
if train_mode == 'erm':
|
390
396
|
for epoch in range(1, epochs+1):
|
391
397
|
model.train()
|
@@ -412,7 +418,13 @@ def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='
|
|
412
418
|
optimizer.zero_grad()
|
413
419
|
|
414
420
|
avg_loss = running_loss / batch_idx
|
415
|
-
print(f'\rTrain: epoch: {epoch} batch: {batch_idx}/{len(train_loaders)} avg_loss: {avg_loss:.5f} time: {(time.time()-start_time):.5f}', end='\r', flush=True)
|
421
|
+
#print(f'\rTrain: epoch: {epoch} batch: {batch_idx}/{len(train_loaders)} avg_loss: {avg_loss:.5f} time: {(time.time()-start_time):.5f}', end='\r', flush=True)
|
422
|
+
|
423
|
+
batch_size = len(train_loaders)
|
424
|
+
duration = time.time() - start_time
|
425
|
+
time_ls.append(duration)
|
426
|
+
metricks = f"Loss: {avg_loss:.5f}"
|
427
|
+
print_progress(files_processed=epoch, files_to_process=epochs, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type=f"Training {model_type} model", metricks=metricks)
|
416
428
|
|
417
429
|
end_time = time.time()
|
418
430
|
train_time = end_time - start_time
|
@@ -421,6 +433,7 @@ def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='
|
|
421
433
|
train_names = 'train'
|
422
434
|
results_df, train_test_time = evaluate_model_performance(train_loaders, model, train_names, epoch, train_mode='erm', loss_type=loss_type)
|
423
435
|
train_metrics_df['train_test_time'] = train_test_time
|
436
|
+
|
424
437
|
if val_loaders != None:
|
425
438
|
val_names = 'val'
|
426
439
|
result, val_time = evaluate_model_performance(val_loaders, model, val_names, epoch, train_mode='erm', loss_type=loss_type)
|
@@ -430,6 +443,7 @@ def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='
|
|
430
443
|
|
431
444
|
results_df = pd.concat([results_df, result])
|
432
445
|
train_metrics_df['val_time'] = val_time
|
446
|
+
|
433
447
|
if test_loaders != None:
|
434
448
|
test_names = 'test'
|
435
449
|
result, test_test_time = evaluate_model_performance(test_loaders, model, test_names, epoch, train_mode='erm', loss_type=loss_type)
|
@@ -444,9 +458,30 @@ def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='
|
|
444
458
|
scheduler.step()
|
445
459
|
|
446
460
|
_save_progress(dst, results_df, train_metrics_df, epoch, epochs)
|
447
|
-
clear_output(wait=True)
|
448
|
-
display(results_df)
|
449
|
-
|
461
|
+
#clear_output(wait=True)
|
462
|
+
#display(results_df)
|
463
|
+
|
464
|
+
train_idx = f"{epoch}_train"
|
465
|
+
val_idx = f"{epoch}_val"
|
466
|
+
train_acc = results_df.loc[train_idx, 'accuracy']
|
467
|
+
neg_train_acc = results_df.loc[train_idx, 'neg_accuracy']
|
468
|
+
pos_train_acc = results_df.loc[train_idx, 'pos_accuracy']
|
469
|
+
val_acc = results_df.loc[val_idx, 'accuracy']
|
470
|
+
neg_val_acc = results_df.loc[val_idx, 'neg_accuracy']
|
471
|
+
pos_val_acc = results_df.loc[val_idx, 'pos_accuracy']
|
472
|
+
train_loss = results_df.loc[train_idx, 'loss']
|
473
|
+
train_prauc = results_df.loc[train_idx, 'prauc']
|
474
|
+
val_loss = results_df.loc[val_idx, 'loss']
|
475
|
+
val_prauc = results_df.loc[val_idx, 'prauc']
|
476
|
+
|
477
|
+
metricks = f"Train Acc: {train_acc:.5f} Val Acc: {val_acc:.5f} Train Loss: {train_loss:.5f} Val Loss: {val_loss:.5f} Train PRAUC: {train_prauc:.5f} Val PRAUC: {val_prauc:.5f}, Nc Train Acc: {neg_train_acc:.5f} Nc Val Acc: {neg_val_acc:.5f} Pc Train Acc: {pos_train_acc:.5f} Pc Val Acc: {pos_val_acc:.5f}"
|
478
|
+
|
479
|
+
batch_size = len(train_loaders)
|
480
|
+
duration = time.time() - start_time
|
481
|
+
time_ls.append(duration)
|
482
|
+
print_progress(files_processed=epoch, files_to_process=epochs, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type=f"Training {model_type} model", metricks=metricks)
|
483
|
+
|
484
|
+
model_path = _save_model(model, model_type, results_df, dst, epoch, epochs, intermedeate_save=[0.99,0.98,0.95,0.94], channels=channels)
|
450
485
|
|
451
486
|
if train_mode == 'irm':
|
452
487
|
dummy_w = torch.nn.Parameter(torch.Tensor([1.0])).to(device)
|
@@ -517,9 +552,10 @@ def train_model(dst, model_type, train_loaders, train_loader_names, train_mode='
|
|
517
552
|
clear_output(wait=True)
|
518
553
|
display(results_df)
|
519
554
|
_save_progress(dst, results_df, train_metrics_df, epoch, epochs)
|
520
|
-
_save_model(model, model_type, results_df, dst, epoch, epochs, intermedeate_save=[0.99,0.98,0.95,0.94])
|
521
|
-
print(f'Saved model: {
|
522
|
-
|
555
|
+
model_path = _save_model(model, model_type, results_df, dst, epoch, epochs, intermedeate_save=[0.99,0.98,0.95,0.94])
|
556
|
+
print(f'Saved model: {model_path}')
|
557
|
+
|
558
|
+
return model, model_path
|
523
559
|
|
524
560
|
def visualize_saliency_map(src, model_type='maxvit', model_path='', image_size=224, channels=[1,2,3], normalize=True, class_names=None, save_saliency=False, save_dir='saliency_maps'):
|
525
561
|
|
@@ -778,8 +814,32 @@ def visualize_smooth_grad(src, model_path, target_label_idx, image_size=224, cha
|
|
778
814
|
smooth_grad_image = Image.fromarray((smooth_grad_map * 255).astype(np.uint8))
|
779
815
|
smooth_grad_image.save(os.path.join(save_dir, f'smooth_grad_{file}'))
|
780
816
|
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
817
|
+
def deep_spacr(settings={}):
|
818
|
+
from .settings import deep_spacr_defaults
|
819
|
+
from .core import generate_training_dataset, generate_dataset, apply_model_to_tar
|
820
|
+
|
821
|
+
settings = deep_spacr_defaults(settings)
|
822
|
+
src = settings['src']
|
823
|
+
|
824
|
+
if settings['train'] or settings['test']:
|
825
|
+
if settings['generate_training_dataset']:
|
826
|
+
print(f"Generating train and test datasets ...")
|
827
|
+
train_path, test_path = generate_training_dataset(settings)
|
828
|
+
print(f'Generated Train set: {train_path}')
|
829
|
+
print(f'Generated Train set: {test_path}')
|
830
|
+
settings['src'] = os.path.dirname(train_path)
|
831
|
+
|
832
|
+
if settings['train_DL_model']:
|
833
|
+
print(f"Training model ...")
|
834
|
+
model_path = train_test_model(settings)
|
835
|
+
settings['model_path'] = model_path
|
836
|
+
settings['src'] = src
|
837
|
+
|
838
|
+
if settings['apply_model_to_dataset']:
|
839
|
+
if not os.path.exists(settings['tar_path']):
|
840
|
+
print(f"Generating dataset ...")
|
841
|
+
tar_path = generate_dataset(settings)
|
842
|
+
settings['tar_path'] = tar_path
|
843
|
+
|
844
|
+
if os.path.exists(settings['model_path']):
|
845
|
+
apply_model_to_tar(settings)
|
spacr/gui.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
import tkinter as tk
|
2
2
|
from tkinter import ttk
|
3
|
-
from PIL import Image, ImageTk, ImageDraw
|
4
|
-
import os
|
5
3
|
from multiprocessing import set_start_method
|
6
4
|
from .gui_elements import spacrButton, create_menu_bar, set_dark_style
|
7
5
|
from .gui_core import initiate_root
|
@@ -29,7 +27,7 @@ class MainApp(tk.Tk):
|
|
29
27
|
}
|
30
28
|
|
31
29
|
self.additional_gui_apps = {
|
32
|
-
"Sequencing": (lambda frame: initiate_root(self, 'sequencing'), "Analyze sequencing data."),
|
30
|
+
#"Sequencing": (lambda frame: initiate_root(self, 'sequencing'), "Analyze sequencing data."),
|
33
31
|
"Umap": (lambda frame: initiate_root(self, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
|
34
32
|
"Train Cellpose": (lambda frame: initiate_root(self, 'train_cellpose'), "Train custom Cellpose models."),
|
35
33
|
"ML Analyze": (lambda frame: initiate_root(self, 'ml_analyze'), "Machine learning analysis of data."),
|
spacr/gui_core.py
CHANGED
@@ -1,23 +1,21 @@
|
|
1
|
-
import
|
1
|
+
import traceback, ctypes, csv, re, time
|
2
2
|
import tkinter as tk
|
3
3
|
from tkinter import ttk
|
4
4
|
from tkinter import filedialog
|
5
5
|
from multiprocessing import Process, Value, Queue, set_start_method
|
6
|
-
from multiprocessing.sharedctypes import Synchronized
|
7
6
|
from tkinter import ttk, scrolledtext
|
8
7
|
from matplotlib.figure import Figure
|
9
8
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
10
9
|
import numpy as np
|
11
10
|
import psutil
|
12
11
|
import GPUtil
|
13
|
-
import tkinter.font as tkFont
|
14
12
|
|
15
13
|
try:
|
16
14
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
17
15
|
except AttributeError:
|
18
16
|
pass
|
19
17
|
|
20
|
-
from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks,
|
18
|
+
from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks, set_default_generate_barecode_mapping, set_default_umap_image_settings
|
21
19
|
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style
|
22
20
|
|
23
21
|
# Define global variables
|
@@ -45,13 +43,15 @@ def toggle_settings(button_scrollable_frame):
|
|
45
43
|
def toggle_category(settings):
|
46
44
|
for setting in settings:
|
47
45
|
if setting in vars_dict:
|
48
|
-
label, widget, _ = vars_dict[setting]
|
46
|
+
label, widget, _, frame = vars_dict[setting]
|
49
47
|
if widget.grid_info():
|
50
48
|
label.grid_remove()
|
51
49
|
widget.grid_remove()
|
50
|
+
frame.grid_remove()
|
52
51
|
else:
|
53
52
|
label.grid()
|
54
53
|
widget.grid()
|
54
|
+
frame.grid()
|
55
55
|
|
56
56
|
def on_category_select(selected_category):
|
57
57
|
if selected_category == "Select Category":
|
@@ -81,15 +81,20 @@ def process_fig_queue():
|
|
81
81
|
|
82
82
|
try:
|
83
83
|
while not fig_queue.empty():
|
84
|
+
time.sleep(1)
|
84
85
|
clear_canvas(canvas)
|
85
86
|
fig = fig_queue.get_nowait()
|
87
|
+
|
86
88
|
for ax in fig.get_axes():
|
87
89
|
ax.set_xticks([]) # Remove x-axis ticks
|
88
90
|
ax.set_yticks([]) # Remove y-axis ticks
|
89
91
|
ax.xaxis.set_visible(False) # Hide the x-axis
|
90
92
|
ax.yaxis.set_visible(False) # Hide the y-axis
|
91
|
-
|
93
|
+
|
94
|
+
# Adjust layout to minimize spacing between axes
|
95
|
+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0.01, hspace=0.01)
|
92
96
|
fig.set_facecolor('black')
|
97
|
+
|
93
98
|
canvas.figure = fig
|
94
99
|
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
95
100
|
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
@@ -97,12 +102,9 @@ def process_fig_queue():
|
|
97
102
|
except Exception as e:
|
98
103
|
traceback.print_exc()
|
99
104
|
finally:
|
100
|
-
after_id = canvas_widget.after(
|
105
|
+
after_id = canvas_widget.after(1, process_fig_queue)
|
101
106
|
parent_frame.after_tasks.append(after_id)
|
102
107
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
108
|
def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, progress_bar_var, usage_bars_var):
|
107
109
|
global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
|
108
110
|
thread_control = thread_control_var
|
@@ -120,7 +122,7 @@ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var,
|
|
120
122
|
def import_settings(settings_type='mask'):
|
121
123
|
from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
|
122
124
|
global vars_dict, scrollable_frame, button_scrollable_frame
|
123
|
-
from .settings import generate_fields
|
125
|
+
from .settings import generate_fields, set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model, set_default_generate_barecode_mapping, set_default_umap_image_settings
|
124
126
|
|
125
127
|
def read_settings_from_csv(csv_file_path):
|
126
128
|
settings = {}
|
@@ -156,7 +158,7 @@ def import_settings(settings_type='mask'):
|
|
156
158
|
elif settings_type == 'classify':
|
157
159
|
settings = set_default_train_test_model(settings={})
|
158
160
|
elif settings_type == 'sequencing':
|
159
|
-
settings =
|
161
|
+
settings = set_default_generate_barecode_mapping(settings={})
|
160
162
|
elif settings_type == 'umap':
|
161
163
|
settings = set_default_umap_image_settings(settings={})
|
162
164
|
else:
|
@@ -169,14 +171,15 @@ def import_settings(settings_type='mask'):
|
|
169
171
|
|
170
172
|
def setup_settings_panel(vertical_container, settings_type='mask'):
|
171
173
|
global vars_dict, scrollable_frame
|
172
|
-
from .settings import get_identify_masks_finetune_default_settings, set_default_analyze_screen, set_default_settings_preprocess_generate_masks, get_measure_crop_settings,
|
173
|
-
from .gui_utils import convert_settings_dict_for_gui
|
174
|
+
from .settings import get_identify_masks_finetune_default_settings, set_default_analyze_screen, set_default_settings_preprocess_generate_masks, get_measure_crop_settings, deep_spacr_defaults, set_default_generate_barecode_mapping, set_default_umap_image_settings, generate_fields, get_perform_regression_default_settings, get_train_cellpose_default_settings, get_map_barcodes_default_settings, get_analyze_recruitment_default_settings, get_check_cellpose_models_default_settings
|
175
|
+
from .gui_utils import convert_settings_dict_for_gui
|
176
|
+
from .gui_elements import set_element_size
|
174
177
|
|
175
|
-
size_dict = set_element_size(
|
178
|
+
size_dict = set_element_size()
|
176
179
|
settings_width = size_dict['settings_width']
|
177
180
|
|
178
181
|
# Create a PanedWindow for the settings panel
|
179
|
-
settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
|
182
|
+
settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL, width=size_dict['settings_width'])
|
180
183
|
vertical_container.add(settings_paned_window, stretch="always")
|
181
184
|
|
182
185
|
settings_frame = tk.Frame(settings_paned_window, width=settings_width)
|
@@ -194,9 +197,7 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
|
|
194
197
|
elif settings_type == 'measure':
|
195
198
|
settings = get_measure_crop_settings(settings={})
|
196
199
|
elif settings_type == 'classify':
|
197
|
-
settings =
|
198
|
-
elif settings_type == 'sequencing':
|
199
|
-
settings = get_analyze_reads_default_settings(settings={})
|
200
|
+
settings = deep_spacr_defaults(settings={})
|
200
201
|
elif settings_type == 'umap':
|
201
202
|
settings = set_default_umap_image_settings(settings={})
|
202
203
|
elif settings_type == 'train_cellpose':
|
@@ -208,7 +209,7 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
|
|
208
209
|
elif settings_type == 'cellpose_all':
|
209
210
|
settings = get_check_cellpose_models_default_settings(settings={})
|
210
211
|
elif settings_type == 'map_barcodes':
|
211
|
-
settings =
|
212
|
+
settings = set_default_generate_barecode_mapping(settings={})
|
212
213
|
elif settings_type == 'regression':
|
213
214
|
settings = get_perform_regression_default_settings(settings={})
|
214
215
|
elif settings_type == 'recruitment':
|
@@ -230,8 +231,12 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
|
|
230
231
|
|
231
232
|
def setup_plot_section(vertical_container):
|
232
233
|
global canvas, canvas_widget
|
233
|
-
|
234
|
+
|
235
|
+
# Create a frame for the plot section
|
236
|
+
plot_frame = tk.Frame(vertical_container, bg='lightgrey')
|
234
237
|
vertical_container.add(plot_frame, stretch="always")
|
238
|
+
|
239
|
+
# Set up the plot
|
235
240
|
figure = Figure(figsize=(30, 4), dpi=100)
|
236
241
|
plot = figure.add_subplot(111)
|
237
242
|
plot.plot([], []) # This creates an empty plot.
|
@@ -239,7 +244,11 @@ def setup_plot_section(vertical_container):
|
|
239
244
|
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
240
245
|
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
241
246
|
canvas_widget = canvas.get_tk_widget()
|
242
|
-
|
247
|
+
canvas_widget.grid(row=0, column=0, sticky="nsew") # Use grid for the canvas widget
|
248
|
+
|
249
|
+
plot_frame.grid_rowconfigure(0, weight=1)
|
250
|
+
plot_frame.grid_columnconfigure(0, weight=1)
|
251
|
+
|
243
252
|
canvas.draw()
|
244
253
|
canvas.figure = figure
|
245
254
|
style_out = set_dark_style(ttk.Style())
|
@@ -250,6 +259,7 @@ def setup_plot_section(vertical_container):
|
|
250
259
|
widgets = [canvas_widget]
|
251
260
|
style = ttk.Style(vertical_container)
|
252
261
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
262
|
+
|
253
263
|
return canvas, canvas_widget
|
254
264
|
|
255
265
|
def setup_console(vertical_container):
|
@@ -260,46 +270,47 @@ def setup_console(vertical_container):
|
|
260
270
|
style = ttk.Style()
|
261
271
|
style_out = set_dark_style(style)
|
262
272
|
|
263
|
-
# Create a
|
264
|
-
|
265
|
-
vertical_container.add(
|
273
|
+
# Create a frame for the console section
|
274
|
+
console_frame = tk.Frame(vertical_container, bg=style_out['bg_color'])
|
275
|
+
vertical_container.add(console_frame, stretch="always")
|
266
276
|
|
267
|
-
# Create the
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
# Create a frame to hold the console and button sections
|
272
|
-
console_button_frame = tk.Frame(main_paned_window, bg=style_out['bg_color'])
|
273
|
-
main_paned_window.add(console_button_frame, stretch="always")
|
274
|
-
|
275
|
-
# Create the main console frame
|
276
|
-
console_frame = tk.Frame(console_button_frame, bg=style_out['bg_color'])
|
277
|
-
console_frame.pack(fill=tk.BOTH, expand=True)
|
277
|
+
# Create a thicker frame at the top for the hover effect
|
278
|
+
top_border = tk.Frame(console_frame, height=5, bg=style_out['bg_color'])
|
279
|
+
top_border.grid(row=0, column=0, sticky="ew", pady=(0, 2))
|
278
280
|
|
279
281
|
# Create the scrollable frame (which is a Text widget) with white text
|
280
282
|
family = style_out['font_family']
|
281
|
-
|
282
|
-
|
283
|
-
console_output = tk.Text(console_frame, bg=style_out['bg_color'], fg=style_out['fg_color'], font=
|
284
|
-
|
283
|
+
font_size = style_out['font_size'] - 2
|
284
|
+
font_loader = style_out['font_loader']
|
285
|
+
console_output = tk.Text(console_frame, bg=style_out['bg_color'], fg=style_out['fg_color'], font=font_loader.get_font(size=font_size), bd=0, highlightthickness=0)
|
286
|
+
|
287
|
+
console_output.grid(row=1, column=0, sticky="nsew") # Use grid for console_output
|
285
288
|
|
286
289
|
# Configure the grid to allow expansion
|
287
|
-
console_frame.grid_rowconfigure(
|
290
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
288
291
|
console_frame.grid_columnconfigure(0, weight=1)
|
289
292
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
+
def on_enter(event):
|
294
|
+
top_border.config(bg=style_out['active_color'])
|
295
|
+
|
296
|
+
def on_leave(event):
|
297
|
+
top_border.config(bg=style_out['bg_color'])
|
298
|
+
|
299
|
+
console_output.bind("<Enter>", on_enter)
|
300
|
+
console_output.bind("<Leave>", on_leave)
|
293
301
|
|
294
302
|
return console_output, console_frame
|
295
303
|
|
296
304
|
def setup_progress_frame(vertical_container):
|
297
305
|
global progress_output
|
306
|
+
style_out = set_dark_style(ttk.Style())
|
307
|
+
font_loader = style_out['font_loader']
|
308
|
+
font_size = style_out['font_size']
|
298
309
|
progress_frame = tk.Frame(vertical_container)
|
299
310
|
vertical_container.add(progress_frame, stretch="always")
|
300
311
|
label_frame = tk.Frame(progress_frame)
|
301
312
|
label_frame.grid(row=0, column=0, sticky="ew", pady=(5, 0), padx=10)
|
302
|
-
progress_label = spacrLabel(label_frame, text="Processing: 0%", font=(
|
313
|
+
progress_label = spacrLabel(label_frame, text="Processing: 0%", font=font_loader.get_font(size=font_size), anchor='w', justify='left', align="left")
|
303
314
|
progress_label.grid(row=0, column=0, sticky="w")
|
304
315
|
progress_output = scrolledtext.ScrolledText(progress_frame, height=10)
|
305
316
|
progress_output.grid(row=1, column=0, sticky="nsew")
|
@@ -313,10 +324,10 @@ def setup_progress_frame(vertical_container):
|
|
313
324
|
|
314
325
|
def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
|
315
326
|
global thread_control, parent_frame, button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
|
316
|
-
from .gui_utils import
|
317
|
-
from .
|
327
|
+
from .gui_utils import download_hug_dataset
|
328
|
+
from .gui_elements import set_element_size
|
318
329
|
|
319
|
-
size_dict = set_element_size(
|
330
|
+
size_dict = set_element_size()
|
320
331
|
button_section_height = size_dict['panel_height']
|
321
332
|
button_frame = tk.Frame(horizontal_container, height=button_section_height)
|
322
333
|
|
@@ -337,7 +348,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
|
|
337
348
|
widgets.append(run_button)
|
338
349
|
btn_col += 1
|
339
350
|
|
340
|
-
if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
|
351
|
+
if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap', 'map_barcodes']:
|
341
352
|
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=lambda: initiate_abort(), show_text=False, size=size_dict['btn_size'], animation=False)
|
342
353
|
abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
343
354
|
widgets.append(abort_button)
|
@@ -357,7 +368,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
|
|
357
368
|
|
358
369
|
# Add the progress bar under the settings category menu
|
359
370
|
progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
|
360
|
-
progress_bar.grid(row=btn_row, column=0, columnspan=
|
371
|
+
progress_bar.grid(row=btn_row, column=0, columnspan=7, pady=5, padx=5, sticky='ew')
|
361
372
|
progress_bar.set_label_position() # Set the label position after grid placement
|
362
373
|
widgets.append(progress_bar)
|
363
374
|
|
@@ -371,8 +382,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
|
|
371
382
|
|
372
383
|
def setup_usage_panel(horizontal_container, btn_col):
|
373
384
|
global usage_bars
|
374
|
-
from .
|
375
|
-
from .gui_elements import set_dark_style
|
385
|
+
from .gui_elements import set_dark_style, set_element_size
|
376
386
|
|
377
387
|
usg_col = 1
|
378
388
|
|
@@ -398,7 +408,7 @@ def setup_usage_panel(horizontal_container, btn_col):
|
|
398
408
|
# Schedule the function to run again after 1000 ms (1 second)
|
399
409
|
parent_frame.after(1000, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
|
400
410
|
|
401
|
-
size_dict = set_element_size(
|
411
|
+
size_dict = set_element_size()
|
402
412
|
usage_panel_height = size_dict['panel_height']
|
403
413
|
usage_frame = tk.Frame(horizontal_container, height=usage_panel_height)
|
404
414
|
horizontal_container.add(usage_frame)
|
@@ -413,7 +423,7 @@ def setup_usage_panel(horizontal_container, btn_col):
|
|
413
423
|
widgets = [usage_scrollable_frame.scrollable_frame]
|
414
424
|
|
415
425
|
usage_bars = []
|
416
|
-
max_elements_per_column =
|
426
|
+
max_elements_per_column = 4
|
417
427
|
row = 0
|
418
428
|
col = 0
|
419
429
|
|
@@ -423,9 +433,9 @@ def setup_usage_panel(horizontal_container, btn_col):
|
|
423
433
|
# Configure the style for the label
|
424
434
|
style = ttk.Style()
|
425
435
|
style_out = set_dark_style(style)
|
426
|
-
|
427
|
-
|
428
|
-
style.configure("usage.TLabel", font=
|
436
|
+
font_loader = style_out['font_loader']
|
437
|
+
font_size = style_out['font_size'] - 2
|
438
|
+
style.configure("usage.TLabel", font=font_loader.get_font(size=font_size), foreground=style_out['fg_color'])
|
429
439
|
|
430
440
|
# Try adding RAM bar
|
431
441
|
try:
|
@@ -525,7 +535,6 @@ def start_process(q=None, fig_queue=None, settings_type='mask'):
|
|
525
535
|
q = Queue()
|
526
536
|
if fig_queue is None:
|
527
537
|
fig_queue = Queue()
|
528
|
-
|
529
538
|
try:
|
530
539
|
settings = check_settings(vars_dict, expected_types, q)
|
531
540
|
except ValueError as e:
|
@@ -540,7 +549,7 @@ def start_process(q=None, fig_queue=None, settings_type='mask'):
|
|
540
549
|
|
541
550
|
process_args = (settings_type, settings, q, fig_queue, stop_requested)
|
542
551
|
if settings_type in [
|
543
|
-
'mask', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
|
552
|
+
'mask', 'umap', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
|
544
553
|
'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
|
545
554
|
'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
|
546
555
|
'vision_dataset'
|
@@ -573,7 +582,7 @@ def process_console_queue():
|
|
573
582
|
try:
|
574
583
|
# Extract the progress information
|
575
584
|
match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
|
576
|
-
|
585
|
+
|
577
586
|
if match:
|
578
587
|
current_progress = int(match.group(1))
|
579
588
|
total_progress = int(match.group(2))
|
@@ -622,14 +631,18 @@ def process_console_queue():
|
|
622
631
|
|
623
632
|
except Exception as e:
|
624
633
|
print(f"Error parsing progress message: {e}")
|
625
|
-
|
626
|
-
|
634
|
+
#else:
|
635
|
+
# # Only insert messages that do not start with "Progress:"
|
636
|
+
# console_output.insert(tk.END, clean_message + "\n")
|
637
|
+
# console_output.see(tk.END)
|
638
|
+
|
639
|
+
after_id = console_output.after(1, process_console_queue)
|
627
640
|
parent_frame.after_tasks.append(after_id)
|
628
641
|
|
642
|
+
|
629
643
|
def initiate_root(parent, settings_type='mask'):
|
630
644
|
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
631
|
-
from .gui_utils import main_thread_update_function, setup_frame
|
632
|
-
from .gui import gui_app
|
645
|
+
from .gui_utils import main_thread_update_function, setup_frame
|
633
646
|
from .settings import descriptions
|
634
647
|
|
635
648
|
set_start_method('spawn', force=True)
|
@@ -678,4 +691,4 @@ def initiate_root(parent, settings_type='mask'):
|
|
678
691
|
parent_window.after_tasks.append(after_id)
|
679
692
|
|
680
693
|
print("Root initialization complete")
|
681
|
-
return parent_frame, vars_dict
|
694
|
+
return parent_frame, vars_dict
|