autogaita 1.5.2__py3-none-any.whl → 1.5.4__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.
- autogaita-1.5.4.dist-info/METADATA +173 -0
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/RECORD +13 -20
- autogaita-1.5.4.dist-info/top_level.txt +2 -0
- autogaita_backup/common2D_run_and_done_gui.py +414 -0
- autogaita_backup/dlc_1_preparation.py +426 -0
- autogaita_backup/dlc_2_sc_extraction.py +217 -0
- autogaita_backup/dlc_gui.py +359 -0
- autogaita_backup/sleap_1_preparation.py +303 -0
- autogaita_backup/sleap_2_sc_extraction.py +167 -0
- autogaita_backup/sleap_gui.py +359 -0
- autogaita-1.5.2.dist-info/METADATA +0 -40
- autogaita-1.5.2.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/test_common2D_unit_1_preparation.py +0 -340
- tests/test_common2D_unit_2_sc_extraction.py +0 -367
- tests/test_common2D_unit_3_analysis.py +0 -245
- tests/test_common2D_unit_utils.py +0 -137
- tests/test_dlc_approval.py +0 -110
- tests/test_dlc_unit_1_preparation.py +0 -114
- tests/test_group_approval.py +0 -162
- tests/test_group_unit.py +0 -393
- tests/test_universal3D_approval.py +0 -89
- tests/test_universal3D_unit_1_preparation.py +0 -69
- tests/test_universal3D_unit_2_sc_extraction.py +0 -108
- tests/test_universal3D_unit_3_analysis.py +0 -248
- tests/test_utils.py +0 -442
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/WHEEL +0 -0
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/entry_points.txt +0 -0
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# %% imports
|
|
2
|
+
from autogaita.resources.utils import write_issues_to_textfile
|
|
3
|
+
from autogaita.common2D.common2D_utils import (
|
|
4
|
+
check_cycle_out_of_bounds,
|
|
5
|
+
check_cycle_duplicates,
|
|
6
|
+
check_cycle_order,
|
|
7
|
+
check_tracking,
|
|
8
|
+
handle_issues,
|
|
9
|
+
)
|
|
10
|
+
import os
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
# %% constants
|
|
15
|
+
from autogaita.common2D.common2D_constants import (
|
|
16
|
+
SCXLS_MOUSECOLS,
|
|
17
|
+
SCXLS_SCCOLS,
|
|
18
|
+
SWINGSTART_COL,
|
|
19
|
+
STANCEEND_COL,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# %% workflow step #2 - SC extraction (reading user-provided SC Table)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def extract_stepcycles(data, info, folderinfo, cfg):
|
|
26
|
+
"""Read XLS file with SC annotations, find correct row & return all_cycles"""
|
|
27
|
+
|
|
28
|
+
# ............................... preparation ....................................
|
|
29
|
+
# unpack
|
|
30
|
+
name = info["name"]
|
|
31
|
+
root_dir = folderinfo["root_dir"]
|
|
32
|
+
sctable_filename = folderinfo["sctable_filename"]
|
|
33
|
+
sampling_rate = cfg["sampling_rate"]
|
|
34
|
+
|
|
35
|
+
# check if excel file is .xlsx or .xls, if none found try to fix it
|
|
36
|
+
if (".xls" in sctable_filename) | (".xlsx" in sctable_filename):
|
|
37
|
+
if os.path.exists(os.path.join(root_dir, sctable_filename)):
|
|
38
|
+
SCdf = pd.read_excel(os.path.join(root_dir, sctable_filename))
|
|
39
|
+
else:
|
|
40
|
+
raise FileNotFoundError(
|
|
41
|
+
"No Annotation Table found! sctable_filename has to be @ root_dir"
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
# in cases below use string-concat (+) - otherwise xls added as path
|
|
45
|
+
if os.path.exists(os.path.join(root_dir, sctable_filename + ".xls")):
|
|
46
|
+
SCdf = pd.read_excel(os.path.join(root_dir, sctable_filename + ".xls"))
|
|
47
|
+
elif os.path.exists(os.path.join(root_dir, sctable_filename + ".xlsx")):
|
|
48
|
+
SCdf = pd.read_excel(os.path.join(root_dir, sctable_filename + ".xlsx"))
|
|
49
|
+
else:
|
|
50
|
+
raise FileNotFoundError(
|
|
51
|
+
"No Annotation Table found! sctable_filename has to be @ root_dir"
|
|
52
|
+
)
|
|
53
|
+
# see if table columns are labelled correctly (try a couple to allow user typos)
|
|
54
|
+
valid_col_flags = [False, False]
|
|
55
|
+
header_columns = ["", ""]
|
|
56
|
+
for h, header in enumerate([SCXLS_MOUSECOLS, SCXLS_SCCOLS]):
|
|
57
|
+
for header_col in header:
|
|
58
|
+
if header_col in SCdf.columns:
|
|
59
|
+
valid_col_flags[h] = True
|
|
60
|
+
header_columns[h] = header_col
|
|
61
|
+
break
|
|
62
|
+
if not all(valid_col_flags):
|
|
63
|
+
handle_issues("wrong_scxls_colnames", info)
|
|
64
|
+
return
|
|
65
|
+
# find our info columns & rows
|
|
66
|
+
mouse_col = SCdf.columns.get_loc(header_columns[0]) # INDEXING! (see list above)
|
|
67
|
+
sc_col = SCdf.columns.get_loc(header_columns[1])
|
|
68
|
+
SCdf[header_columns[0]] = SCdf[header_columns[0]].astype(str)
|
|
69
|
+
mouse_row = SCdf.index[SCdf[header_columns[0]] == name] # find this mouse
|
|
70
|
+
# this mouse was not included in sc xls
|
|
71
|
+
if len(mouse_row) == 0:
|
|
72
|
+
handle_issues("no_mouse", info)
|
|
73
|
+
return
|
|
74
|
+
# this mouse was included more than once
|
|
75
|
+
if len(mouse_row) > 1:
|
|
76
|
+
handle_issues("double_mouse", info)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
# next_mouse_idx = mouse_row # search idx of first row of next mouse
|
|
80
|
+
|
|
81
|
+
# .............................. main xls read ...................................
|
|
82
|
+
sc_num = 0
|
|
83
|
+
for column in SCdf.columns:
|
|
84
|
+
if STANCEEND_COL in column:
|
|
85
|
+
if np.isnan(SCdf[column][mouse_row].values[0]) == False:
|
|
86
|
+
sc_num += 1
|
|
87
|
+
if sc_num == 0:
|
|
88
|
+
handle_issues("no_scs", info)
|
|
89
|
+
return
|
|
90
|
+
user_scnum = SCdf.iloc[mouse_row, sc_col].values[0] # sanity check input
|
|
91
|
+
if user_scnum != sc_num: # warn the user, take the values we found
|
|
92
|
+
this_message = (
|
|
93
|
+
"\n***********\n! WARNING !\n***********\n"
|
|
94
|
+
+ "Mismatch between stepcycle number of SC Number column & "
|
|
95
|
+
+ "entries in swing/stance latency columns!"
|
|
96
|
+
+ "\nUsing all valid swing/stance entries."
|
|
97
|
+
)
|
|
98
|
+
print(this_message)
|
|
99
|
+
write_issues_to_textfile(this_message, info)
|
|
100
|
+
|
|
101
|
+
# ........................... idxs to all_cycles .................................
|
|
102
|
+
# use value we found, loop over all runs, throw all scs into all_cycles
|
|
103
|
+
all_cycles = [[None, None] for s in range(sc_num)] # fill :sc_num x 2 list
|
|
104
|
+
for s in range(sc_num):
|
|
105
|
+
if s == 0:
|
|
106
|
+
start_col = SCdf.columns.get_loc(SWINGSTART_COL)
|
|
107
|
+
end_col = SCdf.columns.get_loc(STANCEEND_COL)
|
|
108
|
+
else:
|
|
109
|
+
# str(s) because colnames match s for s>0!
|
|
110
|
+
start_col = SCdf.columns.get_loc(SWINGSTART_COL + "." + str(s))
|
|
111
|
+
end_col = SCdf.columns.get_loc(STANCEEND_COL + "." + str(s))
|
|
112
|
+
user_scnum += 1
|
|
113
|
+
# extract the SC times
|
|
114
|
+
start_in_s = float(SCdf.iloc[mouse_row, start_col].values[0])
|
|
115
|
+
end_in_s = float(SCdf.iloc[mouse_row, end_col].values[0])
|
|
116
|
+
# see if we are rounding to fix inaccurate user input
|
|
117
|
+
# => account for python's float precision leading to inaccuracies
|
|
118
|
+
# => two important steps here (sanity_check_vals only used for these checks)
|
|
119
|
+
# 1. round to 10th decimal to fix python making
|
|
120
|
+
# 3211.999999999999999995 out of 3212
|
|
121
|
+
sanity_check_start = round(start_in_s * sampling_rate, 10)
|
|
122
|
+
sanity_check_end = round(end_in_s * sampling_rate, 10)
|
|
123
|
+
# 2. comparing abs(sanity check vals) to 1e-7 just to be 1000% sure
|
|
124
|
+
if (abs(sanity_check_start % 1) > 1e-7) | (abs(sanity_check_end % 1) > 1e-7):
|
|
125
|
+
round_message = (
|
|
126
|
+
"\n***********\n! WARNING !\n***********\n"
|
|
127
|
+
+ "SC latencies of "
|
|
128
|
+
+ str(start_in_s)
|
|
129
|
+
+ "s to "
|
|
130
|
+
+ str(end_in_s)
|
|
131
|
+
+ "s were not provided in units of the frame rate!"
|
|
132
|
+
+ "\nWe thus use the previous possible frame(s)."
|
|
133
|
+
+ "\nDouble check if this worked as expected or fix annotation table!"
|
|
134
|
+
)
|
|
135
|
+
print(round_message)
|
|
136
|
+
write_issues_to_textfile(round_message, info)
|
|
137
|
+
# assign to all_cycles (note int() rounds down!)
|
|
138
|
+
all_cycles[s][0] = int(start_in_s * sampling_rate)
|
|
139
|
+
all_cycles[s][1] = int(end_in_s * sampling_rate)
|
|
140
|
+
# check if we are in data-bounds
|
|
141
|
+
if (all_cycles[s][0] in data.index) & (all_cycles[s][1] in data.index):
|
|
142
|
+
pass
|
|
143
|
+
else:
|
|
144
|
+
all_cycles[s] = [None, None] # so they can be cleaned later
|
|
145
|
+
this_message = (
|
|
146
|
+
"\n***********\n! WARNING !\n***********\n"
|
|
147
|
+
+ "SC latencies of: "
|
|
148
|
+
+ str(start_in_s)
|
|
149
|
+
+ "s to "
|
|
150
|
+
+ str(end_in_s)
|
|
151
|
+
+ "s not in data/video range!"
|
|
152
|
+
+ "\nSkipping!"
|
|
153
|
+
)
|
|
154
|
+
print(this_message)
|
|
155
|
+
write_issues_to_textfile(this_message, info)
|
|
156
|
+
|
|
157
|
+
# ............................ clean all_cycles ..................................
|
|
158
|
+
# check if we skipped latencies because they were out of data-bounds
|
|
159
|
+
all_cycles = check_cycle_out_of_bounds(all_cycles)
|
|
160
|
+
if all_cycles: # can be None if all SCs were out of bounds
|
|
161
|
+
# check if there are any duplicates (e.g., SC2's start-lat == SC1's end-lat)
|
|
162
|
+
all_cycles = check_cycle_duplicates(all_cycles)
|
|
163
|
+
# check if user input progressively later latencies
|
|
164
|
+
all_cycles = check_cycle_order(all_cycles, info)
|
|
165
|
+
# check if tracking broke for any SCs - if so remove them
|
|
166
|
+
all_cycles = check_tracking(data, info, all_cycles, cfg)
|
|
167
|
+
return all_cycles
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# %% imports
|
|
2
|
+
import autogaita.gui.gaita_widgets as gaita_widgets
|
|
3
|
+
import autogaita.gui.gui_utils as gui_utils
|
|
4
|
+
from autogaita.gui.common2D_columninfo_gui import build_column_info_window
|
|
5
|
+
from autogaita.gui.common2D_advanced_config_gui import build_cfg_window
|
|
6
|
+
from autogaita.gui.common2D_run_and_done_gui import build_run_and_done_windows
|
|
7
|
+
from autogaita.gui.first_level_gui_utils import (
|
|
8
|
+
update_config_file,
|
|
9
|
+
extract_cfg_from_json_file,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
import tkinter as tk
|
|
13
|
+
import customtkinter as ctk
|
|
14
|
+
import os
|
|
15
|
+
import platform
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# %% global constants
|
|
19
|
+
from autogaita.gui.gui_constants import (
|
|
20
|
+
SLEAP_FG_COLOR,
|
|
21
|
+
SLEAP_HOVER_COLOR,
|
|
22
|
+
TEXT_FONT_NAME,
|
|
23
|
+
TEXT_FONT_SIZE,
|
|
24
|
+
WINDOWS_TASKBAR_MAXHEIGHT,
|
|
25
|
+
AUTOGAITA_FOLDER_PATH,
|
|
26
|
+
get_widget_cfg_dict, # function!
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# these colors are GUI-specific - add to common widget cfg
|
|
30
|
+
FG_COLOR = SLEAP_FG_COLOR
|
|
31
|
+
HOVER_COLOR = SLEAP_HOVER_COLOR
|
|
32
|
+
WIDGET_CFG = get_widget_cfg_dict()
|
|
33
|
+
WIDGET_CFG["FG_COLOR"] = FG_COLOR
|
|
34
|
+
WIDGET_CFG["HOVER_COLOR"] = HOVER_COLOR
|
|
35
|
+
|
|
36
|
+
CONFIG_FILE_NAME = "sleap_gui_config.json"
|
|
37
|
+
FLOAT_VARS = ["pixel_to_mm_ratio"]
|
|
38
|
+
INT_VARS = [
|
|
39
|
+
"sampling_rate",
|
|
40
|
+
"x_sc_broken_threshold",
|
|
41
|
+
"y_sc_broken_threshold",
|
|
42
|
+
"bin_num",
|
|
43
|
+
# "mouse_num",
|
|
44
|
+
# "run_num",
|
|
45
|
+
"plot_joint_number",
|
|
46
|
+
]
|
|
47
|
+
LIST_VARS = [
|
|
48
|
+
"hind_joints",
|
|
49
|
+
"fore_joints",
|
|
50
|
+
"x_standardisation_joint",
|
|
51
|
+
"y_standardisation_joint",
|
|
52
|
+
"beam_hind_jointadd",
|
|
53
|
+
"beam_fore_jointadd",
|
|
54
|
+
"beam_col_left",
|
|
55
|
+
"beam_col_right",
|
|
56
|
+
]
|
|
57
|
+
DICT_VARS = ["angles"]
|
|
58
|
+
# TK_BOOL/STR_VARS are only used for initialising widgets based on cfg file
|
|
59
|
+
# (note that numbers are initialised as strings)
|
|
60
|
+
TK_BOOL_VARS = [
|
|
61
|
+
"subtract_beam",
|
|
62
|
+
"dont_show_plots",
|
|
63
|
+
"convert_to_mm",
|
|
64
|
+
"x_acceleration",
|
|
65
|
+
"angular_acceleration",
|
|
66
|
+
"save_to_xls",
|
|
67
|
+
"plot_SE",
|
|
68
|
+
"standardise_y_at_SC_level",
|
|
69
|
+
"standardise_y_to_a_joint",
|
|
70
|
+
"standardise_x_coordinates",
|
|
71
|
+
# "invert_y_axis",
|
|
72
|
+
"flip_gait_direction",
|
|
73
|
+
"analyse_average_x",
|
|
74
|
+
"legend_outside",
|
|
75
|
+
]
|
|
76
|
+
TK_STR_VARS = [
|
|
77
|
+
"name",
|
|
78
|
+
"root_dir",
|
|
79
|
+
"results_dir",
|
|
80
|
+
"sctable_filename",
|
|
81
|
+
"data_string",
|
|
82
|
+
"beam_string",
|
|
83
|
+
"sampling_rate",
|
|
84
|
+
"pixel_to_mm_ratio",
|
|
85
|
+
"x_sc_broken_threshold",
|
|
86
|
+
"y_sc_broken_threshold",
|
|
87
|
+
"bin_num",
|
|
88
|
+
"plot_joint_number",
|
|
89
|
+
"color_palette",
|
|
90
|
+
]
|
|
91
|
+
GUI_SPECIFIC_VARS = {
|
|
92
|
+
"CONFIG_FILE_NAME": CONFIG_FILE_NAME,
|
|
93
|
+
"FLOAT_VARS": FLOAT_VARS,
|
|
94
|
+
"INT_VARS": INT_VARS,
|
|
95
|
+
"LIST_VARS": LIST_VARS,
|
|
96
|
+
"DICT_VARS": DICT_VARS,
|
|
97
|
+
"TK_BOOL_VARS": TK_BOOL_VARS,
|
|
98
|
+
"TK_STR_VARS": TK_STR_VARS,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# %% An important Note
|
|
102
|
+
# I am using a global variable called cfg because I need its info to be shared
|
|
103
|
+
# between root and columnconfiguration window. This is not the object-oriented way
|
|
104
|
+
# that one would do this typically. However, it works as expected since:
|
|
105
|
+
# 1) cfg's values are only ever modified @ initialisation & by widgets
|
|
106
|
+
# 2) cfg's values are shared for analysis of a single and multiple video(s)
|
|
107
|
+
# 3) cfg's values are passed to all functions that need them
|
|
108
|
+
# 4) and (IMPORTANTLY!) just before running either (i.e. single/multi) analysis, cfg's
|
|
109
|
+
# and result's values are unpacked and assigned to "this_" dicts that are passed
|
|
110
|
+
# to the runanalysis local function. Hence, from that point onwards, only
|
|
111
|
+
# "this_" dicts are used, never cfg or result dicts themselves.
|
|
112
|
+
# ==> see donewindow for point 4)
|
|
113
|
+
|
|
114
|
+
# %%............................ MAIN PROGRAM ................................
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def run_sleap_gui():
|
|
118
|
+
# ..........................................................................
|
|
119
|
+
# ...................... root window initialisation .......................
|
|
120
|
+
# ..........................................................................
|
|
121
|
+
# Check for config file
|
|
122
|
+
config_file_path = os.path.join(AUTOGAITA_FOLDER_PATH, CONFIG_FILE_NAME)
|
|
123
|
+
if not os.path.isfile(config_file_path):
|
|
124
|
+
config_file_error_msg = (
|
|
125
|
+
"sleap_gui_config.json file not found in autogaita folder.\n"
|
|
126
|
+
"Confirm that the file exists and is named correctly.\n"
|
|
127
|
+
"If not, download it again from the GitHub repository."
|
|
128
|
+
)
|
|
129
|
+
tk.messagebox.showerror(
|
|
130
|
+
title="Config File Error", message=config_file_error_msg
|
|
131
|
+
)
|
|
132
|
+
exit()
|
|
133
|
+
|
|
134
|
+
# CustomTkinter vars
|
|
135
|
+
ctk.set_appearance_mode("dark") # Modes: system (default), light, dark
|
|
136
|
+
ctk.set_default_color_theme("green") # Themes: blue , dark-blue, green
|
|
137
|
+
# root
|
|
138
|
+
root = ctk.CTk()
|
|
139
|
+
# make window pretty
|
|
140
|
+
screen_width = root.winfo_screenwidth() # width of the screen
|
|
141
|
+
screen_height = root.winfo_screenheight() # height of the screen
|
|
142
|
+
if platform.system() == "Windows": # adjust for taskbar in windows only
|
|
143
|
+
screen_height -= WINDOWS_TASKBAR_MAXHEIGHT
|
|
144
|
+
# create root dimensions (based on fullscreen) to pass to other window-functions l8r
|
|
145
|
+
w, h, x, y = screen_width, screen_height, 0, 0
|
|
146
|
+
root_dimensions = (w, h, x, y)
|
|
147
|
+
# set the dimensions of the screen and where it is placed
|
|
148
|
+
# => have it half-wide starting at 1/4 of screen's width (dont change w & x!)
|
|
149
|
+
root.geometry(f"{int(screen_width / 2)}x{screen_height}+{int(screen_width / 4)}+0")
|
|
150
|
+
root.title("SLEAP GaitA")
|
|
151
|
+
gui_utils.fix_window_after_its_creation(root)
|
|
152
|
+
gui_utils.configure_the_icon(root)
|
|
153
|
+
|
|
154
|
+
# ..................... load cfg dict from config .....................
|
|
155
|
+
# use the values in the config json file for the results dictionary
|
|
156
|
+
global cfg
|
|
157
|
+
cfg = extract_cfg_from_json_file(
|
|
158
|
+
root,
|
|
159
|
+
AUTOGAITA_FOLDER_PATH,
|
|
160
|
+
CONFIG_FILE_NAME,
|
|
161
|
+
LIST_VARS,
|
|
162
|
+
DICT_VARS,
|
|
163
|
+
TK_STR_VARS,
|
|
164
|
+
TK_BOOL_VARS,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# ............................... header ..........................................
|
|
168
|
+
# main configuration header
|
|
169
|
+
main_cfg_header_label = gaita_widgets.header_label(
|
|
170
|
+
root,
|
|
171
|
+
"Main Configuration",
|
|
172
|
+
WIDGET_CFG,
|
|
173
|
+
)
|
|
174
|
+
main_cfg_header_label.grid(row=0, column=0, columnspan=3, sticky="nsew")
|
|
175
|
+
|
|
176
|
+
# ............................ main cfg section ..................................
|
|
177
|
+
# sampling rate
|
|
178
|
+
samprate_label, samprate_entry = gaita_widgets.label_and_entry_pair(
|
|
179
|
+
root,
|
|
180
|
+
"Sampling rate of videos in Hertz (frames/second):",
|
|
181
|
+
cfg["sampling_rate"],
|
|
182
|
+
WIDGET_CFG,
|
|
183
|
+
)
|
|
184
|
+
samprate_label.grid(row=1, column=0, columnspan=2, sticky="w")
|
|
185
|
+
samprate_entry.grid(row=1, column=2, sticky="w")
|
|
186
|
+
|
|
187
|
+
# convert pixel to mm - checkbox
|
|
188
|
+
convert_checkbox = gaita_widgets.checkbox(
|
|
189
|
+
root,
|
|
190
|
+
"Convert pixels to millimetres:",
|
|
191
|
+
cfg["convert_to_mm"],
|
|
192
|
+
WIDGET_CFG,
|
|
193
|
+
)
|
|
194
|
+
convert_checkbox.configure(
|
|
195
|
+
command=lambda: gui_utils.change_widget_state_based_on_checkbox(
|
|
196
|
+
cfg, "convert_to_mm", ratio_entry
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
convert_checkbox.grid(row=2, column=0, columnspan=2, sticky="w")
|
|
200
|
+
|
|
201
|
+
# ratio label
|
|
202
|
+
ratio_entry = ctk.CTkEntry(
|
|
203
|
+
root,
|
|
204
|
+
textvariable=cfg["pixel_to_mm_ratio"],
|
|
205
|
+
font=(TEXT_FONT_NAME, TEXT_FONT_SIZE),
|
|
206
|
+
)
|
|
207
|
+
ratio_entry.grid(row=2, column=1, sticky="e")
|
|
208
|
+
ratio_right_string = "pixels = 1 mm"
|
|
209
|
+
ratio_right_label = ctk.CTkLabel(
|
|
210
|
+
root, text=ratio_right_string, font=(TEXT_FONT_NAME, TEXT_FONT_SIZE)
|
|
211
|
+
)
|
|
212
|
+
ratio_right_label.grid(row=2, column=2, sticky="w")
|
|
213
|
+
# to initialise the widget correctly, run this function once
|
|
214
|
+
gui_utils.change_widget_state_based_on_checkbox(cfg, "convert_to_mm", ratio_entry)
|
|
215
|
+
|
|
216
|
+
# NU - I'll implement beam subtraction and gait direction flipping after having
|
|
217
|
+
# data for testing
|
|
218
|
+
|
|
219
|
+
# subtract beam
|
|
220
|
+
subtract_beam_checkbox = gaita_widgets.checkbox(
|
|
221
|
+
root,
|
|
222
|
+
"(not supported yet) - Standardise y-coordinates to baseline height",
|
|
223
|
+
# "Standardise y-coordinates to baseline height (requires to be tracked),"
|
|
224
|
+
cfg["subtract_beam"],
|
|
225
|
+
WIDGET_CFG,
|
|
226
|
+
)
|
|
227
|
+
subtract_beam_checkbox.grid(row=3, column=0, columnspan=3, sticky="w")
|
|
228
|
+
subtract_beam_checkbox.configure(state="disabled")
|
|
229
|
+
|
|
230
|
+
# flip gait direction
|
|
231
|
+
flip_gait_direction_box = gaita_widgets.checkbox(
|
|
232
|
+
root,
|
|
233
|
+
"(not supported yet) - Adjust x-coordinates to follow direction of movement",
|
|
234
|
+
cfg["flip_gait_direction"],
|
|
235
|
+
WIDGET_CFG,
|
|
236
|
+
)
|
|
237
|
+
flip_gait_direction_box.grid(row=4, column=0, columnspan=3, sticky="w")
|
|
238
|
+
flip_gait_direction_box.configure(state="disabled")
|
|
239
|
+
|
|
240
|
+
# plot plots to python
|
|
241
|
+
showplots_checkbox = gaita_widgets.checkbox(
|
|
242
|
+
root,
|
|
243
|
+
"Don't show plots in Figure GUI (save only)",
|
|
244
|
+
cfg["dont_show_plots"],
|
|
245
|
+
WIDGET_CFG,
|
|
246
|
+
)
|
|
247
|
+
showplots_checkbox.grid(row=5, column=0, columnspan=2, sticky="w")
|
|
248
|
+
|
|
249
|
+
# bin number of SC normalisation
|
|
250
|
+
bin_num_label, bin_num_entry = gaita_widgets.label_and_entry_pair(
|
|
251
|
+
root,
|
|
252
|
+
"Number of bins used to normalise the step cycle:",
|
|
253
|
+
cfg["bin_num"],
|
|
254
|
+
WIDGET_CFG,
|
|
255
|
+
)
|
|
256
|
+
bin_num_label.grid(row=6, column=0, columnspan=2, sticky="w")
|
|
257
|
+
bin_num_entry.grid(row=6, column=2, sticky="w")
|
|
258
|
+
|
|
259
|
+
# empty label 1 (for spacing)
|
|
260
|
+
empty_label_one = ctk.CTkLabel(root, text="")
|
|
261
|
+
empty_label_one.grid(row=7, column=0)
|
|
262
|
+
|
|
263
|
+
# .......................... advanced cfg section ................................
|
|
264
|
+
# advanced header string
|
|
265
|
+
advanced_cfg_header_label = gaita_widgets.header_label(
|
|
266
|
+
root,
|
|
267
|
+
"Advanced Configuration",
|
|
268
|
+
WIDGET_CFG,
|
|
269
|
+
)
|
|
270
|
+
advanced_cfg_header_label.grid(row=8, column=0, columnspan=3, sticky="nsew")
|
|
271
|
+
|
|
272
|
+
# column name information window
|
|
273
|
+
column_info_button = gaita_widgets.header_button(
|
|
274
|
+
root, "Customise Joints & Angles", WIDGET_CFG
|
|
275
|
+
)
|
|
276
|
+
column_info_button.configure(
|
|
277
|
+
command=lambda: build_column_info_window(root, cfg, WIDGET_CFG, root_dimensions)
|
|
278
|
+
)
|
|
279
|
+
column_info_button.grid(row=9, column=0, columnspan=3)
|
|
280
|
+
|
|
281
|
+
# advanced cfg
|
|
282
|
+
cfg_window_button = gaita_widgets.header_button(
|
|
283
|
+
root, "Advanced Configuration", WIDGET_CFG
|
|
284
|
+
)
|
|
285
|
+
cfg_window_button.configure(
|
|
286
|
+
command=lambda: build_cfg_window(root, cfg, WIDGET_CFG, root_dimensions)
|
|
287
|
+
)
|
|
288
|
+
cfg_window_button.grid(row=10, column=0, columnspan=3)
|
|
289
|
+
|
|
290
|
+
# empty label 2 (for spacing)
|
|
291
|
+
empty_label_two = ctk.CTkLabel(root, text="")
|
|
292
|
+
empty_label_two.grid(row=11, column=0)
|
|
293
|
+
|
|
294
|
+
# run analysis label
|
|
295
|
+
runheader_label = gaita_widgets.header_label(root, "Run Analysis", WIDGET_CFG)
|
|
296
|
+
runheader_label.grid(row=12, column=0, columnspan=3, sticky="nsew")
|
|
297
|
+
|
|
298
|
+
# single gaita button
|
|
299
|
+
onevid_button = gaita_widgets.header_button(root, "One Video", WIDGET_CFG)
|
|
300
|
+
onevid_button.configure(
|
|
301
|
+
command=lambda: build_run_and_done_windows(
|
|
302
|
+
"SLEAP", "single", root, cfg, WIDGET_CFG, GUI_SPECIFIC_VARS, root_dimensions
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
onevid_button.grid(row=13, column=1, sticky="ew")
|
|
306
|
+
|
|
307
|
+
# multi gaita button
|
|
308
|
+
multivid_button = gaita_widgets.header_button(root, "Batch Analysis", WIDGET_CFG)
|
|
309
|
+
multivid_button.configure(
|
|
310
|
+
command=lambda: build_run_and_done_windows(
|
|
311
|
+
"SLEAP", "multi", root, cfg, WIDGET_CFG, GUI_SPECIFIC_VARS, root_dimensions
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
multivid_button.grid(row=14, column=1, sticky="ew")
|
|
315
|
+
|
|
316
|
+
# empty label 2 (for spacing)
|
|
317
|
+
empty_label_two = ctk.CTkLabel(root, text="")
|
|
318
|
+
empty_label_two.grid(row=15, column=0)
|
|
319
|
+
|
|
320
|
+
# close & exit button
|
|
321
|
+
exit_button = gaita_widgets.exit_button(root, WIDGET_CFG)
|
|
322
|
+
exit_button.configure(
|
|
323
|
+
command=lambda: (
|
|
324
|
+
# results variable is only defined later in populate_run_window()
|
|
325
|
+
# therefore only cfg settings will be updated
|
|
326
|
+
update_config_file(
|
|
327
|
+
"results dict not defined yet",
|
|
328
|
+
cfg,
|
|
329
|
+
AUTOGAITA_FOLDER_PATH,
|
|
330
|
+
CONFIG_FILE_NAME,
|
|
331
|
+
LIST_VARS,
|
|
332
|
+
DICT_VARS,
|
|
333
|
+
TK_STR_VARS,
|
|
334
|
+
TK_BOOL_VARS,
|
|
335
|
+
),
|
|
336
|
+
root.withdraw(),
|
|
337
|
+
root.after(5000, root.destroy),
|
|
338
|
+
),
|
|
339
|
+
)
|
|
340
|
+
exit_button.grid(row=16, column=0, columnspan=3)
|
|
341
|
+
|
|
342
|
+
# # ......................... widget configuration ...............................
|
|
343
|
+
|
|
344
|
+
# first maximise everything according to sticky
|
|
345
|
+
# => Silent_Creme is some undocumented option that makes stuff uniform
|
|
346
|
+
# see: https://stackoverflow.com/questions/45847313/tkinter-grid-rowconfigure-weight-doesnt-work
|
|
347
|
+
root.columnconfigure(list(range(3)), weight=1, uniform="Silent_Creme")
|
|
348
|
+
root.rowconfigure(list(range(17)), weight=1, uniform="Silent_Creme")
|
|
349
|
+
|
|
350
|
+
# then un-maximise main config rows to have them grouped together
|
|
351
|
+
root.rowconfigure(list(range(1, 7)), weight=0)
|
|
352
|
+
|
|
353
|
+
# main loop
|
|
354
|
+
root.mainloop()
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
# %% what happens if we hit run
|
|
358
|
+
if __name__ == "__main__":
|
|
359
|
+
run_sleap_gui()
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: autogaita
|
|
3
|
-
Version: 1.5.2
|
|
4
|
-
Summary: Automatic Gait Analysis in Python.
|
|
5
|
-
Home-page: https://github.com/mahan-hosseini/AutoGaitA/
|
|
6
|
-
Author: Mahan Hosseini
|
|
7
|
-
License: GPLv3
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Description-Content-Type: text/plain
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: customtkinter>=5.2
|
|
12
|
-
Requires-Dist: pandas<3.0,>=2.0
|
|
13
|
-
Requires-Dist: numpy>=1.24
|
|
14
|
-
Requires-Dist: seaborn>=0.13
|
|
15
|
-
Requires-Dist: matplotlib>=3.7
|
|
16
|
-
Requires-Dist: scikit-learn>=1.2
|
|
17
|
-
Requires-Dist: pingouin>=0.5
|
|
18
|
-
Requires-Dist: scipy>=1.11
|
|
19
|
-
Requires-Dist: scikit-bio>=0.7.1
|
|
20
|
-
Requires-Dist: statsmodels>=0.14
|
|
21
|
-
Requires-Dist: ffmpeg-python>=0.2
|
|
22
|
-
Requires-Dist: openpyxl>=3.1
|
|
23
|
-
Requires-Dist: pillow>=10.3
|
|
24
|
-
Requires-Dist: h5py>=3.11
|
|
25
|
-
Requires-Dist: pyobjc; sys_platform == "darwin"
|
|
26
|
-
Provides-Extra: dev
|
|
27
|
-
Requires-Dist: pytest; extra == "dev"
|
|
28
|
-
Requires-Dist: hypothesis; extra == "dev"
|
|
29
|
-
Dynamic: author
|
|
30
|
-
Dynamic: description
|
|
31
|
-
Dynamic: description-content-type
|
|
32
|
-
Dynamic: home-page
|
|
33
|
-
Dynamic: license
|
|
34
|
-
Dynamic: license-file
|
|
35
|
-
Dynamic: provides-extra
|
|
36
|
-
Dynamic: requires-dist
|
|
37
|
-
Dynamic: requires-python
|
|
38
|
-
Dynamic: summary
|
|
39
|
-
|
|
40
|
-
A toolbox to streamline and standardise the analysis of kinematics across species after ML-based body posture tracking. Despite being optimised for gait analyses, AutoGaitA has the potential to be used for any kind of kinematic analysis.
|
tests/__init__.py
DELETED
|
File without changes
|