Skip to content

Commit cc5348e

Browse files
authored
gui: brand new volume slider (#807)
1 parent 9631a09 commit cc5348e

File tree

3 files changed

+67
-101
lines changed

3 files changed

+67
-101
lines changed

feeluown/gui/drawers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ def __init__(self, length, padding):
341341
self._line3 = (QPointF(line_left_x, line_left_bottom_y),
342342
QPointF(body_length, line_right_bottom_y))
343343

344+
def get_volume(self):
345+
return self._volume
346+
344347
def set_volume(self, volume):
345348
self._volume = volume
346349

feeluown/gui/uimain/player_bar.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,10 @@ def __init__(self, *args, **kwargs):
5757
self.toggle_watch_btn = WatchButton(self._app, self)
5858

5959
self.playlist_btn.setObjectName('playlist_btn')
60-
self.volume_btn.setObjectName('volume_btn')
6160
self.download_btn.setObjectName('download_btn')
6261

6362
self.progress_slider = ProgressSlider(app=app, parent=self)
6463

65-
self.volume_btn.setToolTip('调整音量')
6664
self.playlist_btn.setToolTip('显示当前播放列表')
6765
self.download_btn.setToolTip('下载歌曲(未实现,欢迎 PR)')
6866
self.download_btn.setCheckable(True)

feeluown/gui/widgets/volume_button.py

Lines changed: 64 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,73 @@
1-
from PyQt5.QtCore import Qt, QPoint, pyqtSignal
2-
from PyQt5.QtWidgets import (
3-
QVBoxLayout,
4-
QSlider,
5-
QWidget,
6-
)
7-
8-
from feeluown.gui.widgets import VolumeButton as _VolumeButton
9-
10-
11-
class _Slider(QWidget):
12-
"""A popup slider.
13-
14-
TODO: this slide can become a independent component?
15-
TODO: draw border radius for widget
16-
NOTE: inherit from QWidget instead of QSlider since QSlider can not
17-
work with Qt.Popup window flag well. Currently, I don't know why.
18-
"""
19-
20-
about_to_hide = pyqtSignal()
21-
22-
def __init__(self, parent=None, initial_value=100):
23-
super().__init__(parent)
24-
25-
self._slider = QSlider(self)
26-
self._layout = QVBoxLayout(self)
27-
self._layout.addWidget(self._slider)
28-
self._layout.setSpacing(0)
29-
# self._layout.setContentsMargins(0, 0, 0, 0)
30-
31-
# map some slider signals and methods to widget
32-
self.sliderMoved = self._slider.sliderMoved
33-
self.setValue = self._slider.setValue
34-
35-
self._slider.setMinimum(0)
36-
self._slider.setMaximum(100)
37-
self._slider.setValue(initial_value)
38-
self.setWindowFlags(Qt.Popup)
39-
40-
def is_mute(self):
41-
return self._slider.value() <= 0
42-
43-
def hideEvent(self, event):
44-
super().hideEvent(event)
45-
self.about_to_hide.emit()
46-
47-
def showEvent(self, event):
48-
# TODO: move the position calculating logic to VolumeButton class
49-
# In general, the widget itself do not care about its position
50-
parent = self.parent()
51-
if parent:
52-
assert isinstance(parent, QWidget)
53-
pgeom = parent.geometry()
54-
geom = self.geometry()
55-
x = (pgeom.width() - geom.width())//2
56-
y = -geom.height() - 10
57-
point = QPoint(x, y)
58-
self.move(parent.mapToGlobal(point))
59-
60-
61-
class VolumeButton(_VolumeButton):
62-
UNMUTED_ICON = 0
63-
MUTED_ICON = 1
64-
65-
#: (0, 100)
1+
from PyQt5.QtCore import pyqtSignal, Qt
2+
from PyQt5.QtGui import QPainter, QPalette
3+
from PyQt5.QtWidgets import QAbstractSlider
4+
5+
from feeluown.gui.drawers import VolumeIconDrawer
6+
from feeluown.gui.helpers import painter_save, darker_or_lighter
7+
8+
9+
class VolumeButton(QAbstractSlider):
6610
change_volume_needed = pyqtSignal([int])
6711

68-
def __init__(self, parent=None, icons=None, **kwargs):
69-
# TODO: let slider have orientation?
70-
super().__init__(parent=parent, **kwargs)
12+
def __init__(self, length=30, padding=0.25, parent=None):
13+
super().__init__(parent=parent)
7114

72-
self._icons = icons
73-
if self._icons:
74-
self._icon = VolumeButton.UNMUTED_ICON
75-
self.setIcon(self._icons['unmuted'])
15+
self.setToolTip('调整音量')
7616

77-
self.slider = _Slider(self)
78-
self.slider.hide()
17+
font = self.font()
18+
font.setPixelSize(length // 3)
19+
self.setFont(font)
7920

80-
self.setCheckable(True)
21+
self.__pressed = False
22+
self.__checked = False
8123

82-
# TODO: set maximum width in parent widget
83-
self.setMaximumWidth(40)
24+
self.setMinimum(0)
25+
self.setMaximum(100)
8426

85-
self.slider.about_to_hide.connect(lambda: self.setChecked(False))
86-
self.slider.sliderMoved.connect(self.on_slider_moved)
87-
self.clicked.connect(self.slider.show)
27+
padding = int(length * padding if padding < 1 else padding)
28+
self.drawer = VolumeIconDrawer(length, padding)
29+
self.valueChanged.connect(self.change_volume_needed.emit)
30+
self.setFixedSize(length, length)
8831

8932
def on_volume_changed(self, value):
90-
"""(alpha)
91-
92-
.. versionadd:: 3.4
93-
"""
94-
self.slider.setValue(value)
95-
self.set_volume(value)
96-
97-
def on_slider_moved(self, value):
98-
self.change_volume_needed.emit(value)
99-
100-
# update button icon
101-
if not self._icons:
102-
return
103-
if self.slider.is_mute():
104-
self.setIcon(self._icons['muted'])
105-
self._icon = VolumeButton.MUTED_ICON
106-
elif self._icon == VolumeButton.MUTED_ICON:
107-
self.setIcon(self._icons['unmuted'])
108-
self._icon = VolumeButton.UNMUTED_ICON
33+
# blockSignals to avoid circular setVolume.
34+
# https://stackoverflow.com/a/4146392/4302892
35+
self.blockSignals(True)
36+
self.setValue(value)
37+
self.drawer.set_volume(value)
38+
self.blockSignals(False)
39+
self.update()
40+
41+
def paintEvent(self, _) -> None:
42+
painter = QPainter(self)
43+
painter.setRenderHint(QPainter.Antialiasing)
44+
if self.__checked is True:
45+
with painter_save(painter):
46+
painter.setPen(Qt.NoPen)
47+
color = self.palette().color(QPalette.Background)
48+
painter.setBrush(darker_or_lighter(color, 120))
49+
painter.drawEllipse(self.rect())
50+
painter.drawText(self.rect(), Qt.AlignCenter, f'{self.value()}%')
51+
else:
52+
self.drawer.draw(painter, self.palette())
53+
54+
def mousePressEvent(self, e) -> None:
55+
super().mousePressEvent(e)
56+
if e.button() == Qt.LeftButton:
57+
self.__pressed = True
58+
59+
def mouseReleaseEvent(self, e):
60+
super().mouseReleaseEvent(e)
61+
if e.button() == Qt.LeftButton:
62+
if self.__pressed is True:
63+
self.__pressed = False
64+
self.__checked = not self.__checked
65+
# schedule an update to refresh ASAP.
66+
self.update()
67+
68+
69+
if __name__ == '__main__':
70+
from feeluown.gui.debug import simple_layout
71+
72+
with simple_layout() as layout:
73+
layout.addWidget(VolumeButton(100))

0 commit comments

Comments
 (0)