class ControlBar(QWidget): """ Widget that contains the play/pause button, the progress bar and the speed slider. """ play = pyqtSignal(name="Play") pause = pyqtSignal(name="Pause") seek = pyqtSignal(float, name="Time changed") speedChanged = pyqtSignal(int, name="Speed changed") def __init__(self, duration: float, parent: QWidget = None): QWidget.__init__(self, parent) self.log = getLoggerPassFilters(LOGGER_NAMES.LIVEPLAYER) self.button = PlayPauseButton() self.button.setMaximumWidth(100) self.button.clicked.connect(self.onButtonClicked) self.timeSlider = ClickableProgressBar() self.timeSlider.setMinimum(0) self.timeSlider.setMaximum(int(duration * 1000)) self.timeSlider.valueChanged.connect(self.onSeek) self.speedLabel = QLabel("Speed: 1x") self.speedSlider = QSlider(Qt.Horizontal) self.speedSlider.setMaximumWidth(300) self.speedSlider.setMinimum(1) self.speedSlider.setMaximum(10) self.speedSlider.valueChanged.connect(self.onSpeedChanged) vertical = QVBoxLayout() horizontal = QHBoxLayout() horizontal.addWidget(self.speedLabel) horizontal.addWidget(self.speedSlider) horizontal.addItem(QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Expanding)) vertical.addLayout(horizontal) horizontal = QHBoxLayout() horizontal.addWidget(self.button) horizontal.addWidget(self.timeSlider) vertical.addLayout(horizontal) self.setLayout(vertical) self.setGeometry(0, 0, 80, 60) def onButtonClicked(self): if self.button.playing: self.log.debug("Play clicked") self.play.emit() else: self.log.debug("Pause clicked") self.pause.emit() def onSeek(self): time = self.timeSlider.value() / 1000.0 self.log.debug("Seek to %(arg1)d seconds", {"arg1": time}) self.seek.emit(time) def onSpeedChanged(self): speed = self.speedSlider.value() self.log.debug("Slider changed value: %(arg1)d", {"arg1": speed}) self.speedLabel.setText("Speed: {}x".format(speed)) self.speedChanged.emit(speed)
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None ): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0,4,0,0) self.setSpacing(0) self.timeControlFontSize = 12 def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def hideTimeSlider(self,flag): self.timeSlider.setHidden(flag) self.timeEndButton.setHidden(flag) self.timeStartButton.setHidden(flag) def setToolTipTimeButtonsCrop(self,croppingFlag=False): if croppingFlag==True: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current crop.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current crop.") else: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current dataset.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current dataset.") def setToolTipTimeSliderCrop(self,croppingFlag=False): if croppingFlag==True: self.timeSlider.setToolTip("Choose the time coordinate of the current crop.") else: self.timeSlider.setToolTip("Choose the time coordinate of the current dataset.") def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor): self.xLabel, self.xSpinBox = _get_pos_widget('X', xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget('Y', ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget('Z', zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'x') ) self.ySpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'y') ) self.zSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'z') ) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximumWidth(200) self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget( self.busyIndicator ) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(10) self.positionCheckBox = QCheckBox() self.positionCheckBox.setChecked(True) self.positionCheckBox.setCheckable(True) self.positionCheckBox.setText("Position") self.addWidget(self.positionCheckBox) self.addSpacing(20) self.timeSpinBox = DelayedSpinBox(750) self.timeStartButton = QPushButton("Start") self.addWidget(self.timeStartButton) self.timeStartButton.clicked.connect(self._onTimeStartButtonClicked) self.timeSlider = QSlider(Qt.Horizontal) self.timeSlider.setMinimumWidth(10) self.timeSlider.setMaximumWidth(200) self.setToolTipTimeSliderCrop() self.addWidget(self.timeSlider) self.timeSlider.valueChanged.connect(self._onTimeSliderChanged) self.timeEndButton = QPushButton("End") self.timeEndButton.setFixedWidth(4*self.timeControlFontSize) self.timeStartButton.setFixedWidth(4*self.timeControlFontSize) self.setToolTipTimeButtonsCrop() self.addWidget(self.timeEndButton) self.timeEndButton.clicked.connect(self._onTimeEndButtonClicked) self.timeLabel = QLabel(" Time:") self.addWidget(self.timeLabel) timeControlFont = self.timeSpinBox.font() if self.timeControlFontSize > timeControlFont.pointSize(): timeControlFont.setPixelSize(self.timeControlFontSize) self.timeStartButton.setFont(timeControlFont) self.timeEndButton.setFont(timeControlFont) self.timeLabel.setFont(timeControlFont) self.timeSpinBox.setFont(timeControlFont) self.addWidget(self.timeSpinBox) self.timeSpinBox.delayedValueChanged.connect(self._onTimeSpinBoxChanged) def _onTimeStartButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[0]) def _onTimeEndButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[1]) def _onTimeSpinBoxChanged(self): editor = self.parent().parent().parent().editor cropModel = editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): if minValueT > self.timeSpinBox.value() or maxValueT < self.timeSpinBox.value(): for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(True) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) self.timeSlider.setValue(self.timeSpinBox.value()) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) if minValueT > self.timeSpinBox.value(): self.timeSlider.setValue(minValueT) elif maxValueT < self.timeSpinBox.value(): self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSpinBox.value() and self.timeSpinBox.value() <= maxValueT: self.timeSlider.setValue(self.timeSpinBox.value()) def _onTimeSliderChanged(self): cropModel = self.parent().parent().parent().editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): self.timeSpinBox.setValue(self.timeSlider.value()) else: if minValueT > self.timeSlider.value(): self.timeSpinBox.setValue(minValueT) self.timeSlider.setValue(minValueT) elif self.timeSlider.value() > maxValueT: self.timeSpinBox.setValue(maxValueT) self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSlider.value() and self.timeSlider.value() <= maxValueT: self.timeSpinBox.setValue(self.timeSlider.value()) def _handlePositionBoxValueChanged(self, axis, value): new_position = [self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value()] changed_axis = ord(axis) - ord('x') new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0]-1) self.xSpinBox.setMaximum(shape5D[1]-1) self.ySpinBox.setMaximum(shape5D[2]-1) self.zSpinBox.setMaximum(shape5D[3]-1) def updateShape5Dcropped(self, shape5DcropMin, shape5Dmax): self.timeSpinBox.setMaximum(shape5Dmax[0]-1) self.xSpinBox.setMaximum(shape5Dmax[1]-1) self.ySpinBox.setMaximum(shape5Dmax[2]-1) self.zSpinBox.setMaximum(shape5Dmax[3]-1) self.timeSlider.setMaximum(shape5Dmax[0]-1) self.timeSpinBox.setValue(shape5DcropMin[0]) self.xSpinBox.setValue(shape5DcropMin[1]) self.ySpinBox.setValue(shape5DcropMin[2]) self.zSpinBox.setValue(shape5DcropMin[3]) self.timeSlider.setValue(shape5DcropMin[0]) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None ): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0,4,0,0) self.setSpacing(0) self.timeControlFontSize = 12 def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def hideTimeSlider(self,flag): self.timeSlider.setHidden(flag) self.timeEndButton.setHidden(flag) self.timeStartButton.setHidden(flag) def setToolTipTimeButtonsCrop(self,croppingFlag=False): if croppingFlag==True: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current crop.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current crop.") self.timePreviousButton.setToolTip("Set the time coordinate to the previous time frame.") self.timeNextButton.setToolTip("Set the time coordinate to the next time frame.") else: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current dataset.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current dataset.") self.timePreviousButton.setToolTip("Set the time coordinate to the previous time frame.") self.timeNextButton.setToolTip("Set the time coordinate to the next time frame.") def setToolTipTimeSliderCrop(self,croppingFlag=False): if croppingFlag==True: self.timeSlider.setToolTip("Choose the time coordinate of the current crop.") else: self.timeSlider.setToolTip("Choose the time coordinate of the current dataset.") def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor): self.xLabel, self.xSpinBox = _get_pos_widget('X', xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget('Y', ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget('Z', zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'x') ) self.ySpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'y') ) self.zSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'z') ) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximumWidth(200) self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget( self.busyIndicator ) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(10) self.positionCheckBox = QCheckBox() self.positionCheckBox.setChecked(False) self.positionCheckBox.setCheckable(True) self.positionCheckBox.setText("Position") self.addWidget(self.positionCheckBox) self.addSpacing(20) self.timeSpinBox = DelayedSpinBox(750) self.timeStartButton = QPushButton("|<") self.addWidget(self.timeStartButton) self.timeStartButton.clicked.connect(self._onTimeStartButtonClicked) self.timePreviousButton = QPushButton("<") self.addWidget(self.timePreviousButton) self.timePreviousButton.clicked.connect(self._onTimePreviousButtonClicked) self.timePreviousButton.setFixedWidth(4*self.timeControlFontSize) self.timeSlider = QSlider(Qt.Horizontal) self.timeSlider.setMinimumWidth(10) self.timeSlider.setMaximumWidth(200) self.setToolTipTimeSliderCrop() self.addWidget(self.timeSlider) self.timeSlider.valueChanged.connect(self._onTimeSliderChanged) self.timeNextButton = QPushButton(">") self.addWidget(self.timeNextButton) self.timeNextButton.clicked.connect(self._onTimeNextButtonClicked) self.timeNextButton.setFixedWidth(4*self.timeControlFontSize) self.timeEndButton = QPushButton(">|") self.timeEndButton.setFixedWidth(4*self.timeControlFontSize) self.timeStartButton.setFixedWidth(4*self.timeControlFontSize) self.setToolTipTimeButtonsCrop() self.addWidget(self.timeEndButton) self.timeEndButton.clicked.connect(self._onTimeEndButtonClicked) self.timeLabel = QLabel(" Time:") self.addWidget(self.timeLabel) timeControlFont = self.timeSpinBox.font() if self.timeControlFontSize > timeControlFont.pointSize(): timeControlFont.setPixelSize(2*self.timeControlFontSize) self.timeStartButton.setFont(timeControlFont) self.timeEndButton.setFont(timeControlFont) self.timeLabel.setFont(timeControlFont) self.timeSpinBox.setFont(timeControlFont) self.addWidget(self.timeSpinBox) self.timeSpinBox.delayedValueChanged.connect(self._onTimeSpinBoxChanged) def _onTimeStartButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[0]) def _onTimeEndButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[1]) def _onTimePreviousButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value()-1) def _onTimeNextButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value()+1) def _onTimeSpinBoxChanged(self): editor = self.parent().parent().parent().editor cropModel = editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): if minValueT > self.timeSpinBox.value() or maxValueT < self.timeSpinBox.value(): for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(True) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) self.timeSlider.setValue(self.timeSpinBox.value()) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) if minValueT > self.timeSpinBox.value(): self.timeSlider.setValue(minValueT) elif maxValueT < self.timeSpinBox.value(): self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSpinBox.value() and self.timeSpinBox.value() <= maxValueT: self.timeSlider.setValue(self.timeSpinBox.value()) def _onTimeSliderChanged(self): cropModel = self.parent().parent().parent().editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): self.timeSpinBox.setValue(self.timeSlider.value()) else: if minValueT > self.timeSlider.value(): self.timeSpinBox.setValue(minValueT) self.timeSlider.setValue(minValueT) elif self.timeSlider.value() > maxValueT: self.timeSpinBox.setValue(maxValueT) self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSlider.value() and self.timeSlider.value() <= maxValueT: self.timeSpinBox.setValue(self.timeSlider.value()) def _handlePositionBoxValueChanged(self, axis, value): new_position = [self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value()] changed_axis = ord(axis) - ord('x') new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0]-1) self.xSpinBox.setMaximum(shape5D[1]-1) self.ySpinBox.setMaximum(shape5D[2]-1) self.zSpinBox.setMaximum(shape5D[3]-1) def updateShape5Dcropped(self, shape5DcropMin, shape5Dmax): self.timeSpinBox.setMaximum(shape5Dmax[0]-1) self.xSpinBox.setMaximum(shape5Dmax[1]-1) self.ySpinBox.setMaximum(shape5Dmax[2]-1) self.zSpinBox.setMaximum(shape5Dmax[3]-1) self.timeSlider.setMaximum(shape5Dmax[0]-1) self.timeSpinBox.setValue(shape5DcropMin[0]) self.xSpinBox.setValue(shape5DcropMin[1]) self.ySpinBox.setValue(shape5DcropMin[2]) self.zSpinBox.setValue(shape5DcropMin[3]) self.timeSlider.setValue(shape5DcropMin[0]) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)
class SoundLabWindow(QtGui.QMainWindow): """ Window that encapsulates the commonly operations with a sound lab window that contains a widget (QSignalVisualizer or descendant). provides usefull operations that delegates into the widget its implementation """ # region Initialize def __init__(self, parent): QtGui.QMainWindow.__init__(self, parent) self.workSpace = Workspace() # get the status bar to show messages to the user self.statusbar = self.statusBar() self.statusbar.setSizeGripEnabled(False) # action groups of common actions for sound lab window self.play_record_actions = QActionGroup(self) self.widgets_visibility_actions = QActionGroup(self) self.zoom_actions = QActionGroup(self) self.tools_actions = QActionGroup(self) self.save_images_actions = QActionGroup(self) # play volume bar (disabled for now) self.volume_bar = QSlider(QtCore.Qt.Horizontal) self.volume_bar.setToolTip(self.tr(u"Volume bar for Play.")) self.volume_bar.setMaximumWidth(100) self.volume_bar.setRange(0, 300) self.volume_bar.setValue(100) self.volume_bar.valueChanged.connect(self.change_volume) # text edit for the signal name on the toolbar self.signalNameLineEdit = QtGui.QLineEdit(self) self.signalNameLineEdit.setToolTip(self.tr(u"Signal name.")) self.signalNameLineEdit.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) self.signalPropertiesTextLabel = QtGui.QLabel(self) self.signalPropertiesTextLabel.setToolTip(self.tr(u"Signal properties.")) self.signalPropertiesTextLabel.setAlignment(QtCore.Qt.AlignRight) self.signalPropertiesTextLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)) # endregion def configureToolBarActionsGroups(self): """ Configure the actions into groups for best visualization and user configuration. :return: """ sep1, sep2, sep3, sep4 = [QtGui.QAction(self) for _ in range(4)] for sep in [sep1, sep2, sep3, sep4]: sep.setSeparator(True) # region play record actions play_record_actions_list = [self.actionPlay_Sound, self.actionPause_Sound, self.actionStop_Sound, self.actionRecord, self.actionPlayLoop, sep2] for act in play_record_actions_list: act.setActionGroup(self.play_record_actions) # self.toolBar.addWidget(self.volume_bar) # endregion # region widgets visibility actions widgets_visibility_actions_list = [self.actionOscilogram, self.actionSpectogram, self.actionCombined, sep3] for act in widgets_visibility_actions_list: act.setActionGroup(self.widgets_visibility_actions) # endregion # region zoom actions zoom_actions_list = [self.actionZoomIn, self.actionZoom_out, self.actionZoom_out_entire_file, sep4] for act in zoom_actions_list: act.setActionGroup(self.zoom_actions) # endregion # region Save Images actions save_images_actions_list = [self.actionOsc_Image, self.actionSpecgram_Image, self.actionCombined_Image] for act in save_images_actions_list: act.setActionGroup(self.save_images_actions) # endregion # region Tools actions tools_actions_list = [self.actionZoom_Cursor, self.actionPointer_Cursor, self.actionRectangular_Cursor] for act in tools_actions_list: act.setActionGroup(self.tools_actions) # endregion actions_groups = [(self.play_record_actions, self.tr(u"Play/Record")), (self.zoom_actions, self.tr(u"Zoom")), (self.widgets_visibility_actions, self.tr(u"Widgets Visibility"))] # add to the customizable sound lab toolbar for act in actions_groups: # method sig addActionGroup(actionGroup, name) self.toolBar.addActionGroup(act[0], act[1]) # add the label for signal name (and edit line) that always wil be visible as an option # not like the other groups of actions that the user could customize visibility self.toolBar.addWidget(self.signalNameLineEdit) self.toolBar.addAction(sep1) self.toolBar.addWidget(self.signalPropertiesTextLabel) # region Widget Tools @pyqtSlot() def on_actionZoom_Cursor_triggered(self): """ Select the Zoom Tool as current working tool in the widget :return: """ self.select_tool(self.actionZoom_Cursor, Tools.ZoomTool) @pyqtSlot() def on_actionRectangular_Cursor_triggered(self): """ Select the Rectangular Cursor as current working tool in the widget :return: """ self.select_tool(self.actionRectangular_Cursor, Tools.RectangularZoomTool) @pyqtSlot() def on_actionRectangular_Eraser_triggered(self): """ Select the Rectangular Eraser as current working tool in the widget :return: """ self.select_tool(self.actionRectangular_Eraser, Tools.RectangularEraser) @pyqtSlot() def on_actionPointer_Cursor_triggered(self): """ Select the Pointer Cursor as current working tool in the widget :return: """ self.select_tool(self.actionPointer_Cursor, Tools.PointerTool) def deselectToolsActions(self): """ Change the checked status of all the actions tools to False """ self.actionZoom_Cursor.setChecked(False) self.actionRectangular_Cursor.setChecked(False) self.actionPointer_Cursor.setChecked(False) def select_tool(self, tool_action, tool_type): """ :param tool_action: the checkable action that handles the selection of the tool :param tool_type: the enum type of the tool to select in the widget :return: """ self.deselectToolsActions() tool_action.setChecked(True) self.widget.setSelectedTool(tool_type) # endregion # region Save widgets Image @pyqtSlot() def on_actionOsc_Image_triggered(self): """ Save to disc the image of the oscilogram graph. :return: """ if not self.widget.visibleOscilogram: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"The Oscilogram plot widget is not visible.") + u"\n" + self.tr( u"You should see the data that you are going to save.")) return fname = unicode(QFileDialog.getSaveFileName(self, self.tr(u"Save oscilogram graph as an Image"), u"oscilogram-Duetto-Image", u"*.jpg")) if fname: save_image(self.widget.axesOscilogram, fname) @pyqtSlot() def on_actionSpecgram_Image_triggered(self): """ Save to disc the image of the spectrogram graph. :return: """ if not self.widget.visibleSpectrogram: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"The Spectrogram plot widget is not visible.") + " \n" + self.tr( u"You should see the data that you are going to save.")) return path = unicode(QFileDialog.getSaveFileName(self, self.tr(u"Save specgram graph as an Image"), u"specgram-Duetto-Image", u"*.jpg")) save_image(self.widget.axesSpecgram, path) @pyqtSlot() def on_actionCombined_Image_triggered(self): """ Save to disc the image of the both (oscilogram and spectrogram) visualization graphs. :return: """ if not self.widget.visibleOscilogram or not self.widget.visibleSpectrogram: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"One of the plot widgets is not visible.") + " \n" + self.tr( u"You should see the data that you are going to save.")) return path = unicode(QFileDialog.getSaveFileName(self, self.tr(u"Save graph as an Image"), u"Graph Image", u"*.jpg")) save_image(self.widget, path) # endregion # region Zoom # delegate in the widget the zoom interaction with the signal @QtCore.pyqtSlot() def on_actionZoomIn_triggered(self): self.widget.zoomIn() @QtCore.pyqtSlot() def on_actionZoom_out_triggered(self): self.widget.zoomOut() @QtCore.pyqtSlot() def on_actionZoom_out_entire_file_triggered(self): self.widget.zoomNone() # endregion # region Widgets And Window Visibility @pyqtSlot() def on_actionFull_Screen_triggered(self): """ Action that switch the window visualization state between Full Screen and Normal :return: """ if self.actionFull_Screen.isChecked(): self.showFullScreen() else: self.showNormal() @pyqtSlot() def on_actionCombined_triggered(self): """ Shows both axes visualization oscilogram and spectrogram. :return: """ self.changeWidgetsVisibility(True, True) @pyqtSlot() def on_actionSpectogram_triggered(self): """ Shows the spectrogram visualization graph only. :return: """ self.changeWidgetsVisibility(False, True) @pyqtSlot() def on_actionOscilogram_triggered(self): """ Shows the oscilogram visualization graph only. :return: """ self.changeWidgetsVisibility(True, False) def changeWidgetsVisibility(self, visibleOscilogram=True, visibleSpectrogram=True): """ Method that change the visibility of the widgets oscilogram and spectrogram on the main widget :param visibleOscilogram: Visibility of the oscilogram :param visibleSpectrogram: Visibility of the spectrogram :return: """ self.widget.visibleOscilogram = visibleOscilogram self.widget.visibleSpectrogram = visibleSpectrogram # udpate the workspace self.workSpace.visibleOscilogram = visibleOscilogram self.workSpace.visibleSpectrogram = visibleSpectrogram self.widget.graph() # endregion # region Play, Pause, Stop, Record # delegate in the widget reproduction actions def change_volume(self, volume): # change volume in the player of the widget if self.widget: self.widget.change_volume(volume) self.update_status_bar(self.tr(u"The volume has been changed to "+unicode(volume) + u"%."), 1000) @pyqtSlot() def on_actionPlay_Sound_triggered(self): try: self.widget.play() except Exception as ex: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"There is no selected audio input " u"device or the selected is unavailable")) @pyqtSlot() def on_actionPlayLoop_triggered(self): self.widget.setPlayLoopEnabled(self.actionPlayLoop.isChecked()) @pyqtSlot() def on_actionStop_Sound_triggered(self): self.widget.stop() # if previous status was recording the length of signal has changed and must be updated self.updateSignalPropertiesLabel() @pyqtSlot() def on_actionPause_Sound_triggered(self): self.widget.pause() @pyqtSlot() def on_actionSwitchPlayStatus_triggered(self): """ Change the play status of the signal from play-pause and vice versa :return: """ self.widget.switchPlayStatus() # endregion # region Edition and Processing Methods # region Undo Redo @pyqtSlot() def on_actionUndo_triggered(self): self.widget.undo() self.updateSignalPropertiesLabel() @pyqtSlot() def on_actionRedo_triggered(self): self.widget.redo() self.updateSignalPropertiesLabel() # endregion # region Cut, Copy, Paste @pyqtSlot() def on_actionCut_triggered(self): self.widget.cut() self.updateSignalPropertiesLabel() @pyqtSlot() def on_actionCopy_triggered(self): self.widget.copy() @pyqtSlot() def on_actionPaste_triggered(self): self.widget.paste() self.updateSignalPropertiesLabel() # endregion # endregion def update_status_bar(self, line, time_ms=None): """ Set a new message in the status bar of the window. :type time: the time that the line message wouold be visible in status bar. :param line: string with the line to show in the status bar """ time_ms = 1500 if time_ms is None else time_ms self.statusbar.showMessage(line, time_ms) def updateSignalPropertiesLabel(self, signal = None): """ Updates the text of the current signal properties in toolbar. :return: """ signal = self.widget.signal if signal is None else signal # action signal is a place in the tool bar to show the current signal name self.signalNameLineEdit.setText(signal.name) sr, bit_depth, channels = signal.samplingRate, signal.bitDepth, signal.channelCount properties = u" " + \ u" <b>" + self.tr(u"Sampling Rate: ") + u"</b>" + unicode(sr) + u" " + \ u" <b>" + self.tr(u"Bit Depth: ") + u"</b>" + unicode(bit_depth) + u" " + \ u" <b>" + self.tr(u"Channels: ") + u"</b>" + unicode(channels) + u" " + \ u" <b>" + self.tr(u"Duration(s): ") + u"</b>" + unicode(round(signal.duration, DECIMAL_PLACES)) + \ u" " self.signalPropertiesTextLabel.setText(properties)
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0, 4, 0, 0) self.setSpacing(0) self.timeControlFontSize = 12 def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def hideTimeSlider(self, flag): visibleBefore = not self.timeSlider.isHidden() self.timeSlider.setHidden(flag) self.timeEndButton.setHidden(flag) self.timeStartButton.setHidden(flag) self.timePreviousButton.setHidden(flag) self.timeNextButton.setHidden(flag) self._registerTimeframeShortcuts(enabled=not flag, remove=visibleBefore) def setToolTipTimeButtonsCrop(self, croppingFlag=False): if croppingFlag == True: self.timeStartButton.setToolTip( "Jump to lirst frame of current crop") self.timeEndButton.setToolTip("Jump to last frame of current crop") self.timePreviousButton.setToolTip("Previous Frame") self.timeNextButton.setToolTip("Next Frame") else: self.timeStartButton.setToolTip("First Frame") self.timeEndButton.setToolTip("Last Frame") self.timePreviousButton.setToolTip("Previous Frame") self.timeNextButton.setToolTip("Next Frame") def setToolTipTimeSliderCrop(self, croppingFlag=False): if croppingFlag == True: self.timeSlider.setToolTip( "Choose the time coordinate of the current crop.") else: self.timeSlider.setToolTip( "Choose the time coordinate of the current dataset.") def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor): self.xLabel, self.xSpinBox = _get_pos_widget('X', xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget('Y', ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget('Z', zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'x')) self.ySpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'y')) self.zSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'z')) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.crosshairsCheckbox = QCheckBox() self.crosshairsCheckbox.setChecked(False) self.crosshairsCheckbox.setCheckable(True) self.crosshairsCheckbox.setText("Crosshairs") self.addWidget(self.crosshairsCheckbox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximumWidth(200) self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget(self.busyIndicator) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(20) self.timeSpinBox = DelayedSpinBox(750) icons_dir = os.path.dirname(volumina.__file__) + "/icons" self.timeStartButton = QToolButton() self.timeStartButton.setIcon(QIcon(icons_dir + "/skip-start.png")) self.addWidget(self.timeStartButton) self.timeStartButton.clicked.connect(self._onTimeStartButtonClicked) #self.timeStartButton.setFixedWidth(4*self.timeControlFontSize) self.timePreviousButton = QToolButton() self.timePreviousButton.setIcon(QIcon(icons_dir + "/play-reverse.png")) self.addWidget(self.timePreviousButton) self.timePreviousButton.clicked.connect( self._onTimePreviousButtonClicked) #self.timePreviousButton.setFixedWidth(4*self.timeControlFontSize) self.timeSlider = QSlider(Qt.Horizontal) self.timeSlider.setMinimumWidth(10) self.timeSlider.setMaximumWidth(200) self.setToolTipTimeSliderCrop() self.addWidget(self.timeSlider) self.timeSlider.valueChanged.connect(self._onTimeSliderChanged) self.timeNextButton = QToolButton() self.timeNextButton.setIcon(QIcon(icons_dir + "/play.png")) self.addWidget(self.timeNextButton) self.timeNextButton.clicked.connect(self._onTimeNextButtonClicked) #self.timeNextButton.setFixedWidth(4*self.timeControlFontSize) self.timeEndButton = QToolButton() self.timeEndButton.setIcon(QIcon(icons_dir + "/skip-end.png")) #self.timeEndButton.setFixedWidth(4*self.timeControlFontSize) self.setToolTipTimeButtonsCrop() self.addWidget(self.timeEndButton) self.timeEndButton.clicked.connect(self._onTimeEndButtonClicked) self.timeLabel = QLabel(" Time:") self.addWidget(self.timeLabel) timeControlFont = self.timeSpinBox.font() if self.timeControlFontSize > timeControlFont.pointSize(): timeControlFont.setPixelSize(2 * self.timeControlFontSize) self.timeStartButton.setFont(timeControlFont) self.timeEndButton.setFont(timeControlFont) self.timeLabel.setFont(timeControlFont) self.timeSpinBox.setFont(timeControlFont) self.addWidget(self.timeSpinBox) self.timeSpinBox.delayedValueChanged.connect( self._onTimeSpinBoxChanged) self._registerTimeframeShortcuts() def _registerTimeframeShortcuts(self, enabled=True, remove=True): """ Register or deregister "," and "." as keyboard shortcuts for scrolling in time """ mgr = ShortcutManager() ActionInfo = ShortcutManager.ActionInfo def action(key, actionInfo): if enabled: mgr.register(key, actionInfo) else: if remove: mgr.unregister(actionInfo) action( "<", ActionInfo("Navigation", "Go to next time frame", "Go to next time frame", self._onTimeNextButtonClicked, self.timeNextButton, self.timeNextButton)) action( ">", ActionInfo("Navigation", "Go to previous time frame", "Go to previous time frame", self._onTimePreviousButtonClicked, self.timePreviousButton, self.timePreviousButton)) def _onTimeStartButtonClicked(self): self.timeSpinBox.setValue( self.parent().parent().parent().editor.cropModel.get_roi_t()[0]) def _onTimeEndButtonClicked(self): self.timeSpinBox.setValue( self.parent().parent().parent().editor.cropModel.get_roi_t()[1]) def _onTimePreviousButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value() - 1) def _onTimeNextButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value() + 1) def _onTimeSpinBoxChanged(self): editor = self.parent().parent().parent().editor cropModel = editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): if minValueT > self.timeSpinBox.value( ) or maxValueT < self.timeSpinBox.value(): for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame( True) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame( False) self.timeSlider.setValue(self.timeSpinBox.value()) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame( False) if minValueT > self.timeSpinBox.value(): self.timeSlider.setValue(minValueT) elif maxValueT < self.timeSpinBox.value(): self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSpinBox.value( ) and self.timeSpinBox.value() <= maxValueT: self.timeSlider.setValue(self.timeSpinBox.value()) def _onTimeSliderChanged(self): cropModel = self.parent().parent().parent().editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): self.timeSpinBox.setValue(self.timeSlider.value()) else: if minValueT > self.timeSlider.value(): self.timeSpinBox.setValue(minValueT) self.timeSlider.setValue(minValueT) elif self.timeSlider.value() > maxValueT: self.timeSpinBox.setValue(maxValueT) self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSlider.value( ) and self.timeSlider.value() <= maxValueT: self.timeSpinBox.setValue(self.timeSlider.value()) def _handlePositionBoxValueChanged(self, axis, value): new_position = [ self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value() ] changed_axis = ord(axis) - ord('x') new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0] - 1) self.xSpinBox.setMaximum(shape5D[1] - 1) self.ySpinBox.setMaximum(shape5D[2] - 1) self.zSpinBox.setMaximum(shape5D[3] - 1) def updateShape5Dcropped(self, shape5DcropMin, shape5Dmax): self.timeSpinBox.setMaximum(shape5Dmax[0] - 1) self.xSpinBox.setMaximum(shape5Dmax[1] - 1) self.ySpinBox.setMaximum(shape5Dmax[2] - 1) self.zSpinBox.setMaximum(shape5Dmax[3] - 1) self.timeSlider.setMaximum(shape5Dmax[0] - 1) self.timeSpinBox.setValue(shape5DcropMin[0]) self.xSpinBox.setValue(shape5DcropMin[1]) self.ySpinBox.setValue(shape5DcropMin[2]) self.zSpinBox.setValue(shape5DcropMin[3]) self.timeSlider.setValue(shape5DcropMin[0]) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)