Python-FastUI-Widgets 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastuiwidgets/__init__.py +12 -0
- fastuiwidgets/_rc/__init__.py +0 -0
- fastuiwidgets/_rc/resource.py +98835 -0
- fastuiwidgets/common/__init__.py +12 -0
- fastuiwidgets/common/animation.py +530 -0
- fastuiwidgets/common/auto_wrap.py +164 -0
- fastuiwidgets/common/color.py +95 -0
- fastuiwidgets/common/config.py +423 -0
- fastuiwidgets/common/exception_handler.py +31 -0
- fastuiwidgets/common/font.py +38 -0
- fastuiwidgets/common/icon.py +703 -0
- fastuiwidgets/common/image_utils.py +198 -0
- fastuiwidgets/common/overload.py +47 -0
- fastuiwidgets/common/router.py +133 -0
- fastuiwidgets/common/screen.py +25 -0
- fastuiwidgets/common/smooth_scroll.py +141 -0
- fastuiwidgets/common/style_sheet.py +512 -0
- fastuiwidgets/common/theme_listener.py +27 -0
- fastuiwidgets/common/translator.py +14 -0
- fastuiwidgets/components/__init__.py +6 -0
- fastuiwidgets/components/date_time/__init__.py +4 -0
- fastuiwidgets/components/date_time/calendar_picker.py +121 -0
- fastuiwidgets/components/date_time/calendar_view.py +671 -0
- fastuiwidgets/components/date_time/date_picker.py +245 -0
- fastuiwidgets/components/date_time/fast_calendar_view.py +487 -0
- fastuiwidgets/components/date_time/picker_base.py +632 -0
- fastuiwidgets/components/date_time/time_picker.py +223 -0
- fastuiwidgets/components/dialog_box/__init__.py +6 -0
- fastuiwidgets/components/dialog_box/color_dialog.py +414 -0
- fastuiwidgets/components/dialog_box/dialog.py +167 -0
- fastuiwidgets/components/dialog_box/folder_list_dialog.py +307 -0
- fastuiwidgets/components/dialog_box/mask_dialog_base.py +120 -0
- fastuiwidgets/components/dialog_box/message_box_base.py +92 -0
- fastuiwidgets/components/dialog_box/message_dialog.py +65 -0
- fastuiwidgets/components/layout/__init__.py +3 -0
- fastuiwidgets/components/layout/expand_layout.py +96 -0
- fastuiwidgets/components/layout/flow_layout.py +236 -0
- fastuiwidgets/components/layout/v_box_layout.py +41 -0
- fastuiwidgets/components/material/__init__.py +6 -0
- fastuiwidgets/components/material/acrylic_combo_box.py +96 -0
- fastuiwidgets/components/material/acrylic_flyout.py +105 -0
- fastuiwidgets/components/material/acrylic_line_edit.py +27 -0
- fastuiwidgets/components/material/acrylic_menu.py +204 -0
- fastuiwidgets/components/material/acrylic_tool_tip.py +39 -0
- fastuiwidgets/components/material/acrylic_widget.py +42 -0
- fastuiwidgets/components/navigation/__init__.py +9 -0
- fastuiwidgets/components/navigation/breadcrumb.py +350 -0
- fastuiwidgets/components/navigation/navigation_bar.py +416 -0
- fastuiwidgets/components/navigation/navigation_interface.py +268 -0
- fastuiwidgets/components/navigation/navigation_panel.py +657 -0
- fastuiwidgets/components/navigation/navigation_widget.py +686 -0
- fastuiwidgets/components/navigation/pivot.py +272 -0
- fastuiwidgets/components/navigation/segmented_widget.py +174 -0
- fastuiwidgets/components/settings/__init__.py +8 -0
- fastuiwidgets/components/settings/custom_color_setting_card.py +139 -0
- fastuiwidgets/components/settings/expand_setting_card.py +390 -0
- fastuiwidgets/components/settings/folder_list_setting_card.py +134 -0
- fastuiwidgets/components/settings/options_setting_card.py +86 -0
- fastuiwidgets/components/settings/setting_card.py +449 -0
- fastuiwidgets/components/settings/setting_card_group.py +48 -0
- fastuiwidgets/components/widgets/__init__.py +41 -0
- fastuiwidgets/components/widgets/acrylic_label.py +261 -0
- fastuiwidgets/components/widgets/button.py +1059 -0
- fastuiwidgets/components/widgets/card_widget.py +369 -0
- fastuiwidgets/components/widgets/check_box.py +203 -0
- fastuiwidgets/components/widgets/combo_box.py +556 -0
- fastuiwidgets/components/widgets/command_bar.py +636 -0
- fastuiwidgets/components/widgets/cycle_list_widget.py +251 -0
- fastuiwidgets/components/widgets/flip_view.py +430 -0
- fastuiwidgets/components/widgets/flyout.py +521 -0
- fastuiwidgets/components/widgets/frameless_window.py +49 -0
- fastuiwidgets/components/widgets/icon_widget.py +53 -0
- fastuiwidgets/components/widgets/info_badge.py +483 -0
- fastuiwidgets/components/widgets/info_bar.py +596 -0
- fastuiwidgets/components/widgets/label.py +553 -0
- fastuiwidgets/components/widgets/line_edit.py +551 -0
- fastuiwidgets/components/widgets/list_view.py +158 -0
- fastuiwidgets/components/widgets/menu.py +1318 -0
- fastuiwidgets/components/widgets/pips_pager.py +331 -0
- fastuiwidgets/components/widgets/progress_bar.py +311 -0
- fastuiwidgets/components/widgets/progress_ring.py +212 -0
- fastuiwidgets/components/widgets/scroll_area.py +125 -0
- fastuiwidgets/components/widgets/scroll_bar.py +673 -0
- fastuiwidgets/components/widgets/separator.py +43 -0
- fastuiwidgets/components/widgets/slider.py +307 -0
- fastuiwidgets/components/widgets/spin_box.py +306 -0
- fastuiwidgets/components/widgets/stacked_widget.py +211 -0
- fastuiwidgets/components/widgets/state_tool_tip.py +188 -0
- fastuiwidgets/components/widgets/switch_button.py +312 -0
- fastuiwidgets/components/widgets/tab_view.py +804 -0
- fastuiwidgets/components/widgets/table_view.py +360 -0
- fastuiwidgets/components/widgets/teaching_tip.py +657 -0
- fastuiwidgets/components/widgets/tool_tip.py +460 -0
- fastuiwidgets/components/widgets/tree_view.py +216 -0
- fastuiwidgets/multimedia/__init__.py +3 -0
- fastuiwidgets/multimedia/media_play_bar.py +319 -0
- fastuiwidgets/multimedia/media_player.py +124 -0
- fastuiwidgets/multimedia/video_widget.py +93 -0
- fastuiwidgets/window/__init__.py +2 -0
- fastuiwidgets/window/fluent_window.py +413 -0
- fastuiwidgets/window/splash_screen.py +92 -0
- fastuiwidgets/window/stacked_widget.py +66 -0
- python_fastui_widgets-1.0.0.dist-info/METADATA +30 -0
- python_fastui_widgets-1.0.0.dist-info/RECORD +107 -0
- python_fastui_widgets-1.0.0.dist-info/WHEEL +5 -0
- python_fastui_widgets-1.0.0.dist-info/licenses/LICENSE +674 -0
- python_fastui_widgets-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,319 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from PySide6.QtCore import Qt, Signal, QSize, QPropertyAnimation, QPoint
|
3
|
+
from PySide6.QtGui import QPixmap, QPainter, QColor
|
4
|
+
from PySide6.QtWidgets import QWidget, QGraphicsOpacityEffect, QHBoxLayout, QVBoxLayout
|
5
|
+
|
6
|
+
from ..common.icon import FluentIcon
|
7
|
+
from ..common.style_sheet import isDarkTheme, FluentStyleSheet
|
8
|
+
from ..components.widgets.button import TransparentToolButton
|
9
|
+
from ..components.widgets.tool_tip import ToolTipFilter
|
10
|
+
from ..components.widgets.slider import Slider
|
11
|
+
from ..components.widgets.label import CaptionLabel
|
12
|
+
from ..components.widgets.flyout import Flyout, FlyoutViewBase, PullUpFlyoutAnimationManager
|
13
|
+
from .media_player import MediaPlayer, MediaPlayerBase
|
14
|
+
|
15
|
+
|
16
|
+
class MediaPlayBarButton(TransparentToolButton):
|
17
|
+
""" Media play bar button """
|
18
|
+
|
19
|
+
def _postInit(self):
|
20
|
+
super()._postInit()
|
21
|
+
self.installEventFilter(ToolTipFilter(self, 1000))
|
22
|
+
self.setFixedSize(30, 30)
|
23
|
+
self.setIconSize(QSize(16, 16))
|
24
|
+
|
25
|
+
|
26
|
+
class PlayButton(MediaPlayBarButton):
|
27
|
+
""" Play button """
|
28
|
+
|
29
|
+
def _postInit(self):
|
30
|
+
super()._postInit()
|
31
|
+
self.setIconSize(QSize(14, 14))
|
32
|
+
self.setPlay(False)
|
33
|
+
|
34
|
+
def setPlay(self, isPlay: bool):
|
35
|
+
if isPlay:
|
36
|
+
self.setIcon(FluentIcon.PAUSE_BOLD)
|
37
|
+
self.setToolTip(self.tr('Pause'))
|
38
|
+
else:
|
39
|
+
self.setIcon(FluentIcon.PLAY_SOLID)
|
40
|
+
self.setToolTip(self.tr('Play'))
|
41
|
+
|
42
|
+
|
43
|
+
class VolumeView(FlyoutViewBase):
|
44
|
+
""" Volume view """
|
45
|
+
|
46
|
+
def __init__(self, parent=None):
|
47
|
+
super().__init__(parent)
|
48
|
+
self.muteButton = MediaPlayBarButton(FluentIcon.VOLUME, self)
|
49
|
+
self.volumeSlider = Slider(Qt.Horizontal, self)
|
50
|
+
self.volumeLabel = CaptionLabel('30', self)
|
51
|
+
|
52
|
+
self.volumeSlider.setRange(0, 100)
|
53
|
+
self.volumeSlider.setFixedWidth(208)
|
54
|
+
self.setFixedSize(295, 64)
|
55
|
+
|
56
|
+
h = self.height()
|
57
|
+
self.muteButton.move(10, h//2-self.muteButton.height()//2)
|
58
|
+
self.volumeSlider.move(45, 21)
|
59
|
+
|
60
|
+
def setMuted(self, isMute: bool):
|
61
|
+
if isMute:
|
62
|
+
self.muteButton.setIcon(FluentIcon.MUTE)
|
63
|
+
self.muteButton.setToolTip(self.tr('Unmute'))
|
64
|
+
else:
|
65
|
+
self.muteButton.setIcon(FluentIcon.VOLUME)
|
66
|
+
self.muteButton.setToolTip(self.tr('Mute'))
|
67
|
+
|
68
|
+
def setVolume(self, volume: int):
|
69
|
+
self.volumeSlider.setValue(volume)
|
70
|
+
|
71
|
+
self.volumeLabel.setNum(volume)
|
72
|
+
self.volumeLabel.adjustSize()
|
73
|
+
|
74
|
+
tr = self.volumeLabel.fontMetrics().boundingRect(str(volume))
|
75
|
+
self.volumeLabel.move(self.width() - 20 - tr.width(), self.height()//2 - tr.height()//2)
|
76
|
+
|
77
|
+
def paintEvent(self, e):
|
78
|
+
painter = QPainter(self)
|
79
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
80
|
+
|
81
|
+
if isDarkTheme():
|
82
|
+
painter.setBrush(QColor(46, 46, 46))
|
83
|
+
painter.setPen(QColor(0, 0, 0, 20))
|
84
|
+
else:
|
85
|
+
painter.setBrush(QColor(248, 248, 248))
|
86
|
+
painter.setPen(QColor(0, 0, 0, 10))
|
87
|
+
|
88
|
+
painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 8, 8)
|
89
|
+
|
90
|
+
|
91
|
+
class VolumeButton(MediaPlayBarButton):
|
92
|
+
""" Volume button """
|
93
|
+
|
94
|
+
volumeChanged = Signal(int)
|
95
|
+
mutedChanged = Signal(bool)
|
96
|
+
|
97
|
+
def _postInit(self):
|
98
|
+
super()._postInit()
|
99
|
+
self.volumeView = VolumeView(self)
|
100
|
+
self.volumeFlyout = Flyout(self.volumeView, self.window(), False)
|
101
|
+
self.setMuted(False)
|
102
|
+
|
103
|
+
self.volumeFlyout.hide()
|
104
|
+
self.volumeView.muteButton.clicked.connect(lambda: self.mutedChanged.emit(not self.isMuted))
|
105
|
+
self.volumeView.volumeSlider.valueChanged.connect(self.volumeChanged)
|
106
|
+
self.clicked.connect(self._showVolumeFlyout)
|
107
|
+
|
108
|
+
def setMuted(self, isMute: bool):
|
109
|
+
self.isMuted = isMute
|
110
|
+
self.volumeView.setMuted(isMute)
|
111
|
+
|
112
|
+
if isMute:
|
113
|
+
self.setIcon(FluentIcon.MUTE)
|
114
|
+
else:
|
115
|
+
self.setIcon(FluentIcon.VOLUME)
|
116
|
+
|
117
|
+
def setVolume(self, volume: int):
|
118
|
+
self.volumeView.setVolume(volume)
|
119
|
+
|
120
|
+
def _showVolumeFlyout(self):
|
121
|
+
if self.volumeFlyout.isVisible():
|
122
|
+
return
|
123
|
+
|
124
|
+
pos = PullUpFlyoutAnimationManager(self.volumeFlyout).position(self)
|
125
|
+
self.volumeFlyout.exec(pos)
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
class MediaPlayBarBase(QWidget):
|
130
|
+
""" Play bar base class """
|
131
|
+
|
132
|
+
def __init__(self, parent=None):
|
133
|
+
super().__init__(parent=parent)
|
134
|
+
self.player = None # type: MediaPlayerBase
|
135
|
+
|
136
|
+
self.playButton = PlayButton(self)
|
137
|
+
self.volumeButton = VolumeButton(self)
|
138
|
+
self.progressSlider = Slider(Qt.Horizontal, self)
|
139
|
+
|
140
|
+
self.opacityEffect = QGraphicsOpacityEffect(self)
|
141
|
+
self.opacityAni = QPropertyAnimation(self.opacityEffect, b'opacity')
|
142
|
+
self.opacityEffect.setOpacity(1)
|
143
|
+
self.opacityAni.setDuration(250)
|
144
|
+
|
145
|
+
self.setGraphicsEffect(self.opacityEffect)
|
146
|
+
FluentStyleSheet.MEDIA_PLAYER.apply(self)
|
147
|
+
|
148
|
+
self.playButton.clicked.connect(self.togglePlayState)
|
149
|
+
|
150
|
+
def setMediaPlayer(self, player: MediaPlayerBase):
|
151
|
+
""" set media player """
|
152
|
+
self.player = player
|
153
|
+
|
154
|
+
self.player.durationChanged.connect(self.progressSlider.setMaximum)
|
155
|
+
self.player.positionChanged.connect(self._onPositionChanged)
|
156
|
+
self.player.mediaStatusChanged.connect(self._onMediaStatusChanged)
|
157
|
+
self.player.volumeChanged.connect(self.volumeButton.setVolume)
|
158
|
+
self.player.mutedChanged.connect(self.volumeButton.setMuted)
|
159
|
+
|
160
|
+
# don't use valueChanged, otherwise the sound will be interupted
|
161
|
+
self.progressSlider.sliderMoved.connect(self.player.setPosition)
|
162
|
+
self.progressSlider.clicked.connect(self.player.setPosition)
|
163
|
+
self.volumeButton.volumeChanged.connect(self.player.setVolume)
|
164
|
+
self.volumeButton.mutedChanged.connect(self.player.setMuted)
|
165
|
+
|
166
|
+
self.player.setVolume(30)
|
167
|
+
|
168
|
+
def fadeIn(self):
|
169
|
+
self.opacityAni.setStartValue(self.opacityEffect.opacity())
|
170
|
+
self.opacityAni.setEndValue(1)
|
171
|
+
self.opacityAni.start()
|
172
|
+
|
173
|
+
def fadeOut(self):
|
174
|
+
self.opacityAni.setStartValue(self.opacityEffect.opacity())
|
175
|
+
self.opacityAni.setEndValue(0)
|
176
|
+
self.opacityAni.start()
|
177
|
+
|
178
|
+
def play(self):
|
179
|
+
self.player.play()
|
180
|
+
|
181
|
+
def pause(self):
|
182
|
+
self.player.pause()
|
183
|
+
|
184
|
+
def stop(self):
|
185
|
+
self.player.stop()
|
186
|
+
|
187
|
+
def setVolume(self, volume: int):
|
188
|
+
""" Sets the volume of player """
|
189
|
+
self.player.setVolume(volume)
|
190
|
+
|
191
|
+
def setPosition(self, position: int):
|
192
|
+
""" Sets the position of media in ms """
|
193
|
+
self.player.setPosition(position)
|
194
|
+
|
195
|
+
def _onPositionChanged(self, position: int):
|
196
|
+
self.progressSlider.setValue(position)
|
197
|
+
|
198
|
+
def _onMediaStatusChanged(self, status):
|
199
|
+
self.playButton.setPlay(self.player.isPlaying())
|
200
|
+
|
201
|
+
def togglePlayState(self):
|
202
|
+
""" toggle the play state of media player """
|
203
|
+
if self.player.isPlaying():
|
204
|
+
self.player.pause()
|
205
|
+
else:
|
206
|
+
self.player.play()
|
207
|
+
|
208
|
+
self.playButton.setPlay(self.player.isPlaying())
|
209
|
+
|
210
|
+
def paintEvent(self, e):
|
211
|
+
painter = QPainter(self)
|
212
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
213
|
+
|
214
|
+
if isDarkTheme():
|
215
|
+
painter.setBrush(QColor(46, 46, 46))
|
216
|
+
painter.setPen(QColor(0, 0, 0, 20))
|
217
|
+
else:
|
218
|
+
painter.setBrush(QColor(248, 248, 248))
|
219
|
+
painter.setPen(QColor(0, 0, 0, 10))
|
220
|
+
|
221
|
+
painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 8, 8)
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
class SimpleMediaPlayBar(MediaPlayBarBase):
|
226
|
+
""" simple media play bar """
|
227
|
+
|
228
|
+
def __init__(self, parent=None):
|
229
|
+
super().__init__(parent)
|
230
|
+
self.hBoxLayout = QHBoxLayout(self)
|
231
|
+
|
232
|
+
self.hBoxLayout.setContentsMargins(10, 4, 10, 4)
|
233
|
+
self.hBoxLayout.setSpacing(6)
|
234
|
+
self.hBoxLayout.addWidget(self.playButton, 0, Qt.AlignLeft)
|
235
|
+
self.hBoxLayout.addWidget(self.progressSlider, 1)
|
236
|
+
self.hBoxLayout.addWidget(self.volumeButton, 0)
|
237
|
+
|
238
|
+
self.setFixedHeight(48)
|
239
|
+
self.setMediaPlayer(MediaPlayer(self))
|
240
|
+
|
241
|
+
def addButton(self, button: MediaPlayBarButton):
|
242
|
+
""" add button to the right side of play bar """
|
243
|
+
self.hBoxLayout.addWidget(button, 0)
|
244
|
+
|
245
|
+
|
246
|
+
class StandardMediaPlayBar(MediaPlayBarBase):
|
247
|
+
""" Standard media play bar """
|
248
|
+
|
249
|
+
def __init__(self, parent=None):
|
250
|
+
super().__init__(parent)
|
251
|
+
self.vBoxLayout = QVBoxLayout(self)
|
252
|
+
self.timeLayout = QHBoxLayout()
|
253
|
+
self.buttonLayout = QHBoxLayout()
|
254
|
+
self.leftButtonContainer = QWidget()
|
255
|
+
self.centerButtonContainer = QWidget()
|
256
|
+
self.rightButtonContainer = QWidget()
|
257
|
+
self.leftButtonLayout = QHBoxLayout(self.leftButtonContainer)
|
258
|
+
self.centerButtonLayout = QHBoxLayout(self.centerButtonContainer)
|
259
|
+
self.rightButtonLayout = QHBoxLayout(self.rightButtonContainer)
|
260
|
+
|
261
|
+
self.skipBackButton = MediaPlayBarButton(FluentIcon.SKIP_BACK, self)
|
262
|
+
self.skipForwardButton = MediaPlayBarButton(FluentIcon.SKIP_FORWARD, self)
|
263
|
+
|
264
|
+
self.currentTimeLabel = CaptionLabel('0:00:00', self)
|
265
|
+
self.remainTimeLabel = CaptionLabel('0:00:00', self)
|
266
|
+
|
267
|
+
self.__initWidgets()
|
268
|
+
|
269
|
+
def __initWidgets(self):
|
270
|
+
self.setFixedHeight(102)
|
271
|
+
self.vBoxLayout.setSpacing(6)
|
272
|
+
self.vBoxLayout.setContentsMargins(5, 9, 5, 9)
|
273
|
+
self.vBoxLayout.addWidget(self.progressSlider, 1, Qt.AlignTop)
|
274
|
+
|
275
|
+
self.vBoxLayout.addLayout(self.timeLayout)
|
276
|
+
self.timeLayout.setContentsMargins(10, 0, 10, 0)
|
277
|
+
self.timeLayout.addWidget(self.currentTimeLabel, 0, Qt.AlignLeft)
|
278
|
+
self.timeLayout.addWidget(self.remainTimeLabel, 0, Qt.AlignRight)
|
279
|
+
|
280
|
+
self.vBoxLayout.addStretch(1)
|
281
|
+
self.vBoxLayout.addLayout(self.buttonLayout, 1)
|
282
|
+
self.buttonLayout.setContentsMargins(0, 0, 0, 0)
|
283
|
+
self.leftButtonLayout.setContentsMargins(4, 0, 0, 0)
|
284
|
+
self.centerButtonLayout.setContentsMargins(0, 0, 0, 0)
|
285
|
+
self.rightButtonLayout.setContentsMargins(0, 0, 4, 0)
|
286
|
+
|
287
|
+
self.leftButtonLayout.addWidget(self.volumeButton, 0, Qt.AlignLeft)
|
288
|
+
self.centerButtonLayout.addWidget(self.skipBackButton)
|
289
|
+
self.centerButtonLayout.addWidget(self.playButton)
|
290
|
+
self.centerButtonLayout.addWidget(self.skipForwardButton)
|
291
|
+
|
292
|
+
self.buttonLayout.addWidget(self.leftButtonContainer, 0, Qt.AlignLeft)
|
293
|
+
self.buttonLayout.addWidget(self.centerButtonContainer, 0, Qt.AlignHCenter)
|
294
|
+
self.buttonLayout.addWidget(self.rightButtonContainer, 0, Qt.AlignRight)
|
295
|
+
|
296
|
+
self.setMediaPlayer(MediaPlayer(self))
|
297
|
+
|
298
|
+
self.skipBackButton.clicked.connect(lambda: self.skipBack(10000))
|
299
|
+
self.skipForwardButton.clicked.connect(lambda: self.skipForward(30000))
|
300
|
+
|
301
|
+
def skipBack(self, ms: int):
|
302
|
+
""" Back up for specified milliseconds """
|
303
|
+
self.player.setPosition(self.player.position()-ms)
|
304
|
+
|
305
|
+
def skipForward(self, ms: int):
|
306
|
+
""" Fast forward specified milliseconds """
|
307
|
+
self.player.setPosition(self.player.position()+ms)
|
308
|
+
|
309
|
+
def _onPositionChanged(self, position: int):
|
310
|
+
super()._onPositionChanged(position)
|
311
|
+
self.currentTimeLabel.setText(self._formatTime(position))
|
312
|
+
self.remainTimeLabel.setText(self._formatTime(self.player.duration() - position))
|
313
|
+
|
314
|
+
def _formatTime(self, time: int):
|
315
|
+
time = int(time / 1000)
|
316
|
+
s = time % 60
|
317
|
+
m = int(time / 60) % 60
|
318
|
+
h = int(time / 3600)
|
319
|
+
return f'{h}:{m:02}:{s:02}'
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from PySide6.QtCore import Qt, Signal, QObject, QUrl
|
3
|
+
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
|
4
|
+
|
5
|
+
|
6
|
+
class MediaPlayerBase(QObject):
|
7
|
+
""" Media player base class """
|
8
|
+
|
9
|
+
mediaStatusChanged = Signal(QMediaPlayer.MediaStatus)
|
10
|
+
playbackRateChanged = Signal(float)
|
11
|
+
positionChanged = Signal(int)
|
12
|
+
durationChanged = Signal(int)
|
13
|
+
sourceChanged = Signal(QUrl)
|
14
|
+
volumeChanged = Signal(int)
|
15
|
+
mutedChanged = Signal(bool)
|
16
|
+
|
17
|
+
def __init__(self, parent=None):
|
18
|
+
super().__init__(parent=parent)
|
19
|
+
|
20
|
+
def isPlaying(self):
|
21
|
+
""" Whether the media is playing """
|
22
|
+
raise NotImplementedError
|
23
|
+
|
24
|
+
def mediaStatus(self) -> QMediaPlayer.MediaStatus:
|
25
|
+
""" Return the status of the current media stream """
|
26
|
+
raise NotImplementedError
|
27
|
+
|
28
|
+
def playbackState(self) -> QMediaPlayer.PlaybackState:
|
29
|
+
""" Return the playback status of the current media stream """
|
30
|
+
raise NotImplementedError
|
31
|
+
|
32
|
+
def duration(self):
|
33
|
+
""" Returns the duration of the current media in ms """
|
34
|
+
raise NotImplementedError
|
35
|
+
|
36
|
+
def position(self):
|
37
|
+
""" Returns the current position inside the media being played back in ms """
|
38
|
+
raise NotImplementedError
|
39
|
+
|
40
|
+
def volume(self):
|
41
|
+
""" Return the volume of player """
|
42
|
+
raise NotImplementedError
|
43
|
+
|
44
|
+
def source(self) -> QUrl:
|
45
|
+
""" Return the active media source being used """
|
46
|
+
raise NotImplementedError
|
47
|
+
|
48
|
+
def pause(self):
|
49
|
+
""" Pause playing the current source """
|
50
|
+
raise NotImplementedError
|
51
|
+
|
52
|
+
def play(self):
|
53
|
+
""" Start or resume playing the current source """
|
54
|
+
raise NotImplementedError
|
55
|
+
|
56
|
+
def stop(self):
|
57
|
+
""" Stop playing, and reset the play position to the beginning """
|
58
|
+
raise NotImplementedError
|
59
|
+
|
60
|
+
def playbackRate(self) -> float:
|
61
|
+
""" Return the playback rate of the current media """
|
62
|
+
raise NotImplementedError
|
63
|
+
|
64
|
+
def setPosition(self, position: int):
|
65
|
+
""" Sets the position of media in ms """
|
66
|
+
raise NotImplementedError
|
67
|
+
|
68
|
+
def setSource(self, media: QUrl):
|
69
|
+
""" Sets the current source """
|
70
|
+
raise NotImplementedError
|
71
|
+
|
72
|
+
def setPlaybackRate(self, rate: float):
|
73
|
+
""" Sets the playback rate of player """
|
74
|
+
raise NotImplementedError
|
75
|
+
|
76
|
+
def setVolume(self, volume: int):
|
77
|
+
""" Sets the volume of player """
|
78
|
+
raise NotImplementedError
|
79
|
+
|
80
|
+
def setMuted(self, isMuted: bool):
|
81
|
+
raise NotImplementedError
|
82
|
+
|
83
|
+
def videoOutput(self) -> QObject:
|
84
|
+
""" Return the video output to be used by the media player """
|
85
|
+
raise NotImplementedError
|
86
|
+
|
87
|
+
def setVideoOutput(self, output: QObject) -> None:
|
88
|
+
""" Sets the video output to be used by the media player """
|
89
|
+
raise NotImplementedError
|
90
|
+
|
91
|
+
|
92
|
+
class MediaPlayer(QMediaPlayer):
|
93
|
+
""" Media player """
|
94
|
+
|
95
|
+
sourceChanged = Signal(QUrl)
|
96
|
+
mutedChanged = Signal(bool)
|
97
|
+
volumeChanged = Signal(int)
|
98
|
+
|
99
|
+
def __init__(self, parent=None):
|
100
|
+
super().__init__(parent=parent)
|
101
|
+
self._audioOutput = QAudioOutput(parent)
|
102
|
+
self.setAudioOutput(self._audioOutput)
|
103
|
+
|
104
|
+
def isPlaying(self):
|
105
|
+
return self.playbackState() == QMediaPlayer.PlayingState
|
106
|
+
|
107
|
+
def volume(self):
|
108
|
+
""" Return the volume of player """
|
109
|
+
return int(self.audioOutput().volume() * 100)
|
110
|
+
|
111
|
+
def setVolume(self, volume: int):
|
112
|
+
""" Sets the volume of player """
|
113
|
+
if volume == self.volume():
|
114
|
+
return
|
115
|
+
|
116
|
+
self.audioOutput().setVolume(volume / 100)
|
117
|
+
self.volumeChanged.emit(volume)
|
118
|
+
|
119
|
+
def setMuted(self, isMuted: bool):
|
120
|
+
if isMuted == self.audioOutput().isMuted():
|
121
|
+
return
|
122
|
+
|
123
|
+
self.audioOutput().setMuted(isMuted)
|
124
|
+
self.mutedChanged.emit(isMuted)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from PySide6.QtCore import Qt, Signal, QUrl, QSizeF, QTimer
|
3
|
+
from PySide6.QtGui import QPainter
|
4
|
+
from PySide6.QtMultimediaWidgets import QGraphicsVideoItem
|
5
|
+
from PySide6.QtWidgets import QWidget, QGraphicsView, QVBoxLayout, QGraphicsScene
|
6
|
+
|
7
|
+
from ..common.style_sheet import FluentStyleSheet
|
8
|
+
from .media_play_bar import StandardMediaPlayBar
|
9
|
+
|
10
|
+
|
11
|
+
class GraphicsVideoItem(QGraphicsVideoItem):
|
12
|
+
""" Graphics video item """
|
13
|
+
|
14
|
+
def paint(self, painter: QPainter, option, widget):
|
15
|
+
painter.setCompositionMode(QPainter.CompositionMode_Difference)
|
16
|
+
super().paint(painter, option, widget)
|
17
|
+
|
18
|
+
|
19
|
+
class VideoWidget(QGraphicsView):
|
20
|
+
""" Video widget """
|
21
|
+
|
22
|
+
def __init__(self, parent=None):
|
23
|
+
super().__init__(parent)
|
24
|
+
self.isHover = False
|
25
|
+
self.timer = QTimer(self)
|
26
|
+
|
27
|
+
self.vBoxLayout = QVBoxLayout(self)
|
28
|
+
self.videoItem = QGraphicsVideoItem()
|
29
|
+
self.graphicsScene = QGraphicsScene(self)
|
30
|
+
self.playBar = StandardMediaPlayBar(self)
|
31
|
+
|
32
|
+
self.setMouseTracking(True)
|
33
|
+
self.setScene(self.graphicsScene)
|
34
|
+
self.graphicsScene.addItem(self.videoItem)
|
35
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
36
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
37
|
+
self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
|
38
|
+
|
39
|
+
self.player.setVideoOutput(self.videoItem)
|
40
|
+
FluentStyleSheet.MEDIA_PLAYER.apply(self)
|
41
|
+
|
42
|
+
self.timer.timeout.connect(self._onHideTimeOut)
|
43
|
+
|
44
|
+
def setVideo(self, url: QUrl):
|
45
|
+
""" set the video to play """
|
46
|
+
self.player.setSource(url)
|
47
|
+
self.fitInView(self.videoItem, Qt.KeepAspectRatio)
|
48
|
+
|
49
|
+
def hideEvent(self, e):
|
50
|
+
self.pause()
|
51
|
+
e.accept()
|
52
|
+
|
53
|
+
def wheelEvent(self, e):
|
54
|
+
return
|
55
|
+
|
56
|
+
def enterEvent(self, e):
|
57
|
+
self.isHover = True
|
58
|
+
self.playBar.fadeIn()
|
59
|
+
|
60
|
+
def leaveEvent(self, e):
|
61
|
+
self.isHover = False
|
62
|
+
self.timer.start(3000)
|
63
|
+
|
64
|
+
def _onHideTimeOut(self):
|
65
|
+
if not self.isHover:
|
66
|
+
self.playBar.fadeOut()
|
67
|
+
|
68
|
+
def play(self):
|
69
|
+
self.playBar.play()
|
70
|
+
|
71
|
+
def pause(self):
|
72
|
+
self.playBar.pause()
|
73
|
+
|
74
|
+
def stop(self):
|
75
|
+
self.playBar.stop()
|
76
|
+
|
77
|
+
def togglePlayState(self):
|
78
|
+
""" toggle play state """
|
79
|
+
if self.player.isPlaying():
|
80
|
+
self.pause()
|
81
|
+
else:
|
82
|
+
self.play()
|
83
|
+
|
84
|
+
def resizeEvent(self, e):
|
85
|
+
super().resizeEvent(e)
|
86
|
+
self.videoItem.setSize(QSizeF(self.size()))
|
87
|
+
self.fitInView(self.videoItem, Qt.KeepAspectRatio)
|
88
|
+
self.playBar.move(11, self.height() - self.playBar.height() - 11)
|
89
|
+
self.playBar.setFixedSize(self.width() - 22, self.playBar.height())
|
90
|
+
|
91
|
+
@property
|
92
|
+
def player(self):
|
93
|
+
return self.playBar.player
|