Qt Quick 实现一个右下角弹出消息的组件
开发环境
Qt版本: 6.5.3
构建: cmake + minGW64-bit
简介
这是一个纯QML程序,功能是一个消息列表的功能,可以进行插入,删除,清空等操作
预览图

如何使用
直接导入messageQueueView文件夹即可
代码
这里的代码只包括组件的代码,不包括整个项目的代码。
结构如下:
- messageQueueView
- resource
- Background.qml
- MessageView.qml
- ScroolBar.qml
- MessageQueueView.qml
Background.qml: 消息队列的背景
MessageView.qml: 显示插入的消息
ScroolBar.qml: 滑块
MessageQueueView.qml: 消息队列组件
main.qml
展示消息列表组件功能
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
import "./messageQueueView"
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Item {
id: root
width: parent.width
height: parent.height
Pane {
width: parent.width
height: parent.height
Column {
width: 80
height: 100
spacing: 10
Material.theme: Material.System
Row {
Text {
text: "插入消息: "
}
TextField {
Keys.onReturnPressed: {
messageQueueView.insert(text,{
title: "这是一个消息的标题",
message: "这是第 " + messageQueueView.count +" 条消息"
})
}
}
}
Row {
Text {
text: "移除消息: "
}
TextField {
Keys.onReturnPressed: {
messageQueueView.remove(text,1)
}
}
}
Row {
Button {
text: "清空消息"
onClicked: {
messageQueueView.clear()
}
Keys.onReturnPressed: {
messageQueueView.model.clear()
}
}
Button {
text: "打开"
onClicked: {
messageQueueView.show()
}
Keys.onReturnPressed: {
messageQueueView.show()
}
}
Button {
text: "关闭"
onClicked: {
messageQueueView.hide()
}
Keys.onReturnPressed: {
messageQueueView.hide()
}
}
}
}
}
MessageQueueView {
id: messageQueueView
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
Component.onCompleted: {
}
}
}
Timer {
property var data: [
{
title: "这是一个消息的标题",
message: "这是一个消息,这是一个消息,这是一个消息,这是一个消息,这是一个消息,这是一个消息,这是一个消息"
},
{
title: "这是一个消息的标题",
message: "这不是一个消息,这不是一个消息,这不是一个消息,这不是一个消息,这不是一个消息,这不是一个消息"
},
{
title: "这是一个消息的标题",
message: "好吧,这是一一一条消息"
},
]
property int current: 0
interval: 400
onTriggered: {
if(current < data.length) {
messageQueueView.insert(0,data[current]);
current ++;
start();
}
}
Component.onCompleted: {
start()
}
}
}
MessageView.qml
消息列表组件
import QtQuick 2.15
/*
消息视图
*/
ListView {
id: listview
width: content.width
height: content.height
spacing: 10
anchors.horizontalCenter: parent.horizontalCenter
verticalLayoutDirection: ListView.BottomToTop
clip: true
model: root.model
delegate: messageItemDelegate
// add 添加 过渡动画 因add导致被影响的项
add: Transition {
id: addTrans
onRunningChanged: {
// console.log("addTran: " + ViewTransition.item)
}
ScriptAction { // 动态添加一个定时器 延时设置透明度
script: {
addDelayHide(addTrans.ViewTransition.item)
}
}
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: 300
easing.type: Easing.InOutQuad
}
PathAnimation {
duration: 400
easing.type: Easing.InOutQuad
path: Path {
startX: addTrans.ViewTransition.destination.x + 80
startY: addTrans.ViewTransition.destination.y
PathCurve {
x: (listview.width - addTrans.ViewTransition.item.width) / 2
y: addTrans.ViewTransition.destination.y
}
}
}
}
}
// add 添加 过渡动画
addDisplaced: Transition {
id: dispTran
onRunningChanged: {
if(running) {
// console.log("addDispTran: " + ViewTransition.targetItems)
}
}
// 如果数据插入太快会导致动画被中断 然后动画控制的属性值无法回到正确的值,在这里手动回到正确的值
ScriptAction {
script: {
let item = dispTran.ViewTransition.item
if(root.state === "show") {
item.opacity = 1
}
if(root.state === "hide" && item.state === "NONEW") {
item.opacity = 0
console.log(item.children)
}
}
}
PropertyAction { property: "x"; value: (listview.width - dispTran.ViewTransition.item.width) / 2;}
NumberAnimation {
property: "y"
duration: 300
easing.type: Easing.InOutQuad
}
}
// remove 移除 过渡动画
remove: Transition {
id: removeTran
onRunningChanged: {
// console.log("removeTran: " + ViewTransition.targetItems)
}
ParallelAnimation {
NumberAnimation {
property: "x"
to: listview.width
duration: 400
easing.type: Easing.OutQuart
}
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: 400
easing.type: Easing.InOutQuart
}
}
}
// remove 移除 过渡动画 因romove导致被影响的项
removeDisplaced: Transition {
id: removeDispTran
onRunningChanged: {
// console.log("removeDispTran: " + ViewTransition.targetItems)
}
ParallelAnimation {
NumberAnimation {
property: "y"
duration: 500
easing.type: Easing.InOutQuart
}
}
}
// populate 委托项加载时触发
populate: Transition {
id: popuTran
NumberAnimation {
property: "opacity"
duration: 300
from: 0
to: 1
easing.type: Easing.OutCubic
}
}
// 视图滑块
ScroolBar.vertical: ScroolBar {
}
/*
消息视图显示时 使所有加载项显示
消息视图隐藏时 使所有加载项隐藏
*/
Connections {
target: root
function onStateChanged() {
for(let i = 0; i < contentItem.children.length;i++) {
let item = contentItem.children[i]
if(root.state === "show") {
item.opacity = 1
} else if(root.state === "hide"){
item.opacity = 0
}
}
}
}
/*
添加延时定时器
如果视图隐藏时,只显示新添加的消息
*/
function addDelayHide(item) {
if(root.state === "show") return
// let item = contentItem.children[contentItem.children.length-1]
let timer = Qt.createQmlObject("
import QtQml
Timer {}
",item)
let callBack = () => {
if(root.state === "show") return
timer.parent.opacity = 0
timer.parent.state = "NONEW"
console.log("timer: " + timer.parent)
}
timer.interval = 3000
timer.triggered.connect(callBack)
timer.start()
}
}
Background.qml
背景
// Background.qml
import QtQuick 2.15
/*
背景
*/
Loader {
id: backgroundLoader
anchors.fill: parent
sourceComponent: root.background
}
ScroolBar.qml
滑块
// ScrollBar.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
/*
滑动条
*/
T.ScrollBar {
id: control
property color handleNormalColor: "#7FFFFFFF" //按钮颜色
property color handleHoverColor: Qt.lighter(handleNormalColor,1.1)
property color handlePressColor: Qt.darker(handleNormalColor,1.1)
property real handleWidth: 10
property real handleHeight: 10
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
padding: 1 //背景整体size和handle的间隔
visible: control.policy !== T.ScrollBar.AlwaysOff
contentItem: Rectangle {
implicitWidth: control.interactive ? handleWidth : 2
implicitHeight: control.interactive ? handleHeight : 2
radius: width / 2
color: if(control.pressed) return handlePressColor
else if(control.hovered) return handleHoverColor
else return handleNormalColor
// 超出显示范围显示滚动条
opacity:(control.policy === T.ScrollBar.AlwaysOn || control.size < 1.0)?1.0:0.0
}
}
MessageQueueView.qml
使用此组件即可
// MessageQueueView.qml
import QtQuick
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
import "./resource"
Item {
id: root
property real margin: 20
property Component messageItemDelegate: messageDelegate
property Component background: backgourndCmp
property ListModel model: ListModel {}
property alias itemsSpacing: messageView.spacing
property alias count: messageView.count
signal added()
signal removed()
width: 260
height: 400
state: "show"
states: [
State {
name: "hide"
PropertyChanges {
target: content
opacity: 0
x: root.width
}
PropertyChanges {
target: showIcon
parent: root
anchors.right: content.left
anchors.verticalCenter: content.verticalCenter
}
PropertyChanges {
target: messageView
interactive: false
contentY: 0
}
},
State {
name: "show"
PropertyChanges {
target: content
opacity: 1
x: 0
}
PropertyChanges {
target: showIcon
parent: root
anchors.right: content.left
anchors.verticalCenter: content.verticalCenter
}
PropertyChanges {
target: messageView
interactive: true
}
}
]
transitions: [
Transition {
from: "hide"
to: "show"
SequentialAnimation {
ScriptAction {
script: {
messageView.parent = content
console.log("AA")
}
}
ParallelAnimation {
NumberAnimation {
target: content
property: "x"
duration: 400
easing.type: Easing.OutCubic
}
NumberAnimation {
target: content
property: "opacity"
duration: 400
easing.type: Easing.InOutQuad
}
}
}
},
Transition {
from: "show"
to: "hide"
SequentialAnimation {
ParallelAnimation {
NumberAnimation {
target: content
property: "x"
duration: 400
easing.type: Easing.OutCubic
}
NumberAnimation {
target: content
property: "opacity"
duration: 400
easing.type: Easing.InOutQuad
}
}
ScriptAction {
script: {
messageView.parent = root
console.log("AA")
}
}
}
}
]
// 主体内容
Item {
id: content
z: 0
width: parent.width
height: parent.height
// 背景加载
Background {
id: backgroundLoader
}
// 消息视图
MessageView {
id: messageView
}
}
// 打开关闭按钮
ColorImage {
id: showIcon
z: 4
width: 20
height: width
rotation: 90
source: "qrc:/images/downUp.svg"
color: "#4F00FF00"
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if(root.state === "show") {
root.hide()
} else {
root.show()
}
}
}
}
Component {
id: backgourndCmp
Rectangle {
id: background
anchors.fill: parent
color: "#2F000000"
}
}
// 单个消息委托
Component {
id: messageDelegate
Rectangle {
id: msgItem
x: (messageView.width - width) / 2
width: messageView.width - root.margin*2
height: 80
radius: 8
opacity: 1
// visible: opacity
color: "#2F000000"
clip: true
Behavior on opacity {
NumberAnimation {
property: "opacity"
duration: 400
easing.type: Easing.InOutQuad
}
}
Row {
width: parent.width - 20
height: parent.height - 20
spacing: 5
anchors.centerIn: parent
Image { // 消息图标
id: iconImg
width: 35
height: width
anchors.verticalCenter: parent.verticalCenter
source: iconSource
}
Column { // 消息信息
id: infoText
width: parent.width - iconImg.width - parent.spacing*2 - toolBar.width
height: parent.height
spacing: 5
Text {
property real maxHeight: parent.height * 0.3
width: parent.width
height: maxHeight
wrapMode: Text.Wrap
elide: Text.ElideRight
font.pointSize: 12
font.bold: true
text: title
color: "#FFFFFF"
}
Text {
property real maxHeight: parent.height * 0.7 - parent.spacing
width: parent.width
height: maxHeight
wrapMode: Text.Wrap
elide: Text.ElideRight
font.pointSize: 9
text: message
color: "#FFFFFF"
}
}
Column { // 工具栏
id: toolBar
width: 15
MouseArea {
width: parent.width
height: width
cursorShape: Qt.PointingHandCursor
Rectangle {
width: parent.width
height: 1
anchors.centerIn: parent
rotation: 45
}
Rectangle {
width: parent.width
height: 1
anchors.centerIn: parent
rotation: -45
}
onClicked: {
remove(index)
}
}
}
}
}
}
function show() {
root.state = "show"
}
function hide() {
root.state = "hide"
}
function insert(index,info) {
let title = info.title || "标题"
let message = info.message || "信息"
let iconSource = info.iconSource || "qrc:/images/message.svg"
model.insert(index,{
title: title,
message: message,
iconSource: iconSource
})
root.added()
}
function remove(index,count = 1) {
model.remove(index, count)
}
function clear() {
model.clear()
}
}
Qt Quick 实现一个右下角弹出消息的组件的更多相关文章
- C#右下角弹出消息框
打开QQ的时候,QQ新闻弹出窗体在屏幕的右下角就会慢慢升起一个小窗口,占用的地方不大,可以起到提示的作用.下面就让我们来看看,怎样用系统API来轻松实现这个功能.API原型函数:bool Animat ...
- winform C#屏幕右下角弹出消息框并自动消失
①弹出信息框后慢慢下降消失 在主窗体中新增按钮重命名为btnShowAndDisappearMessages,在click事件中写如下代码: private void btnShowAndDisapp ...
- 提问(prompt 消息对话框)用于询问一些需要与用户交互的信息。弹出消息对话框(包含一个确定按钮、取消按钮与一个文本输入框)
提问(prompt 消息对话框) prompt弹出消息对话框,通常用于询问一些需要与用户交互的信息.弹出消息对话框(包含一个确定按钮.取消按钮与一个文本输入框). 语法: prompt(str1, s ...
- jquery easyui 弹出消息框 (转载) jQuery EasyUI API 中文文档 - 消息框(Messager) http://www.cnblogs.com/hantianwei/archive/2012/03/19/2407113.html
<html> <head> <!-- 导入easyui插件的js和css样式; --> <link rel="stylesheet" ty ...
- jquery easyui 弹出消息框
<html> <head> <!-- 导入easyui插件的js和css样式; --> <link rel="stylesheet" ty ...
- PHP自定义弹出消息类,用于弹出提示信息并返回
一个用PHP自写的弹出消息类,用于在程序出错时弹出提示,,弹出警告框,或在程序运行到某阶段的快捷提示,需用时只需传入参数即可,函数并不复杂,但觉得挺实用.具体代码: function Alert($a ...
- bat脚本弹出消息示例(msg命令详细解释)
弹出消息的bat,其实就是通过批处理调用msg命令,msg是系统自在的一个可以发送信息的命令. 示例: @echo off rem 测试MSG msg * "ok" rem 测试M ...
- 弹出消息对话框ScriptManager
//直接调用WebMessageBox方法 #region 弹出消息对话框 /// <summary> /// 弹出消息对话框 /// </summary> /// <p ...
- 右下角弹出"Windows-延缓写入失败"或者"xxx-损坏文件 请运行Chkdsk工具"
知识点分析: 任务栏右下角弹出“Windows-延缓写入失败”或者“xxx-损坏文件 请运行Chkdsk工具”. 操作步骤: 方法一:Chkdsk工具 在开始---运行中输入cmd,然后输入chkds ...
- C#利用API制作类似QQ一样的右下角弹出窗体
C#利用API制作类似QQ一样的右下角弹出窗体 (2009-03-21 15:02:49) 转载▼ 标签: 杂谈 分类: .NET using System;using System.Collecti ...
随机推荐
- Python网络爬虫第一弹
03.Python网络爬虫第一弹<Python网络爬虫相关基础概念> 爬虫介绍 引入 之前在授课过程中,好多同学都问过我这样的一个问题:为什么要学习爬虫,学习爬虫能够为我们以后的发展带来那 ...
- ElementUI ---- dialog点击取消后蒙遮层不消失
场景: 页面A打开了 dialog, 然后点击 页面A dialog 的按钮 跳转到 页面B,并且打开页面B的 dialog 但是页面B的 dialog 关闭后,蒙遮层并没消失(已经设置了 :appe ...
- C#/.NET/.NET Core技术前沿周刊 | 第 14 期(2024年11.18-11.24)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...
- Golang之常用方法[总结]
1. 有一堆数字,如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字? nums := []int{1, 5, 1, 6, 5, 3, 6} i := 0 for _, v := ...
- Redis之常用模块Module
1. Redis-Cell 限流模块 2.RedisBloom 布隆过滤器 https://github.com/RedisBloom/RedisBloom
- uni-app 坑
1.fixed定位 在H5中,tabbar,顶部导航栏,系统状态栏(手机信号,电量显示等)包含在内容区,H5在定位时,需要算上这些高度(如果页面中存在这个元素的话) 解决办法:使用条件编译,针对不同的 ...
- PictureMarkerSymbol透明的问题
由于我使用的是位图图片,所以不可能将图片背景处理成透明.不过还是可以通过参数的设定来达到这种效果. PictureMarkerSymbol pPMS = new PictureMarkerSymbol ...
- npm install报错的解决方法
解决方法 node版本不对,问问前端开发,node版本是什么版本,用nvm install,并切换到正常的node版本: git代码有时候会有冲突,把前端项目中的依赖包node_modules 和 p ...
- Swift中让值类型以引用方式传递
Swift中让值类型以引用方式传递 在 Swift 众多数据类型中,只有 class 是引用类型, 其余的如 Int.Float.Bool.Character.Array.Set.enum.struc ...
- 私有网盘+在线文档:内网离线搭建NextCloud+OnlyOffice详细指南
背景 最近因为工作需要,一堆内网的虚拟机之间需要频繁cp一些文件.视频等,因为都是麒麟系统,有桌面版有服务器版,用scp这种工具也是比较繁琐,索性就搭建一套内网用的共享网盘和在线文档,既方便了自己,也 ...