spacr 0.1.11__tar.gz → 0.1.12__tar.gz
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-0.1.11/spacr.egg-info → spacr-0.1.12}/PKG-INFO +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/setup.py +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/spacr/__init__.py +12 -14
- spacr-0.1.11/spacr/annotate_app.py → spacr-0.1.12/spacr/app_annotate.py +25 -156
- spacr-0.1.11/spacr/classify_app.py → spacr-0.1.12/spacr/app_classify.py +9 -9
- spacr-0.1.11/spacr/make_masks_app.py → spacr-0.1.12/spacr/app_make_masks.py +4 -2
- spacr-0.1.11/spacr/make_masks_app_v2.py → spacr-0.1.12/spacr/app_make_masks_v2.py +1 -1
- spacr-0.1.11/spacr/mask_app.py → spacr-0.1.12/spacr/app_mask.py +2 -0
- spacr-0.1.11/spacr/measure_app.py → spacr-0.1.12/spacr/app_measure.py +2 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/gui.py +9 -12
- {spacr-0.1.11 → spacr-0.1.12}/spacr/gui_utils.py +55 -29
- {spacr-0.1.11 → spacr-0.1.12/spacr.egg-info}/PKG-INFO +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/SOURCES.txt +6 -6
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_annotate_app.py +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_classify_app.py +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_mask_app.py +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_measure_app.py +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_sim_app.py +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_mask_app.py +1 -1
- {spacr-0.1.11 → spacr-0.1.12}/LICENSE +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/MANIFEST.in +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/README.rst +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/setup.cfg +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/__main__.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/chris.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/core.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/deep_spacr.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/graph_learning.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/io.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/logger.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/measure.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/models/cp/toxo_pv_lumen.CP_model +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/plot.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/sequencing.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/settings.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/sim.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/sim_app.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/timelapse.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/utils.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr/version.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/dependency_links.txt +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/entry_points.txt +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/requires.txt +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/top_level.txt +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_core.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_utils.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_io.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_measure.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_plot.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_sim.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_timelapse.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_train.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_umap.py +0 -0
- {spacr-0.1.11 → spacr-0.1.12}/tests/test_utils.py +0 -0
@@ -12,14 +12,13 @@ from . import sim
|
|
12
12
|
from . import sequencing
|
13
13
|
from . import timelapse
|
14
14
|
from . import deep_spacr
|
15
|
-
from . import
|
16
|
-
from . import annotate_app_v2
|
15
|
+
from . import app_annotate
|
17
16
|
from . import gui_utils
|
18
|
-
from . import
|
19
|
-
from . import
|
20
|
-
from . import
|
21
|
-
from . import
|
22
|
-
from . import
|
17
|
+
from . import app_make_masks
|
18
|
+
from . import app_make_masks_v2
|
19
|
+
from . import app_mask
|
20
|
+
from . import app_measure
|
21
|
+
from . import app_classify
|
23
22
|
from . import logger
|
24
23
|
|
25
24
|
|
@@ -34,14 +33,13 @@ __all__ = [
|
|
34
33
|
"sequencing"
|
35
34
|
"timelapse",
|
36
35
|
"deep_spacr",
|
37
|
-
"
|
38
|
-
"annotate_app_v2",
|
36
|
+
"app_annotate",
|
39
37
|
"gui_utils",
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"
|
43
|
-
"
|
44
|
-
"
|
38
|
+
"app_make_masks",
|
39
|
+
"app_make_masks_v2",
|
40
|
+
"app_mask",
|
41
|
+
"app_measure",
|
42
|
+
"app_classify",
|
45
43
|
"logger"
|
46
44
|
]
|
47
45
|
|
@@ -11,29 +11,12 @@ from PIL import ImageTk
|
|
11
11
|
from skimage.exposure import rescale_intensity
|
12
12
|
from IPython.display import display, HTML
|
13
13
|
from tkinter import font as tkFont
|
14
|
+
from tkinter import TclError
|
14
15
|
|
15
16
|
from .gui_utils import ScrollableFrame, CustomButton, set_dark_style, set_default_font, style_text_boxes, create_menu_bar
|
16
17
|
|
17
18
|
class ImageApp:
|
18
19
|
def __init__(self, root, db_path, src, image_type=None, channels=None, grid_rows=None, grid_cols=None, image_size=(200, 200), annotation_column='annotate', normalize=False, percentiles=(1,99), measurement=None, threshold=None):
|
19
|
-
"""
|
20
|
-
Initializes an instance of the ImageApp class.
|
21
|
-
|
22
|
-
Parameters:
|
23
|
-
- root (tkinter.Tk): The root window of the application.
|
24
|
-
- db_path (str): The path to the SQLite database.
|
25
|
-
- src (str): The source directory that should be upstream of 'data' in the paths.
|
26
|
-
- image_type (str): The type of images to display.
|
27
|
-
- channels (list): The channels to filter in the images.
|
28
|
-
- grid_rows (int): The number of rows in the image grid.
|
29
|
-
- grid_cols (int): The number of columns in the image grid.
|
30
|
-
- image_size (tuple): The size of the displayed images.
|
31
|
-
- annotation_column (str): The column name for image annotations in the database.
|
32
|
-
- normalize (bool): Whether to normalize images to their 2nd and 98th percentiles. Defaults to False.
|
33
|
-
- measurement (str): The measurement column to filter by.
|
34
|
-
- threshold (float): The threshold value for filtering the measurement column.
|
35
|
-
"""
|
36
|
-
|
37
20
|
self.root = root
|
38
21
|
self.db_path = db_path
|
39
22
|
self.src = src
|
@@ -69,9 +52,6 @@ class ImageApp:
|
|
69
52
|
self.labels.append(label)
|
70
53
|
|
71
54
|
def prefilter_paths_annotations(self):
|
72
|
-
"""
|
73
|
-
Pre-filters the paths and annotations based on the specified measurement and threshold.
|
74
|
-
"""
|
75
55
|
from .io import _read_and_join_tables
|
76
56
|
from .utils import is_list_of_lists
|
77
57
|
|
@@ -159,21 +139,6 @@ class ImageApp:
|
|
159
139
|
conn.close()
|
160
140
|
|
161
141
|
def load_images(self):
|
162
|
-
"""
|
163
|
-
Loads and displays images with annotations.
|
164
|
-
|
165
|
-
This method retrieves image paths and annotations from a pre-filtered list,
|
166
|
-
loads the images using a ThreadPoolExecutor for parallel processing,
|
167
|
-
adds colored borders to images based on their annotations,
|
168
|
-
and displays the images in the corresponding labels.
|
169
|
-
|
170
|
-
Args:
|
171
|
-
None
|
172
|
-
|
173
|
-
Returns:
|
174
|
-
None
|
175
|
-
"""
|
176
|
-
|
177
142
|
for label in self.labels:
|
178
143
|
label.config(image='')
|
179
144
|
|
@@ -213,16 +178,6 @@ class ImageApp:
|
|
213
178
|
self.root.update()
|
214
179
|
|
215
180
|
def load_single_image(self, path_annotation_tuple):
|
216
|
-
"""
|
217
|
-
Loads a single image from the given path and annotation tuple.
|
218
|
-
|
219
|
-
Args:
|
220
|
-
path_annotation_tuple (tuple): A tuple containing the image path and its annotation.
|
221
|
-
|
222
|
-
Returns:
|
223
|
-
img (PIL.Image.Image): The loaded image.
|
224
|
-
annotation: The annotation associated with the image.
|
225
|
-
"""
|
226
181
|
path, annotation = path_annotation_tuple
|
227
182
|
img = Image.open(path)
|
228
183
|
img = self.normalize_image(img, self.normalize, self.percentiles)
|
@@ -233,18 +188,6 @@ class ImageApp:
|
|
233
188
|
|
234
189
|
@staticmethod
|
235
190
|
def normalize_image(img, normalize=False, percentiles=(1, 99)):
|
236
|
-
"""
|
237
|
-
Normalize the pixel values of an image based on the 2nd and 98th percentiles or the image min and max values,
|
238
|
-
and ensure the image is exported as 8-bit.
|
239
|
-
|
240
|
-
Parameters:
|
241
|
-
- img: PIL.Image.Image. The input image to be normalized.
|
242
|
-
- normalize: bool. Whether to normalize based on the 2nd and 98th percentiles.
|
243
|
-
- percentiles: tuple. The percentiles to use for normalization.
|
244
|
-
|
245
|
-
Returns:
|
246
|
-
- PIL.Image.Image. The normalized and 8-bit converted image.
|
247
|
-
"""
|
248
191
|
img_array = np.array(img)
|
249
192
|
|
250
193
|
if normalize:
|
@@ -261,17 +204,6 @@ class ImageApp:
|
|
261
204
|
return Image.fromarray(img_array)
|
262
205
|
|
263
206
|
def add_colored_border(self, img, border_width, border_color):
|
264
|
-
"""
|
265
|
-
Adds a colored border to an image.
|
266
|
-
|
267
|
-
Args:
|
268
|
-
img (PIL.Image.Image): The input image.
|
269
|
-
border_width (int): The width of the border in pixels.
|
270
|
-
border_color (str): The color of the border in RGB format.
|
271
|
-
|
272
|
-
Returns:
|
273
|
-
PIL.Image.Image: The image with the colored border.
|
274
|
-
"""
|
275
207
|
top_border = Image.new('RGB', (img.width, border_width), color=border_color)
|
276
208
|
bottom_border = Image.new('RGB', (img.width, border_width), color=border_color)
|
277
209
|
left_border = Image.new('RGB', (border_width, img.height), color=border_color)
|
@@ -287,15 +219,6 @@ class ImageApp:
|
|
287
219
|
return bordered_img
|
288
220
|
|
289
221
|
def filter_channels(self, img):
|
290
|
-
"""
|
291
|
-
Filters the channels of an image based on the specified channels.
|
292
|
-
|
293
|
-
Args:
|
294
|
-
img (PIL.Image.Image): The input image.
|
295
|
-
|
296
|
-
Returns:
|
297
|
-
PIL.Image.Image: The filtered image.
|
298
|
-
"""
|
299
222
|
r, g, b = img.split()
|
300
223
|
if self.channels:
|
301
224
|
if 'r' not in self.channels:
|
@@ -312,17 +235,6 @@ class ImageApp:
|
|
312
235
|
return Image.merge("RGB", (r, g, b))
|
313
236
|
|
314
237
|
def get_on_image_click(self, path, label, img):
|
315
|
-
"""
|
316
|
-
Returns a callback function that handles the click event on an image.
|
317
|
-
|
318
|
-
Parameters:
|
319
|
-
path (str): The path of the image file.
|
320
|
-
label (tkinter.Label): The label widget to update with the annotated image.
|
321
|
-
img (PIL.Image.Image): The image object.
|
322
|
-
|
323
|
-
Returns:
|
324
|
-
function: The callback function for the image click event.
|
325
|
-
"""
|
326
238
|
def on_image_click(event):
|
327
239
|
new_annotation = 1 if event.num == 1 else (2 if event.num == 3 else None)
|
328
240
|
|
@@ -356,11 +268,6 @@ class ImageApp:
|
|
356
268
|
"""))
|
357
269
|
|
358
270
|
def update_database_worker(self):
|
359
|
-
"""
|
360
|
-
Worker function that continuously updates the database with pending updates from the update queue.
|
361
|
-
It retrieves the pending updates from the queue, updates the corresponding records in the database,
|
362
|
-
and resets the text in the HTML and status label.
|
363
|
-
"""
|
364
271
|
conn = sqlite3.connect(self.db_path)
|
365
272
|
c = conn.cursor()
|
366
273
|
|
@@ -383,51 +290,24 @@ class ImageApp:
|
|
383
290
|
c.execute(f'UPDATE png_list SET {self.annotation_column} = ? WHERE png_path = ?', (new_annotation, path))
|
384
291
|
conn.commit()
|
385
292
|
|
386
|
-
# Reset the text
|
387
293
|
ImageApp.update_html('')
|
388
294
|
self.status_label.config(text='')
|
389
295
|
self.root.update()
|
390
296
|
time.sleep(0.1)
|
391
297
|
|
392
298
|
def update_gui_text(self, text):
|
393
|
-
"""
|
394
|
-
Update the text of the status label in the GUI.
|
395
|
-
|
396
|
-
Args:
|
397
|
-
text (str): The new text to be displayed in the status label.
|
398
|
-
|
399
|
-
Returns:
|
400
|
-
None
|
401
|
-
"""
|
402
299
|
self.status_label.config(text=text)
|
403
300
|
self.root.update()
|
404
301
|
|
405
302
|
def next_page(self):
|
406
|
-
|
407
|
-
Moves to the next page of images in the grid.
|
408
|
-
|
409
|
-
If there are pending updates in the dictionary, they are added to the update queue.
|
410
|
-
The pending updates dictionary is then cleared.
|
411
|
-
The index is incremented by the number of rows multiplied by the number of columns in the grid.
|
412
|
-
Finally, the images are loaded for the new page.
|
413
|
-
"""
|
414
|
-
if self.pending_updates: # Check if the dictionary is not empty
|
303
|
+
if self.pending_updates:
|
415
304
|
self.update_queue.put(self.pending_updates.copy())
|
416
305
|
self.pending_updates.clear()
|
417
306
|
self.index += self.grid_rows * self.grid_cols
|
418
307
|
self.load_images()
|
419
308
|
|
420
309
|
def previous_page(self):
|
421
|
-
|
422
|
-
Move to the previous page in the grid.
|
423
|
-
|
424
|
-
If there are pending updates in the dictionary, they are added to the update queue.
|
425
|
-
The dictionary of pending updates is then cleared.
|
426
|
-
The index is decremented by the number of rows multiplied by the number of columns in the grid.
|
427
|
-
If the index becomes negative, it is set to 0.
|
428
|
-
Finally, the images are loaded for the new page.
|
429
|
-
"""
|
430
|
-
if self.pending_updates: # Check if the dictionary is not empty
|
310
|
+
if self.pending_updates:
|
431
311
|
self.update_queue.put(self.pending_updates.copy())
|
432
312
|
self.pending_updates.clear()
|
433
313
|
self.index -= self.grid_rows * self.grid_cols
|
@@ -436,23 +316,15 @@ class ImageApp:
|
|
436
316
|
self.load_images()
|
437
317
|
|
438
318
|
def shutdown(self):
|
439
|
-
|
440
|
-
Shuts down the application.
|
441
|
-
|
442
|
-
This method sets the terminate flag to True, clears the pending updates,
|
443
|
-
updates the database, and quits the application.
|
444
|
-
|
445
|
-
"""
|
446
|
-
self.terminate = True # Set terminate first
|
319
|
+
self.terminate = True
|
447
320
|
self.update_queue.put(self.pending_updates.copy())
|
448
321
|
self.pending_updates.clear()
|
449
|
-
self.db_update_thread.join()
|
322
|
+
self.db_update_thread.join()
|
450
323
|
self.root.quit()
|
451
324
|
self.root.destroy()
|
452
325
|
print(f'Quit application')
|
453
326
|
|
454
327
|
def get_annotate_default_settings(settings):
|
455
|
-
|
456
328
|
settings.setdefault('image_type', 'cell_png')
|
457
329
|
settings.setdefault('channels', ['r', 'g', 'b'])
|
458
330
|
settings.setdefault('geom', "3200x2000")
|
@@ -468,24 +340,6 @@ def get_annotate_default_settings(settings):
|
|
468
340
|
return settings
|
469
341
|
|
470
342
|
def annotate(settings):
|
471
|
-
"""
|
472
|
-
Annotates images in a database using a graphical user interface.
|
473
|
-
|
474
|
-
Args:
|
475
|
-
db (str): The path to the SQLite database.
|
476
|
-
src (str): The source directory that should be upstream of 'data' in the paths.
|
477
|
-
image_type (str, optional): The type of images to load from the database. Defaults to None.
|
478
|
-
channels (str, optional): The channels of the images to load from the database. Defaults to None.
|
479
|
-
geom (str, optional): The geometry of the GUI window. Defaults to "1000x1100".
|
480
|
-
img_size (tuple, optional): The size of the images to display in the GUI. Defaults to (200, 200).
|
481
|
-
rows (int, optional): The number of rows in the image grid. Defaults to 5.
|
482
|
-
columns (int, optional): The number of columns in the image grid. Defaults to 5.
|
483
|
-
annotation_column (str, optional): The name of the annotation column in the database table. Defaults to 'annotate'.
|
484
|
-
normalize (bool, optional): Whether to normalize images to their 2nd and 98th percentiles. Defaults to False.
|
485
|
-
measurement (str, optional): The measurement column to filter by.
|
486
|
-
threshold (float, optional): The threshold value for filtering the measurement column.
|
487
|
-
"""
|
488
|
-
|
489
343
|
settings = get_annotate_default_settings(settings)
|
490
344
|
src = settings['src']
|
491
345
|
|
@@ -604,6 +458,9 @@ def annotate_app(parent_frame, settings):
|
|
604
458
|
annotate_with_image_refs(settings, root, lambda: load_next_app(root))
|
605
459
|
|
606
460
|
def annotate_with_image_refs(settings, root, shutdown_callback):
|
461
|
+
from .gui_utils import proceed_with_app
|
462
|
+
from .gui import gui_app
|
463
|
+
|
607
464
|
settings = get_annotate_default_settings(settings)
|
608
465
|
src = settings['src']
|
609
466
|
|
@@ -632,9 +489,9 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
|
|
632
489
|
app.load_images()
|
633
490
|
|
634
491
|
# Store the shutdown function and next app details in the root
|
635
|
-
root.current_app_exit_func = app.shutdown
|
636
|
-
root.next_app_func =
|
637
|
-
root.next_app_args = ()
|
492
|
+
root.current_app_exit_func = lambda: [app.shutdown(), shutdown_callback()]
|
493
|
+
root.next_app_func = proceed_with_app
|
494
|
+
root.next_app_args = ("Main App", gui_app) # Specify the main app function
|
638
495
|
|
639
496
|
def load_next_app(root):
|
640
497
|
# Get the next app function and arguments
|
@@ -642,7 +499,19 @@ def load_next_app(root):
|
|
642
499
|
next_app_args = root.next_app_args
|
643
500
|
|
644
501
|
if next_app_func:
|
645
|
-
|
502
|
+
try:
|
503
|
+
if not root.winfo_exists():
|
504
|
+
raise tk.TclError
|
505
|
+
next_app_func(root, *next_app_args)
|
506
|
+
except tk.TclError:
|
507
|
+
# Reinitialize root if it has been destroyed
|
508
|
+
new_root = tk.Tk()
|
509
|
+
width = new_root.winfo_screenwidth()
|
510
|
+
height = new_root.winfo_screenheight()
|
511
|
+
new_root.geometry(f"{width}x{height}")
|
512
|
+
new_root.title("SpaCr Application")
|
513
|
+
next_app_func(new_root, *next_app_args)
|
514
|
+
|
646
515
|
|
647
516
|
def gui_annotate():
|
648
517
|
root = tk.Tk()
|
@@ -667,4 +536,4 @@ def gui_annotate():
|
|
667
536
|
root.mainloop()
|
668
537
|
|
669
538
|
if __name__ == "__main__":
|
670
|
-
gui_annotate()
|
539
|
+
gui_annotate()
|
@@ -77,7 +77,7 @@ def initiate_classify_root(parent_frame):
|
|
77
77
|
style_text_boxes(style)
|
78
78
|
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
79
79
|
|
80
|
-
parent_frame.configure(bg='
|
80
|
+
parent_frame.configure(bg='black')
|
81
81
|
parent_frame.grid_rowconfigure(0, weight=1)
|
82
82
|
parent_frame.grid_columnconfigure(0, weight=1)
|
83
83
|
fig_queue = Queue()
|
@@ -94,7 +94,7 @@ def initiate_classify_root(parent_frame):
|
|
94
94
|
ax.xaxis.set_visible(False) # Hide the x-axis
|
95
95
|
ax.yaxis.set_visible(False) # Hide the y-axis
|
96
96
|
fig.tight_layout()
|
97
|
-
fig.set_facecolor('
|
97
|
+
fig.set_facecolor('black')
|
98
98
|
canvas.figure = fig
|
99
99
|
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
100
100
|
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
@@ -117,9 +117,9 @@ def initiate_classify_root(parent_frame):
|
|
117
117
|
parent_frame.grid_columnconfigure(0, weight=1)
|
118
118
|
|
119
119
|
# Settings Section
|
120
|
-
settings_frame = tk.Frame(vertical_container, bg='
|
120
|
+
settings_frame = tk.Frame(vertical_container, bg='black')
|
121
121
|
vertical_container.add(settings_frame, stretch="always")
|
122
|
-
settings_label = ttk.Label(settings_frame, text="Settings", background="
|
122
|
+
settings_label = ttk.Label(settings_frame, text="Settings", background="black", foreground="white")
|
123
123
|
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
124
124
|
scrollable_frame = ScrollableFrame(settings_frame, width=500)
|
125
125
|
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
@@ -143,23 +143,23 @@ def initiate_classify_root(parent_frame):
|
|
143
143
|
# Plot Canvas Section
|
144
144
|
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
145
145
|
vertical_container.add(plot_frame, stretch="always")
|
146
|
-
figure = Figure(figsize=(30, 4), dpi=100, facecolor='
|
146
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
147
147
|
plot = figure.add_subplot(111)
|
148
148
|
plot.plot([], [])
|
149
149
|
plot.axis('off')
|
150
150
|
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
151
|
-
canvas.get_tk_widget().configure(cursor='arrow', background='
|
151
|
+
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
152
152
|
canvas_widget = canvas.get_tk_widget()
|
153
153
|
plot_frame.add(canvas_widget, stretch="always")
|
154
154
|
canvas.draw()
|
155
155
|
canvas.figure = figure
|
156
156
|
|
157
157
|
# Console Section
|
158
|
-
console_frame = tk.Frame(vertical_container, bg='
|
158
|
+
console_frame = tk.Frame(vertical_container, bg='black')
|
159
159
|
vertical_container.add(console_frame, stretch="always")
|
160
|
-
console_label = ttk.Label(console_frame, text="Console", background="
|
160
|
+
console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
|
161
161
|
console_label.grid(row=0, column=0, pady=10, padx=10)
|
162
|
-
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='
|
162
|
+
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
|
163
163
|
console_output.grid(row=1, column=0, sticky="nsew")
|
164
164
|
console_frame.grid_rowconfigure(1, weight=1)
|
165
165
|
console_frame.grid_columnconfigure(0, weight=1)
|
@@ -870,7 +870,7 @@ def initiate_mask_app_root(parent_frame):
|
|
870
870
|
container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
871
871
|
container.pack(fill=tk.BOTH, expand=True)
|
872
872
|
|
873
|
-
scrollable_frame = ScrollableFrame(container, bg='
|
873
|
+
scrollable_frame = ScrollableFrame(container, bg='black')
|
874
874
|
container.add(scrollable_frame, stretch="always")
|
875
875
|
|
876
876
|
# Setup input fields
|
@@ -905,7 +905,9 @@ def initiate_mask_app_root(parent_frame):
|
|
905
905
|
|
906
906
|
def gui_make_masks():
|
907
907
|
root = tk.Tk()
|
908
|
-
root.
|
908
|
+
width = root.winfo_screenwidth()
|
909
|
+
height = root.winfo_screenheight()
|
910
|
+
root.geometry(f"{width}x{height}")
|
909
911
|
root.title("Mask Application")
|
910
912
|
|
911
913
|
# Clear previous content if any
|
@@ -645,7 +645,7 @@ def initiate_mask_app_root(width, height):
|
|
645
645
|
container = tk.PanedWindow(root, orient=tk.HORIZONTAL)
|
646
646
|
container.pack(fill=tk.BOTH, expand=True)
|
647
647
|
|
648
|
-
scrollable_frame = ScrollableFrame(container, bg='
|
648
|
+
scrollable_frame = ScrollableFrame(container, bg='black')
|
649
649
|
container.add(scrollable_frame, stretch="always")
|
650
650
|
|
651
651
|
vars_dict = {
|
@@ -22,6 +22,8 @@ from .gui_utils import mask_variables, check_mask_gui_settings, preprocess_gener
|
|
22
22
|
|
23
23
|
thread_control = {"run_thread": None, "stop_requested": False}
|
24
24
|
|
25
|
+
test_mode_button = None
|
26
|
+
|
25
27
|
def toggle_test_mode():
|
26
28
|
global vars_dict
|
27
29
|
current_state = vars_dict['test_mode'][2].get()
|
@@ -28,6 +28,8 @@ def import_settings(scrollable_frame):
|
|
28
28
|
new_settings = update_settings_from_csv(variables, csv_settings)
|
29
29
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
30
30
|
|
31
|
+
test_mode_button = None
|
32
|
+
|
31
33
|
def toggle_test_mode():
|
32
34
|
global vars_dict
|
33
35
|
current_state = vars_dict['test_mode'][2].get()
|
@@ -6,21 +6,21 @@ import os
|
|
6
6
|
import requests
|
7
7
|
|
8
8
|
# Import your GUI apps
|
9
|
-
from .
|
10
|
-
from .
|
11
|
-
from .
|
12
|
-
from .
|
13
|
-
from .
|
9
|
+
from .app_mask import initiate_mask_root
|
10
|
+
from .app_measure import initiate_measure_root
|
11
|
+
from .app_annotate import initiate_annotation_app_root
|
12
|
+
from .app_make_masks import initiate_mask_app_root
|
13
|
+
from .app_classify import initiate_classify_root
|
14
14
|
from .gui_utils import CustomButton, style_text_boxes, create_menu_bar
|
15
15
|
|
16
16
|
class MainApp(tk.Tk):
|
17
17
|
def __init__(self):
|
18
18
|
super().__init__()
|
19
|
+
width = self.winfo_screenwidth()
|
20
|
+
height = self.winfo_screenheight()
|
21
|
+
self.geometry(f"{width}x{height}")
|
19
22
|
self.title("SpaCr GUI Collection")
|
20
|
-
self.geometry("1100x1500")
|
21
23
|
self.configure(bg="black")
|
22
|
-
#self.attributes('-fullscreen', True)
|
23
|
-
|
24
24
|
style = ttk.Style()
|
25
25
|
style_text_boxes(style)
|
26
26
|
|
@@ -37,7 +37,7 @@ class MainApp(tk.Tk):
|
|
37
37
|
|
38
38
|
def create_widgets(self):
|
39
39
|
# Create the menu bar
|
40
|
-
self
|
40
|
+
create_menu_bar(self)
|
41
41
|
|
42
42
|
# Create a canvas to hold the selected app and other elements
|
43
43
|
self.canvas = tk.Canvas(self, bg="black", highlightthickness=0)
|
@@ -52,9 +52,6 @@ class MainApp(tk.Tk):
|
|
52
52
|
# Create startup screen with buttons for each GUI app
|
53
53
|
self.create_startup_screen()
|
54
54
|
|
55
|
-
def gui_create_menu_bar(self):
|
56
|
-
create_menu_bar(self)
|
57
|
-
|
58
55
|
def create_startup_screen(self):
|
59
56
|
self.clear_frame(self.content_frame)
|
60
57
|
|
@@ -65,11 +65,11 @@ def load_app(root, app_name, app_func):
|
|
65
65
|
app_func(root.content_frame)
|
66
66
|
|
67
67
|
def create_menu_bar(root):
|
68
|
-
from .
|
69
|
-
from .
|
70
|
-
from .
|
71
|
-
from .
|
72
|
-
from .
|
68
|
+
from .app_mask import initiate_mask_root
|
69
|
+
from .app_measure import initiate_measure_root
|
70
|
+
from .app_annotate import initiate_annotation_app_root
|
71
|
+
from .app_make_masks import initiate_mask_app_root
|
72
|
+
from .app_classify import initiate_classify_root
|
73
73
|
|
74
74
|
gui_apps = {
|
75
75
|
"Mask": initiate_mask_root,
|
@@ -96,6 +96,42 @@ def create_menu_bar(root):
|
|
96
96
|
# Configure the menu for the root window
|
97
97
|
root.config(menu=menu_bar)
|
98
98
|
|
99
|
+
def proceed_with_app(root, app_name, app_func):
|
100
|
+
|
101
|
+
from .app_mask import gui_mask
|
102
|
+
from .app_measure import gui_measure
|
103
|
+
from .app_annotate import gui_annotate
|
104
|
+
from .app_make_masks import gui_make_masks
|
105
|
+
from .app_classify import gui_classify
|
106
|
+
from .gui import gui_app
|
107
|
+
|
108
|
+
# Clear the current content frame
|
109
|
+
if hasattr(root, 'content_frame'):
|
110
|
+
for widget in root.content_frame.winfo_children():
|
111
|
+
widget.destroy()
|
112
|
+
else:
|
113
|
+
root.content_frame = tk.Frame(root)
|
114
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
115
|
+
root.grid_rowconfigure(1, weight=1)
|
116
|
+
root.grid_columnconfigure(0, weight=1)
|
117
|
+
|
118
|
+
# Initialize the new app in the content frame
|
119
|
+
if app_name == "Main App":
|
120
|
+
root.destroy() # Close the current window
|
121
|
+
gui_app() # Open the main app window
|
122
|
+
elif app_name == "Mask":
|
123
|
+
gui_mask()
|
124
|
+
elif app_name == "Measure":
|
125
|
+
gui_measure()
|
126
|
+
elif app_name == "Annotate":
|
127
|
+
gui_annotate()
|
128
|
+
elif app_name == "Make Masks":
|
129
|
+
gui_make_masks()
|
130
|
+
elif app_name == "Classify":
|
131
|
+
gui_classify()
|
132
|
+
else:
|
133
|
+
raise ValueError(f"Invalid app name: {app_name}")
|
134
|
+
|
99
135
|
def load_app(root, app_name, app_func):
|
100
136
|
# Cancel all scheduled after tasks
|
101
137
|
if hasattr(root, 'after_tasks'):
|
@@ -103,35 +139,25 @@ def load_app(root, app_name, app_func):
|
|
103
139
|
root.after_cancel(task)
|
104
140
|
root.after_tasks = []
|
105
141
|
|
106
|
-
def proceed_with_app():
|
107
|
-
# Clear the current content frame
|
108
|
-
if hasattr(root, 'content_frame'):
|
109
|
-
for widget in root.content_frame.winfo_children():
|
110
|
-
widget.destroy()
|
111
|
-
else:
|
112
|
-
root.content_frame = tk.Frame(root)
|
113
|
-
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
114
|
-
root.grid_rowconfigure(1, weight=1)
|
115
|
-
root.grid_columnconfigure(0, weight=1)
|
116
|
-
|
117
|
-
# Initialize the new app in the content frame
|
118
|
-
app_func(root.content_frame)
|
119
|
-
|
120
142
|
# Exit functionality only for the annotation app
|
121
143
|
if app_name != "Annotate" and hasattr(root, 'current_app_exit_func'):
|
122
144
|
root.next_app_func = proceed_with_app
|
145
|
+
root.next_app_args = (app_name, app_func) # Ensure correct arguments
|
123
146
|
root.current_app_exit_func()
|
124
147
|
else:
|
125
|
-
proceed_with_app()
|
148
|
+
proceed_with_app(root, app_name, app_func)
|
149
|
+
|
126
150
|
|
127
151
|
def create_menu_bar(root):
|
128
|
-
from .
|
129
|
-
from .
|
130
|
-
from .
|
131
|
-
from .
|
132
|
-
from .
|
152
|
+
from .app_mask import initiate_mask_root
|
153
|
+
from .app_measure import initiate_measure_root
|
154
|
+
from .app_annotate import initiate_annotation_app_root
|
155
|
+
from .app_make_masks import initiate_mask_app_root
|
156
|
+
from .app_classify import initiate_classify_root
|
157
|
+
from .gui import gui_app
|
133
158
|
|
134
159
|
gui_apps = {
|
160
|
+
"Main App": gui_app,
|
135
161
|
"Mask": initiate_mask_root,
|
136
162
|
"Measure": initiate_measure_root,
|
137
163
|
"Annotate": initiate_annotation_app_root,
|
@@ -381,19 +407,19 @@ def style_text_boxes_v1(style):
|
|
381
407
|
background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
|
382
408
|
foreground=[('active', '#ffffff'), ('disabled', '#888888')])
|
383
409
|
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
|
384
|
-
style.configure('TCheckbutton', background='
|
410
|
+
style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
|
385
411
|
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
386
412
|
|
387
413
|
def style_text_boxes(style):
|
388
414
|
font_style = tkFont.Font(family="Helvetica", size=10)
|
389
|
-
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='
|
390
|
-
style.configure('TCombobox', fieldbackground='
|
415
|
+
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
|
416
|
+
style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
|
391
417
|
style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
|
392
418
|
style.map('Custom.TButton',
|
393
419
|
background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
|
394
420
|
foreground=[('active', '#ffffff'), ('disabled', '#888888')])
|
395
421
|
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
|
396
|
-
style.configure('TCheckbutton', background='
|
422
|
+
style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
|
397
423
|
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
398
424
|
|
399
425
|
|
@@ -4,9 +4,13 @@ README.rst
|
|
4
4
|
setup.py
|
5
5
|
spacr/__init__.py
|
6
6
|
spacr/__main__.py
|
7
|
-
spacr/
|
7
|
+
spacr/app_annotate.py
|
8
|
+
spacr/app_classify.py
|
9
|
+
spacr/app_make_masks.py
|
10
|
+
spacr/app_make_masks_v2.py
|
11
|
+
spacr/app_mask.py
|
12
|
+
spacr/app_measure.py
|
8
13
|
spacr/chris.py
|
9
|
-
spacr/classify_app.py
|
10
14
|
spacr/core.py
|
11
15
|
spacr/deep_spacr.py
|
12
16
|
spacr/graph_learning.py
|
@@ -14,11 +18,7 @@ spacr/gui.py
|
|
14
18
|
spacr/gui_utils.py
|
15
19
|
spacr/io.py
|
16
20
|
spacr/logger.py
|
17
|
-
spacr/make_masks_app.py
|
18
|
-
spacr/make_masks_app_v2.py
|
19
|
-
spacr/mask_app.py
|
20
21
|
spacr/measure.py
|
21
|
-
spacr/measure_app.py
|
22
22
|
spacr/plot.py
|
23
23
|
spacr/sequencing.py
|
24
24
|
spacr/settings.py
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|