supervertaler 1.9.185__py3-none-any.whl → 1.9.187__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.
- Supervertaler.py +255 -69
- modules/superbrowser.py +22 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.187.dist-info}/METADATA +1 -1
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.187.dist-info}/RECORD +8 -8
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.187.dist-info}/WHEEL +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.187.dist-info}/entry_points.txt +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.187.dist-info}/licenses/LICENSE +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.187.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.187"
|
|
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(sort_btn)
|
|
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,116 @@ 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
|
+
# Store original document order if not already stored
|
|
37691
|
+
if not hasattr(self, '_original_segment_order'):
|
|
37692
|
+
self._original_segment_order = self.current_project.segments.copy()
|
|
37693
|
+
|
|
37694
|
+
# Update current sort state
|
|
37695
|
+
self.current_sort = sort_type
|
|
37696
|
+
|
|
37697
|
+
# If sort_type is None, restore document order
|
|
37698
|
+
if sort_type is None:
|
|
37699
|
+
self.current_project.segments = self._original_segment_order.copy()
|
|
37700
|
+
self.load_segments_to_grid()
|
|
37701
|
+
self.log("↩️ Restored document order")
|
|
37702
|
+
return
|
|
37703
|
+
|
|
37704
|
+
# Helper function to get text without tags for more accurate sorting
|
|
37705
|
+
def strip_tags(text: str) -> str:
|
|
37706
|
+
"""Remove HTML/XML tags from text for sorting"""
|
|
37707
|
+
import re
|
|
37708
|
+
return re.sub(r'<[^>]+>', '', text).strip()
|
|
37709
|
+
|
|
37710
|
+
# Calculate frequency maps if needed
|
|
37711
|
+
frequency_cache = {}
|
|
37712
|
+
if 'freq' in sort_type:
|
|
37713
|
+
from collections import Counter
|
|
37714
|
+
if 'source' in sort_type:
|
|
37715
|
+
counter = Counter(strip_tags(seg.source).lower() for seg in self.current_project.segments)
|
|
37716
|
+
frequency_cache = {strip_tags(seg.source).lower(): counter[strip_tags(seg.source).lower()]
|
|
37717
|
+
for seg in self.current_project.segments}
|
|
37718
|
+
else: # target frequency
|
|
37719
|
+
counter = Counter(strip_tags(seg.target).lower() for seg in self.current_project.segments if seg.target)
|
|
37720
|
+
frequency_cache = {strip_tags(seg.target).lower(): counter[strip_tags(seg.target).lower()]
|
|
37721
|
+
for seg in self.current_project.segments if seg.target}
|
|
37722
|
+
|
|
37723
|
+
# Sort based on selected criterion
|
|
37724
|
+
try:
|
|
37725
|
+
if sort_type == 'source_asc':
|
|
37726
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.source).lower())
|
|
37727
|
+
sort_name = "Source A → Z"
|
|
37728
|
+
elif sort_type == 'source_desc':
|
|
37729
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.source).lower(), reverse=True)
|
|
37730
|
+
sort_name = "Source Z → A"
|
|
37731
|
+
elif sort_type == 'target_asc':
|
|
37732
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.target).lower() if s.target else "")
|
|
37733
|
+
sort_name = "Target A → Z"
|
|
37734
|
+
elif sort_type == 'target_desc':
|
|
37735
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.target).lower() if s.target else "", reverse=True)
|
|
37736
|
+
sort_name = "Target Z → A"
|
|
37737
|
+
elif sort_type == 'source_length_asc':
|
|
37738
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.source)))
|
|
37739
|
+
sort_name = "Source (shorter first)"
|
|
37740
|
+
elif sort_type == 'source_length_desc':
|
|
37741
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.source)), reverse=True)
|
|
37742
|
+
sort_name = "Source (longer first)"
|
|
37743
|
+
elif sort_type == 'target_length_asc':
|
|
37744
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.target)) if s.target else 0)
|
|
37745
|
+
sort_name = "Target (shorter first)"
|
|
37746
|
+
elif sort_type == 'target_length_desc':
|
|
37747
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.target)) if s.target else 0, reverse=True)
|
|
37748
|
+
sort_name = "Target (longer first)"
|
|
37749
|
+
elif sort_type == 'match_asc':
|
|
37750
|
+
self.current_project.segments.sort(key=lambda s: getattr(s, 'match_percent', 0) or 0)
|
|
37751
|
+
sort_name = "Match Rate (lower first)"
|
|
37752
|
+
elif sort_type == 'match_desc':
|
|
37753
|
+
self.current_project.segments.sort(key=lambda s: getattr(s, 'match_percent', 0) or 0, reverse=True)
|
|
37754
|
+
sort_name = "Match Rate (higher first)"
|
|
37755
|
+
elif sort_type == 'source_freq_asc':
|
|
37756
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.source).lower(), 0))
|
|
37757
|
+
sort_name = "Source Frequency (lower first)"
|
|
37758
|
+
elif sort_type == 'source_freq_desc':
|
|
37759
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.source).lower(), 0), reverse=True)
|
|
37760
|
+
sort_name = "Source Frequency (higher first)"
|
|
37761
|
+
elif sort_type == 'target_freq_asc':
|
|
37762
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.target).lower(), 0) if s.target else 0)
|
|
37763
|
+
sort_name = "Target Frequency (lower first)"
|
|
37764
|
+
elif sort_type == 'target_freq_desc':
|
|
37765
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.target).lower(), 0) if s.target else 0, reverse=True)
|
|
37766
|
+
sort_name = "Target Frequency (higher first)"
|
|
37767
|
+
elif sort_type == 'modified_asc':
|
|
37768
|
+
self.current_project.segments.sort(key=lambda s: s.modified_at if s.modified_at else "")
|
|
37769
|
+
sort_name = "Last Changed (oldest first)"
|
|
37770
|
+
elif sort_type == 'modified_desc':
|
|
37771
|
+
self.current_project.segments.sort(key=lambda s: s.modified_at if s.modified_at else "", reverse=True)
|
|
37772
|
+
sort_name = "Last Changed (newest first)"
|
|
37773
|
+
elif sort_type == 'status':
|
|
37774
|
+
# Sort by status in a logical order: not_started, draft, translated, confirmed
|
|
37775
|
+
status_order = {'not_started': 0, 'draft': 1, 'translated': 2, 'confirmed': 3}
|
|
37776
|
+
self.current_project.segments.sort(key=lambda s: status_order.get(s.status, 99))
|
|
37777
|
+
sort_name = "Row Status"
|
|
37778
|
+
else:
|
|
37779
|
+
self.log(f"⚠️ Unknown sort type: {sort_type}")
|
|
37780
|
+
return
|
|
37781
|
+
|
|
37782
|
+
# Reload grid to reflect new order
|
|
37783
|
+
self.load_segments_to_grid()
|
|
37784
|
+
self.log(f"⇅ Sorted by: {sort_name}")
|
|
37785
|
+
|
|
37786
|
+
except Exception as e:
|
|
37787
|
+
self.log(f"❌ Error sorting segments: {e}")
|
|
37788
|
+
import traceback
|
|
37789
|
+
traceback.print_exc()
|
|
37790
|
+
|
|
37570
37791
|
# ========================================================================
|
|
37571
37792
|
# TABBED SEGMENT EDITOR METHODS (for Grid view)
|
|
37572
37793
|
# ========================================================================
|
|
@@ -39560,32 +39781,30 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
39560
39781
|
|
|
39561
39782
|
def _toggle_tag_view_via_shortcut(self):
|
|
39562
39783
|
"""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)
|
|
39784
|
+
if hasattr(self, 'wysiwyg_btn') and hasattr(self, 'tags_btn'):
|
|
39785
|
+
# Toggle between the two modes
|
|
39786
|
+
new_state = not self.show_tags
|
|
39787
|
+
self.toggle_tag_view(new_state, None)
|
|
39568
39788
|
|
|
39569
39789
|
def _enable_tag_view_after_import(self):
|
|
39570
39790
|
"""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)
|
|
39791
|
+
if hasattr(self, 'tags_btn'):
|
|
39792
|
+
self.toggle_tag_view(True, None)
|
|
39574
39793
|
self.log("🏷️ Tag View auto-enabled (formatting tags detected in import)")
|
|
39575
39794
|
|
|
39576
39795
|
def toggle_tag_view(self, checked: bool, button: QPushButton = None):
|
|
39577
39796
|
"""Toggle between Tag View (showing raw tags) and WYSIWYG View (formatted display)"""
|
|
39578
39797
|
self.show_tags = checked
|
|
39579
|
-
|
|
39580
|
-
# Update
|
|
39581
|
-
if
|
|
39798
|
+
|
|
39799
|
+
# Update segmented control buttons if they exist
|
|
39800
|
+
if hasattr(self, 'wysiwyg_btn') and hasattr(self, 'tags_btn'):
|
|
39582
39801
|
if checked:
|
|
39583
|
-
|
|
39802
|
+
self.tags_btn.setChecked(True)
|
|
39584
39803
|
else:
|
|
39585
|
-
|
|
39586
|
-
|
|
39804
|
+
self.wysiwyg_btn.setChecked(True)
|
|
39805
|
+
|
|
39587
39806
|
self.log(f"{'🏷️ Tag View ENABLED - showing raw tags' if checked else '✨ WYSIWYG View ENABLED - showing formatted text'}")
|
|
39588
|
-
|
|
39807
|
+
|
|
39589
39808
|
# Refresh the grid to update display
|
|
39590
39809
|
if hasattr(self, 'table') and self.current_project:
|
|
39591
39810
|
self._refresh_grid_display_mode()
|
|
@@ -40958,45 +41177,12 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
40958
41177
|
event.accept()
|
|
40959
41178
|
|
|
40960
41179
|
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
|
|
41180
|
+
"""Clean up WebEngine views - DISABLED to prevent crash"""
|
|
41181
|
+
# WebEngine cleanup has been disabled because it was causing Python crashes
|
|
41182
|
+
# on program exit. Qt will handle WebEngine cleanup automatically, though
|
|
41183
|
+
# you may see a "Release of profile requested" warning which is harmless.
|
|
41184
|
+
print("[WebEngine Cleanup] Skipping manual cleanup - letting Qt handle it")
|
|
41185
|
+
pass
|
|
41000
41186
|
|
|
41001
41187
|
def _close_detached_log_windows(self):
|
|
41002
41188
|
"""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.187
|
|
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=yjoU32F4RBdhk9YDkX_nDgdi43NcOj5pqCiAFNwP_Kc,2351549
|
|
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.187.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
|
|
83
|
+
supervertaler-1.9.187.dist-info/METADATA,sha256=9cptaoXjY9Z34yHQFE26tjGpuiEElxmslgd31e-kA70,5725
|
|
84
|
+
supervertaler-1.9.187.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
85
|
+
supervertaler-1.9.187.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
|
|
86
|
+
supervertaler-1.9.187.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
|
|
87
|
+
supervertaler-1.9.187.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|