应用场景 :
比如开发了一个基于GUI的应用程序,这个应用程序依赖的东西不是很好安装,或者安装起来很耗时耗力。
那怎么办呢,docker无疑是比较合适的解决方案。
客户拿到docker镜像即可运行使用,避免了麻烦的安装过程和环境依赖问题。
如何基于docker打包基于GUI的应用程序呢?又如何启动这个docker容器呢?
我这里以 ubuntu16.04 操作系统为背景,结合自写的pyqt5的demo GUI程序,来说明如何使用dockerfile生成一个镜像。读者最好有dockerfile的相关了解。
1. 首先写一个pyqt5的小界面,该界面通过点击按钮打开一个摄像头,展示实时画面到界面。
代码文件夹为: ui_demo,里面有2个文件:requirements.txt 和client.py
(1)依赖库 requirements.txt
PyQt5==5.12.1
PyQt5-sip==4.19.19
opencv-python==4.4.0.42
qtawesome==0.7.2(2)界面代码 client.py
# -*- coding: utf-8 -*- """ @author: wind @contact: 367059791@qq.com @time: 2021/05/30 10:43 @desc: """ # PyQt5==5.12.1 # PyQt5-sip==4.19.19 # opencv-python==4.4.0.42 # qtawesome==0.7.2 from PyQt5.QtWidgets import ( QWidget, QApplication, QVBoxLayout, QPushButton, QLabel, ) from PyQt5.QtCore import Qt from PyQt5.QtGui import ( QImage, QPixmap, ) import qtawesome as qta import cv2 class UIBase(QWidget): def __init__(self): super(UIBase, self).__init__() self.main_vLayout = QVBoxLayout(self) # 主布局 self.label_video = QLabel(self) self.btn_video = QPushButton(qta.icon('fa.camera-retro', color='red'), "打开摄像头", self) self.cap = None # init ui self.main_vLayout.addWidget(self.label_video, 5) self.main_vLayout.addWidget(self.btn_video, 1) self.setWindowTitle("Demo v0.1") # 标题 self.setWindowIcon(qta.icon('fa.thumbs-o-up', color='red')) # logo self.btn_video.clicked.connect(self.on_open_cap) self.resize(600, 400) def on_open_cap(self): self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) if self.cap: self.btn_video.clicked.disconnect(self.on_open_cap) self.btn_video.clicked.connect(self.on_close_cap) self.btn_video.setText("关闭摄像头") while self.cap: res, frame = self.cap.read() if res: display = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) scale = self.label_video.width() / display.shape[1] show_width = display.shape[1] * scale show_height = display.shape[0] * scale img = QImage(display.data, display.shape[1], display.shape[0], QImage.Format_RGB888) pixImg = QPixmap.fromImage( img.scaled(show_width, show_height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) self.label_video.setPixmap(pixImg) cv2.waitKey(5) def on_close_cap(self): if self.cap: self.cap.release() self.cap = None self.btn_video.clicked.connect(self.on_open_cap) self.btn_video.clicked.disconnect(self.on_close_cap) self.btn_video.setText("打开摄像头") if __name__ == '__main__': import sys app = QApplication(sys.argv) window = UIBase() window.show() sys.exit(app.exec())
运行效果如下:
2.编写 dockerfile,docker build -t 生成镜像
FROM ubuntu:bionic LABEL maintainer "Wind <367059791@qq.com>" ENV DEBIAN_FRONTEND=noninteractive # 设置为”noninteractive”,可以直接运行命令,无需向用户请求输入 RUN \ apt-get update -qq && \ apt-get install -qq -y \ git \ python3 \ python3-pip \ python3-pyqt5 \ language-pack-zh* \ # 中文编码 ttf-wqy-microhei \ # 安装中文字体 && rm -rf /var/lib/apt/lists/* # 减小最终大小 RUN mkdir /root/workdir RUN python3 -m pip install -U pip setuptools wheel COPY ui_demo /root/workdir/ui_demo # 拷贝本地代码目录到docker里 RUN python3 -m pip install -r /root/workdir/ui_demo/requirements.txt # 安装依赖 # 中文编码设置 RUN locale-gen zh_CN.UTF-8 RUN update-locale LANG=zh_CN.UTF-8 LANGUAGE=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 ENV LANG zh_CN.UTF-8 WORKDIR /root/workdir ENTRYPOINT [ "python3", "/root/workdir/ui_demo/client.py" ]
注意中文编码问题!
最后执行 docker build -t . 来生成docker镜像
3. 运行容器:
把docker容器看做一台没配显示器的电脑,程序可以运行,但是没地方显示。
linux系统目前的主流图像界面服务X11支持 客户端/服务端(C/S)的工作模式,只要在容器启动的时候,将『unix:端口』或『主机名:端口』共享给Docker,
或者直白点,将主机中X服务器使用的文件作为卷传递给容器,Docker就可以通过端口找到显示输出的地方,和linux系统共用显示接口。1).安装 xserver:
sudo apt install x11-xserver-utils2).设置权限:
# 允许所有用户访问显示接口
xhost +3).运行Docker镜像时设置环境变量:
-v /tmp/.X11-unix:/tmp/.X11-unix #共享本地unix端口
-e DISPLAY=unix$DISPLAY #修改环境变量DISPLAY4).使用宿主机的物理设备:
在运行 Docker时,能够通过添加 --privileged 参数来让Docker容器使用宿主机的现有设备。
除此之外,也可使用--device参数详细明确Docker容器能够使用哪些设备(推荐)
在Ubuntu系统中,宿主机中的设备默认都保存在 /dev 目录下5) 那么在Docker中显示图像可以这样运行指令:
docker run -it --privileged -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY
源文章:https://blog.csdn.net/u012219045/article/details/117028695?spm=1001.2014.3001.5501