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 ...
随机推荐
- php ice框架
ice框架是php扩展框架 概念和 yaf Phalcon 那种框架类似,就是把框架编译为C扩展,调用起来就等于调用C,这样框架本身的加载消耗就省下来了. pecl https://pecl.php ...
- etcd错误:Failed to defragment etcd member[127.0.0.1:2379] (context deadline exceeded)
etcd 版本 # etcdctl version etcdctl version: 3.5.1 API version: 3.5 问题 在 执行 etcdctl --endpoints=http:/ ...
- Blazor 组件库 BootstrapBlazor 中Card组件介绍
一个较为完整的Card样子 Card组件介绍 Card组件分为三部分,CardHeader.CardBody.CardFooter. 代码格式如下: <Card> <CardHead ...
- Codeforces Round 856 (Div2)
Counting Factorizations 任何一个正整数 \(m\) 都可以被唯一的分解为 \(p_1^{e_1} \cdot p_2^{e_2} \ldots p_k^{e_k}\) 的形式. ...
- 生成式AI如何辅助医药行业智能营销
生成式AI如何辅助医药行业智能营销 生成式AI在医药行业的智能营销中发挥着日益重要的作用,它通过多种方式辅助医药企业提升市场洞察能力.优化营销策略.增强客户互动和体验,从而推动销售增长和品牌价值的提升 ...
- 【Kotlin】select简介
1 前言 协程的 select 是一种用于异步操作的选择器,它允许同时等待多个挂起函数的结果,并在其中一个完成时执行相应的操作. 能够被 select 的事件都是 SelectClause,在 ...
- 《前端运维》一、Linux基础--12网络
这是linux部分的最后一篇内容,我们一起来学习下Linux网络. 我们先看些命令吧: ifconfig,查看与配置网络状态. netstat,查询网络状态,常用选项如下: -t,列出TCP协议端口 ...
- jmeter使用jdbc连接SQL server,执行SQL报错处理
前置环境参数:jdk-8u391-windows-x64,驱动:sqljdbc4.jar 备注:这是解决后的截图,将就用 问题一:使用jmeter5.5,使用jdbc连接SQL server,执行SQ ...
- 内存Fuzz和WinAFL
文章一开始发表在微信公众号 https://mp.weixin.qq.com/s/XSPrmBb44J8BUpKsj0cwGQ 内存Fuzz和WinAFL FoxitReader 软件分析 目前Fuz ...
- 【Windows】查看笔记本电池寿命/损耗度(查看电池使用时间报告)
① Win+r 运行 命令提示符窗口 ② 输入powercfg/batteryreport 你将会得到电池使用时间报告 将这个地址粘贴到浏览器地址栏访问,或者根据这个地址在资源管理器中找到这个文件夹双 ...