spacr 0.2.68__py3-none-any.whl → 0.2.81__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 +1 -1
- spacr/gui_core.py +152 -105
- spacr/gui_elements.py +190 -18
- spacr/sequencing.py +234 -422
- spacr/settings.py +13 -9
- spacr/utils.py +4 -1
- {spacr-0.2.68.dist-info → spacr-0.2.81.dist-info}/METADATA +2 -2
- {spacr-0.2.68.dist-info → spacr-0.2.81.dist-info}/RECORD +12 -12
- {spacr-0.2.68.dist-info → spacr-0.2.81.dist-info}/LICENSE +0 -0
- {spacr-0.2.68.dist-info → spacr-0.2.81.dist-info}/WHEEL +0 -0
- {spacr-0.2.68.dist-info → spacr-0.2.81.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.68.dist-info → spacr-0.2.81.dist-info}/top_level.txt +0 -0
spacr/core.py
CHANGED
@@ -1638,7 +1638,7 @@ def preprocess_generate_masks(src, settings={}):
|
|
1638
1638
|
|
1639
1639
|
settings = set_default_settings_preprocess_generate_masks(src, settings)
|
1640
1640
|
settings['src'] = src
|
1641
|
-
save_settings(settings)
|
1641
|
+
save_settings(settings, name='gen_mask')
|
1642
1642
|
|
1643
1643
|
if not settings['pathogen_channel'] is None:
|
1644
1644
|
custom_model_ls = ['toxo_pv_lumen','toxo_cyto']
|
spacr/gui_core.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import traceback, ctypes, csv, re
|
1
|
+
import traceback, ctypes, csv, re, platform, time
|
2
2
|
import tkinter as tk
|
3
3
|
from tkinter import ttk
|
4
4
|
from tkinter import filedialog
|
@@ -19,7 +19,7 @@ try:
|
|
19
19
|
except AttributeError:
|
20
20
|
pass
|
21
21
|
|
22
|
-
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , set_dark_style
|
22
|
+
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , spacrSlider, set_dark_style, standardize_figure
|
23
23
|
|
24
24
|
# Define global variables
|
25
25
|
q = None
|
@@ -35,8 +35,6 @@ figures = None
|
|
35
35
|
figure_index = None
|
36
36
|
progress_bar = None
|
37
37
|
usage_bars = None
|
38
|
-
fig_memory_limit = None
|
39
|
-
figure_current_memory_usage = None
|
40
38
|
|
41
39
|
thread_control = {"run_thread": None, "stop_requested": False}
|
42
40
|
|
@@ -80,35 +78,10 @@ def toggle_settings(button_scrollable_frame):
|
|
80
78
|
category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
|
81
79
|
vars_dict = hide_all_settings(vars_dict, categories)
|
82
80
|
|
83
|
-
def process_fig_queue():
|
84
|
-
global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index
|
85
|
-
|
86
|
-
from .gui_elements import standardize_figure
|
87
|
-
try:
|
88
|
-
while not fig_queue.empty():
|
89
|
-
fig = fig_queue.get_nowait()
|
90
|
-
|
91
|
-
if fig is None:
|
92
|
-
print("Warning: Retrieved a None figure from fig_queue.")
|
93
|
-
continue # Skip processing if the figure is None
|
94
|
-
|
95
|
-
# Standardize the figure appearance before adding it to the list
|
96
|
-
standardize_figure(fig)
|
97
|
-
|
98
|
-
figures.append(fig)
|
99
|
-
if figure_index == len(figures) - 2:
|
100
|
-
figure_index += 1
|
101
|
-
display_figure(fig)
|
102
|
-
except Exception as e:
|
103
|
-
traceback.print_exc()
|
104
|
-
finally:
|
105
|
-
after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
|
106
|
-
parent_frame.after_tasks.append(after_id)
|
107
|
-
|
108
81
|
def display_figure(fig):
|
109
82
|
global canvas, canvas_widget
|
110
83
|
|
111
|
-
from .gui_elements import
|
84
|
+
from .gui_elements import save_figure_as_format, modify_figure
|
112
85
|
|
113
86
|
# Apply the dark style to the context menu
|
114
87
|
style_out = set_dark_style(ttk.Style())
|
@@ -197,7 +170,7 @@ def display_figure(fig):
|
|
197
170
|
#flash_feedback("right")
|
198
171
|
show_next_figure()
|
199
172
|
|
200
|
-
def
|
173
|
+
def zoom_v1(event):
|
201
174
|
nonlocal scale_factor
|
202
175
|
|
203
176
|
zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
|
@@ -230,16 +203,55 @@ def display_figure(fig):
|
|
230
203
|
# Redraw the figure
|
231
204
|
fig.canvas.draw_idle()
|
232
205
|
|
206
|
+
def zoom(event):
|
207
|
+
nonlocal scale_factor
|
208
|
+
|
209
|
+
zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
|
210
|
+
|
211
|
+
# Determine the zoom direction based on the scroll event
|
212
|
+
if event.num == 4 or event.delta > 0: # Scroll up (zoom in)
|
213
|
+
scale_factor /= (1 + zoom_speed) # Divide to zoom in
|
214
|
+
elif event.num == 5 or event.delta < 0: # Scroll down (zoom out)
|
215
|
+
scale_factor *= (1 + zoom_speed) # Multiply to zoom out
|
216
|
+
|
217
|
+
# Adjust the axes limits based on the new scale factor
|
218
|
+
for ax in canvas.figure.get_axes():
|
219
|
+
xlim = ax.get_xlim()
|
220
|
+
ylim = ax.get_ylim()
|
221
|
+
|
222
|
+
x_center = (xlim[1] + xlim[0]) / 2
|
223
|
+
y_center = (ylim[1] + ylim[0]) / 2
|
224
|
+
|
225
|
+
x_range = (xlim[1] - xlim[0]) * scale_factor
|
226
|
+
y_range = (ylim[1] - ylim[0]) * scale_factor
|
227
|
+
|
228
|
+
# Set the new limits
|
229
|
+
ax.set_xlim([x_center - x_range / 2, x_center + x_range / 2])
|
230
|
+
ax.set_ylim([y_center - y_range / 2, y_center + y_range / 2])
|
231
|
+
|
232
|
+
# Redraw the figure efficiently
|
233
|
+
canvas.draw_idle()
|
234
|
+
|
235
|
+
|
233
236
|
# Bind events for hover, click interactions, and zoom
|
234
237
|
canvas_widget.bind("<Motion>", on_hover)
|
235
238
|
canvas_widget.bind("<Leave>", on_leave)
|
236
239
|
canvas_widget.bind("<Button-1>", on_click)
|
237
240
|
canvas_widget.bind("<Button-3>", on_right_click)
|
238
241
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
242
|
+
|
243
|
+
# Detect the operating system and bind the appropriate mouse wheel events
|
244
|
+
current_os = platform.system()
|
245
|
+
|
246
|
+
if current_os == "Windows":
|
247
|
+
canvas_widget.bind("<MouseWheel>", zoom) # Windows
|
248
|
+
elif current_os == "Darwin": # macOS
|
249
|
+
canvas_widget.bind("<MouseWheel>", zoom)
|
250
|
+
canvas_widget.bind("<Button-4>", zoom) # Scroll up
|
251
|
+
canvas_widget.bind("<Button-5>", zoom) # Scroll down
|
252
|
+
elif current_os == "Linux":
|
253
|
+
canvas_widget.bind("<Button-4>", zoom) # Linux Scroll up
|
254
|
+
canvas_widget.bind("<Button-5>", zoom) # Linux Scroll down
|
243
255
|
|
244
256
|
def clear_unused_figures():
|
245
257
|
global figures, figure_index
|
@@ -253,6 +265,7 @@ def clear_unused_figures():
|
|
253
265
|
|
254
266
|
def show_previous_figure():
|
255
267
|
global figure_index, figures, fig_queue
|
268
|
+
|
256
269
|
if figure_index is not None and figure_index > 0:
|
257
270
|
figure_index -= 1
|
258
271
|
display_figure(figures[figure_index])
|
@@ -270,8 +283,104 @@ def show_next_figure():
|
|
270
283
|
figure_index += 1
|
271
284
|
display_figure(fig)
|
272
285
|
|
273
|
-
def
|
274
|
-
global
|
286
|
+
def process_fig_queue():
|
287
|
+
global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index, index_control
|
288
|
+
|
289
|
+
from .gui_elements import standardize_figure
|
290
|
+
try:
|
291
|
+
while not fig_queue.empty():
|
292
|
+
fig = fig_queue.get_nowait()
|
293
|
+
|
294
|
+
if fig is None:
|
295
|
+
print("Warning: Retrieved a None figure from fig_queue.")
|
296
|
+
continue # Skip processing if the figure is None
|
297
|
+
|
298
|
+
# Standardize the figure appearance before adding it to the list
|
299
|
+
fig = standardize_figure(fig)
|
300
|
+
|
301
|
+
figures.append(fig)
|
302
|
+
|
303
|
+
# Update the slider range and set the value to the latest figure index
|
304
|
+
index_control.set_to(len(figures) - 1)
|
305
|
+
|
306
|
+
if figure_index == -1:
|
307
|
+
figure_index += 1
|
308
|
+
display_figure(figures[figure_index])
|
309
|
+
index_control.set(figure_index)
|
310
|
+
|
311
|
+
except Exception as e:
|
312
|
+
traceback.print_exc()
|
313
|
+
finally:
|
314
|
+
after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
|
315
|
+
parent_frame.after_tasks.append(after_id)
|
316
|
+
|
317
|
+
def update_figure(value):
|
318
|
+
global figure_index, figures
|
319
|
+
|
320
|
+
# Convert the value to an integer
|
321
|
+
index = int(value)
|
322
|
+
|
323
|
+
# Check if the index is valid
|
324
|
+
if 0 <= index < len(figures):
|
325
|
+
figure_index = index
|
326
|
+
display_figure(figures[figure_index])
|
327
|
+
|
328
|
+
# Update the index control widget's range and value
|
329
|
+
index_control.set_to(len(figures) - 1)
|
330
|
+
index_control.set(figure_index)
|
331
|
+
|
332
|
+
def setup_plot_section(vertical_container):
|
333
|
+
global canvas, canvas_widget, figures, figure_index, index_control
|
334
|
+
|
335
|
+
# Initialize deque for storing figures and the current index
|
336
|
+
figures = deque()
|
337
|
+
|
338
|
+
# Create a frame for the plot section
|
339
|
+
plot_frame = tk.Frame(vertical_container)
|
340
|
+
vertical_container.add(plot_frame, stretch="always")
|
341
|
+
|
342
|
+
# Set up the plot
|
343
|
+
figure = Figure(figsize=(30, 4), dpi=100)
|
344
|
+
plot = figure.add_subplot(111)
|
345
|
+
plot.plot([], [])
|
346
|
+
plot.axis('off')
|
347
|
+
|
348
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
349
|
+
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
350
|
+
canvas_widget = canvas.get_tk_widget()
|
351
|
+
canvas_widget.grid(row=0, column=0, sticky="nsew")
|
352
|
+
|
353
|
+
plot_frame.grid_rowconfigure(0, weight=1)
|
354
|
+
plot_frame.grid_columnconfigure(0, weight=1)
|
355
|
+
|
356
|
+
canvas.draw()
|
357
|
+
canvas.figure = figure # Ensure that the figure is linked to the canvas
|
358
|
+
style_out = set_dark_style(ttk.Style())
|
359
|
+
bg = style_out['bg_color']
|
360
|
+
fg = style_out['fg_color']
|
361
|
+
|
362
|
+
figure.patch.set_facecolor(bg)
|
363
|
+
plot.set_facecolor(bg)
|
364
|
+
containers = [plot_frame]
|
365
|
+
|
366
|
+
# Create slider
|
367
|
+
control_frame = tk.Frame(plot_frame, height=15*2, bg=bg) # Fixed height based on knob_radius
|
368
|
+
control_frame.grid(row=1, column=0, sticky="ew", padx=10, pady=5)
|
369
|
+
control_frame.grid_propagate(False) # Prevent the frame from resizing
|
370
|
+
|
371
|
+
# Pass the update_figure function as the command to spacrSlider
|
372
|
+
index_control = spacrSlider(control_frame, from_=0, to=0, value=0, thickness=2, knob_radius=10, position="center", show_index=True, command=update_figure)
|
373
|
+
index_control.grid(row=0, column=0, sticky="ew")
|
374
|
+
control_frame.grid_columnconfigure(0, weight=1)
|
375
|
+
|
376
|
+
widgets = [canvas_widget, index_control]
|
377
|
+
style = ttk.Style(vertical_container)
|
378
|
+
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
379
|
+
|
380
|
+
return canvas, canvas_widget
|
381
|
+
|
382
|
+
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, figures_var, figure_index_var, index_control_var, progress_bar_var, usage_bars_var):
|
383
|
+
global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, index_control
|
275
384
|
thread_control = thread_control_var
|
276
385
|
q = q_var
|
277
386
|
console_output = console_output_var
|
@@ -285,8 +394,7 @@ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var,
|
|
285
394
|
figure_index = figure_index_var
|
286
395
|
progress_bar = progress_bar_var
|
287
396
|
usage_bars = usage_bars_var
|
288
|
-
|
289
|
-
figure_current_memory_usage = figure_current_memory_usage_var
|
397
|
+
index_control = index_control_var
|
290
398
|
|
291
399
|
def import_settings(settings_type='mask'):
|
292
400
|
from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
|
@@ -400,46 +508,6 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
|
|
400
508
|
print("Settings panel setup complete")
|
401
509
|
return scrollable_frame, vars_dict
|
402
510
|
|
403
|
-
def setup_plot_section(vertical_container):
|
404
|
-
global canvas, canvas_widget, figures, figure_index
|
405
|
-
|
406
|
-
from .gui_elements import set_element_size
|
407
|
-
|
408
|
-
# Initialize deque for storing figures and the current index
|
409
|
-
figures = deque()
|
410
|
-
figure_index = -1
|
411
|
-
|
412
|
-
# Create a frame for the plot section
|
413
|
-
plot_frame = tk.Frame(vertical_container)
|
414
|
-
vertical_container.add(plot_frame, stretch="always")
|
415
|
-
|
416
|
-
# Set up the plot
|
417
|
-
figure = Figure(figsize=(30, 4), dpi=100)
|
418
|
-
plot = figure.add_subplot(111)
|
419
|
-
plot.plot([], [])
|
420
|
-
plot.axis('off')
|
421
|
-
|
422
|
-
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
423
|
-
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
424
|
-
canvas_widget = canvas.get_tk_widget()
|
425
|
-
canvas_widget.grid(row=0, column=0, sticky="nsew")
|
426
|
-
|
427
|
-
plot_frame.grid_rowconfigure(0, weight=1)
|
428
|
-
plot_frame.grid_columnconfigure(0, weight=1)
|
429
|
-
|
430
|
-
canvas.draw()
|
431
|
-
canvas.figure = figure # Ensure that the figure is linked to the canvas
|
432
|
-
style_out = set_dark_style(ttk.Style())
|
433
|
-
|
434
|
-
figure.patch.set_facecolor(style_out['bg_color'])
|
435
|
-
plot.set_facecolor(style_out['bg_color'])
|
436
|
-
containers = [plot_frame]
|
437
|
-
widgets = [canvas_widget]
|
438
|
-
style = ttk.Style(vertical_container)
|
439
|
-
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
440
|
-
|
441
|
-
return canvas, canvas_widget
|
442
|
-
|
443
511
|
def setup_console(vertical_container):
|
444
512
|
global console_output
|
445
513
|
from .gui_elements import set_dark_style
|
@@ -479,27 +547,6 @@ def setup_console(vertical_container):
|
|
479
547
|
|
480
548
|
return console_output, console_frame
|
481
549
|
|
482
|
-
def setup_progress_frame(vertical_container):
|
483
|
-
global progress_output
|
484
|
-
style_out = set_dark_style(ttk.Style())
|
485
|
-
font_loader = style_out['font_loader']
|
486
|
-
font_size = style_out['font_size']
|
487
|
-
progress_frame = tk.Frame(vertical_container)
|
488
|
-
vertical_container.add(progress_frame, stretch="always")
|
489
|
-
label_frame = tk.Frame(progress_frame)
|
490
|
-
label_frame.grid(row=0, column=0, sticky="ew", pady=(5, 0), padx=10)
|
491
|
-
progress_label = spacrLabel(label_frame, text="Processing: 0%", font=font_loader.get_font(size=font_size), anchor='w', justify='left', align="left")
|
492
|
-
progress_label.grid(row=0, column=0, sticky="w")
|
493
|
-
progress_output = scrolledtext.ScrolledText(progress_frame, height=10)
|
494
|
-
progress_output.grid(row=1, column=0, sticky="nsew")
|
495
|
-
progress_frame.grid_rowconfigure(1, weight=1)
|
496
|
-
progress_frame.grid_columnconfigure(0, weight=1)
|
497
|
-
containers = [progress_frame, label_frame]
|
498
|
-
widgets = [progress_label, progress_output]
|
499
|
-
style = ttk.Style(vertical_container)
|
500
|
-
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
501
|
-
return progress_output
|
502
|
-
|
503
550
|
def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
|
504
551
|
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
|
505
552
|
from .gui_utils import download_hug_dataset
|
@@ -836,7 +883,7 @@ def initiate_root(parent, settings_type='mask'):
|
|
836
883
|
tuple: A tuple containing the parent frame and the dictionary of variables used in the GUI.
|
837
884
|
"""
|
838
885
|
|
839
|
-
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar, uppdate_frequency, figures, figure_index,
|
886
|
+
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar, uppdate_frequency, figures, figure_index, index_control, usage_bars
|
840
887
|
|
841
888
|
from .gui_utils import setup_frame
|
842
889
|
from .settings import descriptions
|
@@ -853,8 +900,6 @@ def initiate_root(parent, settings_type='mask'):
|
|
853
900
|
# Initialize global variables
|
854
901
|
figures = deque()
|
855
902
|
figure_index = -1
|
856
|
-
fig_memory_limit = 200 * 1024 * 1024 # 200 MB limit
|
857
|
-
figure_current_memory_usage = 0
|
858
903
|
|
859
904
|
parent_frame = parent
|
860
905
|
|
@@ -886,7 +931,7 @@ def initiate_root(parent, settings_type='mask'):
|
|
886
931
|
button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
|
887
932
|
_, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col, uppdate_frequency)
|
888
933
|
|
889
|
-
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars
|
934
|
+
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, index_control, progress_bar, usage_bars)
|
890
935
|
description_text = descriptions.get(settings_type, "No description available for this module.")
|
891
936
|
|
892
937
|
q.put(f"Console")
|
@@ -899,4 +944,6 @@ def initiate_root(parent, settings_type='mask'):
|
|
899
944
|
parent_window.after_tasks.append(after_id)
|
900
945
|
|
901
946
|
print("Root initialization complete")
|
902
|
-
return parent_frame, vars_dict
|
947
|
+
return parent_frame, vars_dict
|
948
|
+
|
949
|
+
|
spacr/gui_elements.py
CHANGED
@@ -670,25 +670,189 @@ class spacrProgressBar(ttk.Progressbar):
|
|
670
670
|
|
671
671
|
def update_label(self):
|
672
672
|
if self.label and self.progress_label:
|
673
|
-
#
|
673
|
+
# Start with the base progress information
|
674
674
|
label_text = f"Processing: {self['value']}/{self['maximum']}"
|
675
|
+
|
676
|
+
# Include the operation type if it exists
|
675
677
|
if self.operation_type:
|
676
678
|
label_text += f", {self.operation_type}"
|
679
|
+
|
680
|
+
# Handle additional info without adding newlines
|
677
681
|
if hasattr(self, 'additional_info') and self.additional_info:
|
678
|
-
#
|
679
|
-
label_text += "\n\n"
|
680
|
-
# Split the additional_info into a list of items
|
682
|
+
# Join all additional info items with a space and ensure they're on the same line
|
681
683
|
items = self.additional_info.split(", ")
|
682
|
-
formatted_additional_info = ""
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
formatted_additional_info += f"{items[i]}\n\n" # If there's an odd item out, add it alone
|
689
|
-
label_text += formatted_additional_info.strip()
|
684
|
+
formatted_additional_info = " ".join(items)
|
685
|
+
|
686
|
+
# Append the additional info to the label_text, ensuring it's all in one line
|
687
|
+
label_text += f" {formatted_additional_info.strip()}"
|
688
|
+
|
689
|
+
# Update the progress label
|
690
690
|
self.progress_label.config(text=label_text)
|
691
691
|
|
692
|
+
class spacrSlider(tk.Frame):
|
693
|
+
def __init__(self, master=None, length=None, thickness=2, knob_radius=10, position="center", from_=0, to=100, value=None, show_index=False, command=None, **kwargs):
|
694
|
+
super().__init__(master, **kwargs)
|
695
|
+
|
696
|
+
self.specified_length = length # Store the specified length, if any
|
697
|
+
self.knob_radius = knob_radius
|
698
|
+
self.thickness = thickness
|
699
|
+
self.knob_position = knob_radius # Start at the beginning of the slider
|
700
|
+
self.slider_line = None
|
701
|
+
self.knob = None
|
702
|
+
self.position = position.lower() # Store the position option
|
703
|
+
self.offset = 0 # Initialize offset
|
704
|
+
self.from_ = from_ # Minimum value of the slider
|
705
|
+
self.to = to # Maximum value of the slider
|
706
|
+
self.value = value if value is not None else from_ # Initial value of the slider
|
707
|
+
self.show_index = show_index # Whether to show the index Entry widget
|
708
|
+
self.command = command # Callback function to handle value changes
|
709
|
+
|
710
|
+
# Initialize the style and colors
|
711
|
+
style_out = set_dark_style(ttk.Style())
|
712
|
+
self.fg_color = style_out['fg_color']
|
713
|
+
self.bg_color = style_out['bg_color']
|
714
|
+
self.active_color = style_out['active_color']
|
715
|
+
self.inactive_color = style_out['inactive_color']
|
716
|
+
|
717
|
+
# Configure the frame's background color
|
718
|
+
self.configure(bg=self.bg_color)
|
719
|
+
|
720
|
+
# Create a frame for the slider and entry if needed
|
721
|
+
self.grid_columnconfigure(1, weight=1)
|
722
|
+
|
723
|
+
# Entry widget for showing and editing index, if enabled
|
724
|
+
if self.show_index:
|
725
|
+
self.index_var = tk.StringVar(value=str(int(self.value)))
|
726
|
+
self.index_entry = tk.Entry(self, textvariable=self.index_var, width=5, bg=self.bg_color, fg=self.fg_color, insertbackground=self.fg_color)
|
727
|
+
self.index_entry.grid(row=0, column=0, padx=5)
|
728
|
+
# Bind the entry to update the slider on change
|
729
|
+
self.index_entry.bind("<Return>", self.update_slider_from_entry)
|
730
|
+
|
731
|
+
# Create the slider canvas
|
732
|
+
self.canvas = tk.Canvas(self, height=knob_radius * 2, bg=self.bg_color, highlightthickness=0)
|
733
|
+
self.canvas.grid(row=0, column=1, sticky="ew")
|
734
|
+
|
735
|
+
# Set initial length to specified length or default value
|
736
|
+
self.length = self.specified_length if self.specified_length is not None else self.canvas.winfo_reqwidth()
|
737
|
+
|
738
|
+
# Calculate initial knob position based on the initial value
|
739
|
+
self.knob_position = self.value_to_position(self.value)
|
740
|
+
|
741
|
+
# Bind resize event to dynamically adjust the slider length if no length is specified
|
742
|
+
self.canvas.bind("<Configure>", self.resize_slider)
|
743
|
+
|
744
|
+
# Draw the slider components
|
745
|
+
self.draw_slider(inactive=True)
|
746
|
+
|
747
|
+
# Bind mouse events to the knob and slider
|
748
|
+
self.canvas.bind("<B1-Motion>", self.move_knob)
|
749
|
+
self.canvas.bind("<Button-1>", self.activate_knob) # Activate knob on click
|
750
|
+
self.canvas.bind("<ButtonRelease-1>", self.release_knob) # Trigger command on release
|
751
|
+
|
752
|
+
def resize_slider(self, event):
|
753
|
+
if self.specified_length is not None:
|
754
|
+
self.length = self.specified_length
|
755
|
+
else:
|
756
|
+
self.length = int(event.width * 0.9) # 90% of the container width
|
757
|
+
|
758
|
+
# Calculate the horizontal offset based on the position
|
759
|
+
if self.position == "center":
|
760
|
+
self.offset = (event.width - self.length) // 2
|
761
|
+
elif self.position == "right":
|
762
|
+
self.offset = event.width - self.length
|
763
|
+
else: # position is "left"
|
764
|
+
self.offset = 0
|
765
|
+
|
766
|
+
# Update the knob position after resizing
|
767
|
+
self.knob_position = self.value_to_position(self.value)
|
768
|
+
self.draw_slider(inactive=True)
|
769
|
+
|
770
|
+
def value_to_position(self, value):
|
771
|
+
if self.to == self.from_:
|
772
|
+
return self.knob_radius
|
773
|
+
relative_value = (value - self.from_) / (self.to - self.from_)
|
774
|
+
return self.knob_radius + relative_value * (self.length - 2 * self.knob_radius)
|
775
|
+
|
776
|
+
def position_to_value(self, position):
|
777
|
+
if self.to == self.from_:
|
778
|
+
return self.from_
|
779
|
+
relative_position = (position - self.knob_radius) / (self.length - 2 * self.knob_radius)
|
780
|
+
return self.from_ + relative_position * (self.to - self.from_)
|
781
|
+
|
782
|
+
def draw_slider(self, inactive=False):
|
783
|
+
self.canvas.delete("all")
|
784
|
+
|
785
|
+
self.slider_line = self.canvas.create_line(
|
786
|
+
self.offset + self.knob_radius,
|
787
|
+
self.knob_radius,
|
788
|
+
self.offset + self.length - self.knob_radius,
|
789
|
+
self.knob_radius,
|
790
|
+
fill=self.fg_color,
|
791
|
+
width=self.thickness
|
792
|
+
)
|
793
|
+
|
794
|
+
knob_color = self.inactive_color if inactive else self.active_color
|
795
|
+
self.knob = self.canvas.create_oval(
|
796
|
+
self.offset + self.knob_position - self.knob_radius,
|
797
|
+
self.knob_radius - self.knob_radius,
|
798
|
+
self.offset + self.knob_position + self.knob_radius,
|
799
|
+
self.knob_radius + self.knob_radius,
|
800
|
+
fill=knob_color,
|
801
|
+
outline=""
|
802
|
+
)
|
803
|
+
|
804
|
+
def move_knob(self, event):
|
805
|
+
new_position = min(max(event.x - self.offset, self.knob_radius), self.length - self.knob_radius)
|
806
|
+
self.knob_position = new_position
|
807
|
+
self.value = self.position_to_value(self.knob_position)
|
808
|
+
self.canvas.coords(
|
809
|
+
self.knob,
|
810
|
+
self.offset + self.knob_position - self.knob_radius,
|
811
|
+
self.knob_radius - self.knob_radius,
|
812
|
+
self.offset + self.knob_position + self.knob_radius,
|
813
|
+
self.knob_radius + self.knob_radius
|
814
|
+
)
|
815
|
+
if self.show_index:
|
816
|
+
self.index_var.set(str(int(self.value)))
|
817
|
+
|
818
|
+
def activate_knob(self, event):
|
819
|
+
self.draw_slider(inactive=False)
|
820
|
+
self.move_knob(event)
|
821
|
+
|
822
|
+
def release_knob(self, event):
|
823
|
+
self.draw_slider(inactive=True)
|
824
|
+
if self.command:
|
825
|
+
self.command(self.value) # Call the command with the final value when the knob is released
|
826
|
+
|
827
|
+
def set_to(self, new_to):
|
828
|
+
self.to = new_to
|
829
|
+
self.knob_position = self.value_to_position(self.value)
|
830
|
+
self.draw_slider(inactive=False)
|
831
|
+
|
832
|
+
def get(self):
|
833
|
+
return self.value
|
834
|
+
|
835
|
+
def set(self, value):
|
836
|
+
"""Set the slider's value and update the knob position."""
|
837
|
+
self.value = max(self.from_, min(value, self.to)) # Ensure the value is within bounds
|
838
|
+
self.knob_position = self.value_to_position(self.value)
|
839
|
+
self.draw_slider(inactive=False)
|
840
|
+
if self.show_index:
|
841
|
+
self.index_var.set(str(int(self.value)))
|
842
|
+
|
843
|
+
def jump_to_click(self, event):
|
844
|
+
self.activate_knob(event)
|
845
|
+
|
846
|
+
def update_slider_from_entry(self, event):
|
847
|
+
"""Update the slider's value from the entry."""
|
848
|
+
try:
|
849
|
+
index = int(self.index_var.get())
|
850
|
+
self.set(index)
|
851
|
+
if self.command:
|
852
|
+
self.command(self.value)
|
853
|
+
except ValueError:
|
854
|
+
pass
|
855
|
+
|
692
856
|
def spacrScrollbarStyle(style, inactive_color, active_color):
|
693
857
|
# Check if custom elements already exist to avoid duplication
|
694
858
|
if not style.element_names().count('custom.Vertical.Scrollbar.trough'):
|
@@ -1987,6 +2151,11 @@ class AnnotateApp:
|
|
1987
2151
|
style_out = set_dark_style(ttk.Style())
|
1988
2152
|
self.font_loader = style_out['font_loader']
|
1989
2153
|
self.font_size = style_out['font_size']
|
2154
|
+
self.bg_color = style_out['bg_color']
|
2155
|
+
self.fg_color = style_out['fg_color']
|
2156
|
+
self.active_color = style_out['active_color']
|
2157
|
+
self.inactive_color = style_out['inactive_color']
|
2158
|
+
|
1990
2159
|
|
1991
2160
|
if self.font_loader:
|
1992
2161
|
self.font_style = self.font_loader.get_font(size=self.font_size)
|
@@ -2013,13 +2182,13 @@ class AnnotateApp:
|
|
2013
2182
|
self.button_frame = Frame(root, bg=self.root.cget('bg'))
|
2014
2183
|
self.button_frame.grid(row=2, column=1, padx=10, pady=10, sticky="se")
|
2015
2184
|
|
2016
|
-
self.next_button = Button(self.button_frame, text="Next", command=self.next_page, bg=
|
2185
|
+
self.next_button = Button(self.button_frame, text="Next", command=self.next_page, bg=self.bg_color, fg=self.fg_color, highlightbackground=self.fg_color, highlightcolor=self.fg_color, highlightthickness=1)
|
2017
2186
|
self.next_button.pack(side="right", padx=5)
|
2018
2187
|
|
2019
|
-
self.previous_button = Button(self.button_frame, text="Back", command=self.previous_page, bg=
|
2188
|
+
self.previous_button = Button(self.button_frame, text="Back", command=self.previous_page, bg=self.bg_color, fg=self.fg_color, highlightbackground=self.fg_color, highlightcolor=self.fg_color, highlightthickness=1)
|
2020
2189
|
self.previous_button.pack(side="right", padx=5)
|
2021
2190
|
|
2022
|
-
self.exit_button = Button(self.button_frame, text="Exit", command=self.shutdown, bg=
|
2191
|
+
self.exit_button = Button(self.button_frame, text="Exit", command=self.shutdown, bg=self.bg_color, fg=self.fg_color, highlightbackground=self.fg_color, highlightcolor=self.fg_color, highlightthickness=1)
|
2023
2192
|
self.exit_button.pack(side="right", padx=5)
|
2024
2193
|
|
2025
2194
|
# Calculate grid rows and columns based on the root window size and image size
|
@@ -2169,7 +2338,7 @@ class AnnotateApp:
|
|
2169
2338
|
|
2170
2339
|
for i, (img, annotation) in enumerate(loaded_images):
|
2171
2340
|
if annotation:
|
2172
|
-
border_color =
|
2341
|
+
border_color = self.active_color if annotation == 1 else 'red'
|
2173
2342
|
img = self.add_colored_border(img, border_width=5, border_color=border_color)
|
2174
2343
|
|
2175
2344
|
photo = ImageTk.PhotoImage(img)
|
@@ -2215,7 +2384,7 @@ class AnnotateApp:
|
|
2215
2384
|
left_border = Image.new('RGB', (border_width, img.height), color=border_color)
|
2216
2385
|
right_border = Image.new('RGB', (border_width, img.height), color=border_color)
|
2217
2386
|
|
2218
|
-
bordered_img = Image.new('RGB', (img.width + 2 * border_width, img.height + 2 * border_width), color=
|
2387
|
+
bordered_img = Image.new('RGB', (img.width + 2 * border_width, img.height + 2 * border_width), color=self.fg_color)
|
2219
2388
|
bordered_img.paste(top_border, (border_width, 0))
|
2220
2389
|
bordered_img.paste(bottom_border, (border_width, img.height + border_width))
|
2221
2390
|
bordered_img.paste(left_border, (0, border_width))
|
@@ -2255,7 +2424,7 @@ class AnnotateApp:
|
|
2255
2424
|
print(f"Image {os.path.split(path)[1]} annotated: {new_annotation}")
|
2256
2425
|
|
2257
2426
|
img_ = img.crop((5, 5, img.width-5, img.height-5))
|
2258
|
-
border_fill =
|
2427
|
+
border_fill = self.active_color if new_annotation == 1 else ('red' if new_annotation == 2 else None)
|
2259
2428
|
img_ = ImageOps.expand(img_, border=5, fill=border_fill) if border_fill else img_
|
2260
2429
|
|
2261
2430
|
photo = ImageTk.PhotoImage(img_)
|
@@ -2400,6 +2569,7 @@ def standardize_figure(fig):
|
|
2400
2569
|
- Line width: 1
|
2401
2570
|
- Line color: from style
|
2402
2571
|
"""
|
2572
|
+
|
2403
2573
|
|
2404
2574
|
for ax in fig.get_axes():
|
2405
2575
|
# Set font properties for title and labels
|
@@ -2448,6 +2618,8 @@ def standardize_figure(fig):
|
|
2448
2618
|
|
2449
2619
|
fig.canvas.draw_idle()
|
2450
2620
|
|
2621
|
+
return fig
|
2622
|
+
|
2451
2623
|
def modify_figure_properties(fig, scale_x=None, scale_y=None, line_width=None, font_size=None, x_lim=None, y_lim=None, grid=False, legend=None, title=None, x_label_rotation=None, remove_axes=False, bg_color=None, text_color=None, line_color=None):
|
2452
2624
|
"""
|
2453
2625
|
Modifies the properties of the figure, including scaling, line widths, font sizes, axis limits, x-axis label rotation, background color, text color, line color, and other common options.
|