supervertaler 1.9.185__py3-none-any.whl → 1.9.189__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 +288 -69
- modules/superbrowser.py +22 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.189.dist-info}/METADATA +1 -1
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.189.dist-info}/RECORD +8 -8
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.189.dist-info}/WHEEL +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.189.dist-info}/entry_points.txt +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.189.dist-info}/licenses/LICENSE +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.189.dist-info}/top_level.txt +0 -0
Supervertaler.py
CHANGED
|
@@ -32,7 +32,7 @@ License: MIT
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
# Version Information.
|
|
35
|
-
__version__ = "1.9.
|
|
35
|
+
__version__ = "1.9.189"
|
|
36
36
|
__phase__ = "0.9"
|
|
37
37
|
__release_date__ = "2026-02-01"
|
|
38
38
|
__edition__ = "Qt"
|
|
@@ -20348,7 +20348,68 @@ class SupervertalerQt(QMainWindow):
|
|
|
20348
20348
|
advanced_filter_btn.clicked.connect(self.show_advanced_filters_dialog)
|
|
20349
20349
|
advanced_filter_btn.setMaximumWidth(160)
|
|
20350
20350
|
advanced_filter_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold;")
|
|
20351
|
-
|
|
20351
|
+
|
|
20352
|
+
# Sort dropdown button (similar to memoQ)
|
|
20353
|
+
sort_btn = QPushButton("⇅ Sort")
|
|
20354
|
+
sort_btn.setMaximumWidth(100)
|
|
20355
|
+
sort_btn.setStyleSheet("background-color: #FF9800; color: white; font-weight: bold;")
|
|
20356
|
+
sort_menu = QMenu(self)
|
|
20357
|
+
|
|
20358
|
+
# Initialize sort state if not exists
|
|
20359
|
+
if not hasattr(self, 'current_sort'):
|
|
20360
|
+
self.current_sort = None # None = document order
|
|
20361
|
+
|
|
20362
|
+
# Sort by source text
|
|
20363
|
+
sort_menu.addAction("📝 Source A → Z", lambda: self.apply_sort('source_asc'))
|
|
20364
|
+
sort_menu.addAction("📝 Source Z → A", lambda: self.apply_sort('source_desc'))
|
|
20365
|
+
|
|
20366
|
+
sort_menu.addSeparator()
|
|
20367
|
+
|
|
20368
|
+
# Sort by target text
|
|
20369
|
+
sort_menu.addAction("📄 Target A → Z", lambda: self.apply_sort('target_asc'))
|
|
20370
|
+
sort_menu.addAction("📄 Target Z → A", lambda: self.apply_sort('target_desc'))
|
|
20371
|
+
|
|
20372
|
+
sort_menu.addSeparator()
|
|
20373
|
+
|
|
20374
|
+
# Sort by text length
|
|
20375
|
+
sort_menu.addAction("📏 Source (longer first)", lambda: self.apply_sort('source_length_desc'))
|
|
20376
|
+
sort_menu.addAction("📏 Source (shorter first)", lambda: self.apply_sort('source_length_asc'))
|
|
20377
|
+
sort_menu.addAction("📏 Target (longer first)", lambda: self.apply_sort('target_length_desc'))
|
|
20378
|
+
sort_menu.addAction("📏 Target (shorter first)", lambda: self.apply_sort('target_length_asc'))
|
|
20379
|
+
|
|
20380
|
+
sort_menu.addSeparator()
|
|
20381
|
+
|
|
20382
|
+
# Sort by match rate
|
|
20383
|
+
sort_menu.addAction("🎯 Match Rate (higher first)", lambda: self.apply_sort('match_desc'))
|
|
20384
|
+
sort_menu.addAction("🎯 Match Rate (lower first)", lambda: self.apply_sort('match_asc'))
|
|
20385
|
+
|
|
20386
|
+
sort_menu.addSeparator()
|
|
20387
|
+
|
|
20388
|
+
# Sort by frequency
|
|
20389
|
+
sort_menu.addAction("📊 Source Frequency (higher first)", lambda: self.apply_sort('source_freq_desc'))
|
|
20390
|
+
sort_menu.addAction("📊 Source Frequency (lower first)", lambda: self.apply_sort('source_freq_asc'))
|
|
20391
|
+
sort_menu.addAction("📊 Target Frequency (higher first)", lambda: self.apply_sort('target_freq_desc'))
|
|
20392
|
+
sort_menu.addAction("📊 Target Frequency (lower first)", lambda: self.apply_sort('target_freq_asc'))
|
|
20393
|
+
|
|
20394
|
+
sort_menu.addSeparator()
|
|
20395
|
+
|
|
20396
|
+
# Sort by last changed
|
|
20397
|
+
sort_menu.addAction("🕒 Last Changed (newest first)", lambda: self.apply_sort('modified_desc'))
|
|
20398
|
+
sort_menu.addAction("🕒 Last Changed (oldest first)", lambda: self.apply_sort('modified_asc'))
|
|
20399
|
+
|
|
20400
|
+
sort_menu.addSeparator()
|
|
20401
|
+
|
|
20402
|
+
# Sort by row status
|
|
20403
|
+
sort_menu.addAction("🚦 Row Status", lambda: self.apply_sort('status'))
|
|
20404
|
+
|
|
20405
|
+
sort_menu.addSeparator()
|
|
20406
|
+
|
|
20407
|
+
# Reset to document order
|
|
20408
|
+
sort_menu.addAction("↩️ Document Order (default)", lambda: self.apply_sort(None))
|
|
20409
|
+
|
|
20410
|
+
sort_btn.setMenu(sort_menu)
|
|
20411
|
+
sort_btn.setToolTip("Sort segments by various criteria")
|
|
20412
|
+
|
|
20352
20413
|
# File filter dropdown (for multi-file projects)
|
|
20353
20414
|
self.file_filter_combo = QComboBox()
|
|
20354
20415
|
self.file_filter_combo.setMinimumWidth(150)
|
|
@@ -20442,6 +20503,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20442
20503
|
filter_layout.addWidget(clear_filters_btn)
|
|
20443
20504
|
filter_layout.addWidget(quick_filter_btn)
|
|
20444
20505
|
filter_layout.addWidget(advanced_filter_btn)
|
|
20506
|
+
filter_layout.addWidget(sort_btn) # Sort dropdown
|
|
20445
20507
|
filter_layout.addWidget(self.file_filter_combo) # File filter for multi-file projects
|
|
20446
20508
|
filter_layout.addWidget(show_invisibles_btn_home)
|
|
20447
20509
|
filter_layout.addWidget(self.spellcheck_btn)
|
|
@@ -20565,30 +20627,80 @@ class SupervertalerQt(QMainWindow):
|
|
|
20565
20627
|
tab_seg_info.setStyleSheet("font-weight: bold;")
|
|
20566
20628
|
toolbar_layout.addWidget(tab_seg_info)
|
|
20567
20629
|
|
|
20568
|
-
#
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20630
|
+
# View mode segmented control (WYSIWYG / Tags)
|
|
20631
|
+
from PyQt6.QtWidgets import QButtonGroup
|
|
20632
|
+
|
|
20633
|
+
view_mode_container = QWidget()
|
|
20634
|
+
view_mode_layout = QHBoxLayout(view_mode_container)
|
|
20635
|
+
view_mode_layout.setContentsMargins(0, 0, 0, 0)
|
|
20636
|
+
view_mode_layout.setSpacing(0)
|
|
20637
|
+
|
|
20638
|
+
# Create button group to ensure only one is checked
|
|
20639
|
+
view_mode_group = QButtonGroup(self)
|
|
20640
|
+
view_mode_group.setExclusive(True)
|
|
20641
|
+
|
|
20642
|
+
# WYSIWYG button (left)
|
|
20643
|
+
wysiwyg_btn = QPushButton("WYSIWYG")
|
|
20644
|
+
wysiwyg_btn.setCheckable(True)
|
|
20645
|
+
wysiwyg_btn.setChecked(False)
|
|
20646
|
+
wysiwyg_btn.setToolTip("WYSIWYG View (Ctrl+Alt+T)\nShows formatted text without raw tags")
|
|
20647
|
+
wysiwyg_btn.setStyleSheet("""
|
|
20573
20648
|
QPushButton {
|
|
20574
20649
|
background-color: #757575;
|
|
20575
20650
|
color: white;
|
|
20576
20651
|
font-weight: bold;
|
|
20577
|
-
padding: 4px
|
|
20578
|
-
border
|
|
20652
|
+
padding: 4px 12px;
|
|
20653
|
+
border: none;
|
|
20654
|
+
border-top-left-radius: 3px;
|
|
20655
|
+
border-bottom-left-radius: 3px;
|
|
20579
20656
|
}
|
|
20580
20657
|
QPushButton:checked {
|
|
20581
20658
|
background-color: #9C27B0;
|
|
20582
20659
|
}
|
|
20660
|
+
QPushButton:hover:!checked {
|
|
20661
|
+
background-color: #858585;
|
|
20662
|
+
}
|
|
20583
20663
|
""")
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
20587
|
-
|
|
20664
|
+
wysiwyg_btn.clicked.connect(lambda: self.toggle_tag_view(False, None))
|
|
20665
|
+
view_mode_group.addButton(wysiwyg_btn, 0)
|
|
20666
|
+
view_mode_layout.addWidget(wysiwyg_btn)
|
|
20667
|
+
|
|
20668
|
+
# Tags button (right)
|
|
20669
|
+
tags_btn = QPushButton("Tags")
|
|
20670
|
+
tags_btn.setCheckable(True)
|
|
20671
|
+
tags_btn.setChecked(True) # Default: Tags mode
|
|
20672
|
+
tags_btn.setToolTip("Tag View (Ctrl+Alt+T)\nShows raw tags like <b>bold</b>")
|
|
20673
|
+
tags_btn.setStyleSheet("""
|
|
20674
|
+
QPushButton {
|
|
20675
|
+
background-color: #757575;
|
|
20676
|
+
color: white;
|
|
20677
|
+
font-weight: bold;
|
|
20678
|
+
padding: 4px 12px;
|
|
20679
|
+
border: none;
|
|
20680
|
+
border-top-right-radius: 3px;
|
|
20681
|
+
border-bottom-right-radius: 3px;
|
|
20682
|
+
}
|
|
20683
|
+
QPushButton:checked {
|
|
20684
|
+
background-color: #9C27B0;
|
|
20685
|
+
}
|
|
20686
|
+
QPushButton:hover:!checked {
|
|
20687
|
+
background-color: #858585;
|
|
20688
|
+
}
|
|
20689
|
+
""")
|
|
20690
|
+
tags_btn.clicked.connect(lambda: self.toggle_tag_view(True, None))
|
|
20691
|
+
view_mode_group.addButton(tags_btn, 1)
|
|
20692
|
+
view_mode_layout.addWidget(tags_btn)
|
|
20693
|
+
|
|
20694
|
+
toolbar_layout.addWidget(view_mode_container)
|
|
20695
|
+
|
|
20696
|
+
# Store references for keyboard shortcut and programmatic access
|
|
20697
|
+
self.wysiwyg_btn = wysiwyg_btn
|
|
20698
|
+
self.tags_btn = tags_btn
|
|
20699
|
+
self.view_mode_group = view_mode_group
|
|
20588
20700
|
|
|
20589
20701
|
# Initialize tag view state
|
|
20590
20702
|
if not hasattr(self, 'show_tags'):
|
|
20591
|
-
self.show_tags =
|
|
20703
|
+
self.show_tags = True # Default: show tags
|
|
20592
20704
|
|
|
20593
20705
|
# Status selector
|
|
20594
20706
|
from modules.statuses import get_status, STATUSES
|
|
@@ -37566,7 +37678,149 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
37566
37678
|
self.table.setUpdatesEnabled(True)
|
|
37567
37679
|
|
|
37568
37680
|
self.log(f"🔍 Advanced filters: showing {visible_count} of {len(self.current_project.segments)} segments")
|
|
37569
|
-
|
|
37681
|
+
|
|
37682
|
+
def apply_sort(self, sort_type: str = None):
|
|
37683
|
+
"""Sort segments by various criteria (similar to memoQ)"""
|
|
37684
|
+
if not self.current_project or not hasattr(self, 'table') or self.table is None:
|
|
37685
|
+
return
|
|
37686
|
+
|
|
37687
|
+
if not self.current_project.segments:
|
|
37688
|
+
return
|
|
37689
|
+
|
|
37690
|
+
# Show progress dialog for large projects
|
|
37691
|
+
from PyQt6.QtWidgets import QProgressDialog
|
|
37692
|
+
from PyQt6.QtCore import Qt
|
|
37693
|
+
|
|
37694
|
+
progress = QProgressDialog("Sorting segments, please wait...", None, 0, 0, self)
|
|
37695
|
+
progress.setWindowTitle("Sorting")
|
|
37696
|
+
progress.setWindowModality(Qt.WindowModality.WindowModal)
|
|
37697
|
+
progress.setMinimumDuration(500) # Only show if operation takes > 500ms
|
|
37698
|
+
progress.setValue(0)
|
|
37699
|
+
QApplication.processEvents() # Show dialog immediately
|
|
37700
|
+
|
|
37701
|
+
try:
|
|
37702
|
+
# Store original document order if not already stored
|
|
37703
|
+
if not hasattr(self, '_original_segment_order'):
|
|
37704
|
+
self._original_segment_order = self.current_project.segments.copy()
|
|
37705
|
+
|
|
37706
|
+
# Update current sort state
|
|
37707
|
+
self.current_sort = sort_type
|
|
37708
|
+
|
|
37709
|
+
# If sort_type is None, restore document order
|
|
37710
|
+
if sort_type is None:
|
|
37711
|
+
self.current_project.segments = self._original_segment_order.copy()
|
|
37712
|
+
|
|
37713
|
+
# Set pagination to "All" to show all segments
|
|
37714
|
+
if hasattr(self, 'page_size_combo') and self._widget_is_alive(self.page_size_combo):
|
|
37715
|
+
self.page_size_combo.blockSignals(True)
|
|
37716
|
+
self.page_size_combo.setCurrentText("All")
|
|
37717
|
+
self.page_size_combo.blockSignals(False)
|
|
37718
|
+
# Update the internal page size variable
|
|
37719
|
+
if hasattr(self, 'grid_page_size'):
|
|
37720
|
+
self.grid_page_size = 999999
|
|
37721
|
+
|
|
37722
|
+
self.load_segments_to_grid()
|
|
37723
|
+
self.log("↩️ Restored document order (showing all segments)")
|
|
37724
|
+
return
|
|
37725
|
+
|
|
37726
|
+
# Helper function to get text without tags for more accurate sorting
|
|
37727
|
+
def strip_tags(text: str) -> str:
|
|
37728
|
+
"""Remove HTML/XML tags from text for sorting"""
|
|
37729
|
+
import re
|
|
37730
|
+
return re.sub(r'<[^>]+>', '', text).strip()
|
|
37731
|
+
|
|
37732
|
+
# Calculate frequency maps if needed
|
|
37733
|
+
frequency_cache = {}
|
|
37734
|
+
if 'freq' in sort_type:
|
|
37735
|
+
from collections import Counter
|
|
37736
|
+
if 'source' in sort_type:
|
|
37737
|
+
counter = Counter(strip_tags(seg.source).lower() for seg in self.current_project.segments)
|
|
37738
|
+
frequency_cache = {strip_tags(seg.source).lower(): counter[strip_tags(seg.source).lower()]
|
|
37739
|
+
for seg in self.current_project.segments}
|
|
37740
|
+
else: # target frequency
|
|
37741
|
+
counter = Counter(strip_tags(seg.target).lower() for seg in self.current_project.segments if seg.target)
|
|
37742
|
+
frequency_cache = {strip_tags(seg.target).lower(): counter[strip_tags(seg.target).lower()]
|
|
37743
|
+
for seg in self.current_project.segments if seg.target}
|
|
37744
|
+
|
|
37745
|
+
# Sort based on selected criterion
|
|
37746
|
+
if sort_type == 'source_asc':
|
|
37747
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.source).lower())
|
|
37748
|
+
sort_name = "Source A → Z"
|
|
37749
|
+
elif sort_type == 'source_desc':
|
|
37750
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.source).lower(), reverse=True)
|
|
37751
|
+
sort_name = "Source Z → A"
|
|
37752
|
+
elif sort_type == 'target_asc':
|
|
37753
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.target).lower() if s.target else "")
|
|
37754
|
+
sort_name = "Target A → Z"
|
|
37755
|
+
elif sort_type == 'target_desc':
|
|
37756
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.target).lower() if s.target else "", reverse=True)
|
|
37757
|
+
sort_name = "Target Z → A"
|
|
37758
|
+
elif sort_type == 'source_length_asc':
|
|
37759
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.source)))
|
|
37760
|
+
sort_name = "Source (shorter first)"
|
|
37761
|
+
elif sort_type == 'source_length_desc':
|
|
37762
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.source)), reverse=True)
|
|
37763
|
+
sort_name = "Source (longer first)"
|
|
37764
|
+
elif sort_type == 'target_length_asc':
|
|
37765
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.target)) if s.target else 0)
|
|
37766
|
+
sort_name = "Target (shorter first)"
|
|
37767
|
+
elif sort_type == 'target_length_desc':
|
|
37768
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.target)) if s.target else 0, reverse=True)
|
|
37769
|
+
sort_name = "Target (longer first)"
|
|
37770
|
+
elif sort_type == 'match_asc':
|
|
37771
|
+
self.current_project.segments.sort(key=lambda s: getattr(s, 'match_percent', 0) or 0)
|
|
37772
|
+
sort_name = "Match Rate (lower first)"
|
|
37773
|
+
elif sort_type == 'match_desc':
|
|
37774
|
+
self.current_project.segments.sort(key=lambda s: getattr(s, 'match_percent', 0) or 0, reverse=True)
|
|
37775
|
+
sort_name = "Match Rate (higher first)"
|
|
37776
|
+
elif sort_type == 'source_freq_asc':
|
|
37777
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.source).lower(), 0))
|
|
37778
|
+
sort_name = "Source Frequency (lower first)"
|
|
37779
|
+
elif sort_type == 'source_freq_desc':
|
|
37780
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.source).lower(), 0), reverse=True)
|
|
37781
|
+
sort_name = "Source Frequency (higher first)"
|
|
37782
|
+
elif sort_type == 'target_freq_asc':
|
|
37783
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.target).lower(), 0) if s.target else 0)
|
|
37784
|
+
sort_name = "Target Frequency (lower first)"
|
|
37785
|
+
elif sort_type == 'target_freq_desc':
|
|
37786
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.target).lower(), 0) if s.target else 0, reverse=True)
|
|
37787
|
+
sort_name = "Target Frequency (higher first)"
|
|
37788
|
+
elif sort_type == 'modified_asc':
|
|
37789
|
+
self.current_project.segments.sort(key=lambda s: s.modified_at if s.modified_at else "")
|
|
37790
|
+
sort_name = "Last Changed (oldest first)"
|
|
37791
|
+
elif sort_type == 'modified_desc':
|
|
37792
|
+
self.current_project.segments.sort(key=lambda s: s.modified_at if s.modified_at else "", reverse=True)
|
|
37793
|
+
sort_name = "Last Changed (newest first)"
|
|
37794
|
+
elif sort_type == 'status':
|
|
37795
|
+
# Sort by status in a logical order: not_started, draft, translated, confirmed
|
|
37796
|
+
status_order = {'not_started': 0, 'draft': 1, 'translated': 2, 'confirmed': 3}
|
|
37797
|
+
self.current_project.segments.sort(key=lambda s: status_order.get(s.status, 99))
|
|
37798
|
+
sort_name = "Row Status"
|
|
37799
|
+
else:
|
|
37800
|
+
self.log(f"⚠️ Unknown sort type: {sort_type}")
|
|
37801
|
+
return
|
|
37802
|
+
|
|
37803
|
+
# Set pagination to "All" to show all sorted segments
|
|
37804
|
+
if hasattr(self, 'page_size_combo') and self._widget_is_alive(self.page_size_combo):
|
|
37805
|
+
self.page_size_combo.blockSignals(True)
|
|
37806
|
+
self.page_size_combo.setCurrentText("All")
|
|
37807
|
+
self.page_size_combo.blockSignals(False)
|
|
37808
|
+
# Update the internal page size variable
|
|
37809
|
+
if hasattr(self, 'grid_page_size'):
|
|
37810
|
+
self.grid_page_size = 999999
|
|
37811
|
+
|
|
37812
|
+
# Reload grid to reflect new order
|
|
37813
|
+
self.load_segments_to_grid()
|
|
37814
|
+
self.log(f"⇅ Sorted by: {sort_name} (showing all segments)")
|
|
37815
|
+
|
|
37816
|
+
except Exception as e:
|
|
37817
|
+
self.log(f"❌ Error sorting segments: {e}")
|
|
37818
|
+
import traceback
|
|
37819
|
+
traceback.print_exc()
|
|
37820
|
+
finally:
|
|
37821
|
+
# Close progress dialog
|
|
37822
|
+
progress.close()
|
|
37823
|
+
|
|
37570
37824
|
# ========================================================================
|
|
37571
37825
|
# TABBED SEGMENT EDITOR METHODS (for Grid view)
|
|
37572
37826
|
# ========================================================================
|
|
@@ -39560,32 +39814,30 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
39560
39814
|
|
|
39561
39815
|
def _toggle_tag_view_via_shortcut(self):
|
|
39562
39816
|
"""Toggle tag view using keyboard shortcut (Ctrl+Alt+T)"""
|
|
39563
|
-
if hasattr(self, '
|
|
39564
|
-
# Toggle the
|
|
39565
|
-
new_state = not self.
|
|
39566
|
-
self.
|
|
39567
|
-
self.toggle_tag_view(new_state, self.tag_view_btn)
|
|
39817
|
+
if hasattr(self, 'wysiwyg_btn') and hasattr(self, 'tags_btn'):
|
|
39818
|
+
# Toggle between the two modes
|
|
39819
|
+
new_state = not self.show_tags
|
|
39820
|
+
self.toggle_tag_view(new_state, None)
|
|
39568
39821
|
|
|
39569
39822
|
def _enable_tag_view_after_import(self):
|
|
39570
39823
|
"""Auto-enable Tag View after importing a document with formatting tags"""
|
|
39571
|
-
if hasattr(self, '
|
|
39572
|
-
self.
|
|
39573
|
-
self.toggle_tag_view(True, self.tag_view_btn)
|
|
39824
|
+
if hasattr(self, 'tags_btn'):
|
|
39825
|
+
self.toggle_tag_view(True, None)
|
|
39574
39826
|
self.log("🏷️ Tag View auto-enabled (formatting tags detected in import)")
|
|
39575
39827
|
|
|
39576
39828
|
def toggle_tag_view(self, checked: bool, button: QPushButton = None):
|
|
39577
39829
|
"""Toggle between Tag View (showing raw tags) and WYSIWYG View (formatted display)"""
|
|
39578
39830
|
self.show_tags = checked
|
|
39579
|
-
|
|
39580
|
-
# Update
|
|
39581
|
-
if
|
|
39831
|
+
|
|
39832
|
+
# Update segmented control buttons if they exist
|
|
39833
|
+
if hasattr(self, 'wysiwyg_btn') and hasattr(self, 'tags_btn'):
|
|
39582
39834
|
if checked:
|
|
39583
|
-
|
|
39835
|
+
self.tags_btn.setChecked(True)
|
|
39584
39836
|
else:
|
|
39585
|
-
|
|
39586
|
-
|
|
39837
|
+
self.wysiwyg_btn.setChecked(True)
|
|
39838
|
+
|
|
39587
39839
|
self.log(f"{'🏷️ Tag View ENABLED - showing raw tags' if checked else '✨ WYSIWYG View ENABLED - showing formatted text'}")
|
|
39588
|
-
|
|
39840
|
+
|
|
39589
39841
|
# Refresh the grid to update display
|
|
39590
39842
|
if hasattr(self, 'table') and self.current_project:
|
|
39591
39843
|
self._refresh_grid_display_mode()
|
|
@@ -40958,45 +41210,12 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
40958
41210
|
event.accept()
|
|
40959
41211
|
|
|
40960
41212
|
def _cleanup_web_views(self):
|
|
40961
|
-
"""Clean up WebEngine views to prevent
|
|
40962
|
-
|
|
40963
|
-
|
|
40964
|
-
|
|
40965
|
-
|
|
40966
|
-
|
|
40967
|
-
if hasattr(tab, 'web_view'):
|
|
40968
|
-
# Stop any loading/rendering
|
|
40969
|
-
tab.web_view.stop()
|
|
40970
|
-
# Clear page to release resources
|
|
40971
|
-
tab.web_view.setPage(None)
|
|
40972
|
-
tab.web_view.setUrl(QUrl('about:blank'))
|
|
40973
|
-
tab.web_view.deleteLater()
|
|
40974
|
-
except:
|
|
40975
|
-
pass
|
|
40976
|
-
|
|
40977
|
-
# Close Superlookup web views (from Web Resources tab)
|
|
40978
|
-
if hasattr(self, 'web_views'):
|
|
40979
|
-
for resource_id, web_view in list(self.web_views.items()):
|
|
40980
|
-
try:
|
|
40981
|
-
web_view.stop()
|
|
40982
|
-
web_view.setPage(None)
|
|
40983
|
-
web_view.setUrl(QUrl('about:blank'))
|
|
40984
|
-
web_view.deleteLater()
|
|
40985
|
-
except:
|
|
40986
|
-
pass
|
|
40987
|
-
self.web_views.clear()
|
|
40988
|
-
|
|
40989
|
-
# Process events multiple times to ensure cleanup completes
|
|
40990
|
-
from PyQt6.QtWidgets import QApplication
|
|
40991
|
-
from PyQt6.QtCore import QUrl
|
|
40992
|
-
for _ in range(3):
|
|
40993
|
-
QApplication.processEvents()
|
|
40994
|
-
|
|
40995
|
-
# Small delay to allow Qt to finish cleanup
|
|
40996
|
-
import time
|
|
40997
|
-
time.sleep(0.1)
|
|
40998
|
-
except:
|
|
40999
|
-
pass
|
|
41213
|
+
"""Clean up WebEngine views - DISABLED to prevent crash"""
|
|
41214
|
+
# WebEngine cleanup has been disabled because it was causing Python crashes
|
|
41215
|
+
# on program exit. Qt will handle WebEngine cleanup automatically, though
|
|
41216
|
+
# you may see a "Release of profile requested" warning which is harmless.
|
|
41217
|
+
print("[WebEngine Cleanup] Skipping manual cleanup - letting Qt handle it")
|
|
41218
|
+
pass
|
|
41000
41219
|
|
|
41001
41220
|
def _close_detached_log_windows(self):
|
|
41002
41221
|
"""Close all detached log windows when main window closes"""
|
modules/superbrowser.py
CHANGED
|
@@ -160,6 +160,20 @@ class ChatColumn(QWidget):
|
|
|
160
160
|
"""Update URL bar when page changes"""
|
|
161
161
|
self.url_input.setText(url.toString())
|
|
162
162
|
|
|
163
|
+
def cleanup(self):
|
|
164
|
+
"""Clean up web engine resources before deletion"""
|
|
165
|
+
try:
|
|
166
|
+
from PyQt6.QtCore import QUrl
|
|
167
|
+
if hasattr(self, 'web_view'):
|
|
168
|
+
self.web_view.stop()
|
|
169
|
+
self.web_view.setPage(None)
|
|
170
|
+
self.web_view.setUrl(QUrl('about:blank'))
|
|
171
|
+
self.web_view.deleteLater()
|
|
172
|
+
if hasattr(self, 'profile'):
|
|
173
|
+
self.profile.deleteLater()
|
|
174
|
+
except:
|
|
175
|
+
pass
|
|
176
|
+
|
|
163
177
|
|
|
164
178
|
class SuperbrowserWidget(QWidget):
|
|
165
179
|
"""
|
|
@@ -304,6 +318,14 @@ class SuperbrowserWidget(QWidget):
|
|
|
304
318
|
self.claude_column.go_home()
|
|
305
319
|
self.gemini_column.go_home()
|
|
306
320
|
|
|
321
|
+
def cleanup(self):
|
|
322
|
+
"""Clean up all web engine resources before widget deletion"""
|
|
323
|
+
try:
|
|
324
|
+
for column in self.chat_columns:
|
|
325
|
+
column.cleanup()
|
|
326
|
+
except:
|
|
327
|
+
pass
|
|
328
|
+
|
|
307
329
|
|
|
308
330
|
# ============================================================================
|
|
309
331
|
# STANDALONE USAGE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: supervertaler
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.189
|
|
4
4
|
Summary: Professional AI-enhanced translation workbench with multi-LLM support, glossary system, TM, spellcheck, voice commands, and PyQt6 interface. Batteries included (core).
|
|
5
5
|
Home-page: https://supervertaler.com
|
|
6
6
|
Author: Michael Beijer
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Supervertaler.py,sha256=
|
|
1
|
+
Supervertaler.py,sha256=pYjcMW-s3MpKS7oCJZ02Vswsy-6BYzp566_m2p9yplg,2353315
|
|
2
2
|
modules/__init__.py,sha256=G58XleS-EJ2sX4Kehm-3N2m618_W2Es0Kg8CW_eBG7g,327
|
|
3
3
|
modules/ai_actions.py,sha256=i5MJcM-7Y6CAvKUwxmxrVHeoZAVtAP7aRDdWM5KLkO0,33877
|
|
4
4
|
modules/ai_attachment_manager.py,sha256=juZlrW3UPkIkcnj0SREgOQkQROLf0fcu3ShZcKXMxsI,11361
|
|
@@ -49,7 +49,7 @@ modules/spellcheck_manager.py,sha256=jwduHJ66pOKv1MtzSAltxpP8LPgz11FvF6j8h7BiRZY
|
|
|
49
49
|
modules/statuses.py,sha256=t6TCA9pNZHDw3SbKTxT73uKezJhwWk9gFLr0NOgEufs,6911
|
|
50
50
|
modules/style_guide_manager.py,sha256=QBvbygF2E-cgRuwJPWmIatwQl37voU1FErYnycv8Sac,10809
|
|
51
51
|
modules/superbench_ui.py,sha256=O8pSb-R8Mul1Qz9-8gbBrFR1j7Z8b8_G0wSTSLSCf2U,55563
|
|
52
|
-
modules/superbrowser.py,sha256=
|
|
52
|
+
modules/superbrowser.py,sha256=jZw7jNOJyZdLTOVcbAdALH1MYiP43qi_4qGzNWxJ2Hs,13481
|
|
53
53
|
modules/supercleaner.py,sha256=uRJAEh03Eu4Qtujrf_jxuIyPBbcxKAZzryhV9Chi9ro,22939
|
|
54
54
|
modules/supercleaner_ui.py,sha256=sVTnYxlX9R20eWs52BKCeQ15iFVFOIQEl29L72lup1c,18812
|
|
55
55
|
modules/superdocs.py,sha256=vMYyUbHU8zDkS1YMSDF7niDkT8d8FJFDcfIxSktCyGc,522
|
|
@@ -79,9 +79,9 @@ modules/unified_prompt_manager_qt.py,sha256=HkGUnH0wlfxt-hVe-nKCeWLyProYdefuuq2s
|
|
|
79
79
|
modules/voice_commands.py,sha256=iBb-gjWxRMLhFH7-InSRjYJz1EIDBNA2Pog8V7TtJaY,38516
|
|
80
80
|
modules/voice_dictation.py,sha256=QmitXfkG-vRt5hIQATjphHdhXfqmwhzcQcbXB6aRzIg,16386
|
|
81
81
|
modules/voice_dictation_lite.py,sha256=jorY0BmWE-8VczbtGrWwt1zbnOctMoSlWOsQrcufBcc,9423
|
|
82
|
-
supervertaler-1.9.
|
|
83
|
-
supervertaler-1.9.
|
|
84
|
-
supervertaler-1.9.
|
|
85
|
-
supervertaler-1.9.
|
|
86
|
-
supervertaler-1.9.
|
|
87
|
-
supervertaler-1.9.
|
|
82
|
+
supervertaler-1.9.189.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
|
|
83
|
+
supervertaler-1.9.189.dist-info/METADATA,sha256=-vU2TzoVWmyCKRPl_sOcibcyIIGFHQLPSBKSHo09Vd8,5725
|
|
84
|
+
supervertaler-1.9.189.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
85
|
+
supervertaler-1.9.189.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
|
|
86
|
+
supervertaler-1.9.189.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
|
|
87
|
+
supervertaler-1.9.189.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|