c3d-parser 1.5.4__tar.gz → 1.6.0__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.
- {c3d_parser-1.5.4/src/c3d_parser.egg-info → c3d_parser-1.6.0}/PKG-INFO +2 -2
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/requirements.txt +1 -1
- c3d_parser-1.6.0/src/c3d_parser/__init__.py +2 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/c3d_parser.py +42 -21
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/main_window.py +25 -51
- {c3d_parser-1.5.4 → c3d_parser-1.6.0/src/c3d_parser.egg-info}/PKG-INFO +2 -2
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/requires.txt +1 -1
- c3d_parser-1.5.4/src/c3d_parser/__init__.py +0 -2
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/LICENSE +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/README.md +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/pyproject.toml +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/setup.cfg +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/application.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/c3d_patch.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/osim.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/osim_resources/external_loads_template.xml +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/osim_resources/ik_task_set.xml +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/utils.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/general.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/logging.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/AUC.json +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/FMC.json +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/MH.json +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/QCMAS.json +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/RBWH.json +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/RCH.json +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/Sydney.json +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/splash_rc.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/splashscreen.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/about_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/marker_set_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/options_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/resources_rc.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_delete_marker_set_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_main_window.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_marker_set_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_marker_set_import_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_options_dialog.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/utils.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/widgets.py +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/SOURCES.txt +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/dependency_links.txt +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/entry_points.txt +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/top_level.txt +0 -0
- {c3d_parser-1.5.4 → c3d_parser-1.6.0}/tests/test_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c3d-parser
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: C3D parser for gait data harmonisation.
|
|
5
5
|
Author-email: Timothy Salemink <tim.nicolas@outlook.com>, Sally Jack <sallyjaack@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,7 +12,7 @@ Requires-Dist: numpy==1.26.4
|
|
|
12
12
|
Requires-Dist: pandas
|
|
13
13
|
Requires-Dist: c3d==0.5.2
|
|
14
14
|
Requires-Dist: trc-data-reader>=0.4.1
|
|
15
|
-
Requires-Dist: opensim-model-creator==0.
|
|
15
|
+
Requires-Dist: opensim-model-creator==0.10.0
|
|
16
16
|
Requires-Dist: PySide6
|
|
17
17
|
Requires-Dist: matplotlib
|
|
18
18
|
Requires-Dist: mplcursors
|
|
@@ -16,7 +16,6 @@ from decimal import Decimal, ROUND_CEILING, ROUND_FLOOR
|
|
|
16
16
|
|
|
17
17
|
from trc import TRCData
|
|
18
18
|
from opensim_model_creator.Create_Model import create_model
|
|
19
|
-
from ll_visualiser.utils import get_fit_metrics, load_landmarks, define_measurements
|
|
20
19
|
|
|
21
20
|
from c3d_parser.core.c3d_patch import c3d
|
|
22
21
|
from c3d_parser.core.utils import clear_directory
|
|
@@ -38,11 +37,15 @@ torso_markers = ["C7", "T2", "T10", "MAN"]
|
|
|
38
37
|
required_markers = [{"LASI", "RASI"}, {"LKNE", "RKNE"}, {"LANK", "RANK"}, {"LMED", "RMED"}, {"LHEE", "RHEE"},
|
|
39
38
|
{"LTOE", "RTOE"}, ({"LPSI", "RPSI"}, {"SACR"}), ({"LKNEM", "RKNEM"}, {"LKAX", "RKAX"})]
|
|
40
39
|
|
|
40
|
+
required_measurements = ['Sex', 'Age', 'Height', 'Weight', 'ASIS Width', 'Left Knee Width', 'Right Knee Width',
|
|
41
|
+
'Left Ankle Width', 'Right Ankle Width']
|
|
42
|
+
|
|
41
43
|
|
|
42
44
|
def parse_session(static_trial, dynamic_trials, input_directory, output_directory, lab, marker_diameter, static_data,
|
|
43
45
|
left_foot_flat, right_foot_flat, toe_marker_proximal, optimise_knee_axis, filter_trc, filter_grf,
|
|
44
46
|
ik_task_set, running_gait, progress_tracker):
|
|
45
47
|
|
|
48
|
+
validate_static_data(static_data)
|
|
46
49
|
clear_directory(output_directory)
|
|
47
50
|
|
|
48
51
|
logger.info(f"Processing session {os.path.normpath(input_directory)}.")
|
|
@@ -127,16 +130,26 @@ def approximate_anthropometrics(c3d_file, lab, marker_diameter):
|
|
|
127
130
|
|
|
128
131
|
def calculate_anthropometrics(frame_data, marker_diameter):
|
|
129
132
|
|
|
130
|
-
def distance(frame, landmark_1, landmark_2):
|
|
131
|
-
|
|
133
|
+
def distance(frame, landmark_1, landmark_2, marker_diameter=0):
|
|
134
|
+
missing = [landmark for landmark in (landmark_1, landmark_2) if landmark not in frame]
|
|
135
|
+
if missing:
|
|
136
|
+
logger.warn(f"Unable to approximate distance between {landmark_1} and {landmark_2}. "
|
|
137
|
+
f"Missing marker(s): {', '.join(missing)}.")
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
point_1 = np.array(frame[landmark_1])
|
|
141
|
+
point_2 = np.array(frame[landmark_2])
|
|
142
|
+
length = round(np.linalg.norm(point_1 - point_2), 1)
|
|
143
|
+
|
|
144
|
+
return length - marker_diameter
|
|
132
145
|
|
|
133
146
|
mid_frame = frame_data.iloc[len(frame_data) // 2].copy()
|
|
134
147
|
|
|
135
148
|
anthropometrics = {
|
|
136
|
-
'left_knee_width': distance(mid_frame, 'LKNE', 'LKNEM'
|
|
137
|
-
'right_knee_width': distance(mid_frame, 'RKNE', 'RKNEM'
|
|
138
|
-
'left_ankle_width': distance(mid_frame, 'LANK', 'LMED'
|
|
139
|
-
'right_ankle_width': distance(mid_frame, 'RANK', 'RMED'
|
|
149
|
+
'left_knee_width': distance(mid_frame, 'LKNE', 'LKNEM', marker_diameter),
|
|
150
|
+
'right_knee_width': distance(mid_frame, 'RKNE', 'RKNEM', marker_diameter),
|
|
151
|
+
'left_ankle_width': distance(mid_frame, 'LANK', 'LMED', marker_diameter),
|
|
152
|
+
'right_ankle_width': distance(mid_frame, 'RANK', 'RMED', marker_diameter),
|
|
140
153
|
'left_leg_length': distance(mid_frame, 'LASI', 'LMED'),
|
|
141
154
|
'right_leg_length': distance(mid_frame, 'RASI', 'RMED'),
|
|
142
155
|
'inter_asis_distance': distance(mid_frame, 'LASI', 'RASI'),
|
|
@@ -290,6 +303,8 @@ def write_c3d_parser_history(input_directory, output_directory, static_trial, de
|
|
|
290
303
|
|
|
291
304
|
|
|
292
305
|
def define_metrics_and_measurements(output_directory):
|
|
306
|
+
from ll_visualiser.utils import get_fit_metrics, load_landmarks, define_measurements
|
|
307
|
+
|
|
293
308
|
model_directory = os.path.join(output_directory, "Models", "Meshes")
|
|
294
309
|
left_predicted_landmark_file = os.path.join(model_directory, 'predicted_lms_left.txt')
|
|
295
310
|
right_predicted_landmark_file = os.path.join(model_directory, 'predicted_lms_right.txt')
|
|
@@ -525,10 +540,8 @@ def trim_frames(frame_data):
|
|
|
525
540
|
if not drop_frames.empty:
|
|
526
541
|
frame_data.drop(drop_frames, inplace=True)
|
|
527
542
|
|
|
528
|
-
# TODO: If we convert this to a dictionary we can keep the markers that are missing and check if they are required.
|
|
529
543
|
remaining_frames = [frame for frame in incomplete_frames.keys() if trim_start <= frame <= trim_end]
|
|
530
544
|
if remaining_frames:
|
|
531
|
-
# TODO: We should raise a ParserError (skip this trial) if the missing markers are required...?
|
|
532
545
|
logger.warn(f"Frames {remaining_frames} are incomplete.")
|
|
533
546
|
|
|
534
547
|
return trim_start, trim_end
|
|
@@ -1092,8 +1105,6 @@ def scale_grf_data(analog_data):
|
|
|
1092
1105
|
analog_data[columns] = analog_data[columns] / 1000
|
|
1093
1106
|
|
|
1094
1107
|
|
|
1095
|
-
# TODO: This incorrectly classifies Adelaide's static trials as dynamic.
|
|
1096
|
-
# Use `calculate_distance_covered` instead?
|
|
1097
1108
|
def is_dynamic(file_path):
|
|
1098
1109
|
with open(file_path, 'rb') as handle:
|
|
1099
1110
|
try:
|
|
@@ -1269,11 +1280,6 @@ def normalise_kinetics(kinetic_data, events):
|
|
|
1269
1280
|
normalised_data[foot][file_name] = {}
|
|
1270
1281
|
data_segment = data.iloc[start:frame, 1:]
|
|
1271
1282
|
|
|
1272
|
-
# TODO: If the model +tve and -tve are wrong, then we can't just flip these.
|
|
1273
|
-
# Because the non-GRF parts of the moments would also be flipped.
|
|
1274
|
-
# Just visually check the results against some clinical kinetics.
|
|
1275
|
-
# ...
|
|
1276
|
-
# Looks pretty good so far.
|
|
1277
1283
|
# Perform sign transformations.
|
|
1278
1284
|
data_segment[f"hip_flexion_{side}_moment"] = -data_segment[f"hip_flexion_{side}_moment"]
|
|
1279
1285
|
data_segment[f"hip_adduction_{side}_moment"] = -data_segment[f"hip_adduction_{side}_moment"]
|
|
@@ -1366,9 +1372,6 @@ def write_normalised_data(data, column_names, selected_trials, excluded_cycles,
|
|
|
1366
1372
|
file.write('\n')
|
|
1367
1373
|
|
|
1368
1374
|
|
|
1369
|
-
# TODO: Rework this function to be less sequential.
|
|
1370
|
-
# It should be able to calculate metrics for walking and running gait.
|
|
1371
|
-
# Put this at the start of the sequential bit.
|
|
1372
1375
|
def calculate_spatiotemporal_data(frame_data, events, static_data):
|
|
1373
1376
|
stride_lengths = {"Left": {}, "Right": {}}
|
|
1374
1377
|
step_lengths = {"Left": {}, "Right": {}}
|
|
@@ -1391,8 +1394,6 @@ def calculate_spatiotemporal_data(frame_data, events, static_data):
|
|
|
1391
1394
|
for event_time, (event_type, event_plate) in stride_events.items():
|
|
1392
1395
|
time_ordered_events[event_time][foot] = [event_type, stride_number]
|
|
1393
1396
|
|
|
1394
|
-
# TODO: We should be able to use this to calculate ALL measurements non-sequentially.
|
|
1395
|
-
# TODO: Double check that we don't have situations where the events are not ordered in increasing stride number.
|
|
1396
1397
|
# Add terminal foot-strike to events for each cycle.
|
|
1397
1398
|
full_cycle_events = copy.deepcopy(events)
|
|
1398
1399
|
for foot, foot_events in full_cycle_events.items():
|
|
@@ -1700,3 +1701,23 @@ def get_subject_info(static_data):
|
|
|
1700
1701
|
'right_malleolar_width'
|
|
1701
1702
|
])
|
|
1702
1703
|
return info
|
|
1704
|
+
|
|
1705
|
+
|
|
1706
|
+
def height_to_mm(height):
|
|
1707
|
+
if 0 < height < 300:
|
|
1708
|
+
logger.info(f"Subject height ({height}) assumed to be in cm. Converting to mm.")
|
|
1709
|
+
return height * 10
|
|
1710
|
+
|
|
1711
|
+
return height
|
|
1712
|
+
|
|
1713
|
+
|
|
1714
|
+
def validate_static_data(static_data):
|
|
1715
|
+
missing = [key for key in required_measurements if not static_data.get(key)]
|
|
1716
|
+
if missing:
|
|
1717
|
+
raise ParserError("Subject measurements missing:\n- " + "\n- ".join(missing))
|
|
1718
|
+
|
|
1719
|
+
static_data['Height'] = height_to_mm(static_data['Height'])
|
|
1720
|
+
|
|
1721
|
+
# Cap age at 18.
|
|
1722
|
+
# Current shape model is paediatric only, ages over 18 can give strange results.
|
|
1723
|
+
static_data['Age'] = min(static_data['Age'], 18)
|
|
@@ -17,7 +17,7 @@ from ll_visualiser.visualiser import visualise_model
|
|
|
17
17
|
|
|
18
18
|
from c3d_parser.core.c3d_parser import (parse_session, extract_static_data, extract_marker_names, is_dynamic,
|
|
19
19
|
CancelException, write_normalised_grfs, write_normalised_kinematics, write_normalised_kinetics,
|
|
20
|
-
write_spatiotemporal_data, approximate_anthropometrics)
|
|
20
|
+
write_spatiotemporal_data, approximate_anthropometrics, height_to_mm)
|
|
21
21
|
from c3d_parser.settings.general import (APPLICATION_NAME, VERSION, DEFAULT_STYLE_SHEET, INVALID_STYLE_SHEET,
|
|
22
22
|
get_marker_maps_dir)
|
|
23
23
|
from c3d_parser.view.ui.ui_main_window import Ui_MainWindow
|
|
@@ -57,7 +57,6 @@ class ProgressTracker(QObject):
|
|
|
57
57
|
progress = Signal(str, str)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
# TODO: CGM2 event detector on GitHub...?
|
|
61
60
|
class MainWindow(QMainWindow):
|
|
62
61
|
|
|
63
62
|
def __init__(self):
|
|
@@ -124,7 +123,6 @@ class MainWindow(QMainWindow):
|
|
|
124
123
|
self._kinetic_curves.signals.cycles_included.connect(self._update_cycle_count)
|
|
125
124
|
|
|
126
125
|
def _setup_progress_bar(self):
|
|
127
|
-
self._progress_text = ""
|
|
128
126
|
self._progress_value = 0
|
|
129
127
|
self._ui.progressBar.setVisible(False)
|
|
130
128
|
|
|
@@ -160,7 +158,6 @@ class MainWindow(QMainWindow):
|
|
|
160
158
|
self._plotter.clear_actors()
|
|
161
159
|
|
|
162
160
|
def _clear_progress_bar(self):
|
|
163
|
-
self._progress_text = ""
|
|
164
161
|
self._ui.labelProgress.setText("")
|
|
165
162
|
self._progress_value = 0
|
|
166
163
|
self._ui.progressBar.setValue(0)
|
|
@@ -259,12 +256,6 @@ class MainWindow(QMainWindow):
|
|
|
259
256
|
table.deleteLater()
|
|
260
257
|
self._spatiotemporal_tables = []
|
|
261
258
|
|
|
262
|
-
# TODO: The legend is not correctly displayed if the ST tab is open while processing...
|
|
263
|
-
# Repositioning and updating the legend do nothing.
|
|
264
|
-
|
|
265
|
-
# TODO: The border is still having issues.
|
|
266
|
-
# Seems to be related to the area being scrollable.
|
|
267
|
-
# Only happens on some PCs/monitors...
|
|
268
259
|
def _visualise_spatiotemporal_data(self):
|
|
269
260
|
|
|
270
261
|
# Temporarily disable spatio-temporal visualisation for running gait.
|
|
@@ -460,33 +451,32 @@ class MainWindow(QMainWindow):
|
|
|
460
451
|
self._set_subject_info(static_trial)
|
|
461
452
|
self._static_trial = static_trial
|
|
462
453
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
454
|
+
# Fill in any anthropometric gaps.
|
|
455
|
+
if self._approximate_anthropometrics:
|
|
456
|
+
input_directory = self._ui.lineEditInputDirectory.text()
|
|
457
|
+
lab = self._ui.comboBoxLab.currentText()
|
|
458
|
+
marker_diameter = self._ui.doubleSpinBoxMarkerDiameter.value()
|
|
459
|
+
c3d_file = os.path.normpath(os.path.join(input_directory, static_trial))
|
|
460
|
+
anthropometrics = approximate_anthropometrics(c3d_file, lab, marker_diameter)
|
|
461
|
+
|
|
462
|
+
if not self._ui.doubleSpinBoxASISWidth.value():
|
|
463
|
+
self._ui.doubleSpinBoxASISWidth.setValue(anthropometrics['inter_asis_distance'])
|
|
464
|
+
if not self._ui.doubleSpinBoxLeftLegLength.value():
|
|
465
|
+
self._ui.doubleSpinBoxLeftLegLength.setValue(anthropometrics['left_leg_length'])
|
|
466
|
+
if not self._ui.doubleSpinBoxLeftKneeWidth.value():
|
|
467
|
+
self._ui.doubleSpinBoxLeftKneeWidth.setValue(anthropometrics['left_knee_width'])
|
|
468
|
+
if not self._ui.doubleSpinBoxLeftAnkleWidth.value():
|
|
469
|
+
self._ui.doubleSpinBoxLeftAnkleWidth.setValue(anthropometrics['left_ankle_width'])
|
|
470
|
+
if not self._ui.doubleSpinBoxRightLegLength.value():
|
|
471
|
+
self._ui.doubleSpinBoxRightLegLength.setValue(anthropometrics['right_leg_length'])
|
|
472
|
+
if not self._ui.doubleSpinBoxRightKneeWidth.value():
|
|
473
|
+
self._ui.doubleSpinBoxRightKneeWidth.setValue(anthropometrics['right_knee_width'])
|
|
474
|
+
if not self._ui.doubleSpinBoxRightAnkleWidth.value():
|
|
475
|
+
self._ui.doubleSpinBoxRightAnkleWidth.setValue(anthropometrics['right_ankle_width'])
|
|
485
476
|
|
|
486
477
|
# Check height units.
|
|
487
478
|
height = self._ui.doubleSpinBoxHeight.value()
|
|
488
|
-
|
|
489
|
-
self._ui.doubleSpinBoxHeight.setValue(height * 10)
|
|
479
|
+
self._ui.doubleSpinBoxHeight.setValue(height_to_mm(height))
|
|
490
480
|
|
|
491
481
|
|
|
492
482
|
def _clear_subject_info(self):
|
|
@@ -586,10 +576,6 @@ class MainWindow(QMainWindow):
|
|
|
586
576
|
'Right Leg Length': self._ui.doubleSpinBoxRightLegLength.value(),
|
|
587
577
|
}
|
|
588
578
|
|
|
589
|
-
# Cap age at 18.
|
|
590
|
-
# Current shape model is paediatric only, ages over 18 can give strange results.
|
|
591
|
-
static_data['Age'] = min(static_data['Age'], 18)
|
|
592
|
-
|
|
593
579
|
missing = [key for key, value in static_data.items() if not value]
|
|
594
580
|
if 'Left Leg Length' in missing or 'Right Leg Length' in missing:
|
|
595
581
|
reply = QMessageBox.warning(self, "Warning", "Leg length measurements missing. You can continue "
|
|
@@ -604,10 +590,6 @@ class MainWindow(QMainWindow):
|
|
|
604
590
|
QMessageBox.warning(self, "Warning", "Subject measurements missing:\n- " + "\n- ".join(missing))
|
|
605
591
|
return
|
|
606
592
|
|
|
607
|
-
if static_data['Height'] < 300:
|
|
608
|
-
QMessageBox.warning(self, "Warning", "Please ensure subject height is in mm.")
|
|
609
|
-
return
|
|
610
|
-
|
|
611
593
|
optimise_knee_axis = self._optimise_knee_axis
|
|
612
594
|
if not dynamic_trials:
|
|
613
595
|
reply = QMessageBox.information(self, "No Dynamic Trials",
|
|
@@ -685,7 +667,6 @@ class MainWindow(QMainWindow):
|
|
|
685
667
|
|
|
686
668
|
def _update_progress(self, message, color):
|
|
687
669
|
self._ui.labelProgress.setText(message)
|
|
688
|
-
self._progress_text = message
|
|
689
670
|
self._ui.labelProgress.setStyleSheet(f"color: {color};")
|
|
690
671
|
|
|
691
672
|
self._progress_value = min(self._progress_value + 20, 100)
|
|
@@ -910,8 +891,6 @@ class MainWindow(QMainWindow):
|
|
|
910
891
|
if (file_name, f"{foot}_{cycle_number}") not in excluded_cycles:
|
|
911
892
|
counts[foot] += 1
|
|
912
893
|
|
|
913
|
-
# TODO: This is being displayed before processing has finished.
|
|
914
|
-
# Which should not be happening.
|
|
915
894
|
self._ui.labelCount.setText(f"Cycles: L {counts['Left']} / R {counts['Right']}")
|
|
916
895
|
|
|
917
896
|
tab = self._ui.tabWidget.currentWidget()
|
|
@@ -939,7 +918,6 @@ class MainWindow(QMainWindow):
|
|
|
939
918
|
self._update_s_t_colours()
|
|
940
919
|
self._update_legend()
|
|
941
920
|
|
|
942
|
-
# TODO: For some reason the legend doesn't get created at all if we are visualising the ST tab while processing...
|
|
943
921
|
def _update_legend(self):
|
|
944
922
|
if self._boxes:
|
|
945
923
|
for side, boxes in self._boxes.items():
|
|
@@ -1010,6 +988,7 @@ class MainWindow(QMainWindow):
|
|
|
1010
988
|
dlg.exec()
|
|
1011
989
|
self._reset_lab_combo_box()
|
|
1012
990
|
|
|
991
|
+
@handle_runtime_error
|
|
1013
992
|
def _handle_error(self, e):
|
|
1014
993
|
raise e
|
|
1015
994
|
|
|
@@ -1031,8 +1010,6 @@ class MainWindow(QMainWindow):
|
|
|
1031
1010
|
settings.beginGroup('MainWindow')
|
|
1032
1011
|
settings.setValue('size', self.size())
|
|
1033
1012
|
settings.setValue('pos', self.pos())
|
|
1034
|
-
# TODO: The taskbar icon fails if the application opens in full-screen...
|
|
1035
|
-
# DON'T open in full screen...?
|
|
1036
1013
|
settings.setValue('is_maximized', self.isMaximized())
|
|
1037
1014
|
settings.setValue('lab', self._ui.comboBoxLab.currentText())
|
|
1038
1015
|
settings.setValue('marker_diameter', self._ui.doubleSpinBoxMarkerDiameter.value())
|
|
@@ -1237,9 +1214,6 @@ class GaitCurves(defaultdict):
|
|
|
1237
1214
|
line.set_color(colour)
|
|
1238
1215
|
line.set_zorder(z_order)
|
|
1239
1216
|
|
|
1240
|
-
# TODO: Replace all these with `draw_idle`?
|
|
1241
|
-
# Just the ones that handle many curves...?
|
|
1242
|
-
# Probably all...?
|
|
1243
1217
|
self._canvas.draw()
|
|
1244
1218
|
|
|
1245
1219
|
def show_curve_menu(self, event):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c3d-parser
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: C3D parser for gait data harmonisation.
|
|
5
5
|
Author-email: Timothy Salemink <tim.nicolas@outlook.com>, Sally Jack <sallyjaack@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,7 +12,7 @@ Requires-Dist: numpy==1.26.4
|
|
|
12
12
|
Requires-Dist: pandas
|
|
13
13
|
Requires-Dist: c3d==0.5.2
|
|
14
14
|
Requires-Dist: trc-data-reader>=0.4.1
|
|
15
|
-
Requires-Dist: opensim-model-creator==0.
|
|
15
|
+
Requires-Dist: opensim-model-creator==0.10.0
|
|
16
16
|
Requires-Dist: PySide6
|
|
17
17
|
Requires-Dist: matplotlib
|
|
18
18
|
Requires-Dist: mplcursors
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/osim_resources/external_loads_template.xml
RENAMED
|
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
|
{c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py
RENAMED
|
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
|