-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathHysAnalysis.py
More file actions
2450 lines (2066 loc) · 125 KB
/
Copy pathHysAnalysis.py
File metadata and controls
2450 lines (2066 loc) · 125 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""
HysAnalysis - Force-Displacement Hysteresis Curve Analysis Tool
================================================================
A Python application for analyzing force-displacement hysteresis curves,
extracting skeleton curves, and calculating various mechanical performance indices.
GitHub Repository: https://github.com/GarGarfie/HysAnalysis
License: MIT (or your chosen license)
Author: GarGarfie
Version: 1.0.0
For bug reports, feature requests, and contributions, please visit:
https://github.com/GarGarfie/HysAnalysis/issues
"""
import sys
import numpy as np
import pandas as pd
from pathlib import Path
# PySide6 imports
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QListWidget, QLabel, QRadioButton, QCheckBox,
QGroupBox, QSplitter, QTabWidget, QTextEdit, QFileDialog,
QMessageBox, QButtonGroup, QDoubleSpinBox, QSlider, QComboBox
)
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QFont, QKeySequence, QShortcut, QScreen, QIcon
import webbrowser
# Matplotlib imports
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg, NavigationToolbar2QT
from matplotlib.figure import Figure
# Scipy for interpolation
from scipy.interpolate import (
make_interp_spline, UnivariateSpline,
PchipInterpolator, Akima1DInterpolator,
BSpline, splrep
)
from scipy.signal import savgol_filter
# 配置 matplotlib 支持中文显示
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 10
class MplCanvas(FigureCanvasQTAgg):
"""自定义 Matplotlib 画布,支持鼠标滚轮缩放"""
def __init__(self, parent=None, width=12, height=9, dpi=100):
self.analyzer = parent
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.ax = self.fig.add_subplot(111)
super().__init__(self.fig)
self.setParent(parent)
# 初始化图形
self.ax.grid(True, alpha=0.3, linestyle='--', linewidth=0.5, which='both')
self.ax.minorticks_on()
self.ax.grid(True, which='minor', alpha=0.15, linestyle=':', linewidth=0.3)
self.update_labels()
# 连接滚轮事件
self.mpl_connect('scroll_event', self.on_scroll)
# 记录初始视图范围
self.original_xlim = None
self.original_ylim = None
def update_labels(self):
if self.analyzer:
self.ax.set_xlabel(self.analyzer.tr('Displacement (mm)'),
fontsize=11, fontweight='bold')
self.ax.set_ylabel(self.analyzer.tr('Force (N)'),
fontsize=11, fontweight='bold')
self.ax.set_title(self.analyzer.tr('Force-Displacement Hysteresis Curve'),
fontsize=13, fontweight='bold')
def on_scroll(self, event):
"""鼠标滚轮缩放"""
if event.inaxes != self.ax:
return
# 获取当前坐标范围
cur_xlim = self.ax.get_xlim()
cur_ylim = self.ax.get_ylim()
# 获取鼠标位置
xdata = event.xdata
ydata = event.ydata
# 缩放因子
if event.button == 'up':
scale_factor = 0.9 # 放大
elif event.button == 'down':
scale_factor = 1.1 # 缩小
else:
return
# 计算新的范围(以鼠标位置为中心缩放)
new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor
relx = (cur_xlim[1] - xdata) / (cur_xlim[1] - cur_xlim[0])
rely = (cur_ylim[1] - ydata) / (cur_ylim[1] - cur_ylim[0])
self.ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * relx])
self.ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * rely])
self.draw()
class HysteresisAnalyzer(QMainWindow):
def __init__(self):
super().__init__()
# 语言支持
self.current_language = 'en' # 默认英语
self.translations = {
'en': {
'language': 'Language',
'window_title': 'Force-Displacement Curve Analysis',
'File Management': 'File Management',
'Import': 'Import',
'Clear': 'Clear',
'Keyboard shortcut: Delete': 'Keyboard shortcut: "Delete" - Delete selected file',
'Plot Style': 'Plot Style',
'Dot-Line Graph': 'Dot-Line Graph',
'Spline Connected Graph': 'Spline Connected Graph',
'Skeleton curve extraction method': 'Skeleton curve extraction method',
'Method 1: Outer Envelope': 'Method 1: Outer Envelope',
'Method 2: Peak Points': 'Method 2: Peak Points',
'Skeleton curve analysis direction': 'Skeleton curve analysis direction',
'All directions': 'All directions',
'Positive direction only': 'Positive direction only',
'Negative direction only': 'Negative direction only',
'Ductility coefficient calculation method': 'Ductility coefficient calculation method',
'geometric': 'Geometric Method',
'energy': 'Energy Method',
'park': 'Park Method',
'farthest': 'Farthest Point',
'asce': 'ASCE Method',
'eeep': 'EEEP Method',
'elastic_yield': 'Elastic Yield',
'Data filtering options': 'Data filtering options',
'Only retain the first loop of the same displacement level': 'Only retain the first loop of the same displacement level',
'language': 'Language',
'Smoothness parameter': 'Smoothness parameter',
'Preset': 'Preset',
'Show original data points': 'Show original data points',
'Number of interpolation points': 'Number of interpolation points',
'Smoothing algorithm': 'Smoothing algorithm:',
'PCHIP - Shape-preserving interpolation (no overshoot)': 'PCHIP - Shape-preserving interpolation (no overshoot)',
'Akima - Akima interpolation (naturally smooth)': 'Akima - Akima interpolation (naturally smooth)',
'Bezier - Bézier curve (ultra smooth)': 'Bezier - Bézier curve (ultra smooth)',
'BSpline - B-spline (super smooth)': 'BSpline - B-spline (super smooth)',
'SG filter - Savitzky-Golay filter (feature-preserving)': 'SG filter - Savitzky-Golay filter (feature-preserving)',
'UnivariateSpline - General-purpose spline (adjustable smoothness)': 'UnivariateSpline - General-purpose spline (adjustable smoothness)',
'CubicSpline - Cubic spline (exactly passes through points)': 'CubicSpline - Cubic spline (exactly passes through points)',
'Control point density (%)': 'Control point density (%)',
'Value': 'Value',
'Adjustment': 'Adjustment',
'Current value': 'Current value',
'Current value {}': 'Current value {}',
'Current value {:.2f}': 'Current value {:.2f}',
'None': 'None',
'Low': 'Low',
'Medium': 'Medium',
'High': 'High',
'Very high': 'Very high',
'Shape‑preserving piecewise cubic interpolation. Preserves data monotonicity and avoids overshoot and oscillations. Suitable for preserving data trends.': 'Shape‑preserving piecewise cubic interpolation. Preserves data monotonicity and avoids overshoot and oscillations. Suitable for preserving data trends.',
'Akima interpolation method. Reduces oscillations in the curve and is more natural than cubic splines. Suitable for scenarios where you want to reduce fluctuations.': 'Akima interpolation method. Reduces oscillations in the curve and is more natural than cubic splines. Suitable for scenarios where you want to reduce fluctuations.',
'Bézier curve. Generates an extremely smooth curve, suitable for presentation/visualization. The parameter controls control‑point density: 10% = smoothest, 100% = closest to the original data. Recommended 20–40%.': 'Bézier curve. Generates an extremely smooth curve, suitable for presentation/visualization. The parameter controls control‑point density: 10% = smoothest, 100% = closest to the original data. Recommended 20–40%.',
'B‑spline interpolation. Produces a very smooth curve, but may deviate from the original data points. The parameter s controls the smoothness.': 'B‑spline interpolation. Produces a very smooth curve, but may deviate from the original data points. The parameter s controls the smoothness.',
'Savitzky–Golay filtering. Smooths the data while preserving its features (such as peaks). The parameter is the window size.': 'Savitzky–Golay filtering. Smooths the data while preserving its features (such as peaks). The parameter is the window size.',
'General‑purpose spline interpolation with adjustable smoothness. The parameter s controls the level of smoothing: s = 0 forces the spline to pass exactly through the points; the larger s is, the smoother the curve. Suitable for most cases.': 'General‑purpose spline interpolation with adjustable smoothness. The parameter s controls the level of smoothing: s = 0 forces the spline to pass exactly through the points; the larger s is, the smoother the curve. Suitable for most cases.',
'Cubic spline interpolation that passes exactly through all data points. The resulting curve has a continuous second derivative at the knots.': 'Cubic spline interpolation that passes exactly through all data points. The resulting curve has a continuous second derivative at the knots.',
'Smoothing interpolation failed': 'Smoothing interpolation failed',
'Failed to generate Bézier curve': 'Failed to generate Bézier curve',
'Smoothing failed': 'Smoothing failed',
'Displacement (mm)': 'Displacement (mm)',
'Force (N)': 'Force (N)',
'Force-Displacement Hysteresis Curve': 'Force-Displacement Hysteresis Curve',
'Hysteresis Curve': 'Hysteresis Curve',
'Positive Skeleton Curve': 'Positive Skeleton Curve',
'Negative Skeleton Curve': 'Negative Skeleton Curve',
'Skeleton Data Points': 'Skeleton Data Points',
'Positive Skeleton Curve Start Point': 'Positive Curve Start Point',
'Negative Skeleton Curve Start Point': 'Negative Skeleton Curve Start Point',
'Positive Peak': 'Positive Peak',
'Negative Peak': 'Negative Peak',
'Filtered Label': '[Filtered Label]',
'Smooth Label': '[Smooth Label]',
'Hysteresis curve and backbone curve': 'Hysteresis curve and backbone curve',
'Evaluation metrics and analysis results': 'Evaluation metrics and analysis results',
'Detailed information on hysteresis loops': 'Detailed information on hysteresis loops',
'No analysis results': 'No analysis results',
'Force-Displacement Curve Analysis Report': 'Force-Displacement Curve Analysis Report',
'Select force-displacement data files (multiple selections possible)': 'Select force-displacement data files (multiple selections possible)',
'All supported formats (*.txt *.csv *.xls *.xlsx); text files (*.txt); CSV files (*.csv); Excel files (*.xls *.xlsx); all files (*.*)': 'All supported formats (*.txt *.csv *.xls *.xlsx); text files (*.txt); CSV files (*.csv); Excel files (*.xls *.xlsx); all files (*.*)',
'Successfully imported {} files': 'Successfully imported {} files',
'Success': 'Success',
'Confirm': 'Confirm',
'Are you sure you want to clear all files?': 'Are you sure you want to clear all files?',
'Error': 'Error',
'Unsported file format: {}': 'Unsported file format: {}',
'Incorrect data format! The file must contain at least 2 columns:\nColumn 1 - Displacement\nColumn 2 - Force': 'Incorrect data format! The file must contain at least 2 columns:\nColumn 1 - Displacement\nColumn 2 - Force',
'Fail to read file:\n{}': 'Fail to read file:\n{}',
'Deleted': 'Deleted',
'Deleted {} files': 'Deleted {} files',
'Open Source Project | Contributions Welcome': 'Open Source Project | Contributions Welcome',
# 分析结果页面翻译
'File Information': 'File Information',
'File name': 'File name',
'Number of data points': 'Number of data points',
'Number of hysteresis loops': 'Number of hysteresis loops',
'Enabled (Only retain the first loop of the same displacement level)': 'Enabled (Only retain the first loop of the same displacement level)',
'Skeleton Curve Starting Points': 'Skeleton Curve Starting Points',
'Note: Common starting point crossing y-axis between first negative peak and second positive peak': 'Note: Common starting point crossing y-axis between first negative peak and second positive peak',
'Evaluation Metrics': 'Evaluation Metrics',
'Displacement-related': 'Displacement-related',
'Mechanical properties': 'Mechanical properties',
'Energy metrics': 'Energy metrics',
'Coefficient metrics': 'Coefficient metrics',
'Degradation metrics': 'Degradation metrics',
'Ductility coefficient': 'Ductility coefficient',
# 指标名称
'Peak displacement': 'Peak displacement',
'Residual deformation (mm)': 'Residual deformation (mm)',
'Peak load': 'Peak load',
'Initial stiffness (N/mm)': 'Initial stiffness (N/mm)',
'Secant stiffness (N/mm)': 'Secant stiffness (N/mm)',
'Total hysteresis loop area (kN·mm)': 'Total hysteresis loop area (kN·mm)',
'Cumulative energy dissipation (kN·mm)': 'Cumulative energy dissipation (kN·mm)',
'Average loop energy (kN·mm)': 'Average loop energy (kN·mm)',
'Maximum loop energy (kN·mm)': 'Maximum loop energy (kN·mm)',
'Equivalent viscous damping coefficient': 'Equivalent viscous damping coefficient',
'Positive strength degradation (%)': 'Positive strength degradation (%)',
'Negative strength degradation (%)': 'Negative strength degradation (%)',
'Stiffness degradation (%)': 'Stiffness degradation (%)',
'Positive (mm)': 'Positive (mm)',
'Negative (mm)': 'Negative (mm)',
'Positive (N)': 'Positive (N)',
'Negative (N)': 'Negative (N)',
# 滞回环详细信息翻译
'No hysteresis loop information': 'No hysteresis loop information',
'Detailed Hysteresis Loop Information': 'Detailed Hysteresis Loop Information',
'No.': 'No.',
'Type': 'Type',
'Peak Disp.': 'Peak Disp.',
'Peak Force': 'Peak Force',
'Loop Area': 'Loop Area',
'Positive': 'Positive',
'Negative': 'Negative',
'Statistical Information': 'Statistical Information',
'Total loops': 'Total loops',
'Total energy dissipation': 'Total energy dissipation',
'Average energy dissipation': 'Average energy dissipation',
'Maximum energy dissipation': 'Maximum energy dissipation',
'Minimum energy dissipation': 'Minimum energy dissipation',
'Positive loops': 'Positive loops',
'Negative loops': 'Negative loops',
},
'ru': {
'language': 'Язык',
'window_title': 'Анализ кривой сила-перемещение',
'File Management': 'Управление файлами',
'Import': 'Импорт',
'Clear': 'Очистить',
'Keyboard shortcut: Delete': 'Сочетание клавиш: "Delete" - удалить выбранный файл',
'Plot Style': 'Стиль графика',
'Dot-Line Graph': 'Точечный линейный график',
'Spline Connected Graph': 'Сплайн-связанный график',
'Skeleton curve extraction method': 'Метод извлечения скелетной кривой',
'Method 1: Outer Envelope': 'Метод 1: Внешняя огибающая',
'Method 2: Peak Points': 'Метод 2: Пиковые точки',
'Skeleton curve analysis direction': 'Skeleton curve analysis direction',
'All directions': 'Все направления',
'Positive direction only': 'Только положительное',
'Negative direction only': 'Только отрицательное',
'Ductility coefficient calculation method': 'Метод расчета коэффициента пластичности',
'geometric': 'Геометрический',
'energy': 'Энергетический',
'park': 'Метод Парка',
'farthest': 'Дальняя точка',
'asce': 'Метод ASCE',
'eeep': 'Метод EEEP',
'elastic_yield': 'Упругая текучесть',
'Data filtering options': 'Фильтр данных',
'Only retain the first loop of the same displacement level': 'Сохраните только первый круг того же уровня рабочего объема',
'language': 'Язык',
'Smoothness parameter': 'Параметр сглаживания:',
'Show original data points': 'Показать исходные точки',
'Number of interpolation points': 'Число точек интерполяции:',
'Smoothing algorithm': 'Алгоритм сглаживания:',
'Preset': 'Пресет',
'PCHIP - Shape-preserving interpolation (no overshoot)': 'PCHIP — формосохраняющая интерполяция (без выбросов)',
'Akima - Akima interpolation (naturally smooth)': 'Akima — интерполяция Акимы (естественное сглаживание)',
'Bezier - Bézier curve (ultra smooth)': 'Bezier — кривая Безье (максимальная гладкость)',
'BSpline - B-spline (super smooth)': 'BSpline — B-сплайн (сверхгладкое сглаживание)',
'SG filter - Savitzky-Golay filter (feature-preserving)': 'SG filter — фильтр Савицкого–Голея (с сохранением особенностей сигнала)',
'UnivariateSpline - General-purpose spline (adjustable smoothness)': 'UnivariateSpline — универсальный сплайн (настраиваемая степень сглаживания)',
'CubicSpline - Cubic spline (exactly passes through points)': 'CubicSpline — кубический сплайн (строго проходит через точки)',
'Control point density (%)': 'Плотность контрольных точек (%):',
'Value': 'Значение',
'Adjustment': 'Регулировка',
'Current value': 'Текущее значение',
'Current value {}': 'Текущее значение {}',
'Current value {:.2f}': 'Текущее значение {:.2f}',
'None': 'Нет',
'Low': 'Низкий',
'Medium': 'Средний',
'High': 'Высокий',
'Very high': 'Очень высокий',
'Shape‑preserving piecewise cubic interpolation. Preserves data monotonicity and avoids overshoot and oscillations. Suitable for preserving data trends.': 'Формосохраняющая кусочно‑кубическая интерполяция. Сохраняет монотонность данных и избегает выбросов и колебаний. Подходит для сохранения тренда данных.',
'Akima interpolation method. Reduces oscillations in the curve and is more natural than cubic splines. Suitable for scenarios where you want to reduce fluctuations.': 'Метод интерполяции Акимы. Уменьшает колебания кривой и даёт более естественный результат, чем кубический сплайн. Подходит для задач, где нужно снизить флуктуации.',
'Bézier curve. Generates an extremely smooth curve, suitable for presentation/visualization. The parameter controls control‑point density: 10% = smoothest, 100% = closest to the original data. Recommended 20–40%.': 'Кривая Безье. Генерирует предельно гладкую кривую, подходит для наглядного отображения. Параметр задаёт плотность контрольных точек: 10% — максимальная гладкость, 100% — наибольшее соответствие исходным данным. Рекомендуется 20–40%.',
'B‑spline interpolation. Produces a very smooth curve, but may deviate from the original data points. The parameter s controls the smoothness.': 'Интерполяция B‑сплайном. Даёт очень гладкую кривую, но она может отклоняться от исходных точек данных. Параметр s управляет степенью сглаживания.',
'Savitzky–Golay filtering. Smooths the data while preserving its features (such as peaks). The parameter is the window size.': 'Фильтрация Савицкого–Голея. Выполняет сглаживание, сохраняя особенности данных (например, пики). Параметр задаёт размер окна.',
'General‑purpose spline interpolation with adjustable smoothness. The parameter s controls the level of smoothing: s = 0 forces the spline to pass exactly through the points; the larger s is, the smoother the curve. Suitable for most cases.': 'Универсальная сплайн‑интерполяция с настраиваемой степенью сглаживания. Параметр s определяет уровень сглаживания: при s = 0 сплайн строго проходит через точки; чем больше s, тем сильнее сглаживание. Подходит для большинства задач.',
'Cubic spline interpolation that passes exactly through all data points. The resulting curve has a continuous second derivative at the knots.': 'Кубическая сплайн‑интерполяция, строго проходящая через все точки данных. Получающаяся кривая имеет непрерывную вторую производную в узлах.',
'Smoothing interpolation failed': 'Сглаживающая интерполяция не удалась',
'Failed to generate Bézier curve': 'Не удалось сгенерировать кривую Безье',
'Smoothing failed': 'Сглаживание не удалось',
'Displacement (mm)': 'Перемещение (мм)',
'Force (N)': 'Сила (Н)',
'Force-Displacement Hysteresis Curve': 'Кривая гистерезиса сила-перемещение',
'Hysteresis Curve': 'Кривая гистерезиса',
'Positive Skeleton Curve': 'Положительная скелетная кривая',
'Negative Skeleton Curve': 'Отрицательная скелетная кривая',
'Skeleton Data Points': 'Точки данных скелета',
'Positive Skeleton Curve Start Point': 'Положительное начало',
'Negative Skeleton Curve Start Point': 'Отрицательное начало',
'Positive Peak': 'Положительный пик',
'Negative Peak': 'Отрицательный пик',
'Filtered Label': 'Отфильтровано',
'Smooth Label': 'Сглаживание',
'Hysteresis curve and backbone curve': 'Гистерезисная кривая и скелетная (огибающая) кривая',
'Evaluation metrics and analysis results': 'Показатели оценки и результаты анализа',
'Detailed information on hysteresis loops': 'Подробная информация о петлях гистерезиса',
'No analysis results': 'Результаты анализа отсутствуют.',
'Force-Displacement Curve Analysis Report': 'Отчет по анализу кривой «сила-смещение»',
'Select force-displacement data files (multiple selections possible)': 'Выберите файлы данных «сила-смещение» (возможен выбор нескольких вариантов)',
'All supported formats (*.txt *.csv *.xls *.xlsx); text files (*.txt); CSV files (*.csv); Excel files (*.xls *.xlsx); all files (*.*)': 'Все поддерживаемые форматы (*.txt *.csv *.xls *.xlsx); текстовые файлы (*.txt); файлы CSV (*.csv); файлы Excel (*.xls *.xlsx); все файлы (*.*)',
'Successfully imported {} files': 'Успешно импортировано {} файлов.',
'Success': 'Успех',
'Confirm': 'Потвердить',
'Are you sure you want to clear all files?': 'Очистить все файлы?',
'Error': 'Ошибка',
'Unsported file format: {}': 'Неподдерживаемый формат файла: {}',
'Incorrect data format! The file must contain at least 2 columns:\nColumn 1 - Displacement\nColumn 2 - Force': 'Неверный формат данных! Файл должен содержать как минимум два столбца данных: \nСтолбец 1 - Перемещение \nСтолбец 2 - Сила',
'Fail to read file:\n{}': 'Не удалось прочитать файл: \n{}',
'Deleted': 'Удалено',
'Deleted {} files': 'Удалено {} файлов',
'Open Source Project | Contributions Welcome': 'Проект с открытым исходным кодом | Приветствуются вклады',
# Перевод страницы результатов анализа
'File Information': 'Информация о файле',
'File name': 'Имя файла',
'Number of data points': 'Количество точек данных',
'Number of hysteresis loops': 'Количество петель гистерезиса',
'Enabled (Only retain the first loop of the same displacement level)': 'Включено (сохранять только первую петлю для одного и того же уровня перемещения)',
'Skeleton Curve Starting Points': 'Начальные точки скелетной кривой',
'Note: Common starting point crossing y-axis between first negative peak and second positive peak': 'Примечание: типичная начальная точка — пересечение с осью Y между первым отрицательным пиком и вторым положительным пиком',
'Evaluation Metrics': 'Оценочные показатели',
'Displacement-related': 'Показатели, связанные с перемещением',
'Mechanical properties': 'Механические характеристики',
'Energy metrics': 'Энергетические показатели',
'Coefficient metrics': 'Коэффициентные показатели',
'Degradation metrics': 'Показатели деградации',
'Ductility coefficient': 'Коэффициент пластичности',
# Названия показателей
'Peak displacement': 'Пиковое перемещение',
'Residual deformation (mm)': 'Остаточная деформация (мм)',
'Peak load': 'Пиковая нагрузка',
'Initial stiffness (N/mm)': 'Начальная жёсткость (Н/мм)',
'Secant stiffness (k/mm)': 'Секущая жёсткость (Н/мм)',
'Total hysteresis loop area (kN·mm)': 'Общая площадь петель гистерезиса (кН·мм)',
'Cumulative energy dissipation (kN·mm)': 'Суммарное рассеяние энергии (кН·мм)',
'Average loop energy (kN·mm)': 'Средняя энергия петли (кН·мм)',
'Maximum loop energy (kN·mm)': 'Максимальная энергия петли (кН·мм)',
'Equivalent viscous damping coefficient': 'Эквивалентный коэффициент вязкого демпфирования',
'Positive strength degradation (%)': 'Деградация прочности в положительном направлении (%)',
'Negative strength degradation (%)': 'Деградация прочности в отрицательном направлении (%)',
'Stiffness degradation (%)': 'Деградация жёсткости (%)',
'Positive (mm)': 'Положительное (мм)',
'Negative (mm)': 'Отрицательное (мм)',
'Positive (N)': 'Положительное (Н)',
'Negative (N)': 'Отрицательное (Н)',
# Подробная информация о петлях гистерезиса
'No hysteresis loop information': 'Нет данных о петлях гистерезиса',
'Detailed Hysteresis Loop Information': 'Подробная информация о петлях гистерезиса',
'No.': '№',
'Type': 'Тип',
'Peak Disp.': 'Пиковое перемещение',
'Peak Force': 'Пиковое усилие',
'Loop Area': 'Площадь петли',
'Positive': 'Положительная',
'Negative': 'Отрицательная',
'Statistical Information': 'Статистическая информация',
'Total loops': 'Общее число петель',
'Total energy dissipation': 'Общее рассеяние энергии',
'Average energy dissipation': 'Среднее рассеяние энергии',
'Maximum energy dissipation': 'Максимальное рассеяние энергии',
'Minimum energy dissipation': 'Минимальное рассеяние энергии',
'Positive loops': 'Положительные петли',
'Negative loops': 'Отрицательные петли',
},
'zh': {
'language': '语言',
'window_title': '力-位移曲线数据处理与分析程序',
'File Management': '文件管理',
'Import': '导入文件',
'Clear': '清空列表',
'Keyboard shortcut: Delete': '快捷键: "Delete" - 删除选定文件',
'Plot Style': '绘图样式',
'Dot-Line Graph': '点线连接图',
'Spline Connected Graph': '平滑曲线图',
'Skeleton curve extraction method': '骨架曲线提取方法',
'Method 1: Outer Envelope': '方法1: 最外层包络线描边',
'Method 2: Peak Points': '方法2: 峰值点连接法',
'Skeleton curve analysis direction': '骨架曲线分析方向',
'All directions': '全部方向',
'Positive direction only': '仅正向',
'Negative direction only': '仅负向',
'Ductility coefficient calculation method': '延性系数计算方法',
'geometric': '几何作图法',
'energy': '能量法',
'park': 'Park法',
'farthest': '最远点法',
'asce': 'ASCE法',
'eeep': 'EEEP法',
'elastic_yield': '弹性屈服法',
'Data filtering options': '数据过滤选项',
'Only retain the first loop of the same displacement level': '仅保留同级位移首圈',
'language': '语言',
'Smoothness parameter': '平滑度参数:',
'Preset': '预设',
'Show original data points': '显示原始数据点',
'Number of interpolation points': '插值点数:',
'Smoothing algorithm': '平滑算法:',
'PCHIP - Shape-preserving interpolation (no overshoot)': 'PCHIP - 保形插值(无过冲)',
'Akima - Akima interpolation (naturally smooth)': 'Akima - Akima插值(自然平滑)',
'Bezier - Bézier curve (ultra smooth)': 'Bezier - 贝塞尔曲线(极致平滑)',
'BSpline - B-spline (super smooth)': 'BSpline - B样条(超平滑)',
'SG filter - Savitzky-Golay filter (feature-preserving)': 'Savitzky-Golay - SG滤波(保特征)',
'UnivariateSpline - General-purpose spline (adjustable smoothness)': 'UnivariateSpline - 通用样条(可调平滑度)',
'CubicSpline - Cubic spline (exactly passes through points)': 'CubicSpline - 三次样条(严格通过点)',
'Control point density (%)': '控制点密度 (%):',
'Value': '数值',
'Adjustment': '调节',
'Current value': '当前值',
'Current value {}': '当前值: {}',
'Current value {:.2f}': '当前值: {:.2f}',
'None': '无',
'Low': '低',
'Medium': '中',
'High': '高',
'Very high': '极高',
'Shape‑preserving piecewise cubic interpolation. Preserves data monotonicity and avoids overshoot and oscillations. Suitable for preserving data trends.': '保形分段三次插值。保持数据的单调性,避免过冲和振荡。适合保持数据趋势。',
'Akima interpolation method. Reduces oscillations in the curve and is more natural than cubic splines. Suitable for scenarios where you want to reduce fluctuations.': 'Akima插值方法。减少曲线振荡,比三次样条更自然。适合减少波动的场景。',
'Bézier curve. Generates an extremely smooth curve, suitable for presentation/visualization. The parameter controls control‑point density: 10% = smoothest, 100% = closest to the original data. Recommended 20–40%.': '贝塞尔曲线。生成极致平滑的曲线,适合展示用途。参数控制控制点密度:10% = 最平滑,100% = 最贴合原始数据。推荐20–40%。',
'B‑spline interpolation. Produces a very smooth curve, but may deviate from the original data points. The parameter s controls the smoothness.': 'B样条插值。生成最平滑的曲线,但可能偏离原始数据点。参数s控制平滑度。',
'Savitzky–Golay filtering. Smooths the data while preserving its features (such as peaks). The parameter is the window size.': 'Savitzky-Golay滤波。在平滑的同时保持数据特征(如峰值)。参数为窗口大小。',
'General‑purpose spline interpolation with adjustable smoothness. The parameter s controls the level of smoothing: s = 0 forces the spline to pass exactly through the points; the larger s is, the smoother the curve. Suitable for most cases.': '通用样条插值,支持平滑度调节。参数s控制平滑程度:s = 0严格通过点,s越大越平滑。适合大多数情况。',
'Cubic spline interpolation that passes exactly through all data points. The resulting curve has a continuous second derivative at the knots.': '三次样条插值,严格通过所有数据点。生成的曲线在连接点处二阶导数连续。',
'Smoothing interpolation failed': '平滑插值失败',
'Failed to generate Bézier curve': '贝塞尔曲线生成失败',
'Smoothing failed': '平滑失败',
'Displacement (mm)': '位移 (mm)',
'Force (N)': '力 (N)',
'Force-Displacement Hysteresis Curve': '力-位移滞回曲线',
'Hysteresis Curve': '滞回曲线',
'Positive Skeleton Curve': '正向骨架曲线',
'Negative Skeleton Curve': '负向骨架曲线',
'Skeleton Data Points': '骨架曲线数据点',
'Positive Skeleton Curve Start Point': '正向骨架曲线起点',
'Negative Skeleton Curve Start Point': '负向骨架曲线起点',
'Positive Peak': '正向峰值',
'Negative Peak': '负向峰值',
'Filtered Label': '[已过滤]',
'Smooth Label': '[平滑]',
'Hysteresis curve and backbone curve': '滞回曲线与骨架曲线',
'Evaluation metrics and analysis results': '评价指标与分析结果',
'Detailed information on hysteresis loops': '滞回环详细信息',
'No analysis results': '无分析结果',
'Force-Displacement Curve Analysis Report': '力-位移滞回曲线分析报告',
'Select force-displacement data files (multiple selections possible)': '选择力-位移数据文件(可多选)',
'All supported formats (*.txt *.csv *.xls *.xlsx); text files (*.txt); CSV files (*.csv); Excel files (*.xls *.xlsx); all files (*.*)': '所有支持的格式 (*.txt *.csv *.xls *.xlsx); 文本文件 (*.txt); CSV文件 (*.csv); Excel文件 (*.xls *.xlsx); 所有文件 (*.*)',
'Successfully imported {} files': '成功导入 {} 个文件。',
'Success': '成功',
'Confirm': '确认',
'Are you sure you want to clear all files?': ' 确定要清空所有文件吗?',
'Error': '错误',
'Unsported file format: {}': '不支持的文件格式: {}',
'Incorrect data format! The file must contain at least 2 columns:\nColumn 1 - Displacement\nColumn 2 - Force': '数据格式不正确!文件需要至少包含两列数据:\n第1列 - 位移 \n第2列 - 力',
'Fail to read file:\n{}': '读取文件失败: \n{}',
'Deleted': '已删除',
'Deleted {} files': '已删除 {} 个文件',
'Open Source Project | Contributions Welcome': '免费开源项目 | 欢迎贡献',
# 分析结果页面翻译
'File Information': '文件信息',
'File name': '文件名',
'Number of data points': '数据点数量',
'Number of hysteresis loops': '滞回环数量',
'Enabled (Only retain the first loop of the same displacement level)': '启用(仅保留相同位移水平的首个滞回环)',
'Skeleton Curve Starting Points': '骨架曲线起始点',
'Note: Common starting point crossing y-axis between first negative peak and second positive peak': '注:常用起点为第一次负峰与第二次正峰之间穿过 y 轴的点',
'Evaluation Metrics': '评价指标',
'Displacement-related': '位移相关',
'Mechanical properties': '力学性能',
'Energy metrics': '能量指标',
'Coefficient metrics': '系数指标',
'Degradation metrics': '退化指标',
'Ductility coefficient': '延性系数',
# 指标名称
'Peak displacement': '峰值位移',
'Residual deformation (mm)': '残余变形 (mm)',
'Peak load': '峰值荷载',
'Initial stiffness (N/mm)': '初始刚度 (k/mm)',
'Secant stiffness (N/mm)': '割线刚度 (N/mm)',
'Total hysteresis loop area (kN·mm)': '滞回环总面积 (kN·mm)',
'Cumulative energy dissipation (kN·mm)': '累积耗能 (kN·mm)',
'Average loop energy (kN·mm)': '平均单环耗能 (kN·mm)',
'Maximum loop energy (kN·mm)': '最大单环耗能 (kN·mm)',
'Equivalent viscous damping coefficient': '等效粘滞阻尼系数',
'Positive strength degradation (%)': '正向强度退化 (%)',
'Negative strength degradation (%)': '负向强度退化 (%)',
'Stiffness degradation (%)': '刚度退化 (%)',
'Positive (mm)': '正向 (mm)',
'Negative (mm)': '负向 (mm)',
'Positive (N)': '正向 (N)',
'Negative (N)': '负向 (N)',
# 滞回环详细信息翻译
'No hysteresis loop information': '无滞回环信息',
'Detailed Hysteresis Loop Information': '滞回环详细信息',
'No.': '序号',
'Type': '类型',
'Peak Disp.': '峰值位移',
'Peak Force': '峰值力',
'Loop Area': '滞回环面积',
'Positive': '正向',
'Negative': '负向',
'Statistical Information': '统计信息',
'Total loops': '滞回环总数',
'Total energy dissipation': '总耗能',
'Average energy dissipation': '平均耗能',
'Maximum energy dissipation': '最大耗能',
'Minimum energy dissipation': '最小耗能',
'Positive loops': '正向滞回环',
'Negative loops': '负向滞回环',
}
}
self.setWindowTitle("Force-Displacement Curve Analyzer")
# 设置窗口图标
def get_resource_path(relative_path):
"""获取资源文件的绝对路径(支持打包后的exe)"""
try:
# PyInstaller创建临时文件夹,路径存储在_MEIPASS中
base_path = sys._MEIPASS
except Exception:
base_path = Path(__file__).parent
return Path(base_path) / relative_path
icon_path = get_resource_path("icon.ico")
if icon_path.exists():
self.setWindowIcon(QIcon(str(icon_path)))
# 自适应屏幕尺寸
self.setup_window_geometry()
# 数据存储
self.data_files = []
self.current_data = None
self.hysteresis_loops = []
self.skeleton_curve = None
self.history = []
self.indices = {}
# 创建界面
self.init_ui()
# 设置快捷键
self.setup_shortcuts()
def setup_window_geometry(self):
"""设置窗口几何尺寸,自适应屏幕"""
# 获取主屏幕
screen = QApplication.primaryScreen()
if screen:
screen_geometry = screen.availableGeometry()
screen_width = screen_geometry.width()
screen_height = screen_geometry.height()
# 设置窗口为屏幕的85%大小
window_width = int(screen_width * 0.85)
window_height = int(screen_height * 0.85)
# 计算居中位置
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
# 设置窗口位置和大小
self.setGeometry(x, y, window_width, window_height)
else:
# 如果无法获取屏幕信息,使用默认值
self.setGeometry(100, 100, 1400, 800)
def init_ui(self):
"""初始化UI"""
# 中央部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
# 主布局
main_layout = QHBoxLayout(central_widget)
# 创建分割器
splitter = QSplitter(Qt.Horizontal)
main_layout.addWidget(splitter)
# 左侧控制面板
left_widget = self.create_left_panel()
splitter.addWidget(left_widget)
# 右侧显示面板
right_widget = self.create_right_panel()
splitter.addWidget(right_widget)
# 设置分割器比例
splitter.setSizes([350, 1250])
def create_left_panel(self):
"""创建左侧控制面板"""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setSpacing(10)
# 语言选择
self.lang_group = QGroupBox(self.tr('language'))
lang_layout = QHBoxLayout()
self.lang_combo = QComboBox()
self.lang_combo.addItems(['English', 'Русскии', '中文'])
self.lang_combo.setCurrentIndex(0) # 默认英语
self.lang_combo.currentIndexChanged.connect(self.change_language)
lang_layout.addWidget(self.lang_combo)
self.lang_group.setLayout(lang_layout)
layout.addWidget(self.lang_group)
# 文件管理区域
self.file_group = QGroupBox("File Management")
file_layout = QVBoxLayout()
# 按钮行
btn_layout = QHBoxLayout()
self.import_btn = QPushButton("Import")
self.import_btn.clicked.connect(self.import_files)
self.clear_btn = QPushButton("Clear")
self.clear_btn.clicked.connect(self.clear_files)
btn_layout.addWidget(self.import_btn)
btn_layout.addWidget(self.clear_btn)
file_layout.addLayout(btn_layout)
# 文件列表
self.file_list = QListWidget()
self.file_list.currentRowChanged.connect(self.on_file_select)
file_layout.addWidget(self.file_list)
# 提示标签
self.hint_label = QLabel("Keyboard shortcut: Delete")
self.hint_label.setStyleSheet("color: gray; font-size: 8pt;")
file_layout.addWidget(self.hint_label)
self.file_group.setLayout(file_layout)
layout.addWidget(self.file_group)
# 绘图样式选择
self.plot_style_group = QGroupBox("Plot Style")
plot_style_layout = QVBoxLayout()
self.plot_style_group_func = QButtonGroup()
self.rb_line = QRadioButton("Dot-Line Graph")
self.rb_smooth = QRadioButton("Spline Connected Graph")
self.rb_line.setChecked(True)
self.plot_style_group_func.addButton(self.rb_line, 0)
self.plot_style_group_func.addButton(self.rb_smooth, 1)
self.rb_line.toggled.connect(self.on_plot_style_changed)
self.rb_smooth.toggled.connect(self.on_plot_style_changed)
plot_style_layout.addWidget(self.rb_line)
plot_style_layout.addWidget(self.rb_smooth)
# 平滑曲线选项容器
smooth_container = QWidget()
smooth_layout = QVBoxLayout(smooth_container)
smooth_layout.setContentsMargins(15, 5, 5, 5)
# 平滑算法选择
self.algo_label = QLabel("Smoothing algorithm")
self.algo_label.setStyleSheet("font-weight: bold; color: #2c3e50;")
smooth_layout.addWidget(self.algo_label)
self.smooth_algorithm = QComboBox()
self.smooth_algorithm.addItems([
self.tr('PCHIP - Shape-preserving interpolation (no overshoot)'),
self.tr('Akima - Akima interpolation (naturally smooth)'),
self.tr('Bezier - Bézier curve (ultra smooth)'),
self.tr('BSpline - B-spline (super smooth)'),
self.tr('SG filter - Savitzky-Golay filter (feature-preserving)'),
self.tr('UnivariateSpline - General-purpose spline (adjustable smoothness)'),
self.tr('CubicSpline - Cubic spline (exactly passes through points)')
])
self.smooth_algorithm.currentIndexChanged.connect(self.on_algorithm_changed)
smooth_layout.addWidget(self.smooth_algorithm)
# 算法说明
self.algo_description = QLabel()
self.algo_description.setWordWrap(True)
self.algo_description.setStyleSheet(
"color: #34495e; font-size: 8pt; "
"padding: 8px; background-color: #ecf0f1; "
"border-radius: 4px; margin: 5px 0px;"
)
smooth_layout.addWidget(self.algo_description)
# 参数调节区域
param_container = QWidget()
param_layout = QVBoxLayout(param_container)
param_layout.setContentsMargins(0, 5, 0, 5)
# 参数标签
self.param_title = QLabel("Smoothness parameter")
self.param_title.setStyleSheet("font-weight: bold; color: #2c3e50;")
param_layout.addWidget(self.param_title)
# 数字输入框
spinbox_layout = QHBoxLayout()
self.spinbox_label = QLabel("Value")
self.smoothness_spinbox = QDoubleSpinBox()
self.smoothness_spinbox.setRange(0.0, 10.0)
self.smoothness_spinbox.setSingleStep(0.1)
self.smoothness_spinbox.setValue(1.0)
self.smoothness_spinbox.setDecimals(2)
self.smoothness_spinbox.valueChanged.connect(self.on_smoothness_changed)
spinbox_layout.addWidget(self.spinbox_label)
spinbox_layout.addWidget(self.smoothness_spinbox)
param_layout.addLayout(spinbox_layout)
# 滑块控制
slider_layout = QVBoxLayout()
self.slider_label = QLabel("Adjustment")
self.smoothness_slider = QSlider(Qt.Horizontal)
self.smoothness_slider.setRange(0, 100)
self.smoothness_slider.setValue(10)
self.smoothness_slider.setTickPosition(QSlider.TicksBelow)
self.smoothness_slider.setTickInterval(10)
self.smoothness_slider.valueChanged.connect(self.on_slider_changed)
slider_layout.addWidget(self.slider_label)
slider_layout.addWidget(self.smoothness_slider)
param_layout.addLayout(slider_layout)
# 当前值显示
self.smoothness_value_label = QLabel(self.tr('Current value').format("1.00"))
self.smoothness_value_label.setStyleSheet("color: #16a085; font-weight: bold;")
param_layout.addWidget(self.smoothness_value_label)
# 预设按钮
preset_layout = QHBoxLayout()
self.preset_label = QLabel("Preset")
preset_layout.addWidget(self.preset_label)
for value, name in [(0.0, self.tr('None')), (2.5, self.tr('Low')), (5.0, self.tr('Medium')), (7.5, self.tr('High')), (10, self.tr('Very High'))]:
btn = QPushButton(name)
btn.setProperty("smoothness_value", value)
btn.clicked.connect(self.on_preset_clicked)
btn.setMaximumWidth(100)
preset_layout.addWidget(btn)
param_layout.addLayout(preset_layout)
self.param_container = param_container
smooth_layout.addWidget(param_container)
# 插值点数控制
points_layout = QHBoxLayout()
self.points_label = QLabel("Number of interpolation points")
self.interp_points = QComboBox()
self.interp_points.addItems(["100", "200", "300", "500", "1000"])
self.interp_points.setCurrentText("300")
self.interp_points.currentTextChanged.connect(self.update_plot_only)
points_layout.addWidget(self.points_label)
points_layout.addWidget(self.interp_points)
smooth_layout.addLayout(points_layout)
# 显示原始数据点
self.show_original_points = QCheckBox("Show original data points")
self.show_original_points.setChecked(True)
self.show_original_points.stateChanged.connect(self.update_plot_only)
smooth_layout.addWidget(self.show_original_points)
self.smooth_container = smooth_container
plot_style_layout.addWidget(smooth_container)
# 初始状态:隐藏平滑选项
self.smooth_container.setVisible(False)
self.plot_style_group.setLayout(plot_style_layout)
layout.addWidget(self.plot_style_group)
# 骨架曲线提取方法
self.skeleton_group = QGroupBox("Skeleton curve extraction method")
skeleton_layout = QVBoxLayout()
self.skeleton_method_group = QButtonGroup()
self.rb_outer = QRadioButton("Method 1: Outer Envelope")
self.rb_peak = QRadioButton("Method 2: Peak Points")
self.rb_outer.setChecked(True)
self.skeleton_method_group.addButton(self.rb_outer, 0)
self.skeleton_method_group.addButton(self.rb_peak, 1)
self.rb_outer.toggled.connect(self.update_analysis)
self.rb_peak.toggled.connect(self.update_analysis)
skeleton_layout.addWidget(self.rb_outer)
skeleton_layout.addWidget(self.rb_peak)
self.skeleton_group.setLayout(skeleton_layout)
layout.addWidget(self.skeleton_group)
# 分析方向选择
self.direction_group = QGroupBox("Skeleton curve analysis direction")
direction_layout = QVBoxLayout()
self.direction_group_button = QButtonGroup()
self.rb_both = QRadioButton("All directions")
self.rb_positive = QRadioButton("Positive direction only")
self.rb_negative = QRadioButton("Negative direction only")
self.rb_both.setChecked(True)
self.direction_group_button.addButton(self.rb_both, 0)
self.direction_group_button.addButton(self.rb_positive, 1)
self.direction_group_button.addButton(self.rb_negative, 2)
self.rb_both.toggled.connect(self.update_analysis)
self.rb_positive.toggled.connect(self.update_analysis)
self.rb_negative.toggled.connect(self.update_analysis)
direction_layout.addWidget(self.rb_both)
direction_layout.addWidget(self.rb_positive)
direction_layout.addWidget(self.rb_negative)
self.direction_group.setLayout(direction_layout)
layout.addWidget(self.direction_group)
# 延性系数计算方法
self.ductility_group = QGroupBox("Ductility coefficient calculation method")
ductility_layout = QVBoxLayout()
self.ductility_method_group = QButtonGroup()
methods = [
("Geometric Method", "geometric"), #几何作图法
("Energy Method", "energy"), #能量法
("Park Method", "park"), #Park法
("Farthest Point", "farthest"), #最远点法
("ASCE Method", "asce"), #ASCE法
("EEEP Method", "eeep"), #EEEP法
("Elastic Yield", "elastic_yield") #弹性屈服法
]
self.ductility_radios = {}
for i, (text, value) in enumerate(methods):
rb = QRadioButton(text)
rb.setProperty("method_value", value)
if i == 0:
rb.setChecked(True)
rb.toggled.connect(self.update_analysis)
self.ductility_method_group.addButton(rb, i)
self.ductility_radios[value] = rb
ductility_layout.addWidget(rb)
self.ductility_group.setLayout(ductility_layout)
layout.addWidget(self.ductility_group)
# 数据过滤选项
self.filter_group = QGroupBox("Data filtering options")
filter_layout = QVBoxLayout()
self.filter_first_loop = QCheckBox("Only retain the first loop of the same displacement level")
self.filter_first_loop.stateChanged.connect(self.update_analysis)
filter_layout.addWidget(self.filter_first_loop)
self.filter_group.setLayout(filter_layout)
layout.addWidget(self.filter_group)
# GitHub 开源项目链接
github_group = QGroupBox()
github_layout = QVBoxLayout()
# 项目信息标签
self.project_label = QLabel(self.tr('Open Source Project | Contributions Welcome'))
self.project_label.setStyleSheet("font-weight: bold; color: #2c3e50;")
self.project_label.setAlignment(Qt.AlignCenter)
github_layout.addWidget(self.project_label)
# GitHub 链接
github_link = QLabel()
github_link.setText('<a href="https://github.com/GarGarfie/HysAnalysis" style="color: #3498db; text-decoration: none;">📂 github.com/GarGarfie/HysAnalysis</a>')
github_link.setOpenExternalLinks(True)
github_link.setAlignment(Qt.AlignCenter)
github_link.setStyleSheet("""
QLabel {
padding: 8px;
background-color: #ecf0f1;
border-radius: 4px;
font-size: 9pt;
}
QLabel:hover {
background-color: #d5dbdb;
}
""")
github_layout.addWidget(github_link)
github_group.setLayout(github_layout)
layout.addWidget(github_group)
# 添加弹簧
layout.addStretch()
# 初始化算法描述
self.update_algorithm_description()
return widget
def tr(self, key):
"""获取翻译文本"""
return self.translations.get(self.current_language, {}).get(key, key)
def change_language(self, index):
"""切换语言"""
languages = ['en', 'ru', 'zh']
self.current_language = languages[index]
self.update_ui_language()
# matplotlib配置,根据语言调整
if self.current_language == 'zh':
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
elif self.current_language == 'ru':
plt.rcParams['font.sans-serif'] = ['Arial', 'Liberation Sans', 'DejaVu Sans', 'Tahoma']
plt.rcParams['axes.unicode_minus'] = True
else:
plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Liberation Sans']
plt.rcParams['axes.unicode_minus'] = True
self.canvas.update_labels()
if self.current_data:
self.update_plot()
self.update_results()
self.update_loop_info()
def update_ui_language(self):
"""更新UI所有文本"""
self.setWindowTitle(self.tr('window_title'))
self.lang_group.setTitle(self.tr('language'))
self.file_group.setTitle(self.tr('File Management'))
self.import_btn.setText(self.tr('Import'))
self.clear_btn.setText(self.tr('Clear'))
self.hint_label.setText(self.tr('Keyboard shortcut: Delete'))
self.plot_style_group.setTitle(self.tr('Plot Style'))
self.rb_line.setText(self.tr('Dot-Line Graph'))
self.rb_smooth.setText(self.tr('Spline Connected Graph'))
self.skeleton_group.setTitle(self.tr('Skeleton curve extraction method'))
self.rb_outer.setText(self.tr('Method 1: Outer Envelope'))