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.
- ankigammon/__init__.py +7 -0
- ankigammon/__main__.py +6 -0
- ankigammon/analysis/__init__.py +13 -0
- ankigammon/analysis/score_matrix.py +373 -0
- ankigammon/anki/__init__.py +6 -0
- ankigammon/anki/ankiconnect.py +224 -0
- ankigammon/anki/apkg_exporter.py +123 -0
- ankigammon/anki/card_generator.py +1307 -0
- ankigammon/anki/card_styles.py +1034 -0
- ankigammon/gui/__init__.py +8 -0
- ankigammon/gui/app.py +209 -0
- ankigammon/gui/dialogs/__init__.py +10 -0
- ankigammon/gui/dialogs/export_dialog.py +597 -0
- ankigammon/gui/dialogs/import_options_dialog.py +163 -0
- ankigammon/gui/dialogs/input_dialog.py +776 -0
- ankigammon/gui/dialogs/note_dialog.py +93 -0
- ankigammon/gui/dialogs/settings_dialog.py +384 -0
- ankigammon/gui/format_detector.py +292 -0
- ankigammon/gui/main_window.py +1071 -0
- ankigammon/gui/resources/icon.icns +0 -0
- ankigammon/gui/resources/icon.ico +0 -0
- ankigammon/gui/resources/icon.png +0 -0
- ankigammon/gui/resources/style.qss +394 -0
- ankigammon/gui/resources.py +26 -0
- ankigammon/gui/widgets/__init__.py +8 -0
- ankigammon/gui/widgets/position_list.py +193 -0
- ankigammon/gui/widgets/smart_input.py +268 -0
- ankigammon/models.py +322 -0
- ankigammon/parsers/__init__.py +7 -0
- ankigammon/parsers/gnubg_parser.py +454 -0
- ankigammon/parsers/xg_binary_parser.py +870 -0
- ankigammon/parsers/xg_text_parser.py +729 -0
- ankigammon/renderer/__init__.py +5 -0
- ankigammon/renderer/animation_controller.py +406 -0
- ankigammon/renderer/animation_helper.py +221 -0
- ankigammon/renderer/color_schemes.py +145 -0
- ankigammon/renderer/svg_board_renderer.py +824 -0
- ankigammon/settings.py +239 -0
- ankigammon/thirdparty/__init__.py +7 -0
- ankigammon/thirdparty/xgdatatools/__init__.py +17 -0
- ankigammon/thirdparty/xgdatatools/xgimport.py +160 -0
- ankigammon/thirdparty/xgdatatools/xgstruct.py +1032 -0
- ankigammon/thirdparty/xgdatatools/xgutils.py +118 -0
- ankigammon/thirdparty/xgdatatools/xgzarc.py +260 -0
- ankigammon/utils/__init__.py +13 -0
- ankigammon/utils/gnubg_analyzer.py +431 -0
- ankigammon/utils/gnuid.py +622 -0
- ankigammon/utils/move_parser.py +239 -0
- ankigammon/utils/ogid.py +335 -0
- ankigammon/utils/xgid.py +419 -0
- ankigammon-1.0.0.dist-info/METADATA +370 -0
- ankigammon-1.0.0.dist-info/RECORD +56 -0
- ankigammon-1.0.0.dist-info/WHEEL +5 -0
- ankigammon-1.0.0.dist-info/entry_points.txt +2 -0
- ankigammon-1.0.0.dist-info/licenses/LICENSE +21 -0
- 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
|
+
)
|