supervertaler 1.9.153__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 supervertaler might be problematic. Click here for more details.
- Supervertaler.py +47886 -0
- modules/__init__.py +10 -0
- modules/ai_actions.py +964 -0
- modules/ai_attachment_manager.py +343 -0
- modules/ai_file_viewer_dialog.py +210 -0
- modules/autofingers_engine.py +466 -0
- modules/cafetran_docx_handler.py +379 -0
- modules/config_manager.py +469 -0
- modules/database_manager.py +1878 -0
- modules/database_migrations.py +417 -0
- modules/dejavurtf_handler.py +779 -0
- modules/document_analyzer.py +427 -0
- modules/docx_handler.py +689 -0
- modules/encoding_repair.py +319 -0
- modules/encoding_repair_Qt.py +393 -0
- modules/encoding_repair_ui.py +481 -0
- modules/feature_manager.py +350 -0
- modules/figure_context_manager.py +340 -0
- modules/file_dialog_helper.py +148 -0
- modules/find_replace.py +164 -0
- modules/find_replace_qt.py +457 -0
- modules/glossary_manager.py +433 -0
- modules/image_extractor.py +188 -0
- modules/keyboard_shortcuts_widget.py +571 -0
- modules/llm_clients.py +1211 -0
- modules/llm_leaderboard.py +737 -0
- modules/llm_superbench_ui.py +1401 -0
- modules/local_llm_setup.py +1104 -0
- modules/model_update_dialog.py +381 -0
- modules/model_version_checker.py +373 -0
- modules/mqxliff_handler.py +638 -0
- modules/non_translatables_manager.py +743 -0
- modules/pdf_rescue_Qt.py +1822 -0
- modules/pdf_rescue_tkinter.py +909 -0
- modules/phrase_docx_handler.py +516 -0
- modules/project_home_panel.py +209 -0
- modules/prompt_assistant.py +357 -0
- modules/prompt_library.py +689 -0
- modules/prompt_library_migration.py +447 -0
- modules/quick_access_sidebar.py +282 -0
- modules/ribbon_widget.py +597 -0
- modules/sdlppx_handler.py +874 -0
- modules/setup_wizard.py +353 -0
- modules/shortcut_manager.py +932 -0
- modules/simple_segmenter.py +128 -0
- modules/spellcheck_manager.py +727 -0
- modules/statuses.py +207 -0
- modules/style_guide_manager.py +315 -0
- modules/superbench_ui.py +1319 -0
- modules/superbrowser.py +329 -0
- modules/supercleaner.py +600 -0
- modules/supercleaner_ui.py +444 -0
- modules/superdocs.py +19 -0
- modules/superdocs_viewer_qt.py +382 -0
- modules/superlookup.py +252 -0
- modules/tag_cleaner.py +260 -0
- modules/tag_manager.py +333 -0
- modules/term_extractor.py +270 -0
- modules/termbase_entry_editor.py +842 -0
- modules/termbase_import_export.py +488 -0
- modules/termbase_manager.py +1060 -0
- modules/termview_widget.py +1172 -0
- modules/theme_manager.py +499 -0
- modules/tm_editor_dialog.py +99 -0
- modules/tm_manager_qt.py +1280 -0
- modules/tm_metadata_manager.py +545 -0
- modules/tmx_editor.py +1461 -0
- modules/tmx_editor_qt.py +2784 -0
- modules/tmx_generator.py +284 -0
- modules/tracked_changes.py +900 -0
- modules/trados_docx_handler.py +430 -0
- modules/translation_memory.py +715 -0
- modules/translation_results_panel.py +2134 -0
- modules/translation_services.py +282 -0
- modules/unified_prompt_library.py +659 -0
- modules/unified_prompt_manager_qt.py +3951 -0
- modules/voice_commands.py +920 -0
- modules/voice_dictation.py +477 -0
- modules/voice_dictation_lite.py +249 -0
- supervertaler-1.9.153.dist-info/METADATA +896 -0
- supervertaler-1.9.153.dist-info/RECORD +85 -0
- supervertaler-1.9.153.dist-info/WHEEL +5 -0
- supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
- supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
- supervertaler-1.9.153.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Supercleaner UI Module for Supervertaler
|
|
3
|
+
========================================
|
|
4
|
+
|
|
5
|
+
Interactive UI for document cleaning with selectable operations.
|
|
6
|
+
Inspired by TransTools Document Cleaner, Unbreaker, and CodeZapper.
|
|
7
|
+
|
|
8
|
+
Author: Michael Beijer / Supervertaler
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from PyQt6.QtWidgets import (
|
|
12
|
+
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QCheckBox,
|
|
13
|
+
QPushButton, QLabel, QFileDialog, QMessageBox, QTextEdit, QScrollArea
|
|
14
|
+
)
|
|
15
|
+
from PyQt6.QtCore import Qt
|
|
16
|
+
from PyQt6.QtGui import QFont
|
|
17
|
+
import os
|
|
18
|
+
from typing import Optional, Dict
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SupercleanerUI(QWidget):
|
|
22
|
+
"""Interactive UI for document cleaning with selectable operations"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, parent=None):
|
|
25
|
+
super().__init__(parent)
|
|
26
|
+
self.parent_window = parent
|
|
27
|
+
self.init_ui()
|
|
28
|
+
|
|
29
|
+
def init_ui(self):
|
|
30
|
+
"""Initialize the user interface"""
|
|
31
|
+
main_layout = QVBoxLayout(self)
|
|
32
|
+
main_layout.setContentsMargins(10, 10, 10, 10)
|
|
33
|
+
main_layout.setSpacing(10)
|
|
34
|
+
|
|
35
|
+
# === FILE SELECTION ===
|
|
36
|
+
file_group = QGroupBox("📄 Document Selection")
|
|
37
|
+
file_layout = QVBoxLayout()
|
|
38
|
+
|
|
39
|
+
# File path display and browse button
|
|
40
|
+
file_row = QHBoxLayout()
|
|
41
|
+
self.file_label = QLabel("No file selected")
|
|
42
|
+
self.file_label.setStyleSheet("color: #666; padding: 5px; background-color: #f0f0f0; border-radius: 3px;")
|
|
43
|
+
browse_btn = QPushButton("📂 Browse...")
|
|
44
|
+
browse_btn.clicked.connect(self.browse_file)
|
|
45
|
+
browse_btn.setMaximumWidth(150)
|
|
46
|
+
|
|
47
|
+
file_row.addWidget(self.file_label, 1)
|
|
48
|
+
file_row.addWidget(browse_btn, 0)
|
|
49
|
+
file_layout.addLayout(file_row)
|
|
50
|
+
|
|
51
|
+
file_group.setLayout(file_layout)
|
|
52
|
+
main_layout.addWidget(file_group)
|
|
53
|
+
|
|
54
|
+
# === CLEANING OPTIONS ===
|
|
55
|
+
# Create scrollable area for options
|
|
56
|
+
scroll_area = QScrollArea()
|
|
57
|
+
scroll_area.setWidgetResizable(True)
|
|
58
|
+
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
59
|
+
|
|
60
|
+
options_container = QWidget()
|
|
61
|
+
options_layout = QVBoxLayout(options_container)
|
|
62
|
+
|
|
63
|
+
# Document Cleaner Operations
|
|
64
|
+
cleaner_group = QGroupBox("🧹 Document Cleaner Operations")
|
|
65
|
+
cleaner_layout = QVBoxLayout()
|
|
66
|
+
|
|
67
|
+
self.cb_remove_shading = CheckmarkCheckBox("Remove text shading")
|
|
68
|
+
self.cb_remove_highlighting = CheckmarkCheckBox("Remove text highlighting")
|
|
69
|
+
self.cb_font_color_automatic = CheckmarkCheckBox("Change font color from Black to Automatic")
|
|
70
|
+
self.cb_normalize_font_color = CheckmarkCheckBox("Normalize font color in each paragraph")
|
|
71
|
+
self.cb_normalize_font_size = CheckmarkCheckBox("Normalize font size in each paragraph")
|
|
72
|
+
self.cb_normalize_font = CheckmarkCheckBox("Normalize font in each paragraph")
|
|
73
|
+
self.cb_set_default_spacing = CheckmarkCheckBox("Set default text spacing")
|
|
74
|
+
self.cb_remove_hyphens = CheckmarkCheckBox("Remove manual hyphens")
|
|
75
|
+
self.cb_replace_symbols = CheckmarkCheckBox("Replace special symbols (non-breaking spaces, ellipsis)")
|
|
76
|
+
self.cb_replace_symbols.setToolTip("Replaces non-breaking spaces and ellipsis characters that can cause TM matching issues")
|
|
77
|
+
self.cb_simplify_quotes = CheckmarkCheckBox("Simplify quotes & dashes to ASCII (optional)")
|
|
78
|
+
self.cb_simplify_quotes.setToolTip("Convert curly quotes ("") and em-dashes (—) to straight quotes (\") and hyphens (-)")
|
|
79
|
+
self.cb_remove_styles = CheckmarkCheckBox("Remove character styles (aggressive)")
|
|
80
|
+
|
|
81
|
+
# Set defaults (checked)
|
|
82
|
+
for cb in [self.cb_remove_shading, self.cb_remove_highlighting,
|
|
83
|
+
self.cb_font_color_automatic, self.cb_normalize_font_color,
|
|
84
|
+
self.cb_normalize_font_size, self.cb_normalize_font,
|
|
85
|
+
self.cb_set_default_spacing, self.cb_remove_hyphens,
|
|
86
|
+
self.cb_replace_symbols]:
|
|
87
|
+
cb.setChecked(True)
|
|
88
|
+
cleaner_layout.addWidget(cb)
|
|
89
|
+
|
|
90
|
+
# Optional operations unchecked by default
|
|
91
|
+
cleaner_layout.addWidget(self.cb_simplify_quotes) # New optional operation
|
|
92
|
+
cleaner_layout.addWidget(self.cb_remove_styles)
|
|
93
|
+
|
|
94
|
+
cleaner_group.setLayout(cleaner_layout)
|
|
95
|
+
options_layout.addWidget(cleaner_group)
|
|
96
|
+
|
|
97
|
+
# Unbreaker Operations
|
|
98
|
+
unbreaker_group = QGroupBox("🔗 Unbreaker Operations")
|
|
99
|
+
unbreaker_layout = QVBoxLayout()
|
|
100
|
+
|
|
101
|
+
self.cb_fix_line_breaks = CheckmarkCheckBox("Fix incorrect line breaks (within paragraphs)")
|
|
102
|
+
self.cb_join_sentences = CheckmarkCheckBox("Join broken sentences (across paragraphs) - EXPERIMENTAL")
|
|
103
|
+
|
|
104
|
+
self.cb_fix_line_breaks.setChecked(True)
|
|
105
|
+
# join_sentences disabled by default due to word spacing issues
|
|
106
|
+
self.cb_join_sentences.setChecked(False)
|
|
107
|
+
self.cb_join_sentences.setStyleSheet("color: #d32f2f;")
|
|
108
|
+
self.cb_join_sentences.setToolTip(
|
|
109
|
+
"⚠️ WARNING: This feature is experimental and may cause words to stick together.\n"
|
|
110
|
+
"Only enable if you need to fix severely broken paragraph structure."
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
unbreaker_layout.addWidget(self.cb_fix_line_breaks)
|
|
114
|
+
unbreaker_layout.addWidget(self.cb_join_sentences)
|
|
115
|
+
|
|
116
|
+
unbreaker_group.setLayout(unbreaker_layout)
|
|
117
|
+
options_layout.addWidget(unbreaker_group)
|
|
118
|
+
|
|
119
|
+
# Space Removal Operations
|
|
120
|
+
spaces_group = QGroupBox("␣ Remove Excessive Spaces")
|
|
121
|
+
spaces_layout = QVBoxLayout()
|
|
122
|
+
|
|
123
|
+
self.cb_remove_spaces = CheckmarkCheckBox("Remove excessive spaces (2+ spaces become 1)")
|
|
124
|
+
self.cb_remove_spaces.setChecked(True)
|
|
125
|
+
|
|
126
|
+
spaces_layout.addWidget(self.cb_remove_spaces)
|
|
127
|
+
spaces_group.setLayout(spaces_layout)
|
|
128
|
+
options_layout.addWidget(spaces_group)
|
|
129
|
+
|
|
130
|
+
# Quick presets
|
|
131
|
+
presets_layout = QHBoxLayout()
|
|
132
|
+
quick_clean_btn = QPushButton("✨ Quick Clean (Recommended)")
|
|
133
|
+
quick_clean_btn.clicked.connect(self.apply_quick_clean_preset)
|
|
134
|
+
quick_clean_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 5px;")
|
|
135
|
+
|
|
136
|
+
aggressive_btn = QPushButton("⚡ Aggressive Clean")
|
|
137
|
+
aggressive_btn.clicked.connect(self.apply_aggressive_preset)
|
|
138
|
+
aggressive_btn.setStyleSheet("background-color: #FF9800; color: white; font-weight: bold; padding: 5px;")
|
|
139
|
+
|
|
140
|
+
clear_all_btn = QPushButton("❌ Clear All")
|
|
141
|
+
clear_all_btn.clicked.connect(self.clear_all_options)
|
|
142
|
+
|
|
143
|
+
presets_layout.addWidget(quick_clean_btn)
|
|
144
|
+
presets_layout.addWidget(aggressive_btn)
|
|
145
|
+
presets_layout.addWidget(clear_all_btn)
|
|
146
|
+
options_layout.addLayout(presets_layout)
|
|
147
|
+
|
|
148
|
+
scroll_area.setWidget(options_container)
|
|
149
|
+
main_layout.addWidget(scroll_area, 1)
|
|
150
|
+
|
|
151
|
+
# === CLEAN BUTTON ===
|
|
152
|
+
clean_btn = QPushButton("🧹 Clean Document")
|
|
153
|
+
clean_btn.clicked.connect(self.clean_document)
|
|
154
|
+
clean_btn.setStyleSheet(
|
|
155
|
+
"background-color: #2196F3; color: white; font-weight: bold; "
|
|
156
|
+
"font-size: 14pt; padding: 10px; border-radius: 5px;"
|
|
157
|
+
)
|
|
158
|
+
main_layout.addWidget(clean_btn)
|
|
159
|
+
|
|
160
|
+
# === RESULTS LOG ===
|
|
161
|
+
log_group = QGroupBox("📊 Cleaning Results")
|
|
162
|
+
log_layout = QVBoxLayout()
|
|
163
|
+
|
|
164
|
+
self.log_text = QTextEdit()
|
|
165
|
+
self.log_text.setReadOnly(True)
|
|
166
|
+
self.log_text.setMaximumHeight(150)
|
|
167
|
+
self.log_text.setStyleSheet("background-color: #f5f5f5; font-family: Consolas, monospace;")
|
|
168
|
+
|
|
169
|
+
log_layout.addWidget(self.log_text)
|
|
170
|
+
log_group.setLayout(log_layout)
|
|
171
|
+
main_layout.addWidget(log_group)
|
|
172
|
+
|
|
173
|
+
# Initial message
|
|
174
|
+
self.log("👋 Welcome to Supercleaner! Select a DOCX file and choose cleaning operations.")
|
|
175
|
+
self.log("💡 Tip: Use 'Quick Clean' preset for most OCR/PDF documents.")
|
|
176
|
+
|
|
177
|
+
def browse_file(self):
|
|
178
|
+
"""Open file browser to select DOCX file"""
|
|
179
|
+
file_path, _ = QFileDialog.getOpenFileName(
|
|
180
|
+
self,
|
|
181
|
+
"Select DOCX File to Clean",
|
|
182
|
+
"",
|
|
183
|
+
"Word Documents (*.docx);;All Files (*.*)"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if file_path:
|
|
187
|
+
self.file_label.setText(file_path)
|
|
188
|
+
self.log(f"📄 Selected: {os.path.basename(file_path)}")
|
|
189
|
+
|
|
190
|
+
def apply_quick_clean_preset(self):
|
|
191
|
+
"""Apply recommended quick clean settings"""
|
|
192
|
+
# Document Cleaner
|
|
193
|
+
self.cb_remove_shading.setChecked(True)
|
|
194
|
+
self.cb_remove_highlighting.setChecked(True)
|
|
195
|
+
self.cb_font_color_automatic.setChecked(True)
|
|
196
|
+
self.cb_normalize_font_color.setChecked(True)
|
|
197
|
+
self.cb_normalize_font_size.setChecked(True)
|
|
198
|
+
self.cb_normalize_font.setChecked(True)
|
|
199
|
+
self.cb_set_default_spacing.setChecked(True)
|
|
200
|
+
self.cb_remove_hyphens.setChecked(True)
|
|
201
|
+
self.cb_replace_symbols.setChecked(True)
|
|
202
|
+
self.cb_simplify_quotes.setChecked(False) # Optional - preserves curly quotes by default
|
|
203
|
+
self.cb_remove_styles.setChecked(False)
|
|
204
|
+
|
|
205
|
+
# Unbreaker
|
|
206
|
+
self.cb_fix_line_breaks.setChecked(True)
|
|
207
|
+
self.cb_join_sentences.setChecked(False) # Disabled due to spacing issues
|
|
208
|
+
|
|
209
|
+
# Spaces
|
|
210
|
+
self.cb_remove_spaces.setChecked(True)
|
|
211
|
+
|
|
212
|
+
self.log("✨ Applied 'Quick Clean' preset (recommended for OCR/PDF documents)")
|
|
213
|
+
|
|
214
|
+
def apply_aggressive_preset(self):
|
|
215
|
+
"""Apply aggressive cleaning settings"""
|
|
216
|
+
# Check everything
|
|
217
|
+
for cb in [self.cb_remove_shading, self.cb_remove_highlighting,
|
|
218
|
+
self.cb_font_color_automatic, self.cb_normalize_font_color,
|
|
219
|
+
self.cb_normalize_font_size, self.cb_normalize_font,
|
|
220
|
+
self.cb_set_default_spacing, self.cb_remove_hyphens,
|
|
221
|
+
self.cb_replace_symbols, self.cb_simplify_quotes,
|
|
222
|
+
self.cb_remove_styles, self.cb_fix_line_breaks,
|
|
223
|
+
self.cb_remove_spaces]:
|
|
224
|
+
cb.setChecked(True)
|
|
225
|
+
|
|
226
|
+
# Keep sentence joining disabled (known issues)
|
|
227
|
+
self.cb_join_sentences.setChecked(False)
|
|
228
|
+
|
|
229
|
+
self.log("⚡ Applied 'Aggressive Clean' preset")
|
|
230
|
+
self.log("⚠️ Note: 'Join broken sentences' remains disabled due to known spacing issues")
|
|
231
|
+
|
|
232
|
+
def clear_all_options(self):
|
|
233
|
+
"""Clear all cleaning options"""
|
|
234
|
+
for cb in [self.cb_remove_shading, self.cb_remove_highlighting,
|
|
235
|
+
self.cb_font_color_automatic, self.cb_normalize_font_color,
|
|
236
|
+
self.cb_normalize_font_size, self.cb_normalize_font,
|
|
237
|
+
self.cb_set_default_spacing, self.cb_remove_hyphens,
|
|
238
|
+
self.cb_replace_symbols, self.cb_simplify_quotes,
|
|
239
|
+
self.cb_remove_styles, self.cb_fix_line_breaks,
|
|
240
|
+
self.cb_join_sentences, self.cb_remove_spaces]:
|
|
241
|
+
cb.setChecked(False)
|
|
242
|
+
|
|
243
|
+
self.log("❌ Cleared all cleaning options")
|
|
244
|
+
|
|
245
|
+
def get_selected_operations(self) -> Dict[str, bool]:
|
|
246
|
+
"""Get dictionary of selected cleaning operations"""
|
|
247
|
+
return {
|
|
248
|
+
'remove_text_shading': self.cb_remove_shading.isChecked(),
|
|
249
|
+
'remove_highlighting': self.cb_remove_highlighting.isChecked(),
|
|
250
|
+
'font_color_to_automatic': self.cb_font_color_automatic.isChecked(),
|
|
251
|
+
'normalize_font_color': self.cb_normalize_font_color.isChecked(),
|
|
252
|
+
'normalize_font_size': self.cb_normalize_font_size.isChecked(),
|
|
253
|
+
'normalize_font': self.cb_normalize_font.isChecked(),
|
|
254
|
+
'set_default_spacing': self.cb_set_default_spacing.isChecked(),
|
|
255
|
+
'remove_manual_hyphens': self.cb_remove_hyphens.isChecked(),
|
|
256
|
+
'replace_special_symbols': self.cb_replace_symbols.isChecked(),
|
|
257
|
+
'simplify_quotes_and_dashes': self.cb_simplify_quotes.isChecked(),
|
|
258
|
+
'remove_character_styles': self.cb_remove_styles.isChecked(),
|
|
259
|
+
'fix_line_breaks': self.cb_fix_line_breaks.isChecked(),
|
|
260
|
+
'join_broken_sentences': self.cb_join_sentences.isChecked(),
|
|
261
|
+
'remove_excessive_spaces': self.cb_remove_spaces.isChecked(),
|
|
262
|
+
'accept_tracked_changes': False, # Not yet implemented
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
def clean_document(self):
|
|
266
|
+
"""Perform document cleaning with selected operations"""
|
|
267
|
+
# Validate file selection
|
|
268
|
+
file_path = self.file_label.text()
|
|
269
|
+
if file_path == "No file selected" or not os.path.exists(file_path):
|
|
270
|
+
QMessageBox.warning(
|
|
271
|
+
self,
|
|
272
|
+
"No File Selected",
|
|
273
|
+
"Please select a DOCX file to clean."
|
|
274
|
+
)
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
# Get selected operations
|
|
278
|
+
operations = self.get_selected_operations()
|
|
279
|
+
|
|
280
|
+
# Check if any operations are selected
|
|
281
|
+
if not any(operations.values()):
|
|
282
|
+
QMessageBox.warning(
|
|
283
|
+
self,
|
|
284
|
+
"No Operations Selected",
|
|
285
|
+
"Please select at least one cleaning operation."
|
|
286
|
+
)
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
# Ask for output file
|
|
290
|
+
default_output = file_path.replace('.docx', '_cleaned.docx')
|
|
291
|
+
output_path, _ = QFileDialog.getSaveFileName(
|
|
292
|
+
self,
|
|
293
|
+
"Save Cleaned Document As",
|
|
294
|
+
default_output,
|
|
295
|
+
"Word Documents (*.docx);;All Files (*.*)"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
if not output_path:
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
# Perform cleaning
|
|
302
|
+
self.log("━" * 60)
|
|
303
|
+
self.log(f"🧹 Starting Supercleaner...")
|
|
304
|
+
self.log(f"📄 Input: {os.path.basename(file_path)}")
|
|
305
|
+
self.log(f"💾 Output: {os.path.basename(output_path)}")
|
|
306
|
+
self.log("")
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
from modules.supercleaner import DocumentCleaner
|
|
310
|
+
|
|
311
|
+
cleaner = DocumentCleaner()
|
|
312
|
+
stats = cleaner.clean_document(file_path, output_path, operations)
|
|
313
|
+
|
|
314
|
+
self.log("✅ Cleaning Complete!")
|
|
315
|
+
self.log(f" 📊 Paragraphs processed: {stats['paragraphs_processed']}")
|
|
316
|
+
self.log(f" 🔧 Total changes made: {stats['changes_made']}")
|
|
317
|
+
self.log("")
|
|
318
|
+
|
|
319
|
+
if stats.get('operations'):
|
|
320
|
+
self.log(" 📝 Operations performed:")
|
|
321
|
+
for op in stats['operations']:
|
|
322
|
+
self.log(f" • {op}")
|
|
323
|
+
|
|
324
|
+
self.log("")
|
|
325
|
+
self.log(f"✅ Saved to: {output_path}")
|
|
326
|
+
self.log("━" * 60)
|
|
327
|
+
|
|
328
|
+
QMessageBox.information(
|
|
329
|
+
self,
|
|
330
|
+
"Success",
|
|
331
|
+
f"Document cleaned successfully!\n\n"
|
|
332
|
+
f"Paragraphs processed: {stats['paragraphs_processed']}\n"
|
|
333
|
+
f"Changes made: {stats['changes_made']}\n\n"
|
|
334
|
+
f"Saved to:\n{output_path}"
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
except Exception as e:
|
|
338
|
+
self.log(f"❌ Error: {str(e)}")
|
|
339
|
+
self.log("━" * 60)
|
|
340
|
+
QMessageBox.critical(
|
|
341
|
+
self,
|
|
342
|
+
"Error",
|
|
343
|
+
f"Failed to clean document:\n\n{str(e)}"
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
def log(self, message: str):
|
|
347
|
+
"""Add message to log"""
|
|
348
|
+
self.log_text.append(message)
|
|
349
|
+
# Scroll to bottom
|
|
350
|
+
scrollbar = self.log_text.verticalScrollBar()
|
|
351
|
+
scrollbar.setValue(scrollbar.maximum())
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class CheckmarkCheckBox(QCheckBox):
|
|
355
|
+
"""Custom checkbox with green background and white checkmark when checked"""
|
|
356
|
+
|
|
357
|
+
def __init__(self, text="", parent=None):
|
|
358
|
+
super().__init__(text, parent)
|
|
359
|
+
self.setCheckable(True)
|
|
360
|
+
self.setEnabled(True)
|
|
361
|
+
self.setStyleSheet("""
|
|
362
|
+
QCheckBox {
|
|
363
|
+
font-size: 9pt;
|
|
364
|
+
spacing: 6px;
|
|
365
|
+
}
|
|
366
|
+
QCheckBox::indicator {
|
|
367
|
+
width: 16px;
|
|
368
|
+
height: 16px;
|
|
369
|
+
border: 2px solid #999;
|
|
370
|
+
border-radius: 3px;
|
|
371
|
+
background-color: white;
|
|
372
|
+
}
|
|
373
|
+
QCheckBox::indicator:checked {
|
|
374
|
+
background-color: #4CAF50;
|
|
375
|
+
border-color: #4CAF50;
|
|
376
|
+
}
|
|
377
|
+
QCheckBox::indicator:hover {
|
|
378
|
+
border-color: #666;
|
|
379
|
+
}
|
|
380
|
+
QCheckBox::indicator:checked:hover {
|
|
381
|
+
background-color: #45a049;
|
|
382
|
+
border-color: #45a049;
|
|
383
|
+
}
|
|
384
|
+
""")
|
|
385
|
+
|
|
386
|
+
def paintEvent(self, event):
|
|
387
|
+
"""Override paint event to draw white checkmark when checked"""
|
|
388
|
+
super().paintEvent(event)
|
|
389
|
+
|
|
390
|
+
if self.isChecked():
|
|
391
|
+
from PyQt6.QtWidgets import QStyleOptionButton
|
|
392
|
+
from PyQt6.QtGui import QPainter, QPen, QColor
|
|
393
|
+
from PyQt6.QtCore import QPointF, Qt
|
|
394
|
+
|
|
395
|
+
opt = QStyleOptionButton()
|
|
396
|
+
self.initStyleOption(opt)
|
|
397
|
+
indicator_rect = self.style().subElementRect(
|
|
398
|
+
self.style().SubElement.SE_CheckBoxIndicator,
|
|
399
|
+
opt,
|
|
400
|
+
self
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
if indicator_rect.isValid():
|
|
404
|
+
painter = QPainter(self)
|
|
405
|
+
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
406
|
+
pen_width = max(2.0, min(indicator_rect.width(), indicator_rect.height()) * 0.12)
|
|
407
|
+
painter.setPen(QPen(QColor(255, 255, 255), pen_width, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))
|
|
408
|
+
painter.setBrush(QColor(255, 255, 255))
|
|
409
|
+
|
|
410
|
+
x = indicator_rect.x()
|
|
411
|
+
y = indicator_rect.y()
|
|
412
|
+
w = indicator_rect.width()
|
|
413
|
+
h = indicator_rect.height()
|
|
414
|
+
|
|
415
|
+
padding = min(w, h) * 0.15
|
|
416
|
+
x += padding
|
|
417
|
+
y += padding
|
|
418
|
+
w -= padding * 2
|
|
419
|
+
h -= padding * 2
|
|
420
|
+
|
|
421
|
+
check_x1 = x + w * 0.10
|
|
422
|
+
check_y1 = y + h * 0.50
|
|
423
|
+
check_x2 = x + w * 0.35
|
|
424
|
+
check_y2 = y + h * 0.70
|
|
425
|
+
check_x3 = x + w * 0.90
|
|
426
|
+
check_y3 = y + h * 0.25
|
|
427
|
+
|
|
428
|
+
painter.drawLine(QPointF(check_x2, check_y2), QPointF(check_x3, check_y3))
|
|
429
|
+
painter.drawLine(QPointF(check_x1, check_y1), QPointF(check_x2, check_y2))
|
|
430
|
+
|
|
431
|
+
painter.end()
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
if __name__ == "__main__":
|
|
435
|
+
"""Standalone mode for testing"""
|
|
436
|
+
import sys
|
|
437
|
+
from PyQt6.QtWidgets import QApplication
|
|
438
|
+
|
|
439
|
+
app = QApplication(sys.argv)
|
|
440
|
+
window = SupercleanerUI()
|
|
441
|
+
window.setWindowTitle("Supercleaner - Document Cleaner")
|
|
442
|
+
window.resize(800, 900)
|
|
443
|
+
window.show()
|
|
444
|
+
sys.exit(app.exec())
|
modules/superdocs.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""modules.superdocs (deprecated)
|
|
2
|
+
|
|
3
|
+
The in-app Superdocs generator and viewer were removed in favor of the online GitBook
|
|
4
|
+
documentation:
|
|
5
|
+
|
|
6
|
+
https://supervertaler.gitbook.io/superdocs/
|
|
7
|
+
|
|
8
|
+
This module remains as a tiny shim to prevent accidental imports in older code paths.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def __getattr__(name: str):
|
|
13
|
+
raise ImportError(
|
|
14
|
+
"The 'modules.superdocs' module has been removed. Use the online Superdocs at "
|
|
15
|
+
"https://supervertaler.gitbook.io/superdocs/"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__: list[str] = []
|