|
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): |
66 | 10 | change_volume_needed = pyqtSignal([int])
|
67 | 11 |
|
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) |
71 | 14 |
|
72 |
| - self._icons = icons |
73 |
| - if self._icons: |
74 |
| - self._icon = VolumeButton.UNMUTED_ICON |
75 |
| - self.setIcon(self._icons['unmuted']) |
| 15 | + self.setToolTip('调整音量') |
76 | 16 |
|
77 |
| - self.slider = _Slider(self) |
78 |
| - self.slider.hide() |
| 17 | + font = self.font() |
| 18 | + font.setPixelSize(length // 3) |
| 19 | + self.setFont(font) |
79 | 20 |
|
80 |
| - self.setCheckable(True) |
| 21 | + self.__pressed = False |
| 22 | + self.__checked = False |
81 | 23 |
|
82 |
| - # TODO: set maximum width in parent widget |
83 |
| - self.setMaximumWidth(40) |
| 24 | + self.setMinimum(0) |
| 25 | + self.setMaximum(100) |
84 | 26 |
|
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) |
88 | 31 |
|
89 | 32 | 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