Source code for wheel.wheel

"""

WheelWidget
(c) 2025 JDXI Editor

"""

from PySide6.QtCore import Property, QPropertyAnimation, QRectF, Signal
from PySide6.QtGui import QColor, QLinearGradient, QMouseEvent, QPainter, QPen
from PySide6.QtWidgets import QWidget


[docs] class WheelWidget(QWidget): """ Wheel Widget for Pitch and Mod Wheels """
[docs] valueChanged = Signal(float)
def __init__( self, parent: object | None = None, bidirectional: bool = False, label: str = "Wheel", ) -> None: super().__init__(parent)
[docs] self._value = 0.0
[docs] self.label = label
[docs] self.bidirectional = bidirectional
self.setMinimumSize(40, 100) self.setMouseTracking(True)
[docs] self._drag_active = False
# Smooth snap-back animation
[docs] self.snap_animation = QPropertyAnimation(self, b"value")
self.snap_animation.setDuration(300) # self.snap_animation.setEasingCurve(Qt.EaseOutCubic)
[docs] def get_value(self) -> float: return self._value
[docs] def set_value(self, value: float) -> None: """ set_value :param value: float :return: None """ clamped = max(-1.0 if self.bidirectional else 0.0, min(1.0, value)) if clamped != self._value: self._value = clamped self.valueChanged.emit(self._value) self.update()
[docs] value = Property(float, get_value, set_value)
[docs] def paintEvent(self, event: QMouseEvent) -> None: """ paintEvent :param event: QMouseEvent :return: None """ painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # Background and border # Set pen for the border painter.setPen(QPen(QColor("#000000"), 2)) # Create a vertical gradient from top to bottom gradient = QLinearGradient(self.rect().topLeft(), self.rect().bottomLeft()) gradient.setColorAt(0.0, QColor("#111111")) gradient.setColorAt(0.5, QColor("#333333")) gradient.setColorAt(1.0, QColor("#000000")) # Set the gradient as the brush for filling painter.setBrush(gradient) # Draw the rectangle with the gradient fill and border painter.drawRect(self.rect()) # Draw wheel position notch_height = 25 usable_height = self.height() - 40 center_y = self.height() / 2 offset = -self._value * ( usable_height / 2 if self.bidirectional else usable_height ) wheel_y = center_y + offset - notch_height / 2 wheel_rect = QRectF(0, wheel_y, self.width(), int(notch_height)) # Create a vertical gradient for the wheel gradient = QLinearGradient(wheel_rect.topLeft(), wheel_rect.bottomLeft()) gradient.setColorAt(0.0, QColor("black")) gradient.setColorAt(0.5, QColor("#222222")) gradient.setColorAt(1.0, QColor("black")) painter.setBrush(gradient) painter.setPen(QPen(QColor("black"), 2)) painter.drawRect(wheel_rect) # Draw label above the wheel painter.save() painter.restore()
[docs] def mousePressEvent(self, event: QMouseEvent) -> None: """ mousePressEvent :param event: QMouseEvent :return: None """ self._drag_active = True self.snap_animation.stop() self._update_value_from_mouse(event.pos().y())
[docs] def mouseMoveEvent(self, event: QMouseEvent) -> None: """ mouseMoveEvent :param event: QMouseEvent :return: None """ if self._drag_active: self._update_value_from_mouse(event.pos().y())
[docs] def mouseReleaseEvent(self, event: QMouseEvent) -> None: """ mouseReleaseEvent :param event: QMouseEvent :return: None """ self._drag_active = False if self.bidirectional: self.snap_animation.stop() self.snap_animation.setStartValue(self._value) self.snap_animation.setEndValue(0.0) self.snap_animation.start()
[docs] def _update_value_from_mouse(self, y: int) -> None: """ _update_value_from_mouse :param y: int :return: None """ h = self.height() if self.bidirectional: center = h / 2 new_val = max(-1.0, min(1.0, (center - y) / (h / 2))) else: new_val = max(0.0, min(1.0, 1.0 - y / h)) self.set_value(new_val)