ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YASB Media Widget 配置完全指南:打造可滚动标签与弹出播放器的媒体状态栏控件

YASB Media Widget 配置完全指南:打造可滚动标签与弹出播放器的媒体状态栏控件 桌面应用【免费下载链接】yasbA highly configurable Windows status bar written in Python.项目地址https://gitcode.com/gh_mirrors/yas/yasb点击查看免费下载导读Media Widget 是 YASB一个用 Python 编写的 Windows 状态栏中用于展示“正在播放”信息的核心小组件它能在任务栏上显示当前播放的歌曲或视频标题、艺术家与专辑封面内置上一曲 / 播放暂停 / 下一曲按钮支持超长标题滚动并可弹出包含进度条、时间标签、来源应用与逐应用音量滑杆的播放控制菜单。本文以官方文档为主体结合仓库源码widget 实现、校验模型、SMTC 媒体服务逐一讲解全部配置项、默认值与底层工作原理读完你可以独立完成 Media Widget 的接入、外观定制与故障排查。图片预览下图为 Media Widget 在状态栏上的实际呈现来源文档原图。一、组件能力与数据来源Media Widget 是一个“播放器控制控件”它展示当前正在播放的歌曲或视频并具备以下能力显示专辑封面缩略图支持透明度、圆角、边缘渐隐等效果内联媒体按钮上一曲prev_track、播放 / 暂停play/pause、下一曲next_track超长标题滚动scrolling_label弹出媒体菜单popup包含封面大图、标题 / 艺术家 / 来源应用、进度滑块、播放时间与逐应用音量控制。它的数据来自 Windows 系统的SMTCGlobal System Media Transport Controls。在源码中WindowsMedia是一个以QSingleton实现的单例服务src/core/widgets/services/media/media.py通过winrt.windows.media.control的GlobalSystemMediaTransportControlsSessionManager请求会话、订阅会话列表变化 / 当前会话变化 / 媒体属性 / 时间线 / 播放信息五类事件并以约 0.1 秒REFRESH_INTERVAL 0.1的轮询间隔插值当前播放位置_interpolate_and_emit再把状态通过 Qt 信号分发给 Widget。重要限制原文保留时间线与 seek 功能只有在播放器通过 Windows 媒体 API 上报 position 和 duration 时才会生效。很多浏览器和部分应用不上报或上报的是无效值因此 seek 滑块可能会保持禁用、跳动或完全不动。YASB 只展示系统给到的数据——如果来源应用没有暴露可用的时间线Widget 也无能为力。此外源码中还有一层保护MAX_TIMLINE_DURATION 6048007 天——当会话时长超过 7 天或时间线不可用时进度条与时间线容器会被隐藏见 media.py 及show_menu中的判断逻辑。二、完整配置项总览将media段写入 YASB 的config.yaml位于C:/Users/{username}/.config/yasb/或YASB_CONFIG_HOME指定的目录详见 Configuration.mdtype固定为yasb.media.MediaWidget。选项类型默认值文档说明labelstring{artist}{s}{title}主标签格式字符串。label_altstring{title}备用标签格式字符串。separatorstring - 动态分隔符{s}的实际文本位于标签开头或结尾时会被自动剥离详见下文。class_namestring附加到组件上的自定义 CSS 类名。max_field_sizedict—标签最大字段长度。max_field_size.labelinteger20主标签最大长度。max_field_size.label_altinteger30备用标签最大长度。max_field_size.truncate_whole_labelbooleanfalse超长时是否截断整个标签而不是只截断超出的字段。show_thumbnailbooleantrue是否显示媒体缩略图。controls_onlybooleanfalse是否只显示媒体控制按钮。controls_leftbooleantrue控制按钮是否位于左侧。controls_hidebooleanfalse是否隐藏媒体控制按钮。hide_emptybooleantrue无媒体信息时是否隐藏整个组件。thumbnail_alphainteger50缩略图的透明度alpha。thumbnail_paddinginteger8缩略图周围的内边距。thumbnail_corner_radiusinteger0缩略图圆角半径0 为直角。symmetric_corner_radiusbooleanfalse是否对缩略图四角使用对称圆角。thumbnail_edge_fadebooleanfalse是否对缩略图应用边缘渐隐效果。iconsdict—媒体控制按钮图标。icons.prev_trackstring\uf048上一曲按钮图标。icons.next_trackstring\uf051下一曲按钮图标。icons.playstring\uf04b播放按钮图标。icons.pausestring\uf04c暂停按钮图标。media_menudict见下媒体菜单弹出层配置。media_menu_iconsdict见下媒体菜单中的图标配置。scrolling_labeldict见下标签滚动配置。progress_bardict见下组件内嵌进度条配置。callbacksdict见下鼠标事件回调。源码校验模型中的真实默认值文档表格中的“默认值”对应官方推荐预设代码层面真正的默认值由 Pydantic 校验模型定义src/core/validation/widgets/yasb/media.py两者不完全一致配置时以你显式写入的值优先label默认为{title}label_alt默认为{artist} - {title}文档表分别为{artist}{s}{title}与{title}hide_empty默认False文档表为truemax_field_size.label默认15取值范围 0–200truncate_whole_label默认Truethumbnail_alpha取值范围0–255thumbnail_padding取值范围0–200thumbnail_corner_radius取值范围0–100media_menu默认blurTrue、round_cornersTrue、border_colorSystem、alignmentright、thumbnail_size100、max_title_size150、max_artist_size40、max_source_size16scrolling_label.update_interval_ms默认 33代码内会被钳制在 4–1000msstyle默认leftseparator默认空格progress_bar.enabled默认Falsealignment默认bottom。官方安装向导预设src/core/setup/widgets_config.py中Media Widget 使用label: {title}{s}{artist}、hide_empty: True、controls_hide: True、thumbnail_alpha: 100、thumbnail_edge_fade: True、media_menu.alignment: center且show_volume_slider: True可作为一份开箱即用的参考配置。三、示例配置可直接复制media: type: yasb.media.MediaWidget options: label: {title}{s}{artist} label_alt: {title} separator: - hide_empty: true callbacks: on_left: toggle_label on_middle: do_nothing on_right: do_nothing max_field_size: label: 20 label_alt: 30 show_thumbnail: true controls_only: false controls_left: true controls_hide: false thumbnail_alpha: 80 thumbnail_padding: 8 thumbnail_corner_radius: 16 icons: prev_track: \ue892 next_track: \ue893 play: \ue768 pause: \ue769 media_menu: blur: false round_corners: true round_corners_type: normal border_color: system alignment: right direction: down offset_top: 6 offset_left: 0 thumbnail_corner_radius: 8 thumbnail_size: 120 max_title_size: 60 max_artist_size: 20 max_source_size: 16 show_source: true show_volume_slider: true media_menu_icons: play: \ue768 pause: \ue769 prev_track: \ue892 next_track: \ue893 scrolling_label: enabled: false update_interval_ms: 33 style: left # 可选 left、right、bounce、bounce-ease separator: | label_padding: 0 always_scroll: false # 缓动曲线参数参考https://www.desmos.com/calculator/j7eamemxzi ease_slope: 20 ease_pos: 0.8 ease_min: 0.5在状态栏的widgets段left/center/right任一区中引用该段即可启用例如bars: bar: widgets: right: [media]四、各子配置项详解4.1 标签与字段label / label_alt / separator / max_field_sizelabel / label_alt格式字符串可用{title}、{artist}等占位符动态插入媒体信息label_alt用于展示补充信息通常配合toggle_label回调在两条标签间切换。separator 与{s}{s}是动态分隔符占位符。当某段信息在来源端缺失时为避免标签首尾残留悬空分隔符如 - - 歌名Widget 会调用分词器 tokenizer.py 的clean_string先按{占位符} / 字面量分词再丢弃值为空的占位符最后只保留“前后都有非空占位符”的分隔符。这就是文档所说“动态分隔符会被自动剥离”的实现原理。max_field_sizelabel/label_alt为对应标签的最大字符数超长时截断并追加...见_format_max_field_size截断方式为text[:max_size - 3] ...truncate_whole_label: true时对整条格式化结果截断否则仅截断单个超长字段。注意启用scrolling_label后_format_max_field_size会直接返回原文滚动标签自身依赖max_field_size.label作为宽度上限。4.2 缩略图show_thumbnail 与视觉处理缩略图在 media.py 的_crop_thumbnail中按以下流程处理按thumbnail_padding与当前活动标签宽度等比缩放Image.LANCZOS高质量重采样按组件容器高度纵向居中裁剪用thumbnail_alpha值构造 alpha 通道若启用thumbnail_edge_fade则对 alpha 通道应用左右两侧约 30% 宽度的渐隐与容器背景形成平滑过渡此时圆角不生效否则当thumbnail_corner_radius 0时生成圆角遮罩——默认只圆“靠近按钮的对侧”两角controls_left: true时圆右侧两角symmetric_corner_radius: true时四角统一圆角。另外当会话没有封面时popup 会回退到内置的 media.png 占位图_build_empty_thumbnail。4.3 控制按钮controls_only / controls_left / controls_hide / icons三个按钮按controls_left决定排在缩略图与文本的左侧还是右侧controls_only: true时隐藏标签、缩略图与进度条只保留按钮源码中会hide()对应控件。controls_hide: true时完全不创建按钮_create_media_button直接返回None适合只需要信息展示的场景。按钮的可用 / 禁用状态来自 SMTC 的PlaybackInfo快照字段controls_prev_enabled/controls_play_enabled/controls_next_enabled不可用时按钮会带上disabledCSS 类。播放 / 暂停按钮图标会根据is_playing在icons.play与icons.pause间切换同一逻辑也作用于 popup 的media_menu_icons。4.4 媒体菜单media_menu弹出层由工具类PopupWidgetsrc/core/utils/utilities.py创建无边框、置顶、支持背景模糊acrylic、圆角与边框色setPosition按alignmentleft / center / right、directionup / down、offset_top/offset_left相对父组件定位。菜单内容在show_menu中动态构建左侧为按thumbnail_size裁剪成方形的封面大图_create_thumbnail_for_popup可点击唤起来源应用_open_media_source右侧文本区包含max_title_size/max_artist_size限制的标题与艺术家控制区含上一曲 / 播放暂停 / 下一曲按钮以及show_source: true时的来源应用标签底部为时间线容器progress-slider内部精度 0–1000加playback-time.current/playback-time.total两个时间标签用户拖动时通过_on_slider_released把百分比换算成秒并以 100 纳秒为单位调用try_change_playback_position_async完成 seekshow_volume_slider: true时右侧会出现“逐应用音量”竖滑杆先用会话的 AUMID 通过pycaw的AudioUtilities.GetAllSessions()匹配音频会话优先按 AUMID失败则回退按进程可执行文件名再用SimpleAudioVolume接口读取 / 设置音量并切换静音对应mute/unmute图标。无任何会话时菜单显示no-media类的“No media playing”占位文案。在组件与 popup 上滚动鼠标滚轮可在多个媒体会话间切换WheelEventFilter向上滚下一个、向下滚上一个。media_menu: blur: false # 是否对弹出背景应用模糊效果。 round_corners: true # 是否圆角弹出层。 round_corners_type: normal # normal 或 small仅 Win11 border_color: system # 弹出层边框颜色可为 HEX、None 或 system。 alignment: right # 相对组件的对齐方式left、center 或 right。 direction: down # 弹出方向up 或 down。 offset_top: 6 # 距组件的垂直偏移。 offset_left: 0 # 距组件的水平偏移。 thumbnail_corner_radius: 8 # 弹出层封面的圆角半径。 thumbnail_size: 120 # 弹出层封面尺寸。 max_title_size: 60 # 弹出层标题最大长度。 max_artist_size: 20 # 弹出层艺术家最大长度。 max_source_size: 16 # 弹出层来源应用名最大长度。 show_source: true # 是否显示媒体来源应用名由系统/应用元数据解析如 Spotify、Firefox。 show_volume_slider: false # 是否显示逐应用音量滑杆。4.5 媒体菜单图标media_menu_iconsmedia_menu_icons: play: \ue768 # 弹出层播放按钮图标。 pause: \ue769 # 弹出层暂停按钮图标。 prev_track: \ue892 # 弹出层上一曲按钮图标。 next_track: \ue893 # 弹出层下一曲按钮图标。 mute: \ue994 # 弹出层静音按钮图标。 unmute: \ue74f # 弹出层取消静音按钮图标。4.6 滚动标签scrolling_label滚动能力来自工具类ScrollingLabelsrc/core/utils/utilities.py支持四种styleleft/right循环平移滚动超出部分用separator拼接重复文本always_scroll: true时无论文本是否超宽都滚动bounce来回弹跳两侧各留label_padding个字符的边距bounce-ease带缓动的弹跳速度由ease_slope坡度、ease_pos拐点位置、ease_min最小速度比例三条参数控制公式为(1 tanh(-slope·(x - pos)))·(1 - min)/2 min可通过 Desmos 交互曲线直观调参。scrolling_label: enabled: false # 是否启用滚动标签。 update_interval_ms: 33 # 滚动更新间隔毫秒有效范围 4–1000。 style: left # 滚动样式left、right、bounce 或 bounce-ease。 separator: | # left/right 样式中重复文本间的分隔符。 label_padding: 1 # bounce/bounce-ease 样式两侧的填充字符数默认各 1 个字符。 ease_slope: 20 # bounce 缓动坡度。缓动曲线参考https://www.desmos.com/calculator/j7eamemxzi ease_pos: 0.8 # bounce 缓动曲线位置。 ease_min: 0.5 # bounce 缓动曲线的最小值。滚动标签注意事项原文保留滚动标签使用max_field_size限制自身尺寸滚动标签会禁用thumbnail_padding此时应改用.media-widget .label { margin: ... }控制间距。4.7 组件内嵌进度条progress_bar在组件本身而非 popup底部叠加一层细进度条指示当前播放进度只有当会话启用时间线且时长在 7 天以内时才显示。alignment控制它在容器内的垂直位置。progress_bar: enabled: false # 是否启用组件上的进度条。 alignment: bottom # 进度条在组件容器内的对齐方式top、bottom 或 center。4.8 可用回调callbacks回调说明toggle_label在主标签与备用标签间切换显示。toggle_play_pause切换播放 / 暂停状态。toggle_media_menu打开 / 关闭媒体菜单弹出层。open_media_source打开正在播放媒体的来源应用按 AUMID 激活。do_nothing占位回调触发时不做任何事。callbacks的键为on_left、on_middle、on_right分别绑定左 / 中 / 右键。源码细节toggle_label仅在非controls_only模式下注册open_media_source通过activate_app_by_aumid激活会话见 media.py。五、来源应用名的解析与“按来源定制样式”popup 中的source标签显示来源应用名其解析链在 source_apps.py 中定义按优先级依次尝试AppInfo.get_from_app_user_model_id(aumid)的display_nameshell:AppsFolder的ParseName名称保证 PWA 不被错误标记为浏览器由 AUMID 反查进程可执行文件再取进程的FileDescription由窗口 AUMIDFirefox / Zen 等哈希反查 PID 得到应用名最后退化为_humanize去掉扩展名、取!后段、下划线转空格等。解析结果会按 AUMID 缓存。而get_source_app_class_name会把显示名转为小写并将空格替换为连字符——因此样式表中“来源类名”与组件中看到的来源名一一对应例如Windows Media对应.source.windows-media。这意味着你可以为每个播放器定制专属配色见下文示例中的.media-menu .source.spotify、.source.chrome等。六、样式定制CSS ClassesWidget 渲染为 Qt Widgets可使用 Qt 样式表qss定制外观整体主题样式相关约定见 Styling.md。6.1 可用样式类.media-widget {} .media-widget .widget-container {} .media-widget .label {} .media-widget .label.alt {} .media-widget .btn.play {} .media-widget .btn.prev {} .media-widget .btn.next {} .media-widget .btn.disabled {} .media-widget .progress-bar { } .media-widget .progress-bar::chunk {} .media-menu {} .media-menu .no-media {} .media-menu .title {} .media-menu .artist {} .media-menu .source {} .media-menu .btn.play {} .media-menu .btn.prev {} .media-menu .btn.next {} .media-menu .btn.disabled {} .media-menu .thumbnail {} .media-menu .media-timeline-container {} .media-menu .playback-time {} .media-menu .playback-time.current {} .media-menu .playback-time.total {} .media-menu .progress-slider {} .media-menu .progress-slider::groove {} .media-menu .progress-slider::sub-page {} .media-menu .progress-slider::handle {} .media-menu .progress-slider::handle:hover {} .media-menu .app-volume-container {} .media-menu .app-volume-container .volume-slider {} .media-menu .app-volume-container .volume-slider::groove {} .media-menu .app-volume-container .volume-slider::sub-page {} .media-menu .app-volume-container .volume-slider::handle {} .media-menu .app-volume-container .volume-slider::handle:hover {} .media-menu .app-volume-container .mute-button {}6.2 组件样式示例.media-widget { padding: 0; margin: 0; } .media-widget .label { color: #d2d6e2; padding: 0px; padding-right: 4px; font-size: 12px; } .media-widget .btn { color: #9498a8; padding: 0 4px; margin: 0; font-family: Segoe Fluent Icons; font-weight: 400; } .media-widget .btn:hover { color: #babfd3; } .media-widget .btn.play { font-size: 16px; } .media-widget .btn.disabled:hover, .media-widget .btn.disabled { color: #4e525c; font-size: 12px; background-color: rgba(0, 0, 0, 0); } .media-widget .progress-bar { max-height: 2px; background-color: transparent; margin-left: 5px; border: none; } .media-widget .progress-bar::chunk { background-color: #0078D4ee; border-radius: 2px; }6.3 弹出菜单样式示例.media-menu { min-width: 440px; max-width: 440px; background-color: rgba(31, 39, 49, 0.5); } .media-menu .title, .media-menu .artist, .media-menu .source { font-size: 14px; font-weight: 600; margin-left: 10px; font-family: Segoe UI; } .media-menu .artist { font-size: 13px; color: #6c7086; margin-top: 0px; } .media-menu .source { font-size: 11px; color: #000; border-radius: 3px; background-color: #bac2de; padding: 2px 4px; font-weight: 600; font-family: Segoe UI; margin-top: 10px; } /* 来源类名的规则与组件中看到的来源名一致空格换成连字符并转为小写。 示例Windows Media 变成 windows-media */ .media-menu .source.aimp { background-color: #6f42c1; color: #ffffff; } .media-menu .source.apple-music { background-color: #fa2b56; color: #ffffff; } .media-menu .source.brave { background-color: #fb542b; color: #ffffff; } .media-menu .source.chrome { background-color: #4285f4; color: #ffffff; } .media-menu .source.edge { background-color: #0078d4; color: #ffffff; } .media-menu .source.firefox { background-color: #ff7139; color: #ffffff; } .media-menu .source.foobar2000 { background-color: #444444; color: #ffffff; } .media-menu .source.media-player { background-color: #0078d4; color: #ffffff; } .media-menu .source.murglar { background-color: #8a8a8a; color: #ffffff; } .media-menu .source.musicbee { background-color: #ffcc00; color: #000000; } .media-menu .source.nsmusics { background-color: #e64a19; color: #ffffff; } .media-menu .source.opera { background-color: #ff1b2d; color: #ffffff; } .media-menu .source.qobuz { background-color: #003a6f; color: #ffffff; } .media-menu .source.spotify { background-color: #1db954; color: #ffffff; } .media-menu .source.tidal { background-color: #000000; color: #ffffff; } .media-menu .source.winamp { background-color: #f1a11b; color: #000000; } .media-menu .source.youtube { background-color: #ff0000; color: #ffffff; } .media-menu .source.youtube-music { background-color: #c51f1f; color: #ffffff; } .media-menu .source.zen { background-color: #2ecc71; color: #000000; } .media-menu .btn { font-family: Segoe Fluent Icons; font-size: 14px; font-weight: 400; margin: 10px 2px 0px 2px; min-width: 40px; max-width: 40px; min-height: 40px; max-height: 40px; border-radius: 20px; } .media-menu .btn.prev { margin-left: 10px; } .media-menu .btn:hover { color: white; background-color: rgba(255, 255, 255, 0.1); } .media-menu .btn.play { background-color: rgba(255, 255, 255, 0.1); font-size: 20px } .media-menu .btn.disabled:hover, .media-menu .btn.disabled { color: #4e525c; background-color: rgba(0, 0, 0, 0); } .media-menu .playback-time { font-size: 13px; font-family: Segoe UI; color: #7f849c; margin-top: 20px; min-width: 100px; } .media-menu .progress-slider { height: 10px; margin: 5px 4px; border-radius: 3px; } .media-menu .progress-slider::groove { background: transparent; height: 2px; border-radius: 3px; background: rgba(255, 255, 255, 0.1); } .media-menu .progress-slider::groove:hover { background: transparent; height: 6px; border-radius: 3px; background: rgba(255, 255, 255, 0.2); } .media-menu .progress-slider::sub-page { background: white; border-radius: 3px; height: 4px; } .media-menu .app-volume-container { background-color: rgba(255, 255, 255, 0.05); padding: 8px 6px; border-radius: 16px; margin-left: 10px; } .media-menu .app-volume-container .volume-slider::groove { background: rgba(255, 255, 255, 0.1); width: 2px; border-radius: 3px; } .media-menu .app-volume-container .volume-slider::add-page { background: white; border-radius: 3px; } .media-menu .app-volume-container .volume-slider::groove:hover { background: rgba(255, 255, 255, 0.1); width: 6px; border-radius: 3px; } .media-menu .app-volume-container .volume-slider::sub-page { background: rgba(255, 255, 255, 0.1); border-radius: 3px; } .media-menu .app-volume-container .mute-button, .media-menu .app-volume-container .unmute-button { font-size: 16px; color: #ffffff; font-family: Segoe Fluent Icons; margin-top: 4px; } .media-menu .app-volume-container .unmute-button { color: #a0a0a0; }注意原文保留以上样式示例使用 Segoe Fluent Icons 字体作为按钮图标你也可以按设计需求使用任何其它图标字体或自定义图标。七、常见调优建议与排错要点标签不更新 / 按钮灰置按钮可用状态来自播放器上报的 SMTCPlaybackInfo如果来源应用不上报按钮会带disabled类可用.media-widget .btn.disabled弱化其视觉权重。seek 滑块不动或跳动属于来源应用不提供有效时间线的表现不是配置错误参见第一节的官方说明。切换会话在组件或 popup 上滚动滚轮即可在多个媒体会话如浏览器标签页、Spotify、本地播放器间切换源码中 popup 的WheelEventFilter在切换会话后会重建菜单以同步内容。想要更精简的竖向“唱片封面风”方案YASB 还提供同源的media_lite组件yasb.media_lite.MediaWidget支持封面 标题/艺术家单行展示与弹出播放器详见 Media Lite Widget 文档-Media-Lite.md)。图标字体示例默认使用Segoe Fluent Icons自定义图标时同步修改icons/media_menu_icons与对应字体族即可。配置校验所有选项均经过 Pydantic 模型src/core/validation/widgets/yasb/media.py校验非法取值如越界的thumbnail_alpha、非法style枚举会在启动时报错修改配置后保存会自动重载见 watcher.py 的文件监听机制。赞分享桌面应用【免费下载链接】yasbA highly configurable Windows status bar written in Python.项目地址https://gitcode.com/gh_mirrors/yas/yasb点击查看免费下载相关推荐yasb Media Lite 媒体控件配置指南紧凑竖版封面栏与弹出式播放器的完整实战yasb Media Lite 媒体控件配置指南紧凑竖版封面栏与弹出式播放器的完整实战 本文是 yasb基于 Python 与 Qt 的高度可配置 Wind桌面应用YASB状态栏完全配置指南打造专属桌面神器YASB状态栏完全配置指南打造专属桌面神器 YASBYet Another Status Bar是一款基于Python开发的高度可配置Windows状态栏桌面应用ANIMATED TAB BAR与AVFoundation集成媒体播放状态标签栏指示ANIMATED TAB BAR与AVFoundation集成媒体播放状态标签栏指示 你是否曾在使用媒体类App时因无法直观判断播放状态而反复切换页面本文移动开发UI组件前端上一篇VisualCppRedist AIO一站式解决Windows软件运行库依赖难题下一篇Apache Beam PTransform 风格指南编写可复用 Transform 的完整设计规范与仓库源码实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进