树莓派 Bookworm + PyQt5 虚拟环境 Kiosk 开机自启方案(工控全屏无人值守)

发布时间:2026/11/5 3:49:06
树莓派 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 用户

  1. 打开 raspi-config:

    sudo raspi-config
  2. 进入1 System Options→S5 Boot,选择B1 Console(Text console,开机进入命令行,不进桌面)。

  3. 新版 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)测试。

  1. 本地 tty1 手动测试整套流程:

    source DAYA/bin/activate startx

    ✅ 预期:X11 启动,PyQt 窗口弹出;关闭程序后,1 秒自动重启。

    ❌ 禁止:SSH 远程直接执行startx或者python3 main.py,一定会报 display 错误。

  2. 日志查看(调试崩溃):

    # 实时跟踪日志 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 reboot

B4 = boot to desktop autologin,开机直接进 Bookworm 桌面环境。

切回原来 Kiosk 模式:

sudo raspi-config nonint do_boot_behaviour B2 sudo reboot

B2 = Console Autologin,控制台自动登录,配合.bash_profile里 startx 重新启动 kiosk 模式。