应用场景 :

比如开发了一个基于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-utils

2).设置权限:
# 允许所有用户访问显示接口
xhost +

3).运行Docker镜像时设置环境变量:
-v /tmp/.X11-unix:/tmp/.X11-unix #共享本地unix端口
-e DISPLAY=unix$DISPLAY #修改环境变量DISPLAY

4).使用宿主机的物理设备:
在运行 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