graphica-plot 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. graphica/Graphica.ico +0 -0
  2. graphica/__init__.py +1 -0
  3. graphica/__main__.py +111 -0
  4. graphica/assets/__init__.py +0 -0
  5. graphica/assets/icons/__init__.py +0 -0
  6. graphica/assets/icons/arrow-right.svg +21 -0
  7. graphica/assets/icons/bold.svg +20 -0
  8. graphica/assets/icons/calculator.svg +26 -0
  9. graphica/assets/icons/chart-histogram.svg +24 -0
  10. graphica/assets/icons/chart-line.svg +20 -0
  11. graphica/assets/icons/chevron-down.svg +19 -0
  12. graphica/assets/icons/chevron-right.svg +19 -0
  13. graphica/assets/icons/color-swatch.svg +22 -0
  14. graphica/assets/icons/column-insert-right.svg +21 -0
  15. graphica/assets/icons/column-remove.svg +21 -0
  16. graphica/assets/icons/copy.svg +20 -0
  17. graphica/assets/icons/download.svg +21 -0
  18. graphica/assets/icons/edit.svg +21 -0
  19. graphica/assets/icons/eye-off.svg +21 -0
  20. graphica/assets/icons/eye.svg +20 -0
  21. graphica/assets/icons/file-plus.svg +22 -0
  22. graphica/assets/icons/folder-plus.svg +21 -0
  23. graphica/assets/icons/folder.svg +19 -0
  24. graphica/assets/icons/highlight.svg +22 -0
  25. graphica/assets/icons/history.svg +20 -0
  26. graphica/assets/icons/italic.svg +21 -0
  27. graphica/assets/icons/layout-grid.svg +22 -0
  28. graphica/assets/icons/math-function.svg +22 -0
  29. graphica/assets/icons/message-2.svg +21 -0
  30. graphica/assets/icons/mountain.svg +20 -0
  31. graphica/assets/icons/palette.svg +22 -0
  32. graphica/assets/icons/pointer.svg +19 -0
  33. graphica/assets/icons/refresh.svg +20 -0
  34. graphica/assets/icons/row-insert-bottom.svg +21 -0
  35. graphica/assets/icons/row-remove.svg +21 -0
  36. graphica/assets/icons/search.svg +20 -0
  37. graphica/assets/icons/select-all.svg +35 -0
  38. graphica/assets/icons/subscript.svg +20 -0
  39. graphica/assets/icons/superscript.svg +20 -0
  40. graphica/assets/icons/table.svg +21 -0
  41. graphica/assets/icons/trash.svg +23 -0
  42. graphica/assets/icons/typography.svg +23 -0
  43. graphica/assets/icons/x.svg +20 -0
  44. graphica/core/__init__.py +0 -0
  45. graphica/core/analysis.py +1278 -0
  46. graphica/core/app_paths.py +35 -0
  47. graphica/core/axis_settings.py +128 -0
  48. graphica/core/caption_export.py +47 -0
  49. graphica/core/color_palettes.py +56 -0
  50. graphica/core/commands.py +226 -0
  51. graphica/core/cvd_simulation.py +49 -0
  52. graphica/core/dataset.py +443 -0
  53. graphica/core/diagnostics.py +105 -0
  54. graphica/core/excel_utils.py +58 -0
  55. graphica/core/fit_models.py +379 -0
  56. graphica/core/grid_data.py +128 -0
  57. graphica/core/i18n.py +44 -0
  58. graphica/core/json_utils.py +27 -0
  59. graphica/core/label_utils.py +21 -0
  60. graphica/core/methods_text.py +115 -0
  61. graphica/core/named_colors.py +126 -0
  62. graphica/core/plugin_api.py +515 -0
  63. graphica/core/plugin_context.py +167 -0
  64. graphica/core/plugin_install.py +82 -0
  65. graphica/core/plugin_manifest.py +67 -0
  66. graphica/core/plugin_testing.py +213 -0
  67. graphica/core/plugin_types.py +118 -0
  68. graphica/core/provenance.py +19 -0
  69. graphica/core/report_export.py +51 -0
  70. graphica/core/safe_eval.py +194 -0
  71. graphica/core/script_export.py +272 -0
  72. graphica/core/translations_en.py +530 -0
  73. graphica/core/unit_conversion.py +51 -0
  74. graphica/core/update_check.py +43 -0
  75. graphica/core/version.py +14 -0
  76. graphica/gui/__init__.py +0 -0
  77. graphica/gui/app_settings.py +119 -0
  78. graphica/gui/axis_bindings.py +173 -0
  79. graphica/gui/binding.py +135 -0
  80. graphica/gui/builders/__init__.py +1 -0
  81. graphica/gui/builders/axis_panel.py +431 -0
  82. graphica/gui/builders/canvas_area.py +176 -0
  83. graphica/gui/builders/common.py +120 -0
  84. graphica/gui/builders/dataset_panel.py +433 -0
  85. graphica/gui/builders/property_sections.py +208 -0
  86. graphica/gui/canvas.py +519 -0
  87. graphica/gui/color_history.py +47 -0
  88. graphica/gui/color_picker_widget.py +198 -0
  89. graphica/gui/crash_handler.py +77 -0
  90. graphica/gui/cvd_preview.py +23 -0
  91. graphica/gui/data_editor.py +761 -0
  92. graphica/gui/data_import_flow.py +355 -0
  93. graphica/gui/dataset_bindings.py +107 -0
  94. graphica/gui/dataset_style_icon.py +80 -0
  95. graphica/gui/datasets/__init__.py +1 -0
  96. graphica/gui/datasets/actions_menu.py +114 -0
  97. graphica/gui/datasets/colors.py +218 -0
  98. graphica/gui/datasets/fitting.py +70 -0
  99. graphica/gui/datasets/host.py +162 -0
  100. graphica/gui/datasets/operations/__init__.py +1 -0
  101. graphica/gui/datasets/operations/fitting.py +468 -0
  102. graphica/gui/datasets/operations/peaks.py +109 -0
  103. graphica/gui/datasets/operations/processing.py +659 -0
  104. graphica/gui/datasets/operations/runner.py +148 -0
  105. graphica/gui/datasets/operations/transfer.py +227 -0
  106. graphica/gui/datasets/order.py +310 -0
  107. graphica/gui/datasets/overlays.py +74 -0
  108. graphica/gui/datasets/peaks.py +21 -0
  109. graphica/gui/datasets/plugin_runs.py +99 -0
  110. graphica/gui/datasets/processing.py +68 -0
  111. graphica/gui/datasets/property_panel.py +538 -0
  112. graphica/gui/datasets/transfer.py +36 -0
  113. graphica/gui/detached_canvas_window.py +22 -0
  114. graphica/gui/dialogs/__init__.py +137 -0
  115. graphica/gui/dialogs/analysis.py +1322 -0
  116. graphica/gui/dialogs/app.py +1015 -0
  117. graphica/gui/dialogs/appearance.py +851 -0
  118. graphica/gui/dialogs/data_edit.py +577 -0
  119. graphica/gui/dialogs/data_import.py +679 -0
  120. graphica/gui/dialogs/export.py +433 -0
  121. graphica/gui/dock_layout.py +176 -0
  122. graphica/gui/export_preview_panel.py +321 -0
  123. graphica/gui/export_settings.py +16 -0
  124. graphica/gui/file_association.py +97 -0
  125. graphica/gui/icon_utils.py +44 -0
  126. graphica/gui/main_app_window.py +210 -0
  127. graphica/gui/main_window.py +1151 -0
  128. graphica/gui/mathtext_preview.py +168 -0
  129. graphica/gui/menu_bar.py +320 -0
  130. graphica/gui/minimap_widget.py +151 -0
  131. graphica/gui/mixins/__init__.py +0 -0
  132. graphica/gui/mixins/export_mixin.py +520 -0
  133. graphica/gui/mixins/help_mixin.py +127 -0
  134. graphica/gui/mixins/project_io_mixin.py +289 -0
  135. graphica/gui/mixins/quick_access_mixin.py +194 -0
  136. graphica/gui/mixins/ui_setup_mixin.py +206 -0
  137. graphica/gui/notify.py +61 -0
  138. graphica/gui/panels/__init__.py +43 -0
  139. graphica/gui/panels/axis_settings.py +605 -0
  140. graphica/gui/panels/dataset_tree.py +424 -0
  141. graphica/gui/plot_type_drawers.py +131 -0
  142. graphica/gui/plugin_context.py +119 -0
  143. graphica/gui/project_files.py +361 -0
  144. graphica/gui/provenance_panel.py +65 -0
  145. graphica/gui/rendering/__init__.py +1 -0
  146. graphica/gui/rendering/annotations.py +177 -0
  147. graphica/gui/rendering/appearance.py +368 -0
  148. graphica/gui/rendering/common.py +316 -0
  149. graphica/gui/rendering/data_1d.py +376 -0
  150. graphica/gui/rendering/data_2d.py +66 -0
  151. graphica/gui/residual_panel.py +62 -0
  152. graphica/gui/resources.py +16 -0
  153. graphica/gui/single_instance.py +141 -0
  154. graphica/gui/splash.py +83 -0
  155. graphica/gui/task_runner.py +41 -0
  156. graphica/gui/theme.py +864 -0
  157. graphica/gui/tools/__init__.py +56 -0
  158. graphica/gui/tools/annotation.py +275 -0
  159. graphica/gui/tools/cursor.py +259 -0
  160. graphica/gui/tools/layout_edit.py +285 -0
  161. graphica/gui/tools/manager.py +78 -0
  162. graphica/gui/tools/peak_placement.py +132 -0
  163. graphica/gui/tools/range_select.py +191 -0
  164. graphica/gui/tools/region_highlight.py +222 -0
  165. graphica/gui/tools/slice_extraction.py +200 -0
  166. graphica/gui/widget_translation.py +46 -0
  167. graphica/gui/workers.py +280 -0
  168. graphica/models/__init__.py +0 -0
  169. graphica/models/project.py +197 -0
  170. graphica/plugin/__init__.py +20 -0
  171. graphica/plugin/testing.py +57 -0
  172. graphica/sample_data/__init__.py +0 -0
  173. graphica/sample_data/cooling_curve_sample.csv +42 -0
  174. graphica/ui_main_window.py +581 -0
  175. graphica_plot-2.0.0.dist-info/METADATA +317 -0
  176. graphica_plot-2.0.0.dist-info/RECORD +181 -0
  177. graphica_plot-2.0.0.dist-info/WHEEL +5 -0
  178. graphica_plot-2.0.0.dist-info/entry_points.txt +2 -0
  179. graphica_plot-2.0.0.dist-info/licenses/LICENSE +30 -0
  180. graphica_plot-2.0.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +91 -0
  181. graphica_plot-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,74 @@
1
+ """データセットに結びつけて図に重ねる表示: 統計値ラベルと拡大図(インセット)。"""
2
+ import numpy as np
3
+ from PySide6.QtWidgets import QDialog
4
+
5
+ from graphica.gui import notify
6
+ from graphica.gui.dialogs import InsetDialog
7
+
8
+ # 表示名 -> 描画側(canvas の _compute_stat_label_text)が解釈するキー
9
+ STAT_LABEL_CHOICES = {
10
+ 'R²': 'r_squared', 'Y平均': 'mean', 'Y標準偏差': 'std',
11
+ 'Y最大値': 'max', 'Y最小値': 'min',
12
+ }
13
+
14
+
15
+ class OverlayController:
16
+ """今のデータセットに結びつけて図に重ねる表示(統計値ラベル、拡大図)を足す。"""
17
+
18
+ def __init__(self, host):
19
+ self._host = host
20
+
21
+ def add_stat_label(self):
22
+ """
23
+ 今のデータセットの統計値(R²・平均など)を、その軸の左上から縦に積んで表示する。
24
+ 値は描画のたびに計算し直すので、後でフィットし直しても追従する。
25
+ """
26
+ dataset = self._host.current_dataset()
27
+ if dataset is None:
28
+ return
29
+
30
+ choice, ok = notify.get_item(
31
+ self._host.parent_widget, "統計値アンカーラベルの追加", "表示する統計値:",
32
+ list(STAT_LABEL_CHOICES.keys()), 0, False
33
+ )
34
+ if not ok:
35
+ return
36
+ stat = STAT_LABEL_CHOICES[choice]
37
+
38
+ axis_index = dataset.subplot_target
39
+ existing_stat_count = sum(
40
+ 1 for ann in self._host.annotations(axis_index) if ann.get('type') == 'stat'
41
+ )
42
+ xy = (0.05, max(0.95 - 0.07 * existing_stat_count, 0.05))
43
+
44
+ self._host.add_annotation(axis_index, {
45
+ 'type': 'stat', 'dataset_id': dataset.dataset_id, 'stat': stat,
46
+ 'xy': xy, 'color': '#000000',
47
+ }, description="統計値アンカーラベルの追加")
48
+
49
+ def add_inset(self):
50
+ """今のデータセットの X 範囲の一部を拡大した小さな図を、その軸に重ねる。"""
51
+ dataset = self._host.current_dataset()
52
+ if dataset is None:
53
+ return
54
+
55
+ x_data = np.asarray(dataset.x_data, dtype=float)
56
+ x_data = x_data[~np.isnan(x_data)]
57
+ if len(x_data) < 2:
58
+ notify.warning(self._host.parent_widget, "インセット(拡大図)", "有効なデータ点が不足しています(最低2点必要)。")
59
+ return
60
+ x_min, x_max = float(np.min(x_data)), float(np.max(x_data))
61
+ span = x_max - x_min
62
+ default_zoom_min = x_min + span * 0.4
63
+ default_zoom_max = x_min + span * 0.6
64
+
65
+ dialog = InsetDialog(x_min, x_max, default_zoom_min, default_zoom_max, parent=self._host.parent_widget)
66
+ if dialog.exec() != QDialog.DialogCode.Accepted:
67
+ return
68
+ settings = dialog.get_settings()
69
+
70
+ axis_index = dataset.subplot_target
71
+ self._host.add_annotation(axis_index, {
72
+ 'type': 'inset', 'corner': settings['corner'], 'size': settings['size'],
73
+ 'zoom_x_range': settings['zoom_x_range'], 'color': '#000000',
74
+ }, description="インセット(拡大図)の追加")
@@ -0,0 +1,21 @@
1
+ """ピーク検出と、ピーク位置への自動ラベルのメニューの入口。本体は gui/datasets/operations/peaks.py。"""
2
+ from graphica.gui.datasets.operations.peaks import PEAK_OPERATIONS, PEAK_RESULT_WINDOW
3
+ from graphica.gui.datasets.operations.runner import OperationState, run_operation
4
+
5
+
6
+ class PeakController:
7
+
8
+ def __init__(self, host):
9
+ self._host = host
10
+ self._state = OperationState()
11
+
12
+ @property
13
+ def result_dialog(self):
14
+ """結果は非モーダルで出し、グラフと見比べられるようにする。"""
15
+ return self._state.result_windows.get(PEAK_RESULT_WINDOW)
16
+
17
+ def find_peaks(self):
18
+ run_operation(PEAK_OPERATIONS["find_peaks"], self._host, self._state)
19
+
20
+ def add_peak_labels(self):
21
+ run_operation(PEAK_OPERATIONS["add_peak_labels"], self._host, self._state)
@@ -0,0 +1,99 @@
1
+ """プラグインが登録したデータ処理と解析の実行。結果の追加は Undo できる。"""
2
+ import logging
3
+ from PySide6.QtWidgets import QDialog
4
+
5
+ from graphica.gui import notify
6
+ from graphica.core.dataset import Dataset
7
+ from graphica.core.plugin_types import AnalysisResult, PluginExecutionError
8
+ from graphica.gui.dialogs import PluginParamDialog, ResultDialog
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class PluginRunController:
14
+ """プラグインのデータ処理(processor)と解析(analyzer)を今のデータセットに対して実行する。"""
15
+
16
+ def __init__(self, host):
17
+ self._host = host
18
+ self.analysis_result_dialog = None
19
+
20
+ def run_processor(self, processor):
21
+ """processor が返したデータセットを Undo できる形で足す(プラグイン側は Undo を意識しない)。"""
22
+ dataset = self._host.current_dataset()
23
+ if dataset is None:
24
+ notify.information(self._host.parent_widget, processor.name, "データセットを選択してください。")
25
+ return
26
+
27
+ params = {}
28
+ if processor.param_schema:
29
+ dialog = PluginParamDialog(processor.name, processor.param_schema, self._host.parent_widget)
30
+ if dialog.exec() != QDialog.DialogCode.Accepted:
31
+ return
32
+ params = dialog.get_values()
33
+
34
+ try:
35
+ new_dataset = processor.fn(dataset, params)
36
+ if not isinstance(new_dataset, Dataset):
37
+ raise TypeError(f"Datasetを返しませんでした(型: {type(new_dataset).__name__})。")
38
+ except Exception as e:
39
+ logger.exception("[plugin:%s] processor の実行に失敗しました", processor.plugin_name)
40
+ notify.critical(
41
+ self._host.parent_widget, "データ処理エラー",
42
+ str(PluginExecutionError(processor.plugin_name, f"「{processor.name}」の実行に失敗しました: {e}"))
43
+ )
44
+ return
45
+
46
+ # どのプラグインが作ったかを残す
47
+ new_dataset.source_plugin = processor.plugin_name
48
+ self._host.add_dataset_with_undo(
49
+ new_dataset, self._host.target_folder_for_new_dataset(),
50
+ description=f"データ処理: {processor.name}"
51
+ )
52
+ self._host.show_status(f"「{new_dataset.name}」を追加しました")
53
+
54
+ def run_analyzer(self, analyzer):
55
+ """analyzer の結果(派生データセット・注釈・表)を、それぞれ Undo できる形や結果の窓で反映する。"""
56
+ dataset = self._host.current_dataset()
57
+ if dataset is None:
58
+ notify.information(self._host.parent_widget, analyzer.name, "データセットを選択してください。")
59
+ return
60
+
61
+ params = {}
62
+ if analyzer.param_schema:
63
+ dialog = PluginParamDialog(analyzer.name, analyzer.param_schema, self._host.parent_widget)
64
+ if dialog.exec() != QDialog.DialogCode.Accepted:
65
+ return
66
+ params = dialog.get_values()
67
+
68
+ try:
69
+ result = analyzer.fn(dataset, params)
70
+ if not isinstance(result, AnalysisResult):
71
+ raise TypeError(f"AnalysisResultを返しませんでした(型: {type(result).__name__})。")
72
+ except Exception as e:
73
+ logger.exception("[plugin:%s] analyzer の実行に失敗しました", analyzer.plugin_name)
74
+ notify.critical(
75
+ self._host.parent_widget, "解析エラー",
76
+ str(PluginExecutionError(analyzer.plugin_name, f"「{analyzer.name}」の実行に失敗しました: {e}"))
77
+ )
78
+ return
79
+
80
+ if result.new_datasets:
81
+ target_folder = self._host.target_folder_for_new_dataset()
82
+ for new_dataset in result.new_datasets:
83
+ new_dataset.source_plugin = analyzer.plugin_name
84
+ self._host.add_dataset_with_undo(
85
+ new_dataset, target_folder, description=f"解析による追加: {analyzer.name}"
86
+ )
87
+
88
+ if result.annotations:
89
+ self._host.add_annotations_to_active_axis(result.annotations, f"解析による注釈追加: {analyzer.name}")
90
+
91
+ if result.table is not None:
92
+ if self.analysis_result_dialog is not None:
93
+ self.analysis_result_dialog.close()
94
+ self.analysis_result_dialog = ResultDialog(
95
+ analyzer.name, f"[{analyzer.name}] の解析結果", self._host.parent_widget, csv_data=result.table
96
+ )
97
+ self.analysis_result_dialog.show()
98
+
99
+ self._host.show_status(f"「{analyzer.name}」を実行しました")
@@ -0,0 +1,68 @@
1
+ """データセットのデータ処理のメニューの入口。本体は gui/datasets/operations/processing.py の操作の表。"""
2
+ from graphica.gui.datasets.operations.processing import (INTEGRAL_RESULT_WINDOW, OUTLIER_RESULT_WINDOW,
3
+ PROCESSING_OPERATIONS)
4
+ from graphica.gui.datasets.operations.runner import OperationState, run_operation
5
+
6
+
7
+ class ProcessingController:
8
+ """現在のデータセット(または選択中の複数)から新しいデータセットを作る、または行を除外する処理。"""
9
+
10
+ def __init__(self, host):
11
+ self._host = host
12
+ self._state = OperationState()
13
+
14
+ @property
15
+ def integral_result_dialog(self):
16
+ return self._state.result_windows.get(INTEGRAL_RESULT_WINDOW)
17
+
18
+ @property
19
+ def outlier_result_dialog(self):
20
+ return self._state.result_windows.get(OUTLIER_RESULT_WINDOW)
21
+
22
+ def _run(self, name):
23
+ run_operation(PROCESSING_OPERATIONS[name], self._host, self._state)
24
+
25
+ def arithmetic(self):
26
+ self._run("arithmetic")
27
+
28
+ def align_selected(self):
29
+ self._run("align")
30
+
31
+ def mean_and_sd_of_selected(self):
32
+ self._run("mean_and_sd")
33
+
34
+ def normalize(self):
35
+ self._run("normalize")
36
+
37
+ def savgol_smooth(self):
38
+ self._run("savgol_smooth")
39
+
40
+ def baseline_correction(self):
41
+ self._run("baseline_correction")
42
+
43
+ def interval_integral(self):
44
+ self._run("interval_integral")
45
+
46
+ def cumulative_integral(self):
47
+ self._run("cumulative_integral")
48
+
49
+ def split_by_column(self):
50
+ self._run("split_by_column")
51
+
52
+ def resample(self):
53
+ self._run("resample")
54
+
55
+ def histogram_or_kde(self):
56
+ self._run("histogram_or_kde")
57
+
58
+ def detect_duplicate_x(self):
59
+ self._run("detect_duplicate_x")
60
+
61
+ def filter_rows(self):
62
+ self._run("filter_rows")
63
+
64
+ def detect_outliers(self):
65
+ self._run("detect_outliers")
66
+
67
+ def batch_column_calculate(self):
68
+ self._run("batch_column_calculate")