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.
Files changed (47) hide show
  1. {c3d_parser-1.5.4/src/c3d_parser.egg-info → c3d_parser-1.6.0}/PKG-INFO +2 -2
  2. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/requirements.txt +1 -1
  3. c3d_parser-1.6.0/src/c3d_parser/__init__.py +2 -0
  4. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/c3d_parser.py +42 -21
  5. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/main_window.py +25 -51
  6. {c3d_parser-1.5.4 → c3d_parser-1.6.0/src/c3d_parser.egg-info}/PKG-INFO +2 -2
  7. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/requires.txt +1 -1
  8. c3d_parser-1.5.4/src/c3d_parser/__init__.py +0 -2
  9. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/LICENSE +0 -0
  10. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/README.md +0 -0
  11. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/pyproject.toml +0 -0
  12. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/setup.cfg +0 -0
  13. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/application.py +0 -0
  14. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/c3d_patch.py +0 -0
  15. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/osim.py +0 -0
  16. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/osim_resources/external_loads_template.xml +0 -0
  17. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/osim_resources/ik_task_set.xml +0 -0
  18. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/core/utils.py +0 -0
  19. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/general.py +0 -0
  20. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/logging.py +0 -0
  21. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/AUC.json +0 -0
  22. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/FMC.json +0 -0
  23. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/MH.json +0 -0
  24. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/QCMAS.json +0 -0
  25. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/RBWH.json +0 -0
  26. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/RCH.json +0 -0
  27. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/settings/marker_maps/Sydney.json +0 -0
  28. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/splash_rc.py +0 -0
  29. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/splashscreen.py +0 -0
  30. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/about_dialog.py +0 -0
  31. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py +0 -0
  32. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/marker_set_dialog.py +0 -0
  33. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py +0 -0
  34. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/dialogs/options_dialog.py +0 -0
  35. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/resources_rc.py +0 -0
  36. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_delete_marker_set_dialog.py +0 -0
  37. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_main_window.py +0 -0
  38. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_marker_set_dialog.py +0 -0
  39. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_marker_set_import_dialog.py +0 -0
  40. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/ui/ui_options_dialog.py +0 -0
  41. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/utils.py +0 -0
  42. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser/view/widgets.py +0 -0
  43. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/SOURCES.txt +0 -0
  44. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/dependency_links.txt +0 -0
  45. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/entry_points.txt +0 -0
  46. {c3d_parser-1.5.4 → c3d_parser-1.6.0}/src/c3d_parser.egg-info/top_level.txt +0 -0
  47. {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.5.4
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.8.3
15
+ Requires-Dist: opensim-model-creator==0.10.0
16
16
  Requires-Dist: PySide6
17
17
  Requires-Dist: matplotlib
18
18
  Requires-Dist: mplcursors
@@ -3,7 +3,7 @@ numpy==1.26.4
3
3
  pandas
4
4
  c3d==0.5.2
5
5
  trc-data-reader>=0.4.1
6
- opensim-model-creator==0.8.3
6
+ opensim-model-creator==0.10.0
7
7
  PySide6
8
8
  matplotlib
9
9
  mplcursors
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = "1.6.0"
@@ -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
- return round(np.linalg.norm(np.array(frame[landmark_1]) - np.array(frame[landmark_2])), 1)
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') - marker_diameter,
137
- 'right_knee_width': distance(mid_frame, 'RKNE', 'RKNEM') - marker_diameter,
138
- 'left_ankle_width': distance(mid_frame, 'LANK', 'LMED') - marker_diameter,
139
- 'right_ankle_width': distance(mid_frame, 'RANK', 'RMED') - marker_diameter,
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
- # Fill in any anthropometric gaps.
464
- if self._approximate_anthropometrics:
465
- input_directory = self._ui.lineEditInputDirectory.text()
466
- lab = self._ui.comboBoxLab.currentText()
467
- marker_diameter = self._ui.doubleSpinBoxMarkerDiameter.value()
468
- c3d_file = os.path.normpath(os.path.join(input_directory, static_trial))
469
- anthropometrics = approximate_anthropometrics(c3d_file, lab, marker_diameter)
470
-
471
- if not self._ui.doubleSpinBoxASISWidth.value():
472
- self._ui.doubleSpinBoxASISWidth.setValue(anthropometrics['inter_asis_distance'])
473
- if not self._ui.doubleSpinBoxLeftLegLength.value():
474
- self._ui.doubleSpinBoxLeftLegLength.setValue(anthropometrics['left_leg_length'])
475
- if not self._ui.doubleSpinBoxLeftKneeWidth.value():
476
- self._ui.doubleSpinBoxLeftKneeWidth.setValue(anthropometrics['left_knee_width'])
477
- if not self._ui.doubleSpinBoxLeftAnkleWidth.value():
478
- self._ui.doubleSpinBoxLeftAnkleWidth.setValue(anthropometrics['left_ankle_width'])
479
- if not self._ui.doubleSpinBoxRightLegLength.value():
480
- self._ui.doubleSpinBoxRightLegLength.setValue(anthropometrics['right_leg_length'])
481
- if not self._ui.doubleSpinBoxRightKneeWidth.value():
482
- self._ui.doubleSpinBoxRightKneeWidth.setValue(anthropometrics['right_knee_width'])
483
- if not self._ui.doubleSpinBoxRightAnkleWidth.value():
484
- self._ui.doubleSpinBoxRightAnkleWidth.setValue(anthropometrics['right_ankle_width'])
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
- if height < 250:
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.5.4
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.8.3
15
+ Requires-Dist: opensim-model-creator==0.10.0
16
16
  Requires-Dist: PySide6
17
17
  Requires-Dist: matplotlib
18
18
  Requires-Dist: mplcursors
@@ -3,7 +3,7 @@ numpy==1.26.4
3
3
  pandas
4
4
  c3d==0.5.2
5
5
  trc-data-reader>=0.4.1
6
- opensim-model-creator==0.8.3
6
+ opensim-model-creator==0.10.0
7
7
  PySide6
8
8
  matplotlib
9
9
  mplcursors
@@ -1,2 +0,0 @@
1
-
2
- __version__ = "1.5.4"
File without changes
File without changes
File without changes
File without changes