ankigammon 1.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.

Potentially problematic release.


This version of ankigammon might be problematic. Click here for more details.

Files changed (56) hide show
  1. ankigammon/__init__.py +7 -0
  2. ankigammon/__main__.py +6 -0
  3. ankigammon/analysis/__init__.py +13 -0
  4. ankigammon/analysis/score_matrix.py +373 -0
  5. ankigammon/anki/__init__.py +6 -0
  6. ankigammon/anki/ankiconnect.py +224 -0
  7. ankigammon/anki/apkg_exporter.py +123 -0
  8. ankigammon/anki/card_generator.py +1307 -0
  9. ankigammon/anki/card_styles.py +1034 -0
  10. ankigammon/gui/__init__.py +8 -0
  11. ankigammon/gui/app.py +209 -0
  12. ankigammon/gui/dialogs/__init__.py +10 -0
  13. ankigammon/gui/dialogs/export_dialog.py +597 -0
  14. ankigammon/gui/dialogs/import_options_dialog.py +163 -0
  15. ankigammon/gui/dialogs/input_dialog.py +776 -0
  16. ankigammon/gui/dialogs/note_dialog.py +93 -0
  17. ankigammon/gui/dialogs/settings_dialog.py +384 -0
  18. ankigammon/gui/format_detector.py +292 -0
  19. ankigammon/gui/main_window.py +1071 -0
  20. ankigammon/gui/resources/icon.icns +0 -0
  21. ankigammon/gui/resources/icon.ico +0 -0
  22. ankigammon/gui/resources/icon.png +0 -0
  23. ankigammon/gui/resources/style.qss +394 -0
  24. ankigammon/gui/resources.py +26 -0
  25. ankigammon/gui/widgets/__init__.py +8 -0
  26. ankigammon/gui/widgets/position_list.py +193 -0
  27. ankigammon/gui/widgets/smart_input.py +268 -0
  28. ankigammon/models.py +322 -0
  29. ankigammon/parsers/__init__.py +7 -0
  30. ankigammon/parsers/gnubg_parser.py +454 -0
  31. ankigammon/parsers/xg_binary_parser.py +870 -0
  32. ankigammon/parsers/xg_text_parser.py +729 -0
  33. ankigammon/renderer/__init__.py +5 -0
  34. ankigammon/renderer/animation_controller.py +406 -0
  35. ankigammon/renderer/animation_helper.py +221 -0
  36. ankigammon/renderer/color_schemes.py +145 -0
  37. ankigammon/renderer/svg_board_renderer.py +824 -0
  38. ankigammon/settings.py +239 -0
  39. ankigammon/thirdparty/__init__.py +7 -0
  40. ankigammon/thirdparty/xgdatatools/__init__.py +17 -0
  41. ankigammon/thirdparty/xgdatatools/xgimport.py +160 -0
  42. ankigammon/thirdparty/xgdatatools/xgstruct.py +1032 -0
  43. ankigammon/thirdparty/xgdatatools/xgutils.py +118 -0
  44. ankigammon/thirdparty/xgdatatools/xgzarc.py +260 -0
  45. ankigammon/utils/__init__.py +13 -0
  46. ankigammon/utils/gnubg_analyzer.py +431 -0
  47. ankigammon/utils/gnuid.py +622 -0
  48. ankigammon/utils/move_parser.py +239 -0
  49. ankigammon/utils/ogid.py +335 -0
  50. ankigammon/utils/xgid.py +419 -0
  51. ankigammon-1.0.0.dist-info/METADATA +370 -0
  52. ankigammon-1.0.0.dist-info/RECORD +56 -0
  53. ankigammon-1.0.0.dist-info/WHEEL +5 -0
  54. ankigammon-1.0.0.dist-info/entry_points.txt +2 -0
  55. ankigammon-1.0.0.dist-info/licenses/LICENSE +21 -0
  56. ankigammon-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,163 @@
1
+ """
2
+ Import options dialog for XG file imports.
3
+ """
4
+
5
+ from typing import Optional
6
+ from PySide6.QtWidgets import (
7
+ QDialog, QVBoxLayout, QFormLayout,
8
+ QCheckBox, QDoubleSpinBox, QGroupBox,
9
+ QLabel, QDialogButtonBox
10
+ )
11
+ from PySide6.QtCore import Qt, Signal
12
+
13
+ from ankigammon.settings import Settings
14
+
15
+
16
+ class ImportOptionsDialog(QDialog):
17
+ """
18
+ Dialog for configuring XG import filtering options.
19
+
20
+ Allows users to filter imported positions by:
21
+ - Error threshold (only import mistakes above this threshold)
22
+ - Player selection (import mistakes from X, O, or both)
23
+
24
+ Signals:
25
+ options_accepted(float, bool, bool): Emitted when user accepts
26
+ Args: (threshold, include_player_x, include_player_o)
27
+ """
28
+
29
+ options_accepted = Signal(float, bool, bool)
30
+
31
+ def __init__(
32
+ self,
33
+ settings: Settings,
34
+ player1_name: Optional[str] = None,
35
+ player2_name: Optional[str] = None,
36
+ parent: Optional[QDialog] = None
37
+ ):
38
+ super().__init__(parent)
39
+ self.settings = settings
40
+ self.player1_name = player1_name or "Player 1"
41
+ self.player2_name = player2_name or "Player 2"
42
+
43
+ self.setWindowTitle("Import Options")
44
+ self.setModal(True)
45
+ self.setMinimumWidth(450)
46
+
47
+ self._setup_ui()
48
+ self._load_settings()
49
+ self._update_ok_button_state()
50
+
51
+ def _setup_ui(self):
52
+ """Initialize the user interface."""
53
+ layout = QVBoxLayout(self)
54
+
55
+ # Error threshold group
56
+ threshold_group = self._create_threshold_group()
57
+ layout.addWidget(threshold_group)
58
+
59
+ # Player selection group
60
+ player_group = self._create_player_group()
61
+ layout.addWidget(player_group)
62
+
63
+ # Dialog buttons
64
+ self.button_box = QDialogButtonBox(
65
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel
66
+ )
67
+ self.button_box.accepted.connect(self.accept)
68
+ self.button_box.rejected.connect(self.reject)
69
+
70
+ # Add cursor pointers to buttons
71
+ for button in self.button_box.buttons():
72
+ button.setCursor(Qt.PointingHandCursor)
73
+
74
+ layout.addWidget(self.button_box)
75
+
76
+ def _create_threshold_group(self) -> QGroupBox:
77
+ """Create error threshold settings group."""
78
+ group = QGroupBox("Error Threshold")
79
+ form = QFormLayout(group)
80
+
81
+ # Threshold spinbox
82
+ self.spin_threshold = QDoubleSpinBox()
83
+ self.spin_threshold.setMinimum(0.000)
84
+ self.spin_threshold.setMaximum(1.000)
85
+ self.spin_threshold.setSingleStep(0.001)
86
+ self.spin_threshold.setDecimals(3)
87
+ self.spin_threshold.setValue(0.080)
88
+ self.spin_threshold.setCursor(Qt.PointingHandCursor)
89
+ form.addRow("Minimum Error:", self.spin_threshold)
90
+
91
+ return group
92
+
93
+ def _create_player_group(self) -> QGroupBox:
94
+ """Create player selection group."""
95
+ group = QGroupBox("Player Selection")
96
+ form = QFormLayout(group)
97
+
98
+ # Player 1 from XG file = Player.O internally (BOTTOM/Black)
99
+ self.chk_player_o = QCheckBox(self.player1_name)
100
+ self.chk_player_o.setCursor(Qt.PointingHandCursor)
101
+ self.chk_player_o.stateChanged.connect(self._update_ok_button_state)
102
+ form.addRow(self.chk_player_o)
103
+
104
+ # Player 2 from XG file = Player.X internally (TOP/White)
105
+ self.chk_player_x = QCheckBox(self.player2_name)
106
+ self.chk_player_x.setCursor(Qt.PointingHandCursor)
107
+ self.chk_player_x.stateChanged.connect(self._update_ok_button_state)
108
+ form.addRow(self.chk_player_x)
109
+
110
+ # Warning label
111
+ self.lbl_warning = QLabel("At least one player must be selected")
112
+ self.lbl_warning.setStyleSheet("color: #f38ba8; font-size: 11px; margin-top: 8px;")
113
+ self.lbl_warning.setVisible(False)
114
+ form.addRow(self.lbl_warning)
115
+
116
+ return group
117
+
118
+ def _load_settings(self):
119
+ """Load current settings into widgets."""
120
+ self.spin_threshold.setValue(self.settings.import_error_threshold)
121
+ self.chk_player_x.setChecked(self.settings.import_include_player_x)
122
+ self.chk_player_o.setChecked(self.settings.import_include_player_o)
123
+
124
+ def _update_ok_button_state(self):
125
+ """Enable/disable OK button based on player selection."""
126
+ at_least_one_selected = (
127
+ self.chk_player_x.isChecked() or self.chk_player_o.isChecked()
128
+ )
129
+
130
+ ok_button = self.button_box.button(QDialogButtonBox.Ok)
131
+ ok_button.setEnabled(at_least_one_selected)
132
+
133
+ # Show/hide warning
134
+ self.lbl_warning.setVisible(not at_least_one_selected)
135
+
136
+ def accept(self):
137
+ """Save settings and emit options."""
138
+ # Update settings
139
+ self.settings.import_error_threshold = self.spin_threshold.value()
140
+ self.settings.import_include_player_x = self.chk_player_x.isChecked()
141
+ self.settings.import_include_player_o = self.chk_player_o.isChecked()
142
+
143
+ # Emit signal with selected options
144
+ self.options_accepted.emit(
145
+ self.spin_threshold.value(),
146
+ self.chk_player_x.isChecked(),
147
+ self.chk_player_o.isChecked()
148
+ )
149
+
150
+ super().accept()
151
+
152
+ def get_options(self) -> tuple[float, bool, bool]:
153
+ """
154
+ Get the selected import options.
155
+
156
+ Returns:
157
+ Tuple of (threshold, include_player_x, include_player_o)
158
+ """
159
+ return (
160
+ self.spin_threshold.value(),
161
+ self.chk_player_x.isChecked(),
162
+ self.chk_player_o.isChecked()
163
+ )