1import numpy as np 2from PyQt5.QtCore import pyqtSlot, pyqtSignal 3from PyQt5.QtGui import QIcon, QKeySequence 4from PyQt5.QtWidgets import QMenu 5 6from urh.controller.dialogs.FilterBandwidthDialog import FilterBandwidthDialog 7from urh.signalprocessing.Filter import Filter 8from urh.ui.painting.SpectrogramScene import SpectrogramScene 9from urh.ui.painting.SpectrogramSceneManager import SpectrogramSceneManager 10from urh.ui.views.ZoomableGraphicView import ZoomableGraphicView 11from urh.util.Logger import logger 12 13 14class SpectrogramGraphicView(ZoomableGraphicView): 15 MINIMUM_VIEW_WIDTH = 10 16 y_scale_changed = pyqtSignal(float) 17 bandpass_filter_triggered = pyqtSignal(float, float) 18 export_fta_wanted = pyqtSignal() 19 20 def __init__(self, parent=None): 21 super().__init__(parent) 22 23 self.move_y_with_drag = True 24 self.scene_manager = SpectrogramSceneManager(np.zeros(1, dtype=np.complex64), parent=self) 25 self.setScene(self.scene_manager.scene) 26 27 @property 28 def y_center(self): 29 return self.sceneRect().height() // 2 30 31 @property 32 def height_spectrogram(self): 33 if self.scene_manager and self.scene_manager.spectrogram: 34 return self.scene_manager.spectrogram.freq_bins 35 else: 36 return 0 37 38 @property 39 def width_spectrogram(self): 40 if self.scene_manager and self.scene_manager.spectrogram: 41 return self.scene_manager.spectrogram.time_bins 42 else: 43 return 0 44 45 def scene(self) -> SpectrogramScene: 46 return super().scene() 47 48 def create_context_menu(self): 49 menu = QMenu() 50 menu.setToolTipsVisible(True) 51 self._add_zoom_actions_to_menu(menu) 52 53 if self.something_is_selected: 54 filter_bw = Filter.read_configured_filter_bw() 55 text = self.tr("Apply bandpass filter (filter bw={0:n})".format(filter_bw)) 56 create_from_frequency_selection = menu.addAction(text) 57 create_from_frequency_selection.triggered.connect(self.on_create_from_frequency_selection_triggered) 58 create_from_frequency_selection.setIcon(QIcon.fromTheme("view-filter")) 59 60 try: 61 cancel_button = " or ".join(k.toString() for k in QKeySequence.keyBindings(QKeySequence.Cancel)) 62 except Exception as e: 63 logger.debug("Error reading cancel button: " + str(e)) 64 cancel_button = "Esc" 65 66 create_from_frequency_selection.setToolTip("You can abort filtering with <b>{}</b>.".format(cancel_button)) 67 68 configure_filter_bw = menu.addAction(self.tr("Configure filter bandwidth...")) 69 configure_filter_bw.triggered.connect(self.on_configure_filter_bw_triggered) 70 configure_filter_bw.setIcon(QIcon.fromTheme("configure")) 71 72 menu.addSeparator() 73 74 export_fta_action = menu.addAction("Export spectrogram...") 75 export_fta_action.triggered.connect(self.on_export_fta_action_triggered) 76 77 return menu 78 79 def zoom_to_selection(self, start: int, end: int): 80 if start == end: 81 return 82 83 x_center = self.view_rect().x() + self.view_rect().width() / 2 84 y_factor = self.view_rect().height() / (end - start) 85 self.scale(1, y_factor) 86 self.centerOn(x_center, start + (end - start) / 2) 87 self.y_scale_changed.emit(y_factor) 88 89 def auto_fit_view(self): 90 pass 91 92 def emit_selection_start_end_changed(self): 93 h = self.sceneRect().height() 94 self.sel_area_start_end_changed.emit(h - self.selection_area.end, h - self.selection_area.start) 95 96 @pyqtSlot() 97 def on_create_from_frequency_selection_triggered(self): 98 self.bandpass_filter_triggered.emit(*self.__get_freqs()) 99 100 def __get_freqs(self): 101 sh = self.sceneRect().height() 102 y1, y2 = sh / 2 - self.selection_area.start, sh / 2 - self.selection_area.end 103 f_low, f_high = y1 / self.sceneRect().height(), y2 / self.sceneRect().height() 104 return f_low, f_high 105 106 @pyqtSlot() 107 def on_configure_filter_bw_triggered(self): 108 dialog = FilterBandwidthDialog(parent=self) 109 dialog.show() 110 111 @pyqtSlot() 112 def on_export_fta_action_triggered(self): 113 if not(self.scene_manager and self.scene_manager.spectrogram): 114 return 115 116 self.export_fta_wanted.emit() 117