树莓派 Bookworm + PyQt5 虚拟环境 Kiosk 开机自启方案(工控全屏无人值守)
适用:Raspberry Pi OS Bookworm(Debian 12)、PyQt5、Python 虚拟环境、OpenCV(解决 Qt 插件冲突)、开机直接全屏启动程序,不加载桌面环境,崩溃自动重启。
痛点:Bookworm 新版 Xorg 权限限制、OpenCV 自带 Qt 与 PyQt5 插件冲突、could not connect to display、xcb 平台插件加载失败、虚拟环境激活问题。
前置说明
- 系统:Raspberry Pi OS 64-bit Bookworm
- 界面库:PyQt5
- 虚拟环境目录:
/home/pi/DAYA - 程序路径:
/home/pi/daya/V0.4/main.py - 目标:开机自动登录 → 拉起极简 X11 → 激活 venv → 运行 PyQt 程序,程序退出或崩溃后自动重启
⚠️ 重要:PyQt5 只支持 X11,不原生支持 Wayland;不要用系统自带 labwc 桌面做 kiosk,资源占用高且容易异常。
一、系统基础配置:设置开机进入控制台 + tty1 自动登录 pi 用户
打开 raspi-config:
sudo raspi-config进入
1 System Options→S5 Boot,选择B1 Console(Text console,开机进入命令行,不进桌面)。新版 raspi-config 无图形自动登录选项,执行下面一行命令开启 tty1 自动登录:
sudo raspi-config nonint do_boot_behaviour B2
等价于 Console Autologin,开机 tty1 直接登录 pi,无需输入账号密码。
备选手动配置自动登录(上面命令失效时使用)
sudo mkdir -p /etc/systemd/system/getty@tty1.service.d sudo nano /etc/systemd/system/getty@tty1.service.d/override.conf写入以下内容:
[Service] ExecStart= ExecStart=-/sbin/agetty --autologin pi --noclear %I $TERM保存退出,重载 systemd:
sudo systemctl daemon-reload关闭自动登录命令:
sudo rm /etc/systemd/system/getty@tty1.service.d/override.conf sudo systemctl daemon-reload
二、安装 X11 基础服务与 xcb 依赖(解决 xcb 找不到、display 报错)
sudo apt update # 安装 xorg、xinit 极简 X 服务,不装桌面 sudo apt install xorg xinit xserver-xorg-legacy # 补齐 xcb 全套依赖(Bookworm 必装,虚拟环境经常缺) sudo apt install libxcb-cursor0 libxkbcommon-x11-0 libxcb-xinerama0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-shape0修改 Xwrapper 权限,解决Cannot open /dev/tty0 (Permission denied):
sudo nano /etc/X11/Xwrapper.config修改内容为:
allowed_users=anybody needs_root_rights=yes保存退出。
三、处理 OpenCV 与 PyQt5 Qt 插件冲突(关键坑!)
原因:普通
opencv-python包内置 Qt,会篡改 Qt 插件路径,导致 PyQt5 加载 xcb 失败;我们使用 PyQt 绘图,不需要cv2.imshow。
激活虚拟环境:
source /home/pi/DAYA/bin/activate卸载带 GUI 的 opencv,安装无 GUI 的 headless 版本:
pip uninstall opencv-python -y pip install opencv-python-headless如果代码必须使用
cv2.imshow,不能换 headless 方案:在启动脚本中手动指定QT_QPA_PLATFORM_PLUGIN_PATH指向 PyQt5 插件目录。
四、编写 xinit 启动脚本~/.xinitrc
作用:X11 启动后自动执行,关闭屏幕休眠、激活虚拟环境、循环启动 PyQt 程序、输出日志。
nano ~/.xinitrc完整内容(直接复制):
#!/bin/bash # 关闭屏幕休眠、DPMS 节能 xset s off xset -dpms xset s noblank 指定 X 显示编号,解决 could not connect to display export DISPLAY=:0 进入家目录,激活虚拟环境 cd /home/pi source DAYA/bin/activate 循环:程序崩溃或退出后自动重启,日志写入 app.log while true; do python3 /home/pi/daya/V0.4/main.py >> /home/pi/app.log 2>&1 sleep 1 done赋予执行权限:
chmod +x ~/.xinitrc五、配置 bash_profile,tty1 登录后自动执行 startx
nano ~/.bash_profile写入:
if [ "$(tty)" = "/dev/tty1" ]; then startx fi逻辑:仅本地 tty1 终端触发 startx,SSH 远程登录不会拉起 X11,避免报错。
六、本地分步测试(严禁 SSH 直接 startx!)
必须接显示器、键盘,在树莓派本地黑终端(tty1)测试。
本地 tty1 手动测试整套流程:
source DAYA/bin/activate startx✅ 预期:X11 启动,PyQt 窗口弹出;关闭程序后,1 秒自动重启。
❌ 禁止:SSH 远程直接执行
startx或者python3 main.py,一定会报 display 错误。日志查看(调试崩溃):
# 实时跟踪日志 tail -f /home/pi/app.log
七、PyQt 代码配套建议(Kiosk 全屏 + ESC 退出调试)
在主窗口类中添加:
from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget class MainWindow(QWidget): def init(self): super().init() # 全屏模式 self.showFullScreen() # 无边框备选 # self.setWindowFlags(Qt.FramelessWindowHint) # self.show() # ESC 按键退出程序,调试救命快捷键 def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.close()</code></pre> 八、常用维护命令清单 # 查看程序运行日志 tail -f /home/pi/app.log 切换回开机自动进桌面(恢复原来系统) sudo raspi-config nonint do_boot_behaviour B4 关闭 tty1 自动登录(开机需要手动输密码) sudo rm /etc/systemd/system/getty@tty1.service.d/override.conf sudo systemctl daemon-reload 临时取消自动重启(调试代码用,修改 .xinitrc 删掉 while 循环)注:恢复系统原厂桌面模式(完全回到普通桌面系统)
文章内自带命令,直接执行,会关闭控制台自动登录,开机进图形桌面自动登录 pi 用户:
sudo raspi-config nonint do_boot_behaviour B4 sudo rebootB4 = boot to desktop autologin,开机直接进 Bookworm 桌面环境。
切回原来 Kiosk 模式:
sudo raspi-config nonint do_boot_behaviour B2 sudo rebootB2 = Console Autologin,控制台自动登录,配合
.bash_profile里 startx 重新启动 kiosk 模式。