mirror of
https://github.com/chidiwilliams/buzz.git
synced 2026-03-14 14:45:46 +01:00
Added copy-to-clipboard button in recording transcribe widget (#1370)
This commit is contained in:
parent
c4d7971e04
commit
156ec35246
2 changed files with 140 additions and 0 deletions
|
|
@ -16,6 +16,7 @@ from PyQt6.QtWidgets import (
|
|||
QFormLayout,
|
||||
QHBoxLayout,
|
||||
QMessageBox,
|
||||
QApplication,
|
||||
QPushButton,
|
||||
QComboBox,
|
||||
QLabel,
|
||||
|
|
@ -209,6 +210,9 @@ class RecordingTranscriberWidget(QWidget):
|
|||
self.presentation_options_bar = self.create_presentation_options_bar()
|
||||
layout.insertWidget(3, self.presentation_options_bar)
|
||||
self.presentation_options_bar.hide()
|
||||
self.copy_actions_bar = self.create_copy_actions_bar()
|
||||
layout.addWidget(self.copy_actions_bar) # Add at the bottom
|
||||
self.copy_actions_bar.hide()
|
||||
|
||||
def create_presentation_options_bar(self) -> QWidget:
|
||||
"""Crete the presentation options bar widget"""
|
||||
|
|
@ -286,6 +290,56 @@ class RecordingTranscriberWidget(QWidget):
|
|||
|
||||
return bar
|
||||
|
||||
def create_copy_actions_bar(self) -> QWidget:
|
||||
"""Create the copy actions bar widget"""
|
||||
bar = QWidget(self)
|
||||
layout = QHBoxLayout(bar)
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
layout.setSpacing(10)
|
||||
|
||||
layout.addStretch() # Push button to the right
|
||||
|
||||
self.copy_transcript_button = QPushButton(_("Copy"), bar)
|
||||
self.copy_transcript_button.setToolTip(_("Copy transcription to clipboard"))
|
||||
self.copy_transcript_button.clicked.connect(self.on_copy_transcript_clicked)
|
||||
layout.addWidget(self.copy_transcript_button)
|
||||
|
||||
return bar
|
||||
|
||||
def on_copy_transcript_clicked(self):
|
||||
"""Handle copy transcript button click"""
|
||||
transcript_text = self.transcription_text_box.toPlainText().strip()
|
||||
|
||||
if not transcript_text:
|
||||
self.copy_transcript_button.setText(_("Nothing to copy!"))
|
||||
QTimer.singleShot(1500, lambda: self.copy_transcript_button.setText(_("Copy")))
|
||||
return
|
||||
|
||||
app = QApplication.instance()
|
||||
if app is None:
|
||||
logging.warning("QApplication instance not available; clipboard disabled")
|
||||
self.copy_transcript_button.setText(_("Copy failed"))
|
||||
QTimer.singleShot(1500, lambda: self.copy_transcript_button.setText(_("Copy")))
|
||||
return
|
||||
|
||||
clipboard = app.clipboard()
|
||||
if clipboard is None:
|
||||
logging.warning("Clipboard not available")
|
||||
self.copy_transcript_button.setText(_("Copy failed"))
|
||||
QTimer.singleShot(1500, lambda: self.copy_transcript_button.setText(_("Copy")))
|
||||
return
|
||||
|
||||
try:
|
||||
clipboard.setText(transcript_text)
|
||||
except Exception as e:
|
||||
logging.warning("Clipboard error: %s", e)
|
||||
self.copy_transcript_button.setText(_("Copy failed"))
|
||||
QTimer.singleShot(1500, lambda: self.copy_transcript_button.setText(_("Copy")))
|
||||
return
|
||||
|
||||
self.copy_transcript_button.setText(_("Copied!"))
|
||||
QTimer.singleShot(2000, lambda: self.copy_transcript_button.setText(_("Copy")))
|
||||
|
||||
def on_show_presentation_clicked(self):
|
||||
"""Handle click on 'Show in new window' button"""
|
||||
if self.presentation_window is None or not self.presentation_window.isVisible():
|
||||
|
|
@ -464,6 +518,8 @@ class RecordingTranscriberWidget(QWidget):
|
|||
self.transcription_options_group_box.setEnabled(False)
|
||||
self.audio_devices_combo_box.setEnabled(False)
|
||||
self.presentation_options_bar.show()
|
||||
self.copy_actions_bar.hide()
|
||||
|
||||
else: # RecordingStatus.RECORDING
|
||||
self.stop_recording()
|
||||
self.set_recording_status_stopped()
|
||||
|
|
@ -574,6 +630,7 @@ class RecordingTranscriberWidget(QWidget):
|
|||
self.transcription_options_group_box.setEnabled(True)
|
||||
self.audio_devices_combo_box.setEnabled(True)
|
||||
self.presentation_options_bar.hide()
|
||||
self.copy_actions_bar.show() #added this here
|
||||
|
||||
def on_download_model_error(self, error: str):
|
||||
self.reset_model_download()
|
||||
|
|
|
|||
|
|
@ -470,6 +470,89 @@ class TestRecordingTranscriberWidgetPresentation:
|
|||
time.sleep(0.5)
|
||||
widget.close()
|
||||
|
||||
@pytest.mark.timeout(60)
|
||||
def test_on_copy_transcript_clicked_with_text(self, qtbot: QtBot):
|
||||
with (
|
||||
patch("sounddevice.InputStream", side_effect=MockInputStream),
|
||||
patch("sounddevice.check_input_settings"),
|
||||
patch(
|
||||
"buzz.transcriber.recording_transcriber.RecordingTranscriber.get_device_sample_rate",
|
||||
return_value=16_000,
|
||||
),
|
||||
):
|
||||
mock_clipboard = MagicMock()
|
||||
mock_app = MagicMock()
|
||||
mock_app.clipboard.return_value = mock_clipboard
|
||||
|
||||
widget = RecordingTranscriberWidget(custom_sounddevice=MockSoundDevice())
|
||||
qtbot.add_widget(widget)
|
||||
|
||||
widget.transcription_text_box.setPlainText("Hello world")
|
||||
widget.copy_actions_bar.show()
|
||||
|
||||
with patch("buzz.widgets.recording_transcriber_widget.QApplication.instance",
|
||||
return_value=mock_app):
|
||||
widget.on_copy_transcript_clicked()
|
||||
|
||||
mock_clipboard.setText.assert_called_once_with("Hello world")
|
||||
assert widget.copy_transcript_button.text() == _("Copied!")
|
||||
|
||||
time.sleep(0.5)
|
||||
widget.close()
|
||||
|
||||
@pytest.mark.timeout(60)
|
||||
def test_on_copy_transcript_clicked_without_text(self, qtbot: QtBot):
|
||||
"""Test that copy button handles empty transcript gracefully"""
|
||||
with (
|
||||
patch("sounddevice.InputStream", side_effect=MockInputStream),
|
||||
patch("sounddevice.check_input_settings"),
|
||||
patch("buzz.transcriber.recording_transcriber.RecordingTranscriber.get_device_sample_rate",
|
||||
return_value=16_000),
|
||||
):
|
||||
widget = RecordingTranscriberWidget(
|
||||
custom_sounddevice=MockSoundDevice()
|
||||
)
|
||||
qtbot.add_widget(widget)
|
||||
|
||||
widget.transcription_text_box.setPlainText("")
|
||||
widget.copy_actions_bar.show()
|
||||
|
||||
widget.on_copy_transcript_clicked()
|
||||
|
||||
assert widget.copy_transcript_button.text() == _("Nothing to copy!")
|
||||
|
||||
time.sleep(0.5)
|
||||
widget.close()
|
||||
|
||||
@pytest.mark.timeout(60)
|
||||
def test_copy_actions_bar_hidden_when_recording_starts(self, qtbot: QtBot):
|
||||
"""Test that copy actions bar hides when recording starts"""
|
||||
with (
|
||||
patch("sounddevice.InputStream", side_effect=MockInputStream),
|
||||
patch("sounddevice.check_input_settings"),
|
||||
patch("buzz.transcriber.recording_transcriber.RecordingTranscriber.get_device_sample_rate",
|
||||
return_value=16_000),
|
||||
):
|
||||
widget = RecordingTranscriberWidget(
|
||||
custom_sounddevice=MockSoundDevice()
|
||||
)
|
||||
widget.device_sample_rate = 16_000
|
||||
qtbot.add_widget(widget)
|
||||
|
||||
widget.copy_actions_bar.show()
|
||||
assert not widget.copy_actions_bar.isHidden()
|
||||
|
||||
# Mock start_recording to prevent actual recording threads from starting
|
||||
widget.current_status = widget.RecordingStatus.STOPPED
|
||||
with patch.object(widget, 'start_recording'):
|
||||
widget.on_record_button_clicked()
|
||||
|
||||
assert widget.copy_actions_bar.isHidden()
|
||||
|
||||
time.sleep(0.5)
|
||||
widget.close()
|
||||
|
||||
|
||||
@pytest.mark.timeout(60)
|
||||
def test_on_bg_color_clicked(self, qtbot: QtBot):
|
||||
"""Test that background color button opens color dialog and saves selection"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue