求助:Conky中两个Lua函数无法独立更新的问题
问题描述
我编写了两个Lua函数getSongLyrics和getSongTitle,用于在Conky窗口展示内容:
getSongLyrics通过Python脚本执行sptlrx pipe,将结果输出到FIFO文件后读取,再通过conky_displaySongLyrics展示getSongTitle借助playerctl获取当前播放歌曲的歌手与标题,通过conky_displaySongTitle直接展示
当前存在的问题:
- 切换歌曲时,
getSongTitle的输出仅在getSongLyrics更新(出现新歌词行)时才会刷新 - 若播放无歌词的歌曲,
conky_displaySongLyrics始终不更新,conky_displaySongTitle也会停留在上一首歌曲的信息,直到播放有歌词的歌曲才会更新
推测是FIFO文件等待sptlrx pipe新内容导致的阻塞问题,希望实现两个函数独立运行、互不影响。
相关代码
scripts.lua
-- read file and print contents of file function readPrintFile(file) local f = io.open(file, "rb") -- if file doesn't exist, return nil if not f then return nil end local content = f:read("*a") f:close() return content end -- get song title from a music source function getSongTitle() local command = io.popen("playerctl metadata --player spotify --format '{{ artist }} - {{ title }}'", "r") local title = command:read("*l") command:close() return format:gsub("\n", "") end -- get song lyrics from a music source function getSongLyrics() local lyrics = readPrintFile("/tmp/sptlrx_lyrics") or "" return lyrics:gsub("\n", "") end function conky_displaySongTitle() return getSongTitle() end function conky_displaySongLyrics() return getSongLyrics() end
sptlrx_lyrics.py
import os import subprocess pipe_path_lyrics = "/tmp/sptlrx_lyrics" try: os.mkfifo(pipe_path_lyrics) except FileExistsError: pass process_lyrics = subprocess.Popen(["sptlrx", "pipe"], stdout=subprocess.PIPE) for line_lyrics in iter(process_lyrics.stdout.readline, ""): with open(pipe_path_lyrics, "wb") as lyrics: lyrics.write(line_lyrics)
conky.conf
conky.config = { lua_load = "$HOME/.config/conky/scripts.lua", ... } conky.text = [[ ${if_running spotify}\${alignc} ${lua displaySongTitle} ${alignc} ${lua displaySongLyrics}\... ${endif} ]]
解决方案
问题根源
Conky默认同步执行所有Lua函数,而FIFO文件是阻塞式读取——如果sptlrx pipe没有新内容写入,io.open或read操作会卡住整个Conky的更新循环,导致getSongTitle无法执行。
具体修改步骤
修复Lua函数的bug并实现非阻塞读取FIFO
首先修正getSongTitle里的变量错误,再修改readPrintFile为非阻塞读取:-- 非阻塞读取文件内容 function readPrintFile(file) local f = io.open(file, "rb") if not f then return "" end -- 设置非阻塞模式 f:setvbuf("no") -- 用pcall捕获读取阻塞的异常 local ok, content = pcall(function() return f:read("*a") end) f:close() -- 读取失败则返回空字符串 return ok and content or "" end -- 获取歌曲标题(修复变量错误) function getSongTitle() local command = io.popen("playerctl metadata --player spotify --format '{{ artist }} - {{ title }}'", "r") local title = command:read("*l") or "" command:close() return title:gsub("\n", "") end优化Python脚本,监听歌曲切换事件
原脚本仅在有歌词时写入FIFO,切换无歌词歌曲时不会更新。修改后添加歌曲切换监听,主动清空FIFO:import os import subprocess import select pipe_path_lyrics = "/tmp/sptlrx_lyrics" try: os.mkfifo(pipe_path_lyrics) except FileExistsError: pass # 启动sptlrx获取歌词,同时用playerctl监听歌曲切换 sptlrx_proc = subprocess.Popen(["sptlrx", "pipe"], stdout=subprocess.PIPE, text=True) playerctl_proc = subprocess.Popen( ["playerctl", "metadata", "--player", "spotify", "--follow", "--format", "changed"], stdout=subprocess.PIPE, text=True ) def write_fifo(content): try: with open(pipe_path_lyrics, "w") as f: f.write(content.strip() + "\n") except Exception: pass # 初始写入空内容 write_fifo("") while True: # 非阻塞监听两个进程的输出 read_streams, _, _ = select.select([sptlrx_proc.stdout, playerctl_proc.stdout], [], [], 1) for stream in read_streams: line = stream.readline().strip() if stream == sptlrx_proc.stdout and line: write_fifo(line) elif stream == playerctl_proc.stdout: # 歌曲切换时清空歌词 write_fifo("") # 进程退出则终止循环 if sptlrx_proc.poll() is not None or playerctl_proc.poll() is not None: break调整Conky更新配置
在conky.config中确保足够的更新频率:conky.config = { lua_load = "$HOME/.config/conky/scripts.lua", update_interval = 1, -- 每秒更新一次 lua_draw_hook_pre = "", -- 确保Lua函数每次更新都执行 ... }
内容的提问来源于stack exchange,提问作者Anapal
相关产品推荐
相关产品推荐

