Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HorizontalSliderWithBrowser: slider with play button #4176

Merged
merged 12 commits into from
Oct 7, 2024
121 changes: 121 additions & 0 deletions src/silx/gui/widgets/FrameBrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# THE SOFTWARE.
#
# ###########################################################################*/
from __future__ import annotations
"""This module defines two main classes:

- :class:`FrameBrowser`: a widget with 4 buttons (first, previous, next,
Expand Down Expand Up @@ -218,6 +219,70 @@ def setValue(self, value):
self._textChangedSlot()


class _SliderPlayWidgetAction(qt.QWidgetAction):

sigValueChanged = qt.Signal(int)

def __init__(
self,
parent: qt.QWidget | None = None,
label: str | None = None,
tooltip: str | None = None,
):
super().__init__(parent)
self._build(label=label, tooltip=tooltip)

def _build(self, label: str, tooltip: str):
widget = qt.QWidget()
layout = qt.QHBoxLayout()
widget.setLayout(layout)
self._spinbox = qt.QSpinBox()
self._spinbox.setToolTip(tooltip)
self._spinbox.setRange(1,1000000)
self._spinbox.valueChanged.connect(self.sigValueChanged)
label = qt.QLabel(label)
label.setToolTip(tooltip)
layout.addWidget(label)
layout.addWidget(self._spinbox)
self.setDefaultWidget(widget)

def value(self) -> int:
return self._spinbox.value()

def setValue(self, value: int):
self._spinbox.setValue(value)


class _PlayButtonContextMenu(qt.QMenu):

sigFrameRateChanged = qt.Signal(int)

def __init__(self, parent: qt.QWidget | None = None):
super().__init__(parent)
self._build()

def _build(self):
self._framerateAction = _SliderPlayWidgetAction(self, label="FPS:", tooltip="Display speed in frames per second")
self._framerateAction.sigValueChanged.connect(self.sigFrameRateChanged)
self._stepAction = _SliderPlayWidgetAction(self, label="Step:", tooltip="Step between displayed frames")
self.addAction(self._framerateAction)
self._framerateAction.setValue(10)
self.addAction(self._stepAction)
self._stepAction.setValue(1)

def getFrameRate(self) -> int:
return self._framerateAction.value()

def setFrameRate(self, rate: int):
self._framerateAction.setValue(rate)

def getStep(self) -> int:
return self._stepAction.value()

def setStep(self, interval: int):
self._stepAction.setValue(interval)


class HorizontalSliderWithBrowser(qt.QAbstractSlider):
"""
Slider widget combining a :class:`QSlider` and a :class:`FrameBrowser`.
Expand Down Expand Up @@ -254,6 +319,27 @@ def __init__(self, parent=None):
self._slider.valueChanged[int].connect(self._sliderSlot)
self._browser.sigIndexChanged.connect(self._browserSlot)

fontMetric = self.fontMetrics()
iconSize = qt.QSize(fontMetric.height(), fontMetric.height())

self.__timer = qt.QTimer(self)
self.__timer.timeout.connect(self._updateState)

self._playButton = qt.QToolButton(self)
self._playButton.setToolTip("Display dataset movie.")
self._playButton.setIcon(icons.getQIcon("camera"))
self._playButton.setIconSize(iconSize)
self._playButton.setCheckable(True)
self.mainLayout.addWidget(self._playButton)

self._playButton.toggled.connect(self._playButtonToggled)
self._menuPlaySlider = _PlayButtonContextMenu(self)
self._menuPlaySlider.sigFrameRateChanged.connect(self._frameRateChanged)
self._frameRateChanged(self.getFrameRate())
self._playButton.setMenu(self._menuPlaySlider)
self._playButton.setPopupMode(qt.QToolButton.MenuButtonPopup)


def lineEdit(self):
"""Returns the line edit provided by this widget.

Expand Down Expand Up @@ -312,3 +398,38 @@ def setValue(self, value):
def value(self):
"""Get selected value"""
return self._slider.value()

def setFrameRate(self, value: int):
"""Set the frame rate at which images are displayed"""
self._menuPlaySlider.setFrameRate(value)

def getFrameRate(self) -> int:
"""Get the frame rate at which images are displayed"""
return self._menuPlaySlider.getFrameRate()

def setPlayImageStep(self, value: int):
"""Set the step between displayed images when playing"""
self._menuPlaySlider.setStep(value)

def getPlayImageStep(self) -> int:
"""Returns the step between displayed images"""
return self._menuPlaySlider.getStep()

def _frameRateChanged(self, framerate: int):
"""Update the timer interval"""
self.__timer.setInterval(int(1 / framerate * 1e3))

def _playButtonToggled(self, checked: bool):
"""Start/Stop the slider sequence."""
if checked:
self.__timer.start()
return
self.__timer.stop()

def _updateState(self):
"""Advance an interval number of frames in the browser sequence."""
currentIndex = self._browser.getValue()
if currentIndex < self._browser.getRange()[-1]:
self.setValue(currentIndex + self.getPlayImageStep())
else:
self._playButton.setChecked(False)
Loading