Files
local-transcription/gui/transcription_display_qt.py

299 lines
9.4 KiB
Python
Raw Normal View History

"""PySide6 transcription display widget for showing real-time transcriptions."""
from PySide6.QtWidgets import QTextEdit
from PySide6.QtGui import QFont, QTextCursor, QTextCharFormat, QColor
from PySide6.QtCore import Qt, Slot
from datetime import datetime
class TranscriptionDisplay(QTextEdit):
"""Custom text widget for displaying transcriptions using PySide6."""
def __init__(self, parent=None, max_lines=100, show_timestamps=True, font_family="Courier", font_size=12):
"""
Initialize transcription display.
Args:
parent: Parent widget
max_lines: Maximum number of lines to keep in display
show_timestamps: Whether to show timestamps
font_family: Font family name
font_size: Font size in points
"""
super().__init__(parent)
self.max_lines = max_lines
self.show_timestamps = show_timestamps
self.line_count = 0
self.font_family = font_family
self.font_size = font_size
# Track the current preview line for two-stage transcription
self.preview_line_index = -1 # -1 means no active preview
self.preview_block_number = -1 # Block number for the preview line
# Configure text widget
self.setReadOnly(True)
self.setFont(QFont(font_family, font_size))
# Set dark theme styling
self.setStyleSheet("""
QTextEdit {
background-color: #2b2b2b;
color: #ffffff;
border: 1px solid #3d3d3d;
border-radius: 5px;
padding: 10px;
}
""")
def _format_line(self, text: str, user_name: str, timestamp: datetime, is_preview: bool = False) -> str:
"""
Format a transcription line.
Args:
text: Transcription text
user_name: User/speaker name
timestamp: Timestamp of transcription
is_preview: Whether this is a preview line
Returns:
Formatted line string
"""
line_parts = []
if self.show_timestamps:
time_str = timestamp.strftime("%H:%M:%S")
line_parts.append(f"[{time_str}]")
if user_name and user_name.strip():
line_parts.append(f"{user_name}:")
# Add preview indicator for visual distinction
if is_preview:
line_parts.append(f"[...] {text}")
else:
line_parts.append(text)
return " ".join(line_parts)
@Slot(str, str)
def add_transcription(self, text: str, user_name: str = "", timestamp: datetime = None):
"""
Add a new transcription to the display.
Args:
text: Transcription text
user_name: User/speaker name
timestamp: Timestamp of transcription
"""
if timestamp is None:
timestamp = datetime.now()
line = self._format_line(text, user_name, timestamp, is_preview=False)
# If there's an active preview, replace it instead of appending
if self.preview_line_index >= 0:
self._replace_preview_with_final(line)
else:
# Add to display normally
self.append(line)
self.line_count += 1
# Auto-scroll to bottom
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
self.setTextCursor(cursor)
# Remove old lines if exceeding max
if self.line_count > self.max_lines:
self._remove_oldest_lines(self.line_count - self.max_lines)
@Slot(str, str)
def add_preview(self, text: str, user_name: str = "", timestamp: datetime = None):
"""
Add a preview transcription that will be replaced by the final transcription.
Args:
text: Preview transcription text
user_name: User/speaker name
timestamp: Timestamp of transcription
"""
if timestamp is None:
timestamp = datetime.now()
line = self._format_line(text, user_name, timestamp, is_preview=True)
# If there's already a preview, replace it
if self.preview_line_index >= 0:
self._replace_preview_line(line)
else:
# Add new preview line
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
# Apply italic formatting for preview
fmt = QTextCharFormat()
fmt.setFontItalic(True)
if self.line_count > 0:
cursor.insertText("\n")
cursor.insertText(line, fmt)
self.preview_line_index = self.line_count
self.preview_block_number = self.document().blockCount() - 1
self.line_count += 1
# Auto-scroll to bottom
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
self.setTextCursor(cursor)
def _replace_preview_line(self, new_text: str):
"""Replace the current preview line with new preview text."""
if self.preview_block_number < 0:
return
doc = self.document()
block = doc.findBlockByNumber(self.preview_block_number)
if block.isValid():
cursor = QTextCursor(block)
cursor.select(QTextCursor.BlockUnderCursor)
# Apply italic formatting for preview
fmt = QTextCharFormat()
fmt.setFontItalic(True)
cursor.removeSelectedText()
cursor.insertText(new_text, fmt)
def _replace_preview_with_final(self, final_text: str):
"""Replace the preview line with final transcription."""
if self.preview_block_number < 0:
# No preview to replace, just add normally
self.append(final_text)
self.line_count += 1
self.preview_line_index = -1
self.preview_block_number = -1
return
doc = self.document()
block = doc.findBlockByNumber(self.preview_block_number)
if block.isValid():
cursor = QTextCursor(block)
cursor.select(QTextCursor.BlockUnderCursor)
# Apply normal formatting for final text
fmt = QTextCharFormat()
fmt.setFontItalic(False)
fmt.setForeground(QColor(255, 255, 255)) # White for final
cursor.removeSelectedText()
cursor.insertText(final_text, fmt)
# Clear preview tracking
self.preview_line_index = -1
self.preview_block_number = -1
def clear_preview(self):
"""Clear the current preview without adding a final transcription."""
if self.preview_block_number >= 0:
doc = self.document()
block = doc.findBlockByNumber(self.preview_block_number)
if block.isValid():
cursor = QTextCursor(block)
cursor.select(QTextCursor.BlockUnderCursor)
cursor.removeSelectedText()
cursor.deleteChar() # Remove newline
self.line_count -= 1
self.preview_line_index = -1
self.preview_block_number = -1
def _remove_oldest_lines(self, num_lines: int):
"""
Remove oldest lines from the display.
Args:
num_lines: Number of lines to remove
"""
cursor = self.textCursor()
cursor.movePosition(QTextCursor.Start)
for _ in range(num_lines):
cursor.select(QTextCursor.BlockUnderCursor)
cursor.removeSelectedText()
cursor.deleteChar() # Remove the newline
self.line_count -= num_lines
# Adjust preview tracking if lines were removed
if self.preview_line_index >= 0:
self.preview_line_index -= num_lines
self.preview_block_number -= num_lines
if self.preview_line_index < 0:
self.preview_line_index = -1
self.preview_block_number = -1
def clear_all(self):
"""Clear all transcriptions."""
self.clear()
self.line_count = 0
self.preview_line_index = -1
self.preview_block_number = -1
def get_all_text(self) -> str:
"""
Get all transcription text.
Returns:
All text in the display
"""
return self.toPlainText()
def set_max_lines(self, max_lines: int):
"""Update maximum number of lines to keep."""
self.max_lines = max_lines
# Trim if necessary
if self.line_count > self.max_lines:
self._remove_oldest_lines(self.line_count - self.max_lines)
def set_show_timestamps(self, show: bool):
"""Update whether to show timestamps."""
self.show_timestamps = show
def set_font(self, font_family: str, font_size: int):
"""
Update font settings.
Args:
font_family: Font family name
font_size: Font size in points
"""
self.font_family = font_family
self.font_size = font_size
super().setFont(QFont(font_family, font_size))
def save_to_file(self, filepath: str) -> bool:
"""
Save transcriptions to a file.
Args:
filepath: Path to save file
Returns:
True if saved successfully
"""
try:
with open(filepath, 'w') as f:
f.write(self.toPlainText())
return True
except Exception as e:
print(f"Error saving transcriptions: {e}")
return False