第三代软件开发-简易视频播放器-自定义Slider (二)
文章目录
- 第三代软件开发-简易视频播放器-自定义Slider (二)
- 项目介绍
- 简易视频播放器
- 自定义Slider (二)
- 横向
- 纵向
关键字:
Qt
、
Qml
、
关键字3
、
关键字4
、
关键字5
项目介绍
欢迎来到我们的 QML & C++ 项目!这个项目结合了 QML(Qt Meta-Object Language)和 C++ 的强大功能,旨在开发出色的用户界面和高性能的后端逻辑。
在项目中,我们利用 QML 的声明式语法和可视化设计能力创建出现代化的用户界面。通过直观的编码和可重用的组件,我们能够迅速开发出丰富多样的界面效果和动画效果。同时,我们利用 QML 强大的集成能力,轻松将 C++ 的底层逻辑和数据模型集成到前端界面中。
在后端方面,我们使用 C++ 编写高性能的算法、数据处理和计算逻辑。C++ 是一种强大的编程语言,能够提供卓越的性能和可扩展性。我们的团队致力于优化代码,减少资源消耗,以确保我们的项目在各种平台和设备上都能够高效运行。
无论您是对 QML 和 C++ 开发感兴趣,还是需要我们为您构建复杂的用户界面和后端逻辑,我们都随时准备为您提供支持。请随时联系我们,让我们一同打造现代化、高性能的 QML & C++ 项目!
重要说明☝
☀该专栏在第三代软开发更新完将涨价
简易视频播放器
其实咱们在前面屏保哪里已经搞过视频文件播放了,只是哪里没有进度条,也没有时间线,也不能控制播放暂停,今天我们就是把这些再加上去。如下图所示,这里因为原始录制的Gif 太大,无法上传,所以做了减帧处理,看着有点卡顿了。
这里咱就是直接上代码吧;
import QtQuick 2.15
import QtMultimedia 5.15
import QtQuick.Layouts 1.15 // 布局需要
import QtQuick.Controls 2.15
Rectangle
{
property string videoSource: "file"
property bool fullScreen: false
id:root
color: "#000000"
anchors.centerIn: parent
width: 720
height: 480
visible: false
SoundEffect {
id: playSound
source: "qrc:/Audio/T_Resource/T_Audio/T_Base/buttonTach.wav"
}
// Video
// {
// id:video_show
// anchors.fill: parent
// loops: MediaPlayer.Infinite
// source: root.videoSource
// }
MediaPlayer
{
id:media_video
source: videoSource // 绝对路径
loops: MediaPlayer.Infinite
volume: 0.5
}
VideoOutput
{
id:out_put
anchors.fill: parent
source: media_video
}
RowLayout
{
id:layout_menu
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 26
spacing: 20
Item
{
width: 26
height: 20
Image {
anchors.centerIn: parent
height: 26
fillMode: Image.PreserveAspectFit
source: (media_video.playbackState === MediaPlayer.PlayingState ) ? "qrc:/Video/T_Resource/T_Image/Vidoe/zt.png" : "qrc:/Video/T_Resource/T_Image/Vidoe/bf.png"
}
MouseArea
{
anchors.fill: parent
onClicked: (media_video.playbackState === MediaPlayer.PlayingState ) ? media_video.pause() : media_video.play();
}
}
Item {
implicitWidth: 50
Text {
anchors.centerIn: parent
font.pixelSize: 20
color: "#FFFFFF"
text: {
// 创建变量获取时间当前播放位置,单位毫秒
var milliseconds = media_video.position
// 创建变量,将当前播放位置的毫秒转换为分钟,并向下取舍
var minutes = Math.floor(milliseconds / 60000)
// 获取不足 60秒的毫秒数
milliseconds -= minutes * 60000
// 创建变量,不足60秒的毫秒数转换为秒
var seconds = milliseconds / 1000
// 进行四舍五入
seconds = Math.round(seconds)
// 判断秒数是否小于10秒,来输出时间格式,最终格式为:mm:ss
if(seconds < 10)
return minutes + ":0" + seconds
else
return minutes + ":" + seconds
}
}
}
Slider
{
id:durationTimeSlider
Layout.fillWidth: true
value: media_video.position / media_video.duration
background: Rectangle{
x: durationTimeSlider.leftPadding
y: durationTimeSlider.topPadding + durationTimeSlider.availableHeight / 2 - height / 2
implicitHeight: 4
implicitWidth: 200
width: durationTimeSlider.availableWidth
height: implicitHeight
radius: 2
color: "#F0F0F0" // 进度条背景颜色
// 视频已经播放的区域
Rectangle{
width: durationTimeSlider.visualPosition * parent.width
height: parent.height
color: "#36ABDF" // 进度条已经走完的颜色
radius: 2
}
}
// 滑块样式
handle: Rectangle{
antialiasing: true
x: durationTimeSlider.leftPadding + durationTimeSlider.visualPosition
* (durationTimeSlider.availableWidth - width)
y: durationTimeSlider.topPadding + durationTimeSlider.availableHeight / 2 - height / 2
implicitWidth: 20
implicitHeight: 20
radius: 10
border.color: "#bdbebf" // 滑块边框颜色
// 判断滑块按压状态,设置不同的颜色
color: durationTimeSlider.pressed ? "#B0C4DE" : "#F0F0F0"
// 滑块中心的区域,我这里设置了透明
Rectangle{
width: 4
height: 4
radius: 2
color: "transparent"
anchors.centerIn: parent
}
}
property real index: 0
property bool changed: false
// 滑块移动时,将 index 设置为滑块当前位置
onMoved: {
if(pressed){
index = position
}
}
onPressedChanged: {
if(pressed === true){
changed = true
}else if (changed === true){
media_video.seek(index * media_video.duration)
changed = false
}
}
}
Item {
implicitWidth: 50
Text {
anchors.centerIn: parent
font.pixelSize: 20
color: "#FFFFFF"
text: {
var millseconds = media_video.duration.valueOf()
var minutes = Math.floor(millseconds / 60000)
millseconds -= minutes * 6000
var secounds = millseconds / 1000
secounds = Math.round(secounds)
// 返回 mm : ss 格式时间
if(secounds < 10)
return minutes + ":0" + secounds
else
return minutes + ":" + secounds
}
}
}
Item
{
id:item_volume
width: 26
height: 20
Image {
anchors.centerIn: parent
height: 26
fillMode: Image.PreserveAspectFit
source: "qrc:/Video/T_Resource/T_Image/Vidoe/yl_z.png"
}
MouseArea
{
anchors.fill: parent
onClicked: item_volum.visible = !item_volum.visible
}
}
Item
{
width: 26
height: 20
Image {
anchors.centerIn: parent
height: 26
fillMode: Image.PreserveAspectFit
source: fullScreen ? "qrc:/Video/T_Resource/T_Image/Vidoe/sx.png" :"qrc:/Video/T_Resource/T_Image/Vidoe/qp.png"
}
MouseArea
{
anchors.fill: parent
onClicked: root.fullScreen = !root.fullScreen
}
}
}
Item {
id:item_volum
width: 42
height: 235
visible: false
anchors.bottom: layout_menu.top
anchors.right: layout_menu.right
anchors.rightMargin: 36
Text {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 20
color: "#36ABDF"
text: (volumeSlider.value * 100).toFixed(0)
}
Slider
{
id:volumeSlider
width: 42
height: 220
from:0.0
to:1.0
stepSize: 0.01
value: media_video.volume
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
orientation:Qt.Vertical
background: Rectangle{
anchors.horizontalCenter: parent.horizontalCenter
y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2
implicitHeight: 200
implicitWidth: 4
width: 4
height: volumeSlider.availableHeight
radius: 2
color: "#F0F0F0" // 进度条背景颜色
// 视频已经播放的区域
Rectangle{
anchors.bottom: parent.bottom
width: parent.width
height: parent.height - volumeSlider.visualPosition * parent.height
color: "#36ABDF" // 进度条已经走完的颜色
radius: 2
}
}
// 滑块样式
handle: Rectangle{
antialiasing: true
anchors.horizontalCenter: parent.horizontalCenter
y: volumeSlider.topPadding + volumeSlider.visualPosition
* (volumeSlider.availableHeight - height)
implicitWidth: 20
implicitHeight: 20
radius: 10
border.color: "#bdbebf" // 滑块边框颜色
// 判断滑块按压状态,设置不同的颜色
color: volumeSlider.pressed ? "#B0C4DE" : "#F0F0F0"
// 滑块中心的区域,我这里设置了透明
Rectangle{
width: 4
height: 4
radius: 2
color: "transparent"
anchors.centerIn: parent
}
}
onValueChanged: media_video.volume = value
}
}
function play()
{
media_video.play();
}
function stop(){
if((media_video.playbackState === MediaPlayer.PlayingState || media_video.playbackState === MediaPlayer.PausedState))
media_video.stop();
}
}
自定义Slider (二)
横向
Slider
{
id:durationTimeSlider
Layout.fillWidth: true
value: media_video.position / media_video.duration
background: Rectangle{
x: durationTimeSlider.leftPadding
y: durationTimeSlider.topPadding + durationTimeSlider.availableHeight / 2 - height / 2
implicitHeight: 4
implicitWidth: 200
width: durationTimeSlider.availableWidth
height: implicitHeight
radius: 2
color: "#F0F0F0" // 进度条背景颜色
// 视频已经播放的区域
Rectangle{
width: durationTimeSlider.visualPosition * parent.width
height: parent.height
color: "#36ABDF" // 进度条已经走完的颜色
radius: 2
}
}
// 滑块样式
handle: Rectangle{
antialiasing: true
x: durationTimeSlider.leftPadding + durationTimeSlider.visualPosition
* (durationTimeSlider.availableWidth - width)
y: durationTimeSlider.topPadding + durationTimeSlider.availableHeight / 2 - height / 2
implicitWidth: 20
implicitHeight: 20
radius: 10
border.color: "#bdbebf" // 滑块边框颜色
// 判断滑块按压状态,设置不同的颜色
color: durationTimeSlider.pressed ? "#B0C4DE" : "#F0F0F0"
// 滑块中心的区域,我这里设置了透明
Rectangle{
width: 4
height: 4
radius: 2
color: "transparent"
anchors.centerIn: parent
}
}
property real index: 0
property bool changed: false
// 滑块移动时,将 index 设置为滑块当前位置
onMoved: {
if(pressed){
index = position
}
}
onPressedChanged: {
if(pressed === true){
changed = true
}else if (changed === true){
media_video.seek(index * media_video.duration)
changed = false
}
}
}
纵向
Slider
{
id:volumeSlider
width: 42
height: 220
from:0.0
to:1.0
stepSize: 0.01
value: media_video.volume
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
orientation:Qt.Vertical
background: Rectangle{
anchors.horizontalCenter: parent.horizontalCenter
y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2
implicitHeight: 200
implicitWidth: 4
width: 4
height: volumeSlider.availableHeight
radius: 2
color: "#F0F0F0" // 进度条背景颜色
// 视频已经播放的区域
Rectangle{
anchors.bottom: parent.bottom
width: parent.width
height: parent.height - volumeSlider.visualPosition * parent.height
color: "#36ABDF" // 进度条已经走完的颜色
radius: 2
}
}
// 滑块样式
handle: Rectangle{
antialiasing: true
anchors.horizontalCenter: parent.horizontalCenter
y: volumeSlider.topPadding + volumeSlider.visualPosition
* (volumeSlider.availableHeight - height)
implicitWidth: 20
implicitHeight: 20
radius: 10
border.color: "#bdbebf" // 滑块边框颜色
// 判断滑块按压状态,设置不同的颜色
color: volumeSlider.pressed ? "#B0C4DE" : "#F0F0F0"
// 滑块中心的区域,我这里设置了透明
Rectangle{
width: 4
height: 4
radius: 2
color: "transparent"
anchors.centerIn: parent
}
}
onValueChanged: media_video.volume = value
}
这部分qml 代码很好懂,没有啥需要注意的吧,这里需要注意的就是一部分
MediaPlayer
{
id:media_video
source: videoSource // 绝对路径
loops: MediaPlayer.Infinite
volume: 0.5
}
VideoOutput
{
id:out_put
anchors.fill: parent
source: media_video
}
其实我最开始是用了Video组件的,但是再全屏的时候遇到问题,就是画面不会跟着全屏,应该是哪里跟着改下就可,不过我没有时间处理,这个功能就是播放一下宣教视频和宣传视频,所以目前不会有太多的精力放在这里。