소스 검색

Merge remote-tracking branch 'origin/dev-python' into dev-frontend

panqiuyao 7 달 전
부모
커밋
71f355971e
100개의 변경된 파일23072개의 추가작업 그리고 394개의 파일을 삭제
  1. 2 1
      .gitignore
  2. 1 1
      electron/config/config.default.js
  3. 1 1
      electron/config/config.local.js
  4. 1 1
      frontend/src/router/index.ts
  5. 14 14
      public/dist/index.html
  6. 6 2
      python/.gitignore
  7. 29 17
      python/action.json
  8. 379 52
      python/api.py
  9. 6 5
      python/config.ini
  10. 0 0
      python/custom_plugins/__init__.py
  11. 0 0
      python/custom_plugins/plugins/__init__.py
  12. 0 0
      python/custom_plugins/plugins/detail_template/__init__.py
  13. 0 0
      python/custom_plugins/plugins/detail_template/hongqingting/__init__.py
  14. 198 0
      python/custom_plugins/plugins/detail_template/hongqingting/detail_hongqingting1.py
  15. 0 0
      python/custom_plugins/plugins/detail_template/huilima/__init__.py
  16. 245 0
      python/custom_plugins/plugins/detail_template/huilima/detail_huilima1.py
  17. 262 0
      python/custom_plugins/plugins/detail_template/huilima/detail_huilima2.py
  18. 357 0
      python/custom_plugins/plugins/detail_template/huilima/detail_huilima3.py
  19. 304 0
      python/custom_plugins/plugins/detail_template/huilima/detail_huilima4.py
  20. 0 0
      python/custom_plugins/plugins/detail_template/xiaosushuoxie/__init__.py
  21. 337 0
      python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie1.py
  22. 626 0
      python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie2.py
  23. 482 0
      python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie3.py
  24. 459 0
      python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie4.py
  25. 603 0
      python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie5.py
  26. 576 0
      python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie6.py
  27. 0 0
      python/custom_plugins/plugins/generate_and_upload/__init__.py
  28. 151 0
      python/custom_plugins/plugins/generate_and_upload/main_run.py
  29. 0 0
      python/custom_plugins/plugins/micropython_update/__init__.py
  30. 418 0
      python/custom_plugins/plugins/micropython_update/ampy_plugin.py
  31. 0 0
      python/custom_plugins/plugins/micropython_update/other_micropython_code/__init__.py
  32. 23 0
      python/custom_plugins/plugins/micropython_update/other_micropython_code/create_all_folders.py
  33. 50 0
      python/custom_plugins/plugins/micropython_update/other_micropython_code/delete_all_temp.py
  34. 56 0
      python/custom_plugins/plugins/micropython_update/other_micropython_code/get_remote_has256.py
  35. 24 0
      python/custom_plugins/plugins/micropython_update/other_micropython_code/restart.py
  36. 101 0
      python/custom_plugins/plugins/micropython_update/other_micropython_code/update_from_temp.py
  37. 0 0
      python/custom_plugins/plugins_mode/__init__.py
  38. 638 0
      python/custom_plugins/plugins_mode/detail_generate_base.py
  39. 593 0
      python/custom_plugins/plugins_mode/pic_deal.py
  40. 23 0
      python/custom_plugins/plugins_mode/plugins_base_func.py
  41. 36 18
      python/databases.py
  42. 311 0
      python/docs/socket命令.md
  43. 11 10
      python/index.py
  44. 9 0
      python/mcu/BlueToothMode.py
  45. 189 0
      python/mcu/DebugUart.py
  46. 310 39
      python/mcu/DeviceControl.py
  47. 0 1
      python/mcu/McuDebug.py
  48. 76 59
      python/mcu/McuDeviationSet.py
  49. 242 0
      python/mcu/OtherSet.py
  50. 11 6
      python/mcu/ProgramItem.py
  51. 5 2
      python/mcu/RemoteControlV2.py
  52. 63 13
      python/mcu/capture/module_watch_dog.py
  53. 16 15
      python/middleware.py
  54. 1 0
      python/model/device_config.py
  55. 7 2
      python/model/photo_record.py
  56. 43 0
      python/models.py
  57. BIN
      python/requestments.txt
  58. 726 0
      python/service/AutoDealPics.py
  59. 102 0
      python/service/OnePicTest.py
  60. 1007 0
      python/service/auto_deal_pics/base_deal.py
  61. 28 0
      python/service/auto_deal_pics/data.py
  62. 84 0
      python/service/auto_deal_pics/detail_temp.py
  63. 648 0
      python/service/auto_deal_pics/main_control.py
  64. 77 0
      python/service/auto_deal_pics/queren_control.py
  65. 1005 0
      python/service/auto_deal_pics/run_main.py
  66. 473 0
      python/service/auto_deal_pics/upload_pic.py
  67. 8 8
      python/service/base.py
  68. 33 33
      python/service/base_deal.py
  69. 420 4
      python/service/data.py
  70. 296 0
      python/service/data_metaclass.py
  71. 6 6
      python/service/deal_cutout.py
  72. 51 56
      python/service/deal_image.py
  73. 5 6
      python/service/deal_one_image.py
  74. 179 0
      python/service/detail_func.py
  75. 84 0
      python/service/detail_temp.py
  76. 11 11
      python/service/excel_base_func.py
  77. 313 0
      python/service/generate_goods_art_no_table/module_generate_goods_art_no_table.py
  78. 340 0
      python/service/generate_goods_no_detail_pic/ceshi.py
  79. 332 0
      python/service/generate_goods_no_detail_pic/data.py
  80. 176 0
      python/service/generate_goods_no_detail_pic/detail_func.py
  81. 574 0
      python/service/generate_goods_no_detail_pic/detail_generate_base.py
  82. 206 0
      python/service/generate_goods_no_detail_pic/detail_hlm.py
  83. 221 0
      python/service/generate_goods_no_detail_pic/detail_hlm_2.py
  84. 228 0
      python/service/generate_goods_no_detail_pic/detail_hlm_3.py
  85. 325 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie.py
  86. 612 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie2.py
  87. 489 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie3.py
  88. 458 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie4.py
  89. 586 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie5.py
  90. 552 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie6.py
  91. 206 0
      python/service/generate_goods_no_detail_pic/detial_hqt.py
  92. 1177 0
      python/service/generate_goods_no_detail_pic/goods_detail_pics.py
  93. 913 0
      python/service/generate_goods_no_detail_pic/module_generate_goods_no_detail_pics.py
  94. 570 0
      python/service/generate_goods_no_detail_pic/pic_deal.py
  95. 422 0
      python/service/generate_main_image/grenerate_main_image_test.py
  96. 216 0
      python/service/generate_main_image/image_deal_base_func.py
  97. 481 0
      python/service/generate_main_image/module_generate_main_image.py
  98. 9 8
      python/service/grenerate_main_image_test.py
  99. 5 3
      python/service/image_pic_deal.py
  100. 123 0
      python/service/load_plugins.py

+ 2 - 1
.gitignore

@@ -11,4 +11,5 @@ pnpm-lock.yaml
 __pycache__
 *.pyc
 build/*
-**/dist/
+**/dist/
+.DS_Store

+ 1 - 1
electron/config/config.default.js

@@ -64,7 +64,7 @@ module.exports = (appInfo) => {
    * 远程模式-web地址
    */
   config.remoteUrl = {
-    enable: true,
+    enable: false,
     url: 'http://localhost:3000'
   };
 

+ 1 - 1
electron/config/config.local.js

@@ -60,7 +60,7 @@ module.exports = (appInfo) => {
    * 远程模式-web地址
    */
   config.remoteUrl = {
-    enable: true,
+    enable: false,
     url: 'http://localhost:3000/home'
   };
 

+ 1 - 1
frontend/src/router/index.ts

@@ -5,7 +5,7 @@ import { authGuard } from './plugins/authGuard'
 const routes: RouteRecordRaw[] = [
     {
         path: "/",
-        redirect: "/home"
+        redirect: "/photography/check"
     },
     {
         path: "/home",

+ 14 - 14
public/dist/index.html

@@ -1,14 +1,14 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <link rel="icon" type="image/svg+xml" href="./vite.svg" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>智惠映AI自动拍照机</title>
-    <script type="module" crossorigin src="./assets/index-6PwDCeNx.js"></script>
-    <link rel="stylesheet" crossorigin href="./assets/index-CfZ73FPC.css">
-  </head>
-  <body>
-    <div id="app"></div>
-  </body>
-</html>
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="./vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>智惠映AI自动拍照机</title>
+    <script type="module" crossorigin src="./assets/index-CW4uQ-aR.js"></script>
+    <link rel="stylesheet" crossorigin href="./assets/index-CL_4evXq.css">
+  </head>
+  <body>
+    <div id="app"></div>

+  </body>
+</html>

+ 6 - 2
python/.gitignore

@@ -3,6 +3,10 @@ venv/
 *.log
 *.log.*
 resources/
-custom_plugins/
+# custom_plugins/
 *.db
-*.pdf
+*.pdf
+output/*
+qt_test/*
+*.jpg
+*.png

+ 29 - 17
python/action.json

@@ -2,8 +2,9 @@
     {
         "mode_type": "执行左脚程序",
         "execution_type": "程序1",
-        "action_name": "左脚俯拍鞋子",
-        "action_index": 20,
+        "action_name": "俯视",
+        "action_index": 10,
+        "is_system":true,
         "picture_index": 99,
         "camera_height": 200,
         "camera_angle": 14.0,
@@ -21,8 +22,9 @@
     {
         "mode_type": "执行左脚程序",
         "execution_type": "程序1",
-        "action_name": "侧视图",
-        "action_index": 10,
+        "action_name": "侧视",
+        "action_index": 20,
+        "is_system": true,
         "picture_index": 99,
         "camera_height": 0,
         "camera_angle": 3.0,
@@ -40,9 +42,10 @@
     {
         "mode_type": "执行左脚程序",
         "execution_type": "程序1",
-        "action_name": "后跟",
+        "action_name": "后跟",
         "action_index": 30,
         "picture_index": 99,
+        "is_system": true,
         "camera_height": 0,
         "camera_angle": 3.0,
         "number_focus": 0,
@@ -59,9 +62,10 @@
     {
         "mode_type": "执行左脚程序",
         "execution_type": "程序2",
-        "action_name": "鞋底",
+        "action_name": "鞋底",
         "action_index": 40,
         "picture_index": 99,
+        "is_system": true,
         "camera_height": 0,
         "camera_angle": 3.0,
         "number_focus": 0,
@@ -78,10 +82,11 @@
     {
         "mode_type": "执行左脚程序",
         "execution_type": "程序2",
-        "action_name": "内里",
+        "action_name": "内里",
         "action_index": 50,
         "picture_index": 99,
         "camera_height": 0,
+        "is_system": true,
         "camera_angle": 3.0,
         "number_focus": 0,
         "take_picture": true,
@@ -102,6 +107,7 @@
         "picture_index": 99,
         "camera_height": 200,
         "camera_angle": 12.0,
+        "is_system": false,
         "number_focus": 1,
         "take_picture": false,
         "turntable_position": 300.0,
@@ -115,12 +121,13 @@
     },
     {
         "mode_type": "执行右脚程序",
-        "execution_type": "程序1",
-        "action_name": "右脚俯拍鞋子",
-        "action_index": 20,
+        "execution_type": "程序2",
+        "action_name": "俯视",
+        "action_index": 10,
         "picture_index": 99,
         "camera_height": 200,
         "camera_angle": 14.0,
+        "is_system": true,
         "number_focus": 2,
         "take_picture": true,
         "turntable_position": 300.0,
@@ -134,12 +141,13 @@
     },
     {
         "mode_type": "执行右脚程序",
-        "execution_type": "程序1",
-        "action_name": "侧视",
-        "action_index": 10,
+        "execution_type": "程序2",
+        "action_name": "侧视",
+        "action_index": 20,
         "picture_index": 99,
         "camera_height": 0,
         "camera_angle": 3.0,
+        "is_system": true,
         "number_focus": 0,
         "take_picture": true,
         "turntable_position": 300.0,
@@ -153,10 +161,11 @@
     },
     {
         "mode_type": "执行右脚程序",
-        "execution_type": "程序1",
-        "action_name": "后跟",
+        "execution_type": "程序2",
+        "action_name": "后跟",
         "action_index": 30,
         "picture_index": 99,
+        "is_system": true,
         "camera_height": 0,
         "camera_angle": 3.0,
         "number_focus": 0,
@@ -173,9 +182,10 @@
     {
         "mode_type": "执行右脚程序",
         "execution_type": "程序2",
-        "action_name": "鞋底",
+        "action_name": "鞋底",
         "action_index": 40,
         "picture_index": 99,
+        "is_system": true,
         "camera_height": 0,
         "camera_angle": 3.0,
         "number_focus": 0,
@@ -192,9 +202,10 @@
     {
         "mode_type": "执行右脚程序",
         "execution_type": "程序2",
-        "action_name": "内里",
+        "action_name": "内里",
         "action_index": 50,
         "picture_index": 99,
+        "is_system": true,
         "camera_height": 0,
         "camera_angle": 3.0,
         "number_focus": 0,
@@ -215,6 +226,7 @@
         "action_index": 60,
         "picture_index": 99,
         "camera_height": 200,
+        "is_system": false,
         "camera_angle": 12.0,
         "number_focus": 1,
         "take_picture": false,

+ 379 - 52
python/api.py

@@ -6,14 +6,28 @@ import json
 from logger import logger
 from serial.tools import list_ports
 from model import PhotoRecord
+import settings, datetime
+import pandas as pd
 from utils.hlm_http_request import forward_request
+from utils.utils_func import check_path
 from sockets.socket_client import socket_manager
 from mcu.DeviceControl import DeviceControl
-import time
+import time, shutil, os
 from sqlalchemy import and_, asc, desc
-
+from functools import partial
 from service.deal_image import DealImage
-from databases import DeviceConfig, SqlQuery, CRUD, select
+from databases import DeviceConfig, SysConfigs, SqlQuery, CRUD, select
+from service.run_main import RunMain
+import importlib
+from service.auto_deal_pics.upload_pic import UploadPic
+from service.OnePicTest import OnePicTest
+
+# from service.AutoDealPics import AutoDealPics
+# for plugin in settings.plugins:
+#     module_path, class_name = plugin.rsplit(".", 1)
+#     print("module_path", module_path, class_name)
+#     module = importlib.import_module(module_path)
+#     getattr(module, class_name)
 
 
 @app.get("/")
@@ -67,9 +81,7 @@ def test_conndevice():
                     p_list.append(_port_name)
                     device_control.add_port_by_linkage(_port_name)
                 except BaseException as e:
-                    print(
-                        e.__traceback__.tb_frame.f_globals["__file__"]
-                    )  # 发生异常所在的文件
+                    print(e.__traceback__.tb_frame.f_globals["__file__"])  # 发生异常所在的文件
                     print(e.__traceback__.tb_lineno)  # 发生异常所在的行数
                     print("串口不存在{} {}".format(_port_name, e))
 
@@ -77,9 +89,7 @@ def test_conndevice():
             # self.add_port(_p)
 
 
-@app.api_route(
-    "/forward_request", methods=["GET", "POST"], description="代理转发hlm项目得请求"
-)
+@app.api_route("/forward_request", methods=["GET", "POST"], description="代理转发hlm项目得请求")
 async def forwardRequest(request: HlmForwardRequest):
     """
     转发HTTP请求到目标URL
@@ -100,9 +110,7 @@ async def forwardRequest(request: HlmForwardRequest):
         if not target_url:
             raise UnicornException("目标url地址不能为空")
         # 调用 hlm_http_request 中的 forward_request 函数
-        response = forward_request(
-            target_url, params=params, method=method, headers=headers
-        )
+        response = forward_request(target_url, params=params, method=method, headers=headers)
         return response
     except requests.RequestException as e:
         raise UnicornException(e)
@@ -110,43 +118,243 @@ async def forwardRequest(request: HlmForwardRequest):
         raise UnicornException(e)
 
 
+def fromExcelHandler(params: HandlerDetail):
+    excel_path = params.excel_path
+    excel_df = pd.read_excel(excel_path, sheet_name=0, header=0)
+    handler_result = []
+    handler_result_folder = ""
+    if "文件夹名称" not in excel_df.columns:
+        raise UnicornException("缺失 [文件夹名称] 列")
+    if "商品货号" not in excel_df.columns:
+        raise UnicornException("缺失 [商品货号] 列")
+    for index, row in excel_df.iterrows():
+        goods_art_no_image_dir = str(row["文件夹名称"])
+        goods_art_no = str(row["商品货号"])
+        try:
+            if not goods_art_no:
+                raise UnicornException("货号不能为空")
+            token = "Bearer " + params.token
+            session = SqlQuery()
+            pr = CRUD(PhotoRecord)
+            images = pr.read_all(session, conditions={"goods_art_no": goods_art_no})
+            if not images:
+                raise UnicornException("没有可用货号数据")
+            image_dir = "{}/data/".format(os.getcwd()).replace("\\", "/")
+            check_path(image_dir)
+            for itemImg in images:
+                if not os.path.exists(image_dir + "/" + os.path.basename(itemImg.image_path)):
+                    shutil.copy(itemImg.image_path, image_dir)
+            dealImage = DealImage(image_dir)
+            resFlag, path = dealImage.dealMoveImage(
+                image_dir=image_dir,
+                callback_func=None,
+                goods_art_no=goods_art_no_image_dir,
+            )
+            if not resFlag:
+                raise UnicornException(path)
+            temp_class = {}
+            temp_name_list = []
+            for tempItem in params.temp_list:
+                temp_class[tempItem.template_id] = tempItem.template_local_classes
+                temp_name_list.append(tempItem.template_id)
+            # if goods_art_no_image_dir not in path:
+            #     path = path + "/" + goods_art_no_image_dir
+            config_data = {
+                "image_dir": path,
+                "image_order": params.template_image_order,
+                "goods_art_no": goods_art_no,
+                "is_check_number": False,
+                "resize_image_view": "后跟",
+                "cutout_mode": settings.CUTOUT_MODE,
+                "logo_path": params.logo_path,
+                "special_goods_art_no_folder_line": "",
+                "is_use_excel": (False if params.excel_path == "" else True),  # 是否使用excel
+                "excel_path": params.excel_path,  # excel路径
+                "is_check_color_is_all": False,
+                "cutout_is_pass": True,
+                "assigned_page_dict": {},
+                "detail_is_pass": True,
+                "upload_is_pass": False,
+                "upload_is_enable": False,
+                "is_filter": False,
+                "temp_class": temp_class,
+                "temp_name": params.temp_name,
+                "temp_name_list": temp_name_list,
+                "target_error_folder": f"{path}/软件-生成详情错误",
+            }
+            # 动态导入类
+            temp_class_dict = {}
+            for key, class_path in config_data["temp_class"].items():
+                module_path, class_name = class_path.rsplit(".", 1)
+                module = importlib.import_module(module_path)
+                cls = getattr(module, class_name)
+                temp_class_dict[key] = cls
+
+            config_data["temp_class"] = temp_class_dict
+            obj = None
+            run_main = RunMain(obj, token)
+            return_data = run_main.check_before_cutout(config_data)
+            cutout_res = run_main.check_for_cutout_image_first_call_back(return_data)
+            check_for_detail_first_res = None
+            if cutout_res == True:
+                return_data_check_before_detail = run_main.check_before_detail(config_data)
+                print(
+                    "return_data_check_before_detail======> 测试 ==>",
+                    return_data_check_before_detail,
+                )
+                check_for_detail_first_res = run_main.check_for_detail_first_call_back(
+                    return_data_check_before_detail
+                )
+            if isinstance(check_for_detail_first_res, partial):
+                result = check_for_detail_first_res()
+                try:
+                    config_data = result["config_data"]
+                except:
+                    config_data = result
+            if config_data["sign_text"] == "已结束详情处理":
+                # at_pic = AutoDealPics()
+                print("config_data", config_data)
+                if config_data["upload_is_enable"]:
+                    to_deal_dir = "{}/软件-详情图生成".format(config_data["image_dir"])
+                    check_path(to_deal_dir)
+                    print("to_deal_dir", to_deal_dir)
+                    if os.path.exists(to_deal_dir):
+                        upload_pic = UploadPic(
+                            windows=None,
+                            to_deal_dir=to_deal_dir,
+                            config_data=config_data,
+                            token=token,
+                        )
+                        upload_pic.run()
+                out_put_dir = config_data["out_put_dir"]
+                out_put_dir_path = "{}/{}".format(os.getcwd(), out_put_dir).replace("\\", "/")
+                handler_result_folder = os.path.dirname(out_put_dir_path)
+                handler_result.append({"goods_art_no": goods_art_no, "success": True, "info": "处理成功"})
+            else:
+                handler_result.append({"goods_art_no": goods_art_no, "success": False, "info": "处理失败"})
+        except Exception as e:
+            handler_result.append({"goods_art_no": goods_art_no, "success": False, "info": str(e)})
+    handler_result_folder = "/".join(handler_result_folder.split("/")[:-1])
+    return {
+        "code": 0,
+        "msg": "",
+        "data": {"output_folder": handler_result_folder, "list": handler_result},
+    }
+
+
 @app.post("/handle_detail")
-async def handle_detail(request: Request):
-
-    image_dir = "{}/data".format(os.getcwd())
-    dealImage = DealImage(image_dir)
-    dealImage.header = request.headers
-    print(">>>>>>>>>>>>>>>>>请求参数")
-    print(dealImage.header)
-    result = dealImage.dealMoveImage(image_dir=image_dir, callback_func=None)
-    return result
-
-    #
-    # params = json.dump(request.query_params)
-    # #{'image_dir': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18', 'image_order': '俯视,侧视,后跟,鞋底,内里', 'is_check_number': True, 'resize_image_view': '后跟', 'cutout_mode': '1', 'logo_path': '', 'special_goods_art_no_folder_line': '', 'is_use_excel': True, 'excel_path': '', 'is_check_color_is_all': True, 'assigned_page_dict': {}, 'temp_class': {'huilima-2': <class 'detail_template.huilima.detail_huilima2.DetailPicGet'>, 'huilima-3': <class 'detail_template.huilima.detail_huilima3.DetailPicGet'>, 'huilima-4': <class 'detail_template.huilima.detail_huilima4.DetailPicGet'>, 'huilima-1': <class 'detail_template.huilima.detail_huilima1.DetailPicGet'>}, 'temp_name': 'huilima-2', 'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'], 'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'}
-    #
-    # config_data = {
-    #     'image_dir': params['image_dir'],
-    #     'image_order': params['image_order'],
-    #     'is_check_number': params['is_check_number'],
-    #     'resize_image_view': params['resize_image_view'],
-    #     'cutout_mode': '1',
-    #     'logo_path': params['logo_path'],
-    #     'special_goods_art_no_folder_line': '',
-    #     'is_use_excel': params['is_use_excel'],
-    #     'excel_path': params['excel_path'],
-    #     'is_check_color_is_all': params['is_check_color_is_all'],
-    #     'assigned_page_dict': {},
-    #     'temp_class': {
-    #         'huilima-2': 'detail_template.huilima.detail_huilima2.DetailPicGet',
-    #         'huilima-3': 'detail_template.huilima.detail_huilima3.DetailPicGet',
-    #         'huilima-4': 'detail_template.huilima.detail_huilima4.DetailPicGet',
-    #         'huilima-1': 'detail_template.huilima.detail_huilima1.DetailPicGet'
-    #     },
-    #     'temp_name': 'huilima-2',
-    #     'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'],
-    #     'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'
-    # }
+async def handle_detail(request: Request, params: HandlerDetail):
+    goods_art_no_array = params.goods_art_no
+    handler_result = []
+    handler_result_folder = ""
+    if params.excel_path != "" and params.excel_path != None:
+        return fromExcelHandler(params)
+    for goods_art_no in goods_art_no_array:
+        try:
+            if not goods_art_no:
+                raise UnicornException("货号不能为空")
+            token = "Bearer " + params.token
+            session = SqlQuery()
+            pr = CRUD(PhotoRecord)
+            images = pr.read_all(session, conditions={"goods_art_no": goods_art_no})
+            if not images:
+                raise UnicornException("没有可用货号数据")
+            image_dir = "{}/data/".format(os.getcwd()).replace("\\", "/")
+            check_path(image_dir)
+            for itemImg in images:
+                if not os.path.exists(image_dir + "/" + os.path.basename(itemImg.image_path)):
+                    shutil.copy(itemImg.image_path, image_dir)
+            dealImage = DealImage(image_dir)
+            resFlag, path = dealImage.dealMoveImage(
+                image_dir=image_dir, callback_func=None, goods_art_no=goods_art_no
+            )
+            if not resFlag:
+                raise UnicornException(path)
+            temp_class = {}
+            temp_name_list = []
+            for tempItem in params.temp_list:
+                temp_class[tempItem.template_id] = tempItem.template_local_classes
+                temp_name_list.append(tempItem.template_id)
+            config_data = {
+                "image_dir": path,
+                "image_order": params.template_image_order,
+                "goods_art_no": goods_art_no,
+                "is_check_number": False,
+                "resize_image_view": "后跟",
+                "cutout_mode": settings.CUTOUT_MODE,
+                "logo_path": params.logo_path,
+                "special_goods_art_no_folder_line": "",
+                "is_use_excel": (False if params.excel_path == "" else True),  # 是否使用excel
+                "excel_path": params.excel_path,  # excel路径
+                "is_check_color_is_all": False,
+                "cutout_is_pass": True,
+                "assigned_page_dict": {},
+                "detail_is_pass": True,
+                "upload_is_pass": False,
+                "upload_is_enable": False,
+                "is_filter": False,
+                "temp_class": temp_class,
+                "temp_name": params.temp_name,
+                "temp_name_list": temp_name_list,
+                "target_error_folder": f"{path}/软件-生成详情错误",
+            }
+            # 动态导入类
+            temp_class_dict = {}
+            for key, class_path in config_data["temp_class"].items():
+                module_path, class_name = class_path.rsplit(".", 1)
+                module = importlib.import_module(module_path)
+                cls = getattr(module, class_name)
+                temp_class_dict[key] = cls
+
+            config_data["temp_class"] = temp_class_dict
+            obj = None
+            run_main = RunMain(obj, token)
+            return_data = run_main.check_before_cutout(config_data)
+            cutout_res = run_main.check_for_cutout_image_first_call_back(return_data)
+            check_for_detail_first_res = None
+            if cutout_res == True:
+                return_data_check_before_detail = run_main.check_before_detail(config_data)
+                print(
+                    "return_data_check_before_detail======> 测试 ==>",
+                    return_data_check_before_detail,
+                )
+                check_for_detail_first_res = run_main.check_for_detail_first_call_back(
+                    return_data_check_before_detail
+                )
+            if isinstance(check_for_detail_first_res, partial):
+                result = check_for_detail_first_res()
+                try:
+                    config_data = result["config_data"]
+                except:
+                    config_data = result
+            if config_data["sign_text"] == "已结束详情处理":
+                # at_pic = AutoDealPics()
+                print("config_data", config_data)
+                if config_data["upload_is_enable"]:
+                    to_deal_dir = "{}/软件-详情图生成".format(config_data["image_dir"])
+                    check_path(to_deal_dir)
+                    print("to_deal_dir", to_deal_dir)
+                    if os.path.exists(to_deal_dir):
+                        upload_pic = UploadPic(
+                            windows=None,
+                            to_deal_dir=to_deal_dir,
+                            config_data=config_data,
+                            token=token,
+                        )
+                        upload_pic.run()
+                out_put_dir = config_data["out_put_dir"]
+                out_put_dir_path = "{}/{}".format(os.getcwd(), out_put_dir).replace("\\", "/")
+                handler_result_folder = os.path.dirname(out_put_dir_path)
+                handler_result.append({"goods_art_no": goods_art_no, "success": True, "info": "处理成功"})
+            else:
+                handler_result.append({"goods_art_no": goods_art_no, "success": False, "info": "处理失败"})
+        except Exception as e:
+            handler_result.append({"goods_art_no": goods_art_no, "success": False, "info": str(e)})
+    return {
+        "code": 0,
+        "msg": "",
+        "data": {"output_folder": handler_result_folder, "list": handler_result},
+    }
 
 
 @app.post("/get_device_configs", description="获取可执行程序命令列表")
@@ -178,6 +386,18 @@ def get_device_configs(params: ModelGetDeviceConfigDetail):
     return {"code": 0, "msg": "", "data": model}
 
 
+@app.post("/device_config_detail_query", description="通过条件获取可执行程序详情")
+def get_device_configs(params: ModelGetDeviceConfigDetailQuery):
+    mode_type = params.mode_type
+    action_name = params.action_name
+    session = SqlQuery()
+    configModel = CRUD(DeviceConfig)
+    model = configModel.read(session, conditions={"mode_type": mode_type, "action_name": action_name})
+    if model == None:
+        return {"code": 1, "msg": "数据不存在", "data": None}
+    return {"code": 0, "msg": "", "data": model}
+
+
 @app.post("/remove_config", description="删除一条可执行命令")
 def get_device_configs(params: ModelGetDeviceConfigDetail):
     action_id = params.id
@@ -186,6 +406,8 @@ def get_device_configs(params: ModelGetDeviceConfigDetail):
     model = configModel.read(session, conditions={"id": action_id})
     if model == None:
         return {"code": 1, "msg": "数据不存在", "data": None}
+    if model.is_system == True:
+        return {"code": 1, "msg": "系统配置不允许删除", "data": None}
     configModel.delete(session, obj_id=action_id)
     return {"code": 0, "msg": "删除成功", "data": None}
 
@@ -233,6 +455,7 @@ def get_photo_records(page: int = 1, size: int = 5):
 
     session = SqlQuery()
     photos = CRUD(PhotoRecord)
+    print("准备查询拍摄记录", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
     statement = (
         select(PhotoRecord)
         .offset((page - 1) * size)
@@ -242,10 +465,9 @@ def get_photo_records(page: int = 1, size: int = 5):
     )
     list = []
     result = session.exec(statement).all()
+    print("group 完成 ", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
     for item in result:
-        list_item = photos.read_all(
-            session, conditions={"goods_art_no": item.goods_art_no}
-        )
+        list_item = photos.read_all(session, conditions={"goods_art_no": item.goods_art_no})
         list.append(
             {
                 "goods_art_no": item.goods_art_no,
@@ -254,6 +476,7 @@ def get_photo_records(page: int = 1, size: int = 5):
             }
         )
     session.close()
+    print("循环查询 完成 ", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
     return {
         "code": 0,
         "msg": "",
@@ -261,6 +484,20 @@ def get_photo_records(page: int = 1, size: int = 5):
     }
 
 
+@app.get("/get_last_photo_record", description="获取最后一条拍照记录")
+def get_last_photo_record():
+
+    session = SqlQuery()
+    statement = select(PhotoRecord).where(PhotoRecord.image_path != None).order_by(desc("photo_create_time"))
+    result = session.exec(statement).first()
+    session.close()
+    return {
+        "code": 0,
+        "msg": "",
+        "data": result,
+    }
+
+
 @app.get("/get_photo_record_detail", description="通过货号获取拍照记录详情")
 def get_photo_records(goods_art_no: str = None):
     if goods_art_no == None:
@@ -315,5 +552,95 @@ def save_sys_configs(params: SysConfigParams):
         return {"code": 1, "msg": "配置不存在", "data": None}
     # 走编辑逻辑
     kwargs = params.__dict__
-    save_device_config = sysConfig.updateConditions(session, conditions={"key":params.key}, **kwargs)
+    save_device_config = sysConfig.updateConditions(session, conditions={"key": params.key}, **kwargs)
     return {"code": 0, "msg": "操作成功", "data": save_device_config}
+
+
+@app.post("/create_main_image", description="创建主图测试")
+def create_main_image(params: MaineImageTest):
+    file_path = params.file_path
+    onePic = OnePicTest(pic_path=file_path)
+    # session = SqlQuery()
+    # sysConfig = CRUD(SysConfigs)
+    # model = sysConfig.read(session, conditions={"key": params.key})
+    # if model == None:
+    #     return {"code": 1, "msg": "配置不存在", "data": None}
+    # # 走编辑逻辑
+    # kwargs = params.__dict__
+    # save_device_config = sysConfig.updateConditions(
+    #     session, conditions={"key": params.key}, **kwargs
+    # )
+    main_out_path = onePic.HandlerMainImage()
+    return {"code": 0, "msg": "操作成功", "data": {"main_out_path": main_out_path}}
+
+
+def insertEmptyLogoList(session):
+    """插入空logo列表"""
+    data = {"key": "logo_configs", "value": "[]"}
+    config = SysConfigs(**data)
+    session.add(config)
+    session.commit()
+    session.close()
+    item = SysConfigs()
+    item.key = "logo_configs"
+    item.value = "[]"
+    return item
+
+
+@app.get("/logo_list", description="logo列表")
+def logo_list():
+    session = SqlQuery()
+    photos = CRUD(SysConfigs)
+    item = photos.read(session, conditions={"key": "logo_configs"})
+    if item == None:
+        item = insertEmptyLogoList(session)
+    session.close()
+    return {
+        "code": 0,
+        "msg": "",
+        "data": json.loads(item.value),
+    }
+
+
+@app.post("/add_logo", description="添加logo")
+def add_logo(params: LogoParams):
+    logo_path = params.logo_path
+    session = SqlQuery()
+    sysConfig = CRUD(SysConfigs)
+    item = sysConfig.read(session, conditions={"key": "logo_configs"})
+    if item == None:
+        item = insertEmptyLogoList(session)
+    logo_list = json.loads(item.value)
+    if os.path.isfile(logo_path) == False:
+        return {"code": 1, "msg": "logo文件不存在", "data": None}
+    logo_dir = "{}/data/logo/".format(os.getcwd()).replace("\\", "/")
+    check_path(logo_dir)
+    fpath, fname = os.path.split(logo_path)
+    logo_path_info = logo_dir + fname
+    shutil.copy(logo_path, logo_path_info)  # 复制文件
+    if len(logo_list)>0:
+        for item in logo_list:
+            if logo_path_info == item:
+                continue
+            logo_list.append(logo_path_info)
+    else:
+        logo_list.append(logo_path_info)
+    data = {"key": "logo_configs", "value": json.dumps(logo_list)}
+    sysConfig.updateConditions(session, conditions={"key": "logo_configs"}, **data)
+    return {
+        "code": 0,
+        "msg": "",
+        "data": {"logo":logo_path_info},
+    }
+
+@app.post("/delete_logo", description="删除logo")
+def delete_logo(params: LogoParamsupdate):
+    session = SqlQuery()
+    sysConfig = CRUD(SysConfigs)
+    model = sysConfig.read(session, conditions={"key": "logo_configs"})
+    if model == None:
+        insertEmptyLogoList(session)
+    # 走编辑逻辑
+    kwargs = params.__dict__
+    save_device_config = sysConfig.updateConditions(session, conditions={"key": "logo_configs"}, **kwargs)
+    return {"code": 0, "msg": "操作成功", "data": save_device_config}

+ 6 - 5
python/config.ini

@@ -9,15 +9,16 @@ host=127.0.0.1
 app_run=api:app
 # 端口号
 port=7074
-debug=true
+debug=false
 env=dev
 # 线程数
-works=1
+works=3
+project=惠利玛
 [log]
 # 日志相关
-log_file_name=app.log 
+log_file_name=app.log
 #最大字节数
-max_bytes=102400 
+max_bytes=102400
 #备份数量
 backup_counts=3
 # 地址
@@ -39,4 +40,4 @@ left_foot_action_2 = 99
 right_foot_photograph = 99
 right_foot_action_1 = 99
 right_foot_action_2 = 99
-stop = 9
+stop = 9

+ 0 - 0
python/custom_plugins/__init__.py


+ 0 - 0
python/custom_plugins/plugins/__init__.py


+ 0 - 0
python/custom_plugins/plugins/detail_template/__init__.py


+ 0 - 0
python/custom_plugins/plugins/detail_template/hongqingting/__init__.py


+ 198 - 0
python/custom_plugins/plugins/detail_template/hongqingting/detail_hongqingting1.py

@@ -0,0 +1,198 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+import sys
+import settings
+
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["红蜻蜓",]
+template_name = "hongqingt-1"
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\hongqingting\1".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None):
+
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\hongqingting\1".format(os.getcwd())
+        print("run hongqingt-1 ")
+        self.base_bg_color = (228, 196, 147)
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+        ]
+        self.run_all()
+
+    def deal_pic_1(self):
+        bg_color = (246, 246, 246)
+        """   制作主图  """
+        goods_art_no_list = list(self.data.keys())
+
+        pp_1 = self.data[goods_art_no_list[0]]["pic_is_deal"]["侧视"]
+        pp_image = pp_1.resize(mode="relative",
+                               base="by_im",
+                               value=0,
+                               base_im=PictureProcessing("RGB", (550, 340), (248, 248, 248)),
+                               percentage=0)
+
+        pp_image = PictureProcessing("RGB", (550, 340), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = PictureProcessing(r"{}\image (1).jpg".format(self.root)).paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 263))
+        # bg_img.show()
+
+        #   ---------- 第二张图粘贴
+        pp_2 = self.data[goods_art_no_list[0]]["pic_is_deal"]["俯视"]
+        pp_image = pp_2.resize(mode="relative",
+                               base="by_im",
+                               value=0,
+                               base_im=PictureProcessing("RGB", (550, 340), (248, 248, 248)),
+                               percentage=0)
+        pp_image = PictureProcessing("RGB", (550, 340), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+        bg_img = bg_img.paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 626))
+
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = [("商品款号", self.goods_no_value["款号"], 700, 297),
+                     ("商品面料", self.goods_no_value["商品面料"], 700, 380),
+                     ("商品内里", self.goods_no_value["商品内里"], 700, 469),
+                     ("商品鞋底", self.goods_no_value["商品鞋底"], 700, 549),
+                     ("后帮高", self.goods_no_value["后帮高"], 700, 667),
+                     ("前掌宽", self.goods_no_value["前掌宽"], 700, 751),
+                     ("鞋跟高", self.goods_no_value["鞋跟高"], 700, 828), ]
+        for i in text_list:
+            bg_img = bg_img.add_text(mode="pixel",
+                                     value=(i[2], i[3]),
+                                     font=font,
+                                     text="{}:{}".format(i[0], i[1]),
+                                     align="center",
+                                     spacing=10,
+                                     fill=(17, 16, 16))
+        return bg_img
+
+    def deal_pic_2(self):
+        # 设计师说展示图
+        # 设计师说
+        if "FAB说明" not in self.goods_no_value:
+            return
+        text = self.goods_no_value["FAB说明"]  # FAB说明
+        if not text:
+            return
+        pp_1 = PictureProcessing(r"{}\image (2).jpg".format(self.root))
+
+        text = text.replace(r"\n", "\n")
+        _ = text.split("\n")
+        text_img = PictureProcessing("RGB", (1200, len(_) * 100 + 30), (255, 255, 255))
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+
+        text_img = text_img.add_text(mode="pixel",
+                                     value=(600, 50),
+                                     font=font,
+                                     text=text,
+                                     anchor="ma",
+                                     align="center",
+                                     spacing=50,
+                                     fill=(83, 83, 83))
+        new_image = PictureProcessing("RGB", (1200, pp_1.height + text_img.height), (255, 255, 255))
+        new_image = new_image.paste_img(mode="pixel", top_img=pp_1, value=(0, 0))
+        new_image = new_image.paste_img(mode="pixel", top_img=text_img, value=(0, pp_1.height))
+        return new_image
+
+    def deal_pic_3(self):
+        # 制作角度展示图
+        bg_color = (246, 246, 246)
+        detailed_images = []
+        pp_1 = PictureProcessing(r"{}\image (3).jpg".format(self.root))
+        detailed_images.append(pp_1)
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["侧视", "俯视", "后跟", "鞋底"]
+            else:
+                name_list = ["侧视", "俯视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                    continue
+
+                _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+                _pp = _pp.resize(mode="pixel", base="width", value=649 if _name == "后跟" else 1058, )
+                bg_img = PictureProcessing("RGBA", (1200, int(_pp.height + 50)), bg_color)
+                bg_img = bg_img.paste_img(mode="pixel", top_img=_pp, base="center")
+
+                detailed_images.append(bg_img)
+                # 添加分割线
+                detailed_images.append(PictureProcessing("RGB", (1200, 50), (255, 255, 255)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # 制作详细图
+        bg_color = (246, 246, 246)
+        detailed_images = []
+        pp_1 = PictureProcessing(r"{}\image (4).jpg".format(self.root))
+        detailed_images.append(pp_1)
+        name_list = ["俯视", "内里", "后跟", "鞋底"]
+        goods_art_no = list(self.data.keys())[0]
+
+        view_dict = {"俯视": (0, 0, 1100, 1200),
+                     "内里": (-1, -100, 1100, 1200),
+                     "后跟": (0, -100, 1100, 1200),
+                     "鞋底": (0, -100, 1100, 1200),
+                     }
+
+        for _name in name_list:
+            # 处理图片,需要粘贴到背景等处理
+            if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                continue
+            _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+            _pp = _pp.resize(mode="pixel", base="width", value=1300 if _name == "后跟" else 2200, )
+            _pp = _pp.crop_img(mode="pixel", base="sw", value=view_dict[_name], color_fill=bg_color)
+            _pp = _pp.radius(mode="relative", circular_pos=(1, 0, 0, 1), value=100)
+            bg_pp = PictureProcessing("RGB", (1200, 1316), (255, 255, 255))
+            bg_pp.paste_img(mode="pixel", top_img=_pp, base="ec", value=(0, 0))
+            detailed_images.append(bg_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        # 其他图片添加
+        pp_1 = PictureProcessing(r"{}\image (5).jpg".format(self.root))
+        return pp_1

+ 0 - 0
python/custom_plugins/plugins/detail_template/huilima/__init__.py


+ 245 - 0
python/custom_plugins/plugins/detail_template/huilima/detail_huilima1.py

@@ -0,0 +1,245 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+import sys
+import settings
+
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["惠利玛", ]
+template_name = "huilima-1"
+
+
+class A(object):
+    def __init__(self):
+        print("AAAAAAAAAAAAAAAA")
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\huilima\1".format(os.getcwd())
+    a = A()
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None, **kwargs):
+
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\huilima\1".format(os.getcwd())
+        print("run huilima-1 ")
+        self.base_bg_color = (228, 196, 147)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+        ]
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        img = self.add_pic(detailed_images)
+        # img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+        if img:
+            img.show()
+
+    def deal_pic_1(self):
+        bg_color = (246, 246, 246)
+        """   制作主图  """
+        detailed_images = []
+        pp_0 = PictureProcessing(r"{}\image (1).jpg".format(self.root))
+        detailed_images.append(pp_0)
+
+        goods_art_no_list = list(self.data.keys())
+
+        pp_1 = self.data[goods_art_no_list[0]]["pic_is_deal"]["俯视"]
+        pp_image = pp_1.resize(mode="relative",
+                               base="by_im",
+                               base_im=PictureProcessing("RGB", (604, 418), (248, 248, 248)))
+
+        pp_image = PictureProcessing("RGB", (604, 418), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = PictureProcessing("RGB", (1200, 918), (255, 255, 255)).paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 26))
+
+        # bg_img.show()
+
+        #   ---------- 第二张图粘贴
+        pp_2 = self.data[goods_art_no_list[0]]["pic_is_deal"]["侧视"]
+        pp_image = pp_2.resize(mode="relative",
+                               base="by_im",
+                               base_im=PictureProcessing("RGB", (604, 418), (248, 248, 248)), )
+
+        pp_image = PictureProcessing("RGB", (604, 418), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = bg_img.paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 26 + 26 + 418))
+
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = [("商品款号", self.goods_no_value["款号"], 700, 297),
+                     ("商品面料", self.goods_no_value["商品面料"], 700, 380),
+                     ("商品内里", self.goods_no_value["商品内里"], 700, 469),
+                     ("商品鞋底", self.goods_no_value["商品鞋底"], 700, 549), ]
+
+        for i in text_list:
+            bg_img = bg_img.add_text(mode="pixel",
+                                     value=(i[2], i[3]),
+                                     font=font,
+                                     text="{}:{}".format(i[0], i[1]),
+                                     align="center",
+                                     spacing=10,
+                                     fill=(17, 16, 16))
+        detailed_images.append(bg_img)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        # 尺码表
+        pp_1 = PictureProcessing(r"{}\image (2).jpg".format(self.root))
+        return pp_1
+
+    def deal_pic_3(self):
+        # 细节图展示
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        view_dict = {"俯视": {"crop_img_value": (-77, 401, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     "侧视": {"crop_img_value": (0, 0, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     }
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "鞋底", "内里"]
+            else:
+                name_list = ["俯视", "侧视", ]
+
+            # 第一个颜色
+            if "俯视" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "俯视", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1900)
+                       .crop_img(mode="pixel", base="sw", value=(-77, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "侧视" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "侧视", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1733)
+                       .crop_img(mode="pixel", base="wc", value=(715, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "鞋底" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "鞋底", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1733)
+                       .crop_img(mode="pixel", base="wc", value=(715, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "内里" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "内里", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1663)
+                       .crop_img(mode="pixel", base="wc", value=(-40, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # 制作角度展示图
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "后跟", "鞋底"]
+            else:
+                name_list = ["俯视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                    continue
+                _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+                _pp = _pp.resize(mode="pixel", base="width", value=600 if _name == "后跟" else 1058, )
+
+                bg_pp = PictureProcessing("RGB", (1200, int(_pp.height + 50)), bg_color)
+                bg_pp = bg_pp.paste_img(mode="pixel", top_img=_pp, base="cc", value=(0, 0))
+                detailed_images.append(bg_pp)
+                # 分割线
+                _p = PictureProcessing("RGB", (1200, 50), (255, 255, 255))
+                detailed_images.append(_p)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        return PictureProcessing(r"{}\image (3).jpg".format(self.root))

+ 262 - 0
python/custom_plugins/plugins/detail_template/huilima/detail_huilima2.py

@@ -0,0 +1,262 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+import sys
+import settings
+
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["全部"]
+template_name = "huilima-2"
+
+# 皮鞋模板
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\huilima\2".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None, **kwargs):
+
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\huilima\2".format(os.getcwd())
+        print("run huilima-2 ")
+        self.base_bg_color = (255, 255, 255)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+        ]
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        detailed_images.append(self.deal_pic_6())
+        img = self.add_pic(detailed_images)
+        if img:
+            self.create_folder(r"{}/{}".format(self.out_put_dir, template_name))
+            img.save(r"{}/{}/{}.jpg".format(self.out_put_dir, template_name, self.goods_no, format="JPEG"))
+            img.show()
+
+    # 斜视图展示
+    def deal_pic_1(self):
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\1.jpg".format(self.root))
+        detailed_images.append(pp_bg)
+        goods_art_no_list = list(self.data.keys())
+        # ----粘贴俯视图
+        pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                                color_name="俯视",
+                                                bg_color=self.base_bg_color)
+        pp_jpg = pp_jpg.resize(value=1098)
+        pp_jpg = pp_jpg.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 100), self.base_bg_color),
+            base="cc")
+        detailed_images.append(pp_jpg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示设计理念
+    def deal_pic_2(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\3.jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\4.jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\5.jpg".format(self.root)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 角度展示
+    def deal_pic_3(self):
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        # =====添加侧视图
+        pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                                color_name="侧视",
+                                                bg_color=self.base_bg_color)
+        pp_jpg = pp_jpg.resize(value=1098)
+        pp_jpg = pp_jpg.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 100), self.base_bg_color),
+            base="cc")
+        detailed_images.append(pp_jpg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Regular.ttf", 48)  # 颜色名称
+        # =====添加鞋面描述
+        pp_bg_2 = PictureProcessing(r"{}\7.jpg".format(self.root))
+        text = self.get_text_value("提示1主标题")
+        if text:
+            _pp = PictureProcessing().get_text_image_advanced(
+                font=font_1,
+                text=text,
+                fill=(122, 122, 122),
+                return_mode="min_image",
+            )
+            pp_bg_2 = pp_bg_2.paste_img(top_img=_pp, value=(143, 166), base="ne")
+        detailed_images.append(pp_bg_2)
+
+        pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                                color_name="俯视",
+                                                bg_color=self.base_bg_color)
+        pp_jpg = pp_jpg.transpose(mode="left_right")
+        pp_jpg = pp_jpg.resize(value=2210)
+        pp_jpg = pp_jpg.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 200), self.base_bg_color),
+            base="en",
+            value=(149, 0)
+        )
+        detailed_images.append(pp_jpg)
+        # =====添加内里图
+        pp_bg_3 = PictureProcessing(r"{}\8.jpg".format(self.root))
+        text = self.get_text_value("提示2主标题")
+        if text:
+            _pp = PictureProcessing().get_text_image_advanced(
+                font=font_1,
+                text=text,
+                fill=(122, 122, 122),
+                return_mode="min_image",
+            )
+            pp_bg_3 = pp_bg_3.paste_img(top_img=_pp, value=(143, 166), base="nw")
+        detailed_images.append(pp_bg_3)
+
+        pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                                color_name="内里",
+                                                bg_color=self.base_bg_color)
+        pp_jpg = pp_jpg.resize(value=2130)
+        pp_jpg = pp_jpg.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 100), self.base_bg_color),
+            base="wc",
+            value=(294, 0)
+        )
+        detailed_images.append(pp_jpg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示产品信息和颜色图
+    def deal_pic_4(self):
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        #  添加基础信息
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Regular.ttf", 46)  # 颜色名称
+        text_list = [
+            {"text": "产品名称", "pos": (294, 531)},
+            {"text": "产地", "pos": (1235, 531)},
+            {"text": "鞋面材质", "pos": (429, 623)},
+            {"text": "鞋底材质", "pos": (1235, 623)},
+            {"text": "设计理念", "pos": (294, 721)},
+        ]
+        pp_bg_1 = PictureProcessing(r"{}\9.jpg".format(self.root))
+        for text_data in text_list:
+            text = self.get_text_value(text_data["text"])
+            if text:
+                _pp = PictureProcessing().get_text_image_advanced(
+                    font=font_1,
+                    text=text,
+                    fill=(136, 136, 136),
+                    return_mode="min_image",
+                )
+                pp_bg_1 = pp_bg_1.paste_img(top_img=_pp, value=text_data["pos"])
+
+        detailed_images.append(pp_bg_1)
+        # =======添加各个颜色====多余高度需要剪裁
+        pp_bg_2 = PictureProcessing(r"{}\8.jpg".format(self.root))
+
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 34)  # 颜色名称
+        color_pic_list_1 = []
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                    color_name="侧视",
+                                                    bg_color=self.base_bg_color)
+            pp_jpg = pp_jpg.resize(value=390)
+
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGBA", (pp_jpg.width, pp_jpg.height + 70), (255, 255, 255, 0)),
+                base="nw"
+            )
+            text_image = pp_bg_2.get_text_image_advanced(font=font_2,
+                                                         text="{} / COLOR".format(color_name),
+                                                         fill=(0, 0, 0),
+                                                         return_mode="min_image")
+
+            pp_jpg = pp_jpg.paste_img(top_img=text_image, base="sc", value=(0, 10))
+            color_pic_list_1.append(pp_jpg)
+
+        # 颜色列表进行等分展示
+        all_color_pp = PictureProcessing().horizontal_distribution(color_pic_list_1, bg_width=1114,
+                                                                   line_spacing=10,
+                                                                   number_per_row=2)
+
+        all_color_pp = all_color_pp.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1600, all_color_pp.height + 100), self.base_bg_color),
+            base="cc",
+            value=(0, 0))
+        detailed_images.append(all_color_pp)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 产品细节展示
+    def deal_pic_5(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\12.jpg".format(self.root)))
+
+        goods_art_no_list = list(self.data.keys())
+        view_list = ["侧视", "俯视", "后跟"]
+
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            for index, view_name in enumerate(view_list):
+                pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                        color_name=view_name,
+                                                        bg_color=self.base_bg_color)
+                if view_name == "后跟":
+                    pp_jpg = pp_jpg.resize(value=600)
+                else:
+                    pp_jpg = pp_jpg.resize(value=1202)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 100), self.base_bg_color),
+                    base="cc",
+                    value=(0, 0))
+                detailed_images.append(pp_jpg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加注意事项
+    def deal_pic_6(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\15.jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\16.jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\17.jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\18.jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))

+ 357 - 0
python/custom_plugins/plugins/detail_template/huilima/detail_huilima3.py

@@ -0,0 +1,357 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+import sys
+import settings
+
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["全部"]
+template_name = "huilima-3"
+
+
+# 运动鞋详情页
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\huilima\3".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None, **kwargs):
+
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\huilima\3".format(os.getcwd())
+        print("run huilima-3 ")
+        self.base_bg_color = (255, 255, 255)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+        ]
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        img = self.add_pic(detailed_images)
+        if img:
+            self.create_folder(r"{}/{}".format(self.out_put_dir, template_name))
+            img.save(r"{}/{}/{}.jpg".format(self.out_put_dir, template_name, self.goods_no, format="JPEG"))
+            img.show()
+
+    # 使用动态拼接的方式进行实现
+    def deal_pic_1_other(self):
+        pp_bg = PictureProcessing(r"{}\2.jpg".format(self.root))
+        goods_art_no_list = list(self.data.keys())
+        # 粘贴多色图,步骤
+        # 创建一个白色底大画布,循环粘贴鞋子,先粘贴所有的阴影图,再粘贴透明图,记录最大边。
+        # 剪裁再粘贴到背景上
+
+        pp_jpg_1_list = []
+        pp_png_1_list = []
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no,
+                                                    name="俯视",
+                                                    )
+            pp_jpg_1_list.append(pp_jpg_1)
+            pp_png_1_list.append(pp_png_1)
+
+        x, y = 0, 0
+        pp_bg_jpg = PictureProcessing("RGB", (1600, 1600), (255, 255, 255))
+        if len(pp_jpg_1_list) == 1:
+            _resize_value = 1100
+        else:
+            _resize_value = 900
+
+        for index, pp_jpg in enumerate(pp_jpg_1_list):
+            pp_jpg = pp_jpg.resize(value=_resize_value + 100 * index)
+            pp_bg_jpg = pp_bg_jpg.to_overlay_pic_advance(top_img=pp_jpg,
+                                                         value=(x, y))
+            x += 180
+            y += 60
+
+        x, y = 0, 0
+        pp_bg_png = PictureProcessing("RGBA", (1600, 1600), (255, 255, 255, 0))
+        for index, pp_png in enumerate(pp_png_1_list):
+            pp_png = pp_png.resize(value=_resize_value + 100 * index)
+            pp_bg_png = pp_bg_png.paste_img(top_img=pp_png,
+                                            value=(x, y))
+            x += 180
+            y += 60
+
+        min_bbox = pp_bg_png.getbbox()
+        min_bbox = PictureProcessing().expand_bbox(min_bbox)
+        pp_bg_jpg = pp_bg_jpg.crop(bbox=min_bbox)
+        pp_bg_png = pp_bg_png.crop(bbox=min_bbox)
+
+        pp_bg = pp_bg.to_overlay_pic_advance(top_img=pp_bg_jpg, top_png_img=pp_bg_png, base="cc")
+        return pp_bg
+
+    # 斜视图展示  多色进行拼接
+    def deal_pic_1(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\1.jpg".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+        # 粘贴多色图,步骤
+        # 创建一个白色底大画布,循环粘贴鞋子,先粘贴所有的阴影图,再粘贴透明图,记录最大边。
+        # 剪裁再粘贴到背景上
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no_list[0],
+                                            name="组合",
+                                            )
+        if pp_jpg:
+            pp_bg = PictureProcessing(r"{}\2.jpg".format(self.root))
+            pp_jpg = pp_jpg.resize(value=1312)
+            pp_png = pp_png.resize(value=1312)
+            pp_bg = pp_bg.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="cc")
+        else:
+            pp_bg = self.deal_pic_1_other()
+
+        detailed_images.append(pp_bg)
+        pp_bg_2 = PictureProcessing(r"{}\3.jpg".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Regular.ttf", 60)  # 颜色名称
+        text = self.get_text_value("标题")
+        if text:
+            _pp = PictureProcessing().get_text_image_advanced(
+                font=font_1,
+                text=text,
+                fill=(255, 255, 255),
+                return_mode="min_image",
+            )
+            pp_bg_2 = pp_bg_2.paste_img(top_img=_pp, value=(0, 362), base="nc")
+        detailed_images.append(pp_bg_2)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示基础信息
+    def deal_pic_2(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\4.jpg".format(self.root)))
+        pp_bg_1 = PictureProcessing(r"{}\5.jpg".format(self.root))
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="侧视",
+                                                )
+        pp_jpg_1 = pp_jpg_1.resize(value=1312)
+        pp_png_1 = pp_png_1.resize(value=1312)
+        pp_bg_1 = pp_bg_1.to_overlay_pic_advance(top_img=pp_jpg_1, top_png_img=pp_png_1, base="cc")
+        # pp_bg_1.show()
+        detailed_images.append(pp_bg_1)
+
+        # 介绍卖点
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 60)  # 颜色名称
+        text_list = [
+            {"text": "卖点1标题", "pos": (0, 58)},
+            {"text": "卖点2标题", "pos": (0, 283)},
+            {"text": "卖点3标题", "pos": (0, 505)},
+        ]
+        pp_bg_2 = PictureProcessing(r"{}\6.jpg".format(self.root))
+        for text_data in text_list:
+            text = self.get_text_value(text_data["text"])
+            if text:
+                _pp = PictureProcessing().get_text_image_advanced(
+                    font=font_1,
+                    text=text,
+                    fill=(0, 0, 0),
+                    return_mode="min_image",
+                )
+                pp_bg_2 = pp_bg_2.paste_img(top_img=_pp, value=text_data["pos"], base="nc")
+        detailed_images.append(pp_bg_2)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 细节展示
+    def deal_pic_3(self):
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        # 展示鞋面材质  粘贴内里顶视图
+        detailed_images.append(PictureProcessing(r"{}\7.jpg".format(self.root)))
+        _, pp_png_1 = self.image_one_pic(return_orign=True,
+                                         goods_art_no=goods_art_no_list[0],
+                                         name="内里",
+                                         )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=2254)
+        pp_png_1 = pp_png_1.transpose(mode="left_right")
+        pp_png_1 = pp_png_1.rotate_advance(doge=-30)
+
+        pp_bg_1 = PictureProcessing(r"{}\8.jpg".format(self.root))
+        pp_bg_1 = pp_bg_1.paste_img(top_img=pp_png_1, value=(50, 50))
+        detailed_images.append(pp_bg_1)
+        detailed_images.append(PictureProcessing(r"{}\9.jpg".format(self.root)))
+
+        # 展示后跟
+        _, pp_png_2 = self.image_one_pic(return_orign=True,
+                                         goods_art_no=goods_art_no_list[0],
+                                         name="侧视",
+                                         )
+        pp_png_2: PictureProcessing
+        pp_png_2 = pp_png_2.resize(value=2116)
+        # pp_png_1 = pp_png_1.transpose(mode="left_right")
+        pp_png_2 = pp_png_2.rotate_advance(doge=30)
+
+        pp_bg_2 = PictureProcessing(r"{}\10.jpg".format(self.root))
+        pp_bg_2 = pp_bg_2.paste_img(top_img=pp_png_2, value=(100, 80), base="ne")
+        detailed_images.append(pp_bg_2)
+        detailed_images.append(PictureProcessing(r"{}\11.jpg".format(self.root)))
+        # 展示鞋头
+        _, pp_png_3 = self.image_one_pic(return_orign=True,
+                                         goods_art_no=goods_art_no_list[0],
+                                         name="侧视",
+                                         )
+        pp_png_3: PictureProcessing
+        pp_png_3 = pp_png_3.resize(value=2116)
+        # pp_png_1 = pp_png_1.transpose(mode="left_right")
+        pp_png_3 = pp_png_3.rotate_advance(doge=30)
+
+        pp_bg_3 = PictureProcessing(r"{}\12.jpg".format(self.root))
+        pp_bg_3 = pp_bg_3.paste_img(top_img=pp_png_3, value=(294, 125), base="sw")
+        detailed_images.append(pp_bg_3)
+        detailed_images.append(PictureProcessing(r"{}\13.jpg".format(self.root)))
+
+        # 展示鞋垫
+        _, pp_png_4 = self.image_one_pic(return_orign=True,
+                                         goods_art_no=goods_art_no_list[0],
+                                         name="内里",
+                                         )
+        pp_png_4: PictureProcessing
+        pp_png_4 = pp_png_4.resize(value=2300)
+        pp_png_4 = pp_png_4.transpose(mode="left_right")
+        pp_png_4 = pp_png_4.rotate_advance(doge=30)
+
+        pp_bg_4 = PictureProcessing(r"{}\14.jpg".format(self.root))
+        pp_bg_4 = pp_bg_4.paste_img(top_img=pp_png_4, value=(220, 89), base="ne")
+        detailed_images.append(pp_bg_4)
+        detailed_images.append(PictureProcessing(r"{}\15.jpg".format(self.root)))
+
+        # raise 1
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示产品信息和颜色图
+    def deal_pic_4(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\16.jpg".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Regular.ttf", 44)  # 颜色名称
+
+        view_data_list = ["俯视", "内里", "后跟", "鞋底"]
+
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            pp_bg_2 = PictureProcessing(r"{}\17.jpg".format(self.root))
+            # ==================
+            text_list = [
+                {"text": "商品货号", "pos": (83, 1137)},
+            ]
+            for text_data in text_list:
+                text = self.get_text_value(text_data["text"])
+                if text:
+                    _pp = PictureProcessing().get_text_image_advanced(
+                        font=font_1,
+                        text=text,
+                        fill=(136, 136, 136),
+                        return_mode="min_image",
+                    )
+                    pp_bg_2 = pp_bg_2.paste_img(top_img=_pp, value=text_data["pos"])
+
+            # ===================
+            for view_name in view_data_list:
+                pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no,
+                                                    name=view_name,
+                                                    )
+                pp_jpg: PictureProcessing
+                pp_png: PictureProcessing
+
+                if view_name == "俯视":
+                    resize_value = 723
+                    value = (418, 735)
+                if view_name == "内里":
+                    resize_value = 512
+                    value = (1243, 273)
+                if view_name == "后跟":
+                    pp_jpg = pp_jpg.resize(value=500)
+                    pp_png = pp_png.resize(value=500)
+                    pp_jpg, pp_png = self.copy_view(pp_jpg, pp_png)
+                    resize_value = 469
+                    value = (1243, 681)
+                if view_name == "鞋底":
+                    resize_value = 512
+                    value = (1243, 1111)
+
+                pp_jpg = pp_jpg.resize(value=resize_value)
+                pp_png = pp_png.resize(value=resize_value)
+                value = (value[0] - pp_jpg.width / 2, value[1] - pp_jpg.height / 2)
+                pp_bg_2 = pp_bg_2.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="nw", value=value)
+
+            detailed_images.append(pp_bg_2)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加注意事项
+    def deal_pic_5(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\19.jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\20.jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def copy_view(self, pp_jpg: PictureProcessing, pp_png: PictureProcessing):
+        pp_bg_jpg = PictureProcessing("RGB", (1600, 1600), (255, 255, 255))
+        pp_bg_png = PictureProcessing("RGBA", (1600, 1600), (255, 255, 255, 0))
+
+        pp_jpg_1_list = [pp_jpg, pp_jpg]
+        pp_png_1_list = [pp_png, pp_png]
+
+        x, y = 0, 0
+        for index, pp_jpg in enumerate(pp_jpg_1_list):
+            pp_bg_jpg = pp_bg_jpg.to_overlay_pic_advance(top_img=pp_jpg,
+                                                         value=(x, y))
+            x += pp_jpg.width - 20
+
+        x, y = 0, 0
+        for index, pp_png in enumerate(pp_png_1_list):
+            pp_bg_png = pp_bg_png.paste_img(top_img=pp_png,
+                                            value=(x, y))
+            x += pp_jpg.width - 20
+
+        min_bbox = pp_bg_png.getbbox()
+        min_bbox = PictureProcessing().expand_bbox(min_bbox, value=50)
+        pp_bg_jpg = pp_bg_jpg.crop(bbox=min_bbox)
+        pp_bg_png = pp_bg_png.crop(bbox=min_bbox)
+        return pp_bg_jpg, pp_bg_png

+ 304 - 0
python/custom_plugins/plugins/detail_template/huilima/detail_huilima4.py

@@ -0,0 +1,304 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+import sys
+import settings
+
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["全部"]
+template_name = "huilima-4"
+
+
+# 乐福鞋 详情页
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\huilima\4".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None, output_queue=None, **kwargs):
+
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list, output_queue=output_queue)
+
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\huilima\4".format(os.getcwd())
+        print("run huilima-1 ")
+        self.base_bg_color = (255, 255, 255)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+        ]
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        detailed_images.append(self.deal_pic_6())
+        img = self.add_pic(detailed_images)
+        if img:
+            self.create_folder(r"{}/{}".format(self.out_put_dir, template_name))
+            img.save(r"{}/{}/{}.jpg".format(self.out_put_dir, template_name, self.goods_no, format="JPEG"))
+            img.show()
+
+    #
+    def deal_pic_1(self):
+        detailed_images = []
+        pp_bg_1 = PictureProcessing(r"{}\1.jpg".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 120)
+        text_list = [
+            {"text": "标题", "pos": (89, 224)},
+            {"text": "副标题", "pos": (89, 345)},
+        ]
+        for text_data in text_list:
+            text = self.get_text_value(text_data["text"])
+            if text:
+                _pp = PictureProcessing().get_text_image_advanced(
+                    font=font_1,
+                    text=text,
+                    fill=(0, 0, 0),
+                    return_mode="min_image",
+                )
+                pp_bg_1 = pp_bg_1.paste_img(top_img=_pp, value=text_data["pos"], base="nw")
+        detailed_images.append(pp_bg_1)
+
+        # 粘贴组合图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no_list[0],
+                                            name="组合",
+                                            )
+        if not pp_jpg:
+            pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="俯视",
+                                                )
+        pp_jpg: PictureProcessing
+        pp_png: PictureProcessing
+        pp_jpg = pp_jpg.resize(value=1405)
+        pp_png = pp_png.resize(value=1405)
+        pp_bg_2 = PictureProcessing(r"{}\2.jpg".format(self.root))
+        pp_bg_2 = pp_bg_2.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="cc")
+
+        detailed_images.append(pp_bg_2)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示基础信息
+    def deal_pic_2(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\3.jpg".format(self.root)))
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+
+        pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                                color_name="侧视",
+                                                bg_color=self.base_bg_color)
+        pp_jpg = pp_jpg.resize(value=1098)
+        pp_jpg = pp_jpg.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 100), self.base_bg_color),
+            base="cc")
+        detailed_images.append(pp_jpg)
+        # 粘贴基础信息
+        pp_bg_2 = PictureProcessing(r"{}\5.jpg".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Regular.ttf", 62)
+        text_list = [
+            {"text": "鞋面材质", "pos": (767, 176)},
+            {"text": "内里材质", "pos": (1213, 176)},
+            {"text": "鞋底材质", "pos": (767, 345)},
+            {"text": "鞋垫材质", "pos": (1213, 345)},
+            {"text": "跟高", "pos": (767, 616)},
+            {"text": "前底厚度", "pos": (1196, 616)},
+        ]
+        for text_data in text_list:
+            text = self.get_text_value(text_data["text"])
+            if text:
+                _pp = PictureProcessing().get_text_image_advanced(
+                    font=font_1,
+                    text=text,
+                    fill=(0, 0, 0),
+                    return_mode="min_image",
+                )
+                pp_bg_2 = pp_bg_2.paste_img(top_img=_pp, value=text_data["pos"], base="nw")
+        detailed_images.append(pp_bg_2)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 颜色展示
+    def deal_pic_3(self):
+        detailed_images = []
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 34)  # 颜色名称
+        color_pic_list_1 = []
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                    color_name="侧视",
+                                                    bg_color=self.base_bg_color)
+            pp_jpg = pp_jpg.resize(value=390)
+
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGBA", (pp_jpg.width, pp_jpg.height + 70), (255, 255, 255, 0)),
+                base="nw"
+            )
+            text_image = PictureProcessing().get_text_image_advanced(font=font_1,
+                                                                     text="{} / COLOR".format(color_name),
+                                                                     fill=(0, 0, 0),
+                                                                     return_mode="min_image")
+
+            pp_jpg = pp_jpg.paste_img(top_img=text_image, base="sc", value=(0, 10))
+            color_pic_list_1.append(pp_jpg)
+
+        # 颜色列表进行等分展示
+        all_color_pp = PictureProcessing().horizontal_distribution(color_pic_list_1, bg_width=1114,
+                                                                   line_spacing=10,
+                                                                   number_per_row=2)
+        all_color_pp = all_color_pp.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1600, all_color_pp.height + 100), self.base_bg_color),
+            base="cc",
+            value=(0, 0))
+        detailed_images.append(all_color_pp)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示细节图
+    def deal_pic_4(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\8.jpg".format(self.root)))
+
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 85)  # 颜色名称
+
+        view_data_list = ["俯视", "俯视", "侧视"]
+        text_list = ["提示1主标题", "提示2主标题", "提示3主标题"]
+        bg_name_list = ["10.jpg", "12.jpg", "14.jpg"]
+        for index, view_name in enumerate(view_data_list):
+            pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                    color_name=view_name,
+                                                    bg_color=(246, 246, 246))
+            if index == 0:
+                resize_value = 3200
+                paste_base = "cc"
+                paste_value = (0, 200)
+            if index == 1:
+                resize_value = 3200
+                paste_base = "ne"
+                paste_value = (-100, 0)
+            if index == 2:
+                pp_jpg = pp_jpg.transpose(mode="left_right")
+                resize_value = 3513
+                paste_base = "sw"
+                paste_value = (0, 100)
+
+            pp_jpg = pp_jpg.resize(value=resize_value)
+            pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB", (1500, 1500), (246, 246, 246)),
+                                             base=paste_base,
+                                             value=paste_value
+                                             )
+            pp_jpg = pp_jpg.radius(value=100)
+            pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB", (1600, 1600), (255, 255, 255)),
+                                             base="cc"
+                                             )
+            detailed_images.append(pp_jpg)
+            # 添加文字
+            text = self.get_text_value(text_list[index])
+            if text:
+                pp_text_bg = PictureProcessing(r"{}\{}".format(self.root, bg_name_list[index]))
+                _pp = PictureProcessing().get_text_image_advanced(
+                    font=font_1,
+                    text=text,
+                    fill=(0, 0, 0),
+                    return_mode="min_image",
+                )
+                pp_text_bg = pp_text_bg.paste_img(top_img=_pp, value=(73, 0), base="wc")
+                detailed_images.append(pp_text_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 场景图--产品展示
+    def deal_pic_5(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\15.jpg".format(self.root)))
+
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+
+        _flag = True
+        pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                color_name="组合1",
+                                                bg_color=(255, 255, 255))
+        if pp_jpg:
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 200), (255, 255, 255)),
+                base="cc",
+            )
+            detailed_images.append(pp_jpg)
+        else:
+            _flag = False
+
+        pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                color_name="组合2",
+                                                bg_color=(255, 255, 255))
+        if pp_jpg:
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 200), (255, 255, 255)),
+                base="cc",
+            )
+            detailed_images.append(pp_jpg)
+        else:
+            _flag = False
+
+        if not _flag:
+            detailed_images = []
+            detailed_images.append(PictureProcessing(r"{}\15.jpg".format(self.root)))
+            for goods_art_no_dict in self.goods_no_value["货号资料"]:
+                color_name = goods_art_no_dict["颜色名称"]
+                goods_art_no = goods_art_no_dict["货号"]
+                pp_jpg = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                        color_name="侧视",
+                                                        bg_color=self.base_bg_color)
+                pp_jpg = pp_jpg.resize(value=1404)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1600, pp_jpg.height + 200), (255, 255, 255)),
+                    base="cc",
+                )
+                detailed_images.append(pp_jpg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加注意事项
+    def deal_pic_6(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\20.jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\21.jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))

+ 0 - 0
python/custom_plugins/plugins/detail_template/xiaosushuoxie/__init__.py


+ 337 - 0
python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie1.py

@@ -0,0 +1,337 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+from plugins_mode.detail_generate_base import DetailBase
+from plugins_mode.pic_deal import PictureProcessing
+
+
+plugins_name = "详情模板"
+company_name_list = ["小苏",]
+template_name = "xiaosushuoxie-1"
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\1".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\1".format(os.getcwd())
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+        ]
+
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+
+    def deal_pic_1(self):
+        bg_color = (239, 237, 238)
+        """   制作主图  """
+        detailed_images = []
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = []
+        for _set in [("【品    牌】", "品牌"),
+                     ("【货    号】", "款号"),
+                     ("【鞋面材质】", "鞋面材质"),
+                     ("【系列风格】", "系列风格"),
+                     ("【内里材质】", "内里材质"),
+                     ("【尺    码】", "尺码"),
+                     ("【鞋底材质】", "鞋底材质"),
+                     ("【品牌编码】", "品牌编码"),
+                     ]:
+            title, key = _set
+            text = self.get_text_value(key)
+            if text:
+                text_list.append(
+                    (title, text)
+                )
+
+        # 文字排列
+        pp_bg = PictureProcessing(r"{}\bg-1.jpg".format(self.root))
+        x = 101
+        y = 98
+        for _set in text_list:
+            pp_bg = pp_bg.add_text(mode="pixel",
+                                   value=(x, y),
+                                   font=font,
+                                   text="{}:{}".format(_set[0], _set[1]),
+                                   align="left",
+                                   spacing=10,
+                                   fill=(17, 16, 16))
+            x += 588
+            if x > 700:
+                x = 101
+                y += 94
+
+        # ----------主图放置
+        y += 10
+        goods_art_no_list = list(self.data.keys())
+
+        pp_jpg, pp_png = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="俯视", return_orign=True)
+        pp_jpg = pp_jpg.resize(value=1083)
+        pp_png = pp_png.resize(value=1083)
+        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="nc", value=(0, y), top_png_img=pp_png)
+
+        y += pp_jpg.height
+        y += 20
+
+        # ----------配图粘贴
+        if len(goods_art_no_list) > 1:
+            # 大于一个颜色做颜色图展示
+
+            pp_list_1, pp_list_2 = [], []
+            for goods_art_no in goods_art_no_list:
+                pp_jpg, pp_png = self.image_one_pic(goods_art_no=goods_art_no, name="侧视", return_orign=True)
+                pp_jpg = pp_jpg.resize(value=400)
+                pp_png = pp_png.resize(value=400)
+                pp_list_1.append(pp_jpg)
+                pp_list_2.append(pp_png)
+
+            _pp_jpg = PictureProcessing()
+            _pp_png = PictureProcessing()
+            _pp_jpg = _pp_jpg.horizontal_distribution(pp_list=pp_list_1, bg_width=1200, margins=(0, 0, 0, 0),
+                                                      line_spacing=0,
+                                                      number_per_row=3)
+            _pp_png = _pp_png.horizontal_distribution(pp_list=pp_list_2, bg_width=1200, margins=(0, 0, 0, 0),
+                                                      line_spacing=0,
+                                                      number_per_row=3)
+
+            pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=_pp_jpg, base="nc", value=(0, y),
+                                                 top_png_img=_pp_png)
+
+            y += _pp_jpg.height
+            y += 30
+
+        # ------多余的剪裁
+        if y < pp_bg.height:
+            # print("触发剪裁")
+            pp_bg = pp_bg.crop_img(value=(0, 0, pp_bg.width, y))
+
+        return pp_bg
+
+    def deal_pic_2(self):
+        """
+        细节解析
+
+        """
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        font_2 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 15)
+        bg_color = (220, 220, 220)
+        detailed_images = []
+
+        goods_art_no_list = list(self.data.keys())
+        # ------侧视图-------------
+        pp_2 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="侧视", bg_color=bg_color)
+        pp_2 = pp_2.resize(mode="pixel", base="width", value=1327)
+        pp_2 = PictureProcessing("RGB", (550, 550), bg_color).paste_img(mode="pixel", top_img=pp_2,
+                                                                        base="wc", value=(-90, 0))
+        # 文字
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 24),
+                             font=font_1,
+                             text="优质质感鞋面",
+                             align="left",
+                             spacing=10,
+                             fill=(28, 28, 28))
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 76),
+                             font=font_2,
+                             text="颇具层次",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 115),
+                             font=font_2,
+                             text="时尚质感出众",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        # ------后跟-------------
+        pp_3 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="后跟", bg_color=bg_color)
+        pp_3 = pp_3.resize(mode="pixel", base="width", value=625)
+        pp_3 = PictureProcessing("RGB", (550, 550), bg_color).paste_img(mode="pixel", top_img=pp_3,
+                                                                        base="cc", value=(0, 0))
+        # 文字
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35 * 3),
+                             font=font_2,
+                             text="颇具层次",
+                             align="right",
+                             spacing=10,
+                             anchor="rs",
+                             fill=(78, 78, 78))
+
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35 * 2),
+                             font=font_2,
+                             text="时尚质感出众",
+                             align="right",
+                             anchor="rs",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35),
+                             font=font_1,
+                             text="设计感十足",
+                             anchor="rs",
+                             align="right",
+                             spacing=10,
+                             fill=(28, 28, 28))
+
+        # ---------------鞋底图
+        pp_4 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="鞋底", bg_color=bg_color)
+        pp_4 = pp_4.resize(mode="pixel", base="width", value=1635)
+        pp_4 = PictureProcessing("RGB", (1134, 625), bg_color).paste_img(mode="pixel", top_img=pp_4,
+                                                                         base="wc", value=(198, 0))
+        # 文字
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 451),
+                             font=font_1,
+                             text="防滑耐磨鞋底",
+                             align="left",
+                             spacing=10,
+                             fill=(28, 28, 28))
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 510),
+                             font=font_2,
+                             text="优质材质",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 549),
+                             font=font_2,
+                             text="防滑网纹设计坚固耐磨",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        new_bg = PictureProcessing("RGB", (1200, 40 + pp_2.height + 40 + pp_4.height + 40), (239, 239, 239))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_2, base="nw", value=(33, 40))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_3, base="nw", value=(33 + 550 + 33, 40))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_4, base="nw", value=(33, 40 + pp_2.height + 40))
+        detailed_images.append(new_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        # 设计理念
+        bg = PictureProcessing(r"{}\1 (1).jpg".format(self.root))
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 40)
+        # 粘贴文字
+        text = self.get_text_value("设计理念")
+        if not text:
+            text = "告别枯燥沉闷日常还原自然本身色彩\n融合工装、复古、运动元素随性出走感受自在的步伐"
+
+        bg = bg.add_text(mode="pixel",
+                         value=(164, 328),
+                         font=font_1,
+                         text=text,
+                         align="left",
+                         spacing=15,
+                         fill=(39, 39, 39))
+
+        return bg
+
+    def deal_pic_4(self):
+        """   各个颜色细节展示  """
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            # ------俯视图-------------
+            pp_1 = self.image_one_pic(goods_art_no=goods_art_no, name="俯视", bg_color=bg_color)
+            pp_1 = pp_1.resize(mode="pixel", base="width", value=956)
+            pp_1 = PictureProcessing("RGB", (1200, 40 + pp_1.height + 40), bg_color).paste_img(mode="pixel",
+                                                                                               top_img=pp_1,
+                                                                                               base="cc", value=(0, 0))
+            # 粘贴文字与logo
+            pp_1 = pp_1.add_text(mode="pixel",
+                                 value=(51, 26),
+                                 font=font_1,
+                                 text=color_name,
+                                 align="left",
+                                 spacing=10,
+                                 fill=(28, 28, 28))
+            pp_1 = pp_1.paste_img(mode="pixel", top_img=PictureProcessing(
+                r"{}\1 (4).jpg".format(self.root)), base="nw", value=(51, 72))
+
+            # ------后跟-------------
+            pp_2 = self.image_one_pic(goods_art_no=goods_art_no, name="后跟", bg_color=bg_color)
+            pp_2 = pp_2.resize(mode="pixel", base="width", value=345)
+            pp_2 = PictureProcessing("RGB", (537, 356), bg_color).paste_img(mode="pixel", top_img=pp_2,
+                                                                            base="cc", value=(0, 0))
+            # ------内里-------------
+            pp_3 = self.image_one_pic(goods_art_no=goods_art_no, name="内里", bg_color=bg_color)
+            pp_3 = pp_3.resize(mode="pixel", base="width", value=577)
+            pp_3 = PictureProcessing("RGB", (537, 356), bg_color).paste_img(mode="pixel", top_img=pp_3,
+                                                                            base="cc", value=(0, 0))
+            bg_pp = PictureProcessing("RGB", (1200, 20 + pp_1.height + 20 + pp_2.height + 20), bg_color)
+            # 粘贴俯视图
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_1, base="nw", value=(0, 0))
+            # 粘贴后跟
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_2, base="nw", value=(51, 20 + pp_1.height + 20))
+            # 粘贴内里
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_3, base="nw", value=(588, 20 + pp_1.height + 20))
+
+            detailed_images.append(bg_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        # 其他图片
+        detailed_images = []
+        detailed_images.append(
+            PictureProcessing(r"{}\1 (2-1).jpg".format(self.root)))
+        detailed_images.append(
+            PictureProcessing(r"{}\1 (3).jpg".format(self.root)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+
+if __name__ == '__main__':
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data3.txt",
+            "r", encoding="utf-8") as f:
+        data = json.loads(f.read())
+
+    for goods_no, value in data.items():
+        d = DetailPicGetXiaoSuShuoXie(goods_no, value,
+                                      out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-11-19\软件-详情图生成")
+        raise 1

+ 626 - 0
python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie2.py

@@ -0,0 +1,626 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+
+import os
+from PIL import ImageFont, Image, ImageDraw
+import settings
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+
+plugins_name = "详情模板"
+company_name_list = ["小苏",]
+template_name = "xiaosushuoxie-2"
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\2".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\2".format(os.getcwd())
+        self.base_bg_color = (235, 234, 234)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+        ]
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        detailed_images.append(self.deal_pic_6())
+        detailed_images.append(self.deal_pic_7())
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+
+    def deal_pic_1(self):
+        """   制作主图  """
+        detailed_images = []
+        # -------粘贴文字-------
+        pp_bg = PictureProcessing(r"{}\template_1.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 35)
+        # font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 35)
+
+        t1_title = self.get_text_value("标题")
+        t1_sub_title = self.get_text_value("副标题")
+        # t1_sub_title = "副标题副标题副标题副标题副标题\n副标题副标题副标题副标题\n副标题副标题副标题"
+        base_bg_color = (236, 235, 235)
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), base_bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(50, 10),
+                font=font_1,
+                text=t1_title,
+                align="left",
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(0, 0, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), base_bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(50, 10),
+                font=font_2,
+                text=t1_sub_title,
+                align="left",
+                spacing=10,
+                fill=(32, 32, 32),
+                return_mode="min_image_high",
+                margins=(0, 0, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB", (pp_bg.width, 150), self.base_bg_color))
+
+        # ================添加图片
+        goods_art_no_list = list(self.data.keys())
+        _zuhe_pic_list = []
+        color_name_list = ["组合", "组合2", "组合3"]
+        for color_name in color_name_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color,
+            )
+            if not pp_1:
+                continue
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        if not _zuhe_pic_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name="俯视",
+                bg_color=self.base_bg_color,
+            )
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        detailed_images.extend(_zuhe_pic_list)
+
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB", (pp_bg.width, 150), self.base_bg_color))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        """
+        细节解析
+
+        """
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_2.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 30)
+
+        t1_title = self.get_text_value("提示1主标题")
+        t1_sub_title = self.get_text_value("提示1副标题")
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), (249, 249, 249))
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(1200 - 80, 45),
+                font=font_1,
+                text=t1_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), (249, 249, 249))
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(1200 - 80, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        # ----------主图放置
+        goods_art_no_list = list(self.data.keys())
+        # ---------------鞋底图
+        bg_color = (249, 249, 249)
+        pp_jpg = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="鞋底",
+            bg_color=bg_color
+        )
+        pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_jpg.height + 100), bg_color),
+                                         base="nc",
+                                         value=(400, 00)
+                                         )
+        detailed_images.append(pp_jpg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_4.png".format(self.root))
+        detailed_images.append(pp_bg)
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 30)
+
+        t1_title = self.get_text_value("提示2主标题")
+        t1_sub_title = self.get_text_value("提示2副标题")
+        bg_color = (252, 252, 252)
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(80, 45),
+                font=font_1,
+                text=t1_title,
+                align="left",
+                anchor=None,
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(80, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="left",
+                anchor=None,
+                spacing=10,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        # ----------主图放置
+        goods_art_no_list = list(self.data.keys())
+        # ---------------鞋底图
+
+        pp_jpg = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="侧视",
+            bg_color=bg_color
+        )
+        pp_jpg = pp_jpg.resize(value=1686)
+        pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_jpg.height), bg_color),
+                                         base="ec",
+                                         value=(0, 0)
+                                         )
+        # pp_jpg = pp_jpg.resize(value=1000)
+        # pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB",(1200,pp_jpg.height+150),bg_color),
+        #                                  base="wc",
+        #                                  value=(0,0)
+        #                                  )
+        detailed_images.append(pp_jpg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        """各个颜色细节展示"""
+        bg_color = (235, 235, 235)
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_5.png".format(self.root))
+        detailed_images.append(pp_bg)
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 30)
+
+        t1_title = self.get_text_value("提示3主标题")
+        t1_sub_title = self.get_text_value("提示3副标题")
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(1200 - 80, 45),
+                font=font_1,
+                text=t1_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(1200 - 80, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        goods_art_no_list = list(self.data.keys())
+        # ------侧视图-------------
+        pp_2 = self.image_one_pic(
+            goods_art_no=goods_art_no_list[0], name="侧视", bg_color=bg_color
+        )
+        pp_2 = pp_2.resize(mode="pixel", base="width", value=2327)
+        pp_2 = PictureProcessing("RGBA", (200, 300), bg_color).paste_img(
+            mode="pixel", top_img=pp_2, base="wc", value=(-900, 0)
+        )
+
+        # ------侧视-------------
+        pp_3 = self.image_one_pic(
+            goods_art_no=goods_art_no_list[0], name="侧视", bg_color=bg_color
+        )
+        pp_3 = pp_3.resize(mode="pixel", base="width", value=3000)
+        pp_3 = PictureProcessing("RGBA", (200, 300), bg_color).paste_img(
+            mode="pixel", top_img=pp_3, base="cc", value=(-500, 0)
+        )
+        # ---------------侧视图---------------旋转
+        _, pp_4 = self.image_one_pic(
+            goods_art_no=goods_art_no_list[0],
+            name="侧视",
+            bg_color=bg_color,
+            return_orign=True,
+        )
+        pp_4 = pp_4.resize(mode="pixel", base="width", value=1163)
+        pp_4_max = pp_4.width if pp_4.width > pp_4.height else pp_4.height
+        past_y = 0
+        top_img_bg = PictureProcessing("RGBA", (pp_bg.width, pp_2.height), bg_color)
+        top_img_bg = top_img_bg.paste_img(
+            mode="pixel", top_img=pp_2, base="wc", value=(50, past_y)
+        )
+        top_img_bg = top_img_bg.paste_img(
+            mode="pixel", top_img=pp_3, base="wc", value=(267, past_y)
+        )
+        new_pp4_bg = PictureProcessing(
+            "RGBA", (pp_4_max, pp_4_max + 100), (239, 239, 239, 0)
+        )
+        pp_4 = PictureProcessing("RGBA", (pp_4.width, pp_4.height), bg_color).paste_img(
+            mode="pixel", top_img=pp_4, base="nw", value=(0, 0)
+        )
+        new_pp4_bg.paste_img(mode="pixel", top_img=pp_4, base="wc", value=(0, 0))
+        new_pp4_bg = new_pp4_bg.rotate(30)
+        pp4_im = new_pp4_bg.get_im()
+        new_pp4_im = pp4_im.crop(pp4_im.getbbox())
+        new_pp5_bg = PictureProcessing(
+            "RGBA", (pp_bg.width, new_pp4_im.height - 200), bg_color
+        )
+        new_pp5_bg = new_pp5_bg.paste_img(
+            mode="pixel", top_img=new_pp4_bg, base="cc", value=(-50, 0)
+        )
+        new_empty_bg = PictureProcessing("RGBA", (pp_bg.width, 50), bg_color)
+        detailed_images.append(top_img_bg)
+        detailed_images.append(new_pp5_bg)
+        detailed_images.append(new_empty_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+
+        # =============文字=================
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\en\Black.otf", 65)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 65)
+        t1_title = "INFORMATION"
+        t1_sub_title = "产品信息"
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(600, 45),
+                font=font_1,
+                text=t1_title,
+                align="center",
+                anchor="mm",
+                spacing=10,
+                fill=(16, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(600, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="center",
+                anchor="mm",
+                spacing=10,
+                fill=(16, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        goods_art_no_list = list(self.data.keys())
+        product_img = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="俯视",
+            bg_color=bg_color
+        )
+        product_img = product_img.resize(value=1200 / 1.75)
+        product_img = product_img.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1200, product_img.height), bg_color),
+            base="cc",
+        )
+
+        text_font = ImageFont.truetype(r"resources\ttf\puhui\Regular.otf", 30)
+        tips_font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        h = 280 if product_img.height < 312 else 312
+        paramsList = [("帮高", (140, product_img.height - h)), ("跟高", (1200 - 140, product_img.height - h)),
+                      ("鞋宽", (1200 - 140, product_img.height - 87))]
+        tipsList = [("帮高", (140, product_img.height - h - 30)), ("跟高", (1200 - 140, product_img.height - h - 30)),
+                    ("鞋宽", (1200 - 140, product_img.height - 87 - 30))]
+
+        for index, item in enumerate(paramsList):
+            text = "{}".format(self.get_text_value(item[0]))
+            if not text:
+                continue
+
+            product_img = product_img.add_text(
+                mode="pixel",
+                value=item[1],
+                font=text_font,
+                anchor="mm",
+                text=text,
+                align="center",
+                spacing=10,
+                fill=(143, 143, 143),
+            )
+
+            product_img = product_img.add_text(
+                mode="pixel",
+                value=tipsList[index][1],
+                font=tips_font,
+                anchor="mm",
+                text="{}".format(tipsList[index][0]),
+                align="center",
+                spacing=15,
+                fill=(143, 143, 143),
+            )
+
+        detailed_images.append(product_img)
+
+        # ===========================================================================================================d
+        pp_bg = PictureProcessing(r"{}\template_6.png".format(self.root))
+        tableList = [
+            ("鞋面材质", (300, 72)),
+            ("内里材质", (760, 72)),
+            ("鞋垫材质", (300, 72 + 105)),
+            ("鞋底材质", (760, 72 + 105)),
+        ]
+        table_font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        spacing = 15
+        text_arrays = []
+        for i, tableItem in enumerate(tableList):
+            item_text = self.get_text_value(tableItem[0])
+            if not item_text:
+                continue
+            text_arrays.append("{} : {}".format(tableItem[0], item_text))
+
+        for index, item in enumerate(text_arrays):
+            row = index // 2 + 1  # 计算行号(从1开始计数)
+            col = (index % 2) + 1  # 计算列号(从1开始计数)
+            if col == 1:
+                c_x = 102
+            else:
+                c_x = 635
+            if row == 1:
+                c_y = 40
+            else:
+                c_y = 40 + 105
+            pp_bg = pp_bg.add_text(
+                mode="pixel",
+                value=(c_x, c_y),
+                font=table_font,
+                text=item,
+                align="left",
+                spacing=spacing,
+                fill=(80, 80, 80),
+            )
+        detailed_images.append(pp_bg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def searchNotEmptyItem(self, current_idx, tableList):
+        idx = current_idx - 1
+        if idx < 0:
+            idx = 0
+        value = self.get_text_value(tableList[idx][0])
+        if idx == 0:
+            return idx
+        if value == "":
+            return self.searchNotEmptyItem(idx, tableList)
+        else:
+            return idx + 1
+
+    def deal_pic_6(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_7.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        # ==========添加颜色===================
+        pp_list_1 = []
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 25)
+        goods_art_no_list = list(self.data.keys())
+
+        all_color_name = []
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="侧视",
+                bg_color=bg_color,
+            )
+            if pp_jpg is None:
+                continue
+            pp_jpg = pp_jpg.resize(value=440)
+            color_name = self.goods_no_value["货号资料"][index]["颜色名称"]
+            all_color_name.append(color_name)
+            text_bg = PictureProcessing("RGB", (440, 200), bg_color)
+            text_bg = text_bg.get_text_image_advanced(
+                value=(220, 10),
+                font=font,
+                text="{}".format(color_name),
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 5, 0, 0)
+            )
+            # text_bg.show()
+            _bg = PictureProcessing("RGB", (440, pp_jpg.height + text_bg.height), bg_color)
+            _bg = _bg.paste_img(top_img=pp_jpg)
+            _bg = _bg.paste_img(top_img=text_bg, value=(0, pp_jpg.height))
+            pp_list_1.append(_bg)
+
+        rows = 2
+        shoes_bg = PictureProcessing().horizontal_distribution(
+            pp_list=pp_list_1,
+            bg_width=1200,
+            margins=(0, 0, 40, 40),
+            line_spacing=60,
+            number_per_row=rows,
+        )
+        detailed_images.append(shoes_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def get_font_render_size(self, text, font):
+        canvas = Image.new("RGB", (2048, 2048))
+        draw = ImageDraw.Draw(canvas)
+        draw.text((0, 0), text, font=font, fill=(55, 55, 55))
+        bbox = canvas.getbbox()
+        # 宽高
+        size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
+        return size
+
+    def deal_pic_7(self):
+        detailed_images = []
+
+        bg_color = (255, 255, 255)
+        goods_art_no_list = list(self.data.keys())
+        pp_bg = PictureProcessing(r"{}\template_8.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        color_name_1 = ["俯视", "侧视", "内里", "后跟", "鞋底"]
+        color_name_2 = ["俯视", "鞋底"]
+
+        color_name_1 = ["俯视", "侧视", "鞋底"]
+        color_name_2 = ["俯视", "内里", "后跟"]
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index in [0, 2, 4, 6, 8]:
+                color_name = color_name_1
+            else:
+                color_name = color_name_2
+            for name in color_name:
+                pp_jpg = self.get_overlay_pic_from_dict(
+                    goods_art_no=goods_art_no,
+                    color_name=name,
+                    bg_color=bg_color
+                )
+                if pp_jpg:
+                    if name != "后跟":
+                        pp_jpg = pp_jpg.resize(value=int(pp_bg.width / 1.2))
+                    else:
+                        pp_jpg = pp_jpg.resize(value=int(pp_bg.width / 2.2))
+
+                    pp_jpg = pp_jpg.paste_img_invert(
+                        top_img=PictureProcessing("RGB", (pp_bg.width, pp_jpg.height + 30), bg_color),
+                        base="cc")
+                    detailed_images.append(pp_jpg)
+                    detailed_images.append(PictureProcessing("RGB", (pp_bg.width, 220), bg_color))
+
+        # return PictureProcessing(im=self.add_pic(detailed_images))
+        return self.pp_pic_subsection(PictureProcessing(im=self.add_pic(detailed_images)))
+
+
+if __name__ == "__main__":
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data3.txt",
+            "r",
+            encoding="utf-8",
+    ) as f:
+        data = json.loads(f.read())
+
+    for goods_no, value in data.items():
+        d = DetailPicGetXiaoSuShuoXie2(
+            goods_no,
+            value,
+            out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-11-19\软件-详情图生成",
+        )
+        raise 1

+ 482 - 0
python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie3.py

@@ -0,0 +1,482 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+import settings
+
+from PIL import ImageFont
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+from PIL import Image, ImageDraw
+
+
+plugins_name = "详情模板"
+company_name_list = ["小苏"]
+template_name = "xiaosushuoxie-3"
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\3".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False,excel_data=None,assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows,excel_data=excel_data,assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\3".format(os.getcwd())
+        self.template_name = template_name
+        self.base_bg_color = (236, 226, 211)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+        ]
+
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir,self.goods_no, format="JPEG"))
+
+    def deal_pic_1(self):
+        """   制作主图  """
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_1.png".format(self.root))
+        detailed_images.append(pp_bg)
+        # -------粘贴文字-------
+        mainTitle = self.get_text_value("标题")
+        subTitle = self.get_text_value("副标题")
+
+        if mainTitle:
+            font = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 70)
+            text_1_bg = PictureProcessing("RGB", (1200, 500), self.base_bg_color)
+            text_1_bg = text_1_bg.get_text_image_advanced(
+                value=(600, 30),
+                font=font,
+                text=mainTitle,
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            # text_1_bg.show()
+            detailed_images.append(text_1_bg)
+
+        if subTitle:
+            font = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 40)
+            text_2_bg = PictureProcessing("RGB", (1200, 500), self.base_bg_color)
+            text_2_bg = text_2_bg.get_text_image_advanced(
+                value=(600, 30),
+                font=font,
+                text=subTitle,
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(text_2_bg)
+
+
+        # ====粘贴组合图以及其他图
+        goods_art_no_list = list(self.data.keys())
+        # 文字排列
+        _zuhe_pic_list = []
+        color_name_list = ["组合", "组合2", "组合3"]
+        for color_name in color_name_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color,
+            )
+            if not pp_1:
+                continue
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        if not _zuhe_pic_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name="俯视",
+                bg_color=self.base_bg_color,
+            )
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                                         base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        detailed_images.extend(_zuhe_pic_list)
+        detailed_images.append(PictureProcessing(r"{}\template_1_1.png".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        """
+        细节解析
+        """
+        # 文字排列
+        detailed_images = []
+        pp_bg = PictureProcessing(
+            r"{}\template_2.png".format(
+                self.root
+            )
+        )
+        detailed_images.append(pp_bg)
+
+
+        goods_art_no_list = list(self.data.keys())
+
+        pp_1 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="侧视",
+            bg_color=self.base_bg_color,
+        )
+        pp_1= pp_1.resize(value=pp_bg.width / 1.4)
+        pp_1= pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 40), self.base_bg_color),
+                                    base="cc"
+                                    )
+        detailed_images.append(pp_1)
+
+        pp_bg_bottom = PictureProcessing(r"{}\template_2_1.png".format(self.root))
+        detailed_images.append(pp_bg_bottom)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        """   各个颜色细节展示  """
+        detailed_images = []
+        pp_bg = PictureProcessing(
+            r"{}\template_3.png".format(
+                self.root
+            )
+        )
+        detailed_images.append(pp_bg)
+
+
+        # ----------主图放置
+        y = 120
+        pp_list_1 = []
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 25)
+        goods_art_no_list = list(self.data.keys())
+
+        all_color_name = []
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="侧视",
+                bg_color=self.base_bg_color,
+            )
+            if pp_jpg is None:
+                continue
+            pp_jpg = pp_jpg.resize(value=440)
+            color_name = self.goods_no_value["货号资料"][index]["颜色名称"]
+            all_color_name.append(color_name)
+            text_bg = PictureProcessing("RGB", (440, 200), (255, 255, 255))
+            text_bg = text_bg.get_text_image_advanced(
+                value=(220, 10),
+                font=font,
+                text="【{}】".format(color_name),
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 5, 0, 0)
+            )
+            # text_bg.show()
+            _bg = PictureProcessing("RGB", (440, pp_jpg.height+text_bg.height), (255, 255, 255))
+            _bg = _bg.paste_img(top_img=pp_jpg)
+            _bg = _bg.paste_img(top_img=text_bg,value=(0,pp_jpg.height))
+            pp_list_1.append(_bg)
+
+        rows = 2
+        shoes_bg = PictureProcessing().horizontal_distribution(
+            pp_list=pp_list_1,
+            bg_width=1200,
+            margins=(0, 0, 40, 40),
+            line_spacing=60,
+            number_per_row=rows,
+        )
+        detailed_images.append(shoes_bg)
+
+
+        font_bg = PictureProcessing(r"{}\template_3_1_new.png".format(self.root))
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        base_x = pp_bg.width - 229
+        base_y = 205
+
+        text_list = [
+            ("-{}".format("-".join(all_color_name)), (base_x, base_y)),
+            ("-{}".format(self.get_text_value("鞋面材质")), (base_x, base_y+70*1)),
+            ("-{}".format(self.get_text_value("内里材质")), (base_x, base_y+70*2)),
+            ("-{}".format(self.get_text_value("鞋底材质")), (base_x, base_y+70*3)),
+            ("-{}".format(self.get_text_value("鞋垫材质")), (base_x, base_y+70*4 )),
+            ("-{}".format(self.get_text_value("跟高")), (base_x, base_y+70*5)),
+            ("-{}".format(self.get_text_value("尺码")), (base_x, base_y+70*6)),
+        ]
+        for text_item in text_list:
+            position = text_item[1]
+            text_str = text_item[0]
+            xPos = position[0]
+            for char in reversed(text_str):
+                font_bg.add_text(
+                    mode="pixel",
+                    value=(xPos, position[1]),
+                    font=font,
+                    text=char,
+                    align="right",
+                    anchor="mm",
+                    spacing=10,
+                    fill=(30, 30, 30),
+                )
+                if not "\u4e00" <= char <= "\u9fff":
+                    xPos -= 20
+                else:
+                    xPos -= 35
+        # 其他图片
+        detailed_images.append(font_bg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # =============设计理念================
+        detail_images = []
+        top_bg = PictureProcessing(r"{}\template_4.png".format(self.root))
+        detail_images.append(top_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 40)
+        # 粘贴文字
+        text = self.get_text_value("设计理念")
+        if not text:
+            text = "引领全新裸足脚感,开启自信步履亲肤\n优质鞋面材质赋予全新舒适脚感\n多元场景穿搭,满足你的不同STYLE"
+        _, text_height = self.get_font_render_size(text, font_1)
+        text_bg = PictureProcessing(
+            "RGB",
+            (
+                top_bg.width,
+                text_height + 350,
+            ),
+            (236, 227, 212),
+        )
+        goods_art_no_list = list(self.data.keys())
+        # y = 430
+
+        x = top_bg.width / 2
+        text_bg = text_bg.add_text(
+            mode="pixel",
+            value=(x, int(text_bg.height / 2) - 60),
+            font=font_1,
+            text=text,
+            align="center",
+            anchor="mm",
+            spacing=15,
+            fill=(97, 97, 98),
+        )
+        detail_images.append(text_bg)
+
+        # ==========3个提示信息=========
+        title_font = ImageFont.truetype(r"resources\ttf\puhui\Heavy.ttf", 60)
+        sub_title_font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        desc_font = ImageFont.truetype(r"resources\ttf\puhui\Heavy.ttf", 35)
+        color_name_list = ["俯视", "内里", "侧视"]
+        # 添加分割线
+        detail_images.append(PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255)))
+
+        for index, color_name in enumerate(color_name_list):
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 20), (255, 255, 255)))
+
+            t_title = self.get_text_value("提示{}主标题".format(index + 1))
+            if t_title:
+                t_title_pp = PictureProcessing("RGB", (top_bg.width, 500), (255, 255, 255))
+                t_title_pp=t_title_pp.get_text_image_advanced(
+                    value=(int(top_bg.width / 2), 0),
+                    font=title_font,
+                    text=t_title,
+                    align="center",
+                    anchor="ma",
+                    spacing=10,
+                    fill=(55, 55, 55),
+                    return_mode="min_image_high",
+                    margins=(10, 10, 0, 0)
+                )
+                detail_images.append(t_title_pp)
+
+
+                t_sub_title = self.get_text_value("提示{}副标题".format(index + 1))
+                if t_sub_title:
+                    t_sub_title_pp = PictureProcessing("RGB", (top_bg.width, 500), (255, 255, 255))
+                    t_sub_title_pp = t_sub_title_pp.get_text_image_advanced(
+                        value=(int(top_bg.width / 2),0),
+                        font=sub_title_font,
+                        text=t_sub_title,
+                        align="center",
+                        anchor="ma",
+                        spacing=10,
+                        fill=(55, 55, 55),
+                        return_mode="min_image_high",
+                        margins=(10, 10, 0, 0)
+                    )
+                    detail_images.append(t_sub_title_pp)
+
+
+            # 添加图
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color
+            )
+            t_desc = self.get_text_value("提示{}描述".format(index + 1))
+            if color_name == "俯视":
+                pp_jpg = pp_jpg.resize(value=1300)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (749, 1009 if pp_jpg.height>1009 else pp_jpg.height), (236, 227, 212)), base="ws",
+                    value=(-300, -50))
+                text_pos_value = (50, 50)
+                align = "left"
+                anchor = ""
+
+            if color_name == "内里":
+                pp_jpg = pp_jpg.resize(value=2000)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (749, pp_jpg.height + 150), (236, 227, 212)), base="wc",
+                    value=(-230, 0))
+                text_pos_value = (50, 50)
+                align = "left"
+                anchor = ""
+
+            if color_name == "侧视":
+                pp_jpg = pp_jpg.resize(value=1600)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (749, 1009 if pp_jpg.height>1009 else pp_jpg.height), (236, 227, 212)), base="se",
+                    value=(0, -30))
+                text_pos_value = (pp_jpg.width - 30, pp_jpg.height - 360)
+                align = "right"
+                anchor = "rs"
+
+            if t_desc:
+                pp_jpg = pp_jpg.add_text(
+                    mode="pixel",
+                    value=text_pos_value,
+                    font=desc_font,
+                    text=t_desc,
+                    align=align,
+                    anchor=anchor,
+                    spacing=10,
+                    fill=(102, 102, 102),
+                )
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGB", (top_bg.width, pp_jpg.height,), (255, 255, 255), ), base="cc",
+                value=(0, 0))
+            detail_images.append(pp_jpg)
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 60), (255, 255, 255), ))
+
+        # 添加分割线
+        detail_images.append(PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255), ))
+        image = PictureProcessing(im=self.add_pic(detail_images))
+        return image
+
+    def deal_pic_5(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        top_bg = PictureProcessing(
+            r"{}\template_5.png".format(
+                self.root
+            )
+        )
+
+        detailed_images.append(top_bg)
+        pp_list_1 = []
+        color_name_1 = ["俯视", "侧视", "内里", "后跟", "鞋底"]
+        color_name_2 = ["俯视", "鞋底"]
+
+        color_name_1 = ["俯视", "侧视", "鞋底"]
+        color_name_2 = ["俯视", "内里", "后跟"]
+        n = 0
+        pp_1_height = 100
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index in [0, 2, 4, 6, 8]:
+                color_name = color_name_1
+            else:
+                color_name = color_name_2
+
+            for name in color_name:
+                pp_1 = self.get_overlay_pic_from_dict(
+                    goods_art_no=goods_art_no,
+                    color_name=name,
+                    bg_color=self.base_bg_color
+                )
+
+                if pp_1:
+                    n += 1
+                    if name != "后跟":
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 1.4))
+                    else:
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 3))
+
+                    if n == 1:
+                        pp_1_height = pp_1.height
+
+                    _height = pp_1.height if pp_1.height > pp_1_height else pp_1_height
+
+                    pp_1 = pp_1.paste_img_invert(base="cc",
+                                                 top_img=PictureProcessing("RGB",
+                                                                           (int(top_bg.width / 1.4), _height + 10,),
+                                                                           self.base_bg_color))
+
+                    pp_1 = pp_1.paste_img_invert(base="cc",
+                                                 top_img=PictureProcessing("RGB",
+                                                                           (top_bg.width, _height + 100,),
+                                                                           (255, 255, 255)))
+
+                    pp_list_1.append(pp_1)
+
+        if pp_list_1:
+            pp_list_1.append(PictureProcessing("RGB", (top_bg.width, 100), bg_color))
+            detailed_images.extend(pp_list_1)
+
+        # return PictureProcessing(im=self.add_pic(detailed_images))
+        return self.pp_pic_subsection(PictureProcessing(im=self.add_pic(detailed_images)))
+
+
+    def get_font_render_size(self, text, font, canvas_size=(2048, 2048)):
+        canvas = Image.new('RGB', canvas_size)
+        draw = ImageDraw.Draw(canvas)
+        draw.text((0, 0), text, font=font, fill=(55, 55, 55))
+        bbox = canvas.getbbox()
+        # 宽高
+        size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
+        return size
+

+ 459 - 0
python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie4.py

@@ -0,0 +1,459 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+import settings
+
+from PIL import ImageFont
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["小苏"]
+template_name = "xiaosushuoxie-4"
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\4".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+        self.template_name = template_name
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\4".format(os.getcwd())
+        print("run xiaosushuoxie-4 ")
+        self.base_bg_color = (255, 255, 255)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+        ]
+
+        if test:
+            # for k, v in self.goods_no_value.items():
+            #     print(k, v)
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        # detailed_images.append(self.deal_pic_2())
+        # detailed_images.append(self.deal_pic_3())
+        # detailed_images.append(self.deal_pic_4())
+        # detailed_images.append(self.deal_pic_5())
+        # detailed_images.append(self.deal_pic_6())
+        # detailed_images.append(self.deal_pic_7())
+        img = self.add_pic(detailed_images)
+        # img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+        img.show()
+
+    # 主图
+    def deal_pic_1(self):
+        """   制作主图  """
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\t (1).png".format(self.root))
+        # 添加分割线
+        # detailed_images.append(PictureProcessing("RGB", (1200, 100), (255, 255, 255)))
+        detailed_images.append(pp_bg)
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 56)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 26)
+        text_list = []
+
+        text_list.append({"text": self.get_text_value("标题"),
+                          "font": font_1,
+                          "fill": (17, 16, 16),
+                          })
+        text_list.append({"text": self.get_text_value("副标题"),
+                          "font": font_2,
+                          "fill": (17, 16, 16),
+                          })
+        text_image = self.add_text_list(text_list, spacing=25, base="nc")
+        if text_image:
+            text_image = text_image.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1200, text_image.height + 100), self.base_bg_color),
+                base="cc",
+                value=(0, 0))
+            detailed_images.append(text_image)
+        # ====粘贴组合图以及其他图
+        goods_art_no_list = list(self.data.keys())
+        # 文字排列
+        _zuhe_pic_list = []
+        color_name_list = ["组合", "组合2", "组合3", "组合4", "组合5"]
+        for color_name in color_name_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color,
+            )
+            if not pp_1:
+                continue
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+            _zuhe_pic_list.append(pp_1)
+            _zuhe_pic_list.append(PictureProcessing("RGB", (pp_bg.width, 150), self.base_bg_color))
+
+        if not _zuhe_pic_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name="俯视",
+                bg_color=self.base_bg_color,
+            )
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        detailed_images.extend(_zuhe_pic_list)
+        detailed_images.append(PictureProcessing(r"{}\t (3).png".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 商品信息
+    def deal_pic_2(self):
+        """
+        细节解析
+        """
+        # 文字排列
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\t (4).png".format(self.root))
+        detailed_images.append(pp_bg)
+        detailed_images.append(PictureProcessing("RGB", (pp_bg.width, 120), self.base_bg_color))
+
+        goods_art_no_list = list(self.data.keys())
+        pp_bg_2 = PictureProcessing(r"{}\t (26) .png".format(self.root))
+
+        pp_1 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="侧视",
+            bg_color=self.base_bg_color,
+        )
+        pp_1 = pp_1.resize(value=700)
+        pp_bg_2 = pp_bg_2.paste_img(top_img=pp_1,
+                                    base="sc",
+                                    value=(0, 90)
+                                    )
+        # 粘贴文字
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 26)
+        text_key_list = ["鞋面特点", "跟高", "鞋底特点"]
+        for text_key in text_key_list:
+            text = self.get_text_value(text_key)
+            if not text:
+                continue
+            if text_key == "鞋面特点":
+                value = (131, 380)
+            if text_key == "跟高":
+                value = (935, 187)
+                text = "跟高:{}".format(text)
+            if text_key == "鞋底特点":
+                value = (831, 40)
+            text_image = pp_bg_2.get_text_image_advanced(
+                value=(0, 0),
+                font=font,
+                text=text,
+                spacing=5,
+                fill=(15, 15, 15),
+                return_mode="min_image",
+            )
+            if text_image:
+                pp_bg_2 = pp_bg_2.paste_img(top_img=text_image,
+                                            base="sw",
+                                            value=value
+                                            )
+
+        # 剪裁多余的
+        if pp_bg_2.height > pp_1.height + 90:
+            pp_bg_2 = pp_bg_2.crop(bbox=(0, pp_bg_2.height - 90 - pp_1.height, pp_bg_2.width, pp_bg_2.height))
+
+        detailed_images.append(pp_bg_2)
+
+        font_bg = PictureProcessing(r"{}\t (6).png".format(self.root))
+        # 商品信息文字
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 35)
+        text_list = [
+            ("{}".format(self.get_text_value("品牌")), (294 + 20, 49)),
+            ("{}".format(self.get_text_value("临时款号")), (854 + 20, 49)),
+            ("{}".format(self.get_text_value("鞋面材质")), (294 + 20, 138)),
+            ("{}".format(self.get_text_value("内里材质")), (854 + 20, 138)),
+            ("{}".format(self.get_text_value("鞋垫材质")), (294 + 20, 230)),
+            ("{}".format(self.get_text_value("鞋底材质")), (854 + 20, 230)),
+        ]
+
+        for text_item in text_list:
+            if text_item[0]:
+                font_bg = font_bg.get_text_image_advanced(
+                    value=text_item[1],
+                    font=font,
+                    text=text_item[0],
+                    align="center",
+                    anchor=None,
+                    spacing=5,
+                    fill=(30, 30, 30),
+                    return_mode="image",
+                    margins=(0, 0, 0, 0)
+                )
+
+        # 其他图片
+        detailed_images.append(font_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        """   各个颜色细节展示  """
+        detailed_images = []
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB", (1200, 100), (255, 255, 255)))
+
+        # 放各个颜色图
+        pp_list_1 = []
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 25)
+        goods_art_no_list = list(self.data.keys())
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="侧视",
+                bg_color=self.base_bg_color,
+            )
+            if pp_jpg is None:
+                continue
+            pp_jpg = pp_jpg.resize(value=480)
+            color_name = self.goods_no_value["货号资料"][index]["颜色名称"]
+            text_bg = PictureProcessing("RGB", (480, 50), (255, 255, 255))
+            text_bg = text_bg.get_text_image_advanced(
+                value=(480 / 2, 10),
+                font=font,
+                text="{}".format(color_name),
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(55, 55, 55),
+                return_mode="image",
+                margins=(0, 0, 0, 0)
+            )
+            # text_bg.show()
+            _bg = PictureProcessing("RGB", (440, pp_jpg.height + text_bg.height), (255, 255, 255))
+            _bg = _bg.paste_img(top_img=pp_jpg)
+            _bg = _bg.paste_img(top_img=text_bg, value=(0, pp_jpg.height))
+            pp_list_1.append(_bg)
+
+        rows = 2
+        shoes_bg = PictureProcessing().horizontal_distribution(
+            pp_list=pp_list_1,
+            bg_width=1200,
+            margins=(0, 0, 40, 40),
+            line_spacing=60,
+            number_per_row=rows,
+        )
+        detailed_images.append(shoes_bg)
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB", (1200, 200), (255, 255, 255)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 细节展示
+    def deal_pic_4(self):
+        # =============细节展示================
+        detail_images = []
+        top_bg = PictureProcessing(r"{}\t (8).png".format(self.root))
+        detail_images.append(top_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Heavy.ttf", 120)  # 序号标签
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 36)  # 描述主标题
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 26)  # 描述副标题
+
+        # ==========3个图片描述信息=========
+        goods_art_no_list = list(self.data.keys())
+        color_name_list = ["俯视", "鞋底", "侧视"]
+        for index, color_name in enumerate(color_name_list):
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 20), (255, 255, 255)))
+
+            # 添加商品图片
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color
+            )
+            if color_name == "俯视":
+                pp_jpg = pp_jpg.resize(value=1565)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1200, 1050), (255, 255, 255)),
+                    base="ws",
+                    value=(104, 0))
+                text_pos_value = (50, 70)
+                text_pos_base = "nw"  # 粘贴文字的位置
+                text_base = "wn"  # 文字方向
+
+            if color_name == "鞋底":
+                pp_jpg = pp_jpg.resize(value=1766)
+                pp_jpg = pp_jpg.transpose(mode="left_right")
+
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1200, pp_jpg.height + 150), (255, 255, 255)), base="ec",
+                    value=(275, 0))
+                text_pos_value = (50, 150)
+                text_pos_base = "es"  # 粘贴文字的位置
+                text_base = "en"
+
+            if color_name == "侧视":
+                pp_jpg = pp_jpg.resize(value=1681)
+                pp_jpg = pp_jpg.transpose(mode="left_right")
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1200, 843), (255, 255, 255)), base="ws",
+                    value=(216, 0))
+                text_pos_value = (50, 150)
+                text_pos_base = "ws"  # 粘贴文字的位置
+                text_base = "wn"
+
+            text_list = []
+            # 序号
+            text_list.append({"text": "{}".format(index + 1).zfill(2),
+                              "font": font_1,
+                              "fill": (215, 215, 215),
+                              })
+            # 主标题
+            text_list.append({"text": self.get_text_value("提示{}主标题".format(index + 1)),
+                              "font": font_2,
+                              "fill": (55, 55, 55),
+                              })
+            text_list.append({"text": self.get_text_value("提示{}副标题".format(index + 1)),
+                              "font": font_3,
+                              "fill": (55, 55, 55),
+                              })
+
+            text_image = self.add_text_list(text_list, spacing=30, base=text_base)
+            pp_jpg = pp_jpg.paste_img(
+                top_img=text_image,
+                base=text_pos_base,
+                value=text_pos_value, )
+            detail_images.append(pp_jpg)
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 60), (255, 255, 255), ))
+
+        # 添加分割线
+        detail_images.append(PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255), ))
+        image = PictureProcessing(im=self.add_pic(detail_images))
+        return image
+
+    # 添加尺码表
+    def deal_pic_5(self):
+        image_path = r"{}\t (10).png".format(self.root)
+        return PictureProcessing(image_path)
+
+    # 静物展示
+    def deal_pic_6(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        top_bg = PictureProcessing(r"{}\t (11).png".format(self.root))
+
+        detailed_images.append(top_bg)
+        pp_list_1 = []
+        color_name_show_list = []
+        color_name_show_list.append(["组合", "内里", "侧视", "鞋底", "俯视"])
+        color_name_show_list.append(["组合2", "内里", "侧视", "鞋底", "俯视"])
+        color_name_show_list.append(["组合3", "内里", "侧视", "鞋底", "俯视"])
+        color_name_show_list.append(["内里", "侧视", "鞋底", "俯视"])
+
+        color_name_text_show_dict = {
+            "组合": "合并展示 / MERGE",
+            "组合2": "合并展示 / MERGE",
+            "组合3": "合并展示 / MERGE",
+            "内里": "内里展示 / INSIDER",
+            "鞋底": "鞋底展示 / SOLE",
+            "侧视": "后跟展示 / HEELPIECE",
+            "俯视": "45°展示 / 45°",
+        }
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        n = 0
+        pp_1_height = 100
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            try:
+                color_name = color_name_show_list[index]
+            except:
+                color_name = color_name_show_list[-1]
+
+            for name in color_name:
+                pp_1 = self.get_overlay_pic_from_dict(
+                    goods_art_no=goods_art_no,
+                    color_name=name,
+                    bg_color=self.base_bg_color
+                )
+                if pp_1:
+                    n += 1
+                    if name != "后跟":
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 1.4))
+                    else:
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 3))
+
+                    if n == 1:
+                        pp_1_height = pp_1.height
+
+                    _height = pp_1.height if pp_1.height > pp_1_height else pp_1_height
+                    _height = pp_1.height + 50
+                    # pp_1 = pp_1.paste_img_invert(base="sc",
+                    #                              top_img=PictureProcessing("RGB",
+                    #                                                        (int(top_bg.width / 1.4), _height + 10,),
+                    #                                                        self.base_bg_color))
+                    pp_1 = pp_1.paste_img_invert(base="sc",
+                                                 value=(0, 50),
+                                                 top_img=PictureProcessing("RGB",
+                                                                           (top_bg.width, _height + 100,),
+                                                                           (255, 255, 255)))
+
+                    pp_list_1.append(pp_1)
+                    font_bg = PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255))
+                    font_bg = font_bg.get_text_image_advanced(
+                        value=(600, 20),
+                        font=font,
+                        text=color_name_text_show_dict[name],
+                        align="center",
+                        anchor="mm",
+                        spacing=5,
+                        fill=(30, 30, 30),
+                        return_mode="image",
+                        margins=(0, 0, 0, 0)
+                    )
+                    pp_list_1.append(font_bg)
+
+        if pp_list_1:
+            pp_list_1.append(PictureProcessing("RGB", (top_bg.width, 100), bg_color))
+            detailed_images.extend(pp_list_1)
+
+        # return PictureProcessing(im=self.add_pic(detailed_images))
+        return self.pp_pic_subsection(PictureProcessing(im=self.add_pic(detailed_images)))
+
+    # 添加备注图
+    def deal_pic_7(self):
+        image_path = r"{}\t (12).png".format(self.root)
+        return PictureProcessing(image_path)
+
+

+ 603 - 0
python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie5.py

@@ -0,0 +1,603 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+import settings
+
+from PIL import ImageFont
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["小苏" ]
+template_name = "xiaosushuoxie-5"
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\5".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,assigned_page_list=assigned_page_list)
+        self.template_name = template_name
+
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\5".format(os.getcwd())
+        print("run xiaosushuoxie-5 ")
+        self.base_bg_color = (255, 255, 255)
+        self.base_bg_color_2 = (244, 242, 243)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+            self.deal_pic_8,
+            self.deal_pic_9,
+            self.deal_pic_10,
+            self.deal_pic_11,
+        ]
+
+        if test:
+            # pp = self.generate_font_list_to_pic()
+            # pp.im.save(r"C:\Users\gymmc\Desktop\细节图示例/字号.png")
+            # for k, v in self.goods_no_value.items():
+            #     print(k, v)
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        detailed_images.append(self.deal_pic_6())
+        detailed_images.append(self.deal_pic_7())
+        detailed_images.append(self.deal_pic_8())
+        detailed_images.append(self.deal_pic_9())
+        detailed_images.append(self.deal_pic_10())
+        detailed_images.append(self.deal_pic_11())
+
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir,self.goods_no, format="JPEG"))
+        # img.show()
+
+
+    # 标题文字展示
+    def deal_pic_1(self):
+        pp_bg = PictureProcessing(r"{}\t (1).png".format(self.root))
+
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 74)  # 设计理念
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述主标题1
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述副标题1
+
+        text_list = []
+        text_list.append({"text": self.get_text_value("设计理念"),
+                          "font": font_1,
+                          "fill": (47, 44, 51),
+                          "spacing": 50,
+                          })
+        # 主标题
+        text_list.append({"text": self.get_text_value("标题"),
+                          "font": font_2,
+                          "fill": (124, 91, 24),
+                          })
+        text_list.append({"text": self.get_text_value("副标题"),
+                          "font": font_3,
+                          "fill": (55, 55, 55),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=15, base="en")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="ne",
+                value=(133, 277))
+
+        return pp_bg
+
+    # 展示鞋头和后跟
+    def deal_pic_2(self):
+        pp_bg = PictureProcessing(r"{}\t (2).png".format(self.root))
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="内里",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1187)
+        pp_png_1 = pp_png_1.rotate_advance(doge=90)
+        pp_png_1 = pp_png_1.crop(bbox=(0, 0, pp_png_1.width, 473))
+        pp_bg = pp_bg.paste_img(top_img=pp_png_1,
+                                value=(349 - pp_png_1.width / 2, 342),
+                                base="sw"
+                                )
+
+        pp_jpg_2, pp_png_2 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="后跟",
+                                                )
+        pp_png_2: PictureProcessing
+        pp_png_2 = pp_png_2.resize(value=502)
+        pp_png_2 = pp_png_2.crop(mode="min")
+        pp_png_2 = pp_png_2.crop(bbox=(0, 0, pp_png_2.width, 473))
+
+        pp_bg = pp_bg.paste_img(top_img=pp_png_2,
+                                value=(871 - pp_png_2.width / 2, 342),
+                                base="sw"
+                                )
+
+        # 添加标题文字等
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 42)  # 特征标题
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)  # 特征描述
+
+        text_list = []
+        # 鞋头特征
+        text_list.append({"text": self.get_text_value("鞋头特征"),
+                          "font": font_1,
+                          "fill": (48, 43, 49),
+                          "spacing": 0,
+                          })
+        # 主标题
+        # 鞋头描述
+        text_list.append({"text": self.get_text_value("鞋头描述"),
+                          "font": font_2,
+                          "fill": (164, 164, 164),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=15, base="nc")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(349 - text_image.width / 2, 511))
+
+        text_list = []
+        # 后跟特征
+        text_list.append({"text": self.get_text_value("后跟特征"),
+                          "font": font_1,
+                          "fill": (48, 43, 49),
+                          "spacing": 0,
+                          })
+        # 后跟描述
+        text_list.append({"text": self.get_text_value("后跟描述"),
+                          "font": font_2,
+                          "fill": (164, 164, 164),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=15, base="nc")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(871 - text_image.width / 2, 511))
+
+        return pp_bg
+
+    # 展示鞋头放大图
+    def deal_pic_3(self):
+        pp_bg = PictureProcessing(r"{}\t (3).png".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 70)  # 设计理念
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 46)  # 描述主标题1
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述主标题2
+
+        text_list = []
+        # 鞋头特征
+        text_list.append({"text": self.get_text_value("设计理念2"),
+                          "font": font_1,
+                          "fill": (3, 3, 2),
+                          "spacing": 40,
+                          })
+        # 主标题
+        text_list.append({"text": self.get_text_value("提示2主标题"),
+                          "font": font_2,
+                          "fill": (124, 91, 24),
+                          })
+        text_list.append({"text": self.get_text_value("提示2副标题"),
+                          "font": font_3,
+                          "fill": (26, 25, 23),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=10, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(140, 176))
+
+        # 粘贴俯视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="俯视",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1933)
+        pp_png_1 = pp_png_1.crop(mode="min")
+        pp_bg = pp_bg.paste_img(top_img=pp_png_1,
+                                value=(202, 86),
+                                base="sw"
+                                )
+
+        return pp_bg
+
+    # 展示后跟放大图
+    def deal_pic_4(self):
+        pp_bg = PictureProcessing(r"{}\t (4).png".format(self.root))
+        h = 176
+        # ===========粘贴文字============
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 46)  # 描述主标题1
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述主标题2
+
+        text_list = []
+        # 鞋头特征
+        # 主标题
+        text_list.append({"text": self.get_text_value("提示3主标题"),
+                          "font": font_2,
+                          "fill": (124, 91, 24),
+                          })
+        text_list.append({"text": self.get_text_value("提示3副标题"),
+                          "font": font_3,
+                          "fill": (26, 25, 23),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=10, base="ne")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="ne",
+                value=(140, h))
+
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no_list[0],
+                                            name="侧视",
+                                            )
+        pp_jpg = pp_jpg.resize(value=2000)
+        pp_png = pp_png.resize(value=2000)
+
+        if text_image:
+            h = h + text_image.height
+        else:
+            h = h
+        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="en", value=(100, h),
+                                             top_png_img=pp_png)
+
+        # 总高
+        h = h + pp_jpg.height + 50
+        if h < pp_bg.height:
+            # 剪裁
+            pp_bg = pp_bg.crop(bbox=(0, 0, 1200, h))
+
+        return pp_bg
+
+    # 展示后跟细节卡片
+    def deal_pic_5(self):
+        pp_bg = PictureProcessing(r"{}\t (5).png".format(self.root))
+
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="侧视",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1621)
+        pp_png_1 = pp_png_1.rotate_advance(doge=45)
+        pp_png_1 = pp_png_1.transpose()
+        pp_png_1 = pp_png_1.crop(mode="min")
+        shoe_bg = PictureProcessing("RGB", (448, 464), self.base_bg_color)
+        shoe_bg = shoe_bg.paste_img(top_img=pp_png_1,
+                                    value=(-80, 200),
+                                    base="wc"
+                                    )
+        shoe_bg = shoe_bg.radius(circular_pos=(0, 0, 1, 0), value=30)
+        pp_bg = pp_bg.paste_img(top_img=shoe_bg,
+                                value=(613, 174),
+                                base="nw")
+        # 添加文字
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 40)  # 设计理念
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)  # 描述主标题1
+
+        text_list = []
+        text_list.append({"text": self.get_text_value("提示4主标题"),
+                          "font": font_1,
+                          "fill": (10, 7, 6),
+                          })
+        text_list.append({"text": self.get_text_value("提示4副标题"),
+                          "font": font_2,
+                          "fill": (72, 65, 59),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=38, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(154, 210))
+
+        return pp_bg
+
+    # "心动"设计亮点,展示旋转的侧视图
+    def deal_pic_6(self):
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\t (6).png".format(self.root))
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="侧视",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=2050)
+        pp_png_1 = pp_png_1.transpose()
+        pp_png_1 = pp_png_1.rotate_advance(doge=20)
+        pp_png_1 = pp_png_1.crop(mode="min")
+        pp_bg = pp_bg.paste_img(top_img=pp_png_1,
+                                value=(-200, 200),
+                                base="ws"
+                                )
+
+        # ========添加文字==================
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 70)  # 设计理念
+        text = self.get_text_value("设计理念3")
+        pp_bg = pp_bg.get_text_image_advanced(
+            value=(pp_bg.width - 127, pp_bg.height - 43),
+            font=font_1,
+            text=text,
+            anchor="rs",
+            align="right",
+            spacing=5,
+            fill=(109, 107, 109),
+            return_mode="image",
+        )
+        return pp_bg
+
+    # 产品展示,展示大图各个细节
+    def deal_pic_7(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (7).png".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+        # ======展示组合图============
+        pp_1 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no,
+            color_name="组合",
+            bg_color=self.base_bg_color_2
+        )
+        if pp_1:
+            pp_1 = pp_1.resize(value=1200)
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1200, pp_1.height + 300), self.base_bg_color_2),
+                base="cc", )
+            detailed_images.append(pp_1)
+
+        # ======展示鞋面===============
+        detailed_images.append(PictureProcessing(r"{}\t (9).png".format(self.root)))
+        pp_2 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no,
+            color_name="俯视",
+            bg_color=self.base_bg_color_2
+        )
+        pp_2: PictureProcessing
+        pp_2 = pp_2.resize(value=1519)
+        pp_2 = pp_2.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_2.height + 50), self.base_bg_color_2),
+                                     base="ws", value=(171, 0))
+        detailed_images.append(pp_2)
+
+        # =======展示后跟=========
+        detailed_images.append(PictureProcessing(r"{}\t (10).png".format(self.root)))
+        pp_3 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no,
+            color_name="侧视",
+            bg_color=self.base_bg_color_2
+        )
+        pp_3: PictureProcessing
+        pp_3 = pp_3.resize(value=1881)
+        pp_3 = pp_3.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_3.height + 200), self.base_bg_color_2),
+                                     base="ec", value=(171, 0))
+        detailed_images.append(pp_3)
+
+        # =========展示鞋底============
+        detailed_images.append(PictureProcessing(r"{}\t (11).png".format(self.root)))
+        pp_jpg_4, pp_png_4 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no,
+                                                name="鞋底",
+                                                )
+        pp_png_4: PictureProcessing
+        pp_png_4 = pp_png_4.resize(value=2380)
+        pp_png_4 = pp_png_4.rotate_advance(doge=45)
+        pp_png_4 = pp_png_4.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1200, pp_png_4.height + 200), self.base_bg_color_2),
+            base="ws", value=(269, 81))
+        detailed_images.append(pp_png_4)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 功能展示与卖点
+    def deal_pic_8(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (12).png".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+        color_name_list = ["侧视", "鞋底", "俯视"]
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 42)  # 卖点标题1
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 32)  # 卖点标题2
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 26)  # 卖点描述1
+
+        # =====卖点1,展示后跟========
+        for color_name in color_name_list:
+            bg = PictureProcessing(r"{}\t (13).png".format(self.root))
+            pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no,
+                                                name=color_name,
+                                                )
+            temp_bg = PictureProcessing("RGB", (536, 424), (214, 214, 214))
+            if color_name == "侧视":
+                pp_png = pp_png.resize(value=990)
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="es")
+                shoe_paste_base = "wc"
+                text_1, text_2 = self.get_text_value("卖点1标题", subsection_len=2)
+                text_3 = self.get_text_value("卖点1介绍")
+                text_paste_base = "ec"
+                text_paste_value = (312, 0)
+
+            if color_name == "鞋底":
+                pp_png = pp_png.resize(value=1000)
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="wc", value=(0, 0))
+                shoe_paste_base = "ec"
+                text_1, text_2 = self.get_text_value("卖点2标题", subsection_len=2)
+                text_3 = self.get_text_value("卖点2介绍")
+                text_paste_base = "wc"
+                text_paste_value = (312, 0)
+            if color_name == "俯视":
+                pp_png = pp_png.resize(value=1418)
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="cc")
+                shoe_paste_base = "wc"
+                text_1, text_2 = self.get_text_value("卖点3标题", subsection_len=2)
+                text_3 = self.get_text_value("卖点3介绍")
+                text_paste_base = "ec"
+                text_paste_value = (312, 0)
+
+            # 添加圆角
+            temp_bg = temp_bg.radius(circular_pos=(1, 1, 1, 1), value=50)
+            # 粘贴到背景上
+            temp_bg = temp_bg.paste_img_invert(top_img=bg, base=shoe_paste_base, value=(99, 0))
+            text_list = []
+            # 卖点主标题
+            text_list.append({"text": text_1,
+                              "font": font_1,
+                              "fill": (2, 2, 2),
+                              "spacing": 0,
+                              })
+            text_list.append({"text": text_2,
+                              "font": font_2,
+                              "fill": (43, 43, 43),
+                              "spacing": 10,
+                              })
+            # 主标题
+            text_list.append({"text": text_3,
+                              "font": font_3,
+                              "fill": (165, 165, 165),
+                              })
+            text_image = self.add_text_list(text_list, spacing=15, base="nc")
+            if text_image:
+                temp_bg = temp_bg.paste_img(
+                    top_img=text_image,
+                    base=text_paste_base,
+                    value=(text_paste_value[0] - text_image.width / 2, text_paste_value[1]))
+            detailed_images.append(temp_bg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加尺码表
+    def deal_pic_9(self):
+        image_path = r"{}\t (16).png".format(self.root)
+        return PictureProcessing(image_path)
+
+    # 角度展示
+    def deal_pic_10(self):
+        detailed_images = []
+        # 角度展示标题
+        detailed_images.append(PictureProcessing(r"{}\t (17).png".format(self.root)))
+        # 添加颜色名称字体
+        font_color = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 30)
+
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            # ====颜色主图
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="俯视",
+                bg_color=(255, 255, 255)
+            )
+            pp_1: PictureProcessing
+            pp_1 = pp_1.resize(value=865)
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (1152, pp_1.height + 100), (255, 255, 255)),
+                                         base="sc"
+                                         )
+            # 粘贴颜色名称
+            pp_1 = pp_1.get_text_image_advanced(
+                value=(90, 50),
+                font=font_color,
+                text=color_name,
+                spacing=1,
+                return_mode="image",
+            )
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_1.height), (243, 243, 243)),
+                                         base="cc"
+                                         )
+
+            detailed_images.append(pp_1)
+
+            # 添加处理各个角度
+            view_name_list = ["内里", "鞋底", "后跟", "侧视"]
+            color_pic_list = []
+            for view_name in view_name_list:
+                _, pp_png = self.image_one_pic(return_orign=True,
+                                               goods_art_no=goods_art_no,
+                                               name=view_name,
+                                               )
+                temp_bg = PictureProcessing("RGB", (546, 338), (243, 243, 243))
+
+                # if view_name == "内里":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+                # if view_name == "鞋底":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+                # if view_name == "后跟":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+                # if view_name == "侧视":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+
+                pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="cc")
+                temp_bg = temp_bg.radius(value=30)
+                color_pic_list.append(temp_bg)
+
+            # 颜色列表进行等分展示
+            all_color_pp = PictureProcessing().horizontal_distribution(color_pic_list, bg_width=1114, line_spacing=10,
+                                                                       number_per_row=2)
+            all_color_pp = all_color_pp.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1152, all_color_pp.height + 100), (255, 255, 255)), base="cc")
+            all_color_pp = all_color_pp.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1200, all_color_pp.height), (243, 243, 243)), base="cc")
+            detailed_images.append(all_color_pp)
+
+        # 添加底部圆弧分割线
+        b_bg = PictureProcessing("RGB", (1152, 30), (255, 255, 255))
+        b_bg = b_bg.radius(circular_pos=(0, 0, 1, 1), value=30)
+        b_bg = b_bg.paste_img_invert(top_img=PictureProcessing("RGB", (1200, b_bg.height + 50), (243, 243, 243)),
+                                     base="nc")
+        detailed_images.append(b_bg)
+
+        # return PictureProcessing(im=self.add_pic(detailed_images))
+        return self.pp_pic_subsection(PictureProcessing(im=self.add_pic(detailed_images)))
+
+    # 添加注意事项
+    def deal_pic_11(self):
+        image_path = r"{}\t (21).png".format(self.root)
+        return PictureProcessing(image_path)

+ 576 - 0
python/custom_plugins/plugins/detail_template/xiaosushuoxie/detail_xiaosushuoxie6.py

@@ -0,0 +1,576 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+import sys
+import settings
+
+# from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+# from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.detail_generate_base import DetailBase
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.detail_generate_base import DetailBase
+    from plugins_mode.pic_deal import PictureProcessing
+
+plugins_name = "详情模板"
+company_name_list = ["小苏"]
+template_name = "xiaosushuoxie-6"
+
+
+class DetailPicGet(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\6".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None):
+
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\6".format(os.getcwd())
+        print(template_name)
+        self.template_name = template_name
+        self.base_bg_color = (228, 196, 147)
+        # 缩放倍数
+        self.sf = 16/12
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+            self.deal_pic_8,
+            self.deal_pic_9,
+        ]
+        # test = True
+        if test:
+            # pp_1 = self.generate_font_list_to_pic()
+            # pp_1.im.save(r"C:\Users\gymmc\Desktop\细节图示例/字号.png")
+            # for k, v in self.goods_no_value.items():
+            #     print(k, v)
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        # detailed_images.append(self.deal_pic_1())
+        # detailed_images.append(self.deal_pic_2())
+        # detailed_images.append(self.deal_pic_3())
+        # detailed_images.append(self.deal_pic_4())
+        # detailed_images.append(self.deal_pic_5())
+        # detailed_images.append(self.deal_pic_6())
+        detailed_images.append(self.deal_pic_7())
+        # detailed_images.append(self.deal_pic_8())
+        # detailed_images.append(self.deal_pic_9())
+        img = self.add_pic(detailed_images)
+        # img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+        if img:
+            img.show()
+
+    def deal_details_beifen(self):
+        detailed_images = []
+        self.image_list_append(detailed_images, self.deal_pic_1())
+        self.image_list_append(detailed_images, self.deal_pic_2())
+        self.image_list_append(detailed_images, self.deal_pic_3())
+        self.image_list_append(detailed_images, self.deal_pic_4())
+        self.image_list_append(detailed_images, self.deal_pic_5())
+        self.image_list_append(detailed_images, self.deal_pic_6())
+        self.image_list_append(detailed_images, self.deal_pic_7())
+        self.image_list_append(detailed_images, self.deal_pic_8())
+        self.image_list_append(detailed_images, self.deal_pic_9())
+        return [x for x in detailed_images if x]
+
+    # 组合图1
+    def deal_pic_1(self):
+        pp_bg = PictureProcessing(r"{}\t (1).jpg".format(self.root))
+
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 38)  # 设计理念
+        text_list = []
+        text_list.append({"text": self.get_text_value("设计理念"),
+                          "font": font_1,
+                          "fill": (102, 92, 92),
+                          })
+        text_image = self.add_text_list(text_list, spacing=15, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(20*self.sf, 895*self.sf))
+
+        # ----粘贴组合图
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+        view_list = ["组合", "俯视"]
+        for view_name in view_list:
+            pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no,
+                                                    name=view_name,
+                                                    )
+            if not pp_jpg_1:
+                continue
+            pp_jpg_1: PictureProcessing
+            pp_png_1: PictureProcessing
+            pp_jpg_1 = pp_jpg_1.resize(base_by_box=(485*self.sf, 588*self.sf))
+            pp_png_1 = pp_png_1.resize(base_by_box=(485*self.sf, 588*self.sf))
+            pp_bg = pp_bg.to_overlay_pic_advance(top_img=pp_jpg_1, top_png_img=pp_png_1,
+                                                 value=(947*self.sf - pp_jpg_1.width / 2, 888*self.sf - pp_jpg_1.height / 2), base="nw")
+            break
+
+        return pp_bg
+
+    # 展示设计理念2和组合2
+    def deal_pic_2(self):
+        pp_bg = PictureProcessing(r"{}\t (2).jpg".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 38)  # 设计理念
+
+        text_list = []
+        text_list.append({"text": self.get_text_value("设计理念2"),
+                          "font": font_1,
+                          "fill": (109, 103, 97),
+                          })
+        text_image = self.add_text_list(text_list, spacing=15, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(60*self.sf, 141*self.sf))
+
+        # ====粘贴组合2或侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="组合2",
+                                                )
+        if not pp_jpg_1:
+            pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no_list[0],
+                                                    name="侧视",
+                                                    )
+        pp_jpg_1: PictureProcessing
+        pp_png_1: PictureProcessing
+        pp_jpg_1 = pp_jpg_1.resize(base_by_box=(546*self.sf, 592*self.sf))
+        pp_png_1 = pp_png_1.resize(base_by_box=(546*self.sf, 592*self.sf))
+
+        pp_bg = pp_bg.to_overlay_pic_advance(top_img=pp_jpg_1, top_png_img=pp_png_1,
+                                             value=(786*self.sf - pp_jpg_1.width / 2, 816*self.sf - pp_jpg_1.height / 2))
+
+        return pp_bg
+
+    # 亮点解析
+    def deal_pic_3(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (3).jpg".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+
+        # =====添加内里图
+        pp_bg_1 = PictureProcessing(r"{}\t (4).jpg".format(self.root))
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="内里",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1600*self.sf)
+        pp_png_1 = pp_png_1.paste_img_invert(top_img=PictureProcessing("RGB", (1200*self.sf, 1061*self.sf), (255, 255, 255)),
+                                             base="ws",
+                                             value=(-85*self.sf, 0))
+
+        # 添加内里描述
+        _neili_bg = PictureProcessing(r"{}\t (25).png".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 内里描述
+        text_list = [{"text": self.get_text_value("内里特点"),
+                      "font": font_1,
+                      "fill": (110, 110, 110),
+                      }]
+        text_image = self.add_text_list(text_list, spacing=10, base="nc")
+        if text_image:
+            _neili_bg = _neili_bg.paste_img(
+                top_img=text_image,
+                base="sc",
+                value=(0, 69*self.sf))
+        pp_bg_1 = pp_bg_1.paste_img(top_img=pp_png_1, base="sc", value=(0, 266*self.sf))
+        pp_bg_1 = pp_bg_1.paste_img(top_img=_neili_bg, base="sc", value=(0, 137*self.sf))
+        detailed_images.append(pp_bg_1)
+
+        # ======添加鞋底图====
+        pp_2 = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                              color_name="鞋底",
+                                              bg_color=(255, 255, 255),
+                                              )
+        pp_2 = pp_2.resize(value=1396*self.sf)
+        pp_bg_2 = PictureProcessing("RGB", (1600, pp_2.height + 80*self.sf), (255, 255, 255))
+        pp_bg_2 = pp_bg_2.paste_img(top_img=pp_2, base="wc", value=(80*self.sf, 0))
+        detailed_images.append(pp_bg_2)
+
+        # --添加鞋底特点
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 40)  # 内里描述
+        text_list = [{"text": self.get_text_value("鞋底特点"),
+                      "font": font_1,
+                      "fill": (110, 110, 110),
+                      }]
+        text_image = self.add_text_list(text_list, spacing=10, base="nc")
+        if text_image:
+            pp_bg_3 = PictureProcessing("RGB", (1600, text_image.height + 80*self.sf), (255, 255, 255))
+            pp_bg_3 = pp_bg_3.paste_img(top_img=text_image, base="cc", value=(0, 0))
+            detailed_images.append(pp_bg_3)
+
+        # ---添加鞋子的俯视图,并标注尺寸----------------
+        pp_bg_4 = PictureProcessing(r"{}\t (6).jpg".format(self.root))
+        pp_4 = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                              color_name="俯视",
+                                              bg_color=(255, 255, 255),
+                                              )
+        pp_4 = pp_4.resize(value=332 * 1.8*self.sf)
+        pp_bg_4 = pp_bg_4.paste_img(top_img=pp_4, base="sc", value=(0, 222*self.sf))
+        # -添加文字描述等
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 34)
+        if self.get_text_value("鞋长"):
+            text = "长:{}".format(self.get_text_value("鞋长"))
+            text_image = pp_bg_4.get_text_image_advanced(font=font_1,
+                                                         text=text,
+                                                         fill=(110, 110, 110),
+                                                         return_mode="min_image")
+            pp_bg_4 = pp_bg_4.paste_img(top_img=text_image, base="sc", value=(0, 134*self.sf))
+        if self.get_text_value("跟高"):
+            text = "跟高:{}".format(self.get_text_value("跟高"))
+            text_image = pp_bg_4.get_text_image_advanced(font=font_1,
+                                                         text=text,
+                                                         fill=(110, 110, 110),
+                                                         return_mode="min_image")
+            pp_bg_4 = pp_bg_4.paste_img(top_img=text_image, base="se", value=(72*self.sf, 404*self.sf))
+        if self.get_text_value("脚掌围"):
+            text = "脚掌围:{}".format(self.get_text_value("脚掌围"))
+            text_image = pp_bg_4.get_text_image_advanced(font=font_1,
+                                                         text=text,
+                                                         fill=(110, 110, 110),
+                                                         return_mode="min_image")
+            pp_bg_4 = pp_bg_4.paste_img(top_img=text_image, base="sw", value=(179*self.sf, 510*self.sf))
+        if pp_bg_4.height > pp_4.height + 250*self.sf:
+            pp_bg_4 = pp_bg_4.crop(bbox=(0, pp_bg_4.height - pp_4.height - 250*self.sf, pp_bg_4.width, pp_bg_4.height))
+
+        detailed_images.append(pp_bg_4)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示产品信息和颜色图
+    def deal_pic_4(self):
+        detailed_images = []
+
+        pp_bg_1 = PictureProcessing(r"{}\t (7).jpg".format(self.root))
+
+        # 粘贴产品信息
+        # ---添加组合2图片
+        color_name_list = ["组合2", "组合", "俯视"]
+
+        goods_art_no_list = list(self.data.keys())
+
+        for color_name in color_name_list:
+            pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no_list[0],
+                                                    name=color_name,
+                                                    )
+            if not pp_png_1:
+                continue
+
+            pp_jpg_1: PictureProcessing
+            pp_png_1: PictureProcessing
+
+            pp_jpg_1 = pp_jpg_1.resize(base_by_box=(589*self.sf, 416*self.sf))
+            pp_png_1 = pp_png_1.resize(base_by_box=(589*self.sf, 416*self.sf))
+            pp_bg_1 = pp_bg_1.to_overlay_pic_advance(top_img=pp_jpg_1, top_png_img=pp_png_1, base="nc", value=(0, 257*self.sf))
+            break
+
+        # ====添加文字信息
+        text_list = [{"title": "帮面材质:", "get_text": "鞋面材质"},
+                     {"title": "鞋型:", "get_text": "鞋型"},
+                     {"title": "内里材质:", "get_text": "内里材质"},
+                     {"title": "头型:", "get_text": "头型"},
+                     {"title": "大底材质:", "get_text": "鞋底材质"},
+                     {"title": "货号:", "get_text": "款号"},
+                     ]
+        x, y = 176*self.sf, 779*self.sf
+        pos_list = [
+            (x, y),
+            (x + 592*self.sf, y),
+            (x, y + 101*self.sf),
+            (x + 592*self.sf, y + 101*self.sf),
+            (x, y + 101*self.sf + 101*self.sf),
+            (x + 592*self.sf, y + 101*self.sf + 101*self.sf),
+        ]
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Regular.ttf", 40)  # 鞋材料信息
+
+        n = -1
+        for text_data in text_list:
+            text_value = self.get_text_value(text_data["get_text"])
+            if text_value:
+                n += 1
+                text = "{}  {}".format(text_data["title"], text_value)
+                text_image = pp_bg_1.get_text_image_advanced(font=font_1,
+                                                             text=text,
+                                                             fill=(0, 0, 0),
+                                                             return_mode="min_image")
+                pp_bg_1 = pp_bg_1.paste_img(top_img=text_image, base="nw", value=pos_list[n])
+
+        detailed_images.append(pp_bg_1)
+        # =======添加各个颜色====多余高度需要剪裁
+        pp_bg_2 = PictureProcessing(r"{}\t (8).jpg".format(self.root))
+
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 34)  # 颜色名称
+        color_pic_list_1 = []
+        color_pic_list_2 = []
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+
+            pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no,
+                                                name="侧视",
+                                                )
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(value=390*self.sf)
+            pp_png = pp_png.resize(value=390*self.sf)
+
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGBA", (pp_jpg.width, pp_jpg.height + 70*self.sf), (255, 255, 255, 0)),
+                base="nw"
+            )
+            pp_png = pp_png.paste_img_invert(
+                top_img=PictureProcessing("RGBA", (pp_png.width, pp_png.height + 70*self.sf), (255, 255, 255, 0)),
+                base="nw"
+            )
+
+            text_image = pp_bg_2.get_text_image_advanced(font=font_2,
+                                                         text="{} / COLOR".format(color_name),
+                                                         fill=(0, 0, 0),
+                                                         return_mode="min_image")
+
+            pp_jpg = pp_jpg.paste_img(top_img=text_image, base="sc", value=(0, 10*self.sf))
+            color_pic_list_1.append(pp_jpg)
+            color_pic_list_2.append(pp_png)
+
+        # 颜色列表进行等分展示
+        all_color_pp_1 = PictureProcessing().horizontal_distribution(color_pic_list_1, bg_width=1114*self.sf, line_spacing=10,
+                                                                     number_per_row=2)
+        all_color_pp_2 = PictureProcessing().horizontal_distribution(color_pic_list_2, bg_width=1114*self.sf, line_spacing=10,
+                                                                     number_per_row=2)
+
+        pp_bg_2 = pp_bg_2.to_overlay_pic_advance(top_img=all_color_pp_1, top_png_img=all_color_pp_2, base="nc",
+                                                 value=(0, 131*self.sf))
+
+        if pp_bg_2.height > all_color_pp_1.height + 131*self.sf + 50*self.sf:
+            pp_bg_2 = pp_bg_2.crop(
+                bbox=(0, 0, pp_bg_2.width, all_color_pp_1.height + 181*self.sf))
+
+        detailed_images.append(pp_bg_2)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 产品细节
+    def deal_pic_5(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (9).jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\t (10).jpg".format(self.root)))
+
+        # ==================俯视图鞋头===============
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 64)  # 主标题
+        font_1_color = (1, 1, 1)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 34)  # 副标题
+        font_2_color = (51, 51, 51)
+        goods_art_no_list = list(self.data.keys())
+
+        view_list = ["俯视", "侧视", "内里", "鞋底"]
+        for index, view_name in enumerate(view_list):
+            bg = PictureProcessing(r"{}\t (11).jpg".format(self.root))
+            text_list = []
+            text_list.append({"text": self.get_text_value("提示{}主标题".format(index + 1)),
+                              "font": font_1,
+                              "fill": font_1_color,
+                              })
+            # self.get_text_value("提示{}副标题".format(index + 1))
+            # text = "副标题副标题副标题副标题副标题副标题副标题副标题\n副标题副标题副标题副标题副标题副标题副标题副标题\n副标题\n副标题\n副标题\n副标题\n副标题"
+            text_list.append({"text": self.get_text_value("提示{}副标题".format(index + 1)),
+                              "font": font_2,
+                              "fill": font_2_color,
+                              })
+            text_image = self.add_text_list(text_list, spacing=30, base="nc")
+            if text_image:
+                bg = bg.paste_img(
+                    top_img=text_image,
+                    base="nc",
+                    value=(0, 0))
+                bg = bg.crop(bbox=(0, 0, bg.width, text_image.height + 50*self.sf))
+                detailed_images.append(bg)
+
+            # ===粘贴图片
+            pp_1 = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                                  color_name=view_name,
+                                                  bg_color=(255, 255, 255)
+                                                  )
+
+            if view_name == "俯视":
+                resize_value = 1011*self.sf
+                paste_img_value = (252*self.sf, 0)
+                base = "ws"
+            if view_name == "侧视":
+                resize_value = 1253*self.sf
+                paste_img_value = (270*self.sf, 0)
+                base = "es"
+            if view_name == "内里":
+                resize_value = 1539*self.sf
+                paste_img_value = (0, 0)
+                base = "ws"
+            if view_name == "鞋底":
+                resize_value = 1104*self.sf
+                paste_img_value = (0, 0)
+                base = "cc"
+
+            pp_1 = pp_1.resize(value=resize_value)
+
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (1079*self.sf, 572*self.sf), (255, 255, 255)),
+                                         base=base,
+                                         value=paste_img_value
+                                         )
+
+            bg = PictureProcessing(r"{}\t (11).jpg".format(self.root))
+            bg = bg.paste_img(top_img=pp_1,
+                              base="nc",
+                              value=(0, 0),
+                              )
+            bg = bg.crop(bbox=(0, 0, bg.width, pp_1.height + 10*self.sf))
+            detailed_images.append(bg)
+
+        detailed_images.append(PictureProcessing(r"{}\t (13).jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 组合图展示
+    def deal_pic_6(self):
+        flag = False  # 判断是否有组合图
+        detailed_images = []
+        pp_bg_1 = PictureProcessing(r"{}\t (15).jpg".format(self.root))
+        detailed_images.append(pp_bg_1)
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+
+        # 组合图1
+        pp_bg_2 = PictureProcessing(r"{}\t (16).jpg".format(self.root))
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no,
+                                            name="组合",
+                                            )
+        if pp_jpg:
+            flag = True
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(base_by_box=(910 * 0.9*self.sf, 800 * 0.9*self.sf))
+            pp_png = pp_png.resize(base_by_box=(910 * 0.9*self.sf, 800 * 0.9*self.sf))
+            pp_bg_2 = pp_bg_2.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="cc")
+            detailed_images.append(pp_bg_2)
+
+        # ============组合图2
+        pp_bg_3 = PictureProcessing(r"{}\t (17).jpg".format(self.root))
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no,
+                                            name="组合2",
+                                            )
+        if pp_jpg:
+            flag = True
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(base_by_box=(937 * 0.9*self.sf, 827 * 0.9*self.sf))
+            pp_png = pp_png.resize(base_by_box=(937 * 0.9*self.sf, 827 * 0.9*self.sf))
+            pp_bg_3 = pp_bg_3.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="cc")
+            detailed_images.append(pp_bg_3)
+
+        # ==========组合图3
+        pp_bg_4 = PictureProcessing(r"{}\t (18).jpg".format(self.root))
+        detailed_images.append(pp_bg_4)
+        pp_bg_5 = PictureProcessing(r"{}\t (19).jpg".format(self.root))
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no,
+                                            name="组合3",
+                                            )
+        if pp_jpg:
+            flag = True
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(base_by_box=(1062*self.sf, 937*self.sf))
+            pp_png = pp_png.resize(base_by_box=(1062*self.sf, 937*self.sf))
+            pp_bg_5 = pp_bg_5.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="cc")
+            detailed_images.append(pp_bg_5)
+
+        if flag:
+            return PictureProcessing(im=self.add_pic(detailed_images))
+        else:
+            return None
+
+    # 各个颜色展示
+    def deal_pic_7(self):
+        detailed_images = []
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 36)  # 副标题
+        view_list = ["俯视", "侧视", "后跟", "鞋底"]
+        bg_color = (241, 239, 240)
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            print("523   goods_art_no_dict",goods_art_no_dict)
+            bg = PictureProcessing(r"{}\t (20).jpg".format(self.root))
+            # bg.show()
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+
+            # 粘贴颜色名称
+            if color_name:
+                text_image = bg.get_text_image_advanced(font=font_1,
+                                                        text="{}".format(color_name),
+                                                        fill=(0, 0, 0),
+                                                        return_mode="min_image")
+                # text_image.show()
+                bg = bg.paste_img(top_img=text_image, base="nw", value=(85*self.sf, 35*self.sf))
+                # bg.show()
+            # 粘贴图片
+            for view_name in view_list:
+                pp = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                    color_name=view_name,
+                                                    bg_color=bg_color,
+                                                    )
+                if view_name == "俯视":
+                    pp = pp.resize(base_by_box=(779 * 0.8*self.sf, 838 * 0.8*self.sf))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (779*self.sf, 836*self.sf), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(38*self.sf, 152*self.sf))
+                if view_name == "侧视":
+                    pp = pp.resize(base_by_box=(321*self.sf, 244*self.sf))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (321*self.sf, 244*self.sf), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(844*self.sf, 152*self.sf))
+
+                if view_name == "后跟":
+                    pp = pp.resize(base_by_box=(321 * 0.7*self.sf, 278 * 0.9*self.sf))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (321*self.sf, 278*self.sf), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(844*self.sf, 417*self.sf))
+                if view_name == "鞋底":
+                    pp = pp.resize(base_by_box=(321*self.sf, 270*self.sf))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (321*self.sf, 270*self.sf), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(844*self.sf, 720*self.sf))
+            detailed_images.append(bg)
+        return self.pp_pic_subsection(PictureProcessing(im=self.add_pic(detailed_images)))
+
+    # 添加注意事项
+    def deal_pic_8(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (22).jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加注意事项
+    def deal_pic_9(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (23).jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\t (24).jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))

+ 0 - 0
python/custom_plugins/plugins/generate_and_upload/__init__.py


+ 151 - 0
python/custom_plugins/plugins/generate_and_upload/main_run.py

@@ -0,0 +1,151 @@
+plugins_name = "主图生成与上传"
+from UI.auto_deal_pics_ui.show_process import Ui_Form as show_process_Ui_Form
+from UI.auto_deal_pics_ui.auto_deal_pics_ui import Ui_Form as MainUi
+from UI.auto_deal_pics_ui.matching_photos import Ui_Form as matching_photos_Ui_Form
+
+
+
+
+
+
+from functools import partial
+from module.view_control.MineQWidget import MineQWidget, DialogShow
+
+
+from module.view_control.generate_main_image.grenerate_main_image_test import GeneratePic
+from module.view_control.matching_photos.data import DataModeMatchPhoto
+from collections import defaultdict
+
+
+
+import settings
+from module.view_control.generate_goods_no_detail_pic import detail_func
+import json
+from module.base_mode.base import *
+from import_qt_mode import *
+import threading
+import concurrent.futures
+from module.view_control.generate_goods_no_detail_pic.data import DataModeGenerateDetail, DataModeUploadPic
+from module.view_control.generate_goods_no_detail_pic.detail_func import create_folder
+import time
+from PIL import Image
+from io import BytesIO
+import os, re
+from func_timeout import FunctionTimedOut
+
+is_test_plugins = False
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.plugins_base_func import *
+else:
+    from plugins_mode.plugins_base_func import *
+
+_Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF', ".jpge", ".JPGE"]
+
+
+# 主程序入口
+class AutoDealPics1(MineQWidget):
+
+    def __init__(self, windows=None):
+        super().__init__()
+        self.windows = windows
+        self.ui = MainUi()
+        self.ui.setupUi(self)
+        self.setWindowTitle("图片处理(插件)")
+        self.resize(618, 651)
+        # 线程锁
+        self.lock = Lock()
+        self.init()
+        self.show()
+
+    def init(self):
+        self.show_process = ShowProcess(self)
+        self.ui.main_stackedWidget.addWidget(self.show_process)
+        self.show_process.back_sign.connect(self.to_back)
+        self.show_process.stop_sign.connect(self.to_stop)
+
+        # 匹配图片页面
+        self.show_matching_pic = MatchingPhotos(self)
+        self.ui.main_stackedWidget.addWidget(self.show_matching_pic)
+
+        # 抠图与生成详情页
+        self.show_generate_detail_pic = GenerateDetailPic(self, top_windows=self.windows)
+        self.ui.main_stackedWidget.addWidget(self.show_generate_detail_pic)
+
+        self.show_generate_detail_pic.text_show.connect(self.show_progress_detail)
+        self.show_generate_detail_pic.progress_show_sign.connect(self.switch_to_process_ui)
+        self.show_generate_detail_pic.progress_end_sign.connect(self.end_process)
+
+        # 开始抠图生成主图处理
+        self.show_matching_pic.run_cutout_sign.connect(self.switch_to_detail_ui)
+        self.ui.main_stackedWidget.setCurrentIndex(1)
+
+    def to_back(self, *args):
+        self.switch_to_detail_ui()
+
+    def to_stop(self):
+        self.show_generate_detail_pic.to_stop()
+
+    def switch_to_process_ui(self, flag):
+        if flag:
+            self.show_process.clearn()
+            self.ui.main_stackedWidget.setCurrentIndex(0)
+        else:
+            self.ui.main_stackedWidget.setCurrentIndex(2)
+
+    def end_process(self, *args):
+        self.show_process.is_end()
+
+    def show_progress_detail(self, text):
+        self.show_process.show_progress_detail(text)
+
+    # 页面切换到详情页生成的窗口
+    def switch_to_detail_ui(self, data=None):
+        if data:
+            folder_path = data["folder_path"]
+            self.show_generate_detail_pic.change_img_dir_by_text(folder_path)
+        self.ui.main_stackedWidget.setCurrentIndex(2)
+
+
+class ShowProcess(QWidget):
+    back_sign = Signal()
+    stop_sign = Signal()
+
+    def __init__(self, parent=None, windows=None):
+        super().__init__(parent)
+        self.windows = windows
+        self.ui = show_process_Ui_Form()
+        self.ui.setupUi(self)
+        self.ui.back.clicked.connect(self.back_sign.emit)
+        self.ui.stop.clicked.connect(self.stop_sign.emit)
+        self.ui.progressBar.hide()
+        self.show()
+
+    def show_progress_detail(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    def is_end(self):
+        self.ui.back.show()
+        self.ui.stop.hide()
+
+    def clearn(self):
+        self.ui.progressBar.setValue(0)
+        self.ui.textBrowser_4.clear()
+        self.ui.back.hide()
+        self.ui.stop.show()
+
+
+
+
+
+
+
+
+
+
+
+

+ 0 - 0
python/custom_plugins/plugins/micropython_update/__init__.py


+ 418 - 0
python/custom_plugins/plugins/micropython_update/ampy_plugin.py

@@ -0,0 +1,418 @@
+import json
+import time
+
+if __name__ == '__main__':
+    raise "不允许在当前目录下运行"
+
+import os
+import settings
+from ampy.pyboard import Pyboard
+import ampy.files
+from module.view_control.main_window.data_main import DataMode
+from module.base_mode.base import calculate_sha256, download_file, compare_two_times, get_modified_time
+import concurrent.futures
+
+plugins_name = "AMPY插件"
+
+
+class AmpyScribe(object):
+    def __init__(self, port_name="COM37", baud_rate=115200, windows=None):
+        self.port_name = port_name
+        self.baud_rate = baud_rate
+        self.windows = windows
+        self.pyb = Pyboard(self.port_name, baudrate=self.baud_rate)
+
+    def send_log(self, text):
+        if self.windows:
+            self.windows.send_log(text)
+
+    # 运行本地脚本
+    def run_remote_script(self, script_path):
+        """Run a script on the remote device."""
+        try:
+            self.pyb.enter_raw_repl()
+            result = self.pyb.execfile(script_path)
+            print(result.decode())
+            return result.decode()
+        except Exception as e:
+            print(f"An error occurred while running the script: {e}")
+            return None
+
+    def close(self):
+        if self.pyb is not None:
+            self.pyb.close()
+            self.pyb = None
+
+
+    # 上传文件
+    def upload_file(self, local_path, remote_path, local_path_is_file=True):
+        """
+        local_path:电脑本地路径
+        remote_path:单片机路径,/开头表示根目录
+        """
+        try:
+            if local_path_is_file:
+                # 打开文件并读取内容
+                with open(local_path, 'rb') as file:
+                    content = file.read()
+            else:
+                content = local_path
+
+            # 创建一个 Files 对象用于文件操作
+            files = ampy.files.Files(self.pyb)
+            # 将文件内容写入 MicroPython 设备
+            files.put(remote_path, content)
+        except Exception as e:
+            print(f"An error occurred while upload_file: {e}")
+
+    # 获取远程文件
+    def get_file_from_device(self, remote_filename, local_filename):
+        try:
+            # 创建一个 Files 对象用于文件操作
+            files = ampy.files.Files(self.pyb)
+            # 获取远程文件内容
+            content = files.get(remote_filename)
+            # 将内容写入本地文件
+            with open(local_filename, 'wb') as file:
+                file.write(content)
+            print(f"文件 {remote_filename} 下载成功并保存为 {local_filename}")
+        except Exception as e:
+            print(f"get_file_from_device 发生错误: {e}")
+
+
+# 远程更新脚本
+class RemoteUpdate(object):
+    # instance = None
+    # init_flag = None
+
+    def __init__(self, port_name=None, top_windows=None, windows=None, is_test=False):
+        # if self.init_flag:
+        #     return
+        # else:
+        #     self.init_flag = True
+        self.is_test = is_test
+        self.port_name = port_name
+        self.top_windows = top_windows
+        self.windows = windows
+        self.ampy_scribe = None
+        self.root = os.getcwd()
+        self.value = 0
+        # 基础数据mode
+        self.data_mode = DataMode()
+        self.mcu_code_path = "{}\micropython_online_code\mcu_code".format(os.getcwd())
+
+    def send_log(self, text):
+        print(text)
+        pass
+
+    def show_text_info(self, text):
+        print(text)
+        if self.windows:
+            self.windows.text_info_sign.emit({"text": text})
+
+    def show_progress_bar(self, add_value=None, value=None):
+        if self.windows:
+            if value:
+                self.windows.progress_bar_sign.emit(value)
+            else:
+                self.value += add_value
+                self.windows.progress_bar_sign.emit(self.value)
+
+    def check_windows_state(self):
+        if self.windows:
+            if self.windows.state != 1:
+                self.show_text_info("已取消")
+                raise "主动停止"
+
+    def run(self):
+        """
+        步骤:
+        1、进行设备终止,并连接
+        2、获取远程文件的md5合集
+        3、比对线上合集
+        4、整理差异内容,以及对应文件的has校验码
+        5、下发文件到临时目录
+        6、运行远程更新脚本
+        7、自动重启
+        """
+        self.show_progress_bar(add_value=5)
+        self.show_text_info("开始检查处理,停止MCU")
+        self.stop_mcu()
+        time.sleep(0.5)
+        self.connect_pyboard()
+        if self.ampy_scribe is None:
+            self.show_text_info("串口链接失败,已退出")
+            return
+
+        # 删除远程的临时文件
+        self.show_text_info("开始 删除远程的临时文件")
+        self.remove_remote_files()
+        self.show_progress_bar(add_value=5)
+        self.show_text_info("开始 获取MCU文件的md5合集")
+        # 获取远程文件的md5合集
+        micropython_file_dict = self.get_remote_has()
+        if micropython_file_dict is None:
+            self.show_text_info("获取MCU文件的md5失败")
+            return
+
+        print("==============micropython_file_dict")
+        print(micropython_file_dict)
+
+        # 获取线上的数据
+        self.show_text_info("开始 获取线上的数据")
+        online_files_dict = self.check_resources_download()
+        self.show_progress_bar(add_value=5)
+        if not online_files_dict:
+            self.show_text_info("开始 获取线上的数据  失败")
+            return
+        # 线上数据与单片机文件进行比对
+        print("==============online_files_dict")
+        print(online_files_dict)
+
+        # 生成差异文件
+        self.show_text_info("比对差异文件数据")
+        differential_files_dict = self.get_differential_files(micropython_file_dict=micropython_file_dict,
+                                                              online_files_dict=online_files_dict)
+        print("==============differential_files_dict")
+        print(differential_files_dict)
+        ## 动态创建文件夹路径
+        # self.create_all_folder()
+        if not differential_files_dict:
+            self.show_progress_bar(value=95)
+            self.show_text_info("MCU文件为最新无需更新,开始重启")
+            self.restart()
+            self.show_text_info("开始断开连接")
+            self.ampy_scribe.close()
+            # 尝试连接MCU
+            self.mcu_connect()
+            self.show_text_info("完成")
+            self.show_progress_bar(value=100)
+            return
+        # 移动差异文件
+        self.show_text_info("开始下发差异文件,总数:{}".format(len(differential_files_dict)))
+        to_move_files = self.upload_all_file(differential_files_dict)
+        if not to_move_files:
+            self.show_text_info("下发差异文件失败")
+            return
+
+        # 制作更新脚本
+        self.show_text_info("开始MCU主程序更新")
+        result = self.update_from_temp()
+        if result:
+            if "更新成功" in result:
+                self.show_text_info("更新成功,即将重启")
+                print("单片机程序为最新,5秒后重启")
+                # 重启
+                self.restart()
+                self.show_text_info("开始断开连接")
+                self.ampy_scribe.close()
+                # 尝试连接MCU
+                self.mcu_connect()
+                self.show_progress_bar(value=100)
+                return
+
+    # 制作更新脚本
+    def update_from_temp(self):
+        print("制作更新脚本")
+        path = r"{}\custom_plugins\plugins\micropython_update\other_micropython_code\update_from_temp.py".format(
+            os.getcwd())
+        return self.ampy_scribe.run_remote_script(path)
+
+    # 创建所有文件夹路径
+    def create_all_folder(self):
+        path = r"{}\custom_plugins\plugins\micropython_update\other_micropython_code\create_all_folders.py".format(
+            os.getcwd())
+        self.ampy_scribe.run_remote_script(path)
+        print("创建文件夹路径已完成")
+
+    # 批量移动文件到单片机
+    def upload_all_file(self, files_dict):
+        # k 示例:sensor/other_sensor/nrf24.py    settings.py
+        to_move_files = {}
+        f = True
+        total_s = 100 - self.value - 5
+        total_len = len(files_dict)
+        step = int(total_s / total_len)
+
+        for relative_file_path, value in files_dict.items():
+            local_path = "{}\{}".format(self.mcu_code_path, relative_file_path)
+            # 注意路径格式 / 开头
+            remote_path = r"/temp/{}".format(relative_file_path)
+            print("移动文件", local_path, remote_path)
+            try:
+                self.ampy_scribe.upload_file(local_path, remote_path)
+                # to_move_files[remote_path] = value["file_sha256"]
+                # 使用本地has
+                to_move_files[remote_path] = calculate_sha256(local_path)
+                self.show_text_info("文件:{}下发成功".format(relative_file_path))
+            except BaseException as e:
+                self.show_text_info("文件:{}下发失败:{}".format(relative_file_path, e))
+                f = False
+                return None
+            self.show_progress_bar(add_value=step)
+
+        if f:
+            # 创建配置文件
+            print("创建配置文件")
+            self.show_text_info("创建配置md5文件 并开始下发")
+            _to_move_files_config = json.dumps(to_move_files)
+            self.ampy_scribe.upload_file(local_path=_to_move_files_config,
+                                         remote_path=r"/temp/_to_move_files_config.json", local_path_is_file=False)
+            self.show_text_info("创建配置md5文件 并下发成功")
+        if f:
+            return to_move_files
+        else:
+            return None
+
+    # 生成差异文件
+    def get_differential_files(self, micropython_file_dict: dict, online_files_dict: dict):
+        # 生成差异文件
+        ignore_path = ["machine.py", "boot.py"]  # "main.py"
+        differential_files_dict = {}
+        for online_file_path, value in online_files_dict.items():
+            if online_file_path in ignore_path:
+                continue
+            if online_file_path not in micropython_file_dict:
+                differential_files_dict[online_file_path] = {"file_sha256": value["file_sha256"]}
+                print("文件不存在:{}".format(online_file_path))
+            else:
+                if value["file_sha256"] != micropython_file_dict[online_file_path]["file_sha256"]:
+                    differential_files_dict[online_file_path] = {"file_sha256": value["file_sha256"]}
+                else:
+                    print("文件相同:{}".format(online_file_path))
+
+        return differential_files_dict
+
+    # 删除远程的临时文件
+    def remove_remote_files(self):
+        script_path = r"{}\custom_plugins\plugins\micropython_update\other_micropython_code\delete_all_temp.py".format(
+            self.root)
+        result = self.ampy_scribe.run_remote_script(script_path)
+        print(result)
+
+    def run_run_remote_script_restart(self):
+        script_path = r"{}\custom_plugins\plugins\micropython_update\other_micropython_code\restart.py".format(
+            self.root)
+        self.ampy_scribe.run_remote_script(script_path)
+
+    # 重启单片机
+    def restart(self):
+        try:
+            # 使用上下文管理器确保线程池会正确关闭
+            with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
+                future = executor.submit(self.run_run_remote_script_restart)
+                try:
+                    result = future.result(timeout=1)  # 等待最多5秒
+                except BaseException as e:
+                    print("自动超时")
+                finally:
+                    # 如果需要立即释放资源,可以显式地调用 shutdown。
+                    # 在 with 语句块结束时也会自动调用 shutdown。
+                    # executor.shutdown(wait=False)
+                    print("shutdown")
+            print("-----------end--------------")
+        except BaseException as e:
+            print(e)
+            print("程序遇到了一个致命错误")
+
+    def mcu_connect(self):
+        self.show_text_info("5秒后 尝试连接MCU进行恢复")
+        time.sleep(5)
+        self.top_windows.mcu.to_connect_com(port_name=self.port_name)
+        self.show_progress_bar(value=100)
+
+    # 检查file_sha256 并进行下载更新文件
+    def check_resources_download(self):
+        if self.is_test is False:
+            if not settings.IsLogin:
+                print("当前未登录")
+                return
+        # 请求获取配置文件
+        online_data = self.data_mode.get_all_resource_config(plugins_name="plugins_micropython")
+        # print("check_resources_download")
+        # print(data)
+        if not online_data:
+            text = "check_resources_download  data为空"
+            self.send_log(text)
+        elif "data" not in online_data:
+            text = "check_resources_download  data不存在"
+            self.send_log(text)
+        else:
+            # 步骤:
+            # 1、获取线上数据
+            # 2、如本地MD5不同,但是修改时间比线上的最新,则也不更新下载。
+            for relative_file_path, value in online_data["data"].items():
+                file_path = "{}\{}".format(self.mcu_code_path, relative_file_path)
+                if os.path.exists(file_path):
+                    file_has256 = calculate_sha256(file_path)
+                    if file_has256 != value["file_sha256"]:
+                        file_modified_time = get_modified_time(file_path)
+                        if compare_two_times(file_modified_time, online_data["update_time"]) == "left_new":
+                            continue
+                        else:
+                            text = "has256 不同 开始下载:{}".format(file_path)
+                            self.send_log(text)
+                            download_file(url=value["url"], file_path=file_path)
+                    else:
+                        pass
+                else:
+                    text = "文件不存在 开始下载:{}".format(file_path)
+                    self.send_log(text)
+                    download_file(url=value["url"], file_path=file_path)
+        if online_data:
+            return online_data["data"]
+        return None
+
+    def get_remote_has(self):
+        self.ampy_scribe: AmpyScribe
+        script_path = r"{}\custom_plugins\plugins\micropython_update\other_micropython_code\get_remote_has256.py".format(
+            self.root)
+        result = self.ampy_scribe.run_remote_script(script_path)
+        if result is None:
+            return None
+        has_dict = {}
+        try:
+            result_list = result.split("\n")
+            for i in result_list:
+                if "@" not in i:
+                    continue
+                path, _has = i.split("@")
+                has_dict[path[1:]] = {"file_sha256": _has.replace("\r", "")}
+            return has_dict
+        except BaseException as e:
+            return None
+
+    def stop_mcu(self):
+        if self.is_test:
+            return
+        if self.ampy_scribe is None:
+            self.top_windows.mcu.stop_mcu()
+
+    def connect_pyboard(self):
+        if self.is_test:
+            self.ampy_scribe = AmpyScribe(port_name=self.port_name, windows=None)
+            print("ampy_scribe 接连成功")
+            return
+
+        if self.ampy_scribe is None:
+            if self.port_name:
+                pass
+            else:
+                self.port_name = self.top_windows.mcu.port_name
+
+            print("port_name:", self.port_name)
+            if self.port_name:
+                self.top_windows.mcu.close_connect()
+                time.sleep(3)
+                try:
+                    self.ampy_scribe = AmpyScribe(port_name=self.port_name, windows=None)
+                    print("ampy_scribe 接连成功")
+                except BaseException as e:
+                    print("ampy_scribe 连接失败", e)
+                    self.ampy_scribe = None
+
+    def __new1__(cls, *args, **kwargs):
+        """如果当前没有实例时,调用父类__new__方法,生成示例,有则返回保存的内存地址。"""
+        if not cls.instance:
+            cls.instance = super().__new__(cls)
+        return cls.instance

+ 0 - 0
python/custom_plugins/plugins/micropython_update/other_micropython_code/__init__.py


+ 23 - 0
python/custom_plugins/plugins/micropython_update/other_micropython_code/create_all_folders.py

@@ -0,0 +1,23 @@
+import uos
+
+def mkdir_p(path):
+    """递归创建目录"""
+    try:
+        uos.mkdir(path)
+    except :
+        pass
+
+
+mkdir_p('mode')
+mkdir_p('oled')
+mkdir_p('sensor')
+mkdir_p('sensor/base_mode')
+mkdir_p('sensor/mode')
+mkdir_p('sensor/other_sensor')
+mkdir_p('temp')
+mkdir_p('temp/mode')
+mkdir_p('temp/oled')
+mkdir_p('temp/sensor')
+mkdir_p('temp/sensor/base_mode')
+mkdir_p('temp/sensor/mode')
+mkdir_p('temp/sensor/other_sensor')

+ 50 - 0
python/custom_plugins/plugins/micropython_update/other_micropython_code/delete_all_temp.py

@@ -0,0 +1,50 @@
+import uos
+
+
+def rmdir_recursive(path):
+    """Recursively delete a directory and all its contents."""
+    try:
+        # 列出目录下的所有文件和子目录
+        for entry in uos.ilistdir(path):
+            full_path = path + '/' + entry[0]
+            if entry[1] == 0x4000:  # 目录标志
+                # 如果是子目录,递归调用自身
+                rmdir_recursive(full_path)
+            else:
+                # 如果是文件,则直接删除
+                # print("Deleting file:", full_path)
+                uos.remove(full_path)
+        # 删除空的顶级目录
+        # print("Deleting directory:", path)
+        uos.rmdir(path)
+    except OSError as e:
+        pass
+        # print("Error deleting directory:", path, "Error:", e)
+
+
+def mkdir_p(path):
+    """递归创建目录"""
+    try:
+        uos.mkdir(path)
+    except:
+        pass
+
+
+# 使用函数删除指定目录及其所有内容
+rmdir_recursive('/temp')
+
+mkdir_p('other_config')
+mkdir_p('mode')
+mkdir_p('oled')
+mkdir_p('sensor')
+mkdir_p('sensor/base_mode')
+mkdir_p('sensor/mode')
+mkdir_p('sensor/other_sensor')
+mkdir_p('temp')
+mkdir_p('temp/other_config')
+mkdir_p('temp/mode')
+mkdir_p('temp/oled')
+mkdir_p('temp/sensor')
+mkdir_p('temp/sensor/base_mode')
+mkdir_p('temp/sensor/mode')
+mkdir_p('temp/sensor/other_sensor')

+ 56 - 0
python/custom_plugins/plugins/micropython_update/other_micropython_code/get_remote_has256.py

@@ -0,0 +1,56 @@
+# micropython 中使用
+import time
+import uhashlib
+import uos
+import ubinascii  # 导入用于转换二进制到十六进制字符串的模块
+
+
+def calculate_sha256(filename):
+    h = uhashlib.sha256()
+    with open(filename, 'rb') as f:
+        while True:
+            chunk = f.read(1024)
+            if not chunk:
+                break
+            h.update(chunk)
+
+    # 获取摘要并转换为十六进制字符串
+    hash_bytes = h.digest()  # 获取摘要的二进制形式
+    return ubinascii.hexlify(hash_bytes).decode()  # 将二进制摘要转换为十六进制字符串
+
+
+# 遍历目录
+all_file_data = {}
+
+
+def list_directory(path='.', list_all=False):
+    try:
+        for entry in uos.ilistdir(path):
+            name = entry[0]
+            if name == "__pycache__" or name == "temp":
+                continue
+            type_code = entry[1]
+            if type_code == 0x4000:  # 目录标志位,可能根据实现不同
+                _type = "dir"
+            else:
+                _type = "file"
+            file_path = "{}/{}".format(path, name)
+            # print("_type:{},  {}/{}".format(_type, path, name))
+            all_file_data[file_path] = {"_type": _type,
+                                        "file": name,
+                                        }
+            if _type == "dir":
+                list_directory(file_path, list_all=True)
+    except OSError as e:
+        # print("path", path)
+        # print(f"{path} Error listing directory: {e}")
+        pass
+
+
+# 示例使用
+list_directory('', list_all=True)
+# 执行你想要测量的部分代码
+for k, v in all_file_data.items():
+    if v["_type"] == "file":
+        md5_checksum = calculate_sha256(k)
+        print("{}@{}".format(k, md5_checksum))

+ 24 - 0
python/custom_plugins/plugins/micropython_update/other_micropython_code/restart.py

@@ -0,0 +1,24 @@
+import time
+
+import machine
+from sensor.led import LED
+import uasyncio as asyncio
+from machine import WDT
+
+led = LED(25)
+n = 20
+while n:
+    n -= 1
+    time.sleep(0.05)
+    led.toggle()
+
+wdt = WDT(timeout=500)  # 超时时间单位为毫秒
+# machine.reset()
+
+#
+# # 上传重启脚本
+# ampy --port COM37 put test/reset.py
+
+# # 运行重启脚本
+# ampy --port COM37 run /re_set.py
+# /reset.py

+ 101 - 0
python/custom_plugins/plugins/micropython_update/other_micropython_code/update_from_temp.py

@@ -0,0 +1,101 @@
+import uos
+import json
+import time
+import uhashlib
+import ubinascii  # 导入用于转换二进制到十六进制字符串的模块
+
+
+class Main():
+    def __init__(self):
+        pass
+
+    def get_has256_config(self):
+        try:
+            with open("/temp/_to_move_files_config.json", "rb") as f:
+                data = f.read()
+                data = data.decode("utf-8")
+                data = json.loads(data)
+        except:
+            data = {}
+        return data
+
+    def calculate_sha256(self, path):
+        h = uhashlib.sha256()
+        with open(path, 'rb') as f:
+            while True:
+                chunk = f.read(1024)
+                if not chunk:
+                    break
+                h.update(chunk)
+
+        # 获取摘要并转换为十六进制字符串
+        hash_bytes = h.digest()  # 获取摘要的二进制形式
+        return ubinascii.hexlify(hash_bytes).decode()  # 将二进制摘要转换为十六进制字符串
+
+    def run(self):
+        """
+        步骤:
+        1、检查需要更新的配置文件
+        2、检查文件的md5值
+        3、移动文件到指定目录进行替换
+        """
+        config_data = self.get_has256_config()
+        if not config_data:
+            print("错误")
+            print("config_data 为空或异常")
+            return
+        f = True
+        for file_path, value in config_data.items():
+            _has256 = self.calculate_sha256(file_path)
+            if _has256 != value:
+                print("文件:{} has不一致".format(file_path))
+                f = False
+        if not f:
+            print("错误")
+            return
+        # ============移动文件进行替换
+        for file_path in config_data:
+            src_path = file_path
+            dst_path = file_path.replace("/temp", "")
+            self.move_file(src_path, dst_path)
+        print("更新成功")
+
+    def move_file(self, src_path, dst_path):
+        """Move a file from src_path to dst_path, overwriting if the destination exists."""
+        try:
+            # 检查目标文件是否存在
+            if self.check_exists(dst_path):
+                # print(f"Destination file {dst_path} exists. Removing it...")
+                uos.remove(dst_path)  # 删除目标文件以允许覆盖
+
+            # 复制文件内容
+            with open(src_path, 'rb') as src_file:
+                with open(dst_path, 'wb') as dst_file:
+                    while True:
+                        chunk = src_file.read(1024)  # 分块读取和写入以节省内存
+                        if not chunk:
+                            break
+                        dst_file.write(chunk)
+
+            # print(f"File copied from {src_path} to {dst_path}.")
+
+            # 可选:删除源文件(如果需要移动而非复制)
+            uos.remove(src_path)
+            # print(f"Source file {src_path} removed.")
+        except OSError as e:
+            print("文件移动错误")
+            print("Error moving file:", e)
+
+    def check_exists(self, path):
+        """Check if a file or directory exists."""
+        try:
+            # 尝试获取路径的状态
+            uos.stat(path)
+            return True
+        except OSError:
+            # 如果发生OSError异常,说明路径不存在
+            return False
+
+
+if __name__ == '__main__':
+    Main().run()

+ 0 - 0
python/custom_plugins/plugins_mode/__init__.py


+ 638 - 0
python/custom_plugins/plugins_mode/detail_generate_base.py

@@ -0,0 +1,638 @@
+import settings
+import os
+
+try:
+    is_test_plugins = settings.is_test_plugins
+except:
+    is_test_plugins = False
+
+if is_test_plugins:
+    from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+else:
+    from plugins_mode.pic_deal import PictureProcessing
+
+from PIL import Image
+import shutil
+from service.base import get_images, check_path, get_image_mask
+from natsort import ns, natsorted
+# import math
+from PIL import ImageFont
+import settings
+
+
+class DetailBase(object):
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, excel_data=None,
+                 assigned_page_list=None, output_queue=None):
+        self.goods_no = goods_no
+        self.output_queue = output_queue
+        self.out_put_dir = out_put_dir
+        self.deal_pic_func_list = []
+        self.goods_no_value = goods_no_value
+        self.root = ""
+        self.windows = windows
+        self.template_name = None
+
+        print(goods_no_value)
+        # 重新解析为新的数据结构
+        self.data = {}
+        self.detailed_images = []
+        self.assigned_page_list = assigned_page_list
+        self.overlay_pic_dict = {}
+        self.init()
+        # for goods_art_no_dict in self.goods_no_value["货号资料"]:
+        #     print(goods_art_no_dict)
+        #
+        # raise 1
+
+        if excel_data:
+            ig_keys = ["模板名称"]
+            for k, v in excel_data.items():
+                if k not in ig_keys:
+                    self.goods_no_value[k] = v
+
+    def del_detail_folder(self):
+        out_path = "{out_put_dir}/{goods_no}".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        if not os.path.exists(out_path):
+            return
+        try:
+            shutil.rmtree(out_path)
+        except BaseException as e:
+            print("删除文件夹失败", e)
+
+    def run_all(self):
+        if self.template_name:
+            self.out_put_dir = "{}/{}".format(self.out_put_dir, self.template_name)
+
+        print("===================detailed_images=================")
+        # 如果没有指定页面,则删除指定目录下的对应的详情文件夹
+        if not self.assigned_page_list:
+            self.del_detail_folder()
+        detailed_images = self.deal_details()
+        self.create_folder(self.out_put_dir)
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(detail_path)
+        self.save_to_png(detailed_images=detailed_images, detail_path=detail_path)
+        # 生成拼接图
+        self.generate_spliced_picture()
+
+        # ------------移动其他图片---------------------
+        # 获取主图模板列表
+        main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)["main_pic_path_list"]
+        if not main_pic_path_list:
+            self.move_other_pic(move_main_pic=True)
+        else:
+            self.move_other_pic(move_main_pic=True)
+            if not self.assigned_page_list:
+                self.deal_all_main_pic()
+            else:
+                if "主图" in self.assigned_page_list:
+                    self.deal_all_main_pic()
+        return True
+
+    # 生成各个详情图切片
+    def deal_details(self):
+        detailed_images = []
+        for index, func in enumerate(self.deal_pic_func_list):
+            image_pp = func()
+            if not self.assigned_page_list:
+                self.image_list_append(detailed_images, image_pp)
+            else:
+                index = "{}".format(index + 1)
+                if index in self.assigned_page_list:
+                    self.image_list_append(detailed_images, image_pp)
+                else:
+                    self.image_list_append(detailed_images, {"mes": "不生成"})
+
+        return [x for x in detailed_images if x]
+
+    # 生成拼接的图片
+    def generate_spliced_picture(self):
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        if not os.path.exists(detail_path):
+            return
+        detailed_images = []
+        for image_data in get_images(detail_path):
+            detailed_images.append(PictureProcessing(image_data["file_path"]))
+        # 生成拼接图
+        img = self.add_pic(detailed_images)
+        join_path = "{out_put_dir}/{goods_no}/拼接图".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(join_path)
+        img.save("{}/1.jpg".format(join_path), format="JPEG")
+
+    def image_list_append(self, image_list: list, data):
+        self.check_state_end()
+        if isinstance(data, list):
+            image_list.extend(data)
+        else:
+            image_list.append(data)
+
+    def save_to_png(self, detailed_images, detail_path):
+        self.check_state_end()
+        for index, pp in enumerate(detailed_images):
+            if isinstance(pp, dict):
+                continue
+            pp.im.save("{}/{}({}).png".format(detail_path, self.goods_no, str(index + 11).zfill(2)))
+
+    def check_state_end(self):
+        if self.windows is not None:
+            if self.windows.state == 99:
+                raise "用户主动取消"
+
+    @classmethod
+    def get_temp_pic_info(cls, root):
+        """
+        获取详情页模板中的信息
+        """
+        main_pic_list = []
+        mask_pic_list = []
+        if os.path.exists(r"{}\main_image".format(root)):
+            for _name in os.listdir(r"{}\main_image".format(root)):
+                _path = r"{}\main_image\{}".format(root, _name)
+                if os.path.isdir(_path):
+                    main_pic_list.append([x["file_path"] for x in get_images(_path)])
+                    mask_pic_list.append(
+                        [x["file_path"] for x in get_image_mask(_path)]
+                    )
+        _l = get_images(r"{}\show".format(root))
+        temp_pic_path = _l[0]["file_path"] if _l else None
+
+        other_pic_list = [x["file_path"] for x in get_images(r"{}".format(root))]
+        return {
+            "main_pic_path_list": main_pic_list,
+            "temp_pic_path": temp_pic_path,
+            "mask_pic_list": mask_pic_list,
+            "other_pic_path_list": other_pic_list,
+        }
+
+    def init(self):
+        for goods_art_no_value in self.goods_no_value["货号资料"]:
+            self.data[goods_art_no_value["货号"]] = {"pics": goods_art_no_value["pics"],
+                                                   "pic_is_deal": {}
+                                                   }
+
+    def get_text_value(self, key, subsection_len=0):
+        text = ""
+        if key in self.goods_no_value:
+            if self.goods_no_value[key]:
+                text = str(self.goods_no_value[key])
+                text = text.replace(r"\n", "\n")
+
+        if key in ["跟高", "鞋宽", "帮高", "脚掌围", "鞋长"]:
+            if text:
+                text = text.split(".")[0]
+
+        if subsection_len != 0:
+            text = text.split("\n")
+            text = [x for x in text if x]
+            if len(text) == 2:
+                text_1 = text[0]
+                text_2 = text[1]
+                return text_1, text_2
+            else:
+                if text:
+                    text_1 = text[0]
+                else:
+                    text_1 = ""
+                text_2 = ""
+                return text_1, text_2
+
+        return text
+
+    def create_folder(self, path):
+        if not os.path.exists(path):
+            os.makedirs(path)
+
+    def get_all_process_pics(self):
+        """
+        获取所有颜色的过程图片
+        data = [
+            {"货号": "",
+             "素材": [{
+                 "名称": "俯视",
+                 "抠图": "路径1",
+                 "阴影": "路径2"
+             }, ]},
+        ]
+        """
+        return_data = []
+        for goods_art_no in self.data:
+            goods_art_no_dict = {"货号": goods_art_no,
+                                 "素材": [],
+                                 }
+
+            # 图片数据重新排序
+            pic_data = []
+            for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+                root_path, file_name = os.path.split(pic_path)
+                pic_data.append(file_name)
+
+            pic_data = natsorted(pic_data, alg=ns.PATH)
+
+            for file_name in pic_data:
+                if "阴影" in file_name:
+                    _, action_name, _ = file_name.split("_")
+                    pic_path = self.data[goods_art_no]["pics"]["{}-阴影".format(action_name)]
+                    pic_cutout_path = self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)]
+                    if os.path.exists(pic_path) and os.path.exists(pic_cutout_path):
+                        goods_art_no_dict["素材"].append({"名称": action_name, "抠图": pic_cutout_path, "阴影": pic_path})
+            return_data.append(goods_art_no_dict)
+
+        return return_data
+
+    def get_overlay_pic_from_dict(self, goods_art_no, color_name, bg_color) -> PictureProcessing:
+        self.check_state_end()
+        # 增加逻辑,获取任意货号下的组合图
+        if "组合" in color_name:
+            goods_art_no, color_name = self.get_all_scene_list(goods_art_no, color_name)
+
+        key = "{}-{}-{}".format(goods_art_no, color_name, bg_color)
+        if key in self.overlay_pic_dict:
+            return self.overlay_pic_dict[key]
+
+        if goods_art_no in self.data:
+            for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+                if "阴影" in pic_name:
+                    action_name = pic_name.replace("-阴影", "")
+                    if action_name == color_name:
+                        pp1 = PictureProcessing(pic_path)
+                        pp2 = PictureProcessing(self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)])
+                        pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(mode="pixel", base="width",
+                                                                                      value=1600)
+                        self.overlay_pic_dict[key] = pp1
+
+        if key in self.overlay_pic_dict:
+            return self.overlay_pic_dict[key]
+
+    def image_init(self, bg_color=(246, 246, 246)):
+        # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
+        for goods_art_no in self.data:
+            for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+                if "阴影" in pic_name:
+                    action_name = pic_name.replace("-阴影", "")
+                    pp1 = PictureProcessing(pic_path)
+                    pp2 = PictureProcessing(self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)])
+                    pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(mode="pixel", base="width",
+                                                                                  value=1600)
+                    self.data[goods_art_no]["pic_is_deal"][action_name] = pp1
+
+    # 获取任意货号的场景图,优先取指定货号;
+    # 调整,按顺序从货号列表中提取所有组合图
+    def get_all_scene_info(self, goods_art_no):
+        data = []
+        # 收集所有组合图
+        # 找任意一个有组合图的货号
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            _goods_art_no = goods_art_no_dict["货号"]
+            _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
+            for _view_name in _view_name_list:
+                if "组合" not in _view_name:
+                    continue
+                return _goods_art_no
+        return goods_art_no
+
+    def get_all_scene_list(self, goods_art_no, view_name: str):
+        if "组合" == view_name:
+            view_name = "组合1"
+        try:
+            view_index = int(view_name.replace("组合", "")) - 1
+        except:
+            return goods_art_no, "无法匹配"
+
+        data = []
+        # 收集所有组合图
+        # 找任意一个有组合图的货号
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            _goods_art_no = goods_art_no_dict["货号"]
+            _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
+            for _view_name in _view_name_list:
+                if "组合" not in _view_name:
+                    continue
+                data.append(
+                    {"goods_art_no": _goods_art_no,
+                     "view_name": _view_name
+                     }
+                )
+
+        if len(data) <= view_index:
+            return goods_art_no, "无法匹配"
+        else:
+            return data[view_index]["goods_art_no"], data[view_index]["view_name"]
+
+    def image_one_pic(self, goods_art_no, name, bg_color=None, return_orign=None):
+        # 增加逻辑,获取任意货号下的组合图
+        if "组合" in name:
+            goods_art_no, name = self.get_all_scene_list(goods_art_no, name)
+            print("324 goods_art_no, name", goods_art_no, name)
+
+        # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
+        for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+            if "阴影" in pic_name:
+                action_name = pic_name.replace("-阴影", "")
+                if name != action_name:
+                    continue
+                pp1 = PictureProcessing(pic_path)
+                pp2 = PictureProcessing(self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)])
+                if not return_orign:
+                    pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(mode="pixel", base="width",
+                                                                                  value=1600)
+                    return pp1
+                else:
+                    return pp1, pp2
+
+        if not return_orign:
+            return None
+        else:
+            return None, None
+
+    def move_other_pic(self, move_main_pic=True):
+        # ------------------------------移动其他图片------------------------------
+        goods_no_main_pic_number = 0
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            if "800x800" not in goods_art_no_dict:
+                continue
+            if not goods_art_no_dict["800x800"]:
+                continue
+
+            goods_art_no = ""
+            if "编号" in goods_art_no_dict:
+                if goods_art_no_dict["编号"]:
+                    goods_art_no = goods_art_no_dict["编号"]
+            if not goods_art_no:
+                goods_art_no = goods_art_no_dict["货号"]
+            # print("goods_art_no:", goods_art_no)
+            # 移动颜色图=====================
+            goods_art_no_f = "{}/{}/{}".format(self.out_put_dir, self.goods_no, goods_art_no)
+            self.create_folder(goods_art_no_f)
+            # 放入一张主图
+            old_pic_path_1 = goods_art_no_dict["800x800"][0]
+            shutil.copy(old_pic_path_1,
+                        "{}/{}{}".format(goods_art_no_f, goods_art_no, os.path.splitext(old_pic_path_1)[1]))
+
+            # 把其他主图放入作为款号图=====================
+            if move_main_pic:
+                for pic_path in goods_art_no_dict["800x800"]:
+                    goods_no_main_pic_number += 1
+                    e = os.path.splitext(pic_path)[1]
+                    shutil.copy(pic_path,
+                                "{out_put_dir}/{goods_no}/{goods_no}({goods_no_main_pic_number}){e}".format(
+                                    out_put_dir=self.out_put_dir, goods_no=self.goods_no,
+                                    goods_no_main_pic_number=str(goods_no_main_pic_number + 10).zfill(2),
+                                    e=e))
+
+    def deal_all_main_pic(self):
+        """
+        处理主图模板,如存在出图模板则进行对应处理
+        """
+        # 获取主图模板列表
+        all_main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)["main_pic_path_list"]
+        if not all_main_pic_path_list:
+            return
+        mask_pic_list = DetailBase.get_temp_pic_info(root=self.root)["mask_pic_list"]
+        data = self.get_all_process_pics()
+        print("========deal_all_main_pic=========主图相关素材:")
+
+        view_list = ["组合", "组合2", "组合3", "组合4", "组合5", "组合6", "俯视", "侧视", "后跟", "鞋底", "内里", ]
+
+        for _index, main_pic_path_list in enumerate(all_main_pic_path_list):
+            self.check_state_end()
+            out_path_root = "{out_put_dir}/{goods_no}/main_image_{_index}".format(
+                out_put_dir=self.out_put_dir,
+                goods_no=self.goods_no,
+                _index=_index
+            )
+            check_path(out_path_root)
+            if mask_pic_list[_index]:
+                mask_pic = mask_pic_list[_index][0]
+            else:
+                mask_pic = None
+            goods_no_main_pic_number = 10
+            # g_index 为第几个颜色货号
+            for g_index, goods_art_no_dict in enumerate(data):
+                goods_art_no = goods_art_no_dict["货号"]
+                # =====================重新指定=================================
+                _material_sort_dict = {}
+                for index, material_dict in enumerate(goods_art_no_dict["素材"]):
+                    name = material_dict["名称"]
+                    _material_sort_dict[name] = material_dict
+
+                # ======================================================
+                file_name_index = -1
+                for view_name in view_list:
+                    # 组合图比较特殊,为全局获取
+                    if g_index != 0:
+                        if "组合" in view_name:
+                            continue
+
+                    if view_name not in _material_sort_dict:
+                        continue
+                    self.check_state_end()
+                    pp_jpg, pp_png = self.image_one_pic(goods_art_no, view_name, bg_color=None, return_orign=True)
+                    if not pp_jpg:
+                        continue
+
+                    file_name_index += 1
+                    # 获取对应主图模板
+                    if len(main_pic_path_list) < file_name_index + 1:
+                        main_pic_path = main_pic_path_list[-1]
+                    else:
+                        main_pic_path = main_pic_path_list[file_name_index]
+                    pp_bg = PictureProcessing(main_pic_path)
+                    original_width = pp_bg.width
+
+                    if original_width != 1600:
+                        pp_bg = pp_bg.resize(value=1600)
+
+                    if mask_pic:
+                        mask_bg = PictureProcessing(mask_pic)
+                        mask_bg = mask_bg.resize(value=1600)
+                        mask_box_im = mask_bg.get_im()
+                        box_size = mask_box_im.getbbox()
+                        result_image = mask_box_im.crop(box_size)
+                        mask_width, mask_height = result_image.size
+                        mask_x, mask_y = box_size[0], box_size[1]
+                    else:
+                        mask_width, mask_height = pp_bg.size
+                        mask_width, mask_height = int(mask_width * 12 / 16), int(mask_height * 12 / 16)
+                        mask_x, mask_y = int((pp_bg.size[0] - mask_width) / 2), int((pp_bg.size[1] - mask_height) / 2)
+
+                    if view_name != "后跟":
+                        pp_jpg = pp_jpg.resize(base_by_box=(mask_width, mask_height))
+                        pp_png = pp_png.resize(base_by_box=(mask_width, mask_height))
+
+                        # 计算粘贴的位置 mask的位置+图片在mask中的位置
+                        p_x = mask_x + int((mask_width - pp_jpg.width) / 2)
+                        p_y = mask_y + int((mask_height - pp_jpg.height) / 2)
+                        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="nw",
+                                                             value=(p_x, p_y), top_png_img=pp_png)
+                    else:
+                        new_mask_width, new_mask_height = int(mask_width / 1.6), int(mask_height / 1.6)
+
+                        pp_jpg = pp_jpg.resize(base_by_box=(new_mask_width, new_mask_height))
+                        pp_png = pp_png.resize(base_by_box=(new_mask_width, new_mask_height))
+                        new_mask_x = int((mask_width - new_mask_width) / 2 + mask_x)
+                        new_mask_y = int((mask_height - new_mask_height) / 2 + mask_y)
+
+                        # 计算粘贴的位置 mask的位置+图片在mask中的位置
+                        p_x = new_mask_x + int((new_mask_width - pp_jpg.width) / 2)
+                        p_y = new_mask_y + int((new_mask_height - pp_jpg.height) / 2)
+
+                        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="nw", value=(p_x, p_y),
+                                                             top_png_img=pp_png)
+
+                    goods_no_main_pic_number += 1
+                    out_pic_path = "{out_path_root}/{goods_no}({goods_no_main_pic_number}){pic_mode}".format(
+                        out_path_root=out_path_root,
+                        goods_no=self.goods_no,
+                        goods_no_main_pic_number=goods_no_main_pic_number,
+                        pic_mode=settings.OUT_PIC_MODE,
+                    )
+
+                    if settings.OUT_PIC_FACTOR > 1.0:
+                        print("图片锐化处理")
+                        pp_bg = pp_bg.sharpen_image(factor=settings.OUT_PIC_FACTOR)
+
+                    if original_width < 1600:
+                        pp_bg = pp_bg.resize(value=original_width)
+
+                    print("392  out_pic_path", out_pic_path)
+                    if settings.OUT_PIC_MODE == ".jpg":
+                        pp_bg.save_as_rgb(out_pic_path)
+                    else:
+                        pp_bg.save_as_png(out_pic_path)
+
+    def add_pic(self, detailed_images):
+        self.check_state_end()
+        todo_detailed_images = []
+        detailed_images = [x for x in detailed_images if x]
+        if not detailed_images:
+            return
+        for i in detailed_images:
+            if isinstance(i, list):
+                for n in i:
+                    todo_detailed_images.append(n)
+            else:
+                todo_detailed_images.append(i)
+
+        page_len = 0
+        for index, pp in enumerate(todo_detailed_images):
+            page_len += pp.height
+        bg_im = Image.new("RGB", (pp.width, page_len), (255, 255, 255))
+        n = 0
+        for index, pp in enumerate(todo_detailed_images):
+            bg_im.paste(pp.im, (0, n))
+            n += pp.height
+        return bg_im
+
+    # 通用方法,用于写文字
+    def add_text_list(self, text_list, spacing=5, base="wn", base_width=1600):
+        text_list = [x for x in text_list if x["text"]]
+        # print(text_list)
+        # spacing 行间距
+        text_image_list = []
+        max_w = 0
+        total_h = 0
+
+        for text_data in text_list:
+            _pp = PictureProcessing("RGBA", (base_width, 1200), (255, 255, 255, 0))
+            if base == "wn" or base == "nw":
+                align = "left"
+                anchor = None
+                value = (0, 250)
+            if base == "cn" or base == "nc":
+                align = "center"
+                anchor = "mm"
+                value = (int(base_width / 2), 250)
+            if base == "en" or base == "ne":
+                align = "right"
+                anchor = "rs"
+                value = (base_width - 10, 250)
+
+            _pp = _pp.get_text_image_advanced(
+                value=value,
+                font=text_data["font"],
+                text=text_data["text"],
+                align=align,
+                anchor=anchor,
+                spacing=5,
+                fill=text_data["fill"],
+                return_mode="min_image",
+                margins=(0, 0, 0, 0)
+            )
+            text_image_list.append(_pp)
+            if _pp.width > max_w:
+                max_w = _pp.width
+
+            total_h += _pp.height
+            if "spacing" in text_data:
+                total_h += text_data["spacing"]
+
+        if not text_image_list:
+            return None
+        #
+        bg = PictureProcessing("RGBA", (max_w, total_h * 3), (0, 0, 0, 0))
+        y = 0
+        for text_image, text_data in zip(text_image_list, text_list):
+            bg = bg.paste_img(top_img=text_image, value=(0, y), base=base)
+            y += spacing + text_image.height
+            if "spacing" in text_data:
+                y += text_data["spacing"]
+
+        bg = bg.crop(mode="min")
+        # _ = bg.paste_img_invert(top_img=PictureProcessing("RGB", (bg.width,bg.height), (255, 255, 255)))
+        # _.show()
+        return bg
+
+    def generate_font_list_to_pic(self):
+        font_path_list = [r"resources\ttf\puhui\Bold.ttf",
+                          r"resources\ttf\puhui\Medium.ttf",
+                          r"resources\ttf\puhui\Heavy.ttf",
+                          r"resources\ttf\puhui\Light.ttf",
+                          r"resources\ttf\puhui\Regular.ttf",
+                          ]
+        text_v_list = [
+            "这是一段话Bold",
+            "这是一段话Medium",
+            "这是一段话Heavy",
+            "这是一段话Light",
+            "这是一段话Regular",
+        ]
+        detailed_images = []
+        for font_path, text in zip(font_path_list, text_v_list):
+            text_list = []
+            for size in range(26, 80, 2):
+                font = ImageFont.truetype(font_path, size)
+                text_list.append({"text": "{}-字号{}".format(text, size),
+                                  "font": font,
+                                  "fill": (110, 110, 110),
+                                  })
+            text_image = self.add_text_list(text_list, spacing=15, base="nw")
+            text_image = text_image.crop(mode="min")
+            text_image = text_image.paste_img_invert(top_img=PictureProcessing("RGB", text_image.size, (255, 255, 255)))
+            detailed_images.append(text_image)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 图片分段,每段至少大于N长度
+    def pp_pic_subsection(self, pp: PictureProcessing, one_height=3200):
+        total_height = pp.height
+        now_height = 0
+        detailed_images = []
+
+        while 1:
+            if now_height + one_height < total_height:
+                h1 = now_height
+                h2 = now_height + one_height
+                bbox = (0, h1, pp.width, h2)
+                # print("bbox1", bbox)
+                detailed_images.append(pp.crop(bbox=bbox))
+                now_height = now_height + one_height
+                continue
+
+            if now_height + one_height >= total_height:
+                h1 = now_height
+                h2 = total_height
+                bbox = (0, h1, pp.width, h2)
+                # print("bbox2", bbox)
+                detailed_images.append(pp.crop(bbox=bbox))
+                break
+
+        return detailed_images

+ 593 - 0
python/custom_plugins/plugins_mode/pic_deal.py

@@ -0,0 +1,593 @@
+import os.path
+
+from PIL import Image, ImageDraw, ImageEnhance
+# import cv2
+import numpy as np
+from blend_modes import multiply
+
+
+class PictureProcessing(object):
+    def __init__(self, *args, **kwargs):
+        self.im: Image
+        if args:
+            p = args[0]
+            if isinstance(p, str):
+                if p == "RGB" or p == "RGBA":
+                    size = int(args[1][0]), int(args[1][1])
+                    self.im = Image.new(args[0], size, args[2])
+                else:
+                    # 设计优先取值处理
+                    _path = args[0]
+                    file_path, file = os.path.split(_path)
+                    custom_path = "{}/custom/{}".format(file_path, file)
+                    if os.path.exists(custom_path):
+                        _path = custom_path
+                    self.im = Image.open(_path)
+
+        elif "im" in kwargs:
+            self.im = kwargs["im"]
+        else:
+            self.im = Image.new(mode="RGB", size=(1600, 500), color=(255, 255, 255))
+
+    def getbbox(self):
+        return self.im.getbbox()
+
+    def expand_bbox(self, bbox, value=100):
+        x1, y1, x2, y2 = bbox[0] - value, bbox[1] - value, bbox[2] + value, bbox[3] + value
+        if x1 < 0:
+            x1 = 0
+        if y1 < 0:
+            y1 = 0
+        bbox = (x1, y1, x2, y2)
+        return [int(x) for x in bbox]
+
+    def show(self):
+        self.im.show()
+
+    def get_size(self):
+        return self.im.size
+
+    def get_im(self):
+        self.im: Image
+        return self.im
+
+    def to_color_2(self, target, blend):  # 正片叠底
+        return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)
+
+    def get_overlay_pic(self, top_img, color=None):
+        # 正片叠底
+        top_img: PictureProcessing
+        image_white = Image.new("RGB", (self.get_size()), color)
+        backdrop_prepped = np.asfarray(image_white.convert('RGBA'))
+        source_prepped = np.asfarray(self.im.convert('RGBA'))
+        blended_np = multiply(backdrop_prepped, source_prepped, 1)
+        img = Image.fromarray(np.uint8(blended_np)).convert('RGB')
+        img.paste(top_img.im, (0, 0), top_img.im)
+        return PictureProcessing(im=img)
+
+    def to_overlay_pic_advance(self, mode="pixel", top_img="", base="nw", value=(0, 0), percentage=(0, 0),
+                               margins=(0, 0, 0, 0), opacity=100, top_png_img=None):
+        # opacity 透明度
+        # 正片叠底
+        _p = PictureProcessing("RGBA", (self.width, self.height), (255, 255, 255, 255))
+        top_img = _p.paste_img(mode=mode,
+                               top_img=top_img,
+                               base=base,
+                               value=value,
+                               percentage=percentage,
+                               margins=margins)
+
+        backdrop_prepped = np.asfarray(self.im.convert('RGBA'))
+        source_prepped = np.asfarray(top_img.im.convert('RGBA'))
+        blended_np = multiply(backdrop_prepped, source_prepped, opacity / 100)
+        im = Image.fromarray(np.uint8(blended_np)).convert('RGB')
+        pp = PictureProcessing(im=im)
+        if top_png_img:
+            pp = pp.paste_img(mode=mode,
+                              top_img=top_png_img,
+                              base=base,
+                              value=value,
+                              percentage=percentage,
+                              margins=margins)
+
+        return pp
+
+    def __to_resize(self, width=None, height=None):
+        _im_x, _im_y = self.get_size()
+        if width and height:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(height)
+            re_x = int(_im_x * re_y / _im_y)
+        img = self.get_im().resize((re_x, re_y))
+        return img
+
+    # 锐化图片
+    def sharpen_image(self, factor=1.0):
+        # 创建一个ImageEnhance对象
+        enhancer = ImageEnhance.Sharpness(self.im)
+        # 应用增强,值为0.0给出模糊图像,1.0给出原始图像,大于1.0给出锐化效果
+        # 调整这个值来增加或减少锐化的程度
+        sharp_img = enhancer.enhance(factor)
+        return PictureProcessing(im=sharp_img)
+
+    @property
+    def width(self):
+        return self.im.width
+
+    @property
+    def height(self):
+        return self.im.height
+
+    @property
+    def size(self):
+        return self.im.size
+
+    def resize(self, mode="pixel", base="width", value=0, base_im=None, percentage=0, base_by_box=None):
+        """
+        plugins_mode # relative相对(宽度、高度、其他参考图),或  pixel绝对像素
+        base # pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+        value # 固定值,如果为百分比,则为0
+        percentage #百分比
+        base_im # 参考基于其他图 PictureProcessing 格式
+        """
+
+        # 在箱子内
+        if base_by_box:
+            mode = "relative"
+            base = "by_im"
+            base_by_box = (int(base_by_box[0]), int(base_by_box[1]))
+            base_im = Image.new("RGB", base_by_box, (255, 255, 255))
+
+        # 绝对值
+        if mode == "pixel":
+            if base == "width":
+                img = self.__to_resize(width=value)
+            if base == "high":
+                img = self.__to_resize(height=value)
+
+        # 相对值
+        if mode == "relative":
+            if base == "width":
+                img = self.__to_resize(
+                    width=self.width * percentage if not base_im else int(base_im.width * percentage))
+            if base == "height":
+                img = self.__to_resize(
+                    width=self.height * percentage if not base_im else int(base_im.height * percentage))
+
+            # by_im确保能塞进参考图内
+            if base == "by_im":
+                percentage = 1 if not percentage else percentage
+                box_width, box_height = int(base_im.width * percentage), int(base_im.height * percentage)
+                width, height = self.width, self.height
+                if box_width / box_height < width / height:
+                    scale = box_width / width
+                else:
+                    scale = box_height / height
+                img = self.get_im().resize((int(width * scale), int(height * scale)))
+
+        return PictureProcessing(im=img)
+
+    def paste_img(self, mode="pixel", top_img="", base="nw", value=(0, 0), percentage=(0, 0), margins=(0, 0, 0, 0)):
+        top_img: PictureProcessing
+        """
+        {
+            "command": "paste_img",
+            "im": 需要粘贴的图片
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": (0.5, 0.5),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        }
+        """
+        value = (int(value[0]), int(value[1]))
+        # 处理默认值
+        base = "nw" if not base else base
+        percentage = (0, 0) if not percentage else percentage
+        if margins:
+            top, down, left, right = margins
+        else:
+            top, down, left, right = 0, 0, 0, 0
+
+        if percentage != (0, 0):  # percentage 不按占比模式
+            if base in ("nw", "wn", "wc", "cw", "nc", "cn", "center"):
+                value = (int(self.width), int(self.height))
+            if base in ("sw", "ws", "sc", "cs", "center"):
+                value = (int(self.width), -1 * int(self.height))
+            if base in ("ec", "ce"):
+                value = (int(self.width), int(self.height))
+
+        if mode == "pixel":
+            # 基于右边,上下居中
+            if base == "ec" or base == "ce":
+                p_x = int(self.width - (top_img.width + value[0]))
+                p_y = int((self.height - top_img.height) / 2) + value[1]
+
+            # 基于顶部,左右居中
+            if base == "nc" or base == "cn":
+                # 顶部对齐
+                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
+                    (self.height - top_img.height) / 2)
+                p_x = deviation_x + value[0] + left
+                p_y = value[1]
+
+            # 基于右上角
+            if base == "en" or base == "ne":
+                p_x = int(self.width - (top_img.width + value[0])) + left
+                p_y = value[1]
+
+            # 基于左上角
+            if base == "nw" or base == "wn":
+                deviation_x, deviation_y = 0, 0
+                p_x, p_y = value
+
+            # 基于底部,左右居中
+            if base == "cs" or base == "sc":
+                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
+                    (self.height - top_img.height) / 2)
+
+                p_y = self.height - (top_img.height + value[1] + down)
+                p_x = deviation_x + value[0] + left
+
+            # 上下左右居中
+            if base == "center" or base == "cc":
+                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
+                    (self.height - top_img.height) / 2)
+                p_x = deviation_x + value[0] + left
+                p_y = deviation_y + value[1] + top
+
+            # 基于左下角
+            if base == "sw" or base == "ws":
+                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
+                p_x = value[0] + left
+                p_y = self.height - (top_img.height + value[1] + down)
+
+            # 基于左边,上下居中
+            if base == "wc" or base == "cw":
+                p_x = value[0] + left
+                p_y = int((self.height - top_img.height) / 2) + value[1] + top
+
+            # 基于右下角
+            if base == "es" or base == "se":
+                p_x = int(self.width - (top_img.width + value[0])) + left
+                p_y = self.height - (top_img.height + value[1] + down) + top
+
+            img = PictureProcessing(im=self.im).im
+            # top_img.get_im().show()
+            top_img_im = top_img.im
+            try:
+                img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im.convert("RGBA"))
+            except:
+                img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im)
+
+        return PictureProcessing(im=img)
+
+    def paste_img_invert(self, mode="pixel", top_img="", base="nw", value=(0, 0), percentage=(0, 0),
+                         margins=(0, 0, 0, 0)):
+        top_img: PictureProcessing
+        bg_img = top_img.im
+
+        top_img = PictureProcessing(im=self.im)
+        self.im = bg_img
+        return self.paste_img(mode=mode, top_img=top_img, base=base, value=value, percentage=percentage,
+                              margins=margins)
+
+    # 水平分布处理,一行N个
+    def horizontal_distribution(self, pp_list, bg_width=1200, margins=(0, 0, 0, 0), line_spacing=0, number_per_row=3, ):
+        """
+        pp_list
+        line_spacing:行间距
+        number_per_row:每行个数
+        margins: (0, 0, 0, 0),  # 上下左右边距
+        """
+        bg_width = int(bg_width)
+
+        total_row = len(pp_list) // number_per_row
+        if len(pp_list) % number_per_row > 0:
+            total_row = total_row + 1
+        # print("total_row", total_row)
+        every_height = pp_list[0].height
+        bg_pp = PictureProcessing("RGBA", (
+            bg_width, margins[0] + every_height * total_row + line_spacing * (total_row - 1) + margins[1]),
+                                  (255, 255, 255, 0))
+
+        row_num = 0
+        y = margins[0]
+        for i in range(0, len(pp_list), number_per_row):  # 切片获取每N个元素
+            row_list = pp_list[i:i + number_per_row]  # 输出每行的元素
+            if row_list:
+                row_num += 1
+                # 计算每个元素间距
+                t_w = bg_pp.width - margins[2] - margins[3]
+                al_w = sum([x.width for x in row_list])
+                space = int((t_w - al_w) / (len(row_list) + 1))
+                x = margins[2]
+                # 粘贴每个元素
+                for pp in row_list:
+                    x = x + space
+                    bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp, base="", value=(x, y))
+                    x = x + pp.width
+
+                if row_num != total_row:
+                    y += (every_height + line_spacing)
+        return bg_pp
+
+    def get_text_image_advanced(self, value=(0, 0), font="", text="", anchor=None, align="left", spacing=0,
+                                fill=(0, 0, 0),
+                                return_mode="image", margins=(0, 0, 0, 0)):
+        """
+        {
+            "command": "add_text",
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": 0, },
+            "font": "",
+            "text": "",
+            "anchor": "",  # mm 为居中 ma 为左右居中,上面居顶 rs 右边;从哪边开始输入
+            "align": "对齐方式",left  center right
+            "direction": "文本的方向",
+            "max_len_one_line": "单行长度",
+            "spacing": 10,
+            "fill": "文字颜色",
+        }
+        margins 边距像素,上下左右;只有返回min_image_high 有效
+        """
+
+        # =====
+        """
+        return_mode:image返回图片,min_image_high返回最小高度尺寸但宽度不变,min_image返回最小高度与宽度
+        """
+        if not text:
+            return self
+        # pp = PictureProcessing("RGBA", self.size, (0, 0, 0, 0))
+        pp = PictureProcessing("RGBA", self.size, (255, 255, 255, 0))
+        draw_1 = ImageDraw.Draw(pp.im)
+        # 定义字体,你需要有一个.ttf字体文件
+        spacing = 4 if not spacing else spacing
+        align = "left" if not align else align  # left, center 或 right
+
+        _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
+        value = (int(value[0]), int(value[1]))
+
+        draw_1.multiline_text((value[0], value[1] + margins[0]), text,
+                              fill=fill,
+                              font=font,
+                              anchor=anchor,
+                              spacing=spacing,
+                              align=align,
+                              direction=None,
+                              features=None,
+                              language=None,
+                              stroke_width=0,
+                              stroke_fill=None,
+                              embedded_color=False)
+
+        if return_mode == "image":
+            pp = pp.paste_img_invert(mode="pixel", top_img=self, base="nw", value=(0, 0))
+            pass
+        if return_mode == "min_image_high":
+            # pp.show()
+            bbox = pp.getbbox()
+            bbox = (0, 0, pp.width, bbox[3] + margins[1])
+            pp = pp.paste_img_invert(mode="pixel", top_img=self, base="nw", value=(0, 0))
+            pp = pp.crop(bbox)
+
+        if return_mode == "min_image":
+            bbox = pp.getbbox()
+            pp = pp.crop(bbox)
+
+        return pp
+
+    def add_text(self, mode="", base="", value="", percentage="", font="", text="", anchor="", align="", direction="",
+                 max_len_one_line="", spacing="", fill=""):
+        """
+        {
+            "command": "add_text",
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": 0, },
+            "font": "",
+            "text": "",
+            "anchor": "",  # mm 为居中 ma 为左右居中,上面居顶 rs 右边;从哪边开始输入
+            "align": "对齐方式",left  center right
+            "direction": "文本的方向",
+            "max_len_one_line": "单行长度",
+            "spacing": 10,
+            "fill": "文字颜色",
+        }
+
+        """
+        pp = PictureProcessing(im=self.im)
+        draw_1 = ImageDraw.Draw(pp.im)
+        # 定义字体,你需要有一个.ttf字体文件
+        spacing = 4 if not spacing else spacing
+        anchor = None if not anchor else anchor
+        align = "left" if not align else align  # left, center 或 right
+        _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
+
+        xy = (0, 0)
+        if mode == "pixel":
+            xy = value
+
+        draw_1.multiline_text(xy, text,
+                              fill=fill,
+                              font=font,
+                              anchor=anchor,
+                              spacing=spacing,
+                              align=align,
+                              direction=None,
+                              features=None,
+                              language=None,
+                              stroke_width=0,
+                              stroke_fill=None,
+                              embedded_color=False)
+        return pp
+
+    def crop_img(self, mode="pixel", base="nw", value=(100, 100, 10, 10), color_fill=(255, 255, 255),
+                 margins=(0, 0, 0, 0)):
+        """
+        {
+            "command": "crop_img",
+            "img": {"im": "im"},
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100, 10, 10),# 顶点,以及图片大小
+                    },
+            "color_fill": (255, 255, 255)
+        }
+
+        """
+        pp = PictureProcessing(im=self.im)
+
+        base = "nw" if not base else base
+        if margins:
+            top, down, left, right = margins
+        else:
+            top, down, left, right = 0, 0, 0, 0
+
+        out_img_size = (value[2], value[3])
+        # 默认填充色
+        if not color_fill:
+            color_fill = (0, 0, 0)
+
+        if mode == "pixel":
+            # 左上脚
+            if base == "nw" or "wn":
+                box = value
+            # 左下角
+            if base == "sw" or base == "ws":
+                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
+                box = (value[0], pp.height - (value[1] + value[3]), value[2], value[3])
+                # print(box)
+
+            # 右下角
+            if base == "se" or base == "es":
+                box = (pp.width - (value[0] + value[2]), pp.height - (value[1] + value[3]), value[2], value[3])
+                # print(box)
+
+            # 居中
+            if base == "cc":
+                # print("11-value", value)
+                x = int((pp.width + value[0] - value[2]) / 2)
+                y = int((pp.height + value[1] - value[3]) / 2)
+                box = (x,
+                       y,
+                       value[2],
+                       value[3])
+                # print("11-box", box)
+
+            box = [box[0], box[1], box[0] + box[2], box[1] + box[3]]
+
+        # print("12-box", box)
+        # print("ww-hhh", pp.width, pp.height)
+        out_img = pp.im.crop(box=box)
+        # print("out_img", out_img.width, out_img.height)
+        # print("-----")
+
+        if box[0] < 0:
+            out_img.paste(Image.new("RGB", (-1 * box[0], out_img.height), color_fill), (0, 0))
+
+        if box[1] < 0:
+            out_img.paste(Image.new("RGB", (out_img.width, -1 * box[1]), color_fill), (0, 0))
+
+        if box[2] > pp.width:
+            # print(box[2] - img.width, img.height)
+            i = Image.new("RGB", (box[2] - pp.width, out_img.height), color_fill)
+            out_img.paste(i, (pp.width - box[0], 0))
+
+        if box[3] > pp.height:
+            out_img.paste(Image.new("RGB", (out_img.width, box[3] - pp.height), color_fill),
+                          (0, pp.height - box[1]))
+
+        return PictureProcessing(im=out_img)
+
+    def crop(self, bbox=(0, 0, 0, 0), mode=None):
+        bbox = (int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3]))
+        if mode == "min":
+            bbox = self.getbbox()
+        return PictureProcessing(im=self.im.crop(bbox))
+
+    def rotate(self, doge):
+        return PictureProcessing(im=self.im.rotate(doge))
+
+    def rotate_advance(self, doge, is_crop=True):
+        max_px = max(self.width, self.height)
+        max_px = max_px * 2
+        bg = PictureProcessing("RGBA", (max_px, max_px), (255, 255, 255, 0))
+        bg = bg.paste_img(top_img=self, base="cc")
+        bg = bg.rotate(doge)
+        if is_crop:
+            bg = bg.crop(mode="min")
+
+        return bg
+
+    def radius(self, mode="pixel", circular_pos=(1, 1, 1, 1), value=30, percentage=""):
+        """
+        {"command": "radius",  # radius
+             "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+             "circular_pos": (0, 1, 0, 1),  # 从左上角顺时针,记录圆角数量
+             "value": 649,  # 固定值,如果为百分比,则为0
+             "percentage": 0, }  # 百分比
+        """
+        # 单图圆角处理
+        pp = PictureProcessing(im=self.im)
+
+        radii = value
+        if radii > pp.width / 2:
+            radii = int(pp.width / 2)
+        if radii > pp.height / 2:
+            radii = int(pp.height / 2)
+
+        # 画圆(用于分离4个角)
+        circle = Image.new('L', (radii * 2, radii * 2), 0)  # 创建一个黑色背景的画布
+        draw = ImageDraw.Draw(circle)
+        draw.ellipse((0, 0, radii * 2, radii * 2), fill=255)  # 画白色圆形
+
+        # 原图
+        img = pp.im.convert("RGBA")
+        w, h = img.size
+
+        # 画4个角(将整圆分离为4个部分)
+        alpha = Image.new('L', img.size, 255)
+        _pos = circular_pos
+        if not _pos:
+            _pos = (1, 1, 1, 1)
+        for index, i in enumerate(_pos):
+            if index == 0 and i == 1:
+                alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0))  # 左上角
+            if index == 1 and i == 1:
+                alpha.paste(circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0))  # 右上角
+            if index == 2 and i == 1:
+                alpha.paste(circle.crop((radii, radii, radii * 2, radii * 2)), (w - radii, h - radii))  # 右下角
+            if index == 3 and i == 1:
+                alpha.paste(circle.crop((0, radii, radii, radii * 2)), (0, h - radii))  # 左下角
+        # alpha.show()
+        img.putalpha(alpha)  # 白色区域透明可见,黑色区域不可见
+        return PictureProcessing(im=img)
+
+    # 图片翻转
+    def transpose(self, mode="left_right"):
+        img = self.get_im()
+        img = img.transpose(Image.FLIP_LEFT_RIGHT)
+        return PictureProcessing(im=img)
+
+    def convert(self, mode):
+        return PictureProcessing(im=self.im.convert(mode))
+
+    def save_as_rgb(self, path):
+        self.im = self.im.convert("RGB")
+        self.im.save(path, format="JPEG")
+
+    def save_as_png(self, path):
+        self.im = self.im.convert("RGBA")
+        self.im.save(path)

+ 23 - 0
python/custom_plugins/plugins_mode/plugins_base_func.py

@@ -0,0 +1,23 @@
+import os
+
+
+# 输入文件夹,并检查是否是一个正常的图片文件夹。
+def check_goods_folder(folder_path):
+    all_files = os.listdir(folder_path)
+    for file in all_files:
+        file_path = "{}/{}".format(folder_path, file)
+        if not os.path.isdir(file_path):
+            continue
+        if "原始图" in os.listdir(file_path):
+            return folder_path
+    # 上述检查不通过,可能是选择目录错误
+    if "原始图" in all_files:
+        root_path, _ = os.path.split(folder_path)
+        return root_path
+    return None
+
+
+if __name__ == '__main__':
+    a = check_goods_folder(
+        r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-12-20-2")
+    print(a)

+ 36 - 18
python/databases.py

@@ -4,7 +4,7 @@ from typing import Dict
 from datetime import datetime
 from typing import Optional
 import json
-from sqlalchemy import and_, desc,asc
+from sqlalchemy import and_, desc, asc
 
 from sqlalchemy.dialects import sqlite
 from model import DeviceConfig, PhotoRecord, SysConfigs
@@ -22,9 +22,12 @@ engine = create_engine(
     pool_recycle=1800,
 )
 
+
 # 创建表
 def create_all_database():
     SQLModel.metadata.create_all(engine)
+
+
 # 创建会话
 def __get_session():
     with Session(engine) as session:
@@ -35,7 +38,7 @@ def __get_session():
 
 
 def batch_insert_device_configs(session: Session, data_list: list):
-    '''批量插入数据到设备配置表'''
+    """批量插入数据到设备配置表"""
     for data in data_list:
         device_config = DeviceConfig(**data)
         session.add(device_config)
@@ -52,13 +55,16 @@ def batch_insert_sys_configs(session: Session, data_list: list):
     session.close()
 
 
-def insert_photo_records(image_deal_mode: int, goods_art_no: str, image_index:int):
+def insert_photo_records(
+    image_deal_mode: int, goods_art_no: str, image_index: int, action_id: int
+):
     session = SqlQuery()
     """批量插入数据到照片记录"""
     data = {
         "image_deal_mode": image_deal_mode,
         "goods_art_no": goods_art_no,
         "image_index": image_index,
+        "action_id": action_id,
     }
     device_config = PhotoRecord(**data)
     session.add(device_config)
@@ -82,21 +88,28 @@ class CRUD:
         return db_obj
 
     def read(
-            self,
-            session: Session,
-            conditions: Optional[Dict] = None,
-            order_by: Optional[str] = None,
-            ascending: bool = True,
+        self,
+        session: Session,
+        conditions: Optional[Dict] = None,
+        order_by: Optional[str] = None,
+        ascending: bool = True,
     ):
         query = select(self.model)
         if conditions:
-            query = query.where(and_(*(getattr(self.model, key) == value for key, value in conditions.items())))
+            query = query.where(
+                and_(
+                    *(
+                        getattr(self.model, key) == value
+                        for key, value in conditions.items()
+                    )
+                )
+            )
         if order_by:
             if ascending:
                 query = query.order_by(asc(getattr(self.model, order_by)))
             else:
                 query = query.order_by(desc(getattr(self.model, order_by)))
-        data =  session.exec(query).first()
+        data = session.exec(query).first()
         session.close()
         return data
 
@@ -109,21 +122,28 @@ class CRUD:
     ):
         query = select(self.model)
         if conditions:
-            query = query.where(and_(*(getattr(self.model, key) == value for key, value in conditions.items())))
+            query = query.where(
+                and_(
+                    *(
+                        getattr(self.model, key) == value
+                        for key, value in conditions.items()
+                    )
+                )
+            )
         if order_by:
             if ascending:
                 query = query.order_by(asc(getattr(self.model, order_by)))
             else:
                 query = query.order_by(desc(getattr(self.model, order_by)))
-        data =  session.exec(query).all()
+        data = session.exec(query).all()
         session.close()
         return data
 
     def update(self, session: Session, obj_id: int, **kwargs):
         db_obj = session.get(self.model, obj_id)
         for key, value in kwargs.items():
-            if value == None or value =="":
-                continue
+            # if value == None or value == "":
+            #     continue
             setattr(db_obj, key, value)
         session.commit()
         session.refresh(db_obj)
@@ -131,10 +151,7 @@ class CRUD:
         return db_obj
 
     def updateConditions(
-        self,
-        session: Session,
-        conditions: Optional[Dict] = None,
-        **kwargs
+        self, session: Session, conditions: Optional[Dict] = None, **kwargs
     ):
         query = select(self.model)
         if conditions:
@@ -189,6 +206,7 @@ class CRUD:
 def SqlQuery():
     return next(__get_session())
 
+
 # 使用示例
 if __name__ == "__main__":
     pass

+ 311 - 0
python/docs/socket命令.md

@@ -209,4 +209,315 @@ _(该命令用于单独自定义配置中某一项的单独调整测试,不进
     "msg_type": "photo_take"
 }
 ```
+
+#### 修改配置-独立操作MCU
+* data中参数释义:
+    * msg: 消息文本内容
+    * data :
+        * camera_height:相机高度
+        * after_delay:拍照后延迟
+        * camera_angle:相机角度
+        * led_switch:led开关;true:开;false:关
+        * number_focus:对焦次数;这里因为后端不控制拍照,所以无效
+        * id:固定0
+        * take_picture:固定false;这里因为后端不控制拍照,所以无效
+        * mode_type:模式类型;如:执行左脚程序
+        * turntable_position:转盘位置
+        * action_name:动作名称;如:侧视图
+        * turntable_angle:转盘角度
+        * shoe_upturn:是否翻转;true:翻转;false:不翻转
+        * action_index:固定1
+        * pre_delay:1;拍照后延迟;这里因为后端不控制拍照,所以无效
+* type:当该字段为run_mcu_single时,代表独立执行MCU设备
+##### 请求示例
+```python
+{
+    "data": {
+        "camera_height": 300,
+        "after_delay": 1,
+        "camera_angle": 20,
+        "led_switch": true,
+        "number_focus": 1,
+        "id": 0,
+        "take_picture": false,
+        "mode_type": "执行左脚程序",
+        "turntable_position": 200,
+        "action_name": "侧视图",
+        "turntable_angle": 300,
+        "shoe_upturn": false,
+        "action_index": 1,
+        "pre_delay": 1
+    },
+    "type": "run_mcu_single"
+}
+```
+##### 响应示例
+###### 当 msg_type 为run_mcu_single时,代表独立执行MCU设备完成,前端将此命令作为完成依据
+```python
+{
+    "code": 0,
+    "msg": "执行完成",
+    "status": 2,
+    "data": null,
+    "msg_type": "run_mcu_single_finish"
+}
+```
+#### 设备调平-获取
+<mark>以下操作需要连接设备且初始化<mark>
+* data:空对象或忽略,任何值无意义
+* type:当该字段为get_deviation时,代表获取调平设备信息
+##### 请求示例
+```python
+{
+    "data": {
+    },
+    "type": "get_deviation"
+}
+```
+##### 响应示例
+* data:
+    * camera_high_motor_deviation:相机高度偏移 mm
+    * camera_steering_deviation: 相机角度偏移 度
+    * turntable_steering_deviation: 转盘角度偏移 度
+    * overturn_steering_middle: 翻转舵机-中位角度
+    * overturn_steering_high: 翻转舵机-最位角度
+    * overturn_steering_up_speed: 翻转舵机-上升速度
+    * overturn_steering_down_speed: 翻转舵机-下降速度
+    * turntable_front_end_deviation: 转盘前后电机偏移度
+* type:当该字段为get_deviation_data时,代表成功获取到调平设备信息
+```python
+{
+    "code": 0,
+    "msg": "接收偏移量信息",
+    "status": 2,
+    "data": {
+        "camera_high_motor_deviation": 10,
+        "camera_steering_deviation": 0.0,
+        "turntable_steering_deviation": 0.0,
+        "overturn_steering_middle": 109.0,
+        "overturn_steering_high": 150.0,
+        "overturn_steering_up_speed": 2,
+        "overturn_steering_down_speed": 4,
+        "turntable_front_end_deviation":300
+    },
+    "msg_type": "get_deviation_data"
+}
+```
+
+
+#### 设备调平-设置
+<mark>以下操作需要连接设备且初始化<mark>
+* data:
+    * value: 偏移量值,最好传递浮点型
+    * action_name:
+        * "相机电机",  # min 0 max 400,步长1
+        * "相机舵机",  # min -40 max 40,步长0.1
+        * "转盘舵机",  # min -720 max 720,步长1
+        * "转盘前后电机",  # min 0 max 950,步长1
+        * "翻板舵机中位",  # min 0 max 180,步长0.5
+        * "翻板舵机高位",  # min 0 max 180,步长0.5
+        * "翻板舵机上升速度",  # min 1 max 10,步长1
+        * "翻板舵机下降速度",  # min 1 max 10,步长1
+    * type: 
+        * "move" 为移动
+        * "set" 为设置
+* type:
+    * 当该字段为set_deviation时,代表设置调平设备信息
+    * 当该字段为move_deviation时,代表移动调平设备偏移
+##### 请求示例
+```python
+{
+    "data": {
+        "value": 10,
+        "action_name":"相机电机",
+        "type":"move"
+    },
+    "type": "set_deviation"
+}
+```
+##### 响应示例
+* data:忽略
+* type:
+    * 当该字段为set_deviation时,代表响应调平设备设置
+    * 当该字段为move_deviation时,代表响应调平设备移动
+###### 成功状态
+```python
+{
+    "code": 0,
+    "msg": "相机电机 设置成功",
+    "status": 2,
+    "data": {},
+    "msg_type": "set_deviation"
+}
+```
+###### 失败状态
+```python
+{
+    "code": 1,
+    "msg": "设置参数有误,请检查",
+    "status": 0,
+    "data": {},
+    "msg_type": "set_deviation"
+}
+```
+
+
+
+#### 获取MCU其他设置信息
+<mark>以下操作需要连接设备且初始化<mark>
+* data:为null或其他值  后端忽略
+* type:
+    * 当该字段为get_mcu_other_info时,代表获取MCU其他设置信息
+##### 请求示例
+```python
+{
+    "data":null,
+    "type": "get_mcu_other_info"
+}
+```
+##### 响应示例
+* data:忽略
+* type:
+    * 当该字段为get_other_mcu_info,代表成功获取MCU其他设置信息
+<mark>以下数据包作为写入设备时对应得字段名称<mark>
+###### 成功状态
+```python
+{
+    "code": 0,
+    "msg": "获取mcu其他配置信息",
+    "status": 2,
+    "data": {
+        "is_auto_send_base_info": 0,
+        "is_move_retry": 0,
+        "is_data_response": 0,
+        "low_speed": 300,
+        "is_test": 0,
+        "to_init_mode": 1,
+        "turntable_move_to_init_mode": 0,
+        "led_count": 20,
+        "turntable_steering_angle_ratio": 356,
+        "is_manual_check": 1,
+        "camera_steering_angle_ratio": 50174,
+        "is_auto_motor_to_disable": 1,
+        "diff_dir": 0,
+        "is_auto_send_pos_info": 0,
+        "is_dog": 0,
+        "has_been_set_motor_config": 1
+    },
+    "msg_type": "get_other_mcu_info"
+}
+```
+
+
+
+#### 设置MCU其他设置信息
+<mark>以下操作需要连接设备且初始化<mark>
+* data:设置得数据包
+* type:
+    * 当该字段为set_mcu_other_info时,代表设置MCU其他设置信息
+##### 请求示例
+```python
+{
+    "data":{
+        "is_auto_send_base_info": 0,
+        "is_move_retry": 0,
+        "is_data_response": 0,
+        "low_speed": 300,
+        "is_test": 0,
+        "to_init_mode": 1,
+        "turntable_move_to_init_mode": 0,
+        "led_count": 20,
+        "turntable_steering_angle_ratio": 356,
+        "is_manual_check": 1,
+        "camera_steering_angle_ratio": 50174,
+        "is_auto_motor_to_disable": 1,
+        "diff_dir": 0,
+        "is_auto_send_pos_info": 0,
+        "is_dog": 0,
+        "has_been_set_motor_config": 1
+    },
+    "type": "set_mcu_other_info"
+}
+```
+##### 响应示例
+* data:忽略
+* type:
+    * 当该字段为get_other_mcu_info,代表成功获取MCU其他设置信息
+<mark>以下数据包作为写入设备时对应得字段名称<mark>
+###### 成功状态
+```python
+{
+    "code": 0,
+    "msg": "设置mcu其他配置信息成功",
+    "status": 2,
+    "data": null,
+    "msg_type": "set_mcu_other_info"
+}
+```
+
+
+#### 通过命令行发送设备指令
+<mark>以下操作需要连接设备且初始化<mark>
+* data:
+    * command:命令行指令,字符串“0x01 0x42 0x6C 0x6b”为前端默认展示得字符串值,写死即可
+* type:
+    * 当该字段为send_command时,代表设置通过命令行发送指令
+##### 请求示例
+```python
+{
+    "data": {
+        "command": "0x01 0x42 0x6C 0x6b"
+    },
+    "type": "send_command"
+}
+```
+##### 响应示例
+* data:
+    * command:收到的指令
+    * type:
+        * 为input时代表输入指令被转换成功,前端需要填充到发送命令得输入框中覆盖用户输入值;
+        * 为output时代表收到指令回复,前端需要填充到回复得文本展示框中;
+* type:
+    * 当该字段为send_command,代表成功获取到指令回复
+<mark>以下数据包作为写入设备时对应得字段名称<mark>
+###### 发送后指令转换
+```python
+{
+    "code": 0,
+    "msg": "命令发送完成",
+    "status": 2,
+    "data": {
+        "command": "0x01 0x42 0x6C 0x6B",
+        "type": "input"
+    },
+    "msg_type": "send_command"
+}
+```
+###### 接收到指令回复
+```python
+{
+    "code": 0,
+    "msg": "命令发送完成",
+    "status": 2,
+    "data": {
+        "command": "0xa8 0x12 0x4 0x14 0x14 0x24 0x24 0x4 0x4 0x15 0x14 0x14 0x64 0x4 0xc5 0x4 0xb6 0x84 0x1b 0x42 0x0 0x7 0x0 0x1 0x0 0x1 0x0 0x8 0x4 0x4c 0x4 0xb0 0x0 0x3 0x6b",
+        "type": "output"
+    },
+    "msg_type": "send_command"
+}
+```
+#### 执行重拍操作
+<mark>以下操作需要连接设备且初始化<mark>
+* data:
+    * record_id:原记录id
+* type:
+    * 当该字段为re_take_picture时,代表进行重拍操作
+<mark>注:后续得拍照动作参考消息回复为的type均为:re_take_picture,照片拍摄完成的消息依旧为photo_take<mark>
+##### 请求示例
+```python
+{
+    "data":{"record_id":1},
+    "type": "re_take_picture"
+}
+```
 ##### 未完待续.....

+ 11 - 10
python/index.py

@@ -11,7 +11,6 @@ import uvicorn.lifespan.on
 from multiprocessing import Process, freeze_support
 from service.init_load_source import init_load_source
 
-
 def handle_shutdown(signum, frame):
     """关闭系统应用服务"""
      # 终止事件循环
@@ -33,6 +32,8 @@ async def run_server():
     await server.serve()
 
 async def main():
+    # await init_load_source().load_source()
+    # await run_server()
     await  asyncio.gather(run_server(), init_load_source().load_source())
 
 
@@ -41,12 +42,12 @@ if __name__ == "__main__":
     signal.signal(signal.SIGTERM, handle_shutdown)
     print("python server is running at port:", PORT)
     print("python server is running at port:", APP_RUN)
-    # asyncio.run(main())
-    isDebug = True if IS_DEBUG == "true" else False
-    uvicorn.run(
-        app=APP_RUN,
-        host=APP_HOST,
-        port=int(PORT),
-        reload=isDebug,
-        loop="auto",
-    )
+    asyncio.run(main())
+    # isDebug = True if IS_DEBUG == "true" else False
+    # uvicorn.run(
+    #     app=APP_RUN,
+    #     host=APP_HOST,
+    #     port=int(PORT),
+    #     reload=isDebug,
+    #     loop="auto",
+    # )

+ 9 - 0
python/mcu/BlueToothMode.py

@@ -22,6 +22,7 @@ class BlueToothMode(BaseClass,metaclass=SingletonType):
         else:
             self.init_flag = True
         self.msg_type = "blue_tooth"
+        self.bluetooth_exit = False
         self.remote_control_v2 = RemoteControlV2(self, websocket_manager)
         # 用于存储找到的目标设备的地址
         self.target_device_address = None
@@ -241,15 +242,23 @@ class BlueToothMode(BaseClass,metaclass=SingletonType):
         await self.scan_for_esp32()
         # 定期重新扫描以发现新设备
         while True:
+            print("正在扫描设备...")
+            if self.bluetooth_exit:
+                break
             if self.devices:
                 await asyncio.sleep(20)
             else:
                 await asyncio.sleep(3)
             await self.scan_for_esp32()
+        print("蓝牙断开连接,已释放")
     def run(self):
         self.print_error("开启蓝牙扫描")
         asyncio.run(self.main_func())
 
+    def clearMyInstance(self):
+        print("清除蓝牙实例...")
+        SingletonType.clear_instance()
+
     def write_cmd(self, address, data: list):
         buf = []
         buf.extend([0x55, 0x55, (0xFF & len(data))])

+ 189 - 0
python/mcu/DebugUart.py

@@ -0,0 +1,189 @@
+import os
+import time
+
+# from lief import Object
+import asyncio
+from collections import OrderedDict
+
+
+class DebugUart():
+
+    def __init__(self, mcu):
+        # super().__init__()
+        self.mcu = mcu
+        # 0x01 0x42 0x6C 0x6b
+
+    async def set(self, text):
+        await asyncio.sleep(0.1)
+        #  1 0x42 0x6C 0x6b
+        # text = self.ui.textEdit.toPlainText()
+        # self.ui.textEdit_2.clear()
+        if not text:
+            return
+        text = text.replace(",", ",")
+        text = text.replace(" ", ",")
+        text = text.replace("\n", ",")
+        data = text.split(",")
+        try:
+            buf = [
+                self.mcu.command["signal_forwarding"],
+                0x01,
+                0x01,
+                0x00,
+                0x00,
+            ]
+            data = [int(x, 16) for x in data if x]
+            buf.extend(data)
+            text = " ".join([f"0x{x:02X}" for x in data])
+            self.mcu.msg_type = "send_command"
+            self.mcu.sendSocketMessage(
+                code=0,
+                msg="命令发送完成",
+                device_status=2,
+                data={"command": text, "type": "input"},
+            )
+            self.mcu.msg_type = "mcu"
+            # self.ui.textEdit.setText(text)
+            # # 刷新界面命令
+            # QApplication.processEvents()
+            self.mcu.add_send_data_queue(buf)
+            loop = asyncio.get_event_loop()
+            loop.create_task(self.get(), name="sendCommand3")
+        except BaseException as e:
+            print("解析错误", e)
+            pass
+
+    async def get(self, *args):
+        await asyncio.sleep(0.1)
+        self.mcu.last_from_mcu_move_respond_data = None
+        _s = time.time()
+        print("_s", _s)
+        while 1:
+            await asyncio.sleep(0.1)
+            if time.time() - _s > 3:
+                return False
+            # print("last_from_mcu_move_respond_data 1", time.time() - _s)
+            # print(
+            #     "last_from_mcu_move_respond_data 2",
+            #     self.mcu.last_from_mcu_move_respond_data,
+            # )
+            if self.mcu.last_from_mcu_move_respond_data is not None:
+                break
+        receive_data = self.mcu.last_from_mcu_move_respond_data
+        receive_data = receive_data[2:]
+        print("<------------------get_from_mcu_move_respond_data")
+        # self.ui.textEdit_2.setText(" ".join([hex(x) for x in receive_data]))
+        text = " ".join([hex(x) for x in receive_data])
+        self.mcu.msg_type = "send_command"
+        self.mcu.sendSocketMessage(
+            code=0,
+            msg="命令发送完成",
+            device_status=2,
+            data={"command": text, "type": "output"},
+        )
+        self.mcu.msg_type = "mcu"
+        if len(receive_data) >= 37:
+            # 锁定按键菜单 Lock 为 Disable(0x01 为 Enable);
+            data = OrderedDict()
+            # 锁定按键菜单 Lock 为 Disable(0x01 为 Enable);
+            data["锁定按键菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=4, len_data=1
+            )
+            # 控制模式菜单 Ctrl_Mode 为 CR_VFOC,即 FOC 矢量闭环控制模式;
+            data["控制模式菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=5, len_data=1
+            )
+            # 脉冲端口复用功能菜单 P_PUL 为 PUL_ENA,即使能脉冲输入控制
+            data["脉冲端口复用功能菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=6, len_data=1
+            )
+            # 通讯端口复用功能菜单 P_Serial 为 UART_FUN,即使能串口通讯;
+            data["通讯端口复用功能菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=7, len_data=1
+            )
+            # En 引脚的有效电平菜单 En 为 Hold,即一直有效
+            data["En引脚的有效电平菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=8, len_data=1
+            )
+            # 电机旋转正方向菜单 Dir 为 CW,即顺时针方向
+            data["电机旋转正方向菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=9, len_data=1
+            )
+            # 细分菜单 MStep 为 16 细分;(注:256 细分用 00 表示)
+            data["细分菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=10, len_data=1
+            )
+            # 细分插补功能菜单 MPlyer 为 Enable,即使能细分插补;
+            data["细分插补功能菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=11, len_data=1
+            )
+            # 自动熄屏功能菜单 AutoSDD 为 Disable,即关闭自动熄屏功能
+            data["自动熄屏功能菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=12, len_data=1
+            )
+            # 采样电流低通滤波器强度菜单 LPFilter 为 Def
+            data["采样电流低通滤波器强度菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=13, len_data=1
+            )
+            # 开环模式工作电流菜单 Ma 为 120 0Ma
+            data["开环模式工作电流菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=14, len_data=2
+            )
+            # 闭环模式最大电流菜单 Ma_Limit 为 2200Ma;
+            data["闭环模式最大电流菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=16, len_data=2
+            )
+            # 闭环模式最大转速菜单 Vm_Limit 为 3000RPM(转/每分钟);
+            data["闭环模式最大转速菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=18, len_data=2
+            )
+            # 电流环带宽菜单 CurBW_Hz 为 1000rad/s;
+            data["电流环带宽菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=20, len_data=2
+            )
+            # 串口波特率菜单 UartBaud 为 115200;(对应小屏幕选项顺序)
+            data["串口波特率菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=22, len_data=1
+            )
+            # CAN 通讯速率菜单 CAN_Baud 为 500000;(对应小屏幕选项顺序)
+            data["CAN通讯速率菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=23, len_data=1
+            )
+            # 通讯校验方式菜单 Checksum 为 0x6B;
+            data["通讯校验方式菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=24, len_data=1
+            )
+            # 控制命令应答菜单 Response 为 Receive,即只返回确认收到命令;
+            data["控制命令应答菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=25, len_data=1
+            )
+            # 通讯控制输入角度精确度选项菜单 S_PosTDP 为 Disable;
+            data["通讯控制输入角度精确度选项菜单"] = (
+                self.mcu.get_data_from_receive_data(
+                    receive_data=receive_data, start=26, len_data=1
+                )
+            )
+            # 堵转保护功能菜单 Clog_Pro 为 Enable,即使能堵转保护;
+            data["堵转保护功能菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=27, len_data=1
+            )
+            # 堵转保护转速阈值菜单 Clog_Rpm 为 8RPM(转/每分钟);
+            data["堵转保护转速阈值菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=28, len_data=2
+            )
+            # 堵转保护电流阈值菜单 Clog_Ma 为 2000Ma;
+            data["堵转保护电流阈值菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=30, len_data=2
+            )
+            # 堵转保护检测时间阈值菜单 Clog_Ms 为 2000ms;
+            data["堵转保护检测时间阈值菜单"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=32, len_data=2
+            )
+            # 位置到达窗口为 0.3
+            data["位置到达窗口"] = self.mcu.get_data_from_receive_data(
+                receive_data=receive_data, start=34, len_data=2
+            )
+
+            for k, v in data.items():
+                print("{}:{}".format(k, v))
+            return data

+ 310 - 39
python/mcu/DeviceControl.py

@@ -12,6 +12,9 @@ import settings
 from .ProgramItem import ProgramItem
 from .capture.module_digicam import DigiCam
 from databases import insert_photo_records
+from .McuDeviationSet import McuDeviationSet
+from .OtherSet import OtherSet
+from .DebugUart import DebugUart
 
 # mcu命令
 class DeviceControl(BaseClass, metaclass=SingletonType):
@@ -20,6 +23,9 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
     def __init__(self, websocket_manager: ConnectionManager):
         super().__init__(websocket_manager)
         self.msg_type = "mcu"
+        self.mcu_deviation_set = McuDeviationSet(self)
+        self.mcu_other_set = OtherSet(self)
+        self.debug_uart = DebugUart(self)
         self.m_t = 1
         # 0未开始  1进行中 2已结束  99异常
         self.action_state = 2
@@ -35,6 +41,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         self.camera_motor_value = 0
         self.init_state = False
         self.port_name = ""
+        self.mcu_exit = False
         self.t_n = 0
         self.serial_ins = None
         self.connected_ports_dict = {}  # 已连接的ports
@@ -104,15 +111,65 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             92: self.get_from_mcu_move_respond_data,  # 获取MCU响应
             100: self.print_mcu_error_data,  # 打印下位机的错误内容
         }
+    async def sendCommand(self,command):
+        await asyncio.sleep(0.01)
+        loop = asyncio.get_event_loop()
+        loop.create_task(self.debug_uart.set(command), name="sendCommand2")
+    async def getMcuOtherInfo(self):
+        await asyncio.sleep(0.01)
+        self.get_other_info()
+    async def setMcuOtherInfo(self,data):
+        await asyncio.sleep(0.01)
+        for k, v in data.items():
+            print("k:{},v:{}".format(k, v))
+            data[k] = self.setOtherMaxMinValue(k, v)
+        self.set_to_mcu_other_info(data)
+        self.msg_type = "set_other_mcu_info"
+        self.sendSocketMessage(
+            code=0,
+            msg="设置mcu其他配置信息完成",
+            device_status=2,
+        )
+        self.msg_type = "mcu"
 
-    async def initDevice(self):
+    def setOtherMaxMinValue(self,item, value):
+        value = int(value)
+        parameter_limits = {
+            "is_auto_send_base_info": (0, 10000),
+            "is_move_retry": (0, 10000),
+            "is_data_response": (0, 10000),
+            "low_speed": (0, 10000),  # 假设低速范围是0到1000
+            "is_test": (0, 10000),
+            "to_init_mode": (0, 10000),
+            "turntable_move_to_init_mode": (0, 10000),  # 假设转盘通讯方式范围是0到3
+            "led_count": (0, 10000),  # 假设LED数量范围是0到10
+            "turntable_steering_angle_ratio": (0, 10000),  # 假设角度比例范围是0到100
+            "is_manual_check": (0, 10000),
+            "camera_steering_angle_ratio": (0, 100000),  # 假设角度比例范围是0到100
+            "is_auto_motor_to_disable": (0, 10000),
+            "diff_dir": (0, 10000),
+            "is_auto_send_pos_info": (0, 10000),
+            "is_dog": (0, 10000),
+            "has_been_set_motor_config": (0, 10000),
+        }
+
+        # 获取参数的最小值和最大值
+        min_value, max_value = parameter_limits.get(item, (0, 10000))
+        value = max(min_value, min(max_value, value))
+        return value
+
+    async def initDevice(self,is_force=False):
         if not self.is_running:
             self.sendSocketMessage(
                 code=1, msg="mcu设备未连接,请先连接设备", device_status=0
             )
             return False
+        if self.init_state ==True:
+            print("已经初始化过,请勿重复初始化")
+            self.sendSocketMessage(msg="设备初始化完成", device_status=2)
+            return False
         self.serial_ins.clearn_flush()
-        self.to_init_device_origin_point(device_name="mcu")
+        self.to_init_device_origin_point(device_name="mcu",is_force=is_force)
         print("MCU 开始循环~")
         while 1:
             await asyncio.sleep(0.01)
@@ -198,7 +255,8 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             )
             self.add_send_data_queue(buf)
 
-    def get_deviation(self):
+    async def getDeviationInfo(self):
+        await asyncio.sleep(0.01)
         # 发送获取偏移量
         data = [self.command["get_deviation"], 1]
         self.add_send_data_queue(data)
@@ -206,6 +264,59 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         #     self.serial_ins.write_cmd(data)
         print("发送获取偏移量")
 
+    def set_deviation(self, device_name, _type=0, deviation=0):
+        # turntable----0 angle_ratio   1 turntable_steering_deviation
+        # overturn ----0 middle   1 high
+        if device_name == "camera_high_motor":
+            deviation = deviation / 10  # deviation 原单位为mm
+        if device_name == "turntable_position_motor":
+            deviation = deviation / 10  # deviation 原单位为mm
+        if device_name == "camera_steering":
+            pass
+        if device_name == "turntable_steering":
+            pass
+        if device_name == "overturn_steering":
+            pass
+
+        device_id = self.device_name_dict[device_name]
+        _dir = 1 if deviation >= 0 else 0
+        deviation = int(abs(deviation * 10))
+        data = [
+            self.command["set_deviation"],
+            device_id,
+            _type,
+            _dir,
+            0xFF & deviation >> 8,
+            0xFF & deviation,
+        ]
+        self.add_send_data_queue(data)
+        # self.controlDevice(device_name, deviation)
+
+    async def set_deviation_cmd(self,value,action_name,type):
+        await asyncio.sleep(0.01)
+        name_sets = [
+            "相机电机",  # min 0 max 400,步长1
+            "相机舵机",  # min -40 max 40,步长0.1
+            "转盘舵机",  # min -720 max 720,步长1
+            "转盘前后电机",  # min 0 max 950,步长1
+            "翻板舵机中位",  # min 0 max 180,步长0.5
+            "翻板舵机高位",  # min 0 max 180,步长0.5
+            "翻板舵机上升速度",  # min 1 max 10,步长1
+            "翻板舵机下降速度",  # min 1 max 10,步长1
+        ]
+        if action_name not in name_sets:
+            self.msg_type = f"{type}_deviation"
+            self.sendSocketMessage(msg="设置参数有误,请检查", device_status=0,code=1)
+            self.msg_type = "mcu"
+            return
+        # 发送获取偏移量
+        if type == "move":
+            self.mcu_deviation_set.change_value(value, action_name)
+        elif type == "set":
+            self.mcu_deviation_set.set_deviation(action_name)
+        self.msg_type = f"{type}_deviation"
+        self.sendSocketMessage(msg=f"{action_name} 设置成功", device_status=2,code=0)
+        self.msg_type = "mcu"
     def get_other_info(self):
         # 发送获取偏移量
         data = [self.command["get_other_info"], 1]
@@ -215,7 +326,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
     def add_send_data_queue(self, data):
         self.lock.acquire()
         if self.serial_ins:
-            # print("send_data_queue  append  :{}".format(data))
+            print("send_data_queue  append  :{}".format(data))
             self.send_data_queue.append(data)
         self.lock.release()
     def send_all_cmd(self):
@@ -286,6 +397,51 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             self.is_just_init_time = False
 
         return True
+
+    # 设置其他信息
+    def set_to_mcu_other_info(self, data):
+        is_auto_send_base_info = data["is_auto_send_base_info"]
+        is_move_retry = data["is_move_retry"]
+        is_data_response = data["is_data_response"]
+        low_speed = data["low_speed"]
+        is_test = data["is_test"]
+        to_init_mode = data["to_init_mode"]
+        turntable_move_to_init_mode = data["turntable_move_to_init_mode"]
+        led_count = data["led_count"]
+        turntable_steering_angle_ratio = data["turntable_steering_angle_ratio"]
+        is_manual_check = data["is_manual_check"]
+        camera_steering_angle_ratio = data["camera_steering_angle_ratio"]
+        is_auto_motor_to_disable = data["is_auto_motor_to_disable"]
+        diff_dir = data["diff_dir"]
+        is_auto_send_pos_info = data["is_auto_send_pos_info"]
+        is_dog = data["is_dog"]
+        has_been_set_motor_config = data["has_been_set_motor_config"]
+
+        buf = [self.command["set_other_info"]]
+        buf.extend(self.encapsulation_data(data=is_auto_send_base_info, len_data=1))
+        buf.extend(self.encapsulation_data(data=is_move_retry, len_data=1))
+        buf.extend(self.encapsulation_data(data=is_data_response, len_data=1))
+        buf.extend(self.encapsulation_data(data=low_speed, len_data=2))
+        buf.extend(self.encapsulation_data(data=is_test, len_data=1))
+        buf.extend(self.encapsulation_data(data=to_init_mode, len_data=1))
+        buf.extend(
+            self.encapsulation_data(data=turntable_move_to_init_mode, len_data=1)
+        )
+        buf.extend(self.encapsulation_data(data=led_count, len_data=2))
+        buf.extend(
+            self.encapsulation_data(data=turntable_steering_angle_ratio, len_data=2)
+        )
+        buf.extend(self.encapsulation_data(data=is_manual_check, len_data=1))
+        buf.extend(
+            self.encapsulation_data(data=camera_steering_angle_ratio, len_data=4)
+        )
+        buf.extend(self.encapsulation_data(data=is_auto_motor_to_disable, len_data=1))
+        buf.extend(self.encapsulation_data(data=diff_dir, len_data=1))
+        buf.extend(self.encapsulation_data(data=is_auto_send_pos_info, len_data=1))
+        buf.extend(self.encapsulation_data(data=is_dog, len_data=1))
+        buf.extend(self.encapsulation_data(data=has_been_set_motor_config, len_data=1))
+        self.add_send_data_queue(buf)
+
     def cleanAllReceiveData(self):
         while True:
             receive_data = self.serial_ins.read_cmd(out_time=1)
@@ -299,10 +455,14 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             return False
         if not receive_data:
             return
-        # print("receive_data")
+        # print("receive_data", receive_data)
         # 数据 结构 command,按命令解析
         # command 0(9) 相机高度1-2  相机角度3-4  转盘角度5-6 灯光状态7  激光指示器状态8,运行状态9
         command = receive_data[0]
+        # receive_data_temp = receive_data[2:]
+        # receive_data_temp_text = " ".join([hex(x) for x in receive_data_temp])
+        # print("command",command)
+        # print("receive_data", receive_data_temp_text)
         if command in self.deal_code_func_dict:
             self.deal_code_func_dict[command](receive_data)
 
@@ -402,25 +562,60 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             #         },
             #     }
             # )
-            message = {
-                "_type": "get_deviation_data",
-                "plugins_mode": "mcu",
-                "data": {
-                    "camera_high_motor_deviation": camera_high_motor_deviation,
-                    "camera_steering_deviation": camera_steering_deviation,
-                    "turntable_steering_deviation": turntable_steering_deviation,
-                    "overturn_steering_middle": overturn_steering_middle,
-                    "overturn_steering_high": overturn_steering_high,
-                    "overturn_steering_up_speed": overturn_steering_up_speed,
-                    "overturn_steering_down_speed": overturn_steering_down_speed,
-                },
+            # message = {
+            #     "_type": "get_deviation_data",
+            #     "plugins_mode": "mcu",
+            #     "data": {
+            #         "camera_high_motor_deviation": camera_high_motor_deviation,
+            #         "camera_steering_deviation": camera_steering_deviation,
+            #         "turntable_steering_deviation": turntable_steering_deviation,
+            #         "overturn_steering_middle": overturn_steering_middle,
+            #         "overturn_steering_high": overturn_steering_high,
+            #         "overturn_steering_up_speed": overturn_steering_up_speed,
+            #         "overturn_steering_down_speed": overturn_steering_down_speed,
+            #     },
+            # }
+            get_deviation_data = {
+                "camera_high_motor_deviation": camera_high_motor_deviation,
+                "camera_steering_deviation": camera_steering_deviation,
+                "turntable_steering_deviation": turntable_steering_deviation,
+                "turntable_front_end_deviation": 300,
+                "overturn_steering_middle": overturn_steering_middle,
+                "overturn_steering_high": overturn_steering_high,
+                "overturn_steering_up_speed": overturn_steering_up_speed,
+                "overturn_steering_down_speed": overturn_steering_down_speed,
             }
-            self.sendSocketMessage(msg="接收偏移量信息", data=message)
+            self.initDeviationInfo(get_deviation_data)
+            self.msg_type = "get_deviation_data"
+            self.sendSocketMessage(msg="接收偏移量信息", data=get_deviation_data)
+            self.msg_type = "mcu"
             print("接收偏移量信息")
         return
 
+    def initDeviationInfo(self, get_deviation_data):
+        # 初始化偏移量信息
+        camera_high_motor_deviation = get_deviation_data["camera_high_motor_deviation"]
+        camera_steering_deviation = get_deviation_data["camera_steering_deviation"]
+        turntable_steering_deviation = get_deviation_data["turntable_steering_deviation"]
+        overturn_steering_middle=get_deviation_data["overturn_steering_middle"]
+        overturn_steering_high = get_deviation_data["overturn_steering_high"]
+        overturn_steering_up_speed = get_deviation_data["overturn_steering_up_speed"]
+        overturn_steering_down_speed = get_deviation_data["overturn_steering_down_speed"]
+        self.mcu_deviation_set.last_value["相机电机"] = camera_high_motor_deviation
+        self.mcu_deviation_set.last_value["相机舵机"] = camera_steering_deviation
+        self.mcu_deviation_set.last_value["转盘舵机"] = turntable_steering_deviation
+        self.mcu_deviation_set.last_value["翻板舵机中位"] = overturn_steering_middle
+        self.mcu_deviation_set.last_value["翻板舵机高位"] = overturn_steering_high
+        self.mcu_deviation_set.last_value["翻板舵机上升速度"] = (
+            overturn_steering_up_speed
+        )
+        self.mcu_deviation_set.last_value["翻板舵机下降速度"] = (
+            overturn_steering_down_speed
+        )
+
     # 获取其他信息
     def get_from_mcu_other_info(self, receive_data):
+        # TODO  已完成设置
         is_auto_send_base_info = self.get_data_from_receive_data(
             receive_data=receive_data, start=1, len_data=1
         )
@@ -492,6 +687,14 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         self.last_mcu_other_info_data["num"] += 1
         for k, v in self.last_mcu_other_info_data["data"].items():
             print("k:{},v:{}".format(k, v))
+        self.msg_type = "get_mcu_other_info"
+        self.sendSocketMessage(
+            code=0,
+            msg="获取mcu其他配置信息",
+            device_status=2,
+            data=self.last_mcu_other_info_data["data"],
+        )
+        self.msg_type = "mcu"
 
     def get_data_from_receive_data(
         self, receive_data, start, len_data, data_magnification=1
@@ -588,6 +791,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             # if self.state_camera_motor
             if all(value == 2 for value in [self.state_camera_motor, self.state_camera_steering, self.state_turntable_steering, self.state_overturn_steering]):
                 self.init_state = True
+                self.sendSocketMessage(msg="设备初始化完成", device_status=2)
             self.sendSocketMessage(msg="获取mcu设备运行状态信息", data=message)
         # 检查是否成功初始化
         if self.is_just_init_time is False:
@@ -670,7 +874,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 1,
                 msg="串口:{} 被占用,或无法识别".format(port_name).format(port_name),
                 data=message_data,
-                device_status=3,
+                device_status=-1,
             )
             print("串口:{} 被占用".format(port_name))
             return
@@ -751,13 +955,18 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             # asyncio.gather(getBaseInfo())
         else:
             print("串口无法识别")
+            self.sendSocketMessage(
+                code=1,
+                msg="串口无法识别,请重新插拔拍照机USB", data=message_data, device_status=-1
+            )
             # 走其他途径处理
 
         # 检查当前MCU链接是否正常
         # 正常跳过;记录为其他列表
         # 不正常进行尝试连接
         # 连接不上,记录为其他列表
-
+    def clearMyInstance(self):
+        SingletonType.clear_instance()
     def to_connect_com(self, port_name):
         # 关闭串口
         print("to_connect_com", port_name)
@@ -931,6 +1140,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
 
         cmd = 1
         device_id = self.device_name_dict[device_name]
+        print("正在执行",device_name)
         match device_name:
             case "camera_high_motor":
                 # value 单位毫米
@@ -1063,7 +1273,9 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 code=1, msg="mcu设备未初始化", device_status=4
             )
             return False
-        _is_debug = 1
+        _is_debug = 1 if settings.IS_DEBUG == "true" else 0
+        is_deviation = 0 if settings.IS_DEBUG == "true" else 1
+        print("控制设备==>_is_debug", _is_debug)
         match device_name:
             case "camera_high_motor":
                 # 相机电机
@@ -1071,11 +1283,11 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 self.to_device_move(
                     device_name=device_name,
                     value=float(value),
-                    max_speed=1400,
-                    up_speed=400,
-                    down_speed=100,
-                    _is_debug=_is_debug,
-                    is_deviation=0,
+                    # max_speed=1400,
+                    # up_speed=400,
+                    # down_speed=100,
+                    # _is_debug=_is_debug,
+                    # is_deviation=is_deviation,
                 )
             case "camera_steering":
                 print(device_name, value)
@@ -1083,7 +1295,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 self.to_device_move(
                     device_name=device_name,
                     value=float(value),
-                    _is_debug=_is_debug,
+                    # _is_debug=_is_debug,
                     is_deviation=0,
                 )
             case "turntable_steering":
@@ -1091,7 +1303,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 self.to_device_move(
                     device_name=device_name,
                     value=float(value),
-                    _is_debug=_is_debug,
+                    # _is_debug=_is_debug,
                     is_deviation=0,
                 )
             case "turntable_position_motor":
@@ -1099,11 +1311,11 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 self.to_device_move(
                     device_name=device_name,
                     value=float(value),
-                    max_speed=1400,
-                    up_speed=400,
-                    down_speed=100,
-                    _is_debug=_is_debug,
-                    is_deviation=0,
+                    # max_speed=1400,
+                    # up_speed=400,
+                    # down_speed=100,
+                    # _is_debug=_is_debug,
+                    # is_deviation=is_deviation,
                 )
             case "overturn_steering":
                 # 翻板舵机中位
@@ -1117,10 +1329,10 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             case "take_picture":
                 capture_one = DigiCam()
                 try:
-                    camera_is_connect = capture_one.checkCameraConnect()
-                    if camera_is_connect is not True:
-                        self.sendSocketMessage(1,"相机未连接,请检查",device_status=-1)
-                        return
+                    # camera_is_connect = capture_one.checkCameraConnect()
+                    # if camera_is_connect is not True:
+                    #     self.sendSocketMessage(1,"相机未连接,请检查",device_status=-1)
+                    #     return
                     capture_one.getCaptureFolderPath()
                     if value > 0:
                         capture_one.auto_focus()
@@ -1157,11 +1369,17 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         if config_list:
             for idx, item in enumerate(config_list):
                 is_take_picture = item["take_picture"]
+                action_id = item["id"]
                 if is_take_picture:
                     image_counts += 1
                     # 批量插入
                     image_deal_mode = 0 if action_info == "执行左脚程序" else 1
-                    insert_photo_records(image_deal_mode=image_deal_mode, goods_art_no=goods_art_no, image_index=idx)
+                    insert_photo_records(
+                        image_deal_mode=image_deal_mode,
+                        goods_art_no=goods_art_no,
+                        image_index=idx,
+                        action_id=action_id,
+                    )
             total_len = len(config_list)
             self.action_state = 1
             self.msg_type = "image_process"
@@ -1172,9 +1390,10 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 data={
                     "goods_art_no": goods_art_no,
                     "image_counts": image_counts,
-                    "current_time":datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+                    "current_time":datetime.datetime.now(settings.TIME_ZONE).strftime("%Y-%m-%d %H:%M:%S"),
                 },
             )
+            self.controlDevice("laser_position", 0)
             self.msg_type = "mcu"
             for index, action in enumerate(config_list):
                 await asyncio.sleep(0.1)
@@ -1234,6 +1453,55 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 device_status=2,
             )
             self.msg_type = "mcu"
+            self.controlDevice("laser_position", 1)
+
+    async def run_mcu_config_single(self, config_info, goods_art_no,msg_type="run_mcu_single_finish",image_index=-1):
+        '''独立拍照  仅作测试用'''
+        if self.checkDevice() == False:
+            return
+        if config_info:
+            self.action_state = 1
+            self.msg_type = "mcu"
+            await asyncio.sleep(0.1)
+            program_item = ProgramItem(
+                    websocket_manager=self.websocket_manager,
+                    action_data=config_info,
+                    mcu=self,
+                    goods_art_no=goods_art_no,
+                    image_index=image_index,
+                )
+            print("self.action_state===>", self.action_state)
+            if self.action_state != 1:
+                # 异常终止
+                print("action异常终止")
+                return
+            self.msg_type = "photo_take"
+            # if not program_item.run_only_mcu(1):
+            #     self.sendSocketMessage(
+            #             code=1,
+            #             msg="{} 执行失败~".format(program_item.action_name),
+            #             device_status=0,
+            #         )
+            #     self.to_deal_device(device_name="buzzer", times=3)
+            #     return
+            # else:
+            program_item.run(3)
+            # self.show_info("{}执行完成~".format(action.action_name))
+            self.sendSocketMessage(
+                        code=0,
+                        msg="{} 执行完成~".format(program_item.action_name),
+                        data={"goods_art_no": goods_art_no},
+                        device_status=2,
+                    )
+            self.msg_type = "mcu"
+            self.action_state = 2
+            self.msg_type = msg_type
+            self.sendSocketMessage(
+                    code=0,
+                    msg=f"执行完成",
+                    device_status=2,
+                )
+            self.msg_type = "mcu"
 
 
 async def checkMcuConnection(device_ctrl: DeviceControl):
@@ -1249,6 +1517,8 @@ async def checkMcuConnection(device_ctrl: DeviceControl):
     """实时检测串口是否连接"""
     while True:
         await asyncio.sleep(0.5)
+        if device_ctrl.mcu_exit:
+            break
         ports_dict = device_ctrl.scan_serial_port()
         device_ctrl.temp_ports_dict = ports_dict
         if not ports_dict:
@@ -1271,3 +1541,4 @@ async def checkMcuConnection(device_ctrl: DeviceControl):
                         device_ctrl.add_port_by_linkage(_port_name)
                     except BaseException as e:
                         print("串口不存在{} {}".format(_port_name, e))
+    print("MCU断开连接,已释放")

+ 0 - 1
python/mcu/McuDebug.py

@@ -1,5 +1,4 @@
 import time
-
 class McuDebug(object):
 
     def __init__(self, mcu, is_debug=True, is_deviation=False):

+ 76 - 59
python/mcu/McuDeviationSet.py

@@ -1,11 +1,11 @@
 from collections import defaultdict
 from .McuDebug import McuDebug
 from functools import partial
-import time
+import time, asyncio
 import settings
 
 
-class McuDeviationSet():
+class McuDeviationSet:
     # 调试处理
     # data_sign = Signal(dict)
     # info_sign = Signal(str)
@@ -19,8 +19,10 @@ class McuDeviationSet():
         # self.set_enable_by_mcu()
         # self.show()
         self.last_value = defaultdict(float)
-        self.mcu_debug = McuDebug(mcu, is_debug=True, is_deviation=False)
-        self.get_mcu_deviation()
+        is_debug = True if settings.IS_DEBUG == "true" else False
+        self.mcu_debug = McuDebug(mcu, is_debug=is_debug, is_deviation=False)
+        loop = asyncio.get_event_loop()
+        loop.create_task(self.get_mcu_deviation(), name="get_mcu_deviation")
         # # 运动到设定位
         # QTimer.singleShot(2500, self.init_pos)
 
@@ -164,29 +166,29 @@ class McuDeviationSet():
                 return True
 
     def change_value(self, value, name):
-        value = round(value, 2)
-        if self.last_value[name] - 1 <= value <= self.last_value[name] + 1:
-            if name == "相机电机":
-                self.mcu_debug.camera_high_motor(value=value)
-            if name == "相机舵机":
-                self.mcu_debug.camera_steering(value=value)
-            if name == "转盘舵机":
-                self.mcu_debug.turntable_steering(value=value)
-            if name == "转盘前后电机":
-                self.mcu_debug.turntable_position_motor(value=value)
-            if name == "翻板舵机中位":
-                self.mcu_debug.overturn_steering(value=value)
-            if name == "翻板舵机高位":
-                self.mcu_debug.overturn_steering(value=value)
-            if name == "翻板舵机上升速度":
-                pass
-            print(value, name)
+        value = round(float(value), 2)
+        print("change_value===>", value, name)
+        if name == "相机电机":
+            self.mcu_debug.camera_high_motor(value=value)
+        if name == "相机舵机":
+            self.mcu_debug.camera_steering(value=value)
+        if name == "转盘舵机":
+            self.mcu_debug.turntable_steering(value=value)
+        if name == "转盘前后电机":
+            self.mcu_debug.turntable_position_motor(value=value)
+        if name == "翻板舵机中位":
+            self.mcu_debug.overturn_steering(value=value)
+        if name == "翻板舵机高位":
+            self.mcu_debug.overturn_steering(value=value)
+        if name == "翻板舵机上升速度":
+            pass
+        print(value, name)
         self.last_value[name] = value
 
     def set_deviation(self, name):
         if name == "相机电机":
             # 设定相机高度偏移量 单位mm
-            camera_high_motor_deviation = int(self.doubleSpinBox.value())
+            camera_high_motor_deviation = int(self.last_value[name])
             device_name = "camera_high_motor"
             self.mcu.set_deviation(
                 device_name=device_name, _type=0, deviation=camera_high_motor_deviation
@@ -194,16 +196,16 @@ class McuDeviationSet():
 
         if name == "相机舵机":
             # 设定相机舵机偏移量
-            camera_steering_deviation = self.doubleSpinBox_2.value()
+            camera_steering_deviation = self.last_value[name]
             device_name = "camera_steering"
             self.mcu.set_deviation(
                 device_name=device_name, _type=0, deviation=camera_steering_deviation
             )
-            self.doubleSpinBox_2.setValue(0.0)
+            # self.doubleSpinBox_2.setValue(0.0)
 
         if name == "转盘舵机":
             # 设定转盘舵机偏移角度  单位 度
-            turntable_steering_deviation = self.doubleSpinBox_3.value()
+            turntable_steering_deviation = self.last_value[name]
             device_name = "turntable_steering"
             self.mcu.set_deviation(
                 device_name=device_name, _type=1, deviation=turntable_steering_deviation
@@ -211,81 +213,96 @@ class McuDeviationSet():
 
         if name == "翻板舵机中位":
             # 设定翻版舵机偏移量
-            value = self.doubleSpinBox_4.value()
+            value = self.last_value[name]
             device_name = "overturn_steering"
-            self.mcu.set_deviation(
-                device_name=device_name, _type=0, deviation=value
-            )
+            self.mcu.set_deviation(device_name=device_name, _type=0, deviation=value)
         if name == "翻板舵机高位":
             # 设定翻版舵机偏移量
-            value = self.doubleSpinBox_5.value()
+            value = self.last_value[name]
             device_name = "overturn_steering"
-            self.mcu.set_deviation(
-                device_name=device_name, _type=1, deviation=value
-            )
+            self.mcu.set_deviation(device_name=device_name, _type=1, deviation=value)
         if name == "翻板舵机上升速度":
             # 设定翻版舵机偏移量
-            value = self.doubleSpinBox_6.value()
+            value = self.last_value[name]
             device_name = "overturn_steering"
-            self.mcu.set_deviation(
-                device_name=device_name, _type=2, deviation=value
-            )
+            self.mcu.set_deviation(device_name=device_name, _type=2, deviation=value)
         if name == "翻板舵机下降速度":
             # 设定翻版舵机偏移量
-            value = self.doubleSpinBox_8.value()
+            value = self.last_value[name]
             device_name = "overturn_steering"
-            self.mcu.set_deviation(
-                device_name=device_name, _type=3, deviation=value
-            )
+            self.mcu.set_deviation(device_name=device_name, _type=3, deviation=value)
 
     def _to_init_all(self, *args):
         self.mcu.to_init_device_origin_point(device_name="mcu", is_force=True)
 
-    def get_mcu_deviation(self):
-        self.mcu.get_deviation()
+    async def get_mcu_deviation(self):
+        await self.mcu.getDeviationInfo()
 
     def get_mcu_deviation_info(self, data):
         if "_type" not in data:
             return
+        get_deviation_data = {}
         if data["_type"] == "get_deviation_data":
             data = data["data"]
             print("偏移量信息")
             # overturn_steering_up_speed  overturn_steering_down_speed
             # 相机电机 单位mm
             if "camera_high_motor_deviation" in data:
-                self.doubleSpinBox.setValue(float(data["camera_high_motor_deviation"]))
+                # self.doubleSpinBox.setValue(float(data["camera_high_motor_deviation"]))
+                get_deviation_data["camera_high_motor_deviation"] = float(
+                    data["camera_high_motor_deviation"]
+                )
             # 相机舵机
             if "camera_steering_deviation" in data:
-                self.doubleSpinBox_2.setValue(float(data["camera_steering_deviation"]))
+                # self.doubleSpinBox_2.setValue(float(data["camera_steering_deviation"]))
+                get_deviation_data["camera_steering_deviation"] = float(
+                    data["camera_steering_deviation"]
+                )
             # 转盘偏移位
             if "turntable_steering_deviation" in data:
-                self.doubleSpinBox_3.setValue(
-                    float(data["turntable_steering_deviation"])
+                # self.doubleSpinBox_3.setValue(
+                #     float(data["turntable_steering_deviation"])
+                # )
+                get_deviation_data["turntable_steering_deviation"] = float(
+                    data["turntable_steering_deviation"]
                 )
             # 翻板舵机中位
             if "overturn_steering_middle" in data:
-                self.doubleSpinBox_4.setValue(float(data["overturn_steering_middle"]))
+                # self.doubleSpinBox_4.setValue(float(data["overturn_steering_middle"]))
+                get_deviation_data["overturn_steering_middle"] = float(
+                    data["overturn_steering_middle"]
+                )
             # 翻板舵机高位
             if "overturn_steering_high" in data:
-                self.doubleSpinBox_5.setValue(float(data["overturn_steering_high"]))
+                # self.doubleSpinBox_5.setValue(float(data["overturn_steering_high"]))
+                get_deviation_data["overturn_steering_high"] = float(
+                    data["overturn_steering_high"]
+                )
 
             # 翻板舵机
             if "overturn_steering_up_speed" in data:
-                self.doubleSpinBox_6.setValue(float(data["overturn_steering_up_speed"]))
+                # self.doubleSpinBox_6.setValue(float(data["overturn_steering_up_speed"]))
+                get_deviation_data["overturn_steering_up_speed"] = float(
+                    data["overturn_steering_up_speed"]
+                )
 
             if "overturn_steering_down_speed" in data:
-                self.doubleSpinBox_8.setValue(
-                    float(data["overturn_steering_down_speed"])
+                # self.doubleSpinBox_8.setValue(
+                #     float(data["overturn_steering_down_speed"])
+                # )
+                get_deviation_data["overturn_steering_down_speed"] = float(
+                    data["overturn_steering_down_speed"]
                 )
 
             # 初始化位置
-            data = {
-                "相机电机": self.doubleSpinBox.value(),
-                "相机舵机": self.doubleSpinBox_2.value(),
-                "转盘舵机": self.doubleSpinBox_3.value(),
-                "转盘前后电机": self.doubleSpinBox_7.value(),
-            }
-            self.is_get_offset.emit(data)
+            # data = {
+            #     "相机电机": self.doubleSpinBox.value(),
+            #     "相机舵机": self.doubleSpinBox_2.value(),
+            #     "转盘舵机": self.doubleSpinBox_3.value(),
+            #     "转盘前后电机": self.doubleSpinBox_7.value(),
+            # }
+            # self.is_get_offset.emit(data)
+            print("获取初始化位置  [get_deviation_data]  ", get_deviation_data)
 
     def __del__(self):
         self.state = 2

+ 242 - 0
python/mcu/OtherSet.py

@@ -0,0 +1,242 @@
+import threading
+
+import settings
+from collections import defaultdict
+import time
+from .McuDebug import McuDebug
+
+
+class OtherSet():
+    def __init__(self, mcu):
+        super().__init__()
+        self.mcu = mcu
+        self.get_value_flag = False
+        self.last_value = defaultdict(float)
+        is_debug = True if settings.IS_DEBUG == "true" else False
+        self.mcu_debug = McuDebug(mcu, is_debug=is_debug, is_deviation=False)
+        # self.ampy_run = None
+        # 自动获取数据
+        # QTimer.singleShot(500, self.get_data_from_mcu)
+
+    def get_data_from_mcu(self, *args):
+        self.get()
+
+    def init(self):
+        self.ui.is_auto_send_base_info.setMinimum(0)
+        self.ui.is_auto_send_base_info.setMaximum(10000)
+        self.ui.is_move_retry.setMinimum(0)
+        self.ui.is_move_retry.setMaximum(10000)
+        self.ui.is_data_response.setMinimum(0)
+        self.ui.is_data_response.setMaximum(10000)
+        self.ui.low_speed.setMinimum(0)
+        self.ui.low_speed.setMaximum(10000)
+        self.ui.is_test.setMinimum(0)
+        self.ui.is_test.setMaximum(10000)
+        self.ui.to_init_mode.setMinimum(0)
+        self.ui.to_init_mode.setMaximum(10000)
+        self.ui.turntable_move_to_init_mode.setMinimum(0)
+        self.ui.turntable_move_to_init_mode.setMaximum(10000)
+        self.ui.led_count.setMinimum(0)
+        self.ui.led_count.setMaximum(10000)
+        self.ui.turntable_steering_angle_ratio.setMinimum(0)
+        self.ui.turntable_steering_angle_ratio.setMaximum(10000)
+        self.ui.is_manual_check.setMinimum(0)
+        self.ui.is_manual_check.setMaximum(10000)
+        self.ui.camera_steering_angle_ratio.setMinimum(0)
+        self.ui.camera_steering_angle_ratio.setMaximum(100000)
+        self.ui.is_auto_motor_to_disable.setMinimum(0)
+        self.ui.is_auto_motor_to_disable.setMaximum(10000)
+        self.ui.diff_dir.setMinimum(0)
+        self.ui.diff_dir.setMaximum(10000)
+        self.ui.is_auto_send_pos_info.setMinimum(0)
+        self.ui.is_auto_send_pos_info.setMaximum(10000)
+        self.ui.is_dog.setMinimum(0)
+        self.ui.is_dog.setMaximum(10000)
+        self.ui.has_been_set_motor_config.setMinimum(0)
+        self.ui.has_been_set_motor_config.setMaximum(10000)
+        # 相机舵机
+        self.ui.camera_degree.setSingleStep(0.1)
+        self.ui.camera_degree.setMinimum(-90)
+        self.ui.camera_degree.setMaximum(90)
+        self.ui.camera_degree.valueChanged.connect(
+            lambda x: self.change_value(x, "相机舵机")
+        )
+
+        self.ui.set.clicked.connect(self.set)
+        self.ui.get.clicked.connect(self.get)
+        self.ui.init_mcu.clicked.connect(self.init_mcu)
+        self.ui.pushButton.clicked.connect(self.check_and_upload)
+        self.ui.pushButton_2.clicked.connect(self.stop_mcu)
+        # 设定相机舵机偏移量
+        self.ui.set_camera_degree.clicked.connect(
+            lambda *args: self.set_deviation("相机舵机")
+        )
+
+    def check_and_upload(self, *args):
+        try:
+            ampy_class = settings.PLUGINS_DICT["ampy_scribe"]["AMPY插件"]
+        except:
+            return
+        self.s = threading.Thread(
+            target=ampy_class(top_windows=self.windows, windows=self).run, args=()
+        )
+        self.s.start()
+
+    def stop_mcu(self, *args):
+        self.mcu.stop_mcu()
+
+    def send_log(self, text):
+        if self.mcu:
+            self.mcu.send_log(text)
+
+    # 写入文件测试
+    def init_mcu(self, *args):
+        self.mcu.to_init_device_origin_point(device_name="mcu", is_force=True)
+
+    def change_value(self, value, name):
+        value = round(value, 2)
+        if self.last_value[name] - 1 <= value <= self.last_value[name] + 1:
+            if name == "相机电机":
+                self.mcu_debug.camera_high_motor(value=value)
+            if name == "相机舵机":
+                self.mcu_debug.camera_steering(value=value)
+            if name == "转盘舵机":
+                self.mcu_debug.turntable_steering(value=value)
+            if name == "转盘前后电机":
+                self.mcu_debug.turntable_position_motor(value=value)
+            if name == "翻板舵机中位":
+                self.mcu_debug.overturn_steering(value=value)
+            if name == "翻板舵机高位":
+                self.mcu_debug.overturn_steering(value=value)
+            if name == "翻板舵机上升速度":
+                pass
+            print(value, name)
+        self.last_value[name] = value
+
+    def set_deviation(self, name, camera_steering_deviation):
+        if name == "相机舵机":
+            # 设定相机舵机偏移量
+            # camera_steering_deviation = self.ui.camera_degree.value()
+            device_name = "camera_steering"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=0, deviation=camera_steering_deviation
+            )
+            # self.ui.camera_degree.setValue(0.0)
+
+    def setMcuInfo(self, data):
+        self.mcu.msg_type = "set_other_mcu_info"
+        if not self.get_value_flag:
+            self.mcu.sendSocketMessage(
+                code=1, msg="请先获取其他mcu配置信息", device_status=0, data=None
+            )
+            self.mcu.msg_type = "mcu"
+            return
+
+        # is_auto_send_base_info = data.get("is_auto_send_base_info")
+        # is_move_retry = data.get("is_move_retry")
+        # is_data_response = data.get("is_data_response")
+        # low_speed = data.get("low_speed")
+        # is_test = data.get("is_test")
+        # to_init_mode = data.get("to_init_mode")
+        # turntable_move_to_init_mode = data.get("turntable_move_to_init_mode")
+        # led_count = data.get("led_count")
+        # turntable_steering_angle_ratio = data.get("turntable_steering_angle_ratio")
+        # is_manual_check = data.get("is_manual_check")
+        # camera_steering_angle_ratio = data.get("camera_steering_angle_ratio")
+        # is_auto_motor_to_disable = data.get("is_auto_motor_to_disable")
+        # diff_dir = data.get("diff_dir")
+        # is_auto_send_pos_info = data.get("is_auto_send_pos_info")
+        # is_dog = data.get("is_dog")
+        # has_been_set_motor_config = data.get("has_been_set_motor_config")
+
+        # data = {
+        #     "is_auto_send_base_info": is_auto_send_base_info,
+        #     "is_move_retry": is_move_retry,
+        #     "is_data_response": is_data_response,
+        #     "low_speed": low_speed,
+        #     "is_test": is_test,
+        #     "to_init_mode": to_init_mode,
+        #     "turntable_move_to_init_mode": turntable_move_to_init_mode,
+        #     "led_count": led_count,
+        #     "turntable_steering_angle_ratio": turntable_steering_angle_ratio,
+        #     "is_manual_check": is_manual_check,
+        #     "camera_steering_angle_ratio": camera_steering_angle_ratio,
+        #     "is_auto_motor_to_disable": is_auto_motor_to_disable,
+        #     "diff_dir": diff_dir,
+        #     "is_auto_send_pos_info": is_auto_send_pos_info,
+        #     "is_dog": is_dog,
+        #     "has_been_set_motor_config": has_been_set_motor_config,
+        # }
+        self.mcu.set_to_mcu_other_info(data)
+        self.mcu.sendSocketMessage(code=0, msg="已完成设置", device_status=2, data=None)
+        print("已完成设置")
+        self.mcu.msg_type = "mcu"
+
+    def getMcuInfo(self, *args):
+        self.mcu.get_other_info()
+        _s = time.time()
+        last_num = self.mcu.last_mcu_other_info_data["num"]
+        print("_s", _s)
+        while 1:
+            time.sleep(0.1)
+            if time.time() - _s > 2:
+                return False
+            print(
+                "last_mcu_other_info_data  num",
+                self.mcu.last_mcu_other_info_data["num"],
+            )
+            if last_num < self.mcu.last_mcu_other_info_data["num"]:
+                data = self.mcu.last_mcu_other_info_data["data"]
+                print("获取中断", data)
+                break
+            # if self.mcu.last_mcu_other_info_data["time"] - _s > 0:
+            #     data = self.mcu.last_mcu_other_info_data["data"]
+            #     break
+        print("获取成功")
+        is_auto_send_base_info = data["is_auto_send_base_info"]
+        is_move_retry = data["is_move_retry"]
+        is_data_response = data["is_data_response"]
+        low_speed = data["low_speed"]
+        is_test = data["is_test"]
+        to_init_mode = data["to_init_mode"]
+        turntable_move_to_init_mode = data["turntable_move_to_init_mode"]
+        led_count = data["led_count"]
+        turntable_steering_angle_ratio = data["turntable_steering_angle_ratio"]
+        is_manual_check = data["is_manual_check"]
+        camera_steering_angle_ratio = data["camera_steering_angle_ratio"]
+        diff_dir = data["diff_dir"]
+        is_auto_send_pos_info = data["is_auto_send_pos_info"]
+        is_dog = data["is_dog"]
+        has_been_set_motor_config = data["has_been_set_motor_config"]
+
+        is_auto_motor_to_disable = data["is_auto_motor_to_disable"]
+        if is_auto_motor_to_disable is None:
+            is_auto_motor_to_disable = False
+        dataSend = {
+            "is_auto_send_base_info": is_auto_send_base_info,
+            "is_move_retry": is_move_retry,
+            "is_data_response": is_data_response,
+            "low_speed": low_speed,
+            "is_test": is_test,
+            "to_init_mode": to_init_mode,
+            "turntable_move_to_init_mode": turntable_move_to_init_mode,
+            "led_count": led_count,
+            "turntable_steering_angle_ratio": turntable_steering_angle_ratio,
+            "is_manual_check": is_manual_check,
+            "camera_steering_angle_ratio": camera_steering_angle_ratio,
+            "is_auto_motor_to_disable": is_auto_motor_to_disable,
+            "diff_dir": diff_dir,
+            "is_auto_send_pos_info": is_auto_send_pos_info,
+            "is_dog": is_dog,
+            "has_been_set_motor_config": has_been_set_motor_config,
+        }
+        self.get_value_flag = True
+        self.mcu.msg_type = "get_other_mcu_info"
+        self.mcu.sendSocketMessage(
+            code=0, msg="获取mcu其他配置信息", device_status=2, data=dataSend
+        )
+        self.mcu.msg_type = "mcu"
+
+    def __del__(self):
+        self.state = 2
+        self.t = None

+ 11 - 6
python/mcu/ProgramItem.py

@@ -15,12 +15,15 @@ class ProgramItem(BaseClass):
         super().__init__(BaseClass)
         # 1 表示等待中,2表示没有等待
         self.wait_state = 2
+        self.msg_type = "mcu"
+        print("action_data====>", action_data)
         self.data = action_data
         self.capture_one = DigiCam()
         captrure_folder_path = self.capture_one.getCaptureFolderPath()
         self.watch_dog = FileEventHandler()
         self.watch_dog.goods_art_no = goods_art_no
         self.watch_dog.image_index = image_index
+        self.watch_dog.mcu = mcu
         self.watch_dog.start_observer(captrure_folder_path)
         print("21 =========ProgramItem=======action_data=====")
         print(action_data)
@@ -72,7 +75,9 @@ class ProgramItem(BaseClass):
     def get_value(self, data, key, default=None):
         if key not in data:
             data[key] = default
+            print("取值模式==>获取到key:{}的值为:{}".format(key, data[key]))
             return default
+        print("默认值模式==>获取到key:{}的值为:{}".format(key, data[key]))
         return data[key]
 
     def reset(self):
@@ -202,10 +207,11 @@ class ProgramItem(BaseClass):
                     device_name="turntable_steering", value=self.turntable_angle
                 )
                 time.sleep(0.1)
+            self.mcu.send_all_cmd()
 
     def do_run(self, *args):
-        if not self.goods_art_no:  # and self.action_name != "初始化位置"
-            return False
+        # if not self.goods_art_no:  # and self.action_name != "初始化位置"
+        #     return False
         start_time = time.time()
         # ============连接MCU 处理步进电机与舵机等
         if settings.IS_MCU:
@@ -269,10 +275,9 @@ class ProgramItem(BaseClass):
         # print("{} 拍照时间:{}".format(self.action_name, time.time() - start_time))
         print("{}-{}已完成".format(self.mode_type, self.action_name))
 
-        if True:
-            if self.after_delay_time:
-                print("拍照后延时:{}".format(self.after_delay_time))
-                time.sleep(self.after_delay_time)
+        if self.after_delay_time:
+            print("拍照后延时:{}".format(self.after_delay_time))
+            time.sleep(self.after_delay_time)
         return True
 
     def rephotograph_one_pic(self, *args):

+ 5 - 2
python/mcu/RemoteControlV2.py

@@ -263,7 +263,10 @@ class RemoteControlV2(BaseClass):
             image_index = record.image_index + 1
             self.photo_take_state = 1
             insert_photo_records(
-                record.image_deal_mode, record.goods_art_no, image_index
+                record.image_deal_mode,
+                record.goods_art_no,
+                image_index,
+                record.action_id,
             )
             print("开始单拍1-插入数据")
             capture_one = DigiCam()
@@ -354,7 +357,7 @@ class RemoteControlV2(BaseClass):
                 self.sendSocketMessage(0, "处理单拍消息", data=_data, device_status=-1)
                 self.msg_type = "blue_tooth"
             self.sendSocketMessage(code=0, msg="", data=message, device_status=2)
-            if settings.IS_DEBUG:
+            if settings.IS_DEBUG == "true":
                 print("收到按键", button_value)
             return
 

+ 63 - 13
python/mcu/capture/module_watch_dog.py

@@ -6,8 +6,10 @@ import os
 from utils.utils_func import get_folder, check_path
 import datetime
 from utils.SingletonType import SingletonType
-from databases import CRUD,SqlQuery,PhotoRecord
 from databases import CRUD, SqlQuery, PhotoRecord
+import asyncio
+
+
 def updateImageRaw(time_str, image_path, goods_art_no, image_index):
     session = SqlQuery()
     crud = CRUD(PhotoRecord)
@@ -43,6 +45,7 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
         #     self.init_flag = True
         self.goods_art_no = None
         self.image_index = -1
+        self.mcu = None
         super().__init__()
         # self.window = window
         FileSystemEventHandler.__init__(self)
@@ -52,7 +55,7 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
         self.observer = None
         self.last_create_time = datetime.datetime.now()
 
-    def start_observer(self,path):
+    def start_observer(self, path):
         if self.observer != None:
             return
         print("图片保存目录:", path)
@@ -62,7 +65,9 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
 
         watch_path = self.check_and_get_real_dir(path)
         if watch_path:
-            self.observer.schedule(self, watch_path, recursive=True)  # recursive 遍历目录
+            self.observer.schedule(
+                self, watch_path, recursive=True
+            )  # recursive 遍历目录
             self.observer.start()
             print("开启开门狗目录监听")
         else:
@@ -73,7 +78,7 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
         逻辑:
         1、检查当前路径如包含Originals 则定位到对应Originals的父级目录
         2、如没有Originals,则认定为一个普通目录(且子文件夹没有Originals),否则定位到父级目录
-        
+
         """
         if not os.path.exists(watch_path):
             return None
@@ -91,9 +96,11 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
         year = now.year
         month = now.month
         day = now.day
-        path = r"{root_path}\Originals\{year}\{month}\{day}".format(root_path=root_path, year=year, month=month, day=day)
+        path = r"{root_path}\Originals\{year}\{month}\{day}".format(
+            root_path=root_path, year=year, month=month, day=day
+        )
         check_path(path)
-        print("watch_path:",path)
+        print("watch_path:", path)
         return path
 
     def on_moved(self, event):
@@ -104,10 +111,30 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
         #     if os.path.split(event.dest_path)[0] == settings.PhotoOutputDir:
         #         print("1111111")
         pass
+
     def updatePhotoRecord(self):
         session = SqlQuery()
         crud = CRUD(PhotoRecord)
         crud.read()
+
+    async def sendCallback(
+        self, file_path, goods_art_no=None, image_index=-1, msg_type="run_mcu_single"
+    ):
+        await asyncio.sleep(1)
+        self.mcu.msg_type = msg_type
+        # print("货号不存在,监听不写入", self.mcu.msg_type)
+        self.mcu.sendSocketMessage(
+            code=0,
+            msg="拍摄完成",
+            device_status=2,
+            data={
+                "file_path": file_path.replace("\\", "/"),
+                "goods_art_no": goods_art_no,
+                "image_index": image_index,
+            },
+        )
+        self.msg_type = "mcu"
+
     def on_created(self, event):
         if not event.is_directory:
             file_path = event.src_path
@@ -119,17 +146,38 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
             try:
                 take_time = time.time()
                 self.send_log("获取文件file_path:{}".format(file_path))
-                create_time = datetime.datetime.fromtimestamp(os.path.getctime(file_path))
+                create_time = datetime.datetime.fromtimestamp(
+                    os.path.getctime(file_path)
+                )
                 # print("获取文件create_time:{}".format(create_time))
-                self.get_photo_info(raw_path=file_path, create_time=create_time, take_time=take_time)
+                self.get_photo_info(
+                    raw_path=file_path, create_time=create_time, take_time=take_time
+                )
                 self.send_log("获取文件create_time:{}".format(create_time))
-                if self.goods_art_no == None:
-                    print("货号不存在,监听不写入")
-                    return
                 if file_path == None:
                     print("file_path不存在,监听不写入")
                     return
-                updateImageRaw(create_time, file_path, self.goods_art_no,self.image_index)
+                if self.goods_art_no == None:
+                    # print("货号不存在,监听不写入")
+                    loop = asyncio.new_event_loop()
+                    asyncio.set_event_loop(loop)
+                    loop.run_until_complete(self.sendCallback(file_path))
+                    loop.close()
+                    return
+                loop = asyncio.new_event_loop()
+                asyncio.set_event_loop(loop)
+                loop.run_until_complete(
+                    self.sendCallback(
+                        file_path,
+                        goods_art_no=self.goods_art_no,
+                        image_index=self.image_index,
+                        msg_type="run_mcu_update",
+                    )
+                )
+                loop.close()
+                updateImageRaw(
+                    create_time, file_path, self.goods_art_no, self.image_index
+                )
             except BaseException as e:
                 print("获取文件create_time失败", e)
                 self.send_log("获取文件处理失败{}".format(e))
@@ -155,7 +203,9 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
                     # 判断是否为文件
                     if os.path.isfile(file_path):
                         # 获取文件创建时间
-                        create_time = datetime.datetime.fromtimestamp(os.path.getctime(file_path))
+                        create_time = datetime.datetime.fromtimestamp(
+                            os.path.getctime(file_path)
+                        )
                         # 更新最早时间
                         if create_time > self.last_create_time:
                             self.last_create_time = create_time

+ 16 - 15
python/middleware.py

@@ -10,7 +10,7 @@ from settings import *
 import random
 import os, json
 from pydantic import BaseModel, validator, conint, constr, Field
-from databases import create_all_database, DeviceConfig,SysConfigs, CRUD,batch_insert_sys_configs, SqlQuery,batch_insert_device_configs
+
 
 # 关闭文档
 app = FastAPI()
@@ -35,20 +35,21 @@ app.add_middleware(
 
 @app.on_event("startup")
 def on_startup():
-    create_all_database()
-    session = SqlQuery()
-    device_config_crud = CRUD(DeviceConfig)
-    all_devices = device_config_crud.read_all(session)
-    if len(all_devices) == 0:
-        # 如果配置表中一条数据都没有,就将初始化数据全部插入到数据表中
-        actions = json.load(open("action.json", encoding="utf-8"))
-        batch_insert_device_configs(session,actions)
-    sys_config_crud = CRUD(SysConfigs)
-    all_sys_configs = sys_config_crud.read_all(session)
-    if len(all_sys_configs) == 0:
-        # 如果配置表中一条数据都没有,就将初始化数据全部插入到数据表中
-        sys_config_json = json.load(open("sys_configs.json", encoding="utf-8"))
-        batch_insert_sys_configs(session, sys_config_json)
+    pass
+    # create_all_database()
+    # session = SqlQuery()
+    # device_config_crud = CRUD(DeviceConfig)
+    # all_devices = device_config_crud.read_all(session)
+    # if len(all_devices) == 0:
+    #     # 如果配置表中一条数据都没有,就将初始化数据全部插入到数据表中
+    #     actions = json.load(open("action.json", encoding="utf-8"))
+    #     batch_insert_device_configs(session,actions)
+    # sys_config_crud = CRUD(SysConfigs)
+    # all_sys_configs = sys_config_crud.read_all(session)
+    # if len(all_sys_configs) == 0:
+    #     # 如果配置表中一条数据都没有,就将初始化数据全部插入到数据表中
+    #     sys_config_json = json.load(open("sys_configs.json", encoding="utf-8"))
+    #     batch_insert_sys_configs(session, sys_config_json)
 
 
 class UnicornException(Exception):

+ 1 - 0
python/model/device_config.py

@@ -10,6 +10,7 @@ class DeviceConfig(SQLModel, table=True):
     action_name: Optional[str] = Field(
         default=None, index=True, max_length=128, description="动作名称"
     )
+    is_system: Optional[bool] = Field(default=False,description="是否是系统配置")
     action_status: Optional[bool] = Field(default=True,description="是否启用;true或者false")
     action_index: Optional[int] = Field(default=999,description="排序,默认999")
     camera_height: Optional[int] = Field(default=None, description="相机高度;步长1;最小0;最大400")

+ 7 - 2
python/model/photo_record.py

@@ -1,15 +1,20 @@
 from sqlmodel import SQLModel, Field
 from datetime import datetime
 from typing import Optional
+import pytz
 
+TIME_ZONE = pytz.timezone("Asia/Shanghai")
 class PhotoRecord(SQLModel, table=True):
     __tablename__ = "photo_record"
     id: Optional[int] = Field(default=None, primary_key=True, index=True)
+    action_id: Optional[int] = Field(default=None)
     goods_art_no: Optional[str] = Field(max_length=128, nullable=False)
     image_path: Optional[str] = Field(default=None)
     image_index: Optional[int] = Field(default=None)
     image_deal_mode: Optional[int] = Field(default=None)
     photo_create_time: Optional[datetime] = Field(default=None)
-    update_time: Optional[datetime] = Field(default=None)
-    create_time: Optional[datetime] = Field(default_factory=datetime.utcnow)
+    update_time: Optional[datetime] = Field(
+        default_factory=lambda: datetime.now(TIME_ZONE)
+    )
+    create_time: Optional[datetime] = Field(default_factory=lambda: datetime.now(TIME_ZONE))
     delete_time: Optional[datetime] = Field(default=None)

+ 43 - 0
python/models.py

@@ -23,6 +23,13 @@ class ModelGetDeviceConfigDetail(BaseModel):
     )
 
 
+class ModelGetDeviceConfigDetailQuery(BaseModel):
+    """获取可执行程序命令列表"""
+
+    mode_type: Optional[str] = Field(default="执行左脚程序", index=True, max_length=128)
+    action_name: Optional[str] = Field(default="", index=True, max_length=128)
+
+
 class SaveDeviceConfig(BaseModel):
     """获取可执行程序命令列表"""
 
@@ -31,6 +38,7 @@ class SaveDeviceConfig(BaseModel):
     action_name: Optional[str] = Field(default="", index=True, max_length=128)
     action_index: Optional[int] = Field(default=None)
     action_status: Optional[bool] = Field(default=None)
+    is_system: Optional[bool] = Field(default=False)
     camera_height: Optional[int] = Field(default=None)
     camera_angle: Optional[float] = Field(default=None)
     number_focus: Optional[int] = Field(default=None)
@@ -55,3 +63,38 @@ class SysConfigParams(BaseModel):
     """系统配置"""
     key: str = Field(default=None, description="类型")
     value: str = Field(default=None, description="json数据")
+
+
+class TemplateItem(BaseModel):
+    """模板项"""
+
+    template_id: str = Field(description="模板名称")
+    template_local_classes: str = Field(description="模板名称")
+
+
+class MaineImageTest(BaseModel):
+    """模板项"""
+
+    file_path: str = Field(description="图片地址")
+
+
+class HandlerDetail(BaseModel):
+    """获取可执行程序命令列表"""
+
+    goods_art_no: list[str] = Field(default=None, description="货号")
+    token: str = Field(default="", description="惠利玛请求token")
+    template_image_order:str = Field(default=None, description="模板图片排序")
+    excel_path: Optional[str] = Field(default="", description="excel路径")
+    temp_name: str = Field(default="", description="选中的模板名称")
+    temp_list: list[TemplateItem] = Field(default=None, description="所有模板列表")
+    logo_path: Optional[str] = Field(default="", description="logo地址路径")
+
+
+class LogoParams(BaseModel):
+    """logo参数"""
+
+    logo_path: str = Field(default="", description="logo地址路径")
+    
+class LogoParamsupdate(BaseModel):
+    """系统配置"""
+    value: str = Field(default=None, description="json数据")

BIN
python/requestments.txt


+ 726 - 0
python/service/AutoDealPics.py

@@ -0,0 +1,726 @@
+import os
+
+
+#
+from .data import DataModeGenerateDetail
+from .base_deal import BaseDealImage
+from .run_main import RunMain
+from utils.utils_func import check_path
+from .upload_pic import UploadPic
+
+# 手动移动图片等操作
+from .manual_image_matching.m_image_matching_cotrol import (
+    MainMatchingWindow,
+)
+
+# 每个详情的模板
+from .detail_temp import TempItem
+
+from .load_plugins import LoadAllPlugins
+
+from functools import partial
+from threading import Lock
+
+# from module.view_control.MineQWidget import MineQWidget, DialogShow
+# from module.base_mode.base import *
+import threading
+import settings
+
+
+def queren_get_config():
+    config_data = {}
+    # config_data["cutout_is_enable"] = self.ui.cutout_is_enable.isChecked()
+    # config_data["cutout_is_pass"] = self.ui.cutout_is_pass.isChecked()
+    # config_data["detail_is_enable"] = self.ui.detail_is_enable.isChecked()
+    # config_data["detail_is_pass"] = self.ui.detail_is_pass.isChecked()
+    # config_data["upload_is_enable"] = self.ui.upload_is_enable.isChecked()
+    # config_data["upload_is_pass"] = self.ui.upload_is_pass.isChecked()
+
+    # # 保存配置
+    # settings.GOODS_DETAIL_CUTOUT_IS_ENABLE = self.ui.cutout_is_enable.isChecked()
+    # settings.GOODS_DETAIL_CUTOUT_IS_PASS = self.ui.cutout_is_pass.isChecked()
+    # settings.GOODS_DETAIL_DETAIL_IS_ENABLE = self.ui.detail_is_enable.isChecked()
+    # settings.GOODS_DETAIL_DETAIL_IS_PASS = self.ui.detail_is_pass.isChecked()
+    # settings.GOODS_DETAIL_UPLOAD_IS_ENABLE = self.ui.upload_is_enable.isChecked()
+    # settings.GOODS_DETAIL_UPLOAD_IS_PASS = self.ui.upload_is_pass.isChecked()
+
+    data_dict = {
+        # "goods_detail_cutout_is_enable": (
+        #     "是" if settings.GOODS_DETAIL_CUTOUT_IS_ENABLE else "否"
+        # ),
+        # "goods_detail_cutout_is_pass": (
+        #     "是" if settings.GOODS_DETAIL_CUTOUT_IS_PASS else "否"
+        # ),
+        # "goods_detail_detail_is_enable": (
+        #     "是" if settings.GOODS_DETAIL_DETAIL_IS_ENABLE else "否"
+        # ),
+        # "goods_detail_detail_is_pass": (
+        #     "是" if settings.GOODS_DETAIL_DETAIL_IS_PASS else "否"
+        # ),
+        # "goods_detail_upload_is_enable": (
+        #     "是" if settings.GOODS_DETAIL_UPLOAD_IS_ENABLE else "否"
+        # ),
+        # "goods_detail_upload_is_pass": (
+        #     "是" if settings.GOODS_DETAIL_UPLOAD_IS_PASS else "否"
+        # ),
+        "goods_detail_cutout_is_enable": "是",
+        "goods_detail_cutout_is_pass": "是",
+        "goods_detail_detail_is_enable": "是",
+        "goods_detail_detail_is_pass": "是",
+        "goods_detail_upload_is_enable": "是",
+        "goods_detail_upload_is_pass": "是",
+    }
+    settings.set_config(data_dict=data_dict, section="goods_detail")
+    return config_data
+
+
+# 抠图与详情生成
+class AutoDealPics:
+    # progress_sign = Signal(dict)
+    # text_show = Signal(str)
+    # progress_show_sign = Signal(bool)
+    # progress_end_sign = Signal()
+    # send_dialog_sign = Signal()
+    # change_state_sign = Signal(int)
+    # run_end_sign = Signal(dict)
+
+    def __init__(self, windows=None, top_windows=None):
+        super().__init__()
+        self.windows = windows
+        self.top_windows = top_windows
+        # self.ui = generate_goods_no_detail_pic_Ui_Form()
+        # self.ui.setupUi(self)
+        # self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
+
+        self.run_main = RunMain(windows=self)
+        # self.run_main.run_end_sign.connect(self.deal_run_end_sign)
+        # self.run_main.show_dialog_sign.connect(self.show_dialog)
+        # self.run_main.show_progress_detail_sign.connect(self.show_progress_detail)
+
+        # 程序执行信号
+        # self.run_end_sign.connect(self.deal_run_end_sign)
+
+        # # 改变状态
+        # self.change_state_sign.connect(self.set_state)
+
+        # self.setWindowTitle("详情生成(new)")
+        # 线程锁
+        self.lock = Lock()
+        self.image_dir = ""
+        # 0禁用  1进行中  2已结束 99等待结束
+        self.state = 2
+        self.data_mode_generate_detail = DataModeGenerateDetail()
+        self.init_cutout()
+        self.init_detail()
+        # self.init_schedule()
+        self.set_state(0)
+        # self.show()
+        # 延时检查是否存在匹配图片
+        # QTimer.singleShot(1000, self.check_and_matching_pics)
+
+    # 添加进度页面
+    # def init_schedule(self, *args):
+    #     self.ui.back.clicked.connect(self.back)
+    #     self.ui.stop.clicked.connect(self.to_stop)
+    #     self.ui.progressBar.hide()
+
+    def back(self, *args):
+        self.set_state(0)
+
+    def to_stop(self, *args):
+        self.state = 99
+
+    # 自动检查匹配图片
+    def check_and_matching_pics(self, *args, **kwargs):
+        func = partial(self.run_matching)
+        self.do_thread_run(func=func, call_back=self.run_matching_call, time_out=30)
+
+    # 运行自动匹配
+    def run_matching(self, *args):
+        image_dir = "{}/data".format(os.getcwd())
+        check_path(image_dir)
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        result = baseDealImage.dealMoveImage(image_dir=image_dir, callback_func=None)
+        # return {'code': 0, 'msg': '处理完成', 'target_path': output_path, 'data': {}}
+        return result
+
+    # 自动匹配的回调函数
+    def run_matching_call(self, return_data):
+
+        print(return_data)
+        code = return_data["code"]
+        if code == 0:
+            output_path = return_data["target_path"]
+            self.image_dir = "{}/{}".format(os.getcwd(), output_path)
+            self.show_img_dir()
+
+    def show_progress_detail(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    # todo ===========主方法入口===========
+    def deal_run_end_sign(self, config_data: dict):
+        if config_data["sign_text"] == "开始抠图":
+            # 先做整体校验
+            func = partial(self.run_main.check_before_cutout, config_data=config_data)
+            self.do_thread_run(
+                func=func,
+                call_back=self.run_main.check_for_cutout_image_first_call_back,
+                time_out=30,
+                is_show_mask=False,
+            )
+
+        print(config_data)
+        if config_data["sign_text"] == "已结束抠图处理":
+            if config_data["detail_is_enable"]:
+                # 先做整体校验
+                # temp_name=self.last_temp, temp_name_list=self.temp_list
+                func = partial(
+                    self.run_main.check_before_detail, config_data=config_data
+                )
+                self.do_thread_run(
+                    func=func,
+                    call_back=self.run_main.check_for_detail_first_call_back,
+                    time_out=30,
+                    is_show_mask=False,
+                )
+            else:
+                self.set_state(state_value=2)
+
+        if config_data["sign_text"] == "已结束详情处理":
+            if config_data["upload_is_enable"]:
+                to_deal_dir = "{}/软件-详情图生成".format(config_data["image_dir"])
+                print("to_deal_dir", to_deal_dir)
+                if os.path.exists(to_deal_dir):
+                    self.upload_pic = UploadPic(
+                        windows=self, to_deal_dir=to_deal_dir, config_data=config_data
+                    )
+                    self.upload_pic.run_end_sign.connect(self.deal_run_end_sign)
+                    self.upload_pic.show_progress_detail_sign.connect(
+                        self.show_progress_detail
+                    )
+                    self.upload_pic.run()
+                    # threading.Thread(target=self.upload_pic.run_by_thread, args=()).start()
+                else:
+                    self.set_state(state_value=2)
+            else:
+                self.set_state(state_value=2)
+
+        if config_data["sign_text"] == "结束":
+            self.set_state(state_value=2)
+
+    def change_temp_mode(self, *args, **kwargs):
+        if self.ui.check_use_excel.isChecked():
+            self.ui.temp_list_get.hide()
+        else:
+            self.ui.temp_list_get.show()
+
+    # 重新载入模块
+    def reload_temp_mode(self, *args, **kwargs):
+        a = QMessageBox.question(
+            self, "确认", "是否重启加载模板", QMessageBox.Yes | QMessageBox.No
+        )
+        if a != QMessageBox.Yes:
+            return
+
+        # 检查资源包
+        if settings.env == "prod":
+            if self.top_windows:
+                self.top_windows.check_resources_download()
+
+        settings.PLUGINS_DICT = LoadAllPlugins(windows=None).load()
+        # 重新载入模板
+        self.load_temp_list()
+
+    #  获取页面配置
+    def get_config(self, *args):
+        if not settings.IsLogin:
+            QMessageBox.question(
+                self, "确认", "请先登录", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        logo_name = self.ui.logo_select_ui.currentText()
+        if logo_name == "请选择":
+            QMessageBox.question(
+                self, "确认", "请先选择logo模板", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if self.ui.cutout_exquisite.isChecked() and not bool(settings.IsExquisiteMode):
+            QMessageBox.question(
+                self, "确认", "暂无 精细化抠图权限", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if "失败" in self.image_dir:
+            self.WaringMessage("文件夹路径不能含有 失败 字样")
+            return
+
+        image_dir = self.ui.path_name.text()
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        resize_image_view = self.ui.need_resize.text()
+        logo_name = self.ui.logo_select_ui.currentText()
+        settings.MATCHING_LAST_LOGO_TEMP = logo_name
+        logo_path = self.logo_path_dict[logo_name]
+
+        cutout_mode = "1"
+        if self.ui.cutout_exquisite.isChecked():
+            cutout_mode = "2"
+        configData = {
+            "imageorder": image_order,
+            "resize_image_view": resize_image_view,
+            "is_check_number": str(is_check_number),
+            "cutout_mode": str(cutout_mode),
+            "matching_mode_last": settings.MATCHING_MODE_LAST,
+            "matching_last_temp": self.last_temp,
+            "matching_last_logo_temp": logo_name,
+        }
+        settings.cutimage_dict["imageorder"] = image_order
+        settings.cutimage_dict["resize_image_view"] = resize_image_view
+        settings.cutimage_dict["is_check_number"] = str(is_check_number)
+        settings.cutimage_dict["cutout_mode"] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section="cutimage")
+        config_data = {}
+        config_data["image_dir"] = image_dir
+        config_data["image_order"] = image_order
+        config_data["is_check_number"] = is_check_number
+        config_data["resize_image_view"] = resize_image_view
+        config_data["cutout_mode"] = cutout_mode
+        config_data["logo_path"] = logo_path
+        config_data["special_goods_art_no_folder_line"] = ""
+
+        # =======================================
+
+        settings.GOODS_DETAIL_IS_USE_EXCEL = self.is_use_excel
+        settings.GOODS_DETAIL_EXCEL_PATH = self.excel_path
+        settings.GOODS_DETAIL_LAST_IMAGE_PATH = self.image_dir
+        settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL = self.is_check_color_is_all
+        settings.GOODS_DETAIL_IS_PASS = self.is_check_is_pass
+        settings.GOODS_DETAIL_LAST_TEMP = self.last_temp
+
+        data_dict = {
+            "is_use_excel": "是" if self.is_use_excel else "否",
+            "excel_path": self.excel_path if self.excel_path else "",
+            "last_image_path": self.image_dir if self.image_dir else "",
+            "is_check_color_is_all": "是" if self.is_check_color_is_all else "否",
+            "last_temp": self.last_temp,
+            "goods_detail_temp_mode_last": settings.GOODS_DETAIL_TEMP_MODE_LAST,
+        }
+        settings.set_config(data_dict=data_dict, section="goods_detail")
+        # 红蜻蜓/惠利玛,且为非表格数据必须需要登录处理
+        assigned_page = self.ui.assigned_page.text()
+        if assigned_page:
+            assigned_page_dict = self.check_assigned_page(assigned_page)
+            if not assigned_page_dict:
+                self.WaringMessage("指定模板填写格式不合规")
+        else:
+            assigned_page_dict = {}
+
+        config_data["is_use_excel"] = self.is_use_excel
+        config_data["excel_path"] = self.excel_path
+        config_data["is_check_color_is_all"] = self.is_check_color_is_all
+        config_data["assigned_page_dict"] = assigned_page_dict
+        config_data["temp_class"] = self.temp_class
+        config_data["temp_name"] = self.last_temp
+        config_data["temp_name_list"] = self.temp_list
+        config_data["target_error_folder"] = self.target_error_folder
+        print("=============config_data=============")
+        print(config_data)
+        return config_data
+
+    # 抠图相关处理
+    def init_cutout(self):
+        # 指定单独处理的颜色文件夹
+        self.special_goods_art_no_folder = []
+        self.show_img_dir()
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.line_image_pos.setText(settings.cutimage_dict["imageorder"])
+        self.ui.need_resize.setText(settings.cutimage_dict["resize_image_view"])
+        self.ui.check_image_total.setChecked(
+            bool(settings.cutimage_dict["is_check_number"])
+        )
+
+        if settings.DEFAULT_CUTOUT_MODE == "普通抠图":
+            self.ui.cutout_nomal.setChecked(True)
+        elif settings.DEFAULT_CUTOUT_MODE == " 精细化抠图":
+            self.ui.cutout_exquisite.setChecked(True)
+
+        if not bool(settings.IsExquisiteMode):
+            self.ui.cutout_exquisite.setEnabled(False)
+
+        self.progress_sign.connect(self.show_progress)
+        self.ui.run.clicked.connect(self.run)  # 抠图与主图加工
+
+        self.set_logo_selected()
+        # 手动匹配图片
+        self.ui.manual_matching_pic.mousePressEvent = (
+            self.manual_matching_pic_mousePressEvent
+        )
+
+    def init_detail(self):
+        # 使用使用表格数据
+        self.is_use_excel = settings.GOODS_DETAIL_IS_USE_EXCEL
+        self.excel_path = settings.GOODS_DETAIL_EXCEL_PATH
+
+        # 目标文件夹
+        self.image_dir = settings.GOODS_DETAIL_LAST_IMAGE_PATH
+        # 对应的错误文件夹目录
+        if self.image_dir and os.path.exists(self.image_dir):
+            self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        else:
+            self.target_error_folder = ""
+
+        # 检查颜色是否齐全
+        self.is_check_color_is_all = settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL
+        # 已生成主图的自动过滤
+        self.is_check_is_pass = settings.GOODS_DETAIL_IS_PASS
+
+        # 上次使用的详情模板
+        self.last_temp = settings.GOODS_DETAIL_LAST_TEMP
+
+        # 重新载入模块
+        self.ui.reload_func.mousePressEvent = self.reload_temp_mode
+        # 手工整理数据
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic
+
+        # -------初始化页面--------
+        if self.is_use_excel:
+            self.ui.check_use_excel.setChecked(True)
+        else:
+            self.ui.check_use_excel.setChecked(False)
+
+        if self.excel_path:
+            self.ui.excel_path_show.setText(self.excel_path)
+
+        if self.image_dir:
+            self.ui.path_name.setText(self.image_dir)
+
+        if self.is_check_color_is_all:
+            self.ui.check_image_total_2.setChecked(True)
+        else:
+            self.ui.check_image_total_2.setChecked(False)
+
+        # 是否开启使用外部数据
+        self.ui.check_use_excel.toggled.connect(self.check_box_change)
+        self.ui.check_image_total_2.toggled.connect(self.check_box_change)
+        self.check_box_change()
+
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.select_excel.mousePressEvent = self.select_excel_path
+
+        # ====================================
+        self.progress_sign.connect(self.show_progress)
+
+        # 复制Excel模板
+        self.ui.get_excel_temp.mousePressEvent = self.copy_excel
+        self.change_temp_mode()
+
+        # 加载模板
+        QTimer.singleShot(1000, self.load_temp_list)
+
+    def manual_matching_pic_mousePressEvent(self, event):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    # 手工整理数据
+    def manual_matching_pic(self, *args, **kwargs):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    def load_temp_list(self):
+        # 注册模板类
+        self.temp_class = {}
+        self.temp_list = []
+        plugins_company_name = "企业名称"
+
+        if settings.PROJECT == "红蜻蜓":
+            self.temp_class = {}
+            plugins_company_name = "红蜻蜓"
+
+        elif settings.PROJECT == "惠利玛":
+            if "惠利玛" in settings.Company:
+                self.temp_class = {}
+                plugins_company_name = "惠利玛"
+            if "小苏" in settings.Company:
+                self.temp_class = {}
+                plugins_company_name = "小苏"
+
+        # 通过动态进行加载获取
+        if settings.PLUGINS_DICT:
+            print("554  settings.PLUGINS_DICT")
+            for _key, _value in settings.PLUGINS_DICT["detail_template"].items():
+                print(_key, _value)
+            try:
+                if "全部" in settings.PLUGINS_DICT["detail_template"]:
+                    for i, v in settings.PLUGINS_DICT["detail_template"][
+                        "全部"
+                    ].items():
+                        self.temp_class[i] = v
+                if plugins_company_name in settings.PLUGINS_DICT["detail_template"]:
+                    for i, v in settings.PLUGINS_DICT["detail_template"][
+                        plugins_company_name
+                    ].items():
+                        self.temp_class[i] = v
+            except BaseException as e:
+                print("209  通过动态进行加载详情获取error", e)
+                pass
+
+        # 模板名称列表
+        self.temp_list = [x for x in self.temp_class]
+        print("570 temp_list", self.temp_list)
+
+        # 删除已渲染的模板
+        for i in self.ui.show_temp_list.findChildren(TempItem):
+            i.deleteLater()
+
+        # 模板列表
+        x, y = 10, 10
+        self.ui.show_temp_list.setMinimumSize(self.ui.widget_10.width(), 100)
+        self.ui.show_temp_list.resize(self.ui.widget_10.width(), 100)
+        temp_high = 100
+        for temp_name in self.temp_list:
+            _class = self.temp_class[temp_name]
+            _d = _class.get_temp_pic_info(_class.root)
+            temp_image_path = _d["temp_pic_path"]
+            # print("224 temp_image_path",temp_image_path)
+            # temp_item = TempItem(self.ui.scrollArea, temp_name, temp_image_path)
+            # temp_item.select_sign.connect(self.change_temp_select)
+            # self.ui.horizontalLayout_18.insertWidget(
+            #     self.ui.horizontalLayout_18.count() - 1, temp_item
+            # )
+            temp_item = TempItem(self.ui.show_temp_list, temp_name, temp_image_path)
+            temp_high = temp_item.height()
+            temp_item.select_sign.connect(self.change_temp_select)
+            temp_item.move(x, y)
+            x += temp_item.width()
+            x += 10
+            if x > self.ui.show_temp_list.width() - temp_item.width():
+                y += temp_item.height()
+                y += 10
+                x = 10
+
+        y += 100
+        y += temp_high
+        print(y)
+        self.ui.scrollAreaWidgetContents_3.setMinimumSize(x, self.height() + y)
+        self.ui.show_temp_list.setMinimumSize(x, y)
+        self.ui.show_temp_list.resize(x, y)
+        print(self.ui.show_temp_list.height())
+
+        # 设置默认值
+        if self.last_temp not in self.temp_list:
+            if self.temp_list:
+                self.last_temp = self.temp_list[0]
+
+        # 设置值
+        for temp_item in self.ui.show_temp_list.findChildren(TempItem):
+            temp_item.select()
+            break
+
+    def change_temp_select(self, _id):
+        self.last_temp = _id
+        print("已设置模板的值为:{}".format(self.last_temp))
+        for temp_item in self.ui.show_temp_list.findChildren(TempItem):
+            if temp_item.id != _id:
+                temp_item.cancel_select()
+
+    def copy_excel(self, *args, **kwargs):
+        excel_path = r"{}\resources\init\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(excel_path):
+            return
+
+        self.check_path(r"{}\temp".format(os.getcwd()))
+        self.check_path(r"{}\temp\excel_temp".format(os.getcwd()))
+        new_excel_path = r"{}\temp\excel_temp\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(new_excel_path):
+            shutil.copy(excel_path, new_excel_path)
+        # 打开文件夹
+        os.startfile(r"{}\temp\excel_temp".format(os.getcwd()))
+
+    def check_box_change(self, *args):
+        if self.ui.check_use_excel.isChecked():
+            self.is_use_excel = True
+            self.ui.widget.show()
+            self.ui.temp_list_get.hide()
+        else:
+            self.is_use_excel = False
+            self.ui.widget.hide()
+            self.ui.temp_list_get.show()
+
+        if self.ui.check_image_total_2.isChecked():
+            self.is_check_color_is_all = True
+        else:
+            self.is_check_color_is_all = False
+
+    # 设置选择logo模板
+    def set_logo_selected(self):
+        logo_root_path = ""
+        if settings.PROJECT == "红蜻蜓":
+            logo_root_path = r"{}\resources\LOGO\HQT".format(os.getcwd())
+        elif settings.PROJECT == "惠利玛":
+            if "小苏" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\xiaosushuoxie".format(os.getcwd())
+            if "惠利玛" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\HLM".format(os.getcwd())
+
+        self.logo_path_dict = {"请选择": "", "无logo": ""}
+
+        if logo_root_path:
+            if os.path.exists(logo_root_path):
+                image_list = get_images(logo_root_path)
+                for image_data in image_list:
+                    self.logo_path_dict[image_data["file_name"]] = image_data[
+                        "file_path"
+                    ]
+
+        self.ui.logo_select_ui.addItems([x for x in self.logo_path_dict])
+
+        self.ui.logo_select_ui.setCurrentText("请选择")
+        if settings.MATCHING_LAST_LOGO_TEMP:
+            if settings.MATCHING_LAST_LOGO_TEMP in [x for x in self.logo_path_dict]:
+                self.ui.logo_select_ui.setCurrentText(settings.MATCHING_LAST_LOGO_TEMP)
+
+    def select_excel_path(self, *args, **kwargs):
+        folder = QFileDialog.getOpenFileName(self, "选取excel", "./")[0]
+        if "xlsx" in folder:
+            # if len(folder) > 20:
+            #     text = folder[-20:]
+            # else:
+            #     text = folder
+
+            self.ui.excel_path_show.setText(folder)
+            self.excel_path = folder
+            print("folder", folder)
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(
+            self, "选取文件夹", settings.GOODS_DETAIL_LAST_IMAGE_PATH
+        )
+        if folder:
+            # todo 增加检测是否有正常文件文件夹
+            folder = check_goods_folder(folder)
+            if folder is None:
+                QMessageBox.question(
+                    self,
+                    "确认",
+                    "您已选的文件夹下不存在任何有效数据",
+                    QMessageBox.Yes,
+                )
+                return
+            else:
+                self.image_dir = folder
+                self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+                self.show_img_dir()
+
+    def change_img_dir_by_text(self, folder):
+        self.image_dir = folder
+        self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        self.show_img_dir()
+
+    def show_img_dir(self):
+        self.ui.path_name.setText(self.image_dir)
+
+    def set_state(self, state_value: int):
+        # 0禁用  1进行中  2已结束
+        if state_value not in [0, 1, 2, 99]:
+            return
+        self.state = state_value
+        # if self.state == 0:
+        #     self.ui.stackedWidget.setCurrentIndex(0)
+        # if self.state == 1:
+        #     self.ui.stackedWidget.setCurrentIndex(1)
+        #     self.ui.progressBar.setValue(0)
+        #     self.ui.textBrowser_4.clear()
+        #     self.ui.back.hide()
+        #     self.ui.stop.show()
+
+        # if self.state == 2:
+        #     self.ui.back.show()
+        #     self.ui.stop.hide()
+
+        #     pass
+
+    def check_path(self, _path):
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def show_progress(self, *args, **kwargs):
+        pass
+
+    # todo 主程序处理
+    def run(self):
+        config_data = self.get_config()
+        if not config_data:
+            return
+        # self.queren = QueRen(self)
+        # ret = self.queren.exec()
+        # if ret == QDialog.Accepted:
+        #     print("对话框以接受状态关闭")
+        for key, value in queren_get_config().items():
+            config_data[key] = value
+        config_data["sign_text"] = "开始抠图"
+        for key, value in config_data.items():
+            print("key:{},  value:{}".format(key, value))
+        self.set_state(state_value=1)
+        self.run_end_sign.emit(config_data)
+
+    # 解析指定模板页面数据
+    def check_assigned_page(self, text: str):
+
+        try:
+            # temp_dict 示例{“4”:“xiaosushuoxie-4”}
+            temp_dict = {}
+            for i in self.temp_list:
+                temp_dict[i[-1]] = i
+
+            text = text.replace(" ", "")
+            text = text.replace(",", ",")
+            text = text.replace(":", ":")
+            text = text.replace(";", ",")
+            assigned_page_list = text.split(",")
+            assigned_page_dict = {}
+            for i in assigned_page_list:
+                if not i:
+                    continue
+                _k, _v = i.split(":")
+                _k = temp_dict[_k]
+                if _k not in assigned_page_dict:
+                    assigned_page_dict[_k] = []
+                if _v not in assigned_page_dict[_k]:
+                    assigned_page_dict[_k].append(_v)
+            # assigned_page_dict 数据结构,一个模板中存在多个页面数值。
+            return assigned_page_dict
+        except:
+            return {}
+
+    # 弹出对话框
+    def show_dialog(self, data):
+        windows = data["windows"]
+        windows.dialog_result = ""
+        my_dialog = DialogShow(
+            self,
+            text=data["text"],
+            button_1=data["button_1"] if "button_1" in data else None,
+            button_2=data["button_2"] if "button_2" in data else None,
+            button_3=data["button_3"] if "button_3" in data else None,
+        )
+        ret = my_dialog.exec()
+        print("641  my_dialog.flag_name", my_dialog.flag_name)
+        # 根据用户的选择发出相应的信号
+        windows.dialog_result_signal.emit(my_dialog.flag_name)

+ 102 - 0
python/service/OnePicTest.py

@@ -0,0 +1,102 @@
+import threading
+import time
+import os
+import settings
+
+from service.remove_bg_ali import RemoveBgALi
+from service.generate_main_image.grenerate_main_image_test import (
+    GeneratePic,
+)
+from service.generate_main_image.image_deal_base_func import *
+from functools import partial
+import io
+from PIL import Image
+from .base import check_path, get_date_time_original
+from middleware import UnicornException
+
+class OnePicTest():
+    # is_end = Signal()
+
+    def __init__(self,  pic_path="", is_auto_closed=False):
+        # super().__init__()
+        # 加载默认配置
+        # self.ui = Ui_Form()
+        # self.ui.setupUi(self)
+        self.pic_path = pic_path
+        self.is_auto_closed = False
+        # self.init()
+        # self.show()
+        # if pic_path:
+        #     self.run()
+
+    def call_back_deal_image(self, data):
+        # 处理后回调
+        print(data)
+        if data["code"] == 0:
+            return data
+        else:
+            if data["message"]:
+                raise UnicornException(data["message"])
+
+    def deal_image(self, image_path):
+        # 处理加工图片
+        return_data = {"code": 99, "message": "", "data": {}}
+
+        # =============清空temp文件夹================
+        check_path("temp")
+        check_path("temp\pic_test")
+        root = r"{}\temp\pic_test".format(os.getcwd())
+        for file_name in os.listdir(root):
+            path = "{}\{}".format(root, file_name)
+            if os.path.isfile(path):
+                os.remove(path)
+
+        # ==============抠图处理=====================
+        remove_pic_ins = RemoveBgALi()
+        file = os.path.split(image_path)[1]
+        cut_image_path = r"{}\temp\pic_test\{}.png".format(
+            os.getcwd(), os.path.splitext(file)[0]
+        )
+        remove_pic_ins.get_image_cut(file_path=image_path, out_file_path=cut_image_path)
+
+        # ==============生成主图====================
+        main_out_path = r"{}\temp\pic_test\{}-主图.jpg".format(
+            os.getcwd(), os.path.splitext(file)[0]
+        )
+        print("image_path", image_path)
+        print("cut_image_path", cut_image_path)
+        print("main_out_path", main_out_path)
+        try:
+            GeneratePic().run(
+                image_path=image_path,
+                cut_image_path=cut_image_path,
+                out_path=main_out_path,
+                image_deal_mode=1,
+                resize_mode=1,
+                out_pic_size=1024,
+                is_logo=False,
+            )
+        except:
+            raise UnicornException("处理失败,请重试")
+
+        # return_data["code"] = 0
+        # return_data["data"]["image_path"] = image_path
+        # return_data["data"]["image_cutout_path"] = cut_image_path
+        # return_data["data"]["image_main_path"] = main_out_path
+        return main_out_path
+
+    def HandlerMainImage(self):
+        # 先做整体校验
+        if not os.path.exists(self.pic_path):
+            raise UnicornException("图片不存在")
+        image_pth = Image.open(self.pic_path)
+        if image_pth.mode != "RGB":
+            raise UnicornException("抠图图片不能是PNG")
+        return_data = self.deal_image(self.pic_path)
+        # if return_data["message"]:
+        #     raise UnicornException(return_data["message"])
+        return return_data.replace("\\", "/")
+
+if __name__ == "__main__":
+    pic = OnePicTest(pic_path="C:/Users/15001/Desktop/mmexport1739929178317.jpg")
+    pic.HandlerMainImage()

+ 1007 - 0
python/service/auto_deal_pics/base_deal.py

@@ -0,0 +1,1007 @@
+import json
+from module.view_control.generate_goods_art_no_table.module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+
+from module.view_control.generate_main_image.grenerate_main_image_test import GeneratePic
+from import_qt_mode import *
+from threading import Lock
+
+import settings
+from collections import defaultdict
+from module.base_mode.remove_bg_ali import RemoveBgALi, Picture
+from module.cutout_mode.deal_cutout import DealCutout
+
+from module.view_control.matching_photos.data import DataModeMatchPhoto
+from module.base_mode.base import *
+
+from module.view_control.auto_deal_pics.data import DataModeAutoDealPics
+import time
+from module.base_mode.image_pic_deal import OnePicDeal
+
+"""
+照片自动货号匹配 将图片放置在指定文件夹下,并自动对应不同的货号进行整理
+"""
+
+_Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF', ".jpge", ".JPGE"]
+
+
+class BaseDealImage(object):
+    def __init__(self, image_dir=None):
+        self.goods_images_count_dict = defaultdict(int)
+        self.dataModeMatchPhoto = DataModeMatchPhoto()
+        # 数据模型
+        self.data_mode_auto_deal_pics = DataModeAutoDealPics()
+        self.image_dir = image_dir
+        pass
+
+    def run_main(self, all_goods_art_no_folder_data, callback_func=None, cutout_mode=None,
+                 resize_image_view=None, windows=None, logo_path=None, image_order_list=None):
+        # 对所有缺失已抠图的进行抠图处理
+        self.run_cutout_image(all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                              callback_func=callback_func,
+                              cutout_mode=cutout_mode,
+                              windows=windows,
+                              )
+        error_num = 0
+        successful_num = 0
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            if windows:
+                if windows.state != 1:
+                    break
+            folder_name = goods_art_no_folder_data["folder_name"]
+            callback_func("开始处理文件夹==========  {} ".format(folder_name))
+            if settings.IS_TEST:
+                flag = self.shoes_run_one_folder_to_deal(goods_art_no_folder_data=goods_art_no_folder_data,
+                                                         resize_image_view=resize_image_view,
+                                                         logo_path=logo_path,
+                                                         image_order_list=image_order_list,
+                                                         callback_func=callback_func,
+                                                         windows=windows,
+                                                         )
+                if flag is None:
+                    callback_func("货号:{} 数据异常".format(folder_name))
+                else:
+                    if flag:
+                        successful_num += 1
+                        callback_func("货号:{} 图片生成处理成功".format(folder_name))
+                    else:
+                        error_num += 1
+                        callback_func("货号:{} 图片生成处理失败".format(folder_name))
+            else:
+                try:
+                    flag = self.shoes_run_one_folder_to_deal(goods_art_no_folder_data=goods_art_no_folder_data,
+                                                             resize_image_view=resize_image_view,
+                                                             logo_path=logo_path,
+                                                             image_order_list=image_order_list,
+                                                             callback_func=callback_func,
+                                                             windows=windows,
+                                                             )
+                    if flag is None:
+                        callback_func("货号:{} 数据异常".format(folder_name))
+                    else:
+                        if flag:
+                            successful_num += 1
+                            callback_func("货号:{} 图片生成处理成功".format(folder_name))
+                        else:
+                            error_num += 1
+                            callback_func("货号:{} 图片生成处理失败".format(folder_name))
+                except BaseException as e:
+                    error_num += 1
+                    callback_func("货号:{} 图片生成处理异常,原因:{}".format(folder_name, e))
+        callback_func("处理成功:{}个,失败:{}".format(successful_num, error_num))
+
+    def checkImageAmount(self, image_dir: str, amount: int, todo_goods_art_no_folder_name_list=None) -> dict:
+        result = {'code': 0, 'msg': '', 'data': {}}
+        for goods_art_no_folder in self.list_dir(image_dir):
+            # 指定内容检查
+            if todo_goods_art_no_folder_name_list is not None:
+                if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                    continue
+
+            if not os.path.isdir("{}/{}".format(image_dir, goods_art_no_folder)):
+                continue
+            if "软件" in goods_art_no_folder:
+                continue
+            if "无法" in goods_art_no_folder:
+                continue
+            if "原始图" not in self.list_dir("{}/{}".format(image_dir, goods_art_no_folder)):
+                result['data'][goods_art_no_folder] = '文件夹下,没有 原始图 文件夹\n'
+                continue
+
+            # 计算单个文件夹原图数量
+            images = [x for x in self.list_dir("{}/{}/原始图".format(image_dir, goods_art_no_folder))]
+            image_num = 0
+            for pic_file_name in images:
+                _, e = os.path.splitext(pic_file_name)
+                if e in _Type:
+                    image_num += 1
+            # if self.number_pictures != 0:
+            if image_num > amount:
+                result['data'][goods_art_no_folder] = '货号图片大于{}张~\n'.format(amount)
+            if image_num < 2:
+                result['data'][goods_art_no_folder] = '货号图片小于于2张~\n'
+        if result['data']:
+            result['code'] = 1
+        return result
+
+    def check_folders_image_amount(self, all_goods_art_no_folder_data, image_order_list):
+        amount = len(image_order_list)
+        message = ""
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            folder_path = goods_art_no_folder_data["folder_path"]
+            folder_name = goods_art_no_folder_data["folder_name"]
+            images = [x for x in self.list_dir("{}/原始图".format(folder_path))]
+            image_num = 0
+            for pic_file_name in images:
+                _, e = os.path.splitext(pic_file_name)
+                if e in _Type:
+                    image_num += 1
+            if image_num > amount:
+                goods_art_no_folder_data["label"] = "错误"
+                message += '货号{}:图片大于{}张~\n'.format(folder_name, amount)
+            if image_num < 2:
+                message += '货号{}:图片小于于2张~\n'.format(folder_name)
+
+        return all_goods_art_no_folder_data, message
+
+    def check_one_folder_image_amount(self, folder_data, amount: int):
+        # 计算单个文件夹原图数量
+        images = [x for x in self.list_dir("{}/原始图".format(folder_data["folder_path"]))]
+        image_num = 0
+        for pic_file_name in images:
+            _, e = os.path.splitext(pic_file_name)
+            if e in _Type:
+                image_num += 1
+        if image_num > amount:
+            return False, '货号{}:图片大于{}张~\n'.format(folder_data["folder_name"], amount)
+        if image_num < 2:
+            return False, '货号{}:图片小于于2张~\n'.format(folder_data["folder_name"])
+        return True, ""
+
+    # 指定的图片顺序
+    def getImageOrder(self, image_order: str, resize_image_view: str):
+        imageOrderList = image_order.replace(",", ",").replace(' ', '').replace('图', '').split(",")
+        if len(set(imageOrderList)) != len(imageOrderList):
+            return {'code': 1, 'msg': '图片位置与顺序重复,请检查您的输入'}
+
+        for val in imageOrderList:
+            if val not in ["俯视", "侧视", "后跟", "鞋底", "内里", "组合", "组合2", "组合3", "组合4", "组合5"]:
+                return {'code': 1, 'msg': '可选项为:俯视,侧视,后跟,鞋底,内里,组合,组合2,组合3,组合4,组合5'}
+
+        if resize_image_view not in imageOrderList:
+            return {'code': 1, 'msg': '缩小的步骤必须是你填写的图片顺序中'}
+
+        return {'code': 0, 'msg': 'sucess', 'imageOrderList': imageOrderList}
+
+    def shoes_run_one_folder_to_deal(self,
+                                     goods_art_no_folder_data,
+                                     image_order_list: list,
+                                     resize_image_view: str,
+                                     logo_path="",
+                                     windows=None,
+                                     callback_func=None):
+        """
+        操作步骤:
+        1、查询每个图片的角度
+        2、
+        """
+
+        is_successful = True
+
+        folder_path = goods_art_no_folder_data["folder_path"]
+        folder_name = goods_art_no_folder_data["folder_name"]
+        all_original_images = self.get_images("{}/原始图".format(folder_path))
+        self.check_path('{}/800x800'.format(folder_path))
+        self.crate_all_folders(folder_path)
+
+        if not all_original_images:
+            return None
+            # _ = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+        for index, image_dict in enumerate(all_original_images):
+            if index < len(image_order_list):
+                image_dict["image_view"] = image_order_list[index]
+            else:
+                image_dict["image_view"] = '其他{}'.format(len(image_order_list) - index + 1)
+
+        # ====================处理所有图片的顺序====================
+        # ["俯视", "侧视", "后跟", "鞋底", "内里"]
+        _config = {"俯视": 1,
+                   "侧视": 2,
+                   "后跟": 3,
+                   "鞋底": 4,
+                   "内里": 5, }
+
+        r = time.time()
+        n = 0
+        _index = 0
+        # 检查是否有重复的角度
+        _d_views = []
+        f = True
+        for image_dict in all_original_images:
+            n += 1
+            _index += 1
+            image_dict["old_file_name"] = image_dict["file_name"]
+            image_dict["random_name"] = "{}-{}".format(r, n)
+            if image_dict["image_view"] in _config:
+                if image_dict["image_view"] not in _d_views:
+                    _d_views.append(image_dict["image_view"])
+                else:
+                    callback_func("货号:{} 处理失败".format(folder_name))
+                    # self.show_progress_detail("货号图{}  存在多个{} 角度~".format(goods_art_no_folder, image_dict["image_view"]))
+                    return None
+
+                image_dict["index"] = _config[image_dict["image_view"]]
+            else:
+                image_dict["index"] = _index
+
+        all_original_images.sort(key=lambda x: x["index"])
+        #  ==========直接进行处理=============
+        i_n = 0
+        image_index = 0  # 图片顺序
+        is_image_deal_mode = 0
+
+        # 删除目录再新建
+        if os.path.exists('{}/阴影图处理'.format(folder_path)):
+            shutil.rmtree('{}/阴影图处理'.format(folder_path))
+
+        self.crate_all_folders(folder_path)
+
+        for image_dict in all_original_images:
+            if windows:
+                if windows.state != 1:
+                    return None
+            i_n += 1
+            image_index += 1
+            original_image_path = "{}/原始图/{}{}".format(folder_path, image_dict["file_name"], image_dict["e"])
+            file_name = image_dict["file_name"]
+            print("正在处理,货号:{}".format(folder_path))
+
+            # self.show_progress_detail("正在处理,货号:{}".format(file_name))
+            # 该文件在800images下没有时,则进行生成新的抠图
+            # 检查是否存在已抠图文件,如没有再去抠图
+
+            original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(folder_path, image_dict["file_name"], ".png")
+
+            # 此处判断鞋子是否为左右脚
+            if image_index == 1:
+                is_image_deal_mode = 0
+                if OnePicDeal().check_shoe_is_right(image_path=original_move_bg_image_path):
+                    is_image_deal_mode = 1  # 1表示要镜像,0表示不做镜像
+
+            """进行800image 生成"""
+            generate_pic = GeneratePic()
+
+            if settings.OUT_PIC_MODE == ".jpg":
+                out_path = "{}/800x800/{}{}".format(folder_path, file_name, ".jpg")
+            else:
+                out_path = "{}/800x800/{}{}".format(folder_path, file_name, ".png")
+
+            out_process_path_1 = "{}/阴影图处理/{}_{}_阴影{}".format(folder_path, file_name,
+                                                              image_dict["image_view"], ".png")
+
+            out_process_path_2 = "{}/阴影图处理/{}_{}_抠图{}".format(folder_path, file_name,
+                                                              image_dict["image_view"], ".png")
+
+            resize_mode = 1
+            max_box = None
+
+            print("------------1", image_dict["image_view"], resize_image_view)
+            if image_dict["image_view"] == resize_image_view:
+                print(image_dict["image_view"], resize_image_view)
+                resize_mode = 2
+
+            if settings.Mode == "皮具":
+                max_box = (1000, 1200)
+
+            if resize_mode == 2:
+                print(
+                    is_image_deal_mode,
+                    resize_mode,
+                    settings.OUT_PIC_SIZE,
+                    True if i_n == 1 else False,
+                    max_box
+                )
+
+            if not generate_pic.run(image_path=original_image_path,
+                                    cut_image_path=original_move_bg_image_path,
+                                    out_path=out_path,
+                                    image_deal_mode=is_image_deal_mode,
+                                    resize_mode=resize_mode,
+                                    out_pic_size=settings.OUT_PIC_SIZE,
+                                    is_logo=True if i_n == 1 else False,
+                                    out_process_path_1=out_process_path_1,
+                                    out_process_path_2=out_process_path_2,
+                                    max_box=max_box,
+                                    logo_path=logo_path,
+                                    ):
+                is_successful = False
+        if is_successful:
+            return True
+        else:
+            return False
+
+    def to_upload_pic(self, file_path, is_resize=True):
+        file_name = os.path.split(file_path)[1]
+        e = os.path.splitext(file_name)[1][1:]
+
+        im = Picture(file_path)
+        if im.x > 500:
+            im.resize(width=500)
+
+        _ = {"jpg": "JPEG",
+             "JPG": "JPEG",
+             "JPEG": "JPEG",
+             "jpeg": "JPEG",
+             "png": "PNG",
+             "PNG": "PNG", }
+        e = _[e]
+        image_io = im.save_to_io(e)
+        goods_data = {"file_path": os.path.split(file_path)[1],
+                      "image_io": image_io,
+                      "e": e
+                      }
+        url = self.data_mode_image_cut.get_online_data.upload_pic(goods_data=goods_data)
+        return url
+
+    def get_images(self, path):
+        image_list = []  # 过滤非图片数据
+        for _file in self.list_dir(path):
+            file_name, e = os.path.splitext(_file)
+            if e in _Type:
+                image_list.append({"file_path": "{}/{}".format(path, _file),
+                                   "file_name": file_name,
+                                   "e": e})
+        return image_list
+
+    def crate_all_folders(self, root_path):
+        path_list = ["800x800", "原始图_已抠图", "阴影图处理"]
+        for i in path_list:
+            path = "{}/{}".format(root_path, i)
+            self.check_path(path)
+
+    def run_cutout_image(self, all_goods_art_no_folder_data, callback_func=None, cutout_mode=1, windows=None):
+        """
+        处理所有的抠图
+        """
+        callback_func('开始处理抠图~')
+        error_goods_art_no_folder = []
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            folder_path = goods_art_no_folder_data["folder_path"]
+            self.crate_all_folders(folder_path)
+            # 检查是否存在已抠图文件,如没有再去抠图
+            images = [x for x in self.list_dir("{}/原始图".format(folder_path))]
+            cutImageList = []
+            for pic_file_name in images:
+                if windows:
+                    if windows.state != 1:
+                        break
+                # 根据名称判断,没有抠图过的,进行统计
+                file_name, suffix = os.path.splitext(pic_file_name)
+                if suffix in _Type:
+                    original_image_path = "{}/原始图/{}".format(folder_path, pic_file_name)
+                    original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(folder_path, file_name, ".png")
+                    if not os.path.exists(original_move_bg_image_path):
+                        # 没有抠图文件,进行抠图生成
+                        callback_func("正在抠图 货号:{}".format(file_name))
+                        if cutout_mode == '2':
+                            cutImageList.append({
+                                "file_name": file_name,  # 文件名
+                                "file_e": suffix,  # 后缀,.jpg
+                                "file_path": original_image_path,  # 完整路径
+                                "file": '{}{}'.format(file_name, suffix),  # 图片文件名,带后缀
+                                "need_cutout": True,  # 必须,需要抠图
+                                "out_path": original_move_bg_image_path
+                            })
+                        else:
+                            remove_pic_ins = RemoveBgALi()
+                            if settings.IS_TEST:
+                                im = remove_pic_ins.get_image_cut(file_path=original_image_path,
+                                                                  out_file_path=original_move_bg_image_path)
+                            else:
+                                try:
+                                    im = remove_pic_ins.get_image_cut(file_path=original_image_path,
+                                                                      out_file_path=original_move_bg_image_path)
+                                except FunctionTimedOut as f:
+                                    callback_func("货号图{} 抠图处理超时~".format(file_name))
+                                    im = None
+                                except BaseException as e:
+                                    callback_func("货号图{} 抠图处理失败,原因{}".format(file_name, e))
+                                    im = None
+
+                            if not im:
+                                callback_func("货号图{} 抠图处理失败~".format(file_name))
+                                continue
+                            else:
+                                callback_func("货号图{} 抠图完成~".format(file_name))
+
+            if cutout_mode == '2':
+                dealCutout = DealCutout(windows=None)
+                dealCutout.need_cutout_images = cutImageList
+                dealCutout.run()
+                while True:
+                    time.sleep(1)
+                    if windows:
+                        if windows.state != 1:
+                            break
+                    if dealCutout.state == 3:
+                        if len(dealCutout.resultData) != len(cutImageList):
+                            error_goods_art_no_folder.append(folder_path)
+                        break
+
+        if error_goods_art_no_folder:
+            print("以下货号抠图失败~\n {}".format(error_goods_art_no_folder))
+            callback_func("以下货号抠图失败~\n {}".format(error_goods_art_no_folder))
+        else:
+            callback_func("完成抠图处理")
+
+    def checkCutoutImage(self, image_dir: str, todo_goods_art_no_folder_name_list=None):
+        """
+        进行图片检查,不合规的直接提示
+        """
+        error_goods_art_no_folder = []
+        self.check_path("{}/软件-处理失败".format(image_dir))
+
+        for goods_art_no_folder in self.list_dir(image_dir):
+            # 指定内容检查
+            if todo_goods_art_no_folder_name_list is not None:
+                if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                    continue
+
+            if not os.path.isdir("{}/{}".format(image_dir, goods_art_no_folder)):
+                continue
+            if "软件" in goods_art_no_folder:
+                continue
+            if "无法" in goods_art_no_folder:
+                continue
+            if "原始图" not in self.list_dir("{}/{}".format(image_dir, goods_art_no_folder)):
+                error_goods_art_no_folder.append(goods_art_no_folder)
+                continue
+
+            self.check_path("{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/800x800".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/阴影图处理".format(image_dir, goods_art_no_folder))
+
+        if error_goods_art_no_folder:
+            self.move_folders(path_list=["{}/{}".format(self.image_dir, x) for x in error_goods_art_no_folder],
+                              target_folder="{}/软件-处理失败".format(self.image_dir))
+            return False
+
+    def move_folders(self, path_list, target_folder):
+        for source_folder in path_list:
+            shutil.move(source_folder, target_folder)
+
+    def rename_folder_for_hqt(self, all_goods_art_no_folder_data):
+        """
+        步骤:
+        规整红蜻蜓的文件名
+        重新按文件名进行命名
+        """
+        goods_art_no_list = []
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if "@" not in goods_art_no_folder_data["folder_name"]:
+                goods_art_no_folder_data["label"] = "待处理"
+                goods_art_no_list.append(goods_art_no_folder_data["folder_name"])
+            else:
+                goods_art_no_folder_data["label"] = "不处理"
+
+        if goods_art_no_list:
+            # goods_art_no_dict 文件夹与货号的字典
+            goods_art_no_dict = self.dataModeMatchPhoto.get_data_from_hqt_with_goods_art_no(
+                goods_art_no_list=goods_art_no_list)
+            for goods_art_no_folder_data in all_goods_art_no_folder_data:
+                if goods_art_no_folder_data["label"] != "待处理":
+                    continue
+                goods_art_no_folder = goods_art_no_folder_data["folder_name"]
+                if goods_art_no_folder in goods_art_no_dict:
+                    print(goods_art_no_folder)
+                    old_folder_path = goods_art_no_folder_data["folder_path"]
+                    new_folder_name = "{}@NUM{}".format(goods_art_no_folder,
+                                                        goods_art_no_dict[goods_art_no_folder]["编号"])
+                    new_folder_path = "{}/{}".format(goods_art_no_folder_data["root_path"], new_folder_name)
+                    try:
+                        os.rename(old_folder_path, new_folder_path)
+                        goods_art_no_folder_data["folder_path"] = new_folder_path
+                        goods_art_no_folder_data["folder_name"] = new_folder_path
+                        goods_art_no_folder_data["label"] = "待处理"
+                    except BaseException as e:
+                        goods_art_no_folder_data["label"] = "不处理"
+                        print("521 文件夹重名命失败:{}".format(e))
+
+        # 重新规整修改图片名称
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            goods_art_no_folder = goods_art_no_folder_data["folder_name"]
+            _img_all = self.list_dir("{}/原始图".format(goods_art_no_folder_data["folder_path"]))
+            index = 0
+            for _file in _img_all:
+                file_name, suffix = os.path.splitext(_file)
+                if suffix in _Type:
+                    index += 1
+                    folder_path = goods_art_no_folder_data["folder_path"]
+                    new_file_name = "{}({}){}".format(goods_art_no_folder, index, suffix)
+                    new_path = "{}/原始图/{}".format(folder_path, new_file_name)
+                    old_path = "{}/原始图/{}".format(folder_path, _file)
+                    crop_new_path = "{}/原始图_已抠图/{}".format(folder_path, "{}({}).png".format(goods_art_no_folder, index))
+                    crop_old_path = "{}/原始图_已抠图/{}".format(folder_path, "{}.png".format(file_name))
+
+                    if old_path != new_path:
+                        # 存在货号命名错误的,进行修正
+                        try:
+                            if os.path.exists(old_path):
+                                os.rename(old_path, new_path)
+                            if os.path.exists(crop_old_path):
+                                os.rename(crop_old_path, crop_new_path)
+                        except BaseException as e:
+                            goods_art_no_folder_data["label"] = "不处理"
+                            print("550 文件夹重名命失败:{}".format(e))
+
+    def cutImagePiju(self, image_dir: str, image_order='', is_check_number=True, is_filter=True, resize_image_view='后跟',
+                     callback_func=None, event=None, todo_goods_art_no_folder_name_list=None):
+        """
+        1、遍历文件夹,基于生成的结果图看哪些需要进行抠图等处理
+        2、压缩并上传平台获取抠图
+        3、抠图处理成白底图
+        4、做成800*800/200*200
+        :return:
+        """
+        logo_path = ""
+
+        res = self.getImageOrder(image_order=image_order, resize_image_view=resize_image_view)
+        if res['code'] != 0:
+            callback_func(res['msg'])
+            return {'code': 1, 'msg': res['msg']}
+
+        imageOrderList = res['imageOrderList']
+
+        """扫描文档,检查有哪些需要进行抠图等处理"""
+        self.lock = Lock()
+        to_do_images_total = 0
+        error_goods_art_no_folder = []
+        for goods_art_no_folder in self.list_dir(image_dir):
+            # 指定内容检查
+            if todo_goods_art_no_folder_name_list is not None:
+                if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                    continue
+
+            if not os.path.isdir("{}/{}".format(image_dir, goods_art_no_folder)):
+                continue
+
+            self.check_path("{}/{}/原始图".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/800x800".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/阴影图处理".format(image_dir, goods_art_no_folder))
+
+            # 遍历原始图片文件夹
+            all_original_images = [x for x in
+                                   self.list_dir(
+                                       "{}/{}/原始图".format(image_dir, goods_art_no_folder))]
+            # 检查已抠图文件夹
+            all_moved_images = [os.path.splitext(x)[0] for x in
+                                self.list_dir(
+                                    "{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))]
+
+            all_800images = [os.path.splitext(x)[0] for x in
+                             self.list_dir(
+                                 "{}/{}/800x800".format(image_dir, goods_art_no_folder))]
+            if is_check_number and len(imageOrderList) != len(all_original_images):
+                callback_func("{} 文件夹下图片数量与订单数量不一致,请检查!".format(goods_art_no_folder))
+                return {'code': 1, 'msg': '{} 文件夹下图片数量与订单数量不一致,请检查!'.format(goods_art_no_folder)}
+
+            all_800images = []
+            image_num = 0
+            for pic_file_name in all_original_images:
+                if pic_file_name not in all_800images:
+                    # 根据名称判断,没有抠图过的,进行统计
+                    _, e = os.path.splitext(pic_file_name)
+                    print(e)
+                    if e in _Type:
+                        image_num += 1
+                        print("----------》", goods_art_no_folder, pic_file_name)
+                        to_do_images_total += 1
+            # if image_num > 5:
+            #     error_goods_art_no_folder.append(goods_art_no_folder)
+
+        # if error_goods_art_no_folder:
+        #     self.show_progress_detail("以下货号图片张数超过5张~\n {}".format(error_goods_art_no_folder))
+        #     self.set_state(state_value=2)
+        #     return
+
+        if to_do_images_total > 0:
+            # self.progress_sign.emit({"type": "处理图片 抠图、加工等", "progress_bar_value": 0})
+
+            for goods_art_no_folder in self.list_dir(image_dir):
+                # 指定内容检查
+                if todo_goods_art_no_folder_name_list is not None:
+                    if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                        continue
+
+                if not os.path.isdir('{}/{}'.format(image_dir, goods_art_no_folder)):
+                    continue
+
+                self.run_one_folder_to_deal(goods_art_no_folder=goods_art_no_folder,
+                                            image_dir=image_dir,
+                                            image_order=image_order,
+                                            resize_image_view=resize_image_view,
+                                            callback_func=callback_func,
+                                            logo_path=logo_path,
+                                            )
+        else:
+            # self.show_progress_detail("没有需要处理的图片~")
+            callback_func('没有需要处理的图片~')
+        # self.set_state(state_value=2)
+        return {'code': 0, 'msg': 'ok'}
+
+    def run_one_folder_to_deal(self, goods_art_no_folder, image_dir, image_order, resize_image_view,
+                               callback_func=None, logo_path=""):
+
+        _img_all = self.list_dir("{}/{}/原始图".format(image_dir, goods_art_no_folder))
+        all_original_images = []  # 过滤非图片数据
+        index = 0
+        for _file in _img_all:
+
+            file_name, e = os.path.splitext(_file)
+            if e in _Type:
+                index += 1
+                new_file_name = "{}({}){}".format(goods_art_no_folder, index, e)
+                new_path = "{}/{}/原始图/{}".format(image_dir, goods_art_no_folder, new_file_name)
+                old_path = "{}/{}/原始图/{}".format(image_dir, goods_art_no_folder, _file)
+                if old_path != new_path:
+                    # 存在货号命名错误的,进行修正
+                    try:
+                        os.rename(old_path, new_path)
+                    except:
+                        pass
+                all_original_images.append(new_file_name)
+
+        if os.path.exists("{}/{}/原始图/镜像.txt".format(image_dir, goods_art_no_folder)):
+            file_mirror_mark = True
+        else:
+            file_mirror_mark = None
+        # if goods_art_no_folder == "AC51016112":
+        #     print(file_mirror_mark)
+        #     raise 111
+
+        all_moved_images = [os.path.splitext(x)[0] for x in
+                            self.list_dir("{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))]
+        all_800images = [os.path.splitext(x)[0] for x in
+                         self.list_dir(
+                             "{}/{}/800x800".format(image_dir, goods_art_no_folder))]
+        all_800images = []
+
+        # 检查哪些图片没有做过抠图处理
+        i_n = 0
+        _name_list = ["视角{}".format(x) for x in range(1, len(all_original_images) + 1)]
+        # if goods_art_no_folder == "AC51028001":
+        #     _name_list = ["正视", "45度", "侧视", "后视", "底视", "其他1", "其他2", "其他3"]
+
+        image_index = 0  # 图片顺序
+        is_image_deal_mode = 0
+        max_box = None
+
+        for file in all_original_images:
+            i_n += 1
+            image_index += 1
+            original_image_path = "{}/{}/原始图/{}".format(image_dir, goods_art_no_folder, file)
+            file_name = os.path.splitext(file)[0]
+            """
+            当第三张就是为后跟
+            """
+            if file_name not in all_800images:  # 所有都重新生成
+                # if goods_art_no_folder != "AC51016112":
+                #     continue
+                goods_art_no_folder_path = "{}/{}".format(image_dir, goods_art_no_folder)
+                print("正在处理,货号:{}".format(goods_art_no_folder_path))
+                # self.show_progress_detail("正在处理,货号:{}".format(file_name))
+                callback_func("正在处理,货号:{}".format(file_name))
+                # 该文件在800images下没有时,则进行生成新的抠图
+                # 检查是否存在已抠图文件,如没有再去抠图
+                original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(goods_art_no_folder_path, file_name,
+                                                                       ".png")
+
+                image_deal_mode = 0  # 默认图片不做镜像处理
+                if not os.path.exists(original_move_bg_image_path):
+                    # 没有抠图文件,进行抠图生成
+                    # self.show_progress_detail("正在抠图 货号:{}".format(file_name))
+                    callback_func("正在抠图 货号:{}".format(file_name))
+                    remove_pic_ins = RemoveBgALi()
+                    im = remove_pic_ins.get_image_cut(file_path=original_image_path,
+                                                      out_file_path=original_move_bg_image_path)
+
+                    if not im:
+                        # self.show_progress_detail("货号图{} 抠图处理失败~".format(file_name))
+                        callback_func("货号图{} 抠图处理失败~".format(file_name))
+                        continue
+
+                if image_index == 1:
+                    is_image_deal_mode = 0
+                    if settings.Mode == "鞋类":
+                        goods_class = "鞋"
+                        # 如果图片已存在,则需要通过加载图片判断是否为左右脚
+                        if OnePicDeal().check_shoe_is_right(image_path=original_move_bg_image_path):
+                            image_deal_mode = 1  # 1表示要镜像,0表示不做镜像
+                            is_image_deal_mode = 1
+                    if settings.Mode == "皮具":
+                        # 图片对应的商品类型
+                        # goods_class = self.get_goods_class(goods_art_no_folder, original_image_path)
+                        max_box = (1000, 1200)
+                        # _ = {"AC51028001": "女包",
+                        #      "AC51028002": "男包",
+                        #      "AC51028003": "皮带",
+                        #      "AC51028004": "女包"}
+                        # if goods_class in _:
+                        #     goods_class = _[goods_class]
+                        # else:
+                        #     goods_class = "女包"
+                        #
+                        # _ = {"女包": (1000, 1200),
+                        #      "男包": (1000, 1200),
+                        #      "皮带": (1000, 1000), }
+                        # max_box = _[goods_class]
+
+                # 获取图片信息非必要程序,用于处理图片模式
+                date_time_original = self.get_date_time_original(original_image_path)  # 获取照片拍照时间
+
+                if date_time_original:
+                    # 基于照片的时间,与数据库匹配goods_art_no
+                    self.lock.acquire()
+                    _data = self.dataModeMatchPhoto.get_goods_art_no(date_time_original)
+                    self.lock.release()
+
+                    if _data:
+                        # 能匹配上数据库
+                        goods_art_no, _image_index, _image_deal_mode = _data
+                        if _image_index < 10:
+                            image_index = _image_index
+
+                        if _image_deal_mode == 1:
+                            image_deal_mode = 1
+                        # print(goods_art_no, image_index, image_deal_mode)
+
+                if file_mirror_mark:
+                    image_deal_mode = 1
+
+                """进行800image 生成"""
+                generate_pic = GeneratePic()
+                if settings.OUT_PIC_MODE == ".jpg":
+                    out_path = "{}/800x800/{}{}".format(goods_art_no_folder_path, file_name, ".jpg")
+                else:
+                    out_path = "{}/800x800/{}{}".format(goods_art_no_folder_path, file_name, ".png")
+
+                out_process_path_1 = "{}/阴影图处理/{}_{}_阴影{}".format(goods_art_no_folder_path, file_name,
+                                                                  _name_list[i_n - 1], ".png")
+                out_process_path_2 = "{}/阴影图处理/{}_{}_抠图{}".format(goods_art_no_folder_path, file_name,
+                                                                  _name_list[i_n - 1], ".png")
+
+                print("image_index", image_index)
+                image_index = 99
+                if generate_pic.run(image_path=original_image_path,
+                                    cut_image_path=original_move_bg_image_path,
+                                    out_path=out_path,
+                                    image_deal_mode=is_image_deal_mode,
+                                    image_index=image_index,
+                                    out_pic_size=settings.OUT_PIC_SIZE,
+                                    is_logo=True if i_n == 1 else False,
+                                    out_process_path_1=out_process_path_1,
+                                    out_process_path_2=out_process_path_2,
+                                    max_box=max_box,
+                                    logo_path=logo_path,
+                                    ):
+                    # self.show_progress_detail("货号图{} _{} 已完成800*800图片制作~".format(image_index, file_name))
+                    callback_func("货号图{} _{} 已完成800*800图片制作~".format(image_index, file_name))
+                else:
+                    # self.show_progress_detail("货号图{} _{}  图片生成处理失败~".format(image_index, file_name))
+                    callback_func("货号图{} _{}  图片生成处理失败~".format(image_index, file_name))
+
+                # 完成处理的图片进度
+                self.lock.acquire()
+                # self.set_progress()
+                self.lock.release()
+
+    def dealMoveImage(self, image_dir: str, callback_func=None) -> dict:
+        if not self.check_path(image_dir=image_dir + "/历史"):
+            if callback_func:
+                callback_func('文件夹创建失败')
+            return {'code': 1, 'msg': '文件夹创建失败', 'data': {}}
+
+        # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
+        files = self.list_dir(image_dir)
+        original_photo_list = []  # 原始图片列表
+        for file in files:
+            # -----图片清洗
+            file_path = image_dir + "/" + file
+            if os.path.isdir(file_path):  # 忽略文件夹
+                continue
+            file_name, suffix = os.path.splitext(file)
+            if suffix not in _Type:  # 非图片进行移除
+                shutil.move(file_path, image_dir + "/历史/" + file)
+                continue
+
+            date_time_original = self.get_date_time_original(file_path)  # 获取照片拍照时间
+            if date_time_original:
+                # 基于照片的时间,与数据库匹配goods_art_no
+                _data = self.dataModeMatchPhoto.get_goods_art_no(date_time_original)
+                if _data:
+                    # 能匹配上数据库
+                    goods_art_no, image_index, image_deal_mode = _data
+                    print("与数据库匹配goods_art_no", file_name, date_time_original, goods_art_no)
+                    original_photo_list.append({"file_path": file_path,
+                                                "file": file,
+                                                "date_time_original": date_time_original,
+                                                "goods_art_no": goods_art_no,
+                                                "image_index": image_index,
+                                                "real_goods_art_no": "",
+                                                "real_goods_number": "",
+                                                })
+                else:
+                    # 匹配不上报错
+                    # self.show_progress_detail("图片:{} 无法对应货号,不做处理".format(file))
+                    if callback_func:
+                        callback_func("图片:{} 无对应货号".format(file))
+                    # shutil.move(photo_dict["file_path"], self.image_dir + "/历史/" + photo_dict["file"])
+                    continue
+            else:
+                shutil.move(file_path, image_dir + "/历史/" + file)
+
+        if not original_photo_list:
+            # self.show_progress_detail("没有任何匹配的图片~")
+            if callback_func:
+                callback_func('没有任何匹配的图片')
+            # self.set_state(state_value=2)
+            return {"code": 1, "msg": "没有任何匹配的图片", 'data': {}}
+
+        if settings.PROJECT == "红蜻蜓":
+            # 批量请求货号图信息
+            goods_art_no_list = [x["goods_art_no"] for x in original_photo_list]
+            goods_art_no_list = list(set(goods_art_no_list))
+            goods_art_no_list = [x for x in goods_art_no_list if "NUM" not in x]
+
+            if goods_art_no_list:
+                goods_art_no_dict = self.dataModeMatchPhoto.get_data_from_hqt_with_goods_art_no(
+                    goods_art_no_list=goods_art_no_list)
+
+                for i in original_photo_list:
+                    if i["goods_art_no"] in goods_art_no_dict:
+                        i["real_goods_art_no"] = i["goods_art_no"]
+                        i["real_goods_number"] = "NUM{}".format(goods_art_no_dict[i["goods_art_no"]]["编号"])
+
+            # 批量请求编号对应信息
+            goods_number_list = [x["goods_art_no"] for x in original_photo_list]
+            goods_number_list = list(set(goods_number_list))
+            goods_number_list = [x for x in goods_number_list if "NUM" in x]
+
+            if goods_number_list:
+                goods_number_dict = self.dataModeMatchPhoto.get_data_from_hqt(goods_number_list=goods_number_list)
+                for i in original_photo_list:
+                    if i["goods_art_no"] in goods_number_dict:
+                        i["real_goods_number"] = i["goods_art_no"]
+                        i["real_goods_art_no"] = goods_number_dict[i["goods_art_no"]]["商品货号"]
+
+        # 排序需要基于拍照的文件序号进行处理
+        original_photo_list.sort(
+            key=lambda x: "{}-{}-{}".format(x["goods_art_no"], x["image_index"], x["file"]))
+        print(original_photo_list)
+
+        # 对有拍摄信息的图片进行数据库比对,如有比对上,则移动至货号文件夹,否则移入历史文件夹
+        total_num = len(original_photo_list)
+        # 当天日期作为文件夹
+        seconds = time.time()
+        output_path = "output/{f_name}".format(f_name=time.strftime("%Y-%m-%d", time.localtime(seconds)))
+
+        # 遍历每个匹配好的数据进行处理
+        n = 0
+        for photo_dict in original_photo_list:
+            n += 1
+            # 进度条
+            goods_art_no = photo_dict["goods_art_no"]
+            original_image_path = photo_dict["file_path"]
+            # 输出货号文件夹
+            if photo_dict["real_goods_art_no"]:
+                goods_art_no = "{}@{}".format(photo_dict["real_goods_art_no"], photo_dict["real_goods_number"])
+
+            goods_art_no_path = "{output_path}/{goods_art_no}".format(output_path=output_path,
+                                                                      goods_art_no=goods_art_no)
+
+            # 创建货号下的一系列文件夹
+            self.create_folder(goods_art_no_path)
+
+            # 重命名并进行移动
+            self.move_images(goods_art_no, goods_art_no_path, original_image_path)  # 货号、货号文件路径、原始图路径
+
+            # self.progress_sign.emit({"type": "移动原始图片", "progress_bar_value": int(n / total_num * 100)})
+            # self.show_progress_detail("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
+            if callback_func:
+                callback_func("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
+
+        if n != 0:
+            # if settings.MattingPics:
+            #     # 检查所有未处理的货号文件夹,查看是否有完成图片加工处理
+            #     self.deal_images()
+
+            # 自动生成一个货号表
+            print("output_path", output_path)
+            GenerateGoodsArtNoTable.deal(output_path)
+
+        # 完成处理
+        # self.set_state(state_value=2)
+        return {'code': 0, 'msg': '处理完成', 'target_path': output_path, 'data': {}}
+
+    def check_path(self, image_dir: str):
+        if not os.path.exists(image_dir):
+            os.mkdir(image_dir)
+        return True
+
+    def get_date_time_original(self, file_path):
+        with open(file_path, 'rb') as file_data:
+            tags = exifread.process_file(file_data)
+            if "EXIF DateTimeOriginal" in tags:
+                return str(tags["EXIF DateTimeOriginal"])
+            else:
+                return False
+
+    def create_folder(self, path):
+        def check_folder(__path):
+            if not os.path.exists(__path):
+                os.makedirs(__path)
+                return False
+            return True
+
+        # 文件夹不存在,创建货号子集文件夹
+        if not check_folder(path):
+            for name in ["原始图", "原始图_已抠图", "800x800", "200images"]:
+                other_path = path + "/" + name
+                check_folder(other_path)
+
+    def move_images(self, goods_art_no, goods_art_no_path, old_image_path):
+        """
+        步骤:
+        1、移动到原始图
+        Args:
+            goods_art_no:
+            goods_art_no_path:
+            old_image_path:
+
+        Returns:
+
+        """
+        # 移动到原始图
+        file = os.path.split(old_image_path)[1]
+        # 扩展名
+        e = os.path.splitext(file)[1]
+        # 获取图片序列
+        self.goods_images_count_dict[goods_art_no] += 1
+        # A9999(1).jpg
+        new_file_name = "{}({})".format(goods_art_no, self.goods_images_count_dict[goods_art_no])
+        original_image_path = "{}/原始图/{}{}".format(goods_art_no_path, new_file_name, e)
+        # 移动图片
+        shutil.move(old_image_path, original_image_path)
+
+    def pixianRemoveImageBg(self, file_path: str, out_file_path: str, callbackek_func=None):
+        url = self.dataModeMatchPhoto.get_online_data.uploadImage(local_path=file_path)
+
+        remonveUrl = settings.AIGC_DOMAIN + '/api/ai_image/main/remove_background'
+        param = {'base_image': url}
+        post_headers = {"Authorization": settings.Authorization,
+                        "Content-Length": "",
+                        "Content-Type": "application/json",
+                        "Accept": "application/json"}
+
+        result = requests.post(remonveUrl, data=json.dumps(param), headers=post_headers).json()
+        print(result)
+        if "code" in result and result['code'] == 0:
+            response = requests.get(result['data']['image'][0])
+            with open(out_file_path, 'wb') as file:
+                file.write(response.content)
+                return result['data']['image'][0]
+        else:
+            callbackek_func("精细化抠图处理失败 {}".format(result['message']))
+            return ''
+
+    def list_dir(self, path):
+        listdir = os.listdir(path)
+        return natsorted(listdir, alg=ns.PATH)

+ 28 - 0
python/service/auto_deal_pics/data.py

@@ -0,0 +1,28 @@
+import settings
+from module.data_mode.data_metaclass import DataBaseModel
+from PIL import Image
+from io import BytesIO
+
+
+class DataModeAutoDealPics(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+
+    def check_is_right_foot_by_api(self, image):
+        image = image.convert('RGB')
+
+        re_x = int(640)
+        re_y = int(image.height * re_x / image.width)
+        image = image.resize((re_x, re_y))
+        e = "JPEG"
+
+        img = BytesIO()
+        image.save(img, format=e)  # format: PNG or JPEG
+        img.seek(0)  # rewind to the start
+
+        image_url = self.get_online_data.upload_image_by_io(image_io=img)
+        if settings.IS_TEST:
+            print("识别左右脚,{}".format(image_url))
+        # 识别左右脚
+        r_data = self.get_online_data.yolo_shoes_category(image_url=image_url)
+        return r_data

+ 84 - 0
python/service/auto_deal_pics/detail_temp.py

@@ -0,0 +1,84 @@
+from import_qt_mode import *
+from UI.auto_deal_pics_ui.temp_item import Ui_Form as UI_temp_item
+import os
+from PIL import Image
+import threading
+from io import BytesIO
+
+
+# 详情itme模板组件
+class TempItem(QWidget):
+    select_sign = Signal(str)
+
+    def __init__(self, parent, _id, image_path):
+        super().__init__(parent)
+        self.ui = UI_temp_item()
+        self.ui.setupUi(self)
+        self.temp_name = _id
+        self.image_path = image_path
+        self.id = _id
+        self.is_select = False
+        self.init()
+        self.show()
+
+    def init(self):
+        self.ui.label.mousePressEvent = self.pic_show
+        self.ui.radioButton.clicked.connect(self.select)
+        self.set_label_image()
+        self.label_temp_name = QLabel(self)
+        self.label_temp_name.setText(str(self.temp_name))
+        self.label_temp_name.move(5, self.height() - self.label_temp_name.height())
+
+    def cancel_select(self, *args):
+        self.is_select = False
+        self.ui.radioButton.setChecked(False)
+
+    def select(self, *args):
+        self.is_select = True
+        self.ui.radioButton.setChecked(True)
+        self.select_sign.emit(self.id)
+
+    def pic_show(self, *args, **kwargs):
+        if os.path.exists(self.image_path):
+            im = Image.open(self.image_path)
+            threading.Thread(target=im.show, args=()).start()
+
+    def set_label_image(self):
+        if not os.path.exists(self.image_path):
+            return
+        try:
+            bytes_io = BytesIO()
+            w, h = self.ui.image_show.width(), self.ui.image_show.height()
+            _img_raw = Image.open(self.image_path)
+            _img_raw = self.to_resize(_img_raw, width=w * 2)
+            _img_raw = _img_raw.crop(box=(0, 0, w * 2, h * 2))
+            # _img_raw.thumbnail((w * 2, int(_img_raw.height * w * 2 / _img_raw.width)))
+            # img_raw = _img_raw.resize(size=(w, h))
+            _img_raw.save(bytes_io, "JPEG")
+            icon = QPixmap()
+            # icon.loadFromData(img)
+            icon.loadFromData(bytes_io.getvalue())
+            icon = icon.scaled(
+                self.ui.image_show.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui.image_show.setPixmap(icon)
+            # self.mousePressEvent = lambda x: self.show_one_data(row)
+        except:
+            pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+

+ 648 - 0
python/service/auto_deal_pics/main_control.py

@@ -0,0 +1,648 @@
+import os
+
+from UI.auto_deal_pics_ui.auto_main import Ui_Form as generate_goods_no_detail_pic_Ui_Form
+
+#
+from module.view_control.generate_goods_no_detail_pic.data import DataModeGenerateDetail
+from module.view_control.auto_deal_pics.base_deal import BaseDealImage
+from module.view_control.auto_deal_pics.run_main import RunMain
+from module.view_control.auto_deal_pics.upload_pic import UploadPic
+from module.view_control.auto_deal_pics.queren_control import QueRen
+
+# 手动移动图片等操作
+from module.view_control.manual_image_matching.m_image_matching_cotrol import MainMatchingWindow
+
+# 每个详情的模板
+from module.view_control.auto_deal_pics.detail_temp import TempItem
+
+from module.base_mode.load_plugins import LoadAllPlugins
+
+from import_qt_mode import *
+from functools import partial
+from threading import Lock
+from module.view_control.MineQWidget import MineQWidget, DialogShow
+from module.base_mode.base import *
+import threading
+import settings
+
+
+# 抠图与详情生成
+class AutoDealPics(MineQWidget):
+    progress_sign = Signal(dict)
+    text_show = Signal(str)
+    progress_show_sign = Signal(bool)
+    progress_end_sign = Signal()
+    send_dialog_sign = Signal()
+    change_state_sign = Signal(int)
+    run_end_sign = Signal(dict)
+
+    def __init__(self, windows=None, top_windows=None):
+        super().__init__()
+        self.windows = windows
+        self.top_windows = top_windows
+        self.ui = generate_goods_no_detail_pic_Ui_Form()
+        self.ui.setupUi(self)
+        self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
+
+        self.run_main = RunMain(windows=self)
+        self.run_main.run_end_sign.connect(self.deal_run_end_sign)
+        self.run_main.show_dialog_sign.connect(self.show_dialog)
+        self.run_main.show_progress_detail_sign.connect(self.show_progress_detail)
+
+        # 程序执行信号
+        self.run_end_sign.connect(self.deal_run_end_sign)
+
+        # 改变状态
+        self.change_state_sign.connect(self.set_state)
+
+        self.setWindowTitle("详情生成(new)")
+        # 线程锁
+        self.lock = Lock()
+        self.image_dir = ""
+        # 0禁用  1进行中  2已结束 99等待结束
+        self.state = 2
+        self.data_mode_generate_detail = DataModeGenerateDetail()
+        self.init_cutout()
+        self.init_detail()
+        self.init_schedule()
+        self.set_state(0)
+        self.show()
+        # 延时检查是否存在匹配图片
+        QTimer.singleShot(1000, self.check_and_matching_pics)
+
+    # 添加进度页面
+    def init_schedule(self, *args):
+        self.ui.back.clicked.connect(self.back)
+        self.ui.stop.clicked.connect(self.to_stop)
+        self.ui.progressBar.hide()
+
+    def back(self, *args):
+        self.set_state(0)
+
+    def to_stop(self, *args):
+        self.state = 99
+
+    # 自动检查匹配图片
+    def check_and_matching_pics(self, *args, **kwargs):
+        func = partial(self.run_matching)
+        self.do_thread_run(func=func, call_back=self.run_matching_call, time_out=30)
+
+    # 运行自动匹配
+    def run_matching(self, *args):
+        image_dir = "{}/data".format(os.getcwd())
+        check_path(image_dir)
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        result = baseDealImage.dealMoveImage(image_dir=image_dir, callback_func=None)
+        # return {'code': 0, 'msg': '处理完成', 'target_path': output_path, 'data': {}}
+        return result
+
+    # 自动匹配的回调函数
+    def run_matching_call(self, return_data):
+
+        print(return_data)
+        code = return_data["code"]
+        if code == 0:
+            output_path = return_data["target_path"]
+            self.image_dir = "{}/{}".format(os.getcwd(), output_path)
+            self.show_img_dir()
+
+    def show_progress_detail(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    # todo ===========主方法入口===========
+    def deal_run_end_sign(self, config_data: dict):
+        if config_data["sign_text"] == "开始抠图":
+            # 先做整体校验
+            func = partial(self.run_main.check_before_cutout, config_data=config_data)
+            self.do_thread_run(func=func, call_back=self.run_main.check_for_cutout_image_first_call_back, time_out=30,
+                               is_show_mask=False)
+
+        print(config_data)
+        if config_data["sign_text"] == "已结束抠图处理":
+            if config_data["detail_is_enable"]:
+                # 先做整体校验
+                # temp_name=self.last_temp, temp_name_list=self.temp_list
+                func = partial(self.run_main.check_before_detail, config_data=config_data)
+                self.do_thread_run(func=func,
+                                   call_back=self.run_main.check_for_detail_first_call_back,
+                                   time_out=30,
+                                   is_show_mask=False)
+            else:
+                self.set_state(state_value=2)
+
+        if config_data["sign_text"] == "已结束详情处理":
+            if config_data["upload_is_enable"]:
+                to_deal_dir = "{}/软件-详情图生成".format(config_data["image_dir"])
+                print("to_deal_dir", to_deal_dir)
+                if os.path.exists(to_deal_dir):
+                    self.upload_pic = UploadPic(windows=self, to_deal_dir=to_deal_dir, config_data=config_data)
+                    self.upload_pic.run_end_sign.connect(self.deal_run_end_sign)
+                    self.upload_pic.show_progress_detail_sign.connect(self.show_progress_detail)
+                    self.upload_pic.run()
+                    # threading.Thread(target=self.upload_pic.run_by_thread, args=()).start()
+                else:
+                    self.set_state(state_value=2)
+            else:
+                self.set_state(state_value=2)
+
+        if config_data["sign_text"] == "结束":
+            self.set_state(state_value=2)
+
+    def change_temp_mode(self, *args, **kwargs):
+        if self.ui.check_use_excel.isChecked():
+            self.ui.temp_list_get.hide()
+        else:
+            self.ui.temp_list_get.show()
+
+    # 重新载入模块
+    def reload_temp_mode(self, *args, **kwargs):
+        a = QMessageBox.question(
+            self, "确认", "是否重启加载模板", QMessageBox.Yes | QMessageBox.No
+        )
+        if a != QMessageBox.Yes:
+            return
+
+        # 检查资源包
+        if settings.env == "prod":
+            if self.top_windows:
+                self.top_windows.check_resources_download()
+
+        settings.PLUGINS_DICT = LoadAllPlugins(windows=None).load()
+        # 重新载入模板
+        self.load_temp_list()
+
+    #  获取页面配置
+    def get_config(self, *args):
+        if not settings.IsLogin:
+            QMessageBox.question(
+                self, "确认", "请先登录", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        logo_name = self.ui.logo_select_ui.currentText()
+        if logo_name == "请选择":
+            QMessageBox.question(
+                self, "确认", "请先选择logo模板", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if self.ui.cutout_exquisite.isChecked() and not bool(settings.IsExquisiteMode):
+            QMessageBox.question(
+                self, "确认", "暂无 精细化抠图权限", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if "失败" in self.image_dir:
+            self.WaringMessage("文件夹路径不能含有 失败 字样")
+            return
+
+        image_dir = self.ui.path_name.text()
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        resize_image_view = self.ui.need_resize.text()
+        logo_name = self.ui.logo_select_ui.currentText()
+        settings.MATCHING_LAST_LOGO_TEMP = logo_name
+        logo_path = self.logo_path_dict[logo_name]
+
+        cutout_mode = '1'
+        if self.ui.cutout_exquisite.isChecked():
+            cutout_mode = '2'
+        configData = {
+            'imageorder': image_order,
+            'resize_image_view': resize_image_view,
+            'is_check_number': str(is_check_number),
+            'cutout_mode': str(cutout_mode),
+            'matching_mode_last': settings.MATCHING_MODE_LAST,
+            "matching_last_temp": self.last_temp,
+            "matching_last_logo_temp": logo_name,
+        }
+        settings.cutimage_dict['imageorder'] = image_order
+        settings.cutimage_dict['resize_image_view'] = resize_image_view
+        settings.cutimage_dict['is_check_number'] = str(is_check_number)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+        config_data = {}
+        config_data["image_dir"] = image_dir
+        config_data["image_order"] = image_order
+        config_data["is_check_number"] = is_check_number
+        config_data["resize_image_view"] = resize_image_view
+        config_data["cutout_mode"] = cutout_mode
+        config_data["logo_path"] = logo_path
+        config_data["special_goods_art_no_folder_line"] = ""
+
+        # =======================================
+
+        settings.GOODS_DETAIL_IS_USE_EXCEL = self.is_use_excel
+        settings.GOODS_DETAIL_EXCEL_PATH = self.excel_path
+        settings.GOODS_DETAIL_LAST_IMAGE_PATH = self.image_dir
+        settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL = self.is_check_color_is_all
+        settings.GOODS_DETAIL_IS_PASS = self.is_check_is_pass
+        settings.GOODS_DETAIL_LAST_TEMP = self.last_temp
+
+        data_dict = {
+            "is_use_excel": "是" if self.is_use_excel else "否",
+            "excel_path": self.excel_path if self.excel_path else "",
+            "last_image_path": self.image_dir if self.image_dir else "",
+            "is_check_color_is_all": "是" if self.is_check_color_is_all else "否",
+            "last_temp": self.last_temp,
+            "goods_detail_temp_mode_last": settings.GOODS_DETAIL_TEMP_MODE_LAST,
+        }
+        settings.set_config(data_dict=data_dict, section="goods_detail")
+        # 红蜻蜓/惠利玛,且为非表格数据必须需要登录处理
+        assigned_page = self.ui.assigned_page.text()
+        if assigned_page:
+            assigned_page_dict = self.check_assigned_page(assigned_page)
+            if not assigned_page_dict:
+                self.WaringMessage("指定模板填写格式不合规")
+        else:
+            assigned_page_dict = {}
+
+        config_data["is_use_excel"] = self.is_use_excel
+        config_data["excel_path"] = self.excel_path
+        config_data["is_check_color_is_all"] = self.is_check_color_is_all
+        config_data["assigned_page_dict"] = assigned_page_dict
+        config_data["temp_class"] = self.temp_class
+        config_data["temp_name"] = self.last_temp
+        config_data["temp_name_list"] = self.temp_list
+        config_data["target_error_folder"] = self.target_error_folder
+        print("=============config_data=============")
+        print(
+            config_data
+        )
+        return config_data
+
+    # 抠图相关处理
+    def init_cutout(self):
+        # 指定单独处理的颜色文件夹
+        self.special_goods_art_no_folder = []
+        self.show_img_dir()
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.line_image_pos.setText(settings.cutimage_dict['imageorder'])
+        self.ui.need_resize.setText(settings.cutimage_dict['resize_image_view'])
+        self.ui.check_image_total.setChecked(bool(settings.cutimage_dict['is_check_number']))
+
+        if settings.DEFAULT_CUTOUT_MODE == '普通抠图':
+            self.ui.cutout_nomal.setChecked(True)
+        elif settings.DEFAULT_CUTOUT_MODE == ' 精细化抠图':
+            self.ui.cutout_exquisite.setChecked(True)
+
+        if not bool(settings.IsExquisiteMode):
+            self.ui.cutout_exquisite.setEnabled(False)
+
+        self.progress_sign.connect(self.show_progress)
+        self.ui.run.clicked.connect(self.run)  # 抠图与主图加工
+
+        self.set_logo_selected()
+        # 手动匹配图片
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic_mousePressEvent
+
+    def init_detail(self):
+        # 使用使用表格数据
+        self.is_use_excel = settings.GOODS_DETAIL_IS_USE_EXCEL
+        self.excel_path = settings.GOODS_DETAIL_EXCEL_PATH
+
+        # 目标文件夹
+        self.image_dir = settings.GOODS_DETAIL_LAST_IMAGE_PATH
+        # 对应的错误文件夹目录
+        if self.image_dir and os.path.exists(self.image_dir):
+            self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        else:
+            self.target_error_folder = ""
+
+        # 检查颜色是否齐全
+        self.is_check_color_is_all = settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL
+        # 已生成主图的自动过滤
+        self.is_check_is_pass = settings.GOODS_DETAIL_IS_PASS
+
+        # 上次使用的详情模板
+        self.last_temp = settings.GOODS_DETAIL_LAST_TEMP
+
+        # 重新载入模块
+        self.ui.reload_func.mousePressEvent = self.reload_temp_mode
+        # 手工整理数据
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic
+
+        # -------初始化页面--------
+        if self.is_use_excel:
+            self.ui.check_use_excel.setChecked(True)
+        else:
+            self.ui.check_use_excel.setChecked(False)
+
+        if self.excel_path:
+            self.ui.excel_path_show.setText(self.excel_path)
+
+        if self.image_dir:
+            self.ui.path_name.setText(self.image_dir)
+
+        if self.is_check_color_is_all:
+            self.ui.check_image_total_2.setChecked(True)
+        else:
+            self.ui.check_image_total_2.setChecked(False)
+
+        # 是否开启使用外部数据
+        self.ui.check_use_excel.toggled.connect(self.check_box_change)
+        self.ui.check_image_total_2.toggled.connect(self.check_box_change)
+        self.check_box_change()
+
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.select_excel.mousePressEvent = self.select_excel_path
+
+        # ====================================
+        self.progress_sign.connect(self.show_progress)
+
+        # 复制Excel模板
+        self.ui.get_excel_temp.mousePressEvent = self.copy_excel
+        self.change_temp_mode()
+
+        # 加载模板
+        QTimer.singleShot(1000, self.load_temp_list)
+
+    def manual_matching_pic_mousePressEvent(self, event):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    # 手工整理数据
+    def manual_matching_pic(self, *args, **kwargs):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    def load_temp_list(self):
+        # 注册模板类
+        self.temp_class = {}
+        self.temp_list = []
+        plugins_company_name = "企业名称"
+
+        if settings.PROJECT == "红蜻蜓":
+            self.temp_class = {}
+            plugins_company_name = "红蜻蜓"
+
+        elif settings.PROJECT == "惠利玛":
+            if "惠利玛" in settings.Company:
+                self.temp_class = {}
+                plugins_company_name = "惠利玛"
+            if "小苏" in settings.Company:
+                self.temp_class = {}
+                plugins_company_name = "小苏"
+
+        # 通过动态进行加载获取
+        if settings.PLUGINS_DICT:
+            print("554  settings.PLUGINS_DICT")
+            for _key, _value in settings.PLUGINS_DICT["detail_template"].items():
+                print(_key, _value)
+            try:
+                if "全部" in settings.PLUGINS_DICT["detail_template"]:
+                    for i, v in settings.PLUGINS_DICT["detail_template"]["全部"].items():
+                        self.temp_class[i] = v
+                if plugins_company_name in settings.PLUGINS_DICT["detail_template"]:
+                    for i, v in settings.PLUGINS_DICT["detail_template"][plugins_company_name].items():
+                        self.temp_class[i] = v
+            except BaseException as e:
+                print("209  通过动态进行加载详情获取error", e)
+                pass
+
+        # 模板名称列表
+        self.temp_list = [x for x in self.temp_class]
+        print("570 temp_list", self.temp_list)
+
+        # 删除已渲染的模板
+        for i in self.ui.show_temp_list.findChildren(TempItem):
+            i.deleteLater()
+
+        # 模板列表
+        x, y = 10, 10
+        self.ui.show_temp_list.setMinimumSize(self.ui.widget_10.width(), 100)
+        self.ui.show_temp_list.resize(self.ui.widget_10.width(), 100)
+        temp_high = 100
+        for temp_name in self.temp_list:
+            _class = self.temp_class[temp_name]
+            _d = _class.get_temp_pic_info(_class.root)
+            temp_image_path = _d["temp_pic_path"]
+            # print("224 temp_image_path",temp_image_path)
+            # temp_item = TempItem(self.ui.scrollArea, temp_name, temp_image_path)
+            # temp_item.select_sign.connect(self.change_temp_select)
+            # self.ui.horizontalLayout_18.insertWidget(
+            #     self.ui.horizontalLayout_18.count() - 1, temp_item
+            # )
+            temp_item = TempItem(self.ui.show_temp_list, temp_name, temp_image_path)
+            temp_high = temp_item.height()
+            temp_item.select_sign.connect(self.change_temp_select)
+            temp_item.move(x, y)
+            x += temp_item.width()
+            x += 10
+            if x > self.ui.show_temp_list.width() - temp_item.width():
+                y += temp_item.height()
+                y += 10
+                x = 10
+
+        y += 100
+        y += temp_high
+        print(y)
+        self.ui.scrollAreaWidgetContents_3.setMinimumSize(x, self.height() + y)
+        self.ui.show_temp_list.setMinimumSize(x, y)
+        self.ui.show_temp_list.resize(x, y)
+        print(self.ui.show_temp_list.height())
+
+        # 设置默认值
+        if self.last_temp not in self.temp_list:
+            if self.temp_list:
+                self.last_temp = self.temp_list[0]
+
+        # 设置值
+        for temp_item in self.ui.show_temp_list.findChildren(TempItem):
+            temp_item.select()
+            break
+
+    def change_temp_select(self, _id):
+        self.last_temp = _id
+        print("已设置模板的值为:{}".format(self.last_temp))
+        for temp_item in self.ui.show_temp_list.findChildren(TempItem):
+            if temp_item.id != _id:
+                temp_item.cancel_select()
+
+    def copy_excel(self, *args, **kwargs):
+        excel_path = r"{}\resources\init\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(excel_path):
+            return
+
+        self.check_path(r"{}\temp".format(os.getcwd()))
+        self.check_path(r"{}\temp\excel_temp".format(os.getcwd()))
+        new_excel_path = r"{}\temp\excel_temp\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(new_excel_path):
+            shutil.copy(excel_path, new_excel_path)
+        # 打开文件夹
+        os.startfile(r"{}\temp\excel_temp".format(os.getcwd()))
+
+    def check_box_change(self, *args):
+        if self.ui.check_use_excel.isChecked():
+            self.is_use_excel = True
+            self.ui.widget.show()
+            self.ui.temp_list_get.hide()
+        else:
+            self.is_use_excel = False
+            self.ui.widget.hide()
+            self.ui.temp_list_get.show()
+
+        if self.ui.check_image_total_2.isChecked():
+            self.is_check_color_is_all = True
+        else:
+            self.is_check_color_is_all = False
+
+    # 设置选择logo模板
+    def set_logo_selected(self):
+        logo_root_path = ""
+        if settings.PROJECT == "红蜻蜓":
+            logo_root_path = r"{}\resources\LOGO\HQT".format(os.getcwd())
+        elif settings.PROJECT == "惠利玛":
+            if "小苏" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\xiaosushuoxie".format(os.getcwd())
+            if "惠利玛" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\HLM".format(os.getcwd())
+
+        self.logo_path_dict = {"请选择": "", "无logo": ""}
+
+        if logo_root_path:
+            if os.path.exists(logo_root_path):
+                image_list = get_images(logo_root_path)
+                for image_data in image_list:
+                    self.logo_path_dict[image_data["file_name"]] = image_data["file_path"]
+
+        self.ui.logo_select_ui.addItems([x for x in self.logo_path_dict])
+
+        self.ui.logo_select_ui.setCurrentText("请选择")
+        if settings.MATCHING_LAST_LOGO_TEMP:
+            if settings.MATCHING_LAST_LOGO_TEMP in [x for x in self.logo_path_dict]:
+                self.ui.logo_select_ui.setCurrentText(settings.MATCHING_LAST_LOGO_TEMP)
+
+    def select_excel_path(self, *args, **kwargs):
+        folder = QFileDialog.getOpenFileName(self, "选取excel", "./")[0]
+        if "xlsx" in folder:
+            # if len(folder) > 20:
+            #     text = folder[-20:]
+            # else:
+            #     text = folder
+
+            self.ui.excel_path_show.setText(folder)
+            self.excel_path = folder
+            print("folder", folder)
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", settings.GOODS_DETAIL_LAST_IMAGE_PATH)
+        if folder:
+            # todo 增加检测是否有正常文件文件夹
+            folder = check_goods_folder(folder)
+            if folder is None:
+                QMessageBox.question(self, "确认", "您已选的文件夹下不存在任何有效数据", QMessageBox.Yes, )
+                return
+            else:
+                self.image_dir = folder
+                self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+                self.show_img_dir()
+
+    def change_img_dir_by_text(self, folder):
+        self.image_dir = folder
+        self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        self.show_img_dir()
+
+    def show_img_dir(self):
+        self.ui.path_name.setText(self.image_dir)
+
+    def set_state(self, state_value: int):
+        # 0禁用  1进行中  2已结束
+        if state_value not in [0, 1, 2, 99]:
+            return
+        self.state = state_value
+        if self.state == 0:
+            self.ui.stackedWidget.setCurrentIndex(0)
+        if self.state == 1:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.progressBar.setValue(0)
+            self.ui.textBrowser_4.clear()
+            self.ui.back.hide()
+            self.ui.stop.show()
+
+        if self.state == 2:
+            self.ui.back.show()
+            self.ui.stop.hide()
+
+            pass
+
+    def check_path(self, _path):
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def show_progress(self, *args, **kwargs):
+        pass
+
+    # todo 主程序处理
+    def run(self):
+        config_data = self.get_config()
+        if not config_data:
+            return
+        self.queren = QueRen(self)
+        ret = self.queren.exec()
+        if ret == QDialog.Accepted:
+            print("对话框以接受状态关闭")
+            for key, value in self.queren.get_config().items():
+                config_data[key] = value
+            config_data["sign_text"] = "开始抠图"
+            for key, value in config_data.items():
+                print("key:{},  value:{}".format(key, value))
+            self.set_state(state_value=1)
+            self.run_end_sign.emit(config_data)
+
+    # 解析指定模板页面数据
+    def check_assigned_page(self, text: str):
+
+        try:
+            # temp_dict 示例{“4”:“xiaosushuoxie-4”}
+            temp_dict = {}
+            for i in self.temp_list:
+                temp_dict[i[-1]] = i
+
+            text = text.replace(" ", "")
+            text = text.replace(",", ",")
+            text = text.replace(":", ":")
+            text = text.replace(";", ",")
+            assigned_page_list = text.split(",")
+            assigned_page_dict = {}
+            for i in assigned_page_list:
+                if not i:
+                    continue
+                _k, _v = i.split(":")
+                _k = temp_dict[_k]
+                if _k not in assigned_page_dict:
+                    assigned_page_dict[_k] = []
+                if _v not in assigned_page_dict[_k]:
+                    assigned_page_dict[_k].append(_v)
+            # assigned_page_dict 数据结构,一个模板中存在多个页面数值。
+            return assigned_page_dict
+        except:
+            return {}
+
+    # 弹出对话框
+    def show_dialog(self, data):
+        windows = data["windows"]
+        windows.dialog_result = ""
+        my_dialog = DialogShow(self, text=data["text"],
+                               button_1=data["button_1"] if "button_1" in data else None,
+                               button_2=data["button_2"] if "button_2" in data else None,
+                               button_3=data["button_3"] if "button_3" in data else None, )
+        ret = my_dialog.exec()
+        print("641  my_dialog.flag_name", my_dialog.flag_name)
+        # 根据用户的选择发出相应的信号
+        windows.dialog_result_signal.emit(my_dialog.flag_name)

+ 77 - 0
python/service/auto_deal_pics/queren_control.py

@@ -0,0 +1,77 @@
+from UI.auto_deal_pics_ui.queren import Ui_Dialog as queren_Ui_Dialog
+from import_qt_mode import *
+import settings
+
+
+class QueRen(QDialog):
+    def __init__(self, parent):
+        super().__init__(parent)
+        self.ui = queren_Ui_Dialog()
+        self.ui.setupUi(self)
+        self.init()
+        self.show()
+
+    def get_config(self):
+        config_data = {}
+        config_data["cutout_is_enable"] = self.ui.cutout_is_enable.isChecked()
+        config_data["cutout_is_pass"] = self.ui.cutout_is_pass.isChecked()
+        config_data["detail_is_enable"] = self.ui.detail_is_enable.isChecked()
+        config_data["detail_is_pass"] = self.ui.detail_is_pass.isChecked()
+        config_data["upload_is_enable"] = self.ui.upload_is_enable.isChecked()
+        config_data["upload_is_pass"] = self.ui.upload_is_pass.isChecked()
+
+        # 保存配置
+        settings.GOODS_DETAIL_CUTOUT_IS_ENABLE = self.ui.cutout_is_enable.isChecked()
+        settings.GOODS_DETAIL_CUTOUT_IS_PASS = self.ui.cutout_is_pass.isChecked()
+        settings.GOODS_DETAIL_DETAIL_IS_ENABLE = self.ui.detail_is_enable.isChecked()
+        settings.GOODS_DETAIL_DETAIL_IS_PASS = self.ui.detail_is_pass.isChecked()
+        settings.GOODS_DETAIL_UPLOAD_IS_ENABLE = self.ui.upload_is_enable.isChecked()
+        settings.GOODS_DETAIL_UPLOAD_IS_PASS = self.ui.upload_is_pass.isChecked()
+
+        data_dict = {
+            "goods_detail_cutout_is_enable": "是" if settings.GOODS_DETAIL_CUTOUT_IS_ENABLE else "否",
+            "goods_detail_cutout_is_pass": "是" if settings.GOODS_DETAIL_CUTOUT_IS_PASS else "否",
+            "goods_detail_detail_is_enable": "是" if settings.GOODS_DETAIL_DETAIL_IS_ENABLE else "否",
+            "goods_detail_detail_is_pass": "是" if settings.GOODS_DETAIL_DETAIL_IS_PASS else "否",
+            "goods_detail_upload_is_enable": "是" if settings.GOODS_DETAIL_UPLOAD_IS_ENABLE else "否",
+            "goods_detail_upload_is_pass": "是" if settings.GOODS_DETAIL_UPLOAD_IS_PASS else "否",
+        }
+        settings.set_config(data_dict=data_dict, section="goods_detail")
+        return config_data
+
+    def result(self):
+        if self.result() == QDialog.Accepted:
+            print("用户点击了确认")
+        else:
+            print("用户点击了取消")
+
+    def init(self):
+        # 修改按钮文本
+        self.ui.buttonBox.button(QDialogButtonBox.Ok).setText("确认")
+        self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText("取消")
+        # 连接 accepted和rejected信号到槽函数
+        self.ui.buttonBox.accepted.connect(self.accept)
+        self.ui.buttonBox.rejected.connect(self.reject)
+
+        # GOODS_DETAIL_CUTOUT_IS_ENABLE = get_dict_value(cutimage_dict, "goods_detail_cutout_is_enable", True)  # 上次的模式
+        # GOODS_DETAIL_CUTOUT_IS_PASS = get_dict_value(cutimage_dict, "goods_detail_cutout_is_pass", True)  # 上次的模式
+        # GOODS_DETAIL_DETAIL_IS_ENABLE = get_dict_value(cutimage_dict, "goods_detail_detail_is_enable", True)  # 上次的模式
+        # GOODS_DETAIL_DETAIL_IS_PASS = get_dict_value(cutimage_dict, "goods_detail_detail_is_pass", True)  # 上次的模式
+        # GOODS_DETAIL_UPLOAD_IS_ENABLE = get_dict_value(cutimage_dict, "goods_detail_upload_is_enable", True)  # 上次的模式
+        self.ui.cutout_is_enable.setChecked(settings.GOODS_DETAIL_CUTOUT_IS_ENABLE)
+        self.ui.cutout_is_pass.setChecked(settings.GOODS_DETAIL_CUTOUT_IS_PASS)
+        self.ui.detail_is_enable.setChecked(settings.GOODS_DETAIL_DETAIL_IS_ENABLE)
+        self.ui.detail_is_pass.setChecked(settings.GOODS_DETAIL_DETAIL_IS_PASS)
+
+        print("settings.Company", settings.Company)
+        if "红蜻蜓" in settings.Company or "惠利玛" in settings.Company:
+            self.ui.upload_is_enable.show()
+            self.ui.label_3.show()
+            self.ui.upload_is_enable.setChecked(settings.GOODS_DETAIL_UPLOAD_IS_ENABLE)
+            self.ui.upload_is_pass.setChecked(settings.GOODS_DETAIL_UPLOAD_IS_PASS)
+        else:
+            self.ui.upload_is_enable.hide()
+            self.ui.label_3.hide()
+            self.ui.upload_is_pass.hide()
+            self.ui.upload_is_enable.setChecked(False)
+            self.ui.upload_is_pass.setChecked(False)

+ 1005 - 0
python/service/auto_deal_pics/run_main.py

@@ -0,0 +1,1005 @@
+import settings
+from module.view_control.generate_goods_no_detail_pic import detail_func
+import json
+from module.base_mode.base import *
+from import_qt_mode import *
+from module.view_control.match_and_cutout_mode_control.base_deal_image_v2 import BaseDealImage
+from module.view_control.MineQWidget import DialogShow, WorkerOneThread
+import threading
+from concurrent.futures import ThreadPoolExecutor
+import concurrent.futures
+from module.view_control.generate_goods_no_detail_pic.data import DataModeGenerateDetail, DataModeUploadPic
+from module.view_control.generate_goods_no_detail_pic.detail_func import create_folder
+import time
+from PIL import Image
+from io import BytesIO
+import os, re
+from functools import partial
+# from multiprocessing import Process, Queue
+import pickle
+
+
+class RunMain(QThread):
+    run_end_sign = Signal(dict)
+    show_dialog_sign = Signal(dict)
+    show_progress_detail_sign = Signal(str)
+
+    # 定义一个信号用于请求显示对话框
+    show_dialog_signal = Signal()
+    # 定义一个信号用于返回对话框的结果
+    dialog_result_signal = Signal(str)
+
+    # dialog_result_signal = Signal(str)
+
+    def __init__(self, windows):
+        super().__init__()
+        self.windows = windows
+        self.dialog_result = ""
+        self.data_mode_generate_detail = DataModeGenerateDetail()
+        self.dialog_result_signal.connect(self.on_dialog_result)
+        self.event = threading.Event()
+
+    def set_state(self, state_value: int):
+        # 0禁用  1进行中  2已结束
+        if state_value not in [0, 1, 2, 99]:
+            return
+        self.windows.change_state_sign.emit(state_value)
+        # self.windows.set_state(state_value)
+
+    # 抠图前先做数据规整处理;类似详情图生成逻辑
+    def check_before_cutout(self, config_data):
+        return_data = {
+            "code": 99,
+            "message": "",
+            "data": {
+                "all_goods_art_no_folder_data": [],
+                "config_data": config_data,
+            },
+        }
+
+        image_dir = config_data["image_dir"]
+        image_order = config_data["image_order"]
+        is_check_number = config_data["is_check_number"]
+        is_filter = config_data["cutout_is_pass"]
+        resize_image_view = config_data["resize_image_view"]
+        logo_path = config_data["logo_path"]
+        cutout_mode = config_data["cutout_mode"]  # 是否精细化抠图,默认为普通抠图
+        special_goods_art_no_folder_line = config_data["special_goods_art_no_folder_line"]
+
+        # 自动处理红蜻蜓货号,进行重命名
+        if settings.PROJECT == "红蜻蜓":
+            # 规整红蜻蜓货号图
+            all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+            BaseDealImage().rename_folder_for_hqt(all_goods_art_no_folder_data=all_goods_art_no_folder_data)
+
+        # 重新获取文件夹信息
+        all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+
+        f = True
+        is_do_other = False
+        if is_do_other:
+            for i in all_goods_art_no_folder_data:
+                i["label"] = "不处理"
+            is_filter = False
+            specified_goods_art_no_folder = special_goods_art_no_folder_line
+            specified_goods_art_no_folder = specified_goods_art_no_folder.strip()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.replace(",", ",")
+            specified_goods_art_no_folder_list = specified_goods_art_no_folder.split(",")
+            specified_goods_art_no_folder_list = [x for x in specified_goods_art_no_folder_list if x]
+            if not specified_goods_art_no_folder_list:
+                return_data["message"] += '请手动输入文件夹名称(多选),或关闭指定文件夹模式\n'
+            else:
+                for i in all_goods_art_no_folder_data:
+                    if i["folder_path"] in specified_goods_art_no_folder_list:
+                        i["label"] = "待处理"
+            # 哪些数据不合规
+            all_folder_name_list = [x["folder_name"] for x in all_goods_art_no_folder_data]
+            for goods_art_no_folder_name in specified_goods_art_no_folder_list:
+                if goods_art_no_folder_name not in all_folder_name_list:
+                    return_data["message"] += '文件夹:{},在您选的目录下不存在\n'.format(goods_art_no_folder_name)
+                    f = False
+
+        if not f:
+            self.set_state(state_value=2)
+            return
+
+        # 清空指定文件夹的已抠图文件
+        if is_do_other:
+            for folder_data in all_goods_art_no_folder_data:
+                goods_art_no_folder_path = "{}/原始图_已抠图".format(folder_data["folder_path"])
+                if os.path.exists(goods_art_no_folder_path):
+                    remove_all_file(goods_art_no_folder_path)
+
+        return_data["data"]["succeed_folder_list"] = 1
+
+        # ==================检查填写的图片视角是否符合要求
+        res = BaseDealImage().getImageOrder(image_order=image_order, resize_image_view=resize_image_view)
+        if res['code'] != 0:
+            return_data["message"] += "{}\n".format(res['msg'])
+            return return_data
+        else:
+            # 图片命名顺序
+            image_order_list = res['imageOrderList']
+            for goods_art_no_folder_data in all_goods_art_no_folder_data:
+                if goods_art_no_folder_data["label"] != "待处理":
+                    continue
+                goods_art_no_folder_data["image_order_list"] = image_order_list
+
+        # ================是否过滤已有生成的文件夹
+        if is_filter:
+            for goods_art_no_folder_data in all_goods_art_no_folder_data:
+                if goods_art_no_folder_data["label"] != "待处理":
+                    continue
+                folder_path = goods_art_no_folder_data["folder_path"]
+                _p = "{}/800x800".format(folder_path)
+                if os.path.exists(_p):
+                    if len(os.listdir(_p)):
+                        goods_art_no_folder_data["label"] = "不处理"
+
+        # ================检查每个货号文件夹图片数量是否符合要求
+        all_goods_art_no_folder_data, message = BaseDealImage().check_folders_image_amount(all_goods_art_no_folder_data,
+                                                                                           image_order_list)
+        if message:
+            return_data["message"] += "{}\n".format(message)
+        return_data["code"] = 0
+        return_data["data"]["all_goods_art_no_folder_data"] = all_goods_art_no_folder_data
+        return_data["data"]["image_dir"] = image_dir
+        return_data["data"]["resize_image_view"] = resize_image_view
+        return_data["data"]["cutout_mode"] = cutout_mode
+        return_data["data"]["image_order_list"] = image_order_list
+        return_data["data"]["logo_path"] = logo_path
+        return return_data
+
+    # 抠图校验后的回调函数处理
+    def check_for_cutout_image_first_call_back(self, return_data):
+        # return_data = {
+        #     "code": 99,
+        #     "message": "",
+        #     "data": {
+        #         "all_goods_art_no_folder_data": [],
+        #     },
+        # }
+        code = return_data["code"]
+        config_data = return_data["data"]["config_data"]
+        config_data["sign_text"] = ""
+        if code != 0:
+            # self.windows.show_message(return_data["message"])
+            # self.show_progress_detail(return_data["message"])
+            _dialog_dict = {"text": return_data["message"],
+                            "windows": self,
+                            }
+            self.show_dialog_sign.emit(_dialog_dict)
+            self.event.wait()
+            return
+
+        do_next = False
+        text = ""
+        all_goods_art_no_folder_data = return_data["data"]["all_goods_art_no_folder_data"]
+        button_1, button_2, button_3 = None, None, None
+        text += return_data["message"]
+        # 存在错误文件夹
+        error_folder = [x for x in all_goods_art_no_folder_data if x["label"] == "错误"]
+        todo_folder = [x for x in all_goods_art_no_folder_data if x["label"] == "待处理"]
+        if error_folder:
+            button_2 = "移除错误文件"
+        if error_folder and todo_folder:
+            button_1 = "移除并继续"
+            button_3 = "继续(忽略其他)"
+        if not error_folder and todo_folder:
+            button_1 = "继续"
+            # do_next = True
+        text += "\n==================\n错误数据:{}个,校验无误数据:{}个".format(len(error_folder), len(todo_folder))
+        self.show_progress_detail(text)
+        if button_1 is None and button_2 is None and button_3 is None:
+            pass
+        elif button_1 == "继续" and button_2 is None and button_3 is None:
+            do_next = True
+        else:
+            print("runmain  179----------------")
+            # print(self)
+            # print(self.windows)
+            _dialog_dict = {"text": text,
+                            "button_1": button_1,
+                            "button_2": button_2,
+                            "button_3": button_3,
+                            "windows": self,
+                            }
+            self.show_dialog_sign.emit(_dialog_dict)
+            # self.exec_()
+            # 等待事件被设置
+            self.event.wait()
+            print("self.dialog_result", self.dialog_result)
+            #
+            # my_dialog = DialogShow(self.windows, text=text, button_1=button_1, button_2=button_2,
+            #                        button_3=button_3)
+            # ret = my_dialog.exec()
+            print("460  ===============my_dialog.flag_name===============")
+            # print(my_dialog.flag_name)
+
+            if "移除" in self.dialog_result:
+                for error_folder_data in [x for x in all_goods_art_no_folder_data if x["label"] == "错误"]:
+                    self.move_error_folders(
+                        one_path=error_folder_data["folder_path"],
+                        target_folder="{}/软件-处理失败".format(config_data["image_dir"]),
+                    )
+            if "继续" in self.dialog_result:
+                do_next = True
+
+        if do_next:
+            all_goods_art_no_folder_data = [x for x in all_goods_art_no_folder_data if x["label"] == "待处理"]
+            print("===============all_goods_art_no_folder_data===============")
+            print(all_goods_art_no_folder_data)
+
+            new_func = partial(self.do_run_cutout_image,
+                               all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                               callback_func=self.show_progress_detail,
+                               image_order_list=return_data["data"]["image_order_list"],
+                               cutout_mode=return_data["data"]["cutout_mode"],
+                               resize_image_view=return_data["data"]["resize_image_view"],
+                               windows=self.windows,
+                               logo_path=return_data["data"]["logo_path"],
+                               config_data=config_data)
+            self._w_3 = WorkerOneThread(func=new_func, name="_w_3")
+            self._w_3.start()
+
+            # self.t = threading.Thread(target=self.do_run_cutout_image,
+            #                           kwargs={"all_goods_art_no_folder_data": all_goods_art_no_folder_data,
+            #                                   "callback_func": self.show_progress_detail,
+            #                                   "image_order_list": return_data["data"]["image_order_list"],
+            #                                   "cutout_mode": return_data["data"]["cutout_mode"],
+            #                                   "resize_image_view": return_data["data"]["resize_image_view"],
+            #                                   "windows": self.windows,
+            #                                   "logo_path": return_data["data"]["logo_path"],
+            #                                   "config_data": config_data,
+            #                                   })
+            # self.t.start()
+
+        else:
+            config_data["sign_text"] = "已结束抠图处理"
+            self.run_end_sign.emit(config_data)
+
+    def do_run_cutout_image(self,
+                            all_goods_art_no_folder_data,
+                            callback_func,
+                            image_order_list,
+                            cutout_mode,
+                            resize_image_view,
+                            windows,
+                            logo_path,
+                            config_data):
+
+        BaseDealImage().run_main(all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                                 callback_func=callback_func,
+                                 image_order_list=image_order_list,
+                                 cutout_mode=cutout_mode,
+                                 resize_image_view=resize_image_view,
+                                 windows=windows,
+                                 logo_path=logo_path,
+                                 )
+
+        # ==============完成处理==============
+        # self.set_state(state_value=2)
+        callback_func("已结束抠图处理")
+        config_data["sign_text"] = "已结束抠图处理"
+        self.run_end_sign.emit(config_data)
+
+    def do_run_cutout_image1111(self, all_goods_art_no_folder_data,
+                                callback_func,
+                                image_order_list,
+                                cutout_mode,
+                                resize_image_view,
+                                windows,
+                                logo_path,
+                                config_data):
+
+        max_workers = 1
+        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
+            futures = []
+            futures.append(executor.submit(
+                BaseDealImage().run_main,
+                all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                callback_func=callback_func,
+                image_order_list=image_order_list,
+                cutout_mode=cutout_mode,
+                resize_image_view=resize_image_view,
+                windows=windows,
+                logo_path=logo_path,
+            ))
+
+            # 使用 wait 方法等待所有任务完成
+            done, not_done = concurrent.futures.wait(futures, timeout=60)
+            # 处理完成的任务
+            for future in done:
+                if settings.IS_TEST:
+                    result = future.result()
+                else:
+                    try:
+                        result = future.result()
+                    except BaseException as e:
+                        print("2039 图片处理失败.{}".format(e))
+                        callback_func("2039 处理失败.{}".format(e))
+
+        # ==============完成处理==============
+
+        # self.set_state(state_value=2)
+        callback_func("已结束抠图处理")
+        config_data["sign_text"] = "已结束抠图处理"
+        self.run_end_sign.emit(config_data)
+
+    def check_before_detail(self, config_data):
+
+        # =============
+        # 整体数据校验,返回错误内容,以及
+        # temp_name:模板名称
+        """
+        步骤:
+        1、 整体文件夹检查,并输出数据结构
+        2、数据进行对应规整(可能有excel,红蜻蜓等)
+        3、执行单个款数据处理
+        """
+        return_data = {
+            "code": 99,
+            "message": "",
+            "data": {
+                "error_folder_list": [],
+                "goods_no_dict": {},
+                "succeed_folder_list": [],
+                "temp_name": config_data["temp_name"],
+                "temp_name_list": config_data["temp_name_list"],
+                "assigned_page_dict": config_data["assigned_page_dict"],
+                "excel_temp_goods_no_data": {},  # 表格数据可能存在多模板,数据结构为一个款号下的多个模板的数据列表
+                "finally_goods_no_need_temps": {},  # 每个款号需要生成的模板数据
+                "config_data": config_data,
+            },
+        }
+        temp_name = config_data["temp_name"]
+        temp_name_list = config_data["temp_name_list"]
+        assigned_page_dict = config_data["assigned_page_dict"]
+        image_dir = config_data["image_dir"]
+        is_use_excel = config_data["is_use_excel"]
+        excel_path = config_data["excel_path"]
+        temp_class = config_data["temp_class"]
+        is_check_color_is_all = config_data["is_check_color_is_all"]
+        detail_is_pass = config_data["detail_is_pass"]
+
+        error_folder_list = []
+        # 遍历货号获取所有货号--可能为编号
+        folder_name_list = detail_func.get_all_dir_info(image_dir=image_dir)
+        if not folder_name_list:
+            return_data["message"] += "不存在任何货号/编号文件夹\n"
+            print("不存在任何货号/编号文件夹")
+            return return_data
+
+        # =========================组装数据---数据来源多种途径=========================
+        _result = {"code": 99, "message": "无法解析到数据,请检查登录企业"}
+        if not is_use_excel:
+            if settings.PROJECT == "红蜻蜓":
+                # goods_no_dict输出为文件夹下涉及到的所有款为key的字典,后续通过解析字典,进行提取对应文件夹
+                _result = self.data_mode_generate_detail.get_basic_goods_art_data_by_hqt_and_hlm(
+                    folder_name_list
+                )
+
+            elif settings.PROJECT == "惠利玛":
+                if settings.Company:
+                    if "惠利玛" in settings.Company:
+                        _result = self.data_mode_generate_detail.get_basic_goods_art_data_by_hqt_and_hlm(
+                            folder_name_list
+                        )
+        else:
+            keys = settings.keys
+            _result = (
+                self.data_mode_generate_detail.get_basic_goods_art_data_form_excel(
+                    folder_name_list,
+                    excel_path,
+                    keys,
+                )
+            )
+
+        if _result["code"] == 0:
+            remote_data = _result["data"]
+        else:
+            return_data["message"] += _result["message"] + "\n"
+            return return_data
+        # print(json.dumps(remote_data))
+        # =========================拼接数据组合为款数据=========================
+        """
+        1、获取所有文件夹基础数据内容
+        2、结合上述返回结果进行数据组合拼接
+        3、输出结果为按款为主键信息
+        4、注意模板ID
+        """
+        # 获取所有文件夹基础数据内容  检查不满足要求的文件不满足要求移动到错误文件夹
+        need_view_list = temp_class[temp_name].need_view
+        _all_dir_info_data = detail_func.get_all_dir_info_and_pic_info(
+            image_dir, folder_name_list, need_view_list
+        )
+        all_dir_info_data = {}
+        for one_folder, value in _all_dir_info_data.items():
+            if "message" in value:
+                if value["message"]:
+                    error_folder_list.append(
+                        {
+                            "folder_name": one_folder,
+                            "folder_path": "{}/{}".format(image_dir, one_folder),
+                        }
+                    )
+                    return_data["message"] += "文件夹:{} 结构错误:{}\n".format(
+                        one_folder, value["message"]
+                    )
+                    continue
+
+            # 符合要求的数据处理
+            all_dir_info_data[one_folder] = value
+
+        # 结合上述返回结果进行数据组合拼接
+        # 返回可能存在的情况:1、存在文件夹不匹配数据,2、存在货号数据缺失
+        goods_no_dict, error_folder_name_list = detail_func.merge_local_and_remote_data(
+            all_dir_info_data=all_dir_info_data, remote_data=remote_data
+        )
+        # 文件在系统中没有匹配的结果
+        if error_folder_name_list:
+            for one_folder in error_folder_name_list:
+                error_folder_list.append(
+                    {
+                        "folder_name": one_folder,
+                        "folder_path": "{}/{}".format(image_dir, one_folder),
+                    }
+                )
+                return_data["message"] += "文件夹:{} 找不到对应数据\n".format(
+                    one_folder
+                )
+
+        print("===============goods_no_dict==================")
+        if settings.IS_TEST:
+            _text = json.dumps(goods_no_dict)
+            print(goods_no_dict)
+            create_folder("qt_test")
+            with open("qt_test/goods_no_dict.txt", 'w', encoding='utf-8') as file:
+                file.write(_text)
+        print("===============goods_no_dict==================")
+
+        # ===================================是否齐色检查=============================================
+        # 如为红蜻蜓企业则还需要检查同款下是否齐全;数据返回结果为款号列表
+        error_data_dict = {}
+        if is_check_color_is_all:
+            if not is_use_excel:
+                if settings.PROJECT == "红蜻蜓":
+                    error_data_dict = (
+                        self.data_mode_generate_detail.check_goods_is_not_deficiency(
+                            goods_no_dict
+                        )
+                    )
+            else:
+                error_data_dict = self.data_mode_generate_detail.check_goods_is_not_deficiency_form_excel(
+                    goods_no_dict, excel_path
+                )
+
+        if error_data_dict:
+            print("----error_data_dict----")
+            print(json.dumps(error_data_dict))
+
+            # 款号反向映射;因为部分key键格式为KUM9999999
+            _x = {}
+            for i, v in goods_no_dict.items():
+                _x[v["款号"]] = i
+
+            for goods_no, value in error_data_dict.items():
+                if goods_no in _x:
+                    goods_no = _x[goods_no]
+                if goods_no in goods_no_dict:
+                    # =====移动到错误文件夹======
+                    for _folder_name in [
+                        x["文件夹名称"] for x in goods_no_dict[goods_no]["货号资料"]
+                    ]:
+                        error_folder_list.append(
+                            {
+                                "folder_name": _folder_name,
+                                "folder_path": "{}\{}".format(
+                                    image_dir, _folder_name
+                                ),
+                            }
+                        )
+                        return_data["message"] += "文件夹:{};{}\n".format(
+                            _folder_name, value["message"]
+                        )
+                    # 从 正确 款下面进行数据剔除
+                    goods_no_dict.pop(goods_no)
+
+        print("-----------------1goods_no_dict---------------")
+        print(json.dumps(goods_no_dict, ensure_ascii=False))
+        print("-----------------1goods_no_dict---------------")
+
+        # 如果没有有效数据则进行退出
+        if not goods_no_dict:
+            return_data["message"] += "没有任何有效数据\n"
+            return return_data
+
+        # 校验无误的文件夹数据  goods_no_dict为最终有效数据
+        for goods_no, value in goods_no_dict.items():
+            return_data["data"]["succeed_folder_list"].extend(
+                [x["文件夹名称"] for x in goods_no_dict[goods_no]["货号资料"]]
+            )
+
+        # 如果是表格数据,则获取表格数据中需要生成的货号
+        # 数据结构
+        # {“款号1”:
+        # {“模板名称1”:data_dict1,
+        # “模板名称2”:data_dict2,
+        # }}
+
+        if is_use_excel:
+            excel_temp_goods_no_data = self.data_mode_generate_detail.get_basic_template_information(
+                _goods_no_dict=goods_no_dict, excel_path=excel_path)
+        else:
+            excel_temp_goods_no_data = {}
+
+        print("731===================excel_temp_goods_no_data,is_use_excel:{}".format(is_use_excel))
+        print(json.dumps(excel_temp_goods_no_data))
+
+        # ===========数据组装,统计每个款需要生成哪些模板==============
+        # 不适用表格指定模板
+        goods_no_need_temps = {}
+        # isUseTemplate 为false 表示使用excel表格数据
+        if not is_use_excel:
+            for i in goods_no_dict:
+                goods_no_need_temps[i] = [temp_name]
+        else:
+            for i in goods_no_dict:
+                # i 为款号
+                if i in excel_temp_goods_no_data:
+                    # 获取 某个款号的所有允许生成的模板列表
+                    goods_no_need_temps[i] = list(excel_temp_goods_no_data[i].keys())
+
+        # ========开始执行详情图生成===================
+        # _goods_no_dict = goods_no_dict
+        _goods_no_dict = {}
+
+        # assigned_page_dict 如存在对应模板的,则不管是否有过滤都需要生成
+        # 检查是否已存在模板,已存在的需要进行跳过;可能部分模板已存在,部分不存在。
+        finally_goods_no_need_temps = {}
+        for goods_no, value in goods_no_dict.items():
+            if goods_no not in goods_no_need_temps:
+                continue
+            for __temp_name in goods_no_need_temps[goods_no]:
+                _path = "{}/{}/{}/{}".format(image_dir, "软件-详情图生成", __temp_name, goods_no)
+                if not os.path.exists(_path):
+                    print("款号详情图不存在", _path)
+                    if goods_no not in finally_goods_no_need_temps:
+                        finally_goods_no_need_temps[goods_no] = []
+                        _goods_no_dict[goods_no] = value  # 需要生成的数据
+                    finally_goods_no_need_temps[goods_no].append(__temp_name)
+                else:
+                    print("款号详情图存在", _path)
+                    # 如果在指定模板中存在,则也需要生成
+                    if __temp_name in assigned_page_dict:
+                        print("指定模板需要更新", _path)
+                        if goods_no not in finally_goods_no_need_temps:
+                            finally_goods_no_need_temps[goods_no] = []
+                            _goods_no_dict[goods_no] = value  # 需要生成的数据
+                        finally_goods_no_need_temps[goods_no].append(__temp_name)
+                    else:
+                        if detail_is_pass:
+                            return_data["message"] += "\n款号:{},模板:{} 已存在".format(goods_no, __temp_name)
+                        else:
+                            if goods_no not in finally_goods_no_need_temps:
+                                finally_goods_no_need_temps[goods_no] = []
+                                _goods_no_dict[goods_no] = value  # 需要生成的数据
+                            finally_goods_no_need_temps[goods_no].append(__temp_name)
+
+            pass
+            # _path = "{}/{}".format(self.image_dir, "软件-详情图生成")
+            # if os.path.exists(_path):
+            #     _goods_no_dict = {}
+            #     # 数据返回为 已有的款数据,为款号列表
+            #     is_pass_goods_no = detail_func.get_all_detail_info(_path)
+            #     for goods_no, value in goods_no_dict.items():
+            #         if "软件" in goods_no:
+            #             continue
+            #
+            #         if value["模板名称"] in assigned_page_dict:
+            #             need_todo = True
+            #         else:
+            #             if goods_no in is_pass_goods_no:
+            #                 need_todo = False
+            #             else:
+            #                 need_todo = True
+            #         if need_todo:
+            #             _goods_no_dict[goods_no] = value
+
+        print("-----------------2goods_no_dict---------------")
+        print(json.dumps(_goods_no_dict, ensure_ascii=False))
+        print("-----------------2goods_no_dict---------------")
+        return_data["data"]["error_folder_list"] = error_folder_list
+
+        if len(_goods_no_dict) == 0:
+            return_data["message"] += "\n没有任何文件夹需要执行"
+
+        return_data["data"]["goods_no_dict"] = _goods_no_dict
+        return_data["data"]["excel_temp_goods_no_data"] = excel_temp_goods_no_data
+        return_data["data"]["finally_goods_no_need_temps"] = finally_goods_no_need_temps
+
+        return_data["code"] = 0
+        return return_data
+
+    def move_error_folders(self, one_path, target_folder, message=""):
+        if os.path.exists(one_path):
+            check_path(target_folder)
+            move_folders(path_list=[one_path], target_folder=target_folder)
+
+    def check_for_detail_first_call_back(self, data):
+        # 首次数据校验的信息返回
+        # self.show_message(text="22222222222222222222222")
+        # QMessageBox.critical(self, "警告", "1111111", QMessageBox.Ok)
+        code = data["code"]
+        config_data = data["data"]["config_data"]
+        target_error_folder = config_data["target_error_folder"]
+        print("635  check_for_detail_first_call_back")
+        print(data)
+        if code != 0:
+            # self.windows.show_message(data["message"])
+            # my_dialog = DialogShow(self.windows.windows, text=data["message"])
+            # ret = my_dialog.exec()
+            self.show_progress_detail(text=data["message"])
+            _dialog_dict = {"text": data["message"],
+                            "windows": self,
+                            }
+            self.show_dialog_sign.emit(_dialog_dict)
+            self.event.wait()
+
+            # self.windows.set_state(2)
+            config_data["sign_text"] = "已结束详情处理"
+            self.run_end_sign.emit(config_data)
+            return
+
+        do_next = False
+        if data["message"]:
+            button_1, button_2, button_3 = None, None, None
+            text = data["message"]
+            if code == 0:
+                if data["data"]:
+                    if data["data"]["error_folder_list"]:
+                        print("22----------error_folder_list------------")
+                        print(json.dumps(data["data"]["error_folder_list"]))
+                        button_2 = "移除错误文件"
+                    if data["data"]["goods_no_dict"]:
+                        if button_2:
+                            button_1 = "移除并继续"
+                            button_3 = "继续(忽略其他)"
+                        else:
+                            button_1 = "继续"
+                    if data["data"]["succeed_folder_list"]:
+                        text += "\n==================\n校验无误数据:{}个文件夹".format(
+                            len(data["data"]["succeed_folder_list"])
+                        )
+            self.show_progress_detail(text=data["message"])
+            if button_1 is None and button_2 is None and button_3 is None:
+                pass
+            elif button_1 == "继续" and button_2 is None and button_3 is None:
+                do_next = True
+            else:
+                print("runmain  642----------------")
+                # todo 弹框修改处理等
+                _dialog_dict = {"text": text,
+                                "button_1": button_1,
+                                "button_2": button_2,
+                                "button_3": button_3,
+                                "windows": self,
+                                }
+                self.show_dialog_sign.emit(_dialog_dict)
+                # 等待事件被设置
+                self.event.wait()
+                print("self.dialog_result", self.dialog_result)
+
+                # my_dialog = DialogShow(
+                #     self.windows, text=text, button_1=button_1, button_2=button_2, button_3=button_3
+                # )
+                print("my_dialog.flag_name", self.dialog_result)
+                if "移除" in self.dialog_result:
+                    for error_folder_data in data["data"]["error_folder_list"]:
+                        self.move_error_folders(
+                            one_path=error_folder_data["folder_path"],
+                            target_folder=target_error_folder,
+                        )
+                if "继续" in self.dialog_result:
+                    do_next = True
+
+        if data["data"]["goods_no_dict"] and not data["data"]["error_folder_list"]:
+            do_next = True
+
+        if do_next:
+            # self.set_state(state_value=1)
+            getAllData = data["data"]
+            base_temp_name = getAllData["temp_name"]
+            set_temp_name = getAllData.get("template_name", "")
+            kwargs = {
+                "config_data": config_data,
+                "_goods_no_dict": data["data"]["goods_no_dict"],
+                "temp_name": base_temp_name,
+                "temp_name_list": data["data"]["temp_name_list"],
+                "assigned_page_dict": data["data"]["assigned_page_dict"],
+                "excel_temp_goods_no_data": data["data"]["excel_temp_goods_no_data"],
+                # 表格数据可能存在多模板,数据结构为一个款号下的多个模板的数据列表
+                "finally_goods_no_need_temps": data["data"]["finally_goods_no_need_temps"],  # 每个款号需要生成的模板数据
+            }
+            # todo work
+            new_func = partial(self.detail_run_by_thread,
+                               config_data=kwargs["config_data"],
+                               _goods_no_dict=kwargs["_goods_no_dict"],
+                               temp_name=kwargs["temp_name"],
+                               temp_name_list=kwargs["temp_name_list"],
+                               assigned_page_dict=kwargs["assigned_page_dict"],
+                               excel_temp_goods_no_data=kwargs["excel_temp_goods_no_data"],
+                               finally_goods_no_need_temps=kwargs["finally_goods_no_need_temps"])
+            self._w_3 = WorkerOneThread(func=new_func, name="_w_3")
+            self._w_3.start()
+            # threading.Thread(target=self.detail_run_by_thread, kwargs=kwargs).start()
+        else:
+            config_data["sign_text"] = "已结束详情处理"
+            self.run_end_sign.emit(config_data)
+
+    def detail_run_by_thread(self, config_data, _goods_no_dict, temp_name, temp_name_list, assigned_page_dict,
+                             excel_temp_goods_no_data,
+                             finally_goods_no_need_temps):
+        """
+        excel_temp_goods_no_data: {},  # 表格数据可能存在多模板,数据结构为一个款号下的多个模板的数据列表
+        finally_goods_no_need_temps: {},  # 每个款号需要生成的模板数据
+        """
+
+        # 开始处理
+        self.n = 0
+        self.total_num = len(_goods_no_dict)
+        self.fail_num = 0
+        is_use_excel = config_data["is_use_excel"]
+        image_dir = config_data["image_dir"]
+
+        # 详情图生成结果文件夹
+        out_put_dir = "{}\软件-详情图生成".format(image_dir)
+        if settings.IS_TEST:
+            print("==============_goods_no_dict  打印=================")
+
+            print(json.dumps(_goods_no_dict))
+
+            print("==============_goods_no_dict  打印-end=================")
+
+        all_detail_path_list = []
+        for goods_no, temp_name_list in finally_goods_no_need_temps.items():
+            try:
+                for _temp_name in temp_name_list:
+                    # if _temp_name != "xiaosushuoxie-4":
+                    #     continue
+
+                    assigned_page_list = []
+                    if _temp_name in assigned_page_dict:
+                        assigned_page_list = assigned_page_dict[_temp_name]
+                    # 如果为使用表格,则获取表格中的数据作为款号的基础数据
+                    temp_info_data = copy.copy(_goods_no_dict[goods_no])
+                    if is_use_excel:
+                        # 将表格中的数据进行替换
+                        if goods_no in excel_temp_goods_no_data:
+                            if _temp_name in excel_temp_goods_no_data[goods_no]:
+                                # 将表格中的特定的模板的行,替换到goods_no的data中,因为不同的模板有数据特殊性
+                                for _key, _key_value in excel_temp_goods_no_data[goods_no][_temp_name].items():
+                                    if _key in temp_info_data:
+                                        temp_info_data[_key] = _key_value
+
+                    print("temp_info_data")
+                    print("goods_no:{},_temp_name:{}".format(goods_no, _temp_name))
+                    all_detail_path_list.append("{}/{}/".format(out_put_dir, _temp_name, goods_no))
+                    # continue
+                    self.detail_deal_one_data(goods_no=goods_no,
+                                              value=temp_info_data,
+                                              out_put_dir=out_put_dir,
+                                              temp_name=_temp_name,
+                                              assigned_page_list=assigned_page_list)
+            except BaseException as e:
+                self.show_progress_detail(
+                    "款:{}生成详情异常:{}".format(goods_no, e))
+                print(e)
+
+        # ==============完成处理==============
+        self.set_state(state_value=2)
+        if self.total_num:
+            if self.fail_num:
+                self.show_progress_detail("处理完成,-----处理失败数据:{}个款".format(self.fail_num))
+            else:
+                self.show_progress_detail("处理完成")
+        else:
+            self.show_progress_detail("没有任何数据")
+
+        config_data["sign_text"] = "已结束详情处理"
+        config_data["all_detail_path_list"] = all_detail_path_list
+        # 打开文件夹
+        os.startfile(out_put_dir)
+
+        self.run_end_sign.emit(config_data)
+
+    def detail_run_by_thread11111(self, config_data, _goods_no_dict, temp_name, temp_name_list, assigned_page_dict,
+                                  excel_temp_goods_no_data,
+                                  finally_goods_no_need_temps):
+        """
+        excel_temp_goods_no_data: {},  # 表格数据可能存在多模板,数据结构为一个款号下的多个模板的数据列表
+        finally_goods_no_need_temps: {},  # 每个款号需要生成的模板数据
+        """
+
+        # 开始处理
+        self.n = 0
+        self.total_num = len(_goods_no_dict)
+        self.fail_num = 0
+        is_use_excel = config_data["is_use_excel"]
+        image_dir = config_data["image_dir"]
+
+        # 详情图生成结果文件夹
+        out_put_dir = "{}\软件-详情图生成".format(image_dir)
+        if settings.IS_TEST:
+            print("==============_goods_no_dict  打印=================")
+
+            print(json.dumps(_goods_no_dict))
+
+            print("==============_goods_no_dict  打印-end=================")
+
+        if settings.IS_TEST:
+            max_workers = 1
+        else:
+            max_workers = 1
+
+        all_detail_path_list = []
+
+        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
+            futures = []
+            for goods_no, temp_name_list in finally_goods_no_need_temps.items():
+                for _temp_name in temp_name_list:
+                    # if _temp_name != "xiaosushuoxie-4":
+                    #     continue
+
+                    assigned_page_list = []
+                    if _temp_name in assigned_page_dict:
+                        assigned_page_list = assigned_page_dict[_temp_name]
+                    # 如果为使用表格,则获取表格中的数据作为款号的基础数据
+                    temp_info_data = copy.copy(_goods_no_dict[goods_no])
+                    if is_use_excel:
+                        # 将表格中的数据进行替换
+                        if goods_no in excel_temp_goods_no_data:
+                            if _temp_name in excel_temp_goods_no_data[goods_no]:
+                                # 将表格中的特定的模板的行,替换到goods_no的data中,因为不同的模板有数据特殊性
+                                for _key, _key_value in excel_temp_goods_no_data[goods_no][_temp_name].items():
+                                    if _key in temp_info_data:
+                                        temp_info_data[_key] = _key_value
+
+                    print("temp_info_data")
+                    print("goods_no:{},_temp_name:{}".format(goods_no, _temp_name))
+                    all_detail_path_list.append("{}/{}/".format(out_put_dir, _temp_name, goods_no))
+                    # continue
+                    futures.append(executor.submit(
+                        self.detail_deal_one_data,
+                        goods_no=goods_no,
+                        value=temp_info_data,
+                        out_put_dir=out_put_dir,
+                        temp_name=_temp_name,
+                        assigned_page_list=assigned_page_list,
+                    ))
+
+            # for goods_no, value in _goods_no_dict.items():
+            #     _temp_name = temp_name
+            #     # 使用自定义的表格数据
+            #     if self.isUseTemplate is False:
+            #         if "模板名称" in value:
+            #             if value["模板名称"] in temp_name_list:
+            #                 _temp_name = value["模板名称"]
+            #     assigned_page_list = []
+            #     if _temp_name in assigned_page_dict:
+            #         assigned_page_list = assigned_page_dict[_temp_name]
+            #
+            #     futures.append(executor.submit(
+            #         self.deal_one_data,
+            #         goods_no=goods_no,
+            #         value=value,
+            #         out_put_dir=out_put_dir,
+            #         temp_name=_temp_name,
+            #         assigned_page_list=assigned_page_list,
+            #     ))
+
+            # 使用 wait 方法等待所有任务完成
+            done, not_done = concurrent.futures.wait(futures)
+
+            # 处理完成的任务
+            for future in done:
+                if settings.IS_TEST:
+                    result = future.result()
+
+        # ==============完成处理==============
+        self.set_state(state_value=2)
+        if self.total_num:
+            if self.fail_num:
+                self.show_progress_detail(
+                    "处理完成,-----------处理失败数据:{}个款".format(self.fail_num)
+                )
+            else:
+                self.show_progress_detail("处理完成")
+        else:
+            self.show_progress_detail("没有任何数据")
+
+        config_data["sign_text"] = "已结束详情处理"
+        config_data["all_detail_path_list"] = all_detail_path_list
+        self.run_end_sign.emit(config_data)
+
+    def show_progress_detail(self, text):
+        self.show_progress_detail_sign.emit(text)
+        # self.windows.show_progress_detail(text)
+
+    def detail_deal_one_data(self, goods_no, value, out_put_dir, temp_name, assigned_page_list):
+        if self.windows.state == 99:
+            self.show_progress_detail("用户主动取消:{}".format(goods_no))
+            return
+
+        self.show_progress_detail("正在生成:{},模板:{}".format(goods_no, temp_name))
+        is_deal_success = False
+        print("=================deal_one_data=====================")
+        print("goods_no", goods_no)
+        print("模板:", temp_name)
+        print("value:", value)
+
+        if settings.IS_TEST:
+            d = self.windows.temp_class[temp_name](goods_no, value,
+                                                   out_put_dir=out_put_dir,
+                                                   windows=self.windows,
+                                                   assigned_page_list=assigned_page_list)
+            is_deal_success = True
+        else:
+            try:
+                # # 处理图片详情图生成
+                d = self.windows.temp_class[temp_name](goods_no, value,
+                                                       out_put_dir=out_put_dir,
+                                                       windows=self.windows,
+                                                       assigned_page_list=assigned_page_list)
+                is_deal_success = True
+            except BaseException as e:
+                self.show_progress_detail("{}处理失败".format(goods_no))
+                error_text = "{}".format(e)
+                if "Unable to allocate" in error_text:
+                    error_text = "电脑内存不足,生成失败"
+
+                self.show_progress_detail("失败原因:{}".format(error_text))
+                self.fail_num += 1
+
+        self.n += 1
+
+        if not is_deal_success:
+            goods_art_no_list = value["货号资料"]
+            self.show_progress_detail("处理失败")
+            self.show_progress_detail(
+                "相关货号:{}".format([x["货号"] for x in goods_art_no_list])
+            )
+            # 将相关的文件夹统一移动至错误文件夹
+            detail_func.move_folders(
+                path_list=[
+                    "{}/{}".format(self.windows.image_dir, x)
+                    for x in [x["文件夹名称"] for x in goods_art_no_list]
+                ],
+                target_folder=self.windows.target_error_folder,
+            )
+            pass
+        # 更新进度
+        print(self.n, self.total_num)
+        self.windows.progress_sign.emit(
+            {
+                "type": "详情图生成",
+                "progress_bar_value": int(self.n / self.total_num * 100),
+            }
+        )
+
+    def check_serializable(self, obj):  # 检查某个对象其中的属性哪些不能被序列化
+        for attr_name in dir(obj):
+            if not attr_name.startswith('__'):
+                try:
+                    attr_value = getattr(obj, attr_name)
+                    serialized = pickle.dumps(attr_value)
+                    print(f"属性 {attr_name} 是可序列化的。")
+                except (TypeError, pickle.PicklingError):
+                    print(f"属性 {attr_name} 不可序列化。")
+
+    def on_dialog_result(self, result):
+        """处理对话框结果"""
+        self.dialog_result = result
+        print("972  处理对话框结果:{}".format(result))
+        # self.quit()  # 结束事件循
+        self.event.set()

+ 473 - 0
python/service/auto_deal_pics/upload_pic.py

@@ -0,0 +1,473 @@
+from ..generate_goods_no_detail_pic.data import DataModeUploadPic
+import os
+import threading
+import time
+import concurrent.futures
+import re
+from PIL import Image
+from io import BytesIO
+from middleware import UnicornException
+
+# 详情图上传
+class UploadPic():
+    # signal_data = Signal(dict)
+    # run_end_sign = Signal(dict)
+    # show_progress_detail_sign = Signal(str)
+
+    def __init__(self, windows, to_deal_dir, config_data,token):
+        super().__init__()
+        self.windows = windows
+        self.data_mode_upload_pic = DataModeUploadPic(token)
+        self.goods_no_data = {}
+        # ------------------
+        # 0未选择文件,1已选文件未开始,2进行中
+        self.state = 0
+        self.state_change(self.state)
+        self.to_deal_dir = to_deal_dir
+        self.config_data = config_data
+
+    def check_path(self, _path):
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def run_by_thread(self):
+        self.run_by_thread_real()
+        self.config_data["sign_text"] = "结束"
+        self.run_end_sign.emit(self.config_data)
+
+    def run(self):
+        self.run_by_thread_real()
+
+    def run_by_thread_real(self, *args):
+        self.show_info("====开始处理====")
+        # 统计哪些需要处理
+        total_num = 0
+        flag = True
+        # group_folders 分组文件夹
+        for group_folders in os.listdir(self.to_deal_dir):
+            path = "{}\{}".format(self.to_deal_dir, group_folders)
+            if not os.path.isdir(path):
+                continue
+            for goods_no in os.listdir(path):
+                # 检测是不是货号或款号文件夹
+                goods_no_path = "{}\{}\{}".format(self.to_deal_dir, group_folders, goods_no)
+                if not os.path.isdir(goods_no_path):
+                    continue
+
+                total_num += 1
+                # 检查每个款号文件夹是不是合规
+                # 款的一级目录文件
+                files = os.listdir(goods_no_path)
+                if "details" not in files:
+                    continue
+                not_goods_art_msg = ""
+                for f in files:
+                    f_path = "{}\{}\{}\{}".format(self.to_deal_dir, group_folders, goods_no, f)
+                    if os.path.isdir(f_path):
+                        # 处理货号文件夹
+                        if "details" != f and "货号素材" not in f and "main" not in f and "拼接图" not in f:
+                            # 是否货号文件夹判断
+                            if not self.is_goods_art_dir(f, goods_no):
+                                self.show_info("{}文件夹下  {} {} 疑似不是货号".format(group_folders, goods_no, f))
+                                not_goods_art_msg += (
+                                    "{}文件夹下  {} {} 疑似不是货号".format(
+                                        group_folders, goods_no, f
+                                    )
+                                    + "\n"
+                                )
+                                flag = False
+        if not flag:
+            self.show_info("请解决以上问题后重试")
+            raise UnicornException(f"请解决:\n{not_goods_art_msg}\n以上问题后重试")
+            return
+
+        if total_num == 0:
+            self.show_info("没有任何款号文件夹 需要上传")
+            raise UnicornException("没有任何款号文件夹 需要上传")
+            return
+
+        self.lock = threading.Lock()
+
+        error_data = []
+        n = 0
+        for group_folders in os.listdir(self.to_deal_dir):
+            path = "{}\{}".format(self.to_deal_dir, group_folders)
+            if not os.path.isdir(path):
+                continue
+
+            for goods_no in os.listdir(path):
+                goods_no_path = "{}\{}\{}".format(self.to_deal_dir, group_folders, goods_no)
+                if not os.path.isdir(goods_no_path):
+                    self.show_info("{} 不是文件夹".format(goods_no))
+                    continue
+                if goods_no in self.data_mode_upload_pic.is_deal_goods_no:
+                    continue
+                else:
+                    self.data_mode_upload_pic.is_deal_goods_no.append(goods_no)
+
+                # ================处理单个款号信息,图片上传处理============
+                self.show_info("开始处理  {}".format(goods_no))
+                # try:
+                if self.deal_goods_pic(goods_no, goods_no_path):
+                        # 移动文件到已完成
+                        # dest = "{}\{}\{}".format(self.is_deal_dir, group_folders, goods_no)
+                        # if os.path.exists(dest):
+                        #     self.show_info("{} 文件夹在目标目录已存在".format(goods_no))
+                        # else:
+                        #     shutil.move(goods_no_path, dest)
+                        self.show_info("{} 上传成功".format(goods_no))
+                else:
+                        self.show_info("{} 处理失败".format(goods_no))
+                        error_data.append(goods_no)
+                        pass
+                # except BaseException as e:
+                #     self.show_info("上传异常:{},{}".format(goods_no, e))
+                #     raise UnicornException("上传异常:{},{}".format(goods_no, e))
+                # n += 1
+                # self.show_info({"type": "show_p",
+                #                 "data": int(n * 100 / total_num)})
+
+        self.show_info("========已结束========== ")
+        if error_data:
+            self.show_info("以下编号/款号处理失败")
+            error_msg = ""
+            for data in error_data:
+                self.show_info("{}".format(data))
+                error_msg += data+"\n"
+            raise UnicornException(f"以下编号/款号处理失败\n{error_msg}")
+        # self.show_info({"type": "change_state",
+        #                 "data": 2})
+
+    def deal_goods_pic(self, goods_no, goods_no_path):
+        """
+        两种模式:货号模式、编号模式
+        :param goods_no:
+        :param goods_no_path:
+        :return:
+        """
+        print("==========开始上传系统=============")
+
+        s = time.time()
+
+        self.show_info("{} 开始上传 ".format(goods_no))
+
+        if "NUM" in goods_no.upper():
+            mode = "编号"
+        else:
+            mode = "货号"
+
+        # 校验所有编号是否存在,是否是同个款
+        _numbers = []
+        if mode == "编号":
+            _numbers.append(goods_no)
+
+        self.goods_no_data[goods_no] = {}
+        # 收集该款号下可能存在的货号或编号
+        files = os.listdir(goods_no_path)
+        if "details" not in files:
+            self.show_info("{} 缺少details 文件夹".format(goods_no, ))
+            raise UnicornException("{} 缺少details 文件夹".format(goods_no))
+            return False
+        for f in files:
+            f_path = "{}\{}".format(goods_no_path, f)
+            if os.path.isdir(f_path):
+                if "details" != f and "货号素材" not in f and "main" not in f and "拼接图" not in f:
+                    if not self.is_goods_art_dir(f, goods_no):
+                        self.show_info("{} {} 疑似不是货号".format(goods_no, f))
+                        # return False
+                        continue
+                    else:
+                        _numbers.append(f)
+
+                if "货号素材" in f:
+                    if "_" not in f:
+                        self.show_info("{} 货号素材文件夹 格式错误".format(goods_no))
+                        raise UnicornException("{} 货号素材文件夹 格式错误".format(goods_no))
+                        return False
+
+        # 校验所有编号是否存在,是否是同个款
+        if mode == "编号":
+            _numbers = [x.replace("KNUM", "").replace("NUM", "") for x in _numbers]
+            _numbers = list(set(_numbers))
+            goods_number_data = self.data_mode_upload_pic.get_goods_art_no_info(numbers_list=_numbers)
+
+        else:
+            goods_number_data = self.data_mode_upload_pic.get_goods_art_no_info(goods_art_list=_numbers)
+
+        t = True
+        for num in _numbers:
+            if num not in goods_number_data:
+                t = False
+                if mode == "编号":
+                    self.show_info("{} 编号在系统中不存在".format(num))
+                else:
+                    self.show_info("{} 货号在系统中不存在".format(num))
+            else:
+                if self.config_data["upload_is_pass"]:
+                    if self.data_mode_upload_pic.check_is_uploaded(num):
+                        t = False
+                        self.show_info("{} 货号在系统已有详情页,已跳过".format(num))
+                        return True
+
+        if not t:
+            return False
+
+        _ = set([goods_number_data[x]["款号"] for x in goods_number_data])
+        if len(_) != 1:
+            self.show_info("{} 存在多个不同的款号".format(_))
+            raise UnicornException("{} 存在多个不同的款号".format(_))
+            return False
+
+        # 重新定义款号
+        goods_no = _.pop()
+
+        # 上传所有图片
+        self.pic_data = {}
+        ignore_text = ["main", "拼接图", "货号素材"]
+        _Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF']
+        for dirpath, dirnames, filenames in os.walk(goods_no_path):
+            for file in filenames:
+                if os.path.splitext(file)[1] in _Type:
+                    # 获取当前路径的文件夹名称
+                    f_path = dirpath + '/' + file
+                    f = True
+                    for i in ignore_text:
+                        if i in f_path:
+                            f = False
+                            break
+                    if f:
+                        print(f_path)
+                        f_path = f_path.replace("\\", "/")
+                        self.pic_data[f_path] = {"url": ""}
+
+        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
+            for key in self.pic_data:
+                if "货号素材" in key:
+                    # 货号素材图不上传
+                    is_resize = False
+                    continue
+                else:
+                    is_resize = True
+                executor.submit(self.to_upload_pic, file_path=key, is_resize=is_resize)
+
+        for key in self.pic_data:
+            if "货号素材" in key:
+                is_resize = False
+                continue
+
+            if not self.pic_data[key]["url"]:
+                self.show_info("{} {} 图片未上传完成".format(key, goods_no))
+                raise UnicornException("{} {} 图片未上传完成".format(key, goods_no))
+                return
+
+        up_data = {"goods_art_no": [],
+                   "details": [],
+                   "goods_no": [],
+                   "goods_art_no_material": []
+                   }
+
+        details_index = 0
+        goods_no_index = 0
+        # 款号转换
+        # files 为当前款下的所有文件或文件夹
+        for f in files:
+            f_path = "{}\{}".format(goods_no_path, f)
+            f_path = f_path.replace("\\", "/")
+            # 如果为文件夹则
+            if os.path.isdir(f_path):
+                if "details" == f:
+                    # 处理详情图
+                    for pic in os.listdir(f_path):
+                        pic_path = "{}\{}".format(f_path, pic)
+                        pic_path = pic_path.replace("\\", "/")
+
+                        details_index += 1
+                        data = {
+                            "index": details_index,
+                            "name": goods_no,
+                            "number": goods_no,
+                            "status": "success",
+                            "type": "2",  # 2 为详情图
+                            "uid": "",
+                            "url": self.pic_data[pic_path]["url"],
+                        }
+                        up_data["details"].append(data)
+                    pass
+                elif "货号素材" in f:
+                    continue
+                    x = f.split("_")[0]
+                    if mode == "编号":
+                        x = f.replace("NUM", "")
+                        x = x.replace("_货号素材", "")
+                        goods_art_no = goods_number_data[x]["商品货号"]
+                    else:
+                        goods_art_no = x
+                    n = 0
+                    for pic in os.listdir(f_path):
+                        pic_path = "{}\{}".format(f_path, pic)
+                        pic_path = pic_path.replace("\\", "/")
+                        n += 1
+                        data = {
+                            "index": n,
+                            "name": goods_art_no,
+                            "number": goods_art_no,
+                            "status": "success",
+                            "type": "3",
+                            "uid": "",
+                            "url": self.pic_data[pic_path]["url"],
+                        }
+                        up_data["goods_art_no_material"].append(data)
+                else:
+                    if "拼接图" in f:
+                        continue
+                    if "main" in f:
+                        continue
+
+                    # 处理货号图
+                    if mode == "编号":
+                        x = f.replace("NUM", "")
+                        goods_art_no = goods_number_data[x]["商品货号"]
+                    else:
+                        goods_art_no = f
+
+                    for pic in os.listdir(f_path):
+                        pic_path = "{}\{}".format(f_path, pic)
+                        pic_path = pic_path.replace("\\", "/")
+                        data = {
+                            "index": 1,
+                            "name": goods_art_no,
+                            "number": goods_art_no,
+                            "status": "success",
+                            "type": "0",  # 0为货号图
+                            "uid": "",
+                            "url": self.pic_data[pic_path]["url"],
+                        }
+                        up_data["goods_art_no"].append(data)
+                        break
+            else:
+                # 处理款号图
+                goods_no_index += 1
+                data = {
+                    "index": goods_no_index,
+                    "name": goods_no,
+                    "number": goods_no,
+                    "status": "success",
+                    "type": "1",
+                    "uid": "",
+                    "url": self.pic_data[f_path]["url"],
+                }
+                up_data["goods_no"].append(data)
+                pass
+
+        # -----------上传数据-----------------
+        # print(up_data["goods_no"])
+        # print(up_data["goods_art_no_material"])
+        # print(up_data["details"])
+
+        # 上传图片
+        for key, value in up_data.items():
+            if value:
+                if not self.data_mode_upload_pic.upload_pic_list_data(data={"images": value}):
+                    self.show_info("{} {}上传错误".format(goods_no, key))
+                    raise UnicornException("{} {}上传错误".format(goods_no, key))
+                    return False
+
+        print("---{}  {} 完成".format(goods_no, time.time() - s))
+        return True
+
+    def to_upload_pic(self, file_path, is_resize=True):
+        file_name = os.path.split(file_path)[1]
+        e = os.path.splitext(file_name)[1][1:]
+
+        im = Picture(file_path)
+        if im.x > 1200:
+            im.resize(width=1200)
+
+        # print(file_name, e)
+        # if e == "png":
+        #     im.im.show()
+        # return
+        _ = {"jpg": "JPEG",
+             "JPG": "JPEG",
+             "JPEG": "JPEG",
+             "jpeg": "JPEG",
+             "png": "PNG",
+             "PNG": "PNG", }
+        e = _[e]
+        image_io = im.save_to_io(e)
+
+        # if is_resize:
+        #     im = Picture(file_path)
+        #     if im.x > 1200:
+        #         im.resize(width=1200)
+        #     image_io = im.save_to_io()
+        #     e = "JPEG"
+        # else:
+        #     file_name = os.path.split(file_path)[1]
+        #     e = os.path.splitext(file_name)[0][1:]
+        #     image_io = open(file_path, 'rb')
+
+        # image_io = open(file_path, 'rb')
+
+        # self.lock.acquire()
+        # self.pic_data[file_path]["url"] = "1111"
+        # self.lock.release()
+
+        # return
+        goods_data = {"file_path": os.path.split(file_path)[1],
+                      "image_io": image_io,
+                      "e": e
+                      }
+
+        url = self.data_mode_upload_pic.upload_pic(goods_data=goods_data)
+        self.lock.acquire()
+        self.pic_data[file_path]["url"] = url
+        self.lock.release()
+
+    def is_goods_art_dir(self, file, goods_no):
+        if goods_no in file:
+            return True
+        # 判断是不是货号文件夹
+        _r_text = re.findall("[a-zA-Z0-9]+", file)
+        if _r_text:
+            # print(_r_text)
+            if _r_text[0] != file:
+                # 文件夹中包含有非字母或数字判断不是货号
+                return False
+            else:
+                return True
+        else:
+            # 非字母和数字 判断为非货号
+            return False
+
+    def show_info(self, text: str):
+        # self.show_progress_detail_sign.emit(text)
+        # self.windows.show_progress_detail(text)
+        print("upload_pic=====>", text)
+
+    def show_text_browser(self, text):
+        pass
+
+    def state_change(self, to_state: int):
+        self.state = to_state
+
+
+class Picture:
+
+    def __init__(self, in_path):
+        self.im = Image.open(in_path)
+        self.x, self.y = self.im.size
+        # print(self.x, self.y)
+
+    def resize(self, width):
+        re_x = int(width)
+        re_y = int(self.y * re_x / self.x)
+        self.im = self.im.resize((re_x, re_y), Image.Resampling.LANCZOS)
+        self.x, self.y = self.im.size
+
+    def save_to_io(self, format):
+        img = BytesIO()
+        self.im.save(img, format=format)  # format: PNG or JPEG
+        img.seek(0)  # rewind to the start
+        return img

+ 8 - 8
python/service/base.py

@@ -1,7 +1,7 @@
 import os
 import copy
 import configparser
-from module.base_mode.module_aes import Aes
+# from module.base_mode.module_aes import Aes
 import exifread
 from natsort import ns, natsorted
 import shutil
@@ -327,13 +327,13 @@ def get_dict_value(_dict, key, default=None):
         return default
 
 
-def set_key(authorization, SecretKey, SecretIv):
-    # --------------------用户身份--------------
-    if authorization:
-        authorization = Aes().get_key(authorization, SecretKey, SecretIv)
-    with open("key", "w") as f:
-        f.write(authorization)
-    return authorization
+# def set_key(authorization, SecretKey, SecretIv):
+#     # --------------------用户身份--------------
+#     if authorization:
+#         authorization = Aes().get_key(authorization, SecretKey, SecretIv)
+#     with open("key", "w") as f:
+#         f.write(authorization)
+#     return authorization
 
 
 def print_dic(_dict):

+ 33 - 33
python/service/base_deal.py

@@ -1,18 +1,18 @@
 import json
-# from module_generate_goods_art_no_table import GenerateGoodsArtNoTable
-
-from grenerate_main_image_test import GeneratePic
+from .module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+from func_timeout import FunctionTimedOut
+from .grenerate_main_image_test import GeneratePic
 from threading import Lock
 
 import settings
 from collections import defaultdict
-from remove_bg_ali import RemoveBgALi, Picture
-# from deal_cutout import DealCutout
+from .remove_bg_ali import RemoveBgALi, Picture
+from .deal_cutout import DealCutout
 
 
 # from data import DataModeAutoDealPics
 import time
-from image_pic_deal import OnePicDeal
+from .image_pic_deal import OnePicDeal
 
 from natsort import natsorted,ns
 import os
@@ -33,10 +33,11 @@ _Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF', ".jpge", ".JPGE"]
 
 
 class BaseDealImage(object):
-    def __init__(self, image_dir=None):
+    def __init__(self, image_dir=None,token=None):
+        self.token = token
         self.goods_images_count_dict = defaultdict(int)
         # 数据模型
-        self.data_mode_auto_deal_pics = DataModeAutoDealPics()
+        # self.data_mode_auto_deal_pics = DataModeAutoDealPics()
         self.image_dir = image_dir
         pass
 
@@ -133,6 +134,7 @@ class BaseDealImage(object):
         return result
 
     def check_folders_image_amount(self, all_goods_art_no_folder_data, image_order_list):
+        print("*****************check_folders_image_amount************************")
         amount = len(image_order_list)
         message = ""
         for goods_art_no_folder_data in all_goods_art_no_folder_data:
@@ -155,6 +157,7 @@ class BaseDealImage(object):
         return all_goods_art_no_folder_data, message
 
     def check_one_folder_image_amount(self, folder_data, amount: int):
+        print("*****************check_one_folder_image_amount************************")
         # 计算单个文件夹原图数量
         images = [x for x in self.list_dir("{}/原始图".format(folder_data["folder_path"]))]
         image_num = 0
@@ -255,7 +258,10 @@ class BaseDealImage(object):
             shutil.rmtree('{}/阴影图处理'.format(folder_path))
 
         self.crate_all_folders(folder_path)
-
+        print(
+            "***************all_original_images*********************",
+            all_original_images,
+        )
         for image_dict in all_original_images:
             if windows:
                 if windows.state != 1:
@@ -271,16 +277,25 @@ class BaseDealImage(object):
             # 检查是否存在已抠图文件,如没有再去抠图
 
             original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(folder_path, image_dict["file_name"], ".png")
-
+            print(
+                f"*****************此处判断鞋子是否为左右脚====>{image_index}<======={is_image_deal_mode}=======>**********************"
+            )
             # 此处判断鞋子是否为左右脚
             if image_index == 1:
                 is_image_deal_mode = 0
-                if OnePicDeal().check_shoe_is_right(image_path=original_move_bg_image_path):
+                print("开始识别左右脚=========>")
+                if OnePicDeal(self.token).check_shoe_is_right(
+                    image_path=original_move_bg_image_path
+                ):
                     is_image_deal_mode = 1  # 1表示要镜像,0表示不做镜像
-
+            print(
+                "*************************进行800image 生成********************************************"
+            )
             """进行800image 生成"""
             generate_pic = GeneratePic()
-
+            print(
+                "*************************进行800image 结束====>>>>********************************************"
+            )
             if settings.OUT_PIC_MODE == ".jpg":
                 out_path = "{}/800x800/{}{}".format(folder_path, file_name, ".jpg")
             else:
@@ -305,13 +320,14 @@ class BaseDealImage(object):
 
             if resize_mode == 2:
                 print(
+                    "参数打印================》",
                     is_image_deal_mode,
                     resize_mode,
                     settings.OUT_PIC_SIZE,
                     True if i_n == 1 else False,
                     max_box
                 )
-
+            print("**********123456********************")
             if not generate_pic.run(image_path=original_image_path,
                                     cut_image_path=original_move_bg_image_path,
                                     out_path=out_path,
@@ -324,6 +340,7 @@ class BaseDealImage(object):
                                     max_box=max_box,
                                     logo_path=logo_path,
                                     ):
+                print("**********222222222222222222222222222********************")
                 is_successful = False
         if is_successful:
             return True
@@ -826,8 +843,6 @@ class BaseDealImage(object):
         else:
             return None
 
-
-
     def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None, headers=None):
         # 获取商品基础信息,入参为商品的编号
         url = "{domain}/api/backend/goods_client/goods_query".format(
@@ -840,7 +855,6 @@ class BaseDealImage(object):
         _s = requests.session().post(url=url, data=data, headers=headers)
         response_data = _s.json()
 
-
         goods_number_data = {}
         # ["", "", "", "", "", "", "", "", "", "", "", ]
         if "data" not in response_data:
@@ -880,9 +894,6 @@ class BaseDealImage(object):
                 _list = []
         return goods_art_no_dict
 
-
-
-
     def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None, headers=None):
         # 获取商品基础信息,入参为商品的编号
         url = "{domain}/api/backend/goods_client/goods_query".format(
@@ -892,15 +903,12 @@ class BaseDealImage(object):
             'goods_art_list': goods_art_list
         }
 
-
         data = json.dumps(data)
 
-
         _s = requests.session().post(url=url, data=data, headers=headers)
         # _s = self.s.get(url=url, params=params, headers=settings.Headers)
         response_data = _s.json()
 
-
         goods_number_data = {}
         # ["", "", "", "", "", "", "", "", "", "", "", ]
         if "data" not in response_data:
@@ -918,9 +926,6 @@ class BaseDealImage(object):
 
         return goods_number_data
 
-
-
-
     def get_data_from_hqt(self, goods_number_list):
         _goods_number_list = copy.deepcopy(goods_number_list)
         _list = []
@@ -945,10 +950,6 @@ class BaseDealImage(object):
                 _list = []
         return goods_number_dict
 
-
-
-
-
     def dealMoveImage(self, image_dir: str, callback_func=None) -> dict:
         if not self.check_path(image_dir=image_dir + "/历史"):
             return {'code': 1, 'msg': '文件夹创建失败', 'data': {}}
@@ -1055,7 +1056,6 @@ class BaseDealImage(object):
             self.move_images(goods_art_no, goods_art_no_path, original_image_path)  # 货号、货号文件路径、原始图路径
             time.sleep(0.2)
 
-
             # self.progress_sign.emit({"type": "移动原始图片", "progress_bar_value": int(n / total_num * 100)})
             # self.show_progress_detail("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
             if callback_func:
@@ -1129,9 +1129,9 @@ class BaseDealImage(object):
     def pixianRemoveImageBg(self, file_path: str, out_file_path: str, callbackek_func=None):
         url = self.dataModeMatchPhoto.get_online_data.uploadImage(local_path=file_path)
 
-        remonveUrl = settings.AIGC_DOMAIN + '/api/ai_image/main/remove_background'
+        remonveUrl = settings.DOMAIN + '/api/ai_image/main/remove_background'
         param = {'base_image': url}
-        post_headers = {"Authorization": settings.Authorization,
+        post_headers = {"Authorization": self.token,
                         "Content-Length": "",
                         "Content-Type": "application/json",
                         "Accept": "application/json"}

+ 420 - 4
python/service/data.py

@@ -1,12 +1,15 @@
 import settings
-from module.data_mode.data_metaclass import DataBaseModel
+from .data_metaclass import DataBaseModel
 from PIL import Image
 from io import BytesIO
-
+import copy
+import pandas as pd
+import settings
+import numpy as np
 
 class DataModeAutoDealPics(DataBaseModel):
-    def __init__(self):
-        super().__init__()
+    def __init__(self,token):
+        super().__init__(token)
 
     def check_is_right_foot_by_api(self, image):
         image = image.convert('RGB')
@@ -26,3 +29,416 @@ class DataModeAutoDealPics(DataBaseModel):
         # 识别左右脚
         r_data = self.get_online_data.yolo_shoes_category(image_url=image_url)
         return r_data
+
+
+class DataModeGenerateDetail(DataBaseModel):
+
+    def __init__(self, token):
+        super().__init__(token)
+        self.token = token
+        self.get_online_data.refresh_headers(token)
+
+    def get_real_color_name(self, color_name: str):
+        _ = ["薄棉", "加厚", "厚棉"]
+        for i in _:
+            color_name = color_name.replace(i, "")
+        return color_name
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        if goods_art_list:
+            data_list = copy.deepcopy(goods_art_list)
+            flag = "Goods_Art"
+        else:
+            data_list = copy.deepcopy(numbers_list)
+            flag = "NUM"
+        _list = []
+        # 单次请求数少于20个
+        goods_dict = {}
+        while data_list:
+            one_data = data_list.pop()
+            _list.append(one_data)
+
+            if len(_list) == 20 or len(data_list) == 0:
+                if flag == "NUM":
+                    # 注意,红蜻蜓和惠利玛函数不同
+                    online_goods_art_data = self.get_online_data.get_goods_art_no_info(
+                        numbers_list=_list,
+                        token=self.token
+                    )
+                else:
+                    online_goods_art_data = self.get_online_data.get_goods_art_no_info(
+                        goods_art_list=_list, token=self.token
+                    )
+                if online_goods_art_data:
+                    for key in online_goods_art_data:
+                        # 映射为真实的文件夹名称
+                        goods_dict[key] = online_goods_art_data[key]
+                _list = []
+        return goods_dict
+
+    def get_basic_goods_art_data_by_hqt_and_hlm(self, folder_name_list):
+        """
+        查询红蜻蜓货号的对应数据,输入写入为编码,可能带有@,可能为货号等 NUN为编码
+        """
+        original_folder_name_list = copy.deepcopy(folder_name_list)
+
+        # 编号与文件名映射表
+        return_dict = {}
+        for folder_name in original_folder_name_list:
+            folder_name: str
+            if "NUM" not in folder_name:
+                # 即为货号
+                return_dict[folder_name] = {
+                    "type": "goods_art_no",
+                    "name": folder_name.upper(),
+                    "文件夹名称": folder_name,
+                    "data": {},
+                }
+            else:
+                if "@" not in folder_name:
+                    return_dict[folder_name] = {
+                        "type": "goods_number",
+                        "name": folder_name.upper().replace("NUM", ""),
+                        "文件夹名称": folder_name,
+                        "data": {},
+                    }
+                else:
+                    return_dict[folder_name] = {
+                        "type": "goods_number",
+                        "name": folder_name.upper().replace("NUM", "").split("@")[1],
+                        "文件夹名称": folder_name,
+                        "data": {},
+                    }
+
+        # -----------请求货号数据处理------------
+        goods_art_no_list = [
+            v["name"] for k, v in return_dict.items() if v["type"] == "goods_art_no"
+        ]
+        goods_art_no_all_data = self.get_goods_art_no_info(
+            goods_art_list=goods_art_no_list
+        )
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", goods_art_no_all_data)
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        print("goods_art_no_all_data===========>>>>>", "")
+        for folder_name, value in return_dict.items():
+            if value["type"] == "goods_art_no":
+                if value["name"] in goods_art_no_all_data:
+                    return_dict[folder_name]["data"] = goods_art_no_all_data[
+                        value["name"]
+                    ]
+
+        # ------------请求编码数据----------------------
+        goods_number_list = [
+            v["name"] for k, v in return_dict.items() if v["type"] == "goods_number"
+        ]
+        goods_number_all_data = self.get_goods_art_no_info(
+            numbers_list=goods_number_list
+        )
+        for folder_name, value in return_dict.items():
+            if value["type"] == "goods_number":
+                if value["name"] in goods_number_all_data:
+                    return_dict[folder_name]["data"] = goods_number_all_data[
+                        value["name"]
+                    ]
+
+        # 清空没有值的数据
+        error_key = []
+        for folder_name, value in return_dict.items():
+            if not value["data"]:
+                error_key.append(folder_name)
+
+        if error_key:
+            for folder_name in error_key:
+                return_dict.pop(folder_name)
+
+        return {"code": 0, "message": "", "data": return_dict}
+
+    def get_basic_goods_art_data_form_excel(self, folder_name_list, excel_path, keys):
+
+        # =====创建虚拟表格并进行连表处理
+        need_df = pd.DataFrame(columns=["文件夹名称"])
+        for folder_name in folder_name_list:
+            new_row = {
+                "文件夹名称": str(folder_name),
+            }
+            need_df = need_df._append(new_row, ignore_index=True)
+
+        need_df = need_df.fillna(value="")
+
+        # 打开表格并进行匹配
+        _df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        # 去重数据
+        duplicates = _df.duplicated(subset=["文件夹名称"], keep="first")
+        _df = _df.loc[~duplicates]
+        _df = _df.fillna(value="")
+        _df = _df.astype(str)
+
+        # 数据匹配关联,左关联
+        need_df = pd.merge(
+            need_df,
+            _df,
+            on=["文件夹名称"],
+            how="left",
+            indicator=False,
+        )
+        # 补全字段
+        header_list = need_df.columns.values.tolist()
+        for key in keys:
+            if key not in header_list:
+                need_df[key] = ""
+
+        need_df = need_df.fillna(value="")
+        need_df = need_df.astype(str)
+        # 数据转字典
+        return_dict = {}
+        message = ""
+        print("need_df.iterrows()   <=============>      ", need_df.to_json())
+        for index, row in need_df.iterrows():
+            if settings.PROJECT == "红蜻蜓":
+                if row["商品货号"] and row["款号"] and row["编号"]:
+                    return_dict[row["文件夹名称"]] = {
+                        "type": "goods_art_no",
+                        "name": row["文件夹名称"].upper(),
+                        "文件夹名称": row["文件夹名称"],
+                        "template_name": row["模板名称"],
+                        "data": row.to_dict(),
+                    }
+                else:
+                    message = "商品货号、款号、编号为空或列不存在"
+            else:
+                if row["商品货号"] and row["款号"] and row["模板名称"]:
+                    return_dict[row["文件夹名称"]] = {
+                        "type": "goods_art_no",
+                        "name": row["文件夹名称"].upper(),
+                        "文件夹名称": row["文件夹名称"],
+                        "template_name": row["模板名称"],
+                        "data": row.to_dict(),
+                    }
+                else:
+                    message = "商品货号、款号、模板名称为空或列不存在"
+
+        print("return_dict", return_dict)
+
+        if not return_dict:
+            message += "\n没有找到任何匹配的数据"
+            return {"code": 99, "message": message, "data": return_dict}
+        else:
+            return {"code": 0, "message": message, "data": return_dict}
+
+    def check_goods_is_not_deficiency(self, goods_no_dict: dict):
+        # 数据返回结果为款号列表
+        error_data = {}
+        for key, value in goods_no_dict.items():
+            number = value["货号资料"][0]["编号"].replace("NUM", "")
+            number_list = [x["编号"].replace("NUM", "") for x in value["货号资料"]]
+            all_color_name = [
+                self.get_real_color_name(x["颜色名称"]) for x in value["货号资料"]
+            ]  # 本地数据
+            goods_no = key
+
+            _t_goods_number, all_goods_number = (
+                self.get_online_data.get_on_goods_all_art(number=number)
+            )
+            # all_goods_number------》 [{'number': '14250232', 'goods_art_no': 'AC52001173', 'color': '杏色'}]
+
+            for i in all_goods_number:
+                if i["number"] not in number_list:
+                    # 检查款号+颜色名称是否已经存在
+                    real_color = self.get_real_color_name(i["color"])
+                    if real_color not in all_color_name:
+                        if goods_no not in error_data:
+                            error_data[goods_no] = {"message": "颜色:"}
+                        error_data[goods_no]["message"] += "{}缺失 ".format(real_color)
+
+        return error_data
+
+    def get_basic_template_information(self, _goods_no_dict, excel_path: str):
+        # 本地的文件夹数据形成一个虚拟表格
+        original_df = pd.DataFrame(columns=["款号"])
+        for goods_no in _goods_no_dict:
+            new_row = {"款号": str(goods_no), "flag": "是"}
+            original_df = original_df._append(new_row, ignore_index=True)
+        original_df = original_df.fillna(value="")
+
+        # EXCEL表格数据
+        local_df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        values = {
+            "模板名称": "任意",
+        }
+        # 确保所有模板都有名称
+        local_df.fillna(value=values, inplace=True)
+        # 分组后取第一个有效值(即不是NaN的值);此处用于获取某个款号的所有基础信息
+        group_local_df = local_df.groupby(["模板名称", "款号"]).agg(
+            lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan
+        )
+        group_local_df = group_local_df.reset_index()
+
+        # -----------数据匹配关联,左关联
+        group_local_df = pd.merge(
+            group_local_df,
+            original_df,
+            on=["款号"],
+            how="left",
+            indicator=False,
+        )
+        # -----------只取有标记的数据
+        group_local_df = group_local_df.loc[~group_local_df["flag"].isnull(), :]
+        group_local_df = group_local_df.loc[group_local_df["模板名称"] != "任意", :]
+        # -----------数据规整
+        group_local_df = group_local_df.fillna(value="")
+        group_local_df = group_local_df.astype(str)
+
+        # 组装成字典
+        temp_goods_no_dict = {}
+        for index, row in group_local_df.iterrows():
+            goods_no = row["款号"]
+            if goods_no not in temp_goods_no_dict:
+                temp_goods_no_dict[goods_no] = {}
+            temp_goods_no_dict[goods_no][row["模板名称"]] = row.to_dict()
+
+        return temp_goods_no_dict
+
+    def check_goods_is_not_deficiency_form_excel(
+        self, goods_no_dict: dict, excel_path: str
+    ):
+        # 检查颜色是否齐全,基于Excel处理
+        """
+        步骤:
+        1、goods_no_dict款号生成一个表格(款号+颜色)
+        2、excel表格数据进行款号+颜色去重
+        3、excel表格关联 上述的款号 进行标记,其他删除
+        4、excel表格关联 上述款号与颜色,进行标记
+        5、上述未标记的就是没有的颜色(有对应款号)。
+        6、对对应的款号进行设置为错误数据
+        返回数据结构
+
+        {
+        “A00001--款号”:{“message”:“这个款的错误原因”},
+        “A00002--款号”:{“message”:“这个款的错误原因”},
+        }
+
+        """
+
+        def change_to_int(x):
+            if pd.isna(x):
+                return 0
+            else:
+                try:
+                    x = int(x)
+                    return str(x)
+                except:
+                    return x
+
+        error_data = {}
+        # -----------------goods_no_dict款号生成一个表格(款号+颜色)
+        new_df = pd.DataFrame(columns=["款号", "颜色名称", "款色标记"])
+        for key, value in goods_no_dict.items():
+            goods_no = value["款号"]
+            for color_name in [x["颜色名称"] for x in value["货号资料"]]:
+                new_row = {
+                    "款号": goods_no,
+                    "颜色名称": self.get_real_color_name(color_name),
+                    "款色标记": 1,
+                }
+
+                new_df = new_df._append(new_row, ignore_index=True)
+        new_df.drop_duplicates(subset=["款号", "颜色名称"], keep="first", inplace=True)
+
+        # -------------参考的本地excel表格数据进行款号+颜色去重------------
+        local_df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        local_df.fillna(
+            value={
+                "颜色名称": "",
+            },
+            inplace=True,
+        )
+        local_df = local_df.astype({"颜色名称": str})
+
+        header_list = local_df.columns.values.tolist()
+
+        for key in ["帮高", "鞋宽", "跟高"]:
+            if key in header_list:
+                local_df[key] = local_df[key].apply(change_to_int)
+
+        #     颜色名称规整
+        local_df["颜色名称"] = local_df["颜色名称"].apply(self.get_real_color_name)
+        local_df.drop_duplicates(
+            subset=["款号", "颜色名称"], keep="first", inplace=True
+        )
+
+        # ------------excel表格关联 上述的款号 进行标记,其他删除
+        _df = new_df.drop_duplicates(subset=["款号"], keep="first", inplace=False)
+        _df = _df[["款号"]]
+        _df["对应款号"] = 1
+        local_df = pd.merge(
+            local_df,
+            _df,
+            on=["款号"],
+            how="left",
+            indicator=False,
+        )
+        local_df = local_df.loc[local_df["对应款号"] == 1, :]
+
+        # ------------excel表格关联 上述款号与颜色,进行标记;取没有关联的颜色数据---------
+        local_df = pd.merge(
+            local_df,
+            new_df,
+            on=["款号", "颜色名称"],
+            how="left",
+            indicator=False,
+        )
+        local_df.fillna(
+            value={
+                "款色标记": 0,
+            },
+            inplace=True,
+        )
+        local_df = local_df.loc[local_df["款色标记"] == 0, :]
+        print("local_df")
+        print(local_df)
+
+        # ------------数据分组,对对应的款号进行设置为错误数据---------------
+        grouped_df = (
+            local_df.groupby("款号")["颜色名称"]
+            .apply(lambda x: "、".join(x))
+            .reset_index()
+        )
+        for index, row in grouped_df.iterrows():
+            error_data[row["款号"]] = {
+                "message": "颜色:{}缺失".format(row["颜色名称"])
+            }
+
+        print("======error_data")
+        print(json.dumps(error_data))
+
+        return error_data
+
+
+class DataModeUploadPic(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+        self.get_online_data.refresh_headers()
+        self.is_deal_goods_no = []
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        return self.get_online_data.get_goods_art_no_info(
+            numbers_list=numbers_list, goods_art_list=goods_art_list
+        )
+
+    def upload_pic_list_data(self, data):
+        return self.get_online_data.upload_pic_list_data(data)
+
+    def upload_pic(self, goods_data):
+        return self.get_online_data.upload_pic(goods_data)
+
+    def check_is_uploaded(self, goods_art_no=None):
+        return self.get_online_data.check_detail_image(goods_art_no)

+ 296 - 0
python/service/data_metaclass.py

@@ -0,0 +1,296 @@
+from logger import logger
+from databases import DeviceConfig, PhotoRecord, SqlQuery
+from sqlalchemy.orm import sessionmaker, scoped_session
+import settings
+from .online_request.module_online_data import OnlineDataRequest, GetOnlineDataHLM
+
+import time
+from datetime import datetime, timedelta
+from sqlalchemy.sql import func
+from sqlalchemy.orm import aliased
+import copy
+import threading
+
+
+class DataBaseModel():
+    # 基础数据基类
+    # signal_data = Signal(dict)
+    # add_data_to_online_sign = Signal(dict)
+
+    def __init__(self, token):
+        # super().__init__(parent=None)
+        self.logger = logger
+        self.token = token
+        # 创建一个实例化的会话对象 session;该会话会自己处理上下文,线程结束时会自动关闭会话;适合多线程处理
+        self.session = SqlQuery()
+
+        if settings.PROJECT == "红蜻蜓":
+            self.get_online_data = OnlineDataRequest(self.token)
+        elif settings.PROJECT == "惠利玛":
+            self.get_online_data = GetOnlineDataHLM(self.token)
+
+        # self.add_data_to_online_sign.connect(self.send_record_to_online)
+
+    # 新增数据的记录发送到云端
+    def send_record_to_online(self, data):
+        # _data = {"goods_art_no":goods_art_no,
+        #          "image_index": image_index,
+        #          "photo_create_time": photo_create_time,
+        #          "image_deal_mode": image_deal_mode,
+        #          "take_photo_created_at": date_time.strftime('%Y-%m-%d %H:%M:%S'),
+        #          }
+        data["photo_create_time"] = "{}".format(data["photo_create_time"])
+        data["take_photo_created_at"] = "{}".format(data["take_photo_created_at"])
+        self.get_online_data.add_auto_photo_logs(data)
+        pass
+
+    def send_info(self, _type="", mode="", data=None):
+        self.signal_data.emit({"_type": _type, "plugins_mode": mode, "data": data})
+
+    def send_log(self, text, is_debug=False):
+        if is_debug:
+            self.logger.debug(text)
+        else:
+            self.logger.info(text)
+
+    def insert_data_to_phone_record(self, data: dict):
+        # 异步提交数据到后台
+        photo_create_time = data["photo_create_time"]
+        try:
+            photo_create_time = time.strptime(photo_create_time, "%Y-%m-%d %H:%M:%S")
+        except:
+            photo_create_time = time.strptime(photo_create_time, "%Y:%m:%d %H:%M:%S")
+
+        photo_create_time = time.mktime(photo_create_time)
+        photo_create_time = datetime.fromtimestamp(photo_create_time)
+
+        image_deal_mode = data["image_deal_mode"] if "image_deal_mode" in data else 0
+        image_index = data["image_index"] if "image_index" in data else 0
+        goods_art_no = data["goods_art_no"]
+        d_1 = PhotoRecord(
+            goods_art_no=goods_art_no,
+            image_index=image_index,
+            photo_create_time=photo_create_time,
+            image_deal_mode=image_deal_mode,
+        )
+
+        self.session.add(d_1)
+        self.session.commit()
+        date_time = datetime.fromtimestamp(time.time())
+        _data = {"goods_art_no": goods_art_no,
+                 "image_index": image_index,
+                 "photo_create_time": photo_create_time,
+                 "image_deal_mode": image_deal_mode,
+                 "take_photo_created_at": date_time.strftime('%Y-%m-%d %H:%M:%S'),
+                 }
+        # self.add_data_to_online_sign.emit(_data)
+
+    def get_goods_art_no(self, date_time_original):
+        time_array = time.strptime(date_time_original, "%Y:%m:%d %H:%M:%S")
+        time_array = time.mktime(time_array)
+        datetime_obj = datetime.fromtimestamp(time_array)
+        result = (
+            self.session.query(PhotoRecord)
+            .filter(PhotoRecord.photo_create_time == datetime_obj)
+            .order_by(PhotoRecord.id.desc())
+            .scalar()
+        )
+        if result:
+            return result.goods_art_no, result.image_index, result.image_deal_mode
+        else:
+            return None
+
+    def get_data_from_hqt_with_goods_art_no(self, goods_art_no_list):
+        _goods_art_no_list = copy.deepcopy(goods_art_no_list)
+        _list = []
+        # 单次请求数少于20个
+        goods_art_no_dict = {}
+
+        while _goods_art_no_list:
+            goods_art_no = _goods_art_no_list.pop()
+
+            _list.append(goods_art_no)
+            if len(_list) == 20 or len(_goods_art_no_list) == 0:
+                online_goods_art_data = self.get_online_data.get_goods_art_no_info(
+                    goods_art_list=_list
+                )
+                if online_goods_art_data:
+                    for _goods_art_no in online_goods_art_data:
+                        goods_art_no_dict[_goods_art_no] = online_goods_art_data[
+                            _goods_art_no
+                        ]
+                _list = []
+        return goods_art_no_dict
+
+    def get_data_from_hqt(self, goods_number_list):
+        _goods_number_list = copy.deepcopy(goods_number_list)
+        _list = []
+        # 单次请求数少于20个
+        goods_number_dict = {}
+
+        while _goods_number_list:
+            goods_art_no = _goods_number_list.pop()
+            if "NUM" in goods_art_no:
+                goods_art_no = goods_art_no.replace("NUM", "")
+
+            _list.append(goods_art_no)
+            if len(_list) == 20 or len(_goods_number_list) == 0:
+                online_goods_art_data = self.get_online_data.get_goods_art_no_info(
+                    numbers_list=_list
+                )
+                if online_goods_art_data:
+                    for number in online_goods_art_data:
+                        goods_number_dict["NUM" + number] = online_goods_art_data[
+                            number
+                        ]
+                _list = []
+        return goods_number_dict
+
+    ##=====设备配置处理=======
+    def device_config_delete(self, config_id):
+        d_c_1 = self.session.query(DeviceConfig).get(config_id)
+        self.session.delete(d_c_1)
+        self.session.commit()
+
+    def device_config_delete_by_mode_type(self, mode_type: str):
+        all_data = (
+            self.session.query(DeviceConfig)
+            .filter(DeviceConfig.mode_type == mode_type)
+            .all()
+        )
+        [self.session.delete(u) for u in all_data]
+        self.session.commit()
+
+    def device_config_copy_by_id(self, config_id):
+        d_c_1 = self.session.query(DeviceConfig).get(config_id)
+
+        d_c_2 = DeviceConfig(
+            mode_type=d_c_1.mode_type,
+            execution_type=d_c_1.execution_type,
+            action_name=d_c_1.action_name + "_复制",
+            action_index=d_c_1.action_index + 1,
+            picture_index=d_c_1.picture_index,
+            camera_height=d_c_1.camera_height,
+            camera_angle=d_c_1.camera_angle,
+            number_focus=d_c_1.number_focus,
+            take_picture=d_c_1.take_picture,
+            turntable_position=d_c_1.turntable_position,
+            turntable_angle=d_c_1.turntable_angle,
+            shoe_upturn=d_c_1.shoe_upturn,
+            pre_delay=d_c_1.pre_delay,
+            after_delay=d_c_1.after_delay,
+            led_switch=d_c_1.led_switch,
+            is_wait=d_c_1.is_wait,
+        )
+        # 复制一个后,其他的数据的排序进行后移
+        other_d_c = self.session.query(DeviceConfig).filter(
+            DeviceConfig.mode_type == d_c_1.mode_type,
+            DeviceConfig.action_index >= d_c_1.action_index + 1,
+            DeviceConfig.delete_time.is_(None),
+        )
+        other_d_c.update(
+            {
+                DeviceConfig.action_index: DeviceConfig.action_index + 1,
+            }
+        )
+        self.session.add(d_c_2)
+
+        self.session.commit()
+
+    def device_config_add(self, data: dict):
+        f_data = (
+            self.session.query(
+                func.max(DeviceConfig.action_index).label("max_action_index")
+            )
+            .filter(
+                DeviceConfig.mode_type == data["mode_type"],
+                DeviceConfig.delete_time.is_(None),
+            )
+            .scalar()
+        )
+
+        if not f_data:
+            data["action_index"] = 10
+        else:
+            data["action_index"] = f_data + 10
+
+        d_c_1 = DeviceConfig(**data)
+        self.session.add(d_c_1)
+        self.session.commit()
+
+    def device_config_change_action_index(self, up_config_id_1, down_config_id_2):
+        d_c_1 = (
+            self.session.query(DeviceConfig.action_index)
+            .filter(DeviceConfig.id == up_config_id_1)
+            .scalar()
+        )
+        d_c_2 = (
+            self.session.query(DeviceConfig.action_index)
+            .filter(DeviceConfig.id == down_config_id_2)
+            .scalar()
+        )
+
+        if d_c_1 == d_c_2:
+            d_c = self.session.query(DeviceConfig).get(up_config_id_1)
+            d_c.update(
+                {
+                    DeviceConfig.action_index: DeviceConfig.action_index - 1,
+                }
+            )
+        else:
+            d_c_update_1 = self.session.query(DeviceConfig).filter(
+                DeviceConfig.id == up_config_id_1
+            )
+            d_c_update_2 = self.session.query(DeviceConfig).filter(
+                DeviceConfig.id == down_config_id_2
+            )
+            d_c_update_1.update(
+                {
+                    DeviceConfig.action_index: d_c_2,
+                }
+            )
+            d_c_update_2.update(
+                {
+                    DeviceConfig.action_index: d_c_1,
+                }
+            )
+        self.session.commit()
+        pass
+
+    def device_config_update(self, data: dict):
+        d_c = self.session.query(DeviceConfig).filter(DeviceConfig.id == data["id"])
+        data.pop("id")
+        d_c.update(data)
+        self.session.commit()
+
+    def device_config_config_list(self, mode_type, action_name=None):
+        _q = self.session.query(DeviceConfig).filter(
+            DeviceConfig.mode_type == mode_type, DeviceConfig.delete_time.is_(None)
+        )
+        if action_name:
+            _q = _q.filter(DeviceConfig.action_name == action_name)
+        results = _q.order_by(DeviceConfig.action_index.asc()).all()
+        return [self.to_dict(row) for row in results]
+
+    def to_dict(self, row):
+        return {c.name: getattr(row, c.name, None) for c in row.__table__.columns}
+
+    def device_config_detail(self, config_id):
+        results = (
+            self.session.query(DeviceConfig)
+            .filter(DeviceConfig.id == config_id, DeviceConfig.delete_time.is_(None))
+            .one()
+        )
+        return self.to_dict(results)
+
+    def delete3MonthAgoRecord(self):
+        """删除3个月以前得数据"""
+        print("d_c_1")
+        # 计算3个月前的日期
+        three_months_ago = datetime.now() - timedelta(days=90)
+        d_c_1 = (
+            self.session.query(PhotoRecord)
+            .filter(PhotoRecord.create_time < three_months_ago)
+            .delete()
+        )
+        self.session.commit()

+ 6 - 6
python/service/deal_cutout.py

@@ -3,13 +3,13 @@ import time
 from concurrent.futures import ThreadPoolExecutor, wait
 import threading
 
-from deal_one_image import DealOneImage, DealOneImageBeforehand
-from module.log.log import MyLogger
-from module.online_request.module_online_data import GetOnlineDataHLM
+from .deal_one_image import DealOneImage, DealOneImageBeforehand
+from logger import logger
+from .online_request.module_online_data import GetOnlineDataHLM
 
 
-class DealCutout(QThread):
-    signal_data = Signal(dict)
+class DealCutout():
+    # signal_data = Signal(dict)
 
     def __init__(self, windows):
         super().__init__()
@@ -22,7 +22,7 @@ class DealCutout(QThread):
         self.is_deal_num = 0
         # 图片列表
         self.upload_pic_dict = {}
-        self.logger = MyLogger().logger
+        self.logger = logger
 
         self.remaining_times = 0  # 剩余总次数
         self.get_online_data = GetOnlineDataHLM()

+ 51 - 56
python/service/deal_image.py

@@ -1,4 +1,3 @@
-
 import os
 
 from  natsort import natsorted,ns
@@ -6,7 +5,7 @@ import shutil
 import exifread
 import time
 import datetime
-from databases import DeviceConfig,SqlQuery,CRUD
+from databases import PhotoRecord,SqlQuery,CRUD
 import settings
 import copy
 import json
@@ -14,15 +13,15 @@ import requests
 from service.pic_deal import Picture
 import xlsxwriter
 from PIL import Image
-
+from .base_deal import BaseDealImage
+from middleware import UnicornException
 _Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF', ".jpge", ".JPGE"]
 
-class DealImage():
-
-
 
+class DealImage(BaseDealImage):
 
     def __init__(self, image_dir=None):
+        super().__init__()
         self.image_dir = image_dir
         self.header = None
     def check_path(self, image_dir: str):
@@ -41,24 +40,25 @@ class DealImage():
             else:
                 return False
 
-    def get_goods_art_no(self, date_time_original):
-        time_array = time.strptime(date_time_original, "%Y:%m:%d %H:%M:%S")
+    def get_goods_art_no(self, goods_art_no):
+        # time_array = time.strptime(date_time_original, "%Y:%m:%d %H:%M:%S")
 
-        time_array = time.mktime(time_array)
-        datetime_obj = datetime.fromtimestamp(time_array)
+        # time_array = time.mktime(time_array)
+        # datetime_obj = datetime.datetime.fromtimestamp(time_array)
 
         session = SqlQuery()
-        configModel = CRUD(DeviceConfig)
+        configModel = CRUD(PhotoRecord)
         result = configModel.read(
-            session, conditions={"photo_create_time": datetime_obj}, order_by="id", ascending=True
+            session,
+            conditions={"goods_art_no": goods_art_no},
+            order_by="id",
+            ascending=True,
         )
         if result:
             return result.goods_art_no, result.image_index, result.image_deal_mode
         else:
             return None
 
-
-
     def get_data_from_hqt_with_goods_art_no(self, goods_art_no_list):
         _goods_art_no_list = copy.deepcopy(goods_art_no_list)
         _list = []
@@ -84,7 +84,7 @@ class DealImage():
     def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
         # 获取商品基础信息,入参为商品的编号
         url = "{domain}/api/backend/goods_client/goods_query".format(
-            domain=settings.APP_HOST
+            domain=settings.DOMAIN
         )
         data = {
             'goods_art_list': goods_art_list
@@ -93,7 +93,6 @@ class DealImage():
         _s = requests.session().post(url=url, data=data, headers=self.header)
         response_data = _s.json()
 
-
         goods_number_data = {}
         # ["", "", "", "", "", "", "", "", "", "", "", ]
         if "data" not in response_data:
@@ -111,7 +110,6 @@ class DealImage():
 
         return goods_number_data
 
-
     def get_data_from_hqt(self, goods_number_list):
         _goods_number_list = copy.deepcopy(goods_number_list)
         _list = []
@@ -149,8 +147,6 @@ class DealImage():
                 other_path = path + "/" + name
                 check_folder(other_path)
 
-
-
     def move_images(self, goods_art_no, goods_art_no_path, old_image_path):
         """
         步骤:
@@ -167,6 +163,7 @@ class DealImage():
         file = os.path.split(old_image_path)[1]
         # 扩展名
         e = os.path.splitext(file)[1]
+        print("self.goods_images_count_dict", self.goods_images_count_dict)
         # 获取图片序列
         self.goods_images_count_dict[goods_art_no] += 1
         # A9999(1).jpg
@@ -175,12 +172,13 @@ class DealImage():
         # 移动图片
         shutil.move(old_image_path, original_image_path)
 
-    def dealMoveImage(self, image_dir: str, callback_func=None) -> dict:
+    def dealMoveImage(self, image_dir: str, callback_func=None,goods_art_no=None) -> dict:
         if not self.check_path(image_dir=image_dir + "/历史"):
-            return {'code': 1, 'msg': '文件夹创建失败', 'data': {}}
+            raise UnicornException("文件夹创建失败")
 
         # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
         files = self.list_dir(image_dir)
+        print("files", files)
         original_photo_list = []  # 原始图片列表
         for file in files:
             # -----图片清洗
@@ -195,7 +193,7 @@ class DealImage():
             date_time_original = self.get_date_time_original(file_path)  # 获取照片拍照时间
             if date_time_original:
                 # 基于照片的时间,与数据库匹配goods_art_no
-                _data = self.get_goods_art_no(date_time_original)
+                _data = self.get_goods_art_no(goods_art_no)
                 if _data:
                     # 能匹配上数据库
                     goods_art_no, image_index, image_deal_mode = _data
@@ -219,34 +217,35 @@ class DealImage():
                 shutil.move(file_path, image_dir + "/历史/" + file)
 
         if not original_photo_list:
-            return {"code": 1, "msg": "没有任何匹配的图片", 'data': {}}
-
-        if settings.PROJECT == "红蜻蜓":
-            # 批量请求货号图信息
-            goods_art_no_list = [x["goods_art_no"] for x in original_photo_list]
-            goods_art_no_list = list(set(goods_art_no_list))
-            goods_art_no_list = [x for x in goods_art_no_list if "NUM" not in x]
-
-            if goods_art_no_list:
-                goods_art_no_dict = self.get_data_from_hqt_with_goods_art_no(
-                    goods_art_no_list=goods_art_no_list)
-
-                for i in original_photo_list:
-                    if i["goods_art_no"] in goods_art_no_dict:
-                        i["real_goods_art_no"] = i["goods_art_no"]
-                        i["real_goods_number"] = "NUM{}".format(goods_art_no_dict[i["goods_art_no"]]["编号"])
-
-            # 批量请求编号对应信息
-            goods_number_list = [x["goods_art_no"] for x in original_photo_list]
-            goods_number_list = list(set(goods_number_list))
-            goods_number_list = [x for x in goods_number_list if "NUM" in x]
-
-            if goods_number_list:
-                goods_number_dict = self.get_data_from_hqt(goods_number_list=goods_number_list)
-                for i in original_photo_list:
-                    if i["goods_art_no"] in goods_number_dict:
-                        i["real_goods_number"] = i["goods_art_no"]
-                        i["real_goods_art_no"] = goods_number_dict[i["goods_art_no"]]["商品货号"]
+            raise UnicornException("没有任何匹配的图片")
+            return False, "没有任何匹配的图片"
+        # 暂不处理红蜻蜓
+        # if settings.PROJECT == "红蜻蜓":
+        #     # 批量请求货号图信息
+        #     goods_art_no_list = [x["goods_art_no"] for x in original_photo_list]
+        #     goods_art_no_list = list(set(goods_art_no_list))
+        #     goods_art_no_list = [x for x in goods_art_no_list if "NUM" not in x]
+
+        #     if goods_art_no_list:
+        #         goods_art_no_dict = self.get_data_from_hqt_with_goods_art_no(
+        #             goods_art_no_list=goods_art_no_list)
+
+        #         for i in original_photo_list:
+        #             if i["goods_art_no"] in goods_art_no_dict:
+        #                 i["real_goods_art_no"] = i["goods_art_no"]
+        #                 i["real_goods_number"] = "NUM{}".format(goods_art_no_dict[i["goods_art_no"]]["编号"])
+
+        #     # 批量请求编号对应信息
+        #     goods_number_list = [x["goods_art_no"] for x in original_photo_list]
+        #     goods_number_list = list(set(goods_number_list))
+        #     goods_number_list = [x for x in goods_number_list if "NUM" in x]
+
+        #     if goods_number_list:
+        #         goods_number_dict = self.get_data_from_hqt(goods_number_list=goods_number_list)
+        #         for i in original_photo_list:
+        #             if i["goods_art_no"] in goods_number_dict:
+        #                 i["real_goods_number"] = i["goods_art_no"]
+        #                 i["real_goods_art_no"] = goods_number_dict[i["goods_art_no"]]["商品货号"]
 
         # 排序需要基于拍照的文件序号进行处理
         original_photo_list.sort(
@@ -281,7 +280,6 @@ class DealImage():
             self.move_images(goods_art_no, goods_art_no_path, original_image_path)  # 货号、货号文件路径、原始图路径
             time.sleep(0.2)
 
-
             # self.progress_sign.emit({"type": "移动原始图片", "progress_bar_value": int(n / total_num * 100)})
             # self.show_progress_detail("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
             if callback_func:
@@ -300,9 +298,7 @@ class DealImage():
 
         # 完成处理
         # self.set_state(state_value=2)
-        return {'code': 0, 'msg': '处理完成', 'target_path': output_path, 'data': {}}
-
-
+        return True, output_path
 
     def deal(cls, dir_path):
 
@@ -429,9 +425,8 @@ class DealImage():
                         },
                         )
 
-
                 except:
                     pass
                 sheet.write_row("A{}".format(index + 2), [goods_number])
                 sheet.write_row("B{}".format(index + 2), [goods_art_no])
-                sheet.write_row("E{}".format(index + 2), [goods_pic_total])
+                sheet.write_row("E{}".format(index + 2), [goods_pic_total])

+ 5 - 6
python/service/deal_one_image.py

@@ -1,17 +1,16 @@
 import copy
 import os.path
 # from module.other.module_online_data import GetOnlineData
-from module.online_request.module_online_data import GetOnlineDataHLM
+from .online_request.module_online_data import GetOnlineDataHLM
 
 import time
-from module.log.log import MyLogger
+from logger import logger
 
 import os
 from PIL import Image
 # from module.other.remove_bg_ali import RemoveBgALi
-from module.base_mode.remove_bg_pixian import RemoveBgPiXian
-from module.base_mode.remove_bg_ali import RemoveBgALi
-from module.base_mode.remove_bg_ali import Picture
+from .remove_bg_pixian import RemoveBgPiXian
+from .remove_bg_ali import RemoveBgALi, Picture
 import cv2
 import numpy as np
 import settings
@@ -28,7 +27,7 @@ class Base(object):
         self.file_name = image_data["file_name"]
         self.file = os.path.split(self.file_path)[1]
         self.is_once_data = {}
-        self.logger = MyLogger().logger
+        self.logger = logger
 
     def add_log(self, text, _type="info"):
         self.logger.info("第{}个,图片名称:{},内容:{}".format(self.num, self.file, text))

+ 179 - 0
python/service/detail_func.py

@@ -0,0 +1,179 @@
+import json
+import os
+import settings
+import shutil
+
+
+def get_all_dir_info(image_dir, goods_art_no):
+    # 遍历货号获取所有货号--可能为编号
+    folder_name_list = []
+    for folder_name in os.listdir(image_dir):
+        _path = "{}/{}".format(image_dir, folder_name)
+        if not os.path.isdir(_path):
+            continue
+        if "无法对应" in folder_name:
+            continue
+        if "软件" in folder_name:
+            if len(os.listdir(_path)) == 0:
+                # 删除空文件夹
+                os.rmdir(_path)
+            continue
+        print("folder_name====>", folder_name)
+        print("goods_art_no====>", goods_art_no)
+        if goods_art_no == folder_name:
+            folder_name_list.append(folder_name)
+
+    return folder_name_list
+
+
+def get_all_detail_info(image_dir):
+    # 遍历货号获取所有货号--可能为编号
+    _list = []
+    for goods_no in os.listdir(image_dir):
+        goods_art_no_path = "{}/{}".format(image_dir, goods_no)
+        if not os.path.isdir(goods_art_no_path):
+            continue
+        _list.append(goods_no)
+    return _list
+
+
+def get_all_dir_info_and_pic_info(root_path, folder_name_list, need_view_list):
+    # 遍历货号获取所有货号--可能为编号
+    """
+    数据返回为每个文件夹下的文件结构
+    """
+
+    return_data = {}
+    for one_folder in folder_name_list:
+        source_material_path = "{}/{}/阴影图处理".format(root_path, one_folder)
+        data = {"pics": {},
+                "message": "",
+                "800x800": []
+                }
+
+        # source_material_path ===================
+        if not os.path.exists(source_material_path):
+            data["message"] = "缺失 阴影图处理 文件夹"
+        else:
+            if len(os.listdir(source_material_path)) % 2 != 0:
+                data["message"] = "缺失 阴影图处理 图片不为2的倍数"
+            else:
+                # 遍历图片
+                _pic_name_list = []
+                for pic in os.listdir(source_material_path):
+                    _pic = pic.replace(".png", "")
+                    _pic = _pic.split("_")
+                    if len(_pic) == 3:
+                        # 示例:侧视-阴影
+                        data["pics"]["{}-{}".format(_pic[1], _pic[2])] = "{}/{}".format(source_material_path, pic)
+                        _pic_name_list.append(_pic[1])
+
+                # 检查图片是否都有
+                _ = [x for x in need_view_list if x not in _pic_name_list]
+                if _:
+                    _ = ",".join(_)
+                    data["message"] += "\图片{}视角缺失".format(_)
+
+        # 放入商品货号主图 ===================
+        goods_art_800_pic_f = "{}/{}/800x800".format(root_path, one_folder)
+        if not os.path.exists(goods_art_800_pic_f):
+            data["message"] += "\缺失主图文件夹"
+        else:
+            if len(os.listdir(goods_art_800_pic_f)) == 0:
+                data["message"] += "\缺失主图"
+            else:
+                for goods_art_pic in os.listdir(goods_art_800_pic_f):
+                    # 过滤非图片信息
+                    if "jpg" in goods_art_pic or "JPG" in goods_art_pic or "png" in goods_art_pic or "PNG" in goods_art_pic:
+                        data["800x800"].append("{}/{}".format(goods_art_800_pic_f, goods_art_pic))
+
+        # =======================
+        return_data[one_folder] = data
+    print("检查没有问题的文件夹-----------return_data")
+    print(json.dumps(return_data))
+
+    return return_data
+
+
+def merge_local_and_remote_data(all_dir_info_data: dict, remote_data):
+    """
+    all_dir_info_data:文件夹基础数据;文件夹结构数据
+    remote_data:远程数据
+    """
+    print("---all_dir_info_data:")
+    print(json.dumps(all_dir_info_data))
+
+    print("---remote_data:")
+    print(json.dumps(remote_data))
+
+    error_folder_name_list = []
+    for folder_name, value in all_dir_info_data.items():
+        if folder_name not in remote_data:
+            value["message"] = "文件夹没有对应数据信息"
+            error_folder_name_list.append(folder_name)
+        else:
+            value["remote_data"] = remote_data[folder_name]
+
+    # =======开始组装数据
+
+    # 组装数据,按款号进行组装数据
+    goods_no_dict = {}
+    for folder_name, value in all_dir_info_data.items():
+        if "remote_data" not in value:
+            continue
+        if "data" not in value["remote_data"]:
+            continue
+
+        _remote_data = value["remote_data"]["data"]
+        goods_no = _remote_data["款号"]
+        if goods_no not in goods_no_dict:
+            goods_no_dict[goods_no] = {}
+            goods_no_dict[goods_no]["款号"] = goods_no
+            goods_no_dict[goods_no]["货号资料"] = []
+
+        _goods_number = _remote_data["编号"] if "编号" in _remote_data else ""
+        if _goods_number:
+            _goods_number = "NUM{}".format(_goods_number)
+        else:
+            _goods_number = ""
+
+        goods_no_dict[goods_no]["货号资料"].append({"货号": _remote_data["商品货号"],
+                                                "文件夹名称": folder_name,
+                                                "编号": _goods_number,
+                                                "颜色名称": _remote_data["颜色名称"] if "颜色名称" in _remote_data else "",
+                                                "pics": value["pics"],
+                                                "800x800": value["800x800"],
+                                                })
+
+        # 添加款号的基础信息,进行去重处理,此处为动态字段
+        key = ["款号", "商品货号", "编号", "颜色名称", ]
+        for _k, _v in _remote_data.items():
+            if _k not in key:
+                if _k not in goods_no_dict[goods_no]:
+                    goods_no_dict[goods_no][_k] = ""
+                if not goods_no_dict[goods_no][_k]:
+                    if _v:
+                        goods_no_dict[goods_no][_k] = _v
+
+    # ======如果为红蜻蜓,则key需要更改为编号=========
+    # 改款号信息  取任意一个商品的编号作为款号,格式为KNUM00001
+    if settings.PROJECT == "红蜻蜓":
+        _goods_no_dict = {}
+        for key, value in goods_no_dict.items():
+            number = "K{}".format(value["货号资料"][0]["编号"])
+            _goods_no_dict[number] = value
+    else:
+        _goods_no_dict = goods_no_dict
+
+    return _goods_no_dict, error_folder_name_list
+
+
+def move_folders(path_list, target_folder):
+    create_folder(target_folder)
+    for source_folder in path_list:
+        shutil.move(source_folder, target_folder)
+
+
+def create_folder(path):
+    if not os.path.exists(path):
+        os.makedirs(path)

+ 84 - 0
python/service/detail_temp.py

@@ -0,0 +1,84 @@
+# from import_qt_mode import *
+# from UI.auto_deal_pics_ui.temp_item import Ui_Form as UI_temp_item
+import os
+from PIL import Image
+import threading
+from io import BytesIO
+
+
+# 详情itme模板组件
+class TempItem():
+    # select_sign = Signal(str)
+
+    def __init__(self, parent, _id, image_path):
+        super().__init__(parent)
+        # self.ui = UI_temp_item()
+        # self.ui.setupUi(self)
+        self.temp_name = _id
+        self.image_path = image_path
+        self.id = _id
+        self.is_select = False
+        self.init()
+        # self.show()
+
+    def init(self):
+        # self.ui.label.mousePressEvent = self.pic_show
+        # self.ui.radioButton.clicked.connect(self.select)
+        self.set_label_image()
+        # self.label_temp_name = QLabel(self)
+        # self.label_temp_name.setText(str(self.temp_name))
+        # self.label_temp_name.move(5, self.height() - self.label_temp_name.height())
+
+    def cancel_select(self, *args):
+        self.is_select = False
+        # self.ui.radioButton.setChecked(False)
+
+    def select(self, *args):
+        self.is_select = True
+        # self.ui.radioButton.setChecked(True)
+        # self.select_sign.emit(self.id)
+
+    def pic_show(self, *args, **kwargs):
+        if os.path.exists(self.image_path):
+            im = Image.open(self.image_path)
+            threading.Thread(target=im.show, args=()).start()
+
+    def set_label_image(self):
+        if not os.path.exists(self.image_path):
+            return
+        # try:
+        #     bytes_io = BytesIO()
+        #     w, h = self.ui.image_show.width(), self.ui.image_show.height()
+        #     _img_raw = Image.open(self.image_path)
+        #     _img_raw = self.to_resize(_img_raw, width=w * 2)
+        #     _img_raw = _img_raw.crop(box=(0, 0, w * 2, h * 2))
+        #     # _img_raw.thumbnail((w * 2, int(_img_raw.height * w * 2 / _img_raw.width)))
+        #     # img_raw = _img_raw.resize(size=(w, h))
+        #     _img_raw.save(bytes_io, "JPEG")
+        #     icon = QPixmap()
+        #     # icon.loadFromData(img)
+        #     icon.loadFromData(bytes_io.getvalue())
+        #     icon = icon.scaled(
+        #         self.ui.image_show.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+        #     )
+        #     self.ui.image_show.setPixmap(icon)
+        #     # self.mousePressEvent = lambda x: self.show_one_data(row)
+        # except:
+        pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+

+ 11 - 11
python/service/excel_base_func.py

@@ -1,4 +1,4 @@
-from PySide6.QtWidgets import QApplication
+# from PySide6.QtWidgets import QApplication
 from PIL import Image
 import settings
 import time
@@ -6,16 +6,16 @@ import os
 
 def get_clip_image(image_path):
     try:
-        cb = QApplication.clipboard()
-        if cb.mimeData().hasImage():
-            qt_img = cb.image()
-            pil_img = Image.fromqimage(qt_img)  # 转换为PIL图像
-            if pil_img.width > 10:
-                pil_img = pil_img.convert("RGB")
-                pil_img.save(image_path)
-                print("图片剪切板保存成功:{}".format(image_path))
-
-            return True
+        # cb = QApplication.clipboard()
+        # if cb.mimeData().hasImage():
+        #     qt_img = cb.image()
+        #     pil_img = Image.fromqimage(qt_img)  # 转换为PIL图像
+        #     if pil_img.width > 10:
+        #         pil_img = pil_img.convert("RGB")
+        #         pil_img.save(image_path)
+        #         print("图片剪切板保存成功:{}".format(image_path))
+
+            # return True
         return False
     except BaseException as e:
         print(e)

+ 313 - 0
python/service/generate_goods_art_no_table/module_generate_goods_art_no_table.py

@@ -0,0 +1,313 @@
+# from UI.generate_goods_art_no_table.generate_goods_art_no_table_ui import (
+#     Ui_Form as generate_goods_art_no_table_Ui_Form,
+# )
+# from import_qt_mode import *
+import os
+import time
+import threading
+import xlsxwriter
+import shutil
+from ..pic_deal import Picture
+from PIL import Image
+# from module.view_control.MineQWidget import MineQWidget
+# from module.base_mode.excel_base_func import *
+
+
+class GenerateGoodsArtNoTable():
+    # 货号表生成
+    # progress_sign = Signal(dict)
+    # info_sign = Signal(str)
+
+    def __init__(self):
+        super().__init__()
+        self.is_del = False
+        self.setupUi(self)
+        # 0禁用  1进行中  2已结束
+        self.state = 2
+        self.init()
+        self.show()
+
+    def init(self):
+        self.label_4.setText("请选择需要生成货号表的文件夹路径")
+        self.label_3.setText("请选择目录")
+        self.label_3.mousePressEvent = self.get_img_dir
+        self.textBrowser_2.hide()
+
+        self.progressBar.setValue(0)
+        self.progressBar.setMaximum(100)
+        self.progressBar.setValue(0)
+        self.progressBar.hide()
+        self.label_5.setText("")
+
+        self.progress_sign.connect(self.show_progress)
+        self.pushButton.clicked.connect(self.run)
+        self.textBrowser_2.hide()
+
+    def get_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
+        self.label_4.setText(folder)
+
+    def show_progress(self, data):
+        progress_bar_value = data["progress_bar_value"]
+        self.label_5.setText(data["type"])
+        self.progressBar.setValue(progress_bar_value)
+        pass
+
+    def check(self):
+        _path = self.image_dir + "/历史"
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def set_state(self, state_value: int):
+        # 0禁用  1进行中  2已结束
+        if state_value not in [0, 1, 2]:
+            return
+        self.state = state_value
+        if self.state == 0:
+            self.pushButton.setText("执行中")
+            self.pushButton.setEnabled(False)
+        if self.state == 1:
+            self.progressBar.show()
+            self.textBrowser_2.show()
+            self.pushButton.setText("执行中")
+            self.pushButton.setEnabled(False)
+            self.textBrowser_2.clear()
+        if self.state == 2:
+            self.pushButton.setText("执行完毕")
+            self.pushButton.setEnabled(True)
+            self.progressBar.hide()
+
+    def run(self):
+        self.set_state(state_value=1)
+        self.t = threading.Thread(target=self.run_by_thread, args=())
+        self.t.start()
+
+    def show_progress_detail(self, text):
+        self.textBrowser_2.append(text)
+
+    def run_by_thread(self, dir_path=None):
+        if not dir_path:
+            dir_path = self.label_4.text()
+        if not dir_path:
+            self.show_progress_detail("请选择文件夹")
+            self.set_state(state_value=2)
+            return
+
+        if not os.path.exists(dir_path):
+            self.show_progress_detail("该文件夹不存在")
+            self.set_state(state_value=2)
+            return
+        GenerateGoodsArtNoTable.deal(dir_path)
+        # 完成处理
+        self.set_state(state_value=2)
+
+    def save_as_excel(self, out_excel_data, out_excel_path=None):
+        self.show_progress_detail("开始尝试导出Excel文件~~~~")
+
+        def close_book(_book):
+            try:
+                _book.close()
+            except BaseException as e:
+                print(e)
+                self.show_progress_detail("请先关闭文件:{}".format(out_excel_path))
+                return False
+            return True
+
+        options = {
+            "default_format_properties": {
+                "align": "left",
+                "valign": "vcenter",
+                "text_wrap": True,
+            }
+        }
+        book = xlsxwriter.Workbook(filename=out_excel_path, options=options)
+        sheet = book.add_worksheet("sheet1")
+        # sheet.freeze_panes(1, 2)
+        sheet.set_column("B:B", 17)
+
+        sheet.write_row("A1", ["货号", "缩略图"])
+        for index, data in enumerate(out_excel_data):
+            # print(data)
+            goods_no, image_file = data
+            try:
+                im = Image.open(image_file)
+                im_x, im_y = im.size
+                image_width = 100
+                image_height = int(im_y * image_width / im_x)
+                sheet.set_row(index + 1, 95)
+                x_scale = round(image_width / im_x, 2)  # 固定宽度/要插入的原始图片宽
+                y_scale = round(image_height / im_y, 2)  # 固定高度/要插入的原始图片高
+
+                sheet.insert_image(
+                    index + 1,
+                    1,
+                    image_file,
+                    {
+                        "x_scale": x_scale,
+                        "y_scale": y_scale,
+                        "x_offset": 5,
+                        "y_offset": 5,
+                    },
+                )
+            except:
+                self.show_progress_detail("图片异常,{}".format(image_file))
+                pass
+            sheet.write_row("A{}".format(index + 2), [goods_no])
+
+        close_book(book)
+        self.show_progress_detail("已保存文件至:{}".format(out_excel_path))
+        # while 1:
+        #     if self.is_del:
+        #         break
+        #
+        #     if not close_book(book):
+        #         time.sleep(1)
+        #         self.show_progress_detail("请先关闭文件:{}".format(out_excel_path))
+        #     else:
+        #         self.show_progress_detail("已保存文件至:{}".format(out_excel_path))
+        #         break
+
+    @classmethod
+    def deal(cls, dir_path):
+        def close_book(_book):
+            try:
+                _book.close()
+            except BaseException as e:
+                print(e)
+                return False
+            return True
+
+        # print("dir_path", dir_path)
+        out_excel_data = []
+        for goods_art_no_folder in os.listdir(dir_path):  # 遍历货号文件夹集合
+            if not os.path.isdir(
+                "{}/{}".format(dir_path, goods_art_no_folder)
+            ):  # 非文件夹进行过滤
+                continue
+            if "软件" in goods_art_no_folder:
+                continue
+
+            # print("goods_art_no_folder", goods_art_no_folder)
+            # 如果存在800的主图,则优先进行使用
+            big_image_folder_path = "{}/{}/800x800".format(
+                dir_path, goods_art_no_folder
+            )
+            if not os.path.exists(big_image_folder_path):
+                os.mkdir(big_image_folder_path)
+            all_big_images = os.listdir(big_image_folder_path)
+            goods_pic_total = len(all_big_images)
+            _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE"]
+            thumb_image_file_path = None
+            # print("all_big_images",all_big_images)
+            if all_big_images:
+                for _file in all_big_images:
+                    # print(_file)
+                    file_name, e = os.path.splitext(_file)
+                    # print(file_name, e)
+                    if e in _Type:
+                        thumb_image_file_path = "{}/{}/800x800/{}".format(
+                            dir_path, goods_art_no_folder, _file
+                        )
+                        break
+
+            # 如果不存在主图则进行使用原始图
+            if thumb_image_file_path is None:
+                _path = "{}/{}/原始图".format(dir_path, goods_art_no_folder)
+                if not os.path.exists(_path):
+                    continue
+
+                all_original_images = os.listdir(_path)  # 遍历货号原始图文件夹
+                goods_pic_total = len(all_original_images)
+                if not all_original_images:
+                    continue
+                image_file = all_original_images[0]  # 取第一个货号图
+                image_file_path = "{}/{}/原始图/{}".format(
+                    dir_path, goods_art_no_folder, image_file
+                )
+
+                if not os.path.exists(
+                    "{}/{}/200images".format(dir_path, goods_art_no_folder)
+                ):
+                    os.mkdir("{}/{}/200images".format(dir_path, goods_art_no_folder))
+
+                thumb_image_file_path = "{}/{}/200images/{}".format(
+                    dir_path, goods_art_no_folder, image_file
+                )
+                if not os.path.exists(thumb_image_file_path):
+                    # 开始触发进行压缩生成文件
+                    shutil.copy(image_file_path, thumb_image_file_path)  # 复制文件
+                    pic = Picture(thumb_image_file_path)
+                    pic.resize(width=600)
+                    pic.save_img(thumb_image_file_path)
+            # print("thumb_image_file_path", thumb_image_file_path)
+
+            goods_number = ""
+            if "@" in goods_art_no_folder:
+                _ = goods_art_no_folder.split("@")
+                goods_art_no_folder = _[0]
+                goods_number = _[1].replace("NUM", "")
+
+            out_excel_data.append(
+                [
+                    goods_number,
+                    goods_art_no_folder,
+                    thumb_image_file_path,
+                    goods_pic_total,
+                ]
+            )
+
+        if out_excel_data:
+            out_excel_path = "{}/货号表-{}.xlsx".format(dir_path, time.time())
+            options = {
+                "default_format_properties": {
+                    "align": "left",
+                    "valign": "vcenter",
+                    "text_wrap": True,
+                }
+            }
+            book = xlsxwriter.Workbook(filename=out_excel_path, options=options)
+            sheet = book.add_worksheet("sheet1")
+            # sheet.freeze_panes(1, 2)
+            sheet.set_column("B:B", 17)
+            sheet.set_column("D:D", 20)
+
+            sheet.write_row("A1", ["编号", "原货号", "新货号", "缩略图", "原始图张数"])
+            for index, data in enumerate(out_excel_data):
+                # print(data)
+                goods_number, goods_art_no, image_file, goods_pic_total = data
+                try:
+                    im = Image.open(image_file)
+                    im_x, im_y = im.size
+                    image_width = 100
+                    image_height = int(im_y * image_width / im_x)
+                    sheet.set_row(index + 1, 95)
+                    x_scale = round(
+                        image_width / im_x, 2
+                    )  # 固定宽度/要插入的原始图片宽
+                    y_scale = round(
+                        image_height / im_y, 2
+                    )  # 固定高度/要插入的原始图片高
+                    sheet.insert_image(
+                        index + 1,
+                        3,
+                        image_file,
+                        {
+                            "x_scale": x_scale,
+                            "y_scale": y_scale,
+                            "x_offset": 5,
+                            "y_offset": 5,
+                            "object_position":1,
+                        },
+                    )
+
+
+                except:
+                    pass
+                sheet.write_row("A{}".format(index + 2), [goods_number])
+                sheet.write_row("B{}".format(index + 2), [goods_art_no])
+                sheet.write_row("E{}".format(index + 2), [goods_pic_total])
+            close_book(book)
+
+    def __del__(self):
+        self.is_del = True

+ 340 - 0
python/service/generate_goods_no_detail_pic/ceshi.py

@@ -0,0 +1,340 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+
+class DetailPicGetXiaoSuShuoXie(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, ):
+        super().__init__(goods_no, goods_no_value, out_put_dir)
+        if __name__ == '__main__':
+            self.root = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo"
+        else:
+            self.root = os.getcwd()
+
+        self.detailed_images = []
+        self.run()
+
+    def run(self):
+        detailed_images = self.deal_details()
+        self.create_folder(self.out_put_dir)
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(detail_path)
+        join_path = "{out_put_dir}/{goods_no}/拼接图".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(join_path)
+
+        for index, pp in enumerate(detailed_images):
+            pp.im.save("{}/{}({}).jpg".format(detail_path, self.goods_no, str(index + 11).zfill(2)), format="JPEG")
+
+        img = self.add_pic(detailed_images)
+        # img.show()
+        img.save("{}/1.jpg".format(join_path), format="JPEG")
+
+        # ------------移动其他图片---------------------
+        self.move_other_pic()
+        return True
+
+    def deal_details(self):
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        return [x for x in detailed_images if x]
+
+    def deal_pic_1(self):
+        bg_color = (239, 237, 238)
+        """   制作主图  """
+        detailed_images = []
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = []
+        for _set in [("【品    牌】", "品牌"),
+                     ("【货    号】", "款号"),
+                     ("【鞋面材质】", "鞋面材质"),
+                     ("【系列风格】", "系列风格"),
+                     ("【内里材质】", "内里材质"),
+                     ("【尺    码】", "尺码"),
+                     ("【鞋底材质】", "鞋底材质"),
+                     ("【品牌编码】", "品牌编码"),
+                     ]:
+            title, key = _set
+            text = self.get_text_value(key)
+            if text:
+                text_list.append(
+                    (title, text)
+                )
+
+        # 文字排列
+        pp_bg = PictureProcessing(r"{}\resources\detail_image_xiaosushuoxie_1\bg-1.jpg".format(self.root))
+        x = 101
+        y = 98
+        for _set in text_list:
+            pp_bg = pp_bg.add_text(mode="pixel",
+                                   value=(x, y),
+                                   font=font,
+                                   text="{}:{}".format(_set[0], _set[1]),
+                                   align="left",
+                                   spacing=10,
+                                   fill=(17, 16, 16))
+            x += 588
+            if x > 700:
+                x = 101
+                y += 94
+
+        # ----------主图放置
+        y += 10
+        goods_art_no_list = list(self.data.keys())
+
+
+        pp_jpg, pp_png = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="俯视", return_orign=True)
+        pp_jpg = pp_jpg.resize(value=1083)
+        pp_png = pp_png.resize(value=1083)
+        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="nc", value=(0, y), top_png_img=pp_png)
+
+        y += pp_jpg.height
+        y += 20
+
+        # ----------配图粘贴
+        if len(goods_art_no_list) > 1:
+            # 大于一个颜色做颜色图展示
+
+            pp_list_1,pp_list_2 = [],[]
+            for goods_art_no in goods_art_no_list:
+                pp_jpg, pp_png = self.image_one_pic(goods_art_no=goods_art_no, name="侧视", return_orign=True)
+                pp_jpg = pp_jpg.resize(value=400)
+                pp_png = pp_png.resize(value=400)
+                pp_list_1.append(pp_jpg)
+                pp_list_2.append(pp_png)
+
+            _pp_jpg = PictureProcessing()
+            _pp_png = PictureProcessing()
+            _pp_jpg = _pp_jpg.horizontal_distribution(pp_list=pp_list_1, bg_width=1200, margins=(0, 0, 0, 0), line_spacing=0,
+                                                  number_per_row=3)
+            _pp_png = _pp_png.horizontal_distribution(pp_list=pp_list_2, bg_width=1200, margins=(0, 0, 0, 0), line_spacing=0,
+                                                  number_per_row=3)
+
+            pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=_pp_jpg, base="nc", value=(0, y),
+                                                 top_png_img=_pp_png)
+
+            y += _pp_jpg.height
+            y += 30
+
+        # ------多余的剪裁
+        if y < pp_bg.height:
+            # print("触发剪裁")
+            pp_bg = pp_bg.crop_img(value=(0, 0, pp_bg.width, y))
+
+        return pp_bg
+
+    def deal_pic_2(self):
+        """
+        细节解析
+
+        """
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        font_2 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 15)
+        bg_color = (220, 220, 220)
+        detailed_images = []
+
+        goods_art_no_list = list(self.data.keys())
+        # ------侧视图-------------
+        pp_2 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="侧视", bg_color=bg_color)
+        pp_2 = pp_2.resize(mode="pixel", base="width", value=1327)
+        pp_2 = PictureProcessing("RGB", (550, 550), bg_color).paste_img(mode="pixel", top_img=pp_2,
+                                                                        base="wc", value=(-90, 0))
+        # 文字
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 24),
+                             font=font_1,
+                             text="优质质感鞋面",
+                             align="left",
+                             spacing=10,
+                             fill=(28, 28, 28))
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 76),
+                             font=font_2,
+                             text="颇具层次",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 115),
+                             font=font_2,
+                             text="时尚质感出众",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        # ------后跟-------------
+        pp_3 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="后跟", bg_color=bg_color)
+        pp_3 = pp_3.resize(mode="pixel", base="width", value=625)
+        pp_3 = PictureProcessing("RGB", (550, 550), bg_color).paste_img(mode="pixel", top_img=pp_3,
+                                                                        base="cc", value=(0, 0))
+        # 文字
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35 * 3),
+                             font=font_2,
+                             text="颇具层次",
+                             align="right",
+                             spacing=10,
+                             anchor="rs",
+                             fill=(78, 78, 78))
+
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35 * 2),
+                             font=font_2,
+                             text="时尚质感出众",
+                             align="right",
+                             anchor="rs",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35),
+                             font=font_1,
+                             text="设计感十足",
+                             anchor="rs",
+                             align="right",
+                             spacing=10,
+                             fill=(28, 28, 28))
+
+        # ---------------鞋底图
+        pp_4 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="鞋底", bg_color=bg_color)
+        pp_4 = pp_4.resize(mode="pixel", base="width", value=1635)
+        pp_4 = PictureProcessing("RGB", (1134, 625), bg_color).paste_img(mode="pixel", top_img=pp_4,
+                                                                         base="wc", value=(198, 0))
+        # 文字
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 451),
+                             font=font_1,
+                             text="防滑耐磨鞋底",
+                             align="left",
+                             spacing=10,
+                             fill=(28, 28, 28))
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 510),
+                             font=font_2,
+                             text="优质材质",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 549),
+                             font=font_2,
+                             text="防滑网纹设计坚固耐磨",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        new_bg = PictureProcessing("RGB", (1200, 40 + pp_2.height + 40 + pp_4.height + 40), (239, 239, 239))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_2, base="nw", value=(33, 40))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_3, base="nw", value=(33 + 550 + 33, 40))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_4, base="nw", value=(33, 40 + pp_2.height + 40))
+        detailed_images.append(new_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        # 设计理念
+        bg = PictureProcessing(r"{}\resources\detail_image_xiaosushuoxie_1\1 (1).jpg".format(self.root))
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 40)
+        # 粘贴文字
+        text = self.get_text_value("设计理念")
+        if not text:
+            text = "告别枯燥沉闷日常还原自然本身色彩\n融合工装、复古、运动元素随性出走感受自在的步伐"
+
+        bg = bg.add_text(mode="pixel",
+                             value=(164, 328),
+                             font=font_1,
+                             text=text,
+                             align="left",
+                             spacing=15,
+                             fill=(39, 39, 39))
+
+
+        return bg
+
+    def deal_pic_4(self):
+        """   各个颜色细节展示  """
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            # ------俯视图-------------
+            pp_1 = self.image_one_pic(goods_art_no=goods_art_no, name="俯视", bg_color=bg_color)
+            pp_1 = pp_1.resize(mode="pixel", base="width", value=956)
+            pp_1 = PictureProcessing("RGB", (1200, 40 + pp_1.height + 40), bg_color).paste_img(mode="pixel",
+                                                                                               top_img=pp_1,
+                                                                                               base="cc", value=(0, 0))
+            # 粘贴文字与logo
+            pp_1 = pp_1.add_text(mode="pixel",
+                                 value=(51, 26),
+                                 font=font_1,
+                                 text=color_name,
+                                 align="left",
+                                 spacing=10,
+                                 fill=(28, 28, 28))
+            pp_1 = pp_1.paste_img(mode="pixel", top_img=PictureProcessing(
+                r"{}\resources\detail_image_xiaosushuoxie_1\1 (4).jpg".format(self.root)), base="nw", value=(51, 72))
+
+            # ------后跟-------------
+            pp_2 = self.image_one_pic(goods_art_no=goods_art_no, name="后跟", bg_color=bg_color)
+            pp_2 = pp_2.resize(mode="pixel", base="width", value=345)
+            pp_2 = PictureProcessing("RGB", (537, 356), bg_color).paste_img(mode="pixel", top_img=pp_2,
+                                                                            base="cc", value=(0, 0))
+            # ------内里-------------
+            pp_3 = self.image_one_pic(goods_art_no=goods_art_no, name="内里", bg_color=bg_color)
+            pp_3 = pp_3.resize(mode="pixel", base="width", value=577)
+            pp_3 = PictureProcessing("RGB", (537, 356), bg_color).paste_img(mode="pixel", top_img=pp_3,
+                                                                            base="cc", value=(0, 0))
+            bg_pp = PictureProcessing("RGB", (1200, 20 + pp_1.height + 20 + pp_2.height + 20), bg_color)
+            # 粘贴俯视图
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_1, base="nw", value=(0, 0))
+            # 粘贴后跟
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_2, base="nw", value=(51, 20 + pp_1.height + 20))
+            # 粘贴内里
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_3, base="nw", value=(588, 20 + pp_1.height + 20))
+
+            detailed_images.append(bg_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        # 其他图片
+        detailed_images = []
+        detailed_images.append(
+            PictureProcessing(r"{}\resources\detail_image_xiaosushuoxie_1\1 (2-1).jpg".format(self.root)))
+        detailed_images.append(
+            PictureProcessing(r"{}\resources\detail_image_xiaosushuoxie_1\1 (3).jpg".format(self.root)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+
+if __name__ == '__main__':
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data3.txt",
+            "r", encoding="utf-8") as f:
+        data = json.loads(f.read())
+
+    for goods_no, value in data.items():
+        d = DetailPicGetXiaoSuShuoXie(goods_no, value,
+                                      out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-11-19\软件-详情图生成")
+        raise 1

+ 332 - 0
python/service/generate_goods_no_detail_pic/data.py

@@ -0,0 +1,332 @@
+import json
+
+from ..data_metaclass import DataBaseModel
+import copy
+import pandas as pd
+import settings
+import numpy as np
+
+
+class DataModeGenerateDetail(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+        self.get_online_data.refresh_headers()
+
+    def get_real_color_name(self, color_name: str):
+        _ = ["薄棉", "加厚", "厚棉"]
+        for i in _:
+            color_name = color_name.replace(i, "")
+        return color_name
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        if goods_art_list:
+            data_list = copy.deepcopy(goods_art_list)
+            flag = "Goods_Art"
+        else:
+            data_list = copy.deepcopy(numbers_list)
+            flag = "NUM"
+        _list = []
+        # 单次请求数少于20个
+        goods_dict = {}
+        while data_list:
+            one_data = data_list.pop()
+            _list.append(one_data)
+
+            if len(_list) == 20 or len(data_list) == 0:
+                if flag == "NUM":
+                    # 注意,红蜻蜓和惠利玛函数不同
+                    online_goods_art_data = self.get_online_data.get_goods_art_no_info(numbers_list=_list)
+                else:
+                    online_goods_art_data = self.get_online_data.get_goods_art_no_info(goods_art_list=_list)
+                if online_goods_art_data:
+                    for key in online_goods_art_data:
+                        # 映射为真实的文件夹名称
+                        goods_dict[key] = online_goods_art_data[key]
+                _list = []
+        return goods_dict
+
+    def get_basic_goods_art_data_by_hqt_and_hlm(self, folder_name_list):
+        """
+        查询红蜻蜓货号的对应数据,输入写入为编码,可能带有@,可能为货号等 NUN为编码
+        """
+        original_folder_name_list = copy.deepcopy(folder_name_list)
+
+        # 编号与文件名映射表
+        return_dict = {}
+        for folder_name in original_folder_name_list:
+            folder_name: str
+            if "NUM" not in folder_name:
+                # 即为货号
+                return_dict[folder_name] = {"type": "goods_art_no",
+                                            "name": folder_name.upper(),
+                                            "文件夹名称": folder_name,
+                                            "data": {}
+                                            }
+            else:
+                if "@" not in folder_name:
+                    return_dict[folder_name] = {"type": "goods_number",
+                                                "name": folder_name.upper().replace("NUM", ""),
+                                                "文件夹名称": folder_name,
+                                                "data": {}
+                                                }
+                else:
+                    return_dict[folder_name] = {"type": "goods_number",
+                                                "name": folder_name.upper().replace("NUM", "").split("@")[1],
+                                                "文件夹名称": folder_name,
+                                                "data": {}
+                                                }
+
+        # -----------请求货号数据处理------------
+        goods_art_no_list = [v["name"] for k, v in return_dict.items() if v["type"] == "goods_art_no"]
+        goods_art_no_all_data = self.get_goods_art_no_info(goods_art_list=goods_art_no_list)
+        for folder_name, value in return_dict.items():
+            if value["type"] == "goods_art_no":
+                if value["name"] in goods_art_no_all_data:
+                    return_dict[folder_name]["data"] = goods_art_no_all_data[value["name"]]
+
+        # ------------请求编码数据----------------------
+        goods_number_list = [v["name"] for k, v in return_dict.items() if v["type"] == "goods_number"]
+        goods_number_all_data = self.get_goods_art_no_info(numbers_list=goods_number_list)
+        for folder_name, value in return_dict.items():
+            if value["type"] == "goods_number":
+                if value["name"] in goods_number_all_data:
+                    return_dict[folder_name]["data"] = goods_number_all_data[value["name"]]
+
+        # 清空没有值的数据
+        error_key = []
+        for folder_name, value in return_dict.items():
+            if not value["data"]:
+                error_key.append(folder_name)
+
+        if error_key:
+            for folder_name in error_key:
+                return_dict.pop(folder_name)
+
+        return {"code": 0, "message": "", "data": return_dict}
+
+    def get_basic_goods_art_data_form_excel(self, folder_name_list, excel_path, keys):
+
+        # =====创建虚拟表格并进行连表处理
+        need_df = pd.DataFrame(columns=["文件夹名称"])
+        for folder_name in folder_name_list:
+            new_row = {"文件夹名称": str(folder_name),
+                       }
+            need_df = need_df._append(new_row, ignore_index=True)
+
+        need_df = need_df.fillna(value='')
+
+        # 打开表格并进行匹配
+        _df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        # 去重数据
+        duplicates = _df.duplicated(subset=['文件夹名称'], keep="first")
+        _df = _df.loc[~duplicates]
+        _df = _df.fillna(value='')
+        _df = _df.astype(str)
+
+        # 数据匹配关联,左关联
+        need_df = pd.merge(need_df, _df, on=["文件夹名称"], how="left", indicator=False, )
+        # 补全字段
+        header_list = need_df.columns.values.tolist()
+        for key in keys:
+            if key not in header_list:
+                need_df[key] = ""
+
+        need_df = need_df.fillna(value='')
+        need_df = need_df.astype(str)
+        # 数据转字典
+        return_dict = {}
+        message = ""
+        for index, row in need_df.iterrows():
+            if settings.PROJECT == "红蜻蜓":
+                if row["商品货号"] and row["款号"] and row["编号"]:
+                    return_dict[row["文件夹名称"]] = {
+                        "type": "goods_art_no",
+                        "name": row["文件夹名称"].upper(),
+                        "文件夹名称": row["文件夹名称"],
+                        "template_name": row["模板名称"],
+                        "data": row.to_dict(),
+                    }
+                else:
+                    message = "商品货号、款号、编号必须有值"
+            else:
+                if row["商品货号"] and row["款号"]:
+                    return_dict[row["文件夹名称"]] = {
+                        "type": "goods_art_no",
+                        "name": row["文件夹名称"].upper(),
+                        "文件夹名称": row["文件夹名称"],
+                        "template_name": row["模板名称"],
+                        "data": row.to_dict(),
+                    }
+
+        print("return_dict", return_dict)
+
+        if not return_dict:
+            message += "\n没有找到任何匹配的数据"
+            return {"code": 99, "message": message, "data": return_dict}
+        else:
+            return {"code": 0, "message": message, "data": return_dict}
+
+    def check_goods_is_not_deficiency(self, goods_no_dict: dict):
+        # 数据返回结果为款号列表
+        error_data = {}
+        for key, value in goods_no_dict.items():
+            number = value["货号资料"][0]["编号"].replace("NUM", "")
+            number_list = [x["编号"].replace("NUM", "") for x in value["货号资料"]]
+            all_color_name = [self.get_real_color_name(x["颜色名称"]) for x in value["货号资料"]]  # 本地数据
+            goods_no = key
+
+            _t_goods_number, all_goods_number = self.get_online_data.get_on_goods_all_art(number=number)
+            # all_goods_number------》 [{'number': '14250232', 'goods_art_no': 'AC52001173', 'color': '杏色'}]
+
+            for i in all_goods_number:
+                if i["number"] not in number_list:
+                    # 检查款号+颜色名称是否已经存在
+                    real_color = self.get_real_color_name(i["color"])
+                    if real_color not in all_color_name:
+                        if goods_no not in error_data:
+                            error_data[goods_no] = {"message": "颜色:"}
+                        error_data[goods_no]["message"] += "{}缺失 ".format(real_color)
+
+        return error_data
+
+    def get_basic_template_information(self, _goods_no_dict, excel_path: str):
+        # 本地的文件夹数据形成一个虚拟表格
+        original_df = pd.DataFrame(columns=["款号"])
+        for goods_no in _goods_no_dict:
+            new_row = {"款号": str(goods_no),
+                       "flag": "是"
+                       }
+            original_df = original_df._append(new_row, ignore_index=True)
+        original_df = original_df.fillna(value='')
+
+        # EXCEL表格数据
+        local_df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        values = {'模板名称': "任意", }
+        # 确保所有模板都有名称
+        local_df.fillna(value=values, inplace=True)
+        # 分组后取第一个有效值(即不是NaN的值);此处用于获取某个款号的所有基础信息
+        group_local_df = local_df.groupby(['模板名称', '款号']).agg(
+            lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan)
+        group_local_df = group_local_df.reset_index()
+
+        # -----------数据匹配关联,左关联
+        group_local_df = pd.merge(group_local_df, original_df, on=["款号"], how="left", indicator=False, )
+        # -----------只取有标记的数据
+        group_local_df = group_local_df.loc[~group_local_df['flag'].isnull(), :]
+        group_local_df = group_local_df.loc[group_local_df['模板名称'] != "任意", :]
+        # -----------数据规整
+        group_local_df = group_local_df.fillna(value='')
+        group_local_df = group_local_df.astype(str)
+
+        # 组装成字典
+        temp_goods_no_dict = {}
+        for index, row in group_local_df.iterrows():
+            goods_no = row["款号"]
+            if goods_no not in temp_goods_no_dict:
+                temp_goods_no_dict[goods_no] = {}
+            temp_goods_no_dict[goods_no][row["模板名称"]] = row.to_dict()
+
+        return temp_goods_no_dict
+
+    def check_goods_is_not_deficiency_form_excel(self, goods_no_dict: dict, excel_path: str):
+        # 检查颜色是否齐全,基于Excel处理
+        """
+        步骤:
+        1、goods_no_dict款号生成一个表格(款号+颜色)
+        2、excel表格数据进行款号+颜色去重
+        3、excel表格关联 上述的款号 进行标记,其他删除
+        4、excel表格关联 上述款号与颜色,进行标记
+        5、上述未标记的就是没有的颜色(有对应款号)。
+        6、对对应的款号进行设置为错误数据
+        返回数据结构
+
+        {
+        “A00001--款号”:{“message”:“这个款的错误原因”},
+        “A00002--款号”:{“message”:“这个款的错误原因”},
+        }
+
+        """
+
+        def change_to_int(x):
+            if pd.isna(x):
+                return 0
+            else:
+                try:
+                    x = int(x)
+                    return str(x)
+                except:
+                    return x
+
+        error_data = {}
+        # -----------------goods_no_dict款号生成一个表格(款号+颜色)
+        new_df = pd.DataFrame(columns=['款号', '颜色名称', '款色标记'])
+        for key, value in goods_no_dict.items():
+            goods_no = value["款号"]
+            for color_name in [x["颜色名称"] for x in value["货号资料"]]:
+                new_row = {"款号": goods_no,
+                           "颜色名称": self.get_real_color_name(color_name),
+                           "款色标记": 1,
+                           }
+
+                new_df = new_df._append(new_row, ignore_index=True)
+        new_df.drop_duplicates(subset=['款号', "颜色名称"], keep='first', inplace=True)
+
+        # -------------参考的本地excel表格数据进行款号+颜色去重------------
+        local_df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        local_df.fillna(value={'颜色名称': "", }, inplace=True)
+        local_df = local_df.astype({"颜色名称": str})
+
+        header_list = local_df.columns.values.tolist()
+
+        for key in ["帮高", "鞋宽", "跟高"]:
+            if key in header_list:
+                local_df[key] = local_df[key].apply(change_to_int)
+
+        #     颜色名称规整
+        local_df["颜色名称"] = local_df["颜色名称"].apply(self.get_real_color_name)
+        local_df.drop_duplicates(subset=['款号', "颜色名称"], keep='first', inplace=True)
+
+        # ------------excel表格关联 上述的款号 进行标记,其他删除
+        _df = new_df.drop_duplicates(subset=['款号'], keep='first', inplace=False)
+        _df = _df[["款号"]]
+        _df["对应款号"] = 1
+        local_df = pd.merge(local_df, _df, on=["款号"], how="left", indicator=False, )
+        local_df = local_df.loc[local_df['对应款号'] == 1, :]
+
+        # ------------excel表格关联 上述款号与颜色,进行标记;取没有关联的颜色数据---------
+        local_df = pd.merge(local_df, new_df, on=['款号', "颜色名称"], how="left", indicator=False, )
+        local_df.fillna(value={'款色标记': 0, }, inplace=True)
+        local_df = local_df.loc[local_df['款色标记'] == 0, :]
+        print("local_df")
+        print(local_df)
+
+        # ------------数据分组,对对应的款号进行设置为错误数据---------------
+        grouped_df = local_df.groupby('款号')['颜色名称'].apply(lambda x: '、'.join(x)).reset_index()
+        for index, row in grouped_df.iterrows():
+            error_data[row["款号"]] = {"message": "颜色:{}缺失".format(row["颜色名称"])}
+
+        print("======error_data")
+        print(json.dumps(error_data))
+
+        return error_data
+
+
+class DataModeUploadPic(DataBaseModel):
+
+    def __init__(self, token):
+        super().__init__(token)
+        self.token = token
+        self.get_online_data.refresh_headers(token)
+        self.is_deal_goods_no = []
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        return self.get_online_data.get_goods_art_no_info(numbers_list=numbers_list, goods_art_list=goods_art_list,token=self.token)
+
+    def upload_pic_list_data(self, data):
+        return self.get_online_data.upload_pic_list_data(data, token=self.token)
+
+    def upload_pic(self,goods_data):
+        return self.get_online_data.upload_pic(goods_data, token=self.token)
+
+    def check_is_uploaded(self,goods_art_no=None):
+        return self.get_online_data.check_detail_image(goods_art_no, token=self.token)

+ 176 - 0
python/service/generate_goods_no_detail_pic/detail_func.py

@@ -0,0 +1,176 @@
+import json
+import os
+import settings
+import shutil
+
+
+def get_all_dir_info(image_dir):
+    # 遍历货号获取所有货号--可能为编号
+    folder_name_list = []
+    for folder_name in os.listdir(image_dir):
+        _path = "{}/{}".format(image_dir, folder_name)
+        if not os.path.isdir(_path):
+            continue
+        if "无法对应" in folder_name:
+            continue
+        if "软件" in folder_name:
+            if len(os.listdir(_path)) == 0:
+                # 删除空文件夹
+                os.rmdir(_path)
+            continue
+        folder_name_list.append(folder_name)
+
+    return folder_name_list
+
+
+def get_all_detail_info(image_dir):
+    # 遍历货号获取所有货号--可能为编号
+    _list = []
+    for goods_no in os.listdir(image_dir):
+        goods_art_no_path = "{}/{}".format(image_dir, goods_no)
+        if not os.path.isdir(goods_art_no_path):
+            continue
+        _list.append(goods_no)
+    return _list
+
+
+def get_all_dir_info_and_pic_info(root_path, folder_name_list, need_view_list):
+    # 遍历货号获取所有货号--可能为编号
+    """
+    数据返回为每个文件夹下的文件结构
+    """
+
+    return_data = {}
+    for one_folder in folder_name_list:
+        source_material_path = "{}/{}/阴影图处理".format(root_path, one_folder)
+        data = {"pics": {},
+                "message": "",
+                "800x800": []
+                }
+
+        # source_material_path ===================
+        if not os.path.exists(source_material_path):
+            data["message"] = "缺失 阴影图处理 文件夹"
+        else:
+            if len(os.listdir(source_material_path)) % 2 != 0:
+                data["message"] = "缺失 阴影图处理 图片不为2的倍数"
+            else:
+                # 遍历图片
+                _pic_name_list = []
+                for pic in os.listdir(source_material_path):
+                    _pic = pic.replace(".png", "")
+                    _pic = _pic.split("_")
+                    if len(_pic) == 3:
+                        # 示例:侧视-阴影
+                        data["pics"]["{}-{}".format(_pic[1], _pic[2])] = "{}/{}".format(source_material_path, pic)
+                        _pic_name_list.append(_pic[1])
+
+                # 检查图片是否都有
+                _ = [x for x in need_view_list if x not in _pic_name_list]
+                if _:
+                    _ = ",".join(_)
+                    data["message"] += "\图片{}视角缺失".format(_)
+
+        # 放入商品货号主图 ===================
+        goods_art_800_pic_f = "{}/{}/800x800".format(root_path, one_folder)
+        if not os.path.exists(goods_art_800_pic_f):
+            data["message"] += "\缺失主图文件夹"
+        else:
+            if len(os.listdir(goods_art_800_pic_f)) == 0:
+                data["message"] += "\缺失主图"
+            else:
+                for goods_art_pic in os.listdir(goods_art_800_pic_f):
+                    # 过滤非图片信息
+                    if "jpg" in goods_art_pic or "JPG" in goods_art_pic or "png" in goods_art_pic or "PNG" in goods_art_pic:
+                        data["800x800"].append("{}/{}".format(goods_art_800_pic_f, goods_art_pic))
+
+        # =======================
+        return_data[one_folder] = data
+    print("检查没有问题的文件夹-----------return_data")
+    print(json.dumps(return_data))
+
+    return return_data
+
+
+def merge_local_and_remote_data(all_dir_info_data: dict, remote_data):
+    """
+    all_dir_info_data:文件夹基础数据;文件夹结构数据
+    remote_data:远程数据
+    """
+    print("---all_dir_info_data:")
+    print(json.dumps(all_dir_info_data))
+
+    print("---remote_data:")
+    print(json.dumps(remote_data))
+
+    error_folder_name_list = []
+    for folder_name, value in all_dir_info_data.items():
+        if folder_name not in remote_data:
+            value["message"] = "文件夹没有对应数据信息"
+            error_folder_name_list.append(folder_name)
+        else:
+            value["remote_data"] = remote_data[folder_name]
+
+    # =======开始组装数据
+
+    # 组装数据,按款号进行组装数据
+    goods_no_dict = {}
+    for folder_name, value in all_dir_info_data.items():
+        if "remote_data" not in value:
+            continue
+        if "data" not in value["remote_data"]:
+            continue
+
+        _remote_data = value["remote_data"]["data"]
+        goods_no = _remote_data["款号"]
+        if goods_no not in goods_no_dict:
+            goods_no_dict[goods_no] = {}
+            goods_no_dict[goods_no]["款号"] = goods_no
+            goods_no_dict[goods_no]["货号资料"] = []
+
+        _goods_number = _remote_data["编号"] if "编号" in _remote_data else ""
+        if _goods_number:
+            _goods_number = "NUM{}".format(_goods_number)
+        else:
+            _goods_number = ""
+
+        goods_no_dict[goods_no]["货号资料"].append({"货号": _remote_data["商品货号"],
+                                                "文件夹名称": folder_name,
+                                                "编号": _goods_number,
+                                                "颜色名称": _remote_data["颜色名称"] if "颜色名称" in _remote_data else "",
+                                                "pics": value["pics"],
+                                                "800x800": value["800x800"],
+                                                })
+
+        # 添加款号的基础信息,进行去重处理,此处为动态字段
+        key = ["款号", "商品货号", "编号", "颜色名称", ]
+        for _k, _v in _remote_data.items():
+            if _k not in key:
+                if _k not in goods_no_dict[goods_no]:
+                    goods_no_dict[goods_no][_k] = ""
+                if not goods_no_dict[goods_no][_k]:
+                    if _v:
+                        goods_no_dict[goods_no][_k] = _v
+
+    # ======如果为红蜻蜓,则key需要更改为编号=========
+    # 改款号信息  取任意一个商品的编号作为款号,格式为KNUM00001
+    if settings.PROJECT == "红蜻蜓":
+        _goods_no_dict = {}
+        for key, value in goods_no_dict.items():
+            number = "K{}".format(value["货号资料"][0]["编号"])
+            _goods_no_dict[number] = value
+    else:
+        _goods_no_dict = goods_no_dict
+
+    return _goods_no_dict, error_folder_name_list
+
+
+def move_folders(path_list, target_folder):
+    create_folder(target_folder)
+    for source_folder in path_list:
+        shutil.move(source_folder, target_folder)
+
+
+def create_folder(path):
+    if not os.path.exists(path):
+        os.makedirs(path)

+ 574 - 0
python/service/generate_goods_no_detail_pic/detail_generate_base.py

@@ -0,0 +1,574 @@
+import json
+import os
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+from PIL import Image
+import shutil
+from module.base_mode.base import get_images, check_path, get_image_mask
+from natsort import ns, natsorted
+import math
+from PIL import ImageFont
+import settings
+
+
+class DetailBase(object):
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, excel_data=None,
+                 assigned_page_list=None):
+        self.goods_no = goods_no
+        self.out_put_dir = out_put_dir
+        self.deal_pic_func_list = []
+        self.goods_no_value = goods_no_value
+        self.root = ""
+        self.windows = windows
+        print(goods_no_value)
+        # 重新解析为新的数据结构
+        self.data = {}
+        self.detailed_images = []
+        self.assigned_page_list = assigned_page_list
+        self.overlay_pic_dict = {}
+        self.init()
+        # for goods_art_no_dict in self.goods_no_value["货号资料"]:
+        #     print(goods_art_no_dict)
+        #
+        # raise 1
+
+        if excel_data:
+            ig_keys = ["模板名称"]
+            for k, v in excel_data.items():
+                if k not in ig_keys:
+                    self.goods_no_value[k] = v
+
+    def run_all(self):
+        print("===================detailed_images=================")
+        detailed_images = self.deal_details()
+        self.create_folder(self.out_put_dir)
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(detail_path)
+        self.save_to_png(detailed_images=detailed_images, detail_path=detail_path)
+        # 生成拼接图
+        self.generate_spliced_picture()
+
+        # ------------移动其他图片---------------------
+        # 获取主图模板列表
+        main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)["main_pic_path_list"]
+        if not main_pic_path_list:
+            self.move_other_pic(move_main_pic=True)
+        else:
+            self.move_other_pic(move_main_pic=True)
+            if not self.assigned_page_list:
+                self.deal_all_main_pic()
+            else:
+                if "主图" in self.assigned_page_list:
+                    self.deal_all_main_pic()
+        return True
+
+    # 生成各个详情图切片
+    def deal_details(self):
+        detailed_images = []
+        for index, func in enumerate(self.deal_pic_func_list):
+            if not self.assigned_page_list:
+                self.image_list_append(detailed_images, func())
+            else:
+                index = "{}".format(index + 1)
+                if index in self.assigned_page_list:
+                    self.image_list_append(detailed_images, func())
+                else:
+                    self.image_list_append(detailed_images, {"mes": "不生成"})
+        return [x for x in detailed_images if x]
+
+    # 生成拼接的图片
+    def generate_spliced_picture(self):
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        if not os.path.exists(detail_path):
+            return
+        detailed_images = []
+        for image_data in get_images(detail_path):
+            detailed_images.append(PictureProcessing(image_data["file_path"]))
+        # 生成拼接图
+        img = self.add_pic(detailed_images)
+        join_path = "{out_put_dir}/{goods_no}/拼接图".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(join_path)
+        img.save("{}/1.jpg".format(join_path), format="JPEG")
+
+    def image_list_append(self, image_list: list, data):
+        self.check_state_end()
+        image_list.append(data)
+
+    def save_to_png(self, detailed_images, detail_path):
+        self.check_state_end()
+        for index, pp in enumerate(detailed_images):
+            if isinstance(pp, dict):
+                continue
+            pp.im.save("{}/{}({}).png".format(detail_path, self.goods_no, str(index + 11).zfill(2)))
+
+    def check_state_end(self):
+        if self.windows is not None:
+            if self.windows.state == 99:
+                raise "用户主动取消"
+
+    @classmethod
+    def get_temp_pic_info(cls, root):
+        """
+        获取详情页模板中的信息
+        """
+        main_pic_list = []
+        mask_pic_list = []
+        if os.path.exists(r"{}\main_image".format(root)):
+            for _name in os.listdir(r"{}\main_image".format(root)):
+                _path = r"{}\main_image\{}".format(root, _name)
+                if os.path.isdir(_path):
+                    main_pic_list.append([x["file_path"] for x in get_images(_path)])
+                    mask_pic_list.append(
+                        [x["file_path"] for x in get_image_mask(_path)]
+                    )
+        _l = get_images(r"{}\show".format(root))
+        temp_pic_path = _l[0]["file_path"] if _l else None
+
+        other_pic_list = [x["file_path"] for x in get_images(r"{}".format(root))]
+        return {
+            "main_pic_path_list": main_pic_list,
+            "temp_pic_path": temp_pic_path,
+            "mask_pic_list": mask_pic_list,
+            "other_pic_path_list": other_pic_list,
+        }
+
+    def init(self):
+        for goods_art_no_value in self.goods_no_value["货号资料"]:
+            self.data[goods_art_no_value["货号"]] = {"pics": goods_art_no_value["pics"],
+                                                   "pic_is_deal": {}
+                                                   }
+
+    def get_text_value(self, key, subsection_len=0):
+        text = ""
+        if key in self.goods_no_value:
+            if self.goods_no_value[key]:
+                text = str(self.goods_no_value[key])
+                text = text.replace(r"\n", "\n")
+
+        if key in ["跟高", "鞋宽", "帮高", "脚掌围", "鞋长"]:
+            if text:
+                text = text.split(".")[0]
+
+        if subsection_len != 0:
+            text = text.split("\n")
+            text = [x for x in text if x]
+            if len(text) == 2:
+                text_1 = text[0]
+                text_2 = text[1]
+                return text_1, text_2
+            else:
+                if text:
+                    text_1 = text[0]
+                else:
+                    text_1 = ""
+                text_2 = ""
+                return text_1, text_2
+
+        return text
+
+    def create_folder(self, path):
+        if not os.path.exists(path):
+            os.makedirs(path)
+
+    def get_all_process_pics(self):
+        """
+        获取所有颜色的过程图片
+        data = [
+            {"货号": "",
+             "素材": [{
+                 "名称": "俯视",
+                 "抠图": "路径1",
+                 "阴影": "路径2"
+             }, ]},
+        ]
+        """
+        return_data = []
+        for goods_art_no in self.data:
+            goods_art_no_dict = {"货号": goods_art_no,
+                                 "素材": [],
+                                 }
+
+            # 图片数据重新排序
+            pic_data = []
+            for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+                root_path, file_name = os.path.split(pic_path)
+                pic_data.append(file_name)
+
+            pic_data = natsorted(pic_data, alg=ns.PATH)
+
+            for file_name in pic_data:
+                if "阴影" in file_name:
+                    _, action_name, _ = file_name.split("_")
+                    pic_path = self.data[goods_art_no]["pics"]["{}-阴影".format(action_name)]
+                    pic_cutout_path = self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)]
+                    if os.path.exists(pic_path) and os.path.exists(pic_cutout_path):
+                        goods_art_no_dict["素材"].append({"名称": action_name, "抠图": pic_cutout_path, "阴影": pic_path})
+            return_data.append(goods_art_no_dict)
+
+        return return_data
+
+    def get_overlay_pic_from_dict(self, goods_art_no, color_name, bg_color) -> PictureProcessing:
+        self.check_state_end()
+        # 增加逻辑,获取任意货号下的组合图
+        if "组合" in color_name:
+            goods_art_no, color_name = self.get_all_scene_list(goods_art_no, color_name)
+
+        key = "{}-{}-{}".format(goods_art_no, color_name, bg_color)
+        if key in self.overlay_pic_dict:
+            return self.overlay_pic_dict[key]
+
+        if goods_art_no in self.data:
+            for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+                if "阴影" in pic_name:
+                    action_name = pic_name.replace("-阴影", "")
+                    if action_name == color_name:
+                        pp1 = PictureProcessing(pic_path)
+                        pp2 = PictureProcessing(self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)])
+                        pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(mode="pixel", base="width",
+                                                                                      value=1600)
+                        self.overlay_pic_dict[key] = pp1
+
+        if key in self.overlay_pic_dict:
+            return self.overlay_pic_dict[key]
+
+    def image_init(self, bg_color=(246, 246, 246)):
+        # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
+        for goods_art_no in self.data:
+            for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+                if "阴影" in pic_name:
+                    action_name = pic_name.replace("-阴影", "")
+                    pp1 = PictureProcessing(pic_path)
+                    pp2 = PictureProcessing(self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)])
+                    pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(mode="pixel", base="width",
+                                                                                  value=1600)
+                    self.data[goods_art_no]["pic_is_deal"][action_name] = pp1
+
+    # 获取任意货号的场景图,优先取指定货号;
+    # 调整,按顺序从货号列表中提取所有组合图
+    def get_all_scene_info(self, goods_art_no):
+        data = []
+        # 收集所有组合图
+        # 找任意一个有组合图的货号
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            _goods_art_no = goods_art_no_dict["货号"]
+            _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
+            for _view_name in _view_name_list:
+                if "组合" not in _view_name:
+                    continue
+                return _goods_art_no
+        return goods_art_no
+
+    def get_all_scene_list(self, goods_art_no, view_name: str):
+        if "组合" == view_name:
+            view_name = "组合1"
+        try:
+            view_index = int(view_name.replace("组合", "")) - 1
+        except:
+            return goods_art_no, "无法匹配"
+
+        data = []
+        # 收集所有组合图
+        # 找任意一个有组合图的货号
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            _goods_art_no = goods_art_no_dict["货号"]
+            _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
+            for _view_name in _view_name_list:
+                if "组合" not in _view_name:
+                    continue
+                data.append(
+                    {"goods_art_no": _goods_art_no,
+                     "view_name": _view_name
+                     }
+                )
+
+        if len(data) <= view_index:
+            return goods_art_no, "无法匹配"
+        else:
+            return data[view_index]["goods_art_no"], data[view_index]["view_name"]
+
+    def image_one_pic(self, goods_art_no, name, bg_color=None, return_orign=None):
+        # 增加逻辑,获取任意货号下的组合图
+        if "组合" in name:
+            goods_art_no, name = self.get_all_scene_list(goods_art_no, name)
+
+        # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
+        for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
+            if "阴影" in pic_name:
+                action_name = pic_name.replace("-阴影", "")
+                if name != action_name:
+                    continue
+                pp1 = PictureProcessing(pic_path)
+                pp2 = PictureProcessing(self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)])
+                if not return_orign:
+                    pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(mode="pixel", base="width",
+                                                                                  value=1600)
+                    return pp1
+                else:
+                    return pp1, pp2
+
+        if not return_orign:
+            return None
+        else:
+            return None, None
+
+    def move_other_pic(self, move_main_pic=True):
+        # ------------------------------移动其他图片------------------------------
+        goods_no_main_pic_number = 0
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            if "800x800" not in goods_art_no_dict:
+                continue
+            if not goods_art_no_dict["800x800"]:
+                continue
+
+            goods_art_no = ""
+            if "编号" in goods_art_no_dict:
+                if goods_art_no_dict["编号"]:
+                    goods_art_no = goods_art_no_dict["编号"]
+            if not goods_art_no:
+                goods_art_no = goods_art_no_dict["货号"]
+            # print("goods_art_no:", goods_art_no)
+            # 移动颜色图=====================
+            goods_art_no_f = "{}/{}/{}".format(self.out_put_dir, self.goods_no, goods_art_no)
+            self.create_folder(goods_art_no_f)
+            # 放入一张主图
+            old_pic_path_1 = goods_art_no_dict["800x800"][0]
+            shutil.copy(old_pic_path_1,
+                        "{}/{}{}".format(goods_art_no_f, goods_art_no, os.path.splitext(old_pic_path_1)[1]))
+
+            # 把其他主图放入作为款号图=====================
+            if move_main_pic:
+                for pic_path in goods_art_no_dict["800x800"]:
+                    goods_no_main_pic_number += 1
+                    e = os.path.splitext(pic_path)[1]
+                    shutil.copy(pic_path,
+                                "{out_put_dir}/{goods_no}/{goods_no}({goods_no_main_pic_number}){e}".format(
+                                    out_put_dir=self.out_put_dir, goods_no=self.goods_no,
+                                    goods_no_main_pic_number=str(goods_no_main_pic_number + 10).zfill(2),
+                                    e=e))
+
+    def deal_all_main_pic(self):
+        """
+        处理主图模板,如存在出图模板则进行对应处理
+        """
+        # 获取主图模板列表
+        all_main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)["main_pic_path_list"]
+        if not all_main_pic_path_list:
+            return
+        mask_pic_list = DetailBase.get_temp_pic_info(root=self.root)["mask_pic_list"]
+        data = self.get_all_process_pics()
+        print("========deal_all_main_pic=========主图相关素材:")
+
+        view_list = ["组合", "组合2", "组合3", "组合4", "组合5", "组合6", "俯视", "侧视", "后跟", "鞋底", "内里", ]
+
+        for _index, main_pic_path_list in enumerate(all_main_pic_path_list):
+            self.check_state_end()
+            out_path_root = "{out_put_dir}/{goods_no}/main_image_{_index}".format(
+                out_put_dir=self.out_put_dir,
+                goods_no=self.goods_no,
+                _index=_index
+            )
+            check_path(out_path_root)
+            if mask_pic_list[_index]:
+                mask_pic = mask_pic_list[_index][0]
+            else:
+                mask_pic = None
+            goods_no_main_pic_number = 10
+            # g_index 为第几个颜色货号
+            for g_index, goods_art_no_dict in enumerate(data):
+                goods_art_no = goods_art_no_dict["货号"]
+                # =====================重新指定=================================
+                _material_sort_dict = {}
+                for index, material_dict in enumerate(goods_art_no_dict["素材"]):
+                    name = material_dict["名称"]
+                    _material_sort_dict[name] = material_dict
+
+                # ======================================================
+                file_name_index = -1
+                for view_name in view_list:
+                    # 组合图比较特殊,为全局获取
+                    if g_index != 0:
+                        if "组合" in view_name:
+                            continue
+
+                    if view_name not in _material_sort_dict:
+                        continue
+                    self.check_state_end()
+                    pp_jpg, pp_png = self.image_one_pic(goods_art_no, view_name, bg_color=None, return_orign=True)
+                    if not pp_jpg:
+                        continue
+
+                    file_name_index += 1
+                    # 获取对应主图模板
+                    if len(main_pic_path_list) < file_name_index + 1:
+                        main_pic_path = main_pic_path_list[-1]
+                    else:
+                        main_pic_path = main_pic_path_list[file_name_index]
+                    pp_bg = PictureProcessing(main_pic_path)
+                    original_width = pp_bg.width
+
+                    if original_width != 1600:
+                        pp_bg = pp_bg.resize(value=1600)
+
+                    if mask_pic:
+                        mask_bg = PictureProcessing(mask_pic)
+                        mask_bg = mask_bg.resize(value=1600)
+                        mask_box_im = mask_bg.get_im()
+                        box_size = mask_box_im.getbbox()
+                        result_image = mask_box_im.crop(box_size)
+                        mask_width, mask_height = result_image.size
+                        mask_x, mask_y = box_size[0], box_size[1]
+                    else:
+                        mask_width, mask_height = pp_bg.size
+                        mask_width, mask_height = int(mask_width * 12 / 16), int(mask_height * 12 / 16)
+                        mask_x, mask_y = int((pp_bg.size[0] - mask_width) / 2), int((pp_bg.size[1] - mask_height) / 2)
+
+                    if view_name != "后跟":
+                        pp_jpg = pp_jpg.resize(base_by_box=(mask_width, mask_height))
+                        pp_png = pp_png.resize(base_by_box=(mask_width, mask_height))
+
+                        # 计算粘贴的位置 mask的位置+图片在mask中的位置
+                        p_x = mask_x + int((mask_width - pp_jpg.width) / 2)
+                        p_y = mask_y + int((mask_height - pp_jpg.height) / 2)
+                        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="nw",
+                                                             value=(p_x, p_y), top_png_img=pp_png)
+                    else:
+                        new_mask_width, new_mask_height = int(mask_width / 1.6), int(mask_height / 1.6)
+
+                        pp_jpg = pp_jpg.resize(base_by_box=(new_mask_width, new_mask_height))
+                        pp_png = pp_png.resize(base_by_box=(new_mask_width, new_mask_height))
+                        new_mask_x = int((mask_width - new_mask_width) / 2 + mask_x)
+                        new_mask_y = int((mask_height - new_mask_height) / 2 + mask_y)
+
+                        # 计算粘贴的位置 mask的位置+图片在mask中的位置
+                        p_x = new_mask_x + int((new_mask_width - pp_jpg.width) / 2)
+                        p_y = new_mask_y + int((new_mask_height - pp_jpg.height) / 2)
+
+                        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="nw", value=(p_x, p_y),
+                                                             top_png_img=pp_png)
+
+                    goods_no_main_pic_number += 1
+                    out_pic_path = "{out_path_root}/{goods_no}({goods_no_main_pic_number}){pic_mode}".format(
+                        out_path_root=out_path_root,
+                        goods_no=self.goods_no,
+                        goods_no_main_pic_number=goods_no_main_pic_number,
+                        pic_mode=settings.OUT_PIC_MODE,
+                    )
+
+                    if settings.OUT_PIC_FACTOR > 1.0:
+                        print("图片锐化处理")
+                        pp_bg = pp_bg.sharpen_image(factor=settings.OUT_PIC_FACTOR)
+
+                    if original_width < 1600:
+                        pp_bg = pp_bg.resize(value=original_width)
+
+                    print("392  out_pic_path", out_pic_path)
+                    if settings.OUT_PIC_MODE == ".jpg":
+                        pp_bg.save_as_rgb(out_pic_path)
+                    else:
+                        pp_bg.save_as_png(out_pic_path)
+
+    def add_pic(self, detailed_images):
+        self.check_state_end()
+        detailed_images = [x for x in detailed_images if x]
+        if not detailed_images:
+            return
+        page_len = 0
+
+        for index, pp in enumerate(detailed_images):
+            page_len += pp.height
+
+        bg_im = Image.new("RGB", (pp.width, page_len), (255, 255, 255))
+        n = 0
+
+        for index, pp in enumerate(detailed_images):
+            bg_im.paste(pp.im, (0, n))
+            n += pp.height
+        return bg_im
+
+    # 通用方法,用于写文字
+    def add_text_list(self, text_list, spacing=5, base="wn"):
+        text_list = [x for x in text_list if x["text"]]
+        # print(text_list)
+        # spacing 行间距
+        text_image_list = []
+        max_w = 0
+        total_h = 0
+
+        for text_data in text_list:
+            _pp = PictureProcessing("RGBA", (1200, 800), (255, 255, 255, 0))
+            if base == "wn" or base == "nw":
+                align = "left"
+                anchor = None
+                value = (0, 200)
+            if base == "cn" or base == "nc":
+                align = "center"
+                anchor = "mm"
+                value = (600, 200)
+            if base == "en" or base == "ne":
+                align = "right"
+                anchor = "rs"
+                value = (1200, 200)
+
+            _pp = _pp.get_text_image_advanced(
+                value=value,
+                font=text_data["font"],
+                text=text_data["text"],
+                align=align,
+                anchor=anchor,
+                spacing=5,
+                fill=text_data["fill"],
+                return_mode="min_image",
+                margins=(0, 0, 0, 0)
+            )
+            text_image_list.append(_pp)
+            if _pp.width > max_w:
+                max_w = _pp.width
+
+            total_h += _pp.height
+            if "spacing" in text_data:
+                total_h += text_data["spacing"]
+
+        if not text_image_list:
+            return None
+        #
+        bg = PictureProcessing("RGBA", (max_w, total_h * 3), (0, 0, 0, 0))
+        y = 0
+        for text_image, text_data in zip(text_image_list, text_list):
+            bg = bg.paste_img(top_img=text_image, value=(0, y), base=base)
+            y += spacing + text_image.height
+            if "spacing" in text_data:
+                y += text_data["spacing"]
+
+        bg = bg.crop(mode="min")
+        # _ = bg.paste_img_invert(top_img=PictureProcessing("RGB", (bg.width,bg.height), (255, 255, 255)))
+        # _.show()
+        return bg
+
+    def generate_font_list_to_pic(self):
+
+        font_path_list = [r"resources\ttf\puhui\Bold.ttf",
+                          r"resources\ttf\puhui\Medium.ttf",
+                          r"resources\ttf\puhui\Heavy.ttf",
+                          r"resources\ttf\puhui\Light.ttf",
+                          r"resources\ttf\puhui\Regular.ttf",
+                          ]
+        text_v_list = [
+            "这是一段话Bold",
+            "这是一段话Medium",
+            "这是一段话Heavy",
+            "这是一段话Light",
+            "这是一段话Regular",
+        ]
+        detailed_images = []
+        for font_path, text in zip(font_path_list, text_v_list):
+            text_list = []
+            for size in range(26, 80, 2):
+                font = ImageFont.truetype(font_path, size)
+                text_list.append({"text": "{}-字号{}".format(text, size),
+                                  "font": font,
+                                  "fill": (110, 110, 110),
+                                  })
+            text_image = self.add_text_list(text_list, spacing=15, base="nw")
+            text_image = text_image.crop(mode="min")
+            text_image = text_image.paste_img_invert(top_img=PictureProcessing("RGB", text_image.size, (255, 255, 255)))
+            detailed_images.append(text_image)
+        return PictureProcessing(im=self.add_pic(detailed_images))

+ 206 - 0
python/service/generate_goods_no_detail_pic/detail_hlm.py

@@ -0,0 +1,206 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+
+class DetailPicGetHLM(DetailBase):
+    need_view = ["俯视","侧视","后跟","鞋底"]
+
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, ):
+        super().__init__(goods_no, goods_no_value, out_put_dir)
+        if __name__ == '__main__':
+            self.root = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo"
+        else:
+            self.root = os.getcwd()
+
+        self.detailed_images = []
+        self.run()
+
+    def run(self):
+        detailed_images = self.deal_details()
+
+        self.create_folder(self.out_put_dir)
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(detail_path)
+        join_path = "{out_put_dir}/{goods_no}/拼接图".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(join_path)
+
+        for index, pp in enumerate(detailed_images):
+            pp.im.save("{}/{}({}).jpg".format(detail_path, self.goods_no, str(index + 11).zfill(2)), format="JPEG")
+
+        img = self.add_pic(detailed_images)
+        # img.show()
+        img.save("{}/1.jpg".format(join_path), format="JPEG")
+
+        # ------------移动其他图片---------------------
+        self.move_other_pic()
+        return True
+
+    def deal_details(self):
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        return [x for x in detailed_images if x]
+
+    def deal_pic_1(self):
+        bg_color = (246, 246, 246)
+        """   制作主图  """
+        detailed_images = []
+        pp_0 = PictureProcessing(r"{}\resources\detail_image_3\image (1).jpg".format(self.root))
+        detailed_images.append(pp_0)
+
+        goods_art_no_list = list(self.data.keys())
+
+        pp_1 = self.data[goods_art_no_list[0]]["pic_is_deal"]["俯视"]
+        pp_image = pp_1.resize(mode="relative",
+                               base="by_im",
+                               base_im=PictureProcessing("RGB", (604, 418), (248, 248, 248)))
+
+        pp_image = PictureProcessing("RGB", (604, 418), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = PictureProcessing("RGB", (1200, 918), (255, 255, 255)).paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 26))
+
+        # bg_img.show()
+
+        #   ---------- 第二张图粘贴
+        pp_2 = self.data[goods_art_no_list[0]]["pic_is_deal"]["侧视"]
+        pp_image = pp_2.resize(mode="relative",
+                               base="by_im",
+                               base_im=PictureProcessing("RGB", (604, 418), (248, 248, 248)),)
+
+        pp_image = PictureProcessing("RGB", (604, 418), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = bg_img.paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 26 + 26 + 418))
+
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = [("商品款号", self.goods_no_value["款号"], 700, 297),
+                     ("商品面料", self.goods_no_value["商品面料"], 700, 380),
+                     ("商品内里", self.goods_no_value["商品内里"], 700, 469),
+                     ("商品鞋底", self.goods_no_value["商品鞋底"], 700, 549), ]
+
+        for i in text_list:
+            bg_img = bg_img.add_text(mode="pixel",
+                                     value=(i[2], i[3]),
+                                     font=font,
+                                     text="{}:{}".format(i[0], i[1]),
+                                     align="center",
+                                     spacing=10,
+                                     fill=(17, 16, 16))
+        detailed_images.append(bg_img)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        # 尺码表
+        pp_1 = PictureProcessing(r"{}\resources\detail_image_3\image (2).jpg".format(self.root))
+        return pp_1
+
+    def deal_pic_3(self):
+        # 细节图展示
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+        pp_1 = PictureProcessing(r"{}\resources\detail_image_3\image (3).jpg".format(self.root))
+        detailed_images.append(pp_1)
+        goods_art_no_list = list(self.data.keys())
+        view_dict = {"俯视": {"crop_img_value": (0, 0, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     "侧视": {"crop_img_value": (0, 0, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     }
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "后跟", "鞋底"]
+            else:
+                name_list = ["俯视", "侧视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                    continue
+
+                _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+                _pp = _pp.resize(mode="pixel", base="width", value=1800 if _name != "后跟" else 1200,)
+                if _name in view_dict:
+                    value = view_dict[_name]["crop_img_value"]
+                else:
+                    value = (0, 0, 1086, 752)
+
+                _pp = _pp.crop_img(mode="pixel", base="cc", value=value, color_fill=bg_color)
+                # _pp.show()
+
+                _pp = _pp.radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255,255,255))
+                bg_img = bg_img.paste_img(mode="pixel", top_img=_pp, base="center")
+                detailed_images.append(bg_img)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # 制作角度展示图
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "后跟", "鞋底"]
+            else:
+                name_list = ["俯视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                    continue
+                _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+                _pp = _pp.resize(mode="pixel", base="width", value=600 if _name == "后跟" else 1058, )
+
+                bg_pp = PictureProcessing("RGB", (1200, int(_pp.height + 50)), bg_color)
+                bg_pp = bg_pp.paste_img(mode="pixel", top_img=_pp, base="cc", value=(0, 0))
+                detailed_images.append(bg_pp)
+                # 分割线
+                _p = PictureProcessing("RGB", (1200, 50), (255, 255, 255))
+                detailed_images.append(_p)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+
+if __name__ == '__main__':
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data2.txt",
+            "r", encoding="utf-8") as f:
+        data = json.loads(f.read())
+
+    n = 0
+    for goods_no, value in data.items():
+        n+=1
+        if n != 1111:
+            d = DetailPicGetHLM(goods_no, value, out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\out_put\测试文件夹\详情图生成")
+            raise 1

+ 221 - 0
python/service/generate_goods_no_detail_pic/detail_hlm_2.py

@@ -0,0 +1,221 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+
+class DetailPicGetHLM2(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\huilima\1".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None,**kwargs):
+        super().__init__(goods_no, goods_no_value, out_put_dir,windows=windows)
+        self.root = r"{}\resources\detail_temp\huilima\1".format(os.getcwd())
+        self.run()
+
+    def run(self):
+        detailed_images = self.deal_details()
+
+        self.create_folder(self.out_put_dir)
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(detail_path)
+        join_path = "{out_put_dir}/{goods_no}/拼接图".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(join_path)
+
+        for index, pp in enumerate(detailed_images):
+            pp.im.save("{}/{}({}).jpg".format(detail_path, self.goods_no, str(index + 11).zfill(2)), format="JPEG")
+
+        img = self.add_pic(detailed_images)
+        # img.show()
+        img.save("{}/1.jpg".format(join_path), format="JPEG")
+
+        # ------------移动其他图片---------------------
+        self.move_other_pic()
+        return True
+
+    def deal_details(self):
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        return [x for x in detailed_images if x]
+
+    def deal_pic_1(self):
+        bg_color = (246, 246, 246)
+        """   制作主图  """
+        detailed_images = []
+        pp_0 = PictureProcessing(r"{}\image (1).jpg".format(self.root))
+        detailed_images.append(pp_0)
+
+        goods_art_no_list = list(self.data.keys())
+
+        pp_1 = self.data[goods_art_no_list[0]]["pic_is_deal"]["俯视"]
+        pp_image = pp_1.resize(mode="relative",
+                               base="by_im",
+                               base_im=PictureProcessing("RGB", (604, 418), (248, 248, 248)))
+
+        pp_image = PictureProcessing("RGB", (604, 418), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = PictureProcessing("RGB", (1200, 918), (255, 255, 255)).paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 26))
+
+        # bg_img.show()
+
+        #   ---------- 第二张图粘贴
+        pp_2 = self.data[goods_art_no_list[0]]["pic_is_deal"]["侧视"]
+        pp_image = pp_2.resize(mode="relative",
+                               base="by_im",
+                               base_im=PictureProcessing("RGB", (604, 418), (248, 248, 248)), )
+
+        pp_image = PictureProcessing("RGB", (604, 418), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = bg_img.paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 26 + 26 + 418))
+
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = [("商品款号", self.goods_no_value["款号"], 700, 297),
+                     ("商品面料", self.goods_no_value["商品面料"], 700, 380),
+                     ("商品内里", self.goods_no_value["商品内里"], 700, 469),
+                     ("商品鞋底", self.goods_no_value["商品鞋底"], 700, 549), ]
+
+        for i in text_list:
+            bg_img = bg_img.add_text(mode="pixel",
+                                     value=(i[2], i[3]),
+                                     font=font,
+                                     text="{}:{}".format(i[0], i[1]),
+                                     align="center",
+                                     spacing=10,
+                                     fill=(17, 16, 16))
+        detailed_images.append(bg_img)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        # 尺码表
+        pp_1 = PictureProcessing(r"{}\image (2).jpg".format(self.root))
+        return pp_1
+
+    def deal_pic_3(self):
+        # 细节图展示
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        view_dict = {"俯视": {"crop_img_value": (-77, 401, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     "侧视": {"crop_img_value": (0, 0, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     }
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "鞋底", "内里"]
+            else:
+                name_list = ["俯视", "侧视", ]
+
+            # 第一个颜色
+            if "俯视" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "俯视", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1900)
+                       .crop_img(mode="pixel", base="sw", value=(-77, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "侧视" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "侧视", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1733)
+                       .crop_img(mode="pixel", base="wc", value=(715, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "鞋底" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "鞋底", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1733)
+                       .crop_img(mode="pixel", base="wc", value=(715, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "内里" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "内里", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1663)
+                       .crop_img(mode="pixel", base="wc", value=(-40, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # 制作角度展示图
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "后跟", "鞋底"]
+            else:
+                name_list = ["俯视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                    continue
+                _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+                _pp = _pp.resize(mode="pixel", base="width", value=600 if _name == "后跟" else 1058, )
+
+                bg_pp = PictureProcessing("RGB", (1200, int(_pp.height + 50)), bg_color)
+                bg_pp = bg_pp.paste_img(mode="pixel", top_img=_pp, base="cc", value=(0, 0))
+                detailed_images.append(bg_pp)
+                # 分割线
+                _p = PictureProcessing("RGB", (1200, 50), (255, 255, 255))
+                detailed_images.append(_p)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        return PictureProcessing(r"{}\image (3).jpg".format(self.root))

+ 228 - 0
python/service/generate_goods_no_detail_pic/detail_hlm_3.py

@@ -0,0 +1,228 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+
+class DetailPicGetHLM3(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\huilima\1".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None,**kwargs):
+        super().__init__(goods_no, goods_no_value, out_put_dir,windows=windows)
+        self.root = r"{}\resources\detail_temp\huilima\1".format(os.getcwd())
+        self.run()
+
+    def run(self):
+        detailed_images = self.deal_details()
+
+        self.create_folder(self.out_put_dir)
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(detail_path)
+        join_path = "{out_put_dir}/{goods_no}/拼接图".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(join_path)
+
+        for index, pp in enumerate(detailed_images):
+            pp.im.save("{}/{}({}).jpg".format(detail_path, self.goods_no, str(index + 11).zfill(2)), format="JPEG")
+
+        img = self.add_pic(detailed_images)
+        # img.show()
+        img.save("{}/1.jpg".format(join_path), format="JPEG")
+
+        # ------------移动其他图片---------------------
+        self.move_other_pic()
+        return True
+
+    def deal_details(self):
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        # detailed_images.append(self.deal_pic_2())
+        # detailed_images.append(self.deal_pic_3())
+        # detailed_images.append(self.deal_pic_4())
+        # detailed_images.append(self.deal_pic_5())
+        return [x for x in detailed_images if x]
+
+    def deal_pic_1(self):
+        bg_color = (255, 255, 255)
+        goods_art_no_list = list(self.data.keys())
+        page_width = 1200
+        baseWidth = page_width / 1.4
+        pp_list_1 = []
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            pp_jpg1, pp_png1 = self.image_one_pic(
+                goods_art_no=goods_art_no, name="俯视", return_orign=True
+            )
+            pp_jpg1 = pp_jpg1.resize(value=baseWidth)
+            pp_png1 = pp_png1.resize(value=baseWidth)
+            maxHeight = pp_jpg1.height
+            overlay_bg1 = PictureProcessing(
+                "RGB",
+                (
+                    pp_jpg1.width,
+                    maxHeight,
+                ),
+                bg_color,
+            )
+            overlay_bg1 = overlay_bg1.to_overlay_pic_advance(
+                mode="pixel",
+                top_img=pp_jpg1,
+                base="cc",
+                value=(0, 0),
+                top_png_img=pp_png1,
+            )
+            pp_list_1.append(overlay_bg1)
+        _pp_jpg = PictureProcessing()
+        rows = 1
+        _pp_jpg = _pp_jpg.horizontal_distribution(
+            pp_list=pp_list_1,
+            bg_width=page_width,
+            margins=(0, 0, 0, 0),
+            line_spacing=100,
+            number_per_row=rows,
+        )
+        overlay_top = PictureProcessing(
+            "RGB",
+            (
+                page_width,
+                100,
+            ),
+            (255, 255, 255),
+        )
+        overlay_bottom = PictureProcessing(
+            "RGB",
+            (
+                page_width,
+                100,
+            ),
+            (255, 255, 255),
+        )
+        detailed_images = []
+        # 其他图片
+        detailed_images.append(overlay_top)
+        detailed_images.append(_pp_jpg)
+        detailed_images.append(overlay_bottom)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        # 尺码表
+        pp_1 = PictureProcessing(r"{}\image (2).jpg".format(self.root))
+        return pp_1
+
+    def deal_pic_3(self):
+        # 细节图展示
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        view_dict = {"俯视": {"crop_img_value": (-77, 401, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     "侧视": {"crop_img_value": (0, 0, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     }
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "鞋底", "内里"]
+            else:
+                name_list = ["俯视", "侧视", ]
+
+            # 第一个颜色
+            if "俯视" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "俯视", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1900)
+                       .crop_img(mode="pixel", base="sw", value=(-77, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "侧视" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "侧视", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1733)
+                       .crop_img(mode="pixel", base="wc", value=(715, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "鞋底" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "鞋底", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1733)
+                       .crop_img(mode="pixel", base="wc", value=(715, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+            if "内里" in name_list:
+                _pp = self.image_one_pic(goods_art_no, "内里", bg_color=bg_color)
+                if _pp is None:
+                    continue
+
+                bg_img = PictureProcessing("RGBA", (1200, 848), (255, 255, 255))
+                _pp = (_pp.resize(value=1663)
+                       .crop_img(mode="pixel", base="wc", value=(-40, 0, 1086, 752), color_fill=bg_color)
+                       .radius(circular_pos=(1, 1, 1, 1), mode="relative", value=100)
+                       .paste_img_invert(mode="pixel", top_img=bg_img, base="center")
+                       )
+                detailed_images.append(_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # 制作角度展示图
+        bg_color = (246, 246, 246)
+
+        detailed_images = []
+
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "后跟", "鞋底"]
+            else:
+                name_list = ["俯视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                    continue
+                _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+                _pp = _pp.resize(mode="pixel", base="width", value=600 if _name == "后跟" else 1058, )
+
+                bg_pp = PictureProcessing("RGB", (1200, int(_pp.height + 50)), bg_color)
+                bg_pp = bg_pp.paste_img(mode="pixel", top_img=_pp, base="cc", value=(0, 0))
+                detailed_images.append(bg_pp)
+                # 分割线
+                _p = PictureProcessing("RGB", (1200, 50), (255, 255, 255))
+                detailed_images.append(_p)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        return PictureProcessing(r"{}\image (3).jpg".format(self.root))

+ 325 - 0
python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie.py

@@ -0,0 +1,325 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+
+class DetailPicGetXiaoSuShuoXie(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\1".format(os.getcwd())
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False,excel_data=None,assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows,excel_data=excel_data,assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\1".format(os.getcwd())
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+        ]
+
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir,self.goods_no, format="JPEG"))
+
+    def deal_pic_1(self):
+        bg_color = (239, 237, 238)
+        """   制作主图  """
+        detailed_images = [] 
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = []
+        for _set in [("【品    牌】", "品牌"),
+                     ("【货    号】", "款号"),
+                     ("【鞋面材质】", "鞋面材质"),
+                     ("【系列风格】", "系列风格"),
+                     ("【内里材质】", "内里材质"),
+                     ("【尺    码】", "尺码"),
+                     ("【鞋底材质】", "鞋底材质"),
+                     ("【品牌编码】", "品牌编码"),
+                     ]:
+            title, key = _set
+            text = self.get_text_value(key)
+            if text:
+                text_list.append(
+                    (title, text)
+                )
+
+        # 文字排列
+        pp_bg = PictureProcessing(r"{}\bg-1.jpg".format(self.root))
+        x = 101
+        y = 98
+        for _set in text_list:
+            pp_bg = pp_bg.add_text(mode="pixel",
+                                   value=(x, y),
+                                   font=font,
+                                   text="{}:{}".format(_set[0], _set[1]),
+                                   align="left",
+                                   spacing=10,
+                                   fill=(17, 16, 16))
+            x += 588
+            if x > 700:
+                x = 101
+                y += 94
+
+        # ----------主图放置
+        y += 10
+        goods_art_no_list = list(self.data.keys())
+
+        pp_jpg, pp_png = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="俯视", return_orign=True)
+        pp_jpg = pp_jpg.resize(value=1083)
+        pp_png = pp_png.resize(value=1083)
+        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="nc", value=(0, y), top_png_img=pp_png)
+
+        y += pp_jpg.height
+        y += 20
+
+        # ----------配图粘贴
+        if len(goods_art_no_list) > 1:
+            # 大于一个颜色做颜色图展示
+
+            pp_list_1,pp_list_2 = [],[]
+            for goods_art_no in goods_art_no_list:
+                pp_jpg, pp_png = self.image_one_pic(goods_art_no=goods_art_no, name="侧视", return_orign=True)
+                pp_jpg = pp_jpg.resize(value=400)
+                pp_png = pp_png.resize(value=400)
+                pp_list_1.append(pp_jpg)
+                pp_list_2.append(pp_png)
+
+            _pp_jpg = PictureProcessing()
+            _pp_png = PictureProcessing()
+            _pp_jpg = _pp_jpg.horizontal_distribution(pp_list=pp_list_1, bg_width=1200, margins=(0, 0, 0, 0), line_spacing=0,
+                                                  number_per_row=3)
+            _pp_png = _pp_png.horizontal_distribution(pp_list=pp_list_2, bg_width=1200, margins=(0, 0, 0, 0), line_spacing=0,
+                                                  number_per_row=3)
+
+            pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=_pp_jpg, base="nc", value=(0, y),
+                                                 top_png_img=_pp_png)
+
+            y += _pp_jpg.height
+            y += 30
+
+        # ------多余的剪裁
+        if y < pp_bg.height:
+            # print("触发剪裁")
+            pp_bg = pp_bg.crop_img(value=(0, 0, pp_bg.width, y))
+
+        return pp_bg
+
+    def deal_pic_2(self):
+        """
+        细节解析
+
+        """
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        font_2 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 15)
+        bg_color = (220, 220, 220)
+        detailed_images = []
+
+        goods_art_no_list = list(self.data.keys())
+        # ------侧视图-------------
+        pp_2 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="侧视", bg_color=bg_color)
+        pp_2 = pp_2.resize(mode="pixel", base="width", value=1327)
+        pp_2 = PictureProcessing("RGB", (550, 550), bg_color).paste_img(mode="pixel", top_img=pp_2,
+                                                                        base="wc", value=(-90, 0))
+        # 文字
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 24),
+                             font=font_1,
+                             text="优质质感鞋面",
+                             align="left",
+                             spacing=10,
+                             fill=(28, 28, 28))
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 76),
+                             font=font_2,
+                             text="颇具层次",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+        pp_2 = pp_2.add_text(mode="pixel",
+                             value=(21, 115),
+                             font=font_2,
+                             text="时尚质感出众",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        # ------后跟-------------
+        pp_3 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="后跟", bg_color=bg_color)
+        pp_3 = pp_3.resize(mode="pixel", base="width", value=625)
+        pp_3 = PictureProcessing("RGB", (550, 550), bg_color).paste_img(mode="pixel", top_img=pp_3,
+                                                                        base="cc", value=(0, 0))
+        # 文字
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35 * 3),
+                             font=font_2,
+                             text="颇具层次",
+                             align="right",
+                             spacing=10,
+                             anchor="rs",
+                             fill=(78, 78, 78))
+
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35 * 2),
+                             font=font_2,
+                             text="时尚质感出众",
+                             align="right",
+                             anchor="rs",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        pp_3 = pp_3.add_text(mode="pixel",
+                             value=(pp_3.width - 21, pp_3.height - 35),
+                             font=font_1,
+                             text="设计感十足",
+                             anchor="rs",
+                             align="right",
+                             spacing=10,
+                             fill=(28, 28, 28))
+
+        # ---------------鞋底图
+        pp_4 = self.image_one_pic(goods_art_no=goods_art_no_list[0], name="鞋底", bg_color=bg_color)
+        pp_4 = pp_4.resize(mode="pixel", base="width", value=1635)
+        pp_4 = PictureProcessing("RGB", (1134, 625), bg_color).paste_img(mode="pixel", top_img=pp_4,
+                                                                         base="wc", value=(198, 0))
+        # 文字
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 451),
+                             font=font_1,
+                             text="防滑耐磨鞋底",
+                             align="left",
+                             spacing=10,
+                             fill=(28, 28, 28))
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 510),
+                             font=font_2,
+                             text="优质材质",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+        pp_4 = pp_4.add_text(mode="pixel",
+                             value=(21, 549),
+                             font=font_2,
+                             text="防滑网纹设计坚固耐磨",
+                             align="left",
+                             spacing=10,
+                             fill=(78, 78, 78))
+
+        new_bg = PictureProcessing("RGB", (1200, 40 + pp_2.height + 40 + pp_4.height + 40), (239, 239, 239))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_2, base="nw", value=(33, 40))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_3, base="nw", value=(33 + 550 + 33, 40))
+        new_bg = new_bg.paste_img(mode="pixel", top_img=pp_4, base="nw", value=(33, 40 + pp_2.height + 40))
+        detailed_images.append(new_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        # 设计理念
+        bg = PictureProcessing(r"{}\1 (1).jpg".format(self.root))
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 40)
+        # 粘贴文字
+        text = self.get_text_value("设计理念")
+        if not text:
+            text = "告别枯燥沉闷日常还原自然本身色彩\n融合工装、复古、运动元素随性出走感受自在的步伐"
+
+        bg = bg.add_text(mode="pixel",
+                             value=(164, 328),
+                             font=font_1,
+                             text=text,
+                             align="left",
+                             spacing=15,
+                             fill=(39, 39, 39))
+
+
+        return bg
+
+    def deal_pic_4(self):
+        """   各个颜色细节展示  """
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        font_1 = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            # ------俯视图-------------
+            pp_1 = self.image_one_pic(goods_art_no=goods_art_no, name="俯视", bg_color=bg_color)
+            pp_1 = pp_1.resize(mode="pixel", base="width", value=956)
+            pp_1 = PictureProcessing("RGB", (1200, 40 + pp_1.height + 40), bg_color).paste_img(mode="pixel",
+                                                                                               top_img=pp_1,
+                                                                                               base="cc", value=(0, 0))
+            # 粘贴文字与logo
+            pp_1 = pp_1.add_text(mode="pixel",
+                                 value=(51, 26),
+                                 font=font_1,
+                                 text=color_name,
+                                 align="left",
+                                 spacing=10,
+                                 fill=(28, 28, 28))
+            pp_1 = pp_1.paste_img(mode="pixel", top_img=PictureProcessing(
+                r"{}\1 (4).jpg".format(self.root)), base="nw", value=(51, 72))
+
+            # ------后跟-------------
+            pp_2 = self.image_one_pic(goods_art_no=goods_art_no, name="后跟", bg_color=bg_color)
+            pp_2 = pp_2.resize(mode="pixel", base="width", value=345)
+            pp_2 = PictureProcessing("RGB", (537, 356), bg_color).paste_img(mode="pixel", top_img=pp_2,
+                                                                            base="cc", value=(0, 0))
+            # ------内里-------------
+            pp_3 = self.image_one_pic(goods_art_no=goods_art_no, name="内里", bg_color=bg_color)
+            pp_3 = pp_3.resize(mode="pixel", base="width", value=577)
+            pp_3 = PictureProcessing("RGB", (537, 356), bg_color).paste_img(mode="pixel", top_img=pp_3,
+                                                                            base="cc", value=(0, 0))
+            bg_pp = PictureProcessing("RGB", (1200, 20 + pp_1.height + 20 + pp_2.height + 20), bg_color)
+            # 粘贴俯视图
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_1, base="nw", value=(0, 0))
+            # 粘贴后跟
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_2, base="nw", value=(51, 20 + pp_1.height + 20))
+            # 粘贴内里
+            bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp_3, base="nw", value=(588, 20 + pp_1.height + 20))
+
+            detailed_images.append(bg_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        # 其他图片
+        detailed_images = []
+        detailed_images.append(
+            PictureProcessing(r"{}\1 (2-1).jpg".format(self.root)))
+        detailed_images.append(
+            PictureProcessing(r"{}\1 (3).jpg".format(self.root)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+
+if __name__ == '__main__':
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data3.txt",
+            "r", encoding="utf-8") as f:
+        data = json.loads(f.read())
+
+    for goods_no, value in data.items():
+        d = DetailPicGetXiaoSuShuoXie(goods_no, value,
+                                      out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-11-19\软件-详情图生成")
+        raise 1

+ 612 - 0
python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie2.py

@@ -0,0 +1,612 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+
+import os
+from PIL import ImageFont, Image, ImageDraw
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import (
+    DetailBase,
+)
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+import math
+
+
+class DetailPicGetXiaoSuShuoXie2(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\2".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\2".format(os.getcwd())
+        self.base_bg_color = (235, 234, 234)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+        ]
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        detailed_images.append(self.deal_pic_6())
+        detailed_images.append(self.deal_pic_7())
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+
+
+    def deal_pic_1(self):
+        """   制作主图  """
+        detailed_images = []
+        # -------粘贴文字-------
+        pp_bg = PictureProcessing(r"{}\template_1.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 35)
+        # font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 35)
+
+        t1_title = self.get_text_value("标题")
+        t1_sub_title = self.get_text_value("副标题")
+        # t1_sub_title = "副标题副标题副标题副标题副标题\n副标题副标题副标题副标题\n副标题副标题副标题"
+        base_bg_color = (236, 235, 235)
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), base_bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(50, 10),
+                font=font_1,
+                text=t1_title,
+                align="left",
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(0, 0, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), base_bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(50, 10),
+                font=font_2,
+                text=t1_sub_title,
+                align="left",
+                spacing=10,
+                fill=(32, 32, 32),
+                return_mode="min_image_high",
+                margins=(0, 0, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB",(pp_bg.width,150),self.base_bg_color))
+
+        # ================添加图片
+        goods_art_no_list = list(self.data.keys())
+        _zuhe_pic_list = []
+        color_name_list = ["组合", "组合2", "组合3"]
+        for color_name in color_name_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color,
+            )
+            if not pp_1:
+                continue
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        if not _zuhe_pic_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name="俯视",
+                bg_color=self.base_bg_color,
+            )
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        detailed_images.extend(_zuhe_pic_list)
+
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB",(pp_bg.width,150),self.base_bg_color))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        """
+        细节解析
+
+        """
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_2.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 30)
+
+        t1_title = self.get_text_value("提示1主标题")
+        t1_sub_title = self.get_text_value("提示1副标题")
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), (249, 249, 249))
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(1200 - 80, 45),
+                font=font_1,
+                text=t1_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), (249, 249, 249))
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(1200 - 80, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        # ----------主图放置
+        goods_art_no_list = list(self.data.keys())
+        # ---------------鞋底图
+        bg_color = (249, 249, 249)
+        pp_jpg = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="鞋底",
+            bg_color=bg_color
+        )
+        pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_jpg.height + 100), bg_color),
+                                         base="nc",
+                                         value=(400, 00)
+                                         )
+        detailed_images.append(pp_jpg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_4.png".format(self.root))
+        detailed_images.append(pp_bg)
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 30)
+
+        t1_title = self.get_text_value("提示2主标题")
+        t1_sub_title = self.get_text_value("提示2副标题")
+        bg_color = (252, 252, 252)
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(80, 45),
+                font=font_1,
+                text=t1_title,
+                align="left",
+                anchor=None,
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(80, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="left",
+                anchor=None,
+                spacing=10,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        # ----------主图放置
+        goods_art_no_list = list(self.data.keys())
+        # ---------------鞋底图
+
+        pp_jpg = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="侧视",
+            bg_color=bg_color
+        )
+        pp_jpg = pp_jpg.resize(value=1686)
+        pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_jpg.height), bg_color),
+                                         base="ec",
+                                         value=(0, 0)
+                                         )
+        # pp_jpg = pp_jpg.resize(value=1000)
+        # pp_jpg = pp_jpg.paste_img_invert(top_img=PictureProcessing("RGB",(1200,pp_jpg.height+150),bg_color),
+        #                                  base="wc",
+        #                                  value=(0,0)
+        #                                  )
+        detailed_images.append(pp_jpg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        """各个颜色细节展示"""
+        bg_color = (235, 235, 235)
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_5.png".format(self.root))
+        detailed_images.append(pp_bg)
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 60)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Light.ttf", 30)
+
+        t1_title = self.get_text_value("提示3主标题")
+        t1_sub_title = self.get_text_value("提示3副标题")
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(1200 - 80, 45),
+                font=font_1,
+                text=t1_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(1200 - 80, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="right",
+                anchor="rs",
+                spacing=10,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        goods_art_no_list = list(self.data.keys())
+        # ------侧视图-------------
+        pp_2 = self.image_one_pic(
+            goods_art_no=goods_art_no_list[0], name="侧视", bg_color=bg_color
+        )
+        pp_2 = pp_2.resize(mode="pixel", base="width", value=2327)
+        pp_2 = PictureProcessing("RGBA", (200, 300), bg_color).paste_img(
+            mode="pixel", top_img=pp_2, base="wc", value=(-900, 0)
+        )
+
+        # ------侧视-------------
+        pp_3 = self.image_one_pic(
+            goods_art_no=goods_art_no_list[0], name="侧视", bg_color=bg_color
+        )
+        pp_3 = pp_3.resize(mode="pixel", base="width", value=3000)
+        pp_3 = PictureProcessing("RGBA", (200, 300), bg_color).paste_img(
+            mode="pixel", top_img=pp_3, base="cc", value=(-500, 0)
+        )
+        # ---------------侧视图---------------旋转
+        _, pp_4 = self.image_one_pic(
+            goods_art_no=goods_art_no_list[0],
+            name="侧视",
+            bg_color=bg_color,
+            return_orign=True,
+        )
+        pp_4 = pp_4.resize(mode="pixel", base="width", value=1163)
+        pp_4_max = pp_4.width if pp_4.width > pp_4.height else pp_4.height
+        past_y = 0
+        top_img_bg = PictureProcessing("RGBA", (pp_bg.width, pp_2.height), bg_color)
+        top_img_bg = top_img_bg.paste_img(
+            mode="pixel", top_img=pp_2, base="wc", value=(50, past_y)
+        )
+        top_img_bg = top_img_bg.paste_img(
+            mode="pixel", top_img=pp_3, base="wc", value=(267, past_y)
+        )
+        new_pp4_bg = PictureProcessing(
+            "RGBA", (pp_4_max, pp_4_max + 100), (239, 239, 239, 0)
+        )
+        pp_4 = PictureProcessing("RGBA", (pp_4.width, pp_4.height), bg_color).paste_img(
+            mode="pixel", top_img=pp_4, base="nw", value=(0, 0)
+        )
+        new_pp4_bg.paste_img(mode="pixel", top_img=pp_4, base="wc", value=(0, 0))
+        new_pp4_bg = new_pp4_bg.rotate(30)
+        pp4_im = new_pp4_bg.get_im()
+        new_pp4_im = pp4_im.crop(pp4_im.getbbox())
+        new_pp5_bg = PictureProcessing(
+            "RGBA", (pp_bg.width, new_pp4_im.height - 200), bg_color
+        )
+        new_pp5_bg = new_pp5_bg.paste_img(
+            mode="pixel", top_img=new_pp4_bg, base="cc", value=(-50, 0)
+        )
+        new_empty_bg = PictureProcessing("RGBA", (pp_bg.width, 50), bg_color)
+        detailed_images.append(top_img_bg)
+        detailed_images.append(new_pp5_bg)
+        detailed_images.append(new_empty_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+
+        # =============文字=================
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\en\Black.otf", 65)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 65)
+        t1_title = "INFORMATION"
+        t1_sub_title = "产品信息"
+        if t1_title:
+            t_title_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_pp = t_title_pp.get_text_image_advanced(
+                value=(600, 45),
+                font=font_1,
+                text=t1_title,
+                align="center",
+                anchor="mm",
+                spacing=10,
+                fill=(16, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_pp)
+        if t1_sub_title:
+            t_title_2_pp = PictureProcessing("RGB", (1200, 500), bg_color)
+            t_title_2_pp = t_title_2_pp.get_text_image_advanced(
+                value=(600, 30),
+                font=font_2,
+                text=t1_sub_title,
+                align="center",
+                anchor="mm",
+                spacing=10,
+                fill=(16, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(t_title_2_pp)
+
+        goods_art_no_list = list(self.data.keys())
+        product_img = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="俯视",
+            bg_color=bg_color
+        )
+        product_img = product_img.resize(value=1200 / 1.75)
+        product_img = product_img.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1200, product_img.height), bg_color),
+            base="cc",
+        )
+
+        text_font = ImageFont.truetype(r"resources\ttf\puhui\Regular.otf", 30)
+        tips_font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        h = 280 if product_img.height < 312 else 312
+        paramsList = [("帮高", (140, product_img.height - h)), ("跟高", (1200 - 140, product_img.height - h)),
+                      ("鞋宽", (1200 - 140, product_img.height - 87))]
+        tipsList = [("帮高", (140, product_img.height - h - 30)), ("跟高", (1200 - 140, product_img.height - h - 30)),
+                    ("鞋宽", (1200 - 140, product_img.height - 87 - 30))]
+
+        for index,item in enumerate(paramsList):
+            text ="{}".format(self.get_text_value(item[0]))
+            if not text:
+                continue
+
+            product_img = product_img.add_text(
+                mode="pixel",
+                value=item[1],
+                font=text_font,
+                anchor="mm",
+                text=text,
+                align="center",
+                spacing=10,
+                fill=(143, 143, 143),
+            )
+
+            product_img = product_img.add_text(
+                mode="pixel",
+                value=tipsList[index][1],
+                font=tips_font,
+                anchor="mm",
+                text="{}".format(tipsList[index][0]),
+                align="center",
+                spacing=15,
+                fill=(143, 143, 143),
+            )
+
+        detailed_images.append(product_img)
+
+        # ===========================================================================================================d
+        pp_bg = PictureProcessing(r"{}\template_6.png".format(self.root))
+        tableList = [
+            ("鞋面材质", (300, 72)),
+            ("内里材质", (760, 72)),
+            ("鞋垫材质", (300, 72 + 105)),
+            ("鞋底材质", (760, 72 + 105)),
+        ]
+        table_font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        spacing = 15
+        text_arrays = []
+        for i, tableItem in enumerate(tableList):
+            item_text = self.get_text_value(tableItem[0])
+            if not item_text:
+                continue
+            text_arrays.append("{} : {}".format(tableItem[0], item_text))
+
+        for index, item in enumerate(text_arrays):
+            row = index // 2 + 1  # 计算行号(从1开始计数)
+            col = (index % 2) + 1  # 计算列号(从1开始计数)
+            if col == 1:
+                c_x = 102
+            else:
+                c_x = 635
+            if row == 1:
+                c_y = 40
+            else:
+                c_y = 40 + 105
+            pp_bg = pp_bg.add_text(
+                mode="pixel",
+                value=(c_x, c_y),
+                font=table_font,
+                text=item,
+                align="left",
+                spacing=spacing,
+                fill=(80, 80, 80),
+            )
+        detailed_images.append(pp_bg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def searchNotEmptyItem(self, current_idx, tableList):
+        idx = current_idx - 1
+        if idx < 0:
+            idx = 0
+        value = self.get_text_value(tableList[idx][0])
+        if idx == 0:
+            return idx
+        if value == "":
+            return self.searchNotEmptyItem(idx, tableList)
+        else:
+            return idx + 1
+
+    def deal_pic_6(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_7.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        # ==========添加颜色===================
+        pp_list_1 = []
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 25)
+        goods_art_no_list = list(self.data.keys())
+
+        all_color_name = []
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="侧视",
+                bg_color=bg_color,
+            )
+            if pp_jpg is None:
+                continue
+            pp_jpg = pp_jpg.resize(value=440)
+            color_name = self.goods_no_value["货号资料"][index]["颜色名称"]
+            all_color_name.append(color_name)
+            text_bg = PictureProcessing("RGB", (440, 200), bg_color)
+            text_bg = text_bg.get_text_image_advanced(
+                value=(220, 10),
+                font=font,
+                text="{}".format(color_name),
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 5, 0, 0)
+            )
+            # text_bg.show()
+            _bg = PictureProcessing("RGB", (440, pp_jpg.height + text_bg.height), bg_color)
+            _bg = _bg.paste_img(top_img=pp_jpg)
+            _bg = _bg.paste_img(top_img=text_bg, value=(0, pp_jpg.height))
+            pp_list_1.append(_bg)
+
+        rows = 2
+        shoes_bg = PictureProcessing().horizontal_distribution(
+            pp_list=pp_list_1,
+            bg_width=1200,
+            margins=(0, 0, 40, 40),
+            line_spacing=60,
+            number_per_row=rows,
+        )
+        detailed_images.append(shoes_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def get_font_render_size(self, text, font):
+        canvas = Image.new("RGB", (2048, 2048))
+        draw = ImageDraw.Draw(canvas)
+        draw.text((0, 0), text, font=font, fill=(55, 55, 55))
+        bbox = canvas.getbbox()
+        # 宽高
+        size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
+        return size
+
+    def deal_pic_7(self):
+        detailed_images = []
+
+        bg_color = (255, 255, 255)
+        goods_art_no_list = list(self.data.keys())
+        pp_bg = PictureProcessing(r"{}\template_8.png".format(self.root))
+        detailed_images.append(pp_bg)
+
+        color_name_1 = ["俯视", "侧视", "内里", "后跟", "鞋底"]
+        color_name_2 = ["俯视", "鞋底"]
+
+        color_name_1 = ["俯视", "侧视", "鞋底"]
+        color_name_2 = ["俯视", "内里", "后跟"]
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index in [0, 2, 4, 6, 8]:
+                color_name = color_name_1
+            else:
+                color_name = color_name_2
+            for name in color_name:
+                pp_jpg = self.get_overlay_pic_from_dict(
+                    goods_art_no=goods_art_no,
+                    color_name=name,
+                    bg_color=bg_color
+                )
+                if pp_jpg:
+                    if name != "后跟":
+                        pp_jpg = pp_jpg.resize(value=int(pp_bg.width / 1.2))
+                    else:
+                        pp_jpg = pp_jpg.resize(value=int(pp_bg.width / 2.2))
+
+                    pp_jpg = pp_jpg.paste_img_invert(
+                        top_img=PictureProcessing("RGB", (pp_bg.width, pp_jpg.height + 30), bg_color),
+                        base="cc")
+                    detailed_images.append(pp_jpg)
+                    detailed_images.append(PictureProcessing("RGB", (pp_bg.width, 220), bg_color))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+
+if __name__ == "__main__":
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data3.txt",
+            "r",
+            encoding="utf-8",
+    ) as f:
+        data = json.loads(f.read())
+
+    for goods_no, value in data.items():
+        d = DetailPicGetXiaoSuShuoXie2(
+            goods_no,
+            value,
+            out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-11-19\软件-详情图生成",
+        )
+        raise 1

+ 489 - 0
python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie3.py

@@ -0,0 +1,489 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+import time
+
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+from PIL import Image, ImageDraw
+import math
+
+
+class DetailPicGetXiaoSuShuoXie3(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\3".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False,excel_data=None,assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows,excel_data=excel_data,assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\3".format(os.getcwd())
+        self.base_bg_color = (236, 226, 211)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+        ]
+
+        if test:
+            self.run_test()
+        else:
+            self.run_all()
+
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir,self.goods_no, format="JPEG"))
+
+    def deal_pic_1(self):
+        """   制作主图  """
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\template_1.png".format(self.root))
+        detailed_images.append(pp_bg)
+        # -------粘贴文字-------
+        mainTitle = self.get_text_value("标题")
+        subTitle = self.get_text_value("副标题")
+
+        if mainTitle:
+            font = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 70)
+            text_1_bg = PictureProcessing("RGB", (1200, 500), self.base_bg_color)
+            text_1_bg = text_1_bg.get_text_image_advanced(
+                value=(600, 30),
+                font=font,
+                text=mainTitle,
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            # text_1_bg.show()
+            detailed_images.append(text_1_bg)
+
+        if subTitle:
+            font = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 40)
+            text_2_bg = PictureProcessing("RGB", (1200, 500), self.base_bg_color)
+            text_2_bg = text_2_bg.get_text_image_advanced(
+                value=(600, 30),
+                font=font,
+                text=mainTitle,
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(17, 16, 16),
+                return_mode="min_image_high",
+                margins=(10, 10, 0, 0)
+            )
+            detailed_images.append(text_2_bg)
+
+
+        # ====粘贴组合图以及其他图
+        goods_art_no_list = list(self.data.keys())
+        # 文字排列
+        _zuhe_pic_list = []
+        color_name_list = ["组合", "组合2", "组合3"]
+        for color_name in color_name_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color,
+            )
+            if not pp_1:
+                continue
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        if not _zuhe_pic_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name="俯视",
+                bg_color=self.base_bg_color,
+            )
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                                         base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        detailed_images.extend(_zuhe_pic_list)
+        detailed_images.append(PictureProcessing(r"{}\template_1_1.png".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_2(self):
+        """
+        细节解析
+        """
+        # 文字排列
+        detailed_images = []
+        pp_bg = PictureProcessing(
+            r"{}\template_2.png".format(
+                self.root
+            )
+        )
+        detailed_images.append(pp_bg)
+
+
+        goods_art_no_list = list(self.data.keys())
+
+        pp_1 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="侧视",
+            bg_color=self.base_bg_color,
+        )
+        pp_1= pp_1.resize(value=pp_bg.width / 1.4)
+        pp_1= pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 40), self.base_bg_color),
+                                    base="cc"
+                                    )
+        detailed_images.append(pp_1)
+
+        pp_bg_bottom = PictureProcessing(r"{}\template_2_1.png".format(self.root))
+        detailed_images.append(pp_bg_bottom)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        """   各个颜色细节展示  """
+        detailed_images = []
+        pp_bg = PictureProcessing(
+            r"{}\template_3.png".format(
+                self.root
+            )
+        )
+        detailed_images.append(pp_bg)
+
+
+        # ----------主图放置
+        y = 120
+        pp_list_1 = []
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 25)
+        goods_art_no_list = list(self.data.keys())
+
+        all_color_name = []
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="侧视",
+                bg_color=self.base_bg_color,
+            )
+            if pp_jpg is None:
+                continue
+            pp_jpg = pp_jpg.resize(value=440)
+            color_name = self.goods_no_value["货号资料"][index]["颜色名称"]
+            all_color_name.append(color_name)
+            text_bg = PictureProcessing("RGB", (440, 200), (255, 255, 255))
+            text_bg = text_bg.get_text_image_advanced(
+                value=(220, 10),
+                font=font,
+                text="【{}】".format(color_name),
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(55, 55, 55),
+                return_mode="min_image_high",
+                margins=(10, 5, 0, 0)
+            )
+            # text_bg.show()
+            _bg = PictureProcessing("RGB", (440, pp_jpg.height+text_bg.height), (255, 255, 255))
+            _bg = _bg.paste_img(top_img=pp_jpg)
+            _bg = _bg.paste_img(top_img=text_bg,value=(0,pp_jpg.height))
+            pp_list_1.append(_bg)
+
+        rows = 2
+        shoes_bg = PictureProcessing().horizontal_distribution(
+            pp_list=pp_list_1,
+            bg_width=1200,
+            margins=(0, 0, 40, 40),
+            line_spacing=60,
+            number_per_row=rows,
+        )
+        detailed_images.append(shoes_bg)
+
+
+        font_bg = PictureProcessing(r"{}\template_3_1_new.png".format(self.root))
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        base_x = pp_bg.width - 229
+        base_y = 205
+
+        text_list = [
+            ("-{}".format("-".join(all_color_name)), (base_x, base_y)),
+            ("-{}".format(self.get_text_value("鞋面材质")), (base_x, base_y+70*1)),
+            ("-{}".format(self.get_text_value("内里材质")), (base_x, base_y+70*2)),
+            ("-{}".format(self.get_text_value("鞋底材质")), (base_x, base_y+70*3)),
+            ("-{}".format(self.get_text_value("鞋垫材质")), (base_x, base_y+70*4 )),
+            ("-{}".format(self.get_text_value("跟高")), (base_x, base_y+70*5)),
+            ("-{}".format(self.get_text_value("尺码")), (base_x, base_y+70*6)),
+        ]
+        for text_item in text_list:
+            position = text_item[1]
+            text_str = text_item[0]
+            xPos = position[0]
+            for char in reversed(text_str):
+                font_bg.add_text(
+                    mode="pixel",
+                    value=(xPos, position[1]),
+                    font=font,
+                    text=char,
+                    align="right",
+                    anchor="mm",
+                    spacing=10,
+                    fill=(30, 30, 30),
+                )
+                if not "\u4e00" <= char <= "\u9fff":
+                    xPos -= 20
+                else:
+                    xPos -= 35
+        # 其他图片
+        detailed_images.append(font_bg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # =============设计理念================
+        detail_images = []
+        top_bg = PictureProcessing(r"{}\template_4.png".format(self.root))
+        detail_images.append(top_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 40)
+        # 粘贴文字
+        text = self.get_text_value("设计理念")
+        if not text:
+            text = "引领全新裸足脚感,开启自信步履亲肤\n优质鞋面材质赋予全新舒适脚感\n多元场景穿搭,满足你的不同STYLE"
+        _, text_height = self.get_font_render_size(text, font_1)
+        text_bg = PictureProcessing(
+            "RGB",
+            (
+                top_bg.width,
+                text_height + 350,
+            ),
+            (236, 227, 212),
+        )
+        goods_art_no_list = list(self.data.keys())
+        # y = 430
+
+        x = top_bg.width / 2
+        text_bg = text_bg.add_text(
+            mode="pixel",
+            value=(x, math.ceil(text_bg.height / 2) - 60),
+            font=font_1,
+            text=text,
+            align="center",
+            anchor="mm",
+            spacing=15,
+            fill=(97, 97, 98),
+        )
+        detail_images.append(text_bg)
+
+        # ==========3个提示信息=========
+        title_font = ImageFont.truetype(r"resources\ttf\puhui\Heavy.ttf", 60)
+        sub_title_font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        desc_font = ImageFont.truetype(r"resources\ttf\puhui\Heavy.ttf", 35)
+        color_name_list = ["俯视", "内里", "侧视"]
+        # 添加分割线
+        detail_images.append(PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255)))
+
+        for index, color_name in enumerate(color_name_list):
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 20), (255, 255, 255)))
+
+            t_title = self.get_text_value("提示{}主标题".format(index + 1))
+            if t_title:
+                t_title_pp = PictureProcessing("RGB", (top_bg.width, 500), (255, 255, 255))
+                t_title_pp=t_title_pp.get_text_image_advanced(
+                    value=(int(top_bg.width / 2), 0),
+                    font=title_font,
+                    text=t_title,
+                    align="center",
+                    anchor="ma",
+                    spacing=10,
+                    fill=(55, 55, 55),
+                    return_mode="min_image_high",
+                    margins=(10, 10, 0, 0)
+                )
+                detail_images.append(t_title_pp)
+
+
+                t_sub_title = self.get_text_value("提示{}副标题".format(index + 1))
+                if t_sub_title:
+                    t_sub_title_pp = PictureProcessing("RGB", (top_bg.width, 500), (255, 255, 255))
+                    t_sub_title_pp = t_sub_title_pp.get_text_image_advanced(
+                        value=(int(top_bg.width / 2),0),
+                        font=sub_title_font,
+                        text=t_sub_title,
+                        align="center",
+                        anchor="ma",
+                        spacing=10,
+                        fill=(55, 55, 55),
+                        return_mode="min_image_high",
+                        margins=(10, 10, 0, 0)
+                    )
+                    detail_images.append(t_sub_title_pp)
+
+
+            # 添加图
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color
+            )
+            t_desc = self.get_text_value("提示{}描述".format(index + 1))
+            if color_name == "俯视":
+                pp_jpg = pp_jpg.resize(value=1300)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (749, 1009 if pp_jpg.height>1009 else pp_jpg.height), (236, 227, 212)), base="ws",
+                    value=(-300, -50))
+                text_pos_value = (50, 50)
+                align = "left"
+                anchor = ""
+
+            if color_name == "内里":
+                pp_jpg = pp_jpg.resize(value=2000)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (749, pp_jpg.height + 150), (236, 227, 212)), base="wc",
+                    value=(-230, 0))
+                text_pos_value = (50, 50)
+                align = "left"
+                anchor = ""
+
+            if color_name == "侧视":
+                pp_jpg = pp_jpg.resize(value=1600)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (749, 1009 if pp_jpg.height>1009 else pp_jpg.height), (236, 227, 212)), base="se",
+                    value=(0, -30))
+                text_pos_value = (pp_jpg.width - 30, pp_jpg.height - 360)
+                align = "right"
+                anchor = "rs"
+
+            if t_desc:
+                pp_jpg = pp_jpg.add_text(
+                    mode="pixel",
+                    value=text_pos_value,
+                    font=desc_font,
+                    text=t_desc,
+                    align=align,
+                    anchor=anchor,
+                    spacing=10,
+                    fill=(102, 102, 102),
+                )
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGB", (top_bg.width, pp_jpg.height,), (255, 255, 255), ), base="cc",
+                value=(0, 0))
+            detail_images.append(pp_jpg)
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 60), (255, 255, 255), ))
+
+        # 添加分割线
+        detail_images.append(PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255), ))
+        image = PictureProcessing(im=self.add_pic(detail_images))
+        return image
+
+    def deal_pic_5(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        top_bg = PictureProcessing(
+            r"{}\template_5.png".format(
+                self.root
+            )
+        )
+
+        detailed_images.append(top_bg)
+        pp_list_1 = []
+        color_name_1 = ["俯视", "侧视", "内里", "后跟", "鞋底"]
+        color_name_2 = ["俯视", "鞋底"]
+
+        color_name_1 = ["俯视", "侧视", "鞋底"]
+        color_name_2 = ["俯视", "内里", "后跟"]
+        n = 0
+        pp_1_height = 100
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index in [0, 2, 4, 6, 8]:
+                color_name = color_name_1
+            else:
+                color_name = color_name_2
+
+            for name in color_name:
+                pp_1 = self.get_overlay_pic_from_dict(
+                    goods_art_no=goods_art_no,
+                    color_name=name,
+                    bg_color=self.base_bg_color
+                )
+
+                if pp_1:
+                    n += 1
+                    if name != "后跟":
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 1.4))
+                    else:
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 3))
+
+                    if n == 1:
+                        pp_1_height = pp_1.height
+
+                    _height = pp_1.height if pp_1.height > pp_1_height else pp_1_height
+
+                    pp_1 = pp_1.paste_img_invert(base="cc",
+                                                 top_img=PictureProcessing("RGB",
+                                                                           (int(top_bg.width / 1.4), _height + 10,),
+                                                                           self.base_bg_color))
+
+                    pp_1 = pp_1.paste_img_invert(base="cc",
+                                                 top_img=PictureProcessing("RGB",
+                                                                           (top_bg.width, _height + 100,),
+                                                                           (255, 255, 255)))
+
+                    pp_list_1.append(pp_1)
+
+        if pp_list_1:
+            pp_list_1.append(PictureProcessing("RGB", (top_bg.width, 100), bg_color))
+            detailed_images.extend(pp_list_1)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def generateTextBg(self, parent, position, font, font_size, text, color):
+        return parent.add_text(
+            mode="pixel",
+            value=position,
+            font=ImageFont.truetype(font, font_size),
+            text=text,
+            align="center",
+            anchor="ma",
+            spacing=10,
+            fill=color,
+        )
+
+    def get_font_render_size(self, text, font, canvas_size=(2048, 2048)):
+        canvas = Image.new('RGB', canvas_size)
+        draw = ImageDraw.Draw(canvas)
+        draw.text((0, 0), text, font=font, fill=(55, 55, 55))
+        bbox = canvas.getbbox()
+        # 宽高
+        size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
+        return size
+
+
+if __name__ == '__main__':
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data3.txt",
+            "r", encoding="utf-8") as f:
+        data = json.loads(f.read())
+
+    for goods_no, value in data.items():
+        d = DetailPicGetXiaoSuShuoXie3(goods_no, value,
+                                       out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-11-19\软件-详情图生成")
+        raise 1

+ 458 - 0
python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie4.py

@@ -0,0 +1,458 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+import time
+
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+from PIL import Image, ImageDraw
+import math
+
+
+class DetailPicGetXiaoSuShuoXie4(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\4".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\4".format(os.getcwd())
+        print("run xiaosushuoxie-4 ")
+        self.base_bg_color = (255, 255, 255)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+        ]
+
+        if test:
+            # for k, v in self.goods_no_value.items():
+            #     print(k, v)
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        # detailed_images.append(self.deal_pic_2())
+        # detailed_images.append(self.deal_pic_3())
+        # detailed_images.append(self.deal_pic_4())
+        # detailed_images.append(self.deal_pic_5())
+        # detailed_images.append(self.deal_pic_6())
+        # detailed_images.append(self.deal_pic_7())
+        img = self.add_pic(detailed_images)
+        # img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+        img.show()
+
+
+    # 主图
+    def deal_pic_1(self):
+        """   制作主图  """
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\t (1).png".format(self.root))
+        detailed_images.append(pp_bg)
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 56)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 26)
+        text_list = []
+        text_list.append({"text": self.get_text_value("标题"),
+                          "font": font_1,
+                          "fill": (17, 16, 16),
+                          })
+        text_list.append({"text": self.get_text_value("副标题"),
+                          "font": font_2,
+                          "fill": (17, 16, 16),
+                          })
+        text_image = self.add_text_list(text_list, spacing=25, base="nc")
+        if text_image:
+            text_image = text_image.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1200, text_image.height + 100), self.base_bg_color),
+                base="cc",
+                value=(0, 0))
+            detailed_images.append(text_image)
+        # ====粘贴组合图以及其他图
+        goods_art_no_list = list(self.data.keys())
+        # 文字排列
+        _zuhe_pic_list = []
+        color_name_list = ["组合", "组合2", "组合3", "组合4", "组合5"]
+        for color_name in color_name_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color,
+            )
+            if not pp_1:
+                continue
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+            _zuhe_pic_list.append(pp_1)
+            _zuhe_pic_list.append(PictureProcessing("RGB", (pp_bg.width, 150), self.base_bg_color))
+
+        if not _zuhe_pic_list:
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name="俯视",
+                bg_color=self.base_bg_color,
+            )
+            pp_1 = pp_1.resize(value=int(pp_bg.width / 1.45))
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (pp_bg.width, pp_1.height + 30), self.base_bg_color),
+                base="cc")
+
+            _zuhe_pic_list.append(pp_1)
+
+        detailed_images.extend(_zuhe_pic_list)
+        detailed_images.append(PictureProcessing(r"{}\t (3).png".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 商品信息
+    def deal_pic_2(self):
+        """
+        细节解析
+        """
+        # 文字排列
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\t (4).png".format(self.root))
+        detailed_images.append(pp_bg)
+        detailed_images.append(PictureProcessing("RGB", (pp_bg.width, 120), self.base_bg_color))
+
+        goods_art_no_list = list(self.data.keys())
+        pp_bg_2 = PictureProcessing(r"{}\t (26) .png".format(self.root))
+
+        pp_1 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no_list[0],
+            color_name="侧视",
+            bg_color=self.base_bg_color,
+        )
+        pp_1 = pp_1.resize(value=700)
+        pp_bg_2 = pp_bg_2.paste_img(top_img=pp_1,
+                                    base="sc",
+                                    value=(0, 90)
+                                    )
+        # 粘贴文字
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 26)
+        text_key_list = ["鞋面特点", "跟高", "鞋底特点"]
+        for text_key in text_key_list:
+            text = self.get_text_value(text_key)
+            if not text:
+                continue
+            if text_key == "鞋面特点":
+                value = (131, 380)
+            if text_key == "跟高":
+                value = (935, 187)
+                text = "跟高:{}".format(text)
+            if text_key == "鞋底特点":
+                value = (831, 40)
+            text_image = pp_bg_2.get_text_image_advanced(
+                value=(0, 0),
+                font=font,
+                text=text,
+                spacing=5,
+                fill=(15, 15, 15),
+                return_mode="min_image",
+            )
+            if text_image:
+                pp_bg_2 = pp_bg_2.paste_img(top_img=text_image,
+                                            base="sw",
+                                            value=value
+                                            )
+
+        # 剪裁多余的
+        if pp_bg_2.height > pp_1.height + 90:
+            pp_bg_2 = pp_bg_2.crop(bbox=(0, pp_bg_2.height - 90 - pp_1.height, pp_bg_2.width, pp_bg_2.height))
+
+        detailed_images.append(pp_bg_2)
+
+        font_bg = PictureProcessing(r"{}\t (6).png".format(self.root))
+        # 商品信息文字
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 35)
+        text_list = [
+            ("{}".format(self.get_text_value("品牌")), (294 + 20, 49)),
+            ("{}".format(self.get_text_value("临时款号")), (854 + 20, 49)),
+            ("{}".format(self.get_text_value("鞋面材质")), (294 + 20, 138)),
+            ("{}".format(self.get_text_value("内里材质")), (854 + 20, 138)),
+            ("{}".format(self.get_text_value("鞋垫材质")), (294 + 20, 230)),
+            ("{}".format(self.get_text_value("鞋底材质")), (854 + 20, 230)),
+        ]
+
+        for text_item in text_list:
+            if text_item[0]:
+                font_bg = font_bg.get_text_image_advanced(
+                    value=text_item[1],
+                    font=font,
+                    text=text_item[0],
+                    align="center",
+                    anchor=None,
+                    spacing=5,
+                    fill=(30, 30, 30),
+                    return_mode="image",
+                    margins=(0, 0, 0, 0)
+                )
+
+        # 其他图片
+        detailed_images.append(font_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_3(self):
+        """   各个颜色细节展示  """
+        detailed_images = []
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB", (1200, 100), (255, 255, 255)))
+
+        # 放各个颜色图
+        pp_list_1 = []
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 25)
+        goods_art_no_list = list(self.data.keys())
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="侧视",
+                bg_color=self.base_bg_color,
+            )
+            if pp_jpg is None:
+                continue
+            pp_jpg = pp_jpg.resize(value=480)
+            color_name = self.goods_no_value["货号资料"][index]["颜色名称"]
+            text_bg = PictureProcessing("RGB", (480, 50), (255, 255, 255))
+            text_bg = text_bg.get_text_image_advanced(
+                value=(480 / 2, 10),
+                font=font,
+                text="{}".format(color_name),
+                align="center",
+                anchor="mm",
+                spacing=5,
+                fill=(55, 55, 55),
+                return_mode="image",
+                margins=(0, 0, 0, 0)
+            )
+            # text_bg.show()
+            _bg = PictureProcessing("RGB", (440, pp_jpg.height + text_bg.height), (255, 255, 255))
+            _bg = _bg.paste_img(top_img=pp_jpg)
+            _bg = _bg.paste_img(top_img=text_bg, value=(0, pp_jpg.height))
+            pp_list_1.append(_bg)
+
+        rows = 2
+        shoes_bg = PictureProcessing().horizontal_distribution(
+            pp_list=pp_list_1,
+            bg_width=1200,
+            margins=(0, 0, 40, 40),
+            line_spacing=60,
+            number_per_row=rows,
+        )
+        detailed_images.append(shoes_bg)
+        # 添加分割线
+        detailed_images.append(PictureProcessing("RGB", (1200, 200), (255, 255, 255)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 细节展示
+    def deal_pic_4(self):
+        # =============细节展示================
+        detail_images = []
+        top_bg = PictureProcessing(r"{}\t (8).png".format(self.root))
+        detail_images.append(top_bg)
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Heavy.ttf", 120)  # 序号标签
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 36)  # 描述主标题
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 26)  # 描述副标题
+
+        # ==========3个图片描述信息=========
+        goods_art_no_list = list(self.data.keys())
+        color_name_list = ["俯视", "鞋底", "侧视"]
+        for index, color_name in enumerate(color_name_list):
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 20), (255, 255, 255)))
+
+            # 添加商品图片
+            pp_jpg = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no_list[0],
+                color_name=color_name,
+                bg_color=self.base_bg_color
+            )
+            if color_name == "俯视":
+                pp_jpg = pp_jpg.resize(value=1565)
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1200, 1050), (255, 255, 255)),
+                    base="ws",
+                    value=(104, 0))
+                text_pos_value = (50, 70)
+                text_pos_base = "nw"  # 粘贴文字的位置
+                text_base = "wn"  # 文字方向
+
+            if color_name == "鞋底":
+                pp_jpg = pp_jpg.resize(value=1766)
+                pp_jpg = pp_jpg.transpose(mode="left_right")
+
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1200, pp_jpg.height + 150), (255, 255, 255)), base="ec",
+                    value=(275, 0))
+                text_pos_value = (50, 150)
+                text_pos_base = "es"  # 粘贴文字的位置
+                text_base = "en"
+
+            if color_name == "侧视":
+                pp_jpg = pp_jpg.resize(value=1681)
+                pp_jpg = pp_jpg.transpose(mode="left_right")
+                pp_jpg = pp_jpg.paste_img_invert(
+                    top_img=PictureProcessing("RGB", (1200, 843), (255, 255, 255)), base="ws",
+                    value=(216, 0))
+                text_pos_value = (50, 150)
+                text_pos_base = "ws"  # 粘贴文字的位置
+                text_base = "wn"
+
+            text_list = []
+            # 序号
+            text_list.append({"text": "{}".format(index + 1).zfill(2),
+                              "font": font_1,
+                              "fill": (215, 215, 215),
+                              })
+            # 主标题
+            text_list.append({"text": self.get_text_value("提示{}主标题".format(index + 1)),
+                              "font": font_2,
+                              "fill": (55, 55, 55),
+                              })
+            text_list.append({"text": self.get_text_value("提示{}副标题".format(index + 1)),
+                              "font": font_3,
+                              "fill": (55, 55, 55),
+                              })
+
+            text_image = self.add_text_list(text_list, spacing=30, base=text_base)
+            pp_jpg = pp_jpg.paste_img(
+                top_img=text_image,
+                base=text_pos_base,
+                value=text_pos_value, )
+            detail_images.append(pp_jpg)
+            # 添加分割线
+            detail_images.append(PictureProcessing("RGB", (top_bg.width, 60), (255, 255, 255), ))
+
+        # 添加分割线
+        detail_images.append(PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255), ))
+        image = PictureProcessing(im=self.add_pic(detail_images))
+        return image
+
+    # 添加尺码表
+    def deal_pic_5(self):
+        image_path = r"{}\t (10).png".format(self.root)
+        return PictureProcessing(image_path)
+
+    # 静物展示
+    def deal_pic_6(self):
+        bg_color = (255, 255, 255)
+        detailed_images = []
+        goods_art_no_list = list(self.data.keys())
+        top_bg = PictureProcessing(r"{}\t (11).png".format(self.root))
+
+        detailed_images.append(top_bg)
+        pp_list_1 = []
+        color_name_show_list = []
+        color_name_show_list.append(["组合", "内里", "侧视", "鞋底", "俯视"])
+        color_name_show_list.append(["组合2", "内里", "侧视", "鞋底", "俯视"])
+        color_name_show_list.append(["组合3", "内里", "侧视", "鞋底", "俯视"])
+        color_name_show_list.append(["内里", "侧视", "鞋底", "俯视"])
+
+        color_name_text_show_dict = {
+            "组合": "合并展示 / MERGE",
+            "组合2": "合并展示 / MERGE",
+            "组合3": "合并展示 / MERGE",
+            "内里": "内里展示 / INSIDER",
+            "鞋底": "鞋底展示 / SOLE",
+            "侧视": "后跟展示 / HEELPIECE",
+            "俯视": "45°展示 / 45°",
+        }
+        font = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)
+        n = 0
+        pp_1_height = 100
+
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            try:
+                color_name = color_name_show_list[index]
+            except:
+                color_name = color_name_show_list[-1]
+
+            for name in color_name:
+                pp_1 = self.get_overlay_pic_from_dict(
+                    goods_art_no=goods_art_no,
+                    color_name=name,
+                    bg_color=self.base_bg_color
+                )
+                if pp_1:
+                    n += 1
+                    if name != "后跟":
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 1.4))
+                    else:
+                        pp_1 = pp_1.resize(value=int(top_bg.width / 3))
+
+                    if n == 1:
+                        pp_1_height = pp_1.height
+
+                    _height = pp_1.height if pp_1.height > pp_1_height else pp_1_height
+                    _height = pp_1.height + 50
+                    # pp_1 = pp_1.paste_img_invert(base="sc",
+                    #                              top_img=PictureProcessing("RGB",
+                    #                                                        (int(top_bg.width / 1.4), _height + 10,),
+                    #                                                        self.base_bg_color))
+                    pp_1 = pp_1.paste_img_invert(base="sc",
+                                                 value=(0, 50),
+                                                 top_img=PictureProcessing("RGB",
+                                                                           (top_bg.width, _height + 100,),
+                                                                           (255, 255, 255)))
+
+                    pp_list_1.append(pp_1)
+                    font_bg = PictureProcessing("RGB", (top_bg.width, 100), (255, 255, 255))
+                    font_bg = font_bg.get_text_image_advanced(
+                        value=(600, 20),
+                        font=font,
+                        text=color_name_text_show_dict[name],
+                        align="center",
+                        anchor="mm",
+                        spacing=5,
+                        fill=(30, 30, 30),
+                        return_mode="image",
+                        margins=(0, 0, 0, 0)
+                    )
+                    pp_list_1.append(font_bg)
+
+        if pp_list_1:
+            pp_list_1.append(PictureProcessing("RGB", (top_bg.width, 100), bg_color))
+            detailed_images.extend(pp_list_1)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加备注图
+    def deal_pic_7(self):
+        image_path = r"{}\t (25).png".format(self.root)
+        return PictureProcessing(image_path)
+
+    def generateTextBg(self, parent, position, font, font_size, text, color):
+        return parent.add_text(
+            mode="pixel",
+            value=position,
+            font=ImageFont.truetype(font, font_size),
+            text=text,
+            align="center",
+            anchor="ma",
+            spacing=10,
+            fill=color,
+        )
+
+    def get_font_render_size(self, text, font, canvas_size=(2048, 2048)):
+        canvas = Image.new('RGB', canvas_size)
+        draw = ImageDraw.Draw(canvas)
+        draw.text((0, 0), text, font=font, fill=(55, 55, 55))
+        bbox = canvas.getbbox()
+        # 宽高
+        size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
+        return size

+ 586 - 0
python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie5.py

@@ -0,0 +1,586 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+import time
+
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+from PIL import Image, ImageDraw
+import math
+
+
+class DetailPicGetXiaoSuShuoXie5(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\5".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\5".format(os.getcwd())
+        print("run xiaosushuoxie-5 ")
+        self.base_bg_color = (255, 255, 255)
+        self.base_bg_color_2 = (244, 242, 243)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+            self.deal_pic_8,
+            self.deal_pic_9,
+            self.deal_pic_10,
+            self.deal_pic_11,
+        ]
+
+        if test:
+            # pp = self.generate_font_list_to_pic()
+            # pp.im.save(r"C:\Users\gymmc\Desktop\细节图示例/字号.png")
+            # for k, v in self.goods_no_value.items():
+            #     print(k, v)
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        detailed_images.append(self.deal_pic_6())
+        detailed_images.append(self.deal_pic_7())
+        detailed_images.append(self.deal_pic_8())
+        detailed_images.append(self.deal_pic_9())
+        detailed_images.append(self.deal_pic_10())
+        detailed_images.append(self.deal_pic_11())
+
+        img = self.add_pic(detailed_images)
+        img.save(r"{}/{}.jpg".format(self.out_put_dir,self.goods_no, format="JPEG"))
+        # img.show()
+
+
+    # 标题文字展示
+    def deal_pic_1(self):
+        pp_bg = PictureProcessing(r"{}\t (1).png".format(self.root))
+
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 74)  # 设计理念
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述主标题1
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述副标题1
+
+        text_list = []
+        text_list.append({"text": self.get_text_value("设计理念"),
+                          "font": font_1,
+                          "fill": (47, 44, 51),
+                          "spacing": 50,
+                          })
+        # 主标题
+        text_list.append({"text": self.get_text_value("标题"),
+                          "font": font_2,
+                          "fill": (124, 91, 24),
+                          })
+        text_list.append({"text": self.get_text_value("副标题"),
+                          "font": font_3,
+                          "fill": (55, 55, 55),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=15, base="en")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="ne",
+                value=(133, 277))
+
+        return pp_bg
+
+    # 展示鞋头和后跟
+    def deal_pic_2(self):
+        pp_bg = PictureProcessing(r"{}\t (2).png".format(self.root))
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="内里",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1187)
+        pp_png_1 = pp_png_1.rotate_advance(doge=90)
+        pp_png_1 = pp_png_1.crop(bbox=(0, 0, pp_png_1.width, 473))
+        pp_bg = pp_bg.paste_img(top_img=pp_png_1,
+                                value=(349 - pp_png_1.width / 2, 342),
+                                base="sw"
+                                )
+
+        pp_jpg_2, pp_png_2 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="后跟",
+                                                )
+        pp_png_2: PictureProcessing
+        pp_png_2 = pp_png_2.resize(value=502)
+        pp_png_2 = pp_png_2.crop(mode="min")
+        pp_png_2 = pp_png_2.crop(bbox=(0, 0, pp_png_2.width, 473))
+
+        pp_bg = pp_bg.paste_img(top_img=pp_png_2,
+                                value=(871 - pp_png_2.width / 2, 342),
+                                base="sw"
+                                )
+
+        # 添加标题文字等
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 42)  # 特征标题
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)  # 特征描述
+
+        text_list = []
+        # 鞋头特征
+        text_list.append({"text": self.get_text_value("鞋头特征"),
+                          "font": font_1,
+                          "fill": (48, 43, 49),
+                          "spacing": 0,
+                          })
+        # 主标题
+        # 鞋头描述
+        text_list.append({"text": self.get_text_value("鞋头描述"),
+                          "font": font_2,
+                          "fill": (164, 164, 164),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=15, base="nc")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(349 - text_image.width / 2, 511))
+
+        text_list = []
+        # 后跟特征
+        text_list.append({"text": self.get_text_value("后跟特征"),
+                          "font": font_1,
+                          "fill": (48, 43, 49),
+                          "spacing": 0,
+                          })
+        # 后跟描述
+        text_list.append({"text": self.get_text_value("后跟描述"),
+                          "font": font_2,
+                          "fill": (164, 164, 164),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=15, base="nc")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(871 - text_image.width / 2, 511))
+
+        return pp_bg
+
+    # 展示鞋头放大图
+    def deal_pic_3(self):
+        pp_bg = PictureProcessing(r"{}\t (3).png".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 70)  # 设计理念
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 46)  # 描述主标题1
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述主标题2
+
+        text_list = []
+        # 鞋头特征
+        text_list.append({"text": self.get_text_value("设计理念2"),
+                          "font": font_1,
+                          "fill": (3, 3, 2),
+                          "spacing": 40,
+                          })
+        # 主标题
+        text_list.append({"text": self.get_text_value("提示2主标题"),
+                          "font": font_2,
+                          "fill": (124, 91, 24),
+                          })
+        text_list.append({"text": self.get_text_value("提示2副标题"),
+                          "font": font_3,
+                          "fill": (26, 25, 23),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=10, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(140, 176))
+
+        # 粘贴俯视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="俯视",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1933)
+        pp_png_1 = pp_png_1.crop(mode="min")
+        pp_bg = pp_bg.paste_img(top_img=pp_png_1,
+                                value=(202, 86),
+                                base="sw"
+                                )
+
+        return pp_bg
+
+    # 展示后跟放大图
+    def deal_pic_4(self):
+        pp_bg = PictureProcessing(r"{}\t (4).png".format(self.root))
+        h = 176
+        # ===========粘贴文字============
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 46)  # 描述主标题1
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 44)  # 描述主标题2
+
+        text_list = []
+        # 鞋头特征
+        # 主标题
+        text_list.append({"text": self.get_text_value("提示3主标题"),
+                          "font": font_2,
+                          "fill": (124, 91, 24),
+                          })
+        text_list.append({"text": self.get_text_value("提示3副标题"),
+                          "font": font_3,
+                          "fill": (26, 25, 23),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=10, base="ne")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="ne",
+                value=(140, h))
+
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no_list[0],
+                                            name="侧视",
+                                            )
+        pp_jpg = pp_jpg.resize(value=2000)
+        pp_png = pp_png.resize(value=2000)
+
+        if text_image:
+            h = h + text_image.height
+        else:
+            h = h
+        pp_bg = pp_bg.to_overlay_pic_advance(mode="pixel", top_img=pp_jpg, base="en", value=(100, h),
+                                             top_png_img=pp_png)
+
+        # 总高
+        h = h + pp_jpg.height + 50
+        if h < pp_bg.height:
+            # 剪裁
+            pp_bg = pp_bg.crop(bbox=(0, 0, 1200, h))
+
+        return pp_bg
+
+    # 展示后跟细节卡片
+    def deal_pic_5(self):
+        pp_bg = PictureProcessing(r"{}\t (5).png".format(self.root))
+
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="侧视",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1621)
+        pp_png_1 = pp_png_1.rotate_advance(doge=45)
+        pp_png_1 = pp_png_1.transpose()
+        pp_png_1 = pp_png_1.crop(mode="min")
+        shoe_bg = PictureProcessing("RGB", (448, 464), self.base_bg_color)
+        shoe_bg = shoe_bg.paste_img(top_img=pp_png_1,
+                                    value=(-80, 200),
+                                    base="wc"
+                                    )
+        shoe_bg = shoe_bg.radius(circular_pos=(0, 0, 1, 0), value=30)
+        pp_bg = pp_bg.paste_img(top_img=shoe_bg,
+                                value=(613, 174),
+                                base="nw")
+        # 添加文字
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 40)  # 设计理念
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 30)  # 描述主标题1
+
+        text_list = []
+        text_list.append({"text": self.get_text_value("提示4主标题"),
+                          "font": font_1,
+                          "fill": (10, 7, 6),
+                          })
+        text_list.append({"text": self.get_text_value("提示4副标题"),
+                          "font": font_2,
+                          "fill": (72, 65, 59),
+                          })
+
+        text_image = self.add_text_list(text_list, spacing=38, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(154, 210))
+
+        return pp_bg
+
+    # "心动"设计亮点,展示旋转的侧视图
+    def deal_pic_6(self):
+        detailed_images = []
+        pp_bg = PictureProcessing(r"{}\t (6).png".format(self.root))
+        # 粘贴侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="侧视",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=2050)
+        pp_png_1 = pp_png_1.transpose()
+        pp_png_1 = pp_png_1.rotate_advance(doge=20)
+        pp_png_1 = pp_png_1.crop(mode="min")
+        pp_bg = pp_bg.paste_img(top_img=pp_png_1,
+                                value=(-200, 200),
+                                base="ws"
+                                )
+
+        # ========添加文字==================
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 70)  # 设计理念
+        text = self.get_text_value("设计理念3")
+        pp_bg = pp_bg.get_text_image_advanced(
+            value=(pp_bg.width - 127, pp_bg.height - 43),
+            font=font_1,
+            text=text,
+            anchor="rs",
+            align="right",
+            spacing=5,
+            fill=(109, 107, 109),
+            return_mode="image",
+        )
+        return pp_bg
+
+    # 产品展示,展示大图各个细节
+    def deal_pic_7(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (7).png".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+        # ======展示组合图============
+        pp_1 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no,
+            color_name="组合",
+            bg_color=self.base_bg_color_2
+        )
+        if pp_1:
+            pp_1 = pp_1.resize(value=1200)
+            pp_1 = pp_1.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1200, pp_1.height + 300), self.base_bg_color_2),
+                base="cc", )
+            detailed_images.append(pp_1)
+
+        # ======展示鞋面===============
+        detailed_images.append(PictureProcessing(r"{}\t (9).png".format(self.root)))
+        pp_2 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no,
+            color_name="俯视",
+            bg_color=self.base_bg_color_2
+        )
+        pp_2: PictureProcessing
+        pp_2 = pp_2.resize(value=1519)
+        pp_2 = pp_2.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_2.height + 50), self.base_bg_color_2),
+                                     base="ws", value=(171, 0))
+        detailed_images.append(pp_2)
+
+        # =======展示后跟=========
+        detailed_images.append(PictureProcessing(r"{}\t (10).png".format(self.root)))
+        pp_3 = self.get_overlay_pic_from_dict(
+            goods_art_no=goods_art_no,
+            color_name="侧视",
+            bg_color=self.base_bg_color_2
+        )
+        pp_3: PictureProcessing
+        pp_3 = pp_3.resize(value=1881)
+        pp_3 = pp_3.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_3.height + 200), self.base_bg_color_2),
+                                     base="ec", value=(171, 0))
+        detailed_images.append(pp_3)
+
+        # =========展示鞋底============
+        detailed_images.append(PictureProcessing(r"{}\t (11).png".format(self.root)))
+        pp_jpg_4, pp_png_4 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no,
+                                                name="鞋底",
+                                                )
+        pp_png_4: PictureProcessing
+        pp_png_4 = pp_png_4.resize(value=2380)
+        pp_png_4 = pp_png_4.rotate_advance(doge=45)
+        pp_png_4 = pp_png_4.paste_img_invert(
+            top_img=PictureProcessing("RGB", (1200, pp_png_4.height + 200), self.base_bg_color_2),
+            base="ws", value=(269, 81))
+        detailed_images.append(pp_png_4)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 功能展示与卖点
+    def deal_pic_8(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (12).png".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+        color_name_list = ["侧视", "鞋底", "俯视"]
+
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 42)  # 卖点标题1
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 32)  # 卖点标题2
+        font_3 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 26)  # 卖点描述1
+
+        # =====卖点1,展示后跟========
+        for color_name in color_name_list:
+            bg = PictureProcessing(r"{}\t (13).png".format(self.root))
+            pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no,
+                                                name=color_name,
+                                                )
+            temp_bg = PictureProcessing("RGB", (536, 424), (214, 214, 214))
+            if color_name == "侧视":
+                pp_png = pp_png.resize(value=990)
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="es")
+                shoe_paste_base = "wc"
+                text_1, text_2 = self.get_text_value("卖点1标题", subsection_len=2)
+                text_3 = self.get_text_value("卖点1介绍")
+                text_paste_base = "ec"
+                text_paste_value = (312, 0)
+
+            if color_name == "鞋底":
+                pp_png = pp_png.resize(value=1000)
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="wc", value=(0, 0))
+                shoe_paste_base = "ec"
+                text_1, text_2 = self.get_text_value("卖点2标题", subsection_len=2)
+                text_3 = self.get_text_value("卖点2介绍")
+                text_paste_base = "wc"
+                text_paste_value = (312, 0)
+            if color_name == "俯视":
+                pp_png = pp_png.resize(value=1418)
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="cc")
+                shoe_paste_base = "wc"
+                text_1, text_2 = self.get_text_value("卖点3标题", subsection_len=2)
+                text_3 = self.get_text_value("卖点3介绍")
+                text_paste_base = "ec"
+                text_paste_value = (312, 0)
+
+            # 添加圆角
+            temp_bg = temp_bg.radius(circular_pos=(1, 1, 1, 1), value=50)
+            # 粘贴到背景上
+            temp_bg = temp_bg.paste_img_invert(top_img=bg, base=shoe_paste_base, value=(99, 0))
+            text_list = []
+            # 卖点主标题
+            text_list.append({"text": text_1,
+                              "font": font_1,
+                              "fill": (2, 2, 2),
+                              "spacing": 0,
+                              })
+            text_list.append({"text": text_2,
+                              "font": font_2,
+                              "fill": (43, 43, 43),
+                              "spacing": 10,
+                              })
+            # 主标题
+            text_list.append({"text": text_3,
+                              "font": font_3,
+                              "fill": (165, 165, 165),
+                              })
+            text_image = self.add_text_list(text_list, spacing=15, base="nc")
+            if text_image:
+                temp_bg = temp_bg.paste_img(
+                    top_img=text_image,
+                    base=text_paste_base,
+                    value=(text_paste_value[0] - text_image.width / 2, text_paste_value[1]))
+            detailed_images.append(temp_bg)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加尺码表
+    def deal_pic_9(self):
+        image_path = r"{}\t (16).png".format(self.root)
+        return PictureProcessing(image_path)
+
+    # 角度展示
+    def deal_pic_10(self):
+        detailed_images = []
+        # 角度展示标题
+        detailed_images.append(PictureProcessing(r"{}\t (17).png".format(self.root)))
+        # 添加颜色名称字体
+        font_color = ImageFont.truetype(r"resources\ttf\puhui\Bold.ttf", 30)
+
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+            # ====颜色主图
+            pp_1 = self.get_overlay_pic_from_dict(
+                goods_art_no=goods_art_no,
+                color_name="俯视",
+                bg_color=(255, 255, 255)
+            )
+            pp_1: PictureProcessing
+            pp_1 = pp_1.resize(value=865)
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (1152, pp_1.height + 100), (255, 255, 255)),
+                                         base="sc"
+                                         )
+            # 粘贴颜色名称
+            pp_1 = pp_1.get_text_image_advanced(
+                value=(90, 50),
+                font=font_color,
+                text=color_name,
+                spacing=1,
+                return_mode="image",
+            )
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (1200, pp_1.height), (243, 243, 243)),
+                                         base="cc"
+                                         )
+
+            detailed_images.append(pp_1)
+
+            # 添加处理各个角度
+            view_name_list = ["内里", "鞋底", "后跟", "侧视"]
+            color_pic_list = []
+            for view_name in view_name_list:
+                _, pp_png = self.image_one_pic(return_orign=True,
+                                               goods_art_no=goods_art_no,
+                                               name=view_name,
+                                               )
+                temp_bg = PictureProcessing("RGB", (546, 338), (243, 243, 243))
+
+                # if view_name == "内里":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+                # if view_name == "鞋底":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+                # if view_name == "后跟":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+                # if view_name == "侧视":
+                #     pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+
+                pp_png = pp_png.resize(base_by_box=(temp_bg.width * 0.9, temp_bg.height * 0.9))
+
+                temp_bg = temp_bg.paste_img(top_img=pp_png, base="cc")
+                temp_bg = temp_bg.radius(value=30)
+                color_pic_list.append(temp_bg)
+
+            # 颜色列表进行等分展示
+            all_color_pp = PictureProcessing().horizontal_distribution(color_pic_list, bg_width=1114, line_spacing=10,
+                                                                       number_per_row=2)
+            all_color_pp = all_color_pp.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1152, all_color_pp.height + 100), (255, 255, 255)), base="cc")
+            all_color_pp = all_color_pp.paste_img_invert(
+                top_img=PictureProcessing("RGB", (1200, all_color_pp.height), (243, 243, 243)), base="cc")
+            detailed_images.append(all_color_pp)
+
+        # 添加底部圆弧分割线
+        b_bg = PictureProcessing("RGB", (1152, 30), (255, 255, 255))
+        b_bg = b_bg.radius(circular_pos=(0, 0, 1, 1), value=30)
+        b_bg = b_bg.paste_img_invert(top_img=PictureProcessing("RGB", (1200, b_bg.height + 50), (243, 243, 243)),
+                                     base="nc")
+        detailed_images.append(b_bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加注意事项
+    def deal_pic_11(self):
+        image_path = r"{}\t (21).png".format(self.root)
+        return PictureProcessing(image_path)

+ 552 - 0
python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie6.py

@@ -0,0 +1,552 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+import time
+
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+from PIL import Image, ImageDraw
+import math
+
+
+class DetailPicGetXiaoSuShuoXie6(DetailBase):
+    need_view = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+    root = r"{}\resources\detail_temp\xiaosushuoxie\6".format(os.getcwd())
+
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None):
+        super().__init__(goods_no, goods_no_value, out_put_dir, windows=windows, excel_data=excel_data,
+                         assigned_page_list=assigned_page_list)
+        self.root = r"{}\resources\detail_temp\xiaosushuoxie\6".format(os.getcwd())
+        print("run xiaosushuoxie-6 ")
+        self.base_bg_color = (228, 196, 147)
+        self.deal_pic_func_list = [
+            self.deal_pic_1,
+            self.deal_pic_2,
+            self.deal_pic_3,
+            self.deal_pic_4,
+            self.deal_pic_5,
+            self.deal_pic_6,
+            self.deal_pic_7,
+            self.deal_pic_8,
+            self.deal_pic_9,
+        ]
+        if test:
+            # pp_1 = self.generate_font_list_to_pic()
+            # pp_1.im.save(r"C:\Users\gymmc\Desktop\细节图示例/字号.png")
+            # for k, v in self.goods_no_value.items():
+            #     print(k, v)
+            self.run_test()
+        else:
+            self.run_all()
+
+    def run_test(self):
+        detailed_images = []
+        # detailed_images.append(self.deal_pic_1())
+        # detailed_images.append(self.deal_pic_2())
+        # detailed_images.append(self.deal_pic_3())
+        # detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        # detailed_images.append(self.deal_pic_6())
+        # detailed_images.append(self.deal_pic_7())
+        # detailed_images.append(self.deal_pic_8())
+        # detailed_images.append(self.deal_pic_9())
+        img = self.add_pic(detailed_images)
+        # img.save(r"{}/{}.jpg".format(self.out_put_dir, self.goods_no, format="JPEG"))
+        img.show()
+
+
+    def deal_details_beifen(self):
+        detailed_images = []
+        self.image_list_append(detailed_images, self.deal_pic_1())
+        self.image_list_append(detailed_images, self.deal_pic_2())
+        self.image_list_append(detailed_images, self.deal_pic_3())
+        self.image_list_append(detailed_images, self.deal_pic_4())
+        self.image_list_append(detailed_images, self.deal_pic_5())
+        self.image_list_append(detailed_images, self.deal_pic_6())
+        self.image_list_append(detailed_images, self.deal_pic_7())
+        self.image_list_append(detailed_images, self.deal_pic_8())
+        self.image_list_append(detailed_images, self.deal_pic_9())
+        return [x for x in detailed_images if x]
+
+    # 组合图1
+    def deal_pic_1(self):
+        pp_bg = PictureProcessing(r"{}\t (1).jpg".format(self.root))
+
+        # -------粘贴文字-------
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 32)  # 设计理念
+        text_list = []
+        text_list.append({"text": self.get_text_value("设计理念"),
+                          "font": font_1,
+                          "fill": (102, 92, 92),
+                          })
+        text_image = self.add_text_list(text_list, spacing=15, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(20, 895))
+
+        # ----粘贴组合图
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+        view_list = ["组合", "俯视"]
+        for view_name in view_list:
+            pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no,
+                                                    name=view_name,
+                                                    )
+            if not pp_jpg_1:
+                continue
+            pp_jpg_1: PictureProcessing
+            pp_png_1: PictureProcessing
+            pp_jpg_1 = pp_jpg_1.resize(base_by_box=(485, 588))
+            pp_png_1 = pp_png_1.resize(base_by_box=(485, 588))
+            pp_bg = pp_bg.to_overlay_pic_advance(top_img=pp_jpg_1, top_png_img=pp_png_1,
+                                                 value=(947 - pp_jpg_1.width / 2, 888 - pp_jpg_1.height / 2), base="nw")
+            break
+
+        return pp_bg
+
+    # 展示设计理念2和组合2
+    def deal_pic_2(self):
+        pp_bg = PictureProcessing(r"{}\t (2).jpg".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 32)  # 设计理念
+
+        text_list = []
+        text_list.append({"text": self.get_text_value("设计理念2"),
+                          "font": font_1,
+                          "fill": (109, 103, 97),
+                          })
+        text_image = self.add_text_list(text_list, spacing=15, base="nw")
+        if text_image:
+            pp_bg = pp_bg.paste_img(
+                top_img=text_image,
+                base="nw",
+                value=(60, 141))
+
+        # ====粘贴组合2或侧视图
+        goods_art_no_list = list(self.data.keys())
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="组合2",
+                                                )
+        if not pp_jpg_1:
+            pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no_list[0],
+                                                    name="侧视",
+                                                    )
+        pp_jpg_1: PictureProcessing
+        pp_png_1: PictureProcessing
+        pp_jpg_1 = pp_jpg_1.resize(base_by_box=(546, 592))
+        pp_png_1 = pp_png_1.resize(base_by_box=(546, 592))
+
+        pp_bg = pp_bg.to_overlay_pic_advance(top_img=pp_jpg_1, top_png_img=pp_png_1,
+                                             value=(786 - pp_jpg_1.width / 2, 816 - pp_jpg_1.height / 2))
+
+        return pp_bg
+
+    # 亮点解析
+    def deal_pic_3(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (3).jpg".format(self.root)))
+        goods_art_no_list = list(self.data.keys())
+
+        # =====添加内里图
+        pp_bg_1 = PictureProcessing(r"{}\t (4).jpg".format(self.root))
+        pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no_list[0],
+                                                name="内里",
+                                                )
+        pp_png_1: PictureProcessing
+        pp_png_1 = pp_png_1.resize(value=1600)
+        pp_png_1 = pp_png_1.paste_img_invert(top_img=PictureProcessing("RGB", (1200, 1061), (255, 255, 255)),
+                                             base="ws",
+                                             value=(-85, 0))
+
+        # 添加内里描述
+        _neili_bg = PictureProcessing(r"{}\t (25).png".format(self.root))
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 38)  # 内里描述
+        text_list = [{"text": self.get_text_value("内里特点"),
+                      "font": font_1,
+                      "fill": (110, 110, 110),
+                      }]
+        text_image = self.add_text_list(text_list, spacing=10, base="nc")
+        if text_image:
+            _neili_bg = _neili_bg.paste_img(
+                top_img=text_image,
+                base="sc",
+                value=(0, 69))
+        pp_bg_1 = pp_bg_1.paste_img(top_img=pp_png_1, base="sc", value=(0, 266))
+        pp_bg_1 = pp_bg_1.paste_img(top_img=_neili_bg, base="sc", value=(0, 137))
+        detailed_images.append(pp_bg_1)
+
+        # ======添加鞋底图====
+        pp_2 = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                              color_name="鞋底",
+                                              bg_color=(255, 255, 255),
+                                              )
+        pp_2 = pp_2.resize(value=1396)
+        pp_bg_2 = PictureProcessing("RGB", (1200, pp_2.height + 80), (255, 255, 255))
+        pp_bg_2 = pp_bg_2.paste_img(top_img=pp_2, base="wc", value=(80, 0))
+        detailed_images.append(pp_bg_2)
+
+        # --添加鞋底特点
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 34)  # 内里描述
+        text_list = [{"text": self.get_text_value("鞋底特点"),
+                      "font": font_1,
+                      "fill": (110, 110, 110),
+                      }]
+        text_image = self.add_text_list(text_list, spacing=10, base="nc")
+        if text_image:
+            pp_bg_3 = PictureProcessing("RGB", (1200, text_image.height + 80), (255, 255, 255))
+            pp_bg_3 = pp_bg_3.paste_img(top_img=text_image, base="cc", value=(0, 0))
+            detailed_images.append(pp_bg_3)
+
+        # ---添加鞋子的俯视图,并标注尺寸----------------
+        pp_bg_4 = PictureProcessing(r"{}\t (6).jpg".format(self.root))
+        pp_4 = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                              color_name="俯视",
+                                              bg_color=(255, 255, 255),
+                                              )
+        pp_4 = pp_4.resize(value=332 * 1.8)
+        pp_bg_4 = pp_bg_4.paste_img(top_img=pp_4, base="sc", value=(0, 222))
+        # -添加文字描述等
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 28)
+        if self.get_text_value("鞋长"):
+            text = "长:{}".format(self.get_text_value("鞋长"))
+            text_image = pp_bg_4.get_text_image_advanced(font=font_1,
+                                                         text=text,
+                                                         fill=(110, 110, 110),
+                                                         return_mode="min_image")
+            pp_bg_4 = pp_bg_4.paste_img(top_img=text_image, base="sc", value=(0, 134))
+        if self.get_text_value("跟高"):
+            text = "跟高:{}".format(self.get_text_value("跟高"))
+            text_image = pp_bg_4.get_text_image_advanced(font=font_1,
+                                                         text=text,
+                                                         fill=(110, 110, 110),
+                                                         return_mode="min_image")
+            pp_bg_4 = pp_bg_4.paste_img(top_img=text_image, base="se", value=(72, 404))
+        if self.get_text_value("脚掌围"):
+            text = "脚掌围:{}".format(self.get_text_value("脚掌围"))
+            text_image = pp_bg_4.get_text_image_advanced(font=font_1,
+                                                         text=text,
+                                                         fill=(110, 110, 110),
+                                                         return_mode="min_image")
+            pp_bg_4 = pp_bg_4.paste_img(top_img=text_image, base="sw", value=(179, 510))
+        if pp_bg_4.height > pp_4.height + 250:
+            pp_bg_4 = pp_bg_4.crop(bbox=(0, pp_bg_4.height - pp_4.height - 250, pp_bg_4.width, pp_bg_4.height))
+
+        detailed_images.append(pp_bg_4)
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 展示产品信息和颜色图
+    def deal_pic_4(self):
+        detailed_images = []
+
+        pp_bg_1 = PictureProcessing(r"{}\t (7).jpg".format(self.root))
+
+        # 粘贴产品信息
+        # ---添加组合2图片
+        color_name_list = ["组合2", "组合", "俯视"]
+
+        goods_art_no_list = list(self.data.keys())
+
+        for color_name in color_name_list:
+            pp_jpg_1, pp_png_1 = self.image_one_pic(return_orign=True,
+                                                    goods_art_no=goods_art_no_list[0],
+                                                    name=color_name,
+                                                    )
+            if not pp_png_1:
+                continue
+
+            pp_jpg_1: PictureProcessing
+            pp_png_1: PictureProcessing
+
+            pp_jpg_1 = pp_jpg_1.resize(base_by_box=(589, 416))
+            pp_png_1 = pp_png_1.resize(base_by_box=(589, 416))
+            pp_bg_1 = pp_bg_1.to_overlay_pic_advance(top_img=pp_jpg_1, top_png_img=pp_png_1, base="nc", value=(0, 257))
+
+            break
+
+        # ====添加文字信息
+        text_list = [{"title": "帮面材质:", "get_text": "鞋面材质"},
+                     {"title": "鞋型:", "get_text": "鞋型"},
+                     {"title": "内里材质:", "get_text": "内里材质"},
+                     {"title": "头型:", "get_text": "头型"},
+                     {"title": "大底材质:", "get_text": "鞋底材质"},
+                     {"title": "货号:", "get_text": "款号"},
+                     ]
+        x, y = 176, 779
+        pos_list = [
+            (x, y),
+            (x + 592, y),
+            (x, y + 101),
+            (x + 592, y + 101),
+            (x, y + 101 + 101),
+            (x + 592, y + 101 + 101),
+        ]
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Regular.ttf", 34)  # 鞋材料信息
+
+        n = -1
+        for text_data in text_list:
+            text_value = self.get_text_value(text_data["get_text"])
+            if text_value:
+                n += 1
+                text = "{}  {}".format(text_data["title"], text_value)
+                text_image = pp_bg_1.get_text_image_advanced(font=font_1,
+                                                             text=text,
+                                                             fill=(0, 0, 0),
+                                                             return_mode="min_image")
+                pp_bg_1 = pp_bg_1.paste_img(top_img=text_image, base="nw", value=pos_list[n])
+
+        detailed_images.append(pp_bg_1)
+        # =======添加各个颜色====多余高度需要剪裁
+        pp_bg_2 = PictureProcessing(r"{}\t (8).jpg".format(self.root))
+
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 28)  # 颜色名称
+        color_pic_list_1 = []
+        color_pic_list_2 = []
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+
+            pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                                goods_art_no=goods_art_no,
+                                                name="侧视",
+                                                )
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(value=390)
+            pp_png = pp_png.resize(value=390)
+
+            pp_jpg = pp_jpg.paste_img_invert(
+                top_img=PictureProcessing("RGBA", (pp_jpg.width, pp_jpg.height + 70), (255, 255, 255, 0)),
+                base="nw"
+            )
+            pp_png = pp_png.paste_img_invert(
+                top_img=PictureProcessing("RGBA", (pp_png.width, pp_png.height + 70), (255, 255, 255, 0)),
+                base="nw"
+            )
+
+            text_image = pp_bg_2.get_text_image_advanced(font=font_2,
+                                                         text="{} / COLOR".format(color_name),
+                                                         fill=(0, 0, 0),
+                                                         return_mode="min_image")
+
+            pp_jpg = pp_jpg.paste_img(top_img=text_image, base="sc", value=(0, 10))
+            color_pic_list_1.append(pp_jpg)
+            color_pic_list_2.append(pp_png)
+
+        # 颜色列表进行等分展示
+        all_color_pp_1 = PictureProcessing().horizontal_distribution(color_pic_list_1, bg_width=1114, line_spacing=10,
+                                                                     number_per_row=2)
+        all_color_pp_2 = PictureProcessing().horizontal_distribution(color_pic_list_2, bg_width=1114, line_spacing=10,
+                                                                     number_per_row=2)
+
+        pp_bg_2 = pp_bg_2.to_overlay_pic_advance(top_img=all_color_pp_1, top_png_img=all_color_pp_2, base="nc",
+                                                 value=(0, 131))
+
+        if pp_bg_2.height > all_color_pp_1.height + 131 + 50:
+            pp_bg_2 = pp_bg_2.crop(
+                bbox=(0, 0, pp_bg_2.width, all_color_pp_1.height + 181))
+
+        detailed_images.append(pp_bg_2)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 产品细节
+    def deal_pic_5(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (9).jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\t (10).jpg".format(self.root)))
+
+        # ==================俯视图鞋头===============
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 60)  # 主标题
+        font_1_color = (1, 1, 1)
+        font_2 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 28)  # 副标题
+        font_2_color = (51, 51, 51)
+        goods_art_no_list = list(self.data.keys())
+
+        view_list = ["俯视", "侧视", "内里", "鞋底"]
+        for index, view_name in enumerate(view_list):
+            bg = PictureProcessing(r"{}\t (11).jpg".format(self.root))
+            text_list = []
+            text_list.append({"text": self.get_text_value("提示{}主标题".format(index + 1)),
+                              "font": font_1,
+                              "fill": font_1_color,
+                              })
+            # self.get_text_value("提示{}副标题".format(index + 1))
+            # text = "副标题副标题副标题副标题副标题副标题副标题副标题\n副标题副标题副标题副标题副标题副标题副标题副标题\n副标题\n副标题\n副标题\n副标题\n副标题"
+            text_list.append({"text": self.get_text_value("提示{}副标题".format(index + 1)),
+                              "font": font_2,
+                              "fill": font_2_color,
+                              })
+            text_image = self.add_text_list(text_list, spacing=30, base="nc")
+            if text_image:
+                bg = bg.paste_img(
+                    top_img=text_image,
+                    base="nc",
+                    value=(0, 0))
+                bg = bg.crop(bbox=(0, 0, bg.width, text_image.height + 50))
+                detailed_images.append(bg)
+
+            # ===粘贴图片
+            pp_1 = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no_list[0],
+                                                  color_name=view_name,
+                                                  bg_color=(255, 255, 255)
+                                                  )
+
+            if view_name == "俯视":
+                resize_value = 1011
+                paste_img_value = (252, 0)
+                base = "ws"
+            if view_name == "侧视":
+                resize_value = 1253
+                paste_img_value = (270, 0)
+                base = "es"
+            if view_name == "内里":
+                resize_value = 1539
+                paste_img_value = (0, 0)
+                base = "ws"
+            if view_name == "鞋底":
+                resize_value = 1104
+                paste_img_value = (0, 0)
+                base = "cc"
+
+            pp_1 = pp_1.resize(value=resize_value)
+
+            pp_1 = pp_1.paste_img_invert(top_img=PictureProcessing("RGB", (1079, 572), (255, 255, 255)),
+                                         base=base,
+                                         value=paste_img_value
+                                         )
+            bg = PictureProcessing(r"{}\t (11).jpg".format(self.root))
+            bg = bg.paste_img(top_img=pp_1,
+                              base="nc",
+                              value=(0, 0),
+                              )
+            bg = bg.crop(bbox=(0, 0, bg.width, pp_1.height + 10))
+            detailed_images.append(bg)
+
+        detailed_images.append(PictureProcessing(r"{}\t (13).jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 组合图展示
+    def deal_pic_6(self):
+        flag = False  # 判断是否有组合图
+        detailed_images = []
+        pp_bg_1 = PictureProcessing(r"{}\t (15).jpg".format(self.root))
+        detailed_images.append(pp_bg_1)
+        goods_art_no_list = list(self.data.keys())
+        goods_art_no = goods_art_no_list[0]
+
+        # 组合图1
+        pp_bg_2 = PictureProcessing(r"{}\t (16).jpg".format(self.root))
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no,
+                                            name="组合",
+                                            )
+        if pp_jpg:
+            flag = True
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(base_by_box=(910 * 0.9, 800 * 0.9))
+            pp_png = pp_png.resize(base_by_box=(910 * 0.9, 800 * 0.9))
+            pp_bg_2 = pp_bg_2.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="cc")
+            detailed_images.append(pp_bg_2)
+
+        # ============组合图2
+        pp_bg_3 = PictureProcessing(r"{}\t (17).jpg".format(self.root))
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no,
+                                            name="组合2",
+                                            )
+        if pp_jpg:
+            flag = True
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(base_by_box=(937 * 0.9, 827 * 0.9))
+            pp_png = pp_png.resize(base_by_box=(937 * 0.9, 827 * 0.9))
+            pp_bg_3 = pp_bg_3.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="nw", value=(124, 446))
+            detailed_images.append(pp_bg_3)
+
+        # ==========组合图3
+        pp_bg_4 = PictureProcessing(r"{}\t (18).jpg".format(self.root))
+        detailed_images.append(pp_bg_4)
+        pp_bg_5 = PictureProcessing(r"{}\t (19).jpg".format(self.root))
+        pp_jpg, pp_png = self.image_one_pic(return_orign=True,
+                                            goods_art_no=goods_art_no,
+                                            name="组合3",
+                                            )
+        if pp_jpg:
+            flag = True
+            pp_jpg: PictureProcessing
+            pp_jpg = pp_jpg.resize(base_by_box=(1062, 937))
+            pp_png = pp_png.resize(base_by_box=(1062, 937))
+            pp_bg_5 = pp_bg_5.to_overlay_pic_advance(top_img=pp_jpg, top_png_img=pp_png, base="cc")
+            detailed_images.append(pp_bg_5)
+
+        if flag:
+            return PictureProcessing(im=self.add_pic(detailed_images))
+        else:
+            return None
+
+    # 各个颜色展示
+    def deal_pic_7(self):
+        detailed_images = []
+        font_1 = ImageFont.truetype(r"resources\ttf\puhui\Medium.ttf", 32)  # 副标题
+        view_list = ["俯视", "侧视", "后跟", "鞋底"]
+        bg_color = (241, 239, 240)
+        for goods_art_no_dict in self.goods_no_value["货号资料"]:
+            bg = PictureProcessing(r"{}\t (20).jpg".format(self.root))
+            color_name = goods_art_no_dict["颜色名称"]
+            goods_art_no = goods_art_no_dict["货号"]
+
+            # 粘贴颜色名称
+            text_image = bg.get_text_image_advanced(font=font_1,
+                                                    text="{}".format(color_name),
+                                                    fill=(0, 0, 0),
+                                                    return_mode="min_image")
+            bg = bg.paste_img(top_img=text_image, base="nw", value=(85, 35))
+
+            # 粘贴图片
+            for view_name in view_list:
+                pp = self.get_overlay_pic_from_dict(goods_art_no=goods_art_no,
+                                                    color_name=view_name,
+                                                    bg_color=bg_color,
+                                                    )
+                if view_name == "俯视":
+                    pp = pp.resize(base_by_box=(779 * 0.8, 838 * 0.8))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (779, 836), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(38, 152))
+                if view_name == "侧视":
+                    pp = pp.resize(base_by_box=(321, 244))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (321, 244), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(844, 152))
+
+                if view_name == "后跟":
+                    pp = pp.resize(base_by_box=(321 * 0.7, 278 * 0.9))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (321, 278), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(844, 417))
+                if view_name == "鞋底":
+                    pp = pp.resize(base_by_box=(321, 270))
+                    pp = pp.paste_img_invert(top_img=PictureProcessing("RGB", (321, 270), bg_color), base="cc")
+                    bg = bg.paste_img(top_img=pp, value=(844, 720))
+            detailed_images.append(bg)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加注意事项
+    def deal_pic_8(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (22).jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    # 添加注意事项
+    def deal_pic_9(self):
+        detailed_images = []
+        detailed_images.append(PictureProcessing(r"{}\t (23).jpg".format(self.root)))
+        detailed_images.append(PictureProcessing(r"{}\t (24).jpg".format(self.root)))
+        return PictureProcessing(im=self.add_pic(detailed_images))

+ 206 - 0
python/service/generate_goods_no_detail_pic/detial_hqt.py

@@ -0,0 +1,206 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import os
+from PIL import ImageFont
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+
+class DetailPicGetHqt(DetailBase):
+    need_view = ["俯视","侧视","后跟","鞋底","内里"]
+    root = r"{}\resources\detail_temp\hongqingting\1".format(os.getcwd())
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None, test=False, excel_data=None,
+                 assigned_page_list=None,**kwargs):
+        super().__init__(goods_no, goods_no_value, out_put_dir,windows=windows)
+        self.root = r"{}\resources\detail_temp\hongqingting\1".format(os.getcwd())
+        self.run()
+
+    def run(self):
+        detailed_images = self.deal_details()
+
+        self.create_folder(self.out_put_dir)
+        detail_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(detail_path)
+        join_path = "{out_put_dir}/{goods_no}/拼接图".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(join_path)
+
+        for index, pp in enumerate(detailed_images):
+            pp.im.save("{}/{}({}).jpg".format(detail_path, self.goods_no, str(index + 11).zfill(2)), format="JPEG")
+
+        img = self.add_pic(detailed_images)
+        img.save("{}/1.jpg".format(join_path), format="JPEG")
+
+        # ------------移动其他图片---------------------
+        self.move_other_pic()
+        return True
+
+    def deal_details(self):
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+        detailed_images = []
+        detailed_images.append(self.deal_pic_1())
+        detailed_images.append(self.deal_pic_2())
+        detailed_images.append(self.deal_pic_3())
+        detailed_images.append(self.deal_pic_4())
+        detailed_images.append(self.deal_pic_5())
+        return [x for x in detailed_images if x]
+
+    def deal_pic_1(self):
+        bg_color = (246, 246, 246)
+        """   制作主图  """
+        goods_art_no_list = list(self.data.keys())
+
+        pp_1 = self.data[goods_art_no_list[0]]["pic_is_deal"]["侧视"]
+        pp_image = pp_1.resize(mode="relative",
+                               base="by_im",
+                               value=0,
+                               base_im=PictureProcessing("RGB", (550, 340), (248, 248, 248)),
+                               percentage=0)
+
+        pp_image = PictureProcessing("RGB", (550, 340), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+
+        bg_img = PictureProcessing(r"{}\image (1).jpg".format(self.root)).paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 263))
+        # bg_img.show()
+
+        #   ---------- 第二张图粘贴
+        pp_2 = self.data[goods_art_no_list[0]]["pic_is_deal"]["俯视"]
+        pp_image = pp_2.resize(mode="relative",
+                               base="by_im",
+                               value=0,
+                               base_im=PictureProcessing("RGB", (550, 340), (248, 248, 248)),
+                               percentage=0)
+        pp_image = PictureProcessing("RGB", (550, 340), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+        bg_img = bg_img.paste_img(
+            mode="pixel", top_img=pp_image, base="nw", value=(26, 626))
+
+        # -------粘贴文字-------
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        text_list = [("商品款号", self.goods_no_value["款号"], 700, 297),
+                     ("商品面料", self.goods_no_value["商品面料"], 700, 380),
+                     ("商品内里", self.goods_no_value["商品内里"], 700, 469),
+                     ("商品鞋底", self.goods_no_value["商品鞋底"], 700, 549),
+                     ("后帮高", self.goods_no_value["后帮高"], 700, 667),
+                     ("前掌宽", self.goods_no_value["前掌宽"], 700, 751),
+                     ("鞋跟高", self.goods_no_value["鞋跟高"], 700, 828), ]
+        for i in text_list:
+            bg_img = bg_img.add_text(mode="pixel",
+                                     value=(i[2], i[3]),
+                                     font=font,
+                                     text="{}:{}".format(i[0], i[1]),
+                                     align="center",
+                                     spacing=10,
+                                     fill=(17, 16, 16))
+        return bg_img
+
+    def deal_pic_2(self):
+        # 设计师说展示图
+        # 设计师说
+        if "FAB说明" not in self.goods_no_value:
+            return
+        text = self.goods_no_value["FAB说明"]  # FAB说明
+        if not text:
+            return
+        pp_1 = PictureProcessing(r"{}\image (2).jpg".format(self.root))
+
+        text = text.replace(r"\n", "\n")
+        _ = text.split("\n")
+        text_img = PictureProcessing("RGB", (1200, len(_) * 100 + 30), (255, 255, 255))
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+
+        text_img = text_img.add_text(mode="pixel",
+                                     value=(600, 50),
+                                     font=font,
+                                     text=text,
+                                     anchor="ma",
+                                     align="center",
+                                     spacing=50,
+                                     fill=(83, 83, 83))
+        new_image = PictureProcessing("RGB", (1200, pp_1.height + text_img.height), (255, 255, 255))
+        new_image = new_image.paste_img(mode="pixel", top_img=pp_1, value=(0, 0))
+        new_image = new_image.paste_img(mode="pixel", top_img=text_img, value=(0, pp_1.height))
+        return new_image
+
+    def deal_pic_3(self):
+        # 制作角度展示图
+        bg_color = (246, 246, 246)
+        detailed_images = []
+        pp_1 = PictureProcessing(r"{}\image (3).jpg".format(self.root))
+        detailed_images.append(pp_1)
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["侧视", "俯视", "后跟", "鞋底"]
+            else:
+                name_list = ["侧视", "俯视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                    continue
+
+                _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+                _pp = _pp.resize(mode="pixel", base="width", value=649 if _name == "后跟" else 1058, )
+                bg_img = PictureProcessing("RGBA", (1200, int(_pp.height + 50)), bg_color)
+                bg_img = bg_img.paste_img(mode="pixel", top_img=_pp, base="center")
+
+                detailed_images.append(bg_img)
+                # 添加分割线
+                detailed_images.append(PictureProcessing("RGB", (1200, 50), (255, 255, 255)))
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_4(self):
+        # 制作详细图
+        bg_color = (246, 246, 246)
+        detailed_images = []
+        pp_1 = PictureProcessing(r"{}\image (4).jpg".format(self.root))
+        detailed_images.append(pp_1)
+        name_list = ["俯视", "内里", "后跟", "鞋底"]
+        goods_art_no = list(self.data.keys())[0]
+
+        view_dict = {"俯视": (0, 0, 1100, 1200),
+                     "内里": (-1, -100, 1100, 1200),
+                     "后跟": (0, -100, 1100, 1200),
+                     "鞋底": (0, -100, 1100, 1200),
+                     }
+
+        for _name in name_list:
+            # 处理图片,需要粘贴到背景等处理
+            if _name not in self.data[goods_art_no]["pic_is_deal"]:
+                continue
+            _pp = self.data[goods_art_no]["pic_is_deal"][_name]
+            _pp = _pp.resize(mode="pixel", base="width", value=1300 if _name == "后跟" else 2200, )
+            _pp = _pp.crop_img(mode="pixel", base="sw", value=view_dict[_name], color_fill=bg_color)
+            _pp = _pp.radius(mode="relative", circular_pos=(1, 0, 0, 1), value=100)
+            bg_pp = PictureProcessing("RGB", (1200, 1316), (255, 255, 255))
+            bg_pp.paste_img(mode="pixel", top_img=_pp, base="ec", value=(0, 0))
+            detailed_images.append(bg_pp)
+
+        return PictureProcessing(im=self.add_pic(detailed_images))
+
+    def deal_pic_5(self):
+        # 其他图片添加
+        pp_1 = PictureProcessing(r"{}\image (5).jpg".format(self.root))
+        return pp_1
+
+
+if __name__ == '__main__':
+    import json
+
+    with open(
+            r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\data2.txt",
+            "r", encoding="utf-8") as f:
+        data = json.loads(f.read())
+
+    for goods_no, value in data.items():
+        d = DetailPicGetHqt(goods_no, value, out_put_dir=r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\out_put\测试文件夹\详情图生成")
+        raise 1

+ 1177 - 0
python/service/generate_goods_no_detail_pic/goods_detail_pics.py

@@ -0,0 +1,1177 @@
+"""
+步骤:
+1、整理需要处理的款号图-输出款号图文件夹
+2、整理所有相关的图片作为素材图
+3、按要求进行拼接
+"""
+import cv2
+import numpy as np
+from PIL import Image, ImageFont
+import os
+import settings
+from module.base_mode.image_pic_deal import OnePicDeal
+from collections import defaultdict
+from module.view_control.generate_goods_no_detail_pic.detail_generate_base import DetailBase
+from module.view_control.generate_goods_no_detail_pic.pic_deal import PictureProcessing
+
+
+class DetailPicGet备份(DetailBase):
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, ):
+        super().__init__(goods_no, goods_no_value, out_put_dir)
+        self.goods_no = goods_no
+        self.out_put_dir = out_put_dir
+        self.goods_no_value = goods_no_value
+        self.data = {}
+        for goods_art_no_value in goods_no_value["货号资料"]:
+            self.data[goods_art_no_value["货号"]] = {}
+            for pic_name, pic_path in goods_art_no_value["pics"].items():
+                pic_name = pic_name.replace("俯视图", "俯视")
+                pic_name = pic_name.replace("侧视图", "侧视")
+                pic_name = pic_name.replace("后跟图", "后跟")
+                pic_name = pic_name.replace("鞋底图", "鞋底")
+                pic_name = pic_name.replace("内里图", "内里")
+                print(pic_name)
+                self.data[goods_art_no_value["货号"]][pic_name] = pic_path
+
+        self.run()
+        pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+
+    def image_init(self):
+        name_size_dict = {"俯视": 1600,
+                          "侧视": 1600,
+                          "后跟": 800,
+                          "鞋底": 1600,
+                          "内里": 1600,
+                          }
+        # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
+        bg_color = (246, 246, 246)
+        for _goods_art_no, value in self.data.items():
+            name_list = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+            for _name in name_list:
+                # 打开第一张图
+                im1 = Image.open(self.data[_goods_art_no]["{name}-阴影".format(name=_name)])
+                im2 = Image.open(self.data[_goods_art_no]["{name}-抠图".format(name=_name)])
+                im = self.get_overlay_pic(im1, im2, bg_color)
+                im = self.to_resize(im, width=name_size_dict[_name])
+                # 保留最小区域
+                # im = self.get_goods_pos(im, im2)
+                self.data[_goods_art_no][_name] = im
+
+    def run(self):
+        self.image_init()
+        bg_color = (246, 246, 246)
+        detailed_images = []
+
+        """   制作主图  """
+        goods_art_no_list = list(self.data.keys())
+        # if len(goods_art_no_list) > 1:
+        #     im_1 = self.data[goods_art_no_list[0]]["侧视"]
+        #     im_2 = self.data[goods_art_no_list[1]]["俯视"]
+        # else:
+        im_1 = self.data[goods_art_no_list[0]]["侧视"]
+        im_2 = self.data[goods_art_no_list[0]]["俯视"]
+
+        data = []
+        data.append({"command": "resize",
+                     "plugins_mode": "relative",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                     "base_im": {"im": Image.new("RGB", (550, 340), (248, 248, 248))},  # 参考基于其他图 PIL格式
+                     "base": "by_im",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                     "value": 0,  # 固定值,如果为百分比,则为0
+                     "percentage": 0, })
+        image = self.deal_one_pic(img=im_1, data=data)
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (0, 0),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+
+
+
+        image = self.deal_one_pic(img=Image.new("RGB", (550, 340), bg_color), data=data)
+        data = []
+        bg_img = Image.open(r"resources\detail_image_2\image (1).jpg")
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "nw",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (26, 263),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        bg_img = self.deal_one_pic(img=bg_img, data=data)
+        #   ---------- 第二张图处理
+        data = []
+        data.append({"command": "resize",
+                     "plugins_mode": "relative",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                     "base_im": {"im": Image.new("RGB", (550, 340), (248, 248, 248))},  # 参考基于其他图 PIL格式
+                     "base": "by_im",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                     "value": 0,  # 固定值,如果为百分比,则为0
+                     "percentage": 0, })  # 百分比
+        image = self.deal_one_pic(img=im_2, data=data)
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (0, 0),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        image = self.deal_one_pic(img=Image.new("RGB", (550, 340), bg_color), data=data)
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "nw",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (26, 626),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        _d = [("商品款号", self.goods_no_value["款号"], 700, 297),
+              ("商品面料", self.goods_no_value["商品面料"], 700, 380),
+              ("商品内里", self.goods_no_value["商品内里"], 700, 469),
+              ("商品鞋底", self.goods_no_value["商品鞋底"], 700, 549),
+              ("后帮高", self.goods_no_value["后帮高"], 700, 667),
+              ("前掌宽", self.goods_no_value["前掌宽"], 700, 751),
+              ("鞋跟高", self.goods_no_value["鞋跟高"], 700, 828), ]
+
+        for i in _d:
+            if i[1]:
+                data.append({
+                    "command": "add_text",
+                    "pos": {"plugins_mode": "pixel",  # pixel  relative
+                            "value": (i[2], i[3]), },
+                    "font": font,
+                    "text": "{}:{}".format(i[0], i[1]),
+                    "align": "center",
+                    "direction": "",
+                    "max_len_one_line": "",
+                    "spacing": 10,
+                    "fill": (17, 16, 16),
+                })
+
+        image = self.deal_one_pic(img=bg_img, data=data)
+        detailed_images.append(image)
+
+        """               设计师说展示图             """
+        # 设计师说
+        text = self.goods_no_value["FAB说明"]
+        if text:
+            detailed_images.append(Image.open(r"resources\detail_image_2\image (2).jpg"))
+            data = []
+            text = text.replace(r"\n", "\n")
+            _ = text.split("\n")
+            bg_img = Image.new("RGB", (1200, len(_) * 100 + 30), (255, 255, 255))
+            data.append({
+                "command": "add_text",
+                "pos": {"plugins_mode": "pixel",  # pixel  relative
+                        "value": (600, 50), },
+                "font": font,
+                "text": text,
+                "anchor": "ma",
+                "align": "center",
+                "direction": "",
+                "max_len_one_line": "",
+                "spacing": 50,
+                "fill": (83, 83, 83),
+            })
+            image = self.deal_one_pic(img=bg_img, data=data)
+            detailed_images.append(image)
+        # image.show()
+        # raise 1
+        """               制作角度展示图             """
+        # 角度展示
+        detailed_images.append(Image.open(r"resources\detail_image_2\image (3).jpg"))
+
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["侧视", "俯视", "后跟", "鞋底"]
+            else:
+                name_list = ["侧视", "俯视", ]
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                _image = self.data[goods_art_no][_name]
+
+                data = []
+                data.append({"command": "resize",
+                             "plugins_mode": "pixel",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                             "base": "width",
+                             # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                             "value": 649 if _name == "后跟" else 1058,  # 固定值,如果为百分比,则为0
+                             })  # 百分比
+
+                _image = self.deal_one_pic(img=_image, data=data)
+                data = []
+                data.append({
+                    "command": "paste_img",
+                    "img": {"im": _image},
+                    "pos": {"plugins_mode": "pixel",  # pixel
+                            "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                            "value": (0, 0),
+                            "percentage": (0, 0),
+                            },
+                    "margins": (0, 0, 0, 0),  # 上下左右边距
+                })
+
+                bg_img = Image.new("RGB", (1200, int(_image.height + 50)), bg_color)
+                _image = self.deal_one_pic(img=bg_img, data=data)
+                # _image.show()
+                detailed_images.append(_image)
+                detailed_images.append(Image.new("RGB", (1200, 50), (255, 255, 255)))
+
+        """     制作详细图      """
+        detailed_images.append(Image.open(r"resources\detail_image_2\image (4).jpg"))
+        views = ["俯视", "内里", "后跟", "鞋底"]
+        goods_art_no = list(self.data.keys())[0]
+        for view in views:
+            to_paste_img = self.data[goods_art_no][view]
+            data = []
+            # 单图缩放处理
+            data.append({"command": "resize",
+                         "plugins_mode": "pixel",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                         "base_im": {"im": ""},  # 参考基于其他图 PIL格式
+                         "base": "width",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                         "value": 1300 if view == "后跟" else 2200,  # 固定值,如果为百分比,则为0
+                         "percentage": 0, })  # 百分比
+
+            view_dict = {"俯视": (0, 0, 1100, 1200),
+                         "内里": (-1, -100, 1100, 1200),
+                         "后跟": (0, -100, 1100, 1200),
+                         "鞋底": (0, -100, 1100, 1200),
+                         }
+
+            data.append({
+                "command": "crop_img",
+                "img": "",
+                "pos": {"plugins_mode": "pixel",  # pixel
+                        "base": "sw",  # nw,nc,ne,ec ... 各个方向参考点
+                        "value": view_dict[view],
+                        },
+                "color_fill": bg_color,
+            })
+
+            # 处理圆角
+            data.append({"command": "radius",  # radius
+                         "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+                         "circular_pos": (1, 0, 0, 1),  # 从左上角顺时针,记录圆角数量
+                         "value": 100,  # 固定值,如果为百分比,则为0
+                         "percentage": 0, })  # 百分比
+
+            to_paste_img = self.deal_one_pic(to_paste_img, data)
+            # print(to_paste_img.size)
+
+            # 粘贴到白底图上
+            img = Image.new("RGB", (1200, 1316), (255, 255, 255))
+            data = []
+            data.append({
+                "command": "paste_img",
+                "img": {"im": to_paste_img},
+                "pos": {"plugins_mode": "pixel",  # pixel  relative
+                        "base": "wc",  # nw,nc,ne,ec,... 各个方向参考点
+                        "value": (100, 0),
+                        "percentage": "",
+                        },
+                "margins": (0, 0, 0, 0),  # 上下左右边距
+            })
+            img = self.deal_one_pic(img, data)
+            # if view == "后跟":
+            #     img.show()
+            detailed_images.append(img)
+
+        detailed_images.append(Image.open(r"resources\detail_image_2\image (5).jpg"))
+
+        output_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(output_path)
+
+        for index, im in enumerate(detailed_images):
+            im.save("{}/{}({}).png".format(output_path, self.goods_no, str(index + 11).zfill(2)))
+
+        # img = self.add_pic(detailed_images)
+        # img.save("{}/0_{}_拼接图.png".format(self.out_put_dir, self.goods_no))
+        return True
+
+    def add_pic(self, detailed_images):
+        if not detailed_images:
+            return
+        page_len = 0
+        for index, im in enumerate(detailed_images):
+            page_len += im.height
+        bg_im = Image.new("RGB", (im.width, page_len), (255, 255, 255))
+
+        n = 0
+        for index, im in enumerate(detailed_images):
+            bg_im.paste(im, (0, n))
+            n += im.height
+        # bg_im.show()
+        return bg_im
+
+    def get_goods_pos(self, im, cut_image):
+        # 保留多余内容
+        old_x, old_y = im.size
+        x1, y1, x2, y2 = cut_image.getbbox()
+        goods_w, goods_h = x2 - x1, y2 - y1
+        _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
+        new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
+        new_x1 = 0 if new_x1 < 0 else new_x1
+        new_y1 = 0 if new_y1 < 0 else new_y1
+        new_x2 = old_x if new_x2 > old_x else new_x2
+        new_y2 = old_y if new_y2 > old_y else new_y2
+        # 剪切掉多余的内容,保留阴影
+        im = im.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+        return im
+
+    def set_dict(self, one_dict):
+        _ = defaultdict(str)
+        for i, v in one_dict.items():
+            if isinstance(v, dict):
+                v = self.set_dict(v)
+            _[i] = v
+        return _
+
+    def deal_one_pic(self, img: Image, data=None):
+        """
+        通用图片处理器
+        1、基于某原始图,进行加工,包括粘贴、单图处理等逻辑
+        """
+        data = [self.set_dict(x) for x in data]
+
+        for command in data:
+            if command["command"] == "resize":
+                img = OnePicDeal().resize(img, command)
+                continue
+            if command["command"] == "paste_img":
+                img = OnePicDeal().paste_img(img, command)
+                continue
+            if command["command"] == "crop_img":
+                img = OnePicDeal().crop_img(img, command)
+                continue
+            if command["command"] == "radius":
+                img = OnePicDeal().radius(img, command)
+                continue
+            if command["command"] == "add_text":
+                img = OnePicDeal().add_text(img, command)
+                continue
+
+        return img
+
+    def get_overlay_pic(self, pil_img_1, pil_img_2, color):
+        im_w, im_h = pil_img_1.size
+        cv_im = cv2.cvtColor(np.asarray(pil_img_1), cv2.COLOR_RGB2BGR)
+
+        # 创建一张纯色底图
+        image_white = Image.new("RGB", (im_w, im_h), color)
+
+        cv_image_white = cv2.cvtColor(np.asarray(image_white), cv2.COLOR_RGB2BGR)
+
+        new_im = self.to_color_2(cv_image_white, cv_im)
+        # new_im = cv2.addWeighted(new_im, 0.7, cv_im_2, 0.3, 0)
+        # new_im = cv2.add(new_im, cv_im_2)
+
+        new_im = Image.fromarray(cv2.cvtColor(new_im, cv2.COLOR_BGR2RGB))
+        new_im.paste(pil_img_2, (0, 0), pil_img_2)
+        return new_im
+
+    def to_color_2(self, target, blend):  # 正片叠底
+        return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)
+
+    def to_color_1(self, img1, img2):  # PS颜色模式
+        img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
+        img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
+        img2[:, :, 0] = img1[:, :, 0]
+        img2[:, :, 1] = img1[:, :, 1]
+        res = cv2.cvtColor(img2, cv2.COLOR_HSV2BGR)
+        return res
+
+
+class DetailPicGetHqt(DetailBase):
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, ):
+        super().__init__(goods_no, goods_no_value, out_put_dir)
+
+    def run(self):
+        bg_color = (246, 246, 246)
+        self.image_init(bg_color)
+
+        detailed_images = []
+
+        """   制作主图  """
+        goods_art_no_list = list(self.data.keys())
+
+        # if len(goods_art_no_list) > 1:
+        #     im_1 = self.data[goods_art_no_list[0]]["侧视"]
+        #     im_2 = self.data[goods_art_no_list[1]]["俯视"]
+        # else:
+
+        pp_1 = self.data[goods_art_no_list[0]]["pic_is_deal"]["侧视"]
+        pp_2 = self.data[goods_art_no_list[0]]["pic_is_deal"]["俯视"]
+        pp_image = pp_1.resize(mode="relative",
+                               base="by_im",
+                               value=0,
+                               base_im=PictureProcessing("RGB", (550, 340), (248, 248, 248)),
+                               percentage=0)
+
+        pp_image = PictureProcessing("RGB", (550, 340), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="center"
+        )
+        pp_image = PictureProcessing("RGB", (550, 340), bg_color).paste_img(
+            mode="pixel", top_img=pp_image, base="nw",value= (26, 263),
+        )
+        bg_img = PictureProcessing(r"resources\detail_image_2\image (1).jpg").paste_img(
+            mode="pixel", top_img=pp_image, base="nw",value= (26, 263))
+        bg_img.show()
+
+        return
+        #   ---------- 第二张图处理
+        data = []
+        data.append({"command": "resize",
+                     "plugins_mode": "relative",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                     "base_im": {"im": Image.new("RGB", (550, 340), (248, 248, 248))},  # 参考基于其他图 PIL格式
+                     "base": "by_im",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                     "value": 0,  # 固定值,如果为百分比,则为0
+                     "percentage": 0, })  # 百分比
+        image = self.deal_one_pic(img=im_2, data=data)
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (0, 0),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        image = self.deal_one_pic(img=Image.new("RGB", (550, 340), bg_color), data=data)
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "nw",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (26, 626),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        _d = [("商品款号", self.goods_no_value["款号"], 700, 297),
+              ("商品面料", self.goods_no_value["商品面料"], 700, 380),
+              ("商品内里", self.goods_no_value["商品内里"], 700, 469),
+              ("商品鞋底", self.goods_no_value["商品鞋底"], 700, 549),
+              ("后帮高", self.goods_no_value["后帮高"], 700, 667),
+              ("前掌宽", self.goods_no_value["前掌宽"], 700, 751),
+              ("鞋跟高", self.goods_no_value["鞋跟高"], 700, 828), ]
+
+        for i in _d:
+            if i[1]:
+                data.append({
+                    "command": "add_text",
+                    "pos": {"plugins_mode": "pixel",  # pixel  relative
+                            "value": (i[2], i[3]), },
+                    "font": font,
+                    "text": "{}:{}".format(i[0], i[1]),
+                    "align": "center",
+                    "direction": "",
+                    "max_len_one_line": "",
+                    "spacing": 10,
+                    "fill": (17, 16, 16),
+                })
+
+        image = self.deal_one_pic(img=bg_img, data=data)
+        detailed_images.append(image)
+
+        """               设计师说展示图             """
+        # 设计师说
+        text = self.goods_no_value["FAB说明"]
+        if text:
+            detailed_images.append(Image.open(r"resources\detail_image_2\image (2).jpg"))
+            data = []
+            text = text.replace(r"\n", "\n")
+            _ = text.split("\n")
+            bg_img = Image.new("RGB", (1200, len(_) * 100 + 30), (255, 255, 255))
+            data.append({
+                "command": "add_text",
+                "pos": {"plugins_mode": "pixel",  # pixel  relative
+                        "value": (600, 50), },
+                "font": font,
+                "text": text,
+                "anchor": "ma",
+                "align": "center",
+                "direction": "",
+                "max_len_one_line": "",
+                "spacing": 50,
+                "fill": (83, 83, 83),
+            })
+            image = self.deal_one_pic(img=bg_img, data=data)
+            detailed_images.append(image)
+        # image.show()
+        # raise 1
+        """               制作角度展示图             """
+        # 角度展示
+        detailed_images.append(Image.open(r"resources\detail_image_2\image (3).jpg"))
+
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["侧视", "俯视", "后跟", "鞋底"]
+            else:
+                name_list = ["侧视", "俯视", ]
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                _image = self.data[goods_art_no][_name]
+
+                data = []
+                data.append({"command": "resize",
+                             "plugins_mode": "pixel",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                             "base": "width",
+                             # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                             "value": 649 if _name == "后跟" else 1058,  # 固定值,如果为百分比,则为0
+                             })  # 百分比
+
+                _image = self.deal_one_pic(img=_image, data=data)
+                data = []
+                data.append({
+                    "command": "paste_img",
+                    "img": {"im": _image},
+                    "pos": {"plugins_mode": "pixel",  # pixel
+                            "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                            "value": (0, 0),
+                            "percentage": (0, 0),
+                            },
+                    "margins": (0, 0, 0, 0),  # 上下左右边距
+                })
+
+                bg_img = Image.new("RGB", (1200, int(_image.height + 50)), bg_color)
+                _image = self.deal_one_pic(img=bg_img, data=data)
+                # _image.show()
+                detailed_images.append(_image)
+                detailed_images.append(Image.new("RGB", (1200, 50), (255, 255, 255)))
+
+        """     制作详细图      """
+        detailed_images.append(Image.open(r"resources\detail_image_2\image (4).jpg"))
+        views = ["俯视", "内里", "后跟", "鞋底"]
+        goods_art_no = list(self.data.keys())[0]
+        for view in views:
+            to_paste_img = self.data[goods_art_no][view]
+            data = []
+            # 单图缩放处理
+            data.append({"command": "resize",
+                         "plugins_mode": "pixel",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                         "base_im": {"im": ""},  # 参考基于其他图 PIL格式
+                         "base": "width",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                         "value": 1300 if view == "后跟" else 2200,  # 固定值,如果为百分比,则为0
+                         "percentage": 0, })  # 百分比
+
+            view_dict = {"俯视": (0, 0, 1100, 1200),
+                         "内里": (-1, -100, 1100, 1200),
+                         "后跟": (0, -100, 1100, 1200),
+                         "鞋底": (0, -100, 1100, 1200),
+                         }
+
+            data.append({
+                "command": "crop_img",
+                "img": "",
+                "pos": {"plugins_mode": "pixel",  # pixel
+                        "base": "sw",  # nw,nc,ne,ec ... 各个方向参考点
+                        "value": view_dict[view],
+                        },
+                "color_fill": bg_color,
+            })
+
+            # 处理圆角
+            data.append({"command": "radius",  # radius
+                         "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+                         "circular_pos": (1, 0, 0, 1),  # 从左上角顺时针,记录圆角数量
+                         "value": 100,  # 固定值,如果为百分比,则为0
+                         "percentage": 0, })  # 百分比
+
+            to_paste_img = self.deal_one_pic(to_paste_img, data)
+            # print(to_paste_img.size)
+
+            # 粘贴到白底图上
+            img = Image.new("RGB", (1200, 1316), (255, 255, 255))
+            data = []
+            data.append({
+                "command": "paste_img",
+                "img": {"im": to_paste_img},
+                "pos": {"plugins_mode": "pixel",  # pixel  relative
+                        "base": "wc",  # nw,nc,ne,ec,... 各个方向参考点
+                        "value": (100, 0),
+                        "percentage": "",
+                        },
+                "margins": (0, 0, 0, 0),  # 上下左右边距
+            })
+            img = self.deal_one_pic(img, data)
+            # if view == "后跟":
+            #     img.show()
+            detailed_images.append(img)
+
+        detailed_images.append(Image.open(r"resources\detail_image_2\image (5).jpg"))
+
+        output_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(output_path)
+
+        for index, im in enumerate(detailed_images):
+            im.save("{}/{}({}).png".format(output_path, self.goods_no, str(index + 11).zfill(2)))
+
+        # img = self.add_pic(detailed_images)
+        # img.save("{}/0_{}_拼接图.png".format(self.out_put_dir, self.goods_no))
+        return True
+
+    def add_pic(self, detailed_images):
+        if not detailed_images:
+            return
+        page_len = 0
+        for index, im in enumerate(detailed_images):
+            page_len += im.height
+        bg_im = Image.new("RGB", (im.width, page_len), (255, 255, 255))
+
+        n = 0
+        for index, im in enumerate(detailed_images):
+            bg_im.paste(im, (0, n))
+            n += im.height
+        # bg_im.show()
+        return bg_im
+
+    def get_goods_pos(self, im, cut_image):
+        # 保留多余内容
+        old_x, old_y = im.size
+        x1, y1, x2, y2 = cut_image.getbbox()
+        goods_w, goods_h = x2 - x1, y2 - y1
+        _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
+        new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
+        new_x1 = 0 if new_x1 < 0 else new_x1
+        new_y1 = 0 if new_y1 < 0 else new_y1
+        new_x2 = old_x if new_x2 > old_x else new_x2
+        new_y2 = old_y if new_y2 > old_y else new_y2
+        # 剪切掉多余的内容,保留阴影
+        im = im.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+        return im
+
+    def set_dict(self, one_dict):
+        _ = defaultdict(str)
+        for i, v in one_dict.items():
+            if isinstance(v, dict):
+                v = self.set_dict(v)
+            _[i] = v
+        return _
+
+    def deal_one_pic(self, img: Image, data=None):
+        """
+        通用图片处理器
+        1、基于某原始图,进行加工,包括粘贴、单图处理等逻辑
+        """
+        data = [self.set_dict(x) for x in data]
+
+        for command in data:
+            if command["command"] == "resize":
+                img = OnePicDeal().resize(img, command)
+                continue
+            if command["command"] == "paste_img":
+                img = OnePicDeal().paste_img(img, command)
+                continue
+            if command["command"] == "crop_img":
+                img = OnePicDeal().crop_img(img, command)
+                continue
+            if command["command"] == "radius":
+                img = OnePicDeal().radius(img, command)
+                continue
+            if command["command"] == "add_text":
+                img = OnePicDeal().add_text(img, command)
+                continue
+
+        return img
+
+    def get_overlay_pic(self, pil_img_1, pil_img_2, color):
+        im_w, im_h = pil_img_1.size
+        cv_im = cv2.cvtColor(np.asarray(pil_img_1), cv2.COLOR_RGB2BGR)
+
+        # 创建一张纯色底图
+        image_white = Image.new("RGB", (im_w, im_h), color)
+
+        cv_image_white = cv2.cvtColor(np.asarray(image_white), cv2.COLOR_RGB2BGR)
+
+        new_im = self.to_color_2(cv_image_white, cv_im)
+        # new_im = cv2.addWeighted(new_im, 0.7, cv_im_2, 0.3, 0)
+        # new_im = cv2.add(new_im, cv_im_2)
+
+        new_im = Image.fromarray(cv2.cvtColor(new_im, cv2.COLOR_BGR2RGB))
+        new_im.paste(pil_img_2, (0, 0), pil_img_2)
+        return new_im
+
+    def to_color_2(self, target, blend):  # 正片叠底
+        return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)
+
+    def to_color_1(self, img1, img2):  # PS颜色模式
+        img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
+        img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
+        img2[:, :, 0] = img1[:, :, 0]
+        img2[:, :, 1] = img1[:, :, 1]
+        res = cv2.cvtColor(img2, cv2.COLOR_HSV2BGR)
+        return res
+
+
+class DetailPicGetHLM(object):
+    def __init__(self, goods_no, goods_no_value: dict, out_put_dir, windows=None):
+        self.windows = windows
+        self.goods_no = goods_no
+        self.out_put_dir = out_put_dir
+        self.goods_no_value = goods_no_value
+        self.data = {}
+        for goods_art_no_value in goods_no_value["货号资料"]:
+            self.data[goods_art_no_value["货号"]] = {}
+            for pic_name, pic_path in goods_art_no_value["pics"].items():
+                self.data[goods_art_no_value["货号"]][pic_name] = pic_path
+        self.run()
+        pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+
+    def image_init(self):
+        name_size_dict = {"俯视": 1600,
+                          "侧视": 1600,
+                          "后跟": 800,
+                          "鞋底": 1600,
+                          "内里": 1600,
+                          }
+        # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
+        bg_color = (246, 246, 246)
+        for _goods_art_no, value in self.data.items():
+            if settings.PROJECT == "红蜻蜓":
+                name_list = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+            elif settings.PROJECT == "惠利玛":
+                name_list = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+            else:
+                name_list = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+
+            for _name in name_list:
+                # 打开第一张图
+                try:
+                    im1 = Image.open(self.data[_goods_art_no]["{name}-阴影".format(name=_name)])
+                    im2 = Image.open(self.data[_goods_art_no]["{name}-抠图".format(name=_name)])
+                    im = self.get_overlay_pic(im1, im2, bg_color)
+                    im = self.to_resize(im, width=name_size_dict[_name])
+                    # 保留最小区域
+                    # im = self.get_goods_pos(im, im2)
+                    self.data[_goods_art_no][_name] = im
+                except BaseException as e:
+                    print(e)
+                    pass
+
+    def run(self):
+        self.image_init()
+        bg_color = (246, 246, 246)
+        detailed_images = []
+        """               头部信息             """
+        detailed_images.append(Image.open(r"resources\detail_image_3\image (1).jpg"))
+
+        """   制作主图  """
+        goods_art_no_list = list(self.data.keys())
+
+        im_1 = self.data[goods_art_no_list[0]]["俯视"]
+        im_2 = self.data[goods_art_no_list[0]]["侧视"]
+
+        data = []
+        data.append({"command": "resize",
+                     "plugins_mode": "relative",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                     "base_im": {"im": Image.new("RGB", (604, 418), (248, 248, 248))},  # 参考基于其他图 PIL格式
+                     "base": "by_im",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                     "value": 0,  # 固定值,如果为百分比,则为0
+                     "percentage": 0, })
+        image = self.deal_one_pic(img=im_1, data=data)
+
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (0, 0),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        image = self.deal_one_pic(img=Image.new("RGB", (604, 418), bg_color), data=data)
+
+        data = []
+        bg_img = Image.new("RGB", (1200, 918), (255, 255, 255))
+
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "nw",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (26, 26),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        bg_img = self.deal_one_pic(img=bg_img, data=data)
+        #   ---------- 第二张图处理
+        data = []
+        data.append({"command": "resize",
+                     "plugins_mode": "relative",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                     "base_im": {"im": Image.new("RGB", (604, 418), (248, 248, 248))},  # 参考基于其他图 PIL格式
+                     "base": "by_im",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                     "value": 0,  # 固定值,如果为百分比,则为0
+                     "percentage": 0, })  # 百分比
+        image = self.deal_one_pic(img=im_2, data=data)
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (0, 0),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        image = self.deal_one_pic(img=Image.new("RGB", (604, 418), bg_color), data=data)
+        data = []
+        data.append({
+            "command": "paste_img",
+            "img": {"im": image},
+            "pos": {"plugins_mode": "pixel",  # pixel
+                    "base": "nw",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (26, 26 + 26 + 418),
+                    "percentage": (0, 0),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        })
+        font = ImageFont.truetype(r'resources\ttf\simhei.ttf', 30)
+        _d = [("款号", self.goods_no_value["款号"], 722, 26),
+              ("面料", self.goods_no_value["商品面料"], 722, 106),
+              ("内里", self.goods_no_value["商品内里"], 722, 186),
+              ("鞋底", self.goods_no_value["商品鞋底"], 722, 266), ]
+
+        for i in _d:
+            if i[1]:
+                data.append({
+                    "command": "add_text",
+                    "pos": {"plugins_mode": "pixel",  # pixel  relative
+                            "value": (i[2], i[3]), },
+                    "font": font,
+                    "text": "{}:{}".format(i[0], i[1]),
+                    "align": "center",
+                    "direction": "",
+                    "max_len_one_line": "",
+                    "spacing": 10,
+                    "fill": (17, 16, 16),
+                })
+
+        image = self.deal_one_pic(img=bg_img, data=data)
+        detailed_images.append(image)
+
+        """               尺码表             """
+        detailed_images.append(Image.open(r"resources\detail_image_3\image (2).jpg"))
+
+        """               细节图展示             """
+        view_dict = {"俯视": {"crop_img_value": (0, 0, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     "侧视": {"crop_img_value": (888, 0, 1086, 752),
+                            "crop_img_base": "sw",
+                            "paste_img_value": (0, 0),
+                            "paste_img_base": "center",
+                            },
+                     }
+
+        goods_art_no = list(self.data.keys())[0]
+        for view in view_dict:
+            to_paste_img = self.data[goods_art_no][view]
+            data = []
+            # 单图缩放处理
+            data.append({"command": "resize",
+                         "plugins_mode": "pixel",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                         "base_im": {"im": ""},  # 参考基于其他图 PIL格式
+                         "base": "width",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                         "value": 1800,  # 固定值,如果为百分比,则为0
+                         "percentage": 0, })  # 百分比
+
+            data.append({
+                "command": "crop_img",
+                "img": "",
+                "pos": {"plugins_mode": "pixel",  # pixel
+                        "base": view_dict[view]["crop_img_base"],  # nw,nc,ne,ec ... 各个方向参考点
+                        "value": view_dict[view]["crop_img_value"],
+                        },
+                "color_fill": bg_color,
+            })
+            # 处理圆角
+            data.append({"command": "radius",  # radius
+                         "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+                         "circular_pos": (1, 1, 1, 1),  # 从左上角顺时针,记录圆角数量
+                         "value": 100,  # 固定值,如果为百分比,则为0
+                         "percentage": 0, })  # 百分比
+
+            to_paste_img = self.deal_one_pic(to_paste_img, data)
+
+            # 粘贴到白底图上
+            data = []
+            data.append({
+                "command": "paste_img",
+                "img": {"im": to_paste_img},
+                "pos": {"plugins_mode": "pixel",  # pixel
+                        "base": view_dict[view]["paste_img_base"],  # nw,nc,ne,ec ... 各个方向参考点
+                        "value": view_dict[view]["paste_img_value"],
+                        },
+                "color_fill": bg_color,
+            })
+            bg_c = Image.new("RGB", (1200, 848), (255, 255, 255))
+            bg_c_img = self.deal_one_pic(bg_c, data)
+            to_paste_img = self.deal_one_pic(bg_c_img, data)
+            detailed_images.append(to_paste_img)
+
+        """               制作角度展示图             """
+        # 角度展示
+        goods_art_no_list = list(self.data.keys())
+        for index, goods_art_no in enumerate(goods_art_no_list):
+            if index == 0:
+                name_list = ["俯视", "侧视", "后跟", "鞋底"]
+            else:
+                name_list = ["俯视", ]
+
+            for _name in name_list:
+                # 处理图片,需要粘贴到背景等处理
+                if _name not in self.data[goods_art_no]:
+                    continue
+
+                _image = self.data[goods_art_no][_name]
+
+                data = []
+                data.append({"command": "resize",
+                             "plugins_mode": "pixel",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                             "base": "width",
+                             # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                             "value": 500 if _name == "后跟" else 1058,  # 固定值,如果为百分比,则为0
+                             })  # 百分比
+
+                _image = self.deal_one_pic(img=_image, data=data)
+                data = []
+                data.append({
+                    "command": "paste_img",
+                    "img": {"im": _image},
+                    "pos": {"plugins_mode": "pixel",  # pixel
+                            "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                            "value": (0, 0),
+                            "percentage": (0, 0),
+                            },
+                    "margins": (0, 0, 0, 0),  # 上下左右边距
+                })
+
+                bg_img = Image.new("RGB", (1200, int(_image.height + 50)), bg_color)
+                _image = self.deal_one_pic(img=bg_img, data=data)
+                # _image.show()
+                detailed_images.append(_image)
+                detailed_images.append(Image.new("RGB", (1200, 50), (255, 255, 255)))
+
+        detailed_images.append(Image.open(r"resources\detail_image_3\image (3).jpg"))
+        """     制作详细图      """
+        output_path = "{out_put_dir}/{goods_no}/details".format(out_put_dir=self.out_put_dir, goods_no=self.goods_no)
+        self.create_folder(output_path)
+
+        for index, im in enumerate(detailed_images):
+            im.save("{}/{}({}).png".format(output_path, self.goods_no, str(index + 11).zfill(2)))
+
+        img = self.add_pic(detailed_images)
+        img.save("{}/0_{}_拼接图.png".format(self.out_put_dir, self.goods_no))
+        return True
+
+    def create_folder(self, path):
+        def check_folder(__path):
+            if not os.path.exists(__path):
+                os.makedirs(__path)
+                return False
+            return True
+
+        # 文件夹不存在,创建货号子集文件夹
+        if not check_folder(path):
+            pass
+            # for name in ["原始图", "原始图_已抠图", "800x800", "200images"]:
+            #     other_path = path + "/" + name
+            #     check_folder(other_path)
+
+    def add_pic(self, detailed_images):
+        if not detailed_images:
+            return
+        page_len = 0
+        for index, im in enumerate(detailed_images):
+            page_len += im.height
+        bg_im = Image.new("RGB", (im.width, page_len), (255, 255, 255))
+
+        n = 0
+        for index, im in enumerate(detailed_images):
+            bg_im.paste(im, (0, n))
+            n += im.height
+        # bg_im.show()
+        return bg_im
+
+    def get_goods_pos(self, im, cut_image):
+        # 保留多余内容
+        old_x, old_y = im.size
+        x1, y1, x2, y2 = cut_image.getbbox()
+        goods_w, goods_h = x2 - x1, y2 - y1
+        _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
+        new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
+        new_x1 = 0 if new_x1 < 0 else new_x1
+        new_y1 = 0 if new_y1 < 0 else new_y1
+        new_x2 = old_x if new_x2 > old_x else new_x2
+        new_y2 = old_y if new_y2 > old_y else new_y2
+        # 剪切掉多余的内容,保留阴影
+        im = im.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+        return im
+
+    def set_dict(self, one_dict):
+        _ = defaultdict(str)
+        for i, v in one_dict.items():
+            if isinstance(v, dict):
+                v = self.set_dict(v)
+            _[i] = v
+        return _
+
+    def deal_one_pic(self, img: Image, data=None):
+        """
+        通用图片处理器
+        1、基于某原始图,进行加工,包括粘贴、单图处理等逻辑
+        """
+        data = [self.set_dict(x) for x in data]
+
+        for command in data:
+            if command["command"] == "resize":
+                img = OnePicDeal().resize(img, command)
+                continue
+            if command["command"] == "paste_img":
+                img = OnePicDeal().paste_img(img, command)
+                continue
+            if command["command"] == "crop_img":
+                img = OnePicDeal().crop_img(img, command)
+                continue
+            if command["command"] == "radius":
+                img = OnePicDeal().radius(img, command)
+                continue
+            if command["command"] == "add_text":
+                img = OnePicDeal().add_text(img, command)
+                continue
+
+        return img
+
+    def get_overlay_pic(self, pil_img_1, pil_img_2, color):
+        im_w, im_h = pil_img_1.size
+        cv_im = cv2.cvtColor(np.asarray(pil_img_1), cv2.COLOR_RGB2BGR)
+
+        # 创建一张纯色底图
+        image_white = Image.new("RGB", (im_w, im_h), color)
+
+        cv_image_white = cv2.cvtColor(np.asarray(image_white), cv2.COLOR_RGB2BGR)
+
+        new_im = self.to_color_2(cv_image_white, cv_im)
+        # new_im = cv2.addWeighted(new_im, 0.7, cv_im_2, 0.3, 0)
+        # new_im = cv2.add(new_im, cv_im_2)
+
+        new_im = Image.fromarray(cv2.cvtColor(new_im, cv2.COLOR_BGR2RGB))
+        new_im.paste(pil_img_2, (0, 0), pil_img_2)
+        return new_im
+
+    def to_color_2(self, target, blend):  # 正片叠底
+        return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)
+
+    def to_color_1(self, img1, img2):  # PS颜色模式
+        img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
+        img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
+        img2[:, :, 0] = img1[:, :, 0]
+        img2[:, :, 1] = img1[:, :, 1]
+        res = cv2.cvtColor(img2, cv2.COLOR_HSV2BGR)
+        return res
+
+
+if __name__ == '__main__':
+    _path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture\IPC\output\2023-11-06"
+
+    goods_no_dict = {}
+    for goods_art_no in os.listdir(_path):
+        if not os.path.isdir("{}/{}".format(_path, goods_art_no)):
+            continue
+
+        if len(goods_art_no) < 10:
+            print("{}--长度异常".format(goods_art_no))
+            continue
+        # print(goods_art_no)
+        goods_no = goods_art_no[:9]
+        if goods_no not in goods_no_dict:
+            goods_no_dict[goods_no] = {}
+        goods_no_dict[goods_no][goods_art_no] = {}
+
+        # 获取该货号下所有素材图  阴影图处理
+        source_material_path = "{}/{}/阴影图处理".format(_path, goods_art_no)
+        if not os.path.exists(source_material_path):
+            print("{}--缺少 阴影图处理 文件夹".format(goods_art_no))
+            continue
+        _ = {}
+        if len(os.listdir(source_material_path)) % 2 != 0:
+            print("{}--阴影图处理 文件错误".format(goods_art_no))
+            continue
+
+        name_list = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+        for pic in os.listdir(source_material_path):
+            # print(pic)
+            _pic = pic.replace(".png", "")
+            _pic = _pic.split("_")
+            if len(_pic) != 3:
+                continue
+            _["{}-{}".format(_pic[1], _pic[2])] = "{}/{}".format(source_material_path, pic)
+        goods_no_dict[goods_no][goods_art_no] = _
+        # print(_)
+
+    # 准备拼接处理
+    for goods_no, goods_no_value in goods_no_dict.items():
+        # if goods_no == "AC5200054":
+        print("开始处理-----{}".format(goods_no))
+        GenerateDetailPic(goods_no, goods_no_value)
+        # raise 1
+    # print(goods_no_dict)

+ 913 - 0
python/service/generate_goods_no_detail_pic/module_generate_goods_no_detail_pics.py

@@ -0,0 +1,913 @@
+# from UI.generate_goods_no_detail_pic.generate_goods_no_detail_pic_ui import Ui_Form as generate_goods_no_detail_pic_Ui_Form
+import json
+from UI.generate_goods_no_detail_pic.new_generate_goods_no_detail_pic_ui import (
+    Ui_Form as generate_goods_no_detail_pic_Ui_Form,
+)
+from UI.generate_goods_no_detail_pic.temp_item import (
+    Ui_Form as UI_temp_item,
+)
+from import_qt_mode import *
+
+import settings
+import threading
+import os
+import shutil
+from PIL import Image
+
+# from module.view_control.generate_goods_no_detail_pic.goods_detail_pics import DetailPicGetHqt, DetailPicGetHLM
+from module.view_control.generate_goods_no_detail_pic.detail_xiaosushuoxie import (
+    DetailPicGetXiaoSuShuoXie,
+)
+from module.view_control.generate_goods_no_detail_pic.detail_xiaosushuoxie2 import (
+    DetailPicGetXiaoSuShuoXie2,
+)
+from module.view_control.generate_goods_no_detail_pic.detail_xiaosushuoxie3 import (
+    DetailPicGetXiaoSuShuoXie3,
+)
+from module.view_control.generate_goods_no_detail_pic.detail_hlm_2 import (
+    DetailPicGetHLM2,
+)
+from module.view_control.generate_goods_no_detail_pic.detail_hlm_3 import (
+    DetailPicGetHLM3,
+)
+from module.view_control.generate_goods_no_detail_pic.detail_xiaosushuoxie4 import DetailPicGetXiaoSuShuoXie4
+from module.view_control.generate_goods_no_detail_pic.detail_xiaosushuoxie5 import DetailPicGetXiaoSuShuoXie5
+from module.view_control.generate_goods_no_detail_pic.detail_xiaosushuoxie6 import DetailPicGetXiaoSuShuoXie6
+from module.view_control.generate_goods_no_detail_pic.detial_hqt import DetailPicGetHqt
+
+from concurrent.futures import ThreadPoolExecutor
+from threading import Lock
+from module.view_control.generate_goods_no_detail_pic.data import DataModeGenerateDetail
+from module.view_control.generate_goods_no_detail_pic import detail_func
+from module.view_control.MineQWidget import MineQWidget, DialogShow, MessageShow
+import time
+from functools import partial
+import io
+import concurrent.futures
+
+"""
+照片自动货号匹配 将图片放置在指定文件夹下,并自动对应不同的货号进行整理
+"""
+
+
+class GenerateDetailPic(MineQWidget):
+    progress_sign = Signal(dict)
+    info_sign = Signal(str)
+    text_show = Signal(str)
+    isUseTemplate = True
+
+    def __init__(self, windows=None):
+        super().__init__()
+        self.windows = windows
+
+        self.ui = generate_goods_no_detail_pic_Ui_Form()
+        self.ui.setupUi(self)
+        # 线程锁
+        self.lock = Lock()
+
+        # 0禁用  1进行中  2已结束 99等待结束
+        self.state = 2
+        self.data_mode_generate_detail = DataModeGenerateDetail()
+
+        # 使用使用表格数据
+        self.is_use_excel = settings.GOODS_DETAIL_IS_USE_EXCEL
+        self.excel_path = settings.GOODS_DETAIL_EXCEL_PATH
+
+        # 目标文件夹
+        self.image_dir = settings.GOODS_DETAIL_LAST_IMAGE_PATH
+        # 对应的错误文件夹目录
+        if self.image_dir and os.path.exists(self.image_dir):
+            self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        else:
+            self.target_error_folder = ""
+
+        # 检查颜色是否齐全
+        self.is_check_color_is_all = settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL
+        # 已生成主图的自动过滤
+        self.is_check_is_pass = settings.GOODS_DETAIL_IS_PASS
+
+        # 上次使用的详情模板
+        self.last_temp = settings.GOODS_DETAIL_LAST_TEMP
+
+        # 注册模板
+        self.temp_class = {
+            "hqt-1": DetailPicGetHqt,
+            "hlm-1": DetailPicGetHLM2,
+            "hlm-2": DetailPicGetHLM3,
+            "xiaosushuoxie-1": DetailPicGetXiaoSuShuoXie,
+            "xiaosushuoxie-2": DetailPicGetXiaoSuShuoXie2,
+            "xiaosushuoxie-3": DetailPicGetXiaoSuShuoXie3,
+            "xiaosushuoxie-4": DetailPicGetXiaoSuShuoXie4,
+            "xiaosushuoxie-5": DetailPicGetXiaoSuShuoXie5,
+            "xiaosushuoxie-6": DetailPicGetXiaoSuShuoXie6,
+        }
+        self.init()
+        self.show()
+
+    def change_temp_mode(self, *args, **kwargs):
+        # 输出按钮1与按钮2的状态,选中还是没选中
+        if self.ui.radioButton.isChecked():
+            self.isUseTemplate = True
+            self.ui.scrollArea.show()
+        else:
+            self.isUseTemplate = False
+            self.ui.scrollArea.hide()
+
+    def init(self):
+        self.show_img_dir()
+        self.ui.stackedWidget.setCurrentIndex(0)
+        print("配置打印")
+        print(self.is_use_excel)
+        print(self.excel_path)
+        print(self.image_dir)
+        print(self.is_check_color_is_all)
+        print(self.is_check_is_pass)
+
+        self.ui.reload_func.hide()
+        # -------初始化页面--------
+        if self.is_use_excel:
+            self.ui.check_use_excel.setChecked(True)
+        else:
+            self.ui.check_use_excel.setChecked(False)
+
+        if self.excel_path:
+            self.ui.excel_path_show.setText(self.excel_path)
+
+        if self.image_dir:
+            self.ui.path_name.setText(self.image_dir)
+
+        if self.is_check_color_is_all:
+            self.ui.check_image_total.setChecked(True)
+        else:
+            self.ui.check_image_total.setChecked(False)
+
+        if self.is_check_is_pass:
+            self.ui.check_is_pass.setChecked(True)
+        else:
+            self.ui.check_is_pass.setChecked(False)
+
+        # 是否开启使用外部数据
+        self.ui.check_use_excel.toggled.connect(self.check_box_change)
+        self.ui.check_image_total.toggled.connect(self.check_box_change)
+        self.ui.check_is_pass.toggled.connect(self.check_box_change)
+        self.check_box_change()
+
+        self.text_show.connect(self.append_text_to_browser)
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.select_excel.mousePressEvent = self.select_excel_path
+
+        # ====================================
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.setMaximum(100)
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.hide()
+        self.progress_sign.connect(self.show_progress)
+
+        self.ui.back.clicked.connect(lambda x: self.ui.stackedWidget.setCurrentIndex(0))
+        self.ui.stop.clicked.connect(self.to_stop)
+        self.ui.run.clicked.connect(self.run)
+
+        # 复制Excel模板
+        self.ui.get_excel_temp.mousePressEvent = self.copy_excel
+        self.test_1 = 0
+
+        self.ui.radioButton.toggled.connect(self.change_temp_mode)
+        self.ui.radioButton_2.toggled.connect(self.change_temp_mode)
+
+        if settings.GOODS_DETAIL_TEMP_MODE_LAST == '选择模板':
+            self.ui.radioButton.setChecked(True)
+            self.change_temp_mode()
+        else:
+            self.ui.radioButton_2.setChecked(True)
+            self.change_temp_mode()
+
+        # 加载模板
+        self.temp_list = []
+        self.load_temp_list()
+
+    def load_temp_list(self):
+        if settings.PROJECT == "红蜻蜓":
+            self.temp_list = ["hqt-1"]
+        elif settings.PROJECT == "惠利玛":
+            if "惠利玛" in settings.Company:
+                self.temp_list = ["hlm-1"]
+            if "小苏" in settings.Company:
+                self.temp_list = [
+                    "xiaosushuoxie-1",
+                    "xiaosushuoxie-2",
+                    "xiaosushuoxie-3",
+                    "xiaosushuoxie-4",
+                    "xiaosushuoxie-5",
+                    "xiaosushuoxie-6",
+                ]
+
+        for i in self.ui.scrollArea.findChildren(QPushButton):
+            i.deleteLater()
+        # 模板列表
+        for temp_name in self.temp_list:
+            _class = self.temp_class[temp_name]
+            _d = _class.get_temp_pic_info(_class.root)
+            temp_image_path = _d["temp_pic_path"]
+            temp_item = TempItem(self.ui.scrollArea, temp_name, temp_image_path)
+            temp_item.select_sign.connect(self.change_temp_select)
+            self.ui.horizontalLayout_10.insertWidget(
+                self.ui.horizontalLayout_10.count() - 1, temp_item
+            )
+
+        # 设置默认值
+        if self.last_temp not in self.temp_list:
+            self.last_temp = self.temp_list[0]
+        # 设置值
+        for temp_item in self.ui.scrollArea.findChildren(TempItem):
+            temp_item.select()
+            break
+
+    def change_temp_select(self, _id):
+        self.last_temp = _id
+        print("已设置模板的值为:{}".format(self.last_temp))
+        for temp_item in self.ui.scrollArea.findChildren(TempItem):
+            if temp_item.id != _id:
+                temp_item.cancel_select()
+
+    def copy_excel(self, *args, **kwargs):
+        excel_path = r"{}\resources\init\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(excel_path):
+            return
+
+        self.check_path(r"{}\temp".format(os.getcwd()))
+        self.check_path(r"{}\temp\excel_temp".format(os.getcwd()))
+        new_excel_path = r"{}\temp\excel_temp\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(new_excel_path):
+            shutil.copy(excel_path, new_excel_path)
+        # 打开文件夹
+        os.startfile(r"{}\temp\excel_temp".format(os.getcwd()))
+
+    def check_box_change(self, *args):
+        if self.ui.check_use_excel.isChecked():
+            self.is_use_excel = True
+            self.ui.widget.show()
+        else:
+            self.is_use_excel = False
+            self.ui.widget.hide()
+
+        if self.ui.check_image_total.isChecked():
+            self.is_check_color_is_all = True
+        else:
+            self.is_check_color_is_all = False
+
+        if self.ui.check_is_pass.isChecked():
+            self.is_check_is_pass = True
+        else:
+            self.is_check_is_pass = False
+
+    def to_stop(self, *args):
+        if self.state == 1:
+            self.state = 99
+
+    def select_excel_path(self, *args, **kwargs):
+        folder = QFileDialog.getOpenFileName(self, "选取excel", "./")[0]
+        if "xlsx" in folder:
+            # if len(folder) > 20:
+            #     text = folder[-20:]
+            # else:
+            #     text = folder
+
+            self.ui.excel_path_show.setText(folder)
+            self.excel_path = folder
+            print("folder", folder)
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
+        self.image_dir = folder
+        self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        self.show_img_dir()
+
+    def show_img_dir(self):
+        self.ui.path_name.setText(self.image_dir)
+
+    def show_progress(self, data):
+        progress_bar_value = data["progress_bar_value"]
+        self.ui.progressBar.setValue(progress_bar_value)
+        pass
+
+    def set_state(self, state_value: int):
+        # 0禁用  1进行中  2已结束
+        if state_value not in [0, 1, 2]:
+            return
+        self.state = state_value
+        if self.state == 0:
+            self.ui.stackedWidget.setCurrentIndex(0)
+        if self.state == 1:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.hide()
+            self.ui.stop.show()
+            self.ui.info_text.setEnabled(False)
+            self.ui.info_text.clear()
+
+        if self.state == 2:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.show()
+            self.ui.stop.hide()
+
+    def check_path(self, _path):
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def run(self):
+        # self.image_dir = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\out_put\测试文件夹"
+        # self.target_error_folder = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\out_put\测试文件夹\软件-错误"
+        # self.is_use_excel = True
+        # self.excel_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\qt_test\商品基础信息.xlsx"
+        # 保存为配置
+
+        a = QMessageBox.question(
+            self, "确认", "是否进行详情图生成", QMessageBox.Yes | QMessageBox.No
+        )
+        if a != QMessageBox.Yes:
+            return
+
+        settings.GOODS_DETAIL_IS_USE_EXCEL = self.is_use_excel
+        settings.GOODS_DETAIL_EXCEL_PATH = self.excel_path
+        settings.GOODS_DETAIL_LAST_IMAGE_PATH = self.image_dir
+        settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL = self.is_check_color_is_all
+        settings.GOODS_DETAIL_IS_PASS = self.is_check_is_pass
+        settings.GOODS_DETAIL_LAST_TEMP = self.last_temp
+
+        if self.ui.radioButton.isChecked():
+            settings.GOODS_DETAIL_TEMP_MODE_LAST = "选择模板"
+        else:
+            settings.GOODS_DETAIL_TEMP_MODE_LAST = "根据EXCEL"
+
+        data_dict = {
+            "is_use_excel": "是" if self.is_use_excel else "否",
+            "excel_path": self.excel_path if self.excel_path else "",
+            "last_image_path": self.image_dir if self.image_dir else "",
+            "is_check_color_is_all": "是" if self.is_check_color_is_all else "否",
+            "is_pass": "是" if self.is_check_is_pass else "否",
+            "last_temp": self.last_temp,
+            "goods_detail_temp_mode_last": settings.GOODS_DETAIL_TEMP_MODE_LAST,
+        }
+
+        settings.set_config(data_dict=data_dict, section="goods_detail")
+
+        # 红蜻蜓/惠利玛,且为非表格数据必须需要登录处理
+        if not self.is_use_excel:
+            if not settings.IsLogin:
+                self.WaringMessage("请先登录账号")
+                return
+
+        if not self.image_dir:
+            self.WaringMessage("请选择货号文件夹集合")
+            return
+        else:
+            if "失败" in self.image_dir:
+                self.WaringMessage("文件夹路径不能含有 失败 字样")
+                return
+
+        assigned_page = self.ui.assigned_page.text()
+        if assigned_page:
+            assigned_page_dict = self.check_assigned_page(assigned_page)
+            if not assigned_page_dict:
+                self.WaringMessage("指定模板填写格式不合规")
+        else:
+            assigned_page_dict = {}
+
+        # 先做整体校验
+        func = partial(self.check_folder, temp_name=self.last_temp, temp_name_list=self.temp_list,
+                       assigned_page_dict=assigned_page_dict)
+        self.do_thread_run(func=func, call_back=self.check_first_call_back, time_out=30)
+        # self.t = threading.Thread(target=self.check_folder, args=())
+        # self.t.start()
+
+    # 解析指定模板页面数据
+    def check_assigned_page(self, text: str):
+
+        try:
+            temp_dict = {}
+            for i in self.temp_list:
+                temp_dict[i[-1]] = i
+
+            text = text.replace(" ", "")
+            text = text.replace(",", ",")
+            text = text.replace(":", ":")
+            text = text.replace(";", ",")
+            assigned_page_list = text.split(",")
+            assigned_page_dict = {}
+            for i in assigned_page_list:
+                if not i:
+                    continue
+                _k, _v = i.split(":")
+                _k = temp_dict[_k]
+                if _k not in assigned_page_dict:
+                    assigned_page_dict[_k] = []
+                if _v not in assigned_page_dict[_k]:
+                    assigned_page_dict[_k].append(_v)
+
+            return assigned_page_dict
+        except:
+            return {}
+
+    def check_first_call_back(self, data):
+        # 首次数据校验的信息返回
+        print(json.dumps(data))
+        # self.show_message(text="22222222222222222222222")
+        # QMessageBox.critical(self, "警告", "1111111", QMessageBox.Ok)
+        code = data["code"]
+        if code != 0:
+            self.show_message(data["message"])
+            return
+
+        do_next = False
+        if data["message"]:
+            button_1, button_2, button_3 = None, None, None
+            text = data["message"]
+            if code == 0:
+                if data["data"]:
+                    if data["data"]["error_folder_list"]:
+                        print("22----------error_folder_list------------")
+                        print(json.dumps(data["data"]["error_folder_list"]))
+                        button_2 = "移除错误文件"
+                    if data["data"]["goods_no_dict"]:
+                        if button_2:
+                            button_1 = "移除并继续"
+                            button_3 = "继续(忽略其他)"
+                        else:
+                            button_1 = "继续"
+                    if data["data"]["succeed_folder_list"]:
+                        text += "\n==================\n校验无误数据:{}个文件夹".format(
+                            len(data["data"]["succeed_folder_list"])
+                        )
+
+            my_dialog = DialogShow(
+                self, text=text, button_1=button_1, button_2=button_2, button_3=button_3
+            )
+            ret = my_dialog.exec()
+            print(my_dialog.flag_name)
+            if "移除" in my_dialog.flag_name:
+                for error_folder_data in data["data"]["error_folder_list"]:
+                    self.move_error_folders(
+                        one_path=error_folder_data["folder_path"],
+                        target_folder=self.target_error_folder,
+                    )
+            if "继续" in my_dialog.flag_name:
+                do_next = True
+
+        if data["data"]["goods_no_dict"] and not data["data"]["error_folder_list"]:
+            do_next = True
+
+        if do_next:
+            self.set_state(state_value=1)
+            getAllData = data["data"]
+            base_temp_name = getAllData["temp_name"]
+            set_temp_name = getAllData.get("template_name", "")
+
+            kwargs = {
+                "_goods_no_dict": data["data"]["goods_no_dict"],
+                "temp_name": base_temp_name,
+                "temp_name_list": data["data"]["temp_name_list"],
+                "assigned_page_dict": data["data"]["assigned_page_dict"]
+            }
+            # else:
+            #     print("data", data)
+            #     kwargs = {
+            #         "_goods_no_dict": data["data"]["goods_no_dict"],
+            #         "temp_name": (
+            #             base_temp_name if set_temp_name == "" else set_temp_name
+            #         ),  # 模板名称
+            #     }
+            threading.Thread(target=self.run_by_thread, kwargs=kwargs).start()
+
+    def show_progress_detail(self, text):
+        self.text_show.emit(text)
+
+    def append_text_to_browser(self, text):
+        self.ui.info_text.append(text)
+
+    def change_to_cm(self, value):
+        if not value:
+            return ""
+        try:
+            value = "{}CM".format(int(value) / 10)
+            return value
+        except:
+            return ""
+
+    def check_folder(self, temp_name, temp_name_list, assigned_page_dict=None):
+        # =============
+        # 整体数据校验,返回错误内容,以及
+        # temp_name:模板名称
+        """
+        步骤:
+        1、 整体文件夹检查,并输出数据结构
+        2、数据进行对应规整(可能有excel,红蜻蜓等)
+        3、执行单个款数据处理
+        """
+        return_data = {
+            "code": 99,
+            "message": "",
+            "data": {
+                "error_folder_list": [],
+                "goods_no_dict": {},
+                "succeed_folder_list": [],
+                "temp_name": temp_name,
+                "temp_name_list": temp_name_list,
+                "assigned_page_dict": assigned_page_dict,
+            },
+        }
+        error_folder_list = []
+        print("--------------------------------------", self.image_dir)
+        # 遍历货号获取所有货号--可能为编号
+        folder_name_list = detail_func.get_all_dir_info(image_dir=self.image_dir)
+        if not folder_name_list:
+            return_data["message"] = "不存在任何货号/编号文件夹"
+            return return_data
+
+        # =========================组装数据---数据来源多种途径=========================
+        _result = {"code": 99, "message": "无法解析到数据,请检查登录企业"}
+        if not self.is_use_excel:
+            if settings.PROJECT == "红蜻蜓":
+                # goods_no_dict输出为文件夹下涉及到的所有款为key的字典,后续通过解析字典,进行提取对应文件夹
+                _result = self.data_mode_generate_detail.get_basic_goods_art_data_by_hqt_and_hlm(
+                    folder_name_list
+                )
+
+            elif settings.PROJECT == "惠利玛":
+                if settings.Company:
+                    if "惠利玛" in settings.Company:
+                        _result = self.data_mode_generate_detail.get_basic_goods_art_data_by_hqt_and_hlm(
+                            folder_name_list
+                        )
+        else:
+            keys = settings.keys
+            _result = (
+                self.data_mode_generate_detail.get_basic_goods_art_data_form_excel(
+                    folder_name_list,
+                    self.excel_path,
+                    keys,
+                )
+            )
+
+        if _result["code"] == 0:
+            remote_data = _result["data"]
+        else:
+
+            return_data["message"] = _result["message"]
+            return return_data
+
+        # print(json.dumps(remote_data))
+        # =========================拼接数据组合为款数据=========================
+        """
+        1、获取所有文件夹基础数据内容
+        2、结合上述返回结果进行数据组合拼接
+        3、输出结果为按款为主键信息
+        4、注意模板ID
+        """
+        # 获取所有文件夹基础数据内容  检查不满足要求的文件不满足要求移动到错误文件夹
+        need_view_list = self.temp_class[temp_name].need_view
+        _all_dir_info_data = detail_func.get_all_dir_info_and_pic_info(
+            self.image_dir, folder_name_list, need_view_list
+        )
+        all_dir_info_data = {}
+        for one_folder, value in _all_dir_info_data.items():
+            if "message" in value:
+                if value["message"]:
+                    error_folder_list.append(
+                        {
+                            "folder_name": one_folder,
+                            "folder_path": "{}/{}".format(self.image_dir, one_folder),
+                        }
+                    )
+                    return_data["message"] += "文件夹:{} 结构错误:{}\n".format(
+                        one_folder, value["message"]
+                    )
+                    continue
+
+            # 符合要求的数据处理
+            all_dir_info_data[one_folder] = value
+
+        # 结合上述返回结果进行数据组合拼接
+        # 返回可能存在的情况:1、存在文件夹不匹配数据,2、存在货号数据缺失
+        goods_no_dict, error_folder_name_list = detail_func.merge_local_and_remote_data(
+            all_dir_info_data=all_dir_info_data, remote_data=remote_data
+        )
+        # 文件在系统中没有匹配的结果
+        if error_folder_name_list:
+            for one_folder in error_folder_name_list:
+                error_folder_list.append(
+                    {
+                        "folder_name": one_folder,
+                        "folder_path": "{}/{}".format(self.image_dir, one_folder),
+                    }
+                )
+                return_data["message"] += "文件夹:{} 找不到对应数据\n".format(
+                    one_folder
+                )
+
+        print("===============goods_no_dict==================")
+        if settings.IS_TEST:
+            _text = json.dumps(goods_no_dict)
+            with open("qt_test/goods_no_dict.txt", 'w', encoding='utf-8') as file:
+                file.write(_text)
+        print("===============goods_no_dict==================")
+
+        # ===================================是否齐色检查=============================================
+        # 如为红蜻蜓企业则还需要检查同款下是否齐全;数据返回结果为款号列表
+        error_data_dict = {}
+        if self.is_check_color_is_all:
+            if not self.is_use_excel:
+                if settings.PROJECT == "红蜻蜓":
+                    error_data_dict = (
+                        self.data_mode_generate_detail.check_goods_is_not_deficiency(
+                            goods_no_dict
+                        )
+                    )
+            else:
+                error_data_dict = self.data_mode_generate_detail.check_goods_is_not_deficiency_form_excel(
+                    goods_no_dict, self.excel_path
+                )
+
+        if error_data_dict:
+            print("----error_data_dict----")
+            print(json.dumps(error_data_dict))
+
+            # 款号反向映射;因为部分key键格式为KUM9999999
+            _x = {}
+            for i, v in goods_no_dict.items():
+                _x[v["款号"]] = i
+
+            for goods_no, value in error_data_dict.items():
+                if goods_no in _x:
+                    goods_no = _x[goods_no]
+                if goods_no in goods_no_dict:
+                    # =====移动到错误文件夹======
+                    for _folder_name in [
+                        x["文件夹名称"] for x in goods_no_dict[goods_no]["货号资料"]
+                    ]:
+                        error_folder_list.append(
+                            {
+                                "folder_name": _folder_name,
+                                "folder_path": "{}\{}".format(
+                                    self.image_dir, _folder_name
+                                ),
+                            }
+                        )
+                        return_data["message"] += "文件夹:{};{}\n".format(
+                            _folder_name, value["message"]
+                        )
+                    # 从 正确 款下面进行数据剔除
+                    goods_no_dict.pop(goods_no)
+
+        print("-----------------1goods_no_dict---------------")
+        print(json.dumps(goods_no_dict, ensure_ascii=False))
+        print("-----------------1goods_no_dict---------------")
+
+        # 校验无误的文件夹数据  goods_no_dict为最终有效数据
+        for goods_no, value in goods_no_dict.items():
+            return_data["data"]["succeed_folder_list"].extend(
+                [x["文件夹名称"] for x in goods_no_dict[goods_no]["货号资料"]]
+            )
+
+        # ========开始执行详情图生成===================
+        _goods_no_dict = goods_no_dict
+        # assigned_page_dict 如存在对应模板的,则不管是否有过滤都需要生成
+
+        if self.is_check_is_pass:
+            _path = "{}/{}".format(self.image_dir, "软件-详情图生成")
+            if os.path.exists(_path):
+                _goods_no_dict = {}
+                # 数据返回为 已有的款数据,为款号列表
+                is_pass_goods_no = detail_func.get_all_detail_info(_path)
+                for goods_no, value in goods_no_dict.items():
+                    if "软件" in goods_no:
+                        continue
+
+                    if value["模板名称"] in assigned_page_dict:
+                        need_todo = True
+                    else:
+                        if goods_no in is_pass_goods_no:
+                            need_todo = False
+                        else:
+                            need_todo = True
+
+                    if need_todo:
+                        _goods_no_dict[goods_no] = value
+
+        print("-----------------2goods_no_dict---------------")
+        print(json.dumps(_goods_no_dict, ensure_ascii=False))
+        print("-----------------2goods_no_dict---------------")
+        return_data["data"]["error_folder_list"] = error_folder_list
+
+        if len(_goods_no_dict) == 0:
+            return_data["message"] += "\n没有任何文件夹需要执行"
+        return_data["data"]["goods_no_dict"] = _goods_no_dict
+
+        return_data["code"] = 0
+        return return_data
+
+    def run_by_thread(self, _goods_no_dict, temp_name, temp_name_list, assigned_page_dict):
+        # 开始处理
+        self.n = 0
+        self.total_num = len(_goods_no_dict)
+        self.fail_num = 0
+
+        # 详情图生成结果文件夹
+        out_put_dir = "{}\软件-详情图生成".format(self.image_dir)
+        if settings.IS_TEST:
+            print("==============_goods_no_dict  打印=================")
+
+            print(json.dumps(_goods_no_dict))
+
+            print("==============_goods_no_dict  打印-end=================")
+
+        if settings.IS_TEST:
+            max_workers = 1
+        else:
+            max_workers = 2
+
+        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
+            futures = []
+            for goods_no, value in _goods_no_dict.items():
+                _temp_name = temp_name
+                # 使用自定义的表格数据
+                if self.isUseTemplate is False:
+                    if "模板名称" in value:
+                        if value["模板名称"] in temp_name_list:
+                            _temp_name = value["模板名称"]
+                assigned_page_list = []
+                if _temp_name in assigned_page_dict:
+                    assigned_page_list = assigned_page_dict[_temp_name]
+
+                futures.append(executor.submit(
+                    self.deal_one_data,
+                    goods_no=goods_no,
+                    value=value,
+                    out_put_dir=out_put_dir,
+                    temp_name=_temp_name,
+                    assigned_page_list=assigned_page_list,
+                ))
+
+            # 使用 wait 方法等待所有任务完成
+            done, not_done = concurrent.futures.wait(futures)
+
+            # 处理完成的任务
+            for future in done:
+                if settings.IS_TEST:
+                    result = future.result()
+
+        # ==============完成处理==============
+        self.set_state(state_value=2)
+        if self.total_num:
+            if self.fail_num:
+                self.show_progress_detail(
+                    "处理完成,-----------处理失败数据:{}个款".format(self.fail_num)
+                )
+            else:
+                self.show_progress_detail("处理完成")
+        else:
+            self.show_progress_detail("没有任何数据")
+
+    def move_error_folders(self, one_path, target_folder, message=""):
+        if os.path.exists(one_path):
+            if message:
+                self.show_progress_detail(message)
+                error_file = open(target_folder + "/错误明细.txt", "a")
+                error_file.write(f"{message}\n")
+                error_file.close()
+            self.check_path(target_folder)
+            detail_func.move_folders(path_list=[one_path], target_folder=target_folder)
+
+    def deal_one_data(self, goods_no, value, out_put_dir, temp_name, assigned_page_list):
+        if self.state == 99:
+            self.show_progress_detail("用户主动取消:{}".format(goods_no))
+            return
+
+        self.show_progress_detail("正在生成:{}".format(goods_no))
+        is_deal_success = False
+        print("模板:", temp_name)
+        if settings.IS_TEST:
+            d = self.temp_class[temp_name](goods_no, value, out_put_dir=out_put_dir, windows=self,
+                                           assigned_page_list=assigned_page_list)
+            # if d.run():
+            is_deal_success = True
+        else:
+            try:
+                # # 处理图片详情图生成
+                d = self.temp_class[temp_name](goods_no, value,
+                                               out_put_dir=out_put_dir,
+                                               assigned_page_list=assigned_page_list)
+                # if settings.PROJECT == "红蜻蜓":
+                #     d = DetailPicGetHqt(goods_no, value, out_put_dir=out_put_dir)
+                # elif settings.PROJECT == "惠利玛":
+                #     if "惠利玛" in settings.Company:
+                #         d = DetailPicGetHLM(goods_no, value, out_put_dir=out_put_dir)
+                #     if "小苏" in settings.Company:
+                #         d = DetailPicGetXiaoSuShuoXie(
+                #             goods_no, value, out_put_dir=out_put_dir
+                #         )
+                # if d.run():
+                #     is_deal_success = True
+                is_deal_success = True
+            except BaseException as e:
+                self.show_progress_detail("{}处理失败".format(goods_no))
+                self.show_progress_detail("失败原因:{}".format(e))
+                self.fail_num += 1
+
+        self.lock.acquire()
+        self.n += 1
+        self.lock.release()
+
+        if not is_deal_success:
+            goods_art_no_list = value["货号资料"]
+            self.show_progress_detail("处理失败")
+            self.show_progress_detail(
+                "相关货号:{}".format([x["货号"] for x in goods_art_no_list])
+            )
+            # 将相关的文件夹统一移动至错误文件夹
+            detail_func.move_folders(
+                path_list=[
+                    "{}/{}".format(self.image_dir, x)
+                    for x in [x["文件夹名称"] for x in goods_art_no_list]
+                ],
+                target_folder=self.target_error_folder,
+            )
+            pass
+        # 更新进度
+        print(self.n, self.total_num)
+        self.progress_sign.emit(
+            {
+                "type": "详情图生成",
+                "progress_bar_value": int(self.n / self.total_num * 100),
+            }
+        )
+
+
+class TempItem(QWidget):
+    select_sign = Signal(str)
+
+    def __init__(self, parent, _id, image_path):
+        super().__init__(parent)
+        self.ui = UI_temp_item()
+        self.ui.setupUi(self)
+        self.image_path = image_path
+        self.id = _id
+        self.is_select = False
+        self.init()
+
+    def init(self):
+        self.ui.label.mousePressEvent = self.pic_show
+        self.ui.radioButton.clicked.connect(self.select)
+        self.set_label_image()
+
+    def cancel_select(self, *args):
+        self.is_select = False
+        self.ui.radioButton.setChecked(False)
+
+    def select(self, *args):
+        self.is_select = True
+        self.ui.radioButton.setChecked(True)
+        self.select_sign.emit(self.id)
+
+    def pic_show(self, *args, **kwargs):
+        if os.path.exists(self.image_path):
+            im = Image.open(self.image_path)
+            threading.Thread(target=im.show, args=()).start()
+
+    def set_label_image(self):
+        if not os.path.exists(self.image_path):
+            return
+        try:
+            bytes_io = io.BytesIO()
+            w, h = self.ui.image_show.width(), self.ui.image_show.height()
+            _img_raw = Image.open(self.image_path)
+            _img_raw = self.to_resize(_img_raw, width=w * 2)
+            _img_raw = _img_raw.crop(box=(0, 0, w * 2, h * 2))
+            # _img_raw.thumbnail((w * 2, int(_img_raw.height * w * 2 / _img_raw.width)))
+            # img_raw = _img_raw.resize(size=(w, h))
+            _img_raw.save(bytes_io, "JPEG")
+            icon = QPixmap()
+            # icon.loadFromData(img)
+            icon.loadFromData(bytes_io.getvalue())
+            icon = icon.scaled(
+                self.ui.image_show.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui.image_show.setPixmap(icon)
+            # self.mousePressEvent = lambda x: self.show_one_data(row)
+        except:
+            pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im

+ 570 - 0
python/service/generate_goods_no_detail_pic/pic_deal.py

@@ -0,0 +1,570 @@
+from PIL import Image, ImageDraw, ImageEnhance
+# import cv2
+import numpy as np
+from blend_modes import multiply
+
+
+class PictureProcessing(object):
+    def __init__(self, *args, **kwargs):
+        self.im: Image
+        if args:
+            p = args[0]
+            if isinstance(p, str):
+                if p == "RGB" or p == "RGBA":
+                    self.im = Image.new(args[0], args[1], args[2])
+                else:
+                    self.im = Image.open(args[0])
+
+        if "im" in kwargs:
+            self.im = kwargs["im"]
+
+    def getbbox(self):
+        return self.im.getbbox()
+
+    def show(self):
+        self.im.show()
+
+    def get_size(self):
+        return self.im.size
+
+    def get_im(self):
+        self.im: Image
+        return self.im
+
+    def to_color_2(self, target, blend):  # 正片叠底
+        return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)
+
+    def get_overlay_pic(self, top_img, color=None):
+        # 正片叠底
+        top_img: PictureProcessing
+        image_white = Image.new("RGB", (self.get_size()), color)
+        backdrop_prepped = np.asfarray(image_white.convert('RGBA'))
+        source_prepped = np.asfarray(self.im.convert('RGBA'))
+        blended_np = multiply(backdrop_prepped, source_prepped, 1)
+        img = Image.fromarray(np.uint8(blended_np)).convert('RGB')
+        img.paste(top_img.im, (0, 0), top_img.im)
+        return PictureProcessing(im=img)
+
+    def to_overlay_pic_advance(self, mode="pixel", top_img="", base="nw", value=(0, 0), percentage=(0, 0),
+                               margins=(0, 0, 0, 0), opacity=100, top_png_img=None):
+        # opacity 透明度
+        # 正片叠底
+        _p = PictureProcessing("RGBA", (self.width, self.height), (255, 255, 255, 255))
+        top_img = _p.paste_img(mode=mode,
+                               top_img=top_img,
+                               base=base,
+                               value=value,
+                               percentage=percentage,
+                               margins=margins)
+
+        backdrop_prepped = np.asfarray(self.im.convert('RGBA'))
+        source_prepped = np.asfarray(top_img.im.convert('RGBA'))
+        blended_np = multiply(backdrop_prepped, source_prepped, opacity / 100)
+        im = Image.fromarray(np.uint8(blended_np)).convert('RGB')
+        pp = PictureProcessing(im=im)
+        if top_png_img:
+            pp = pp.paste_img(mode=mode,
+                              top_img=top_png_img,
+                              base=base,
+                              value=value,
+                              percentage=percentage,
+                              margins=margins)
+
+        return pp
+
+    def __to_resize(self, width=None, height=None):
+        _im_x, _im_y = self.get_size()
+        if width and height:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(height)
+            re_x = int(_im_x * re_y / _im_y)
+        img = self.get_im().resize((re_x, re_y))
+        return img
+
+    # 锐化图片
+    def sharpen_image(self, factor=1.0):
+        # 创建一个ImageEnhance对象
+        enhancer = ImageEnhance.Sharpness(self.im)
+        # 应用增强,值为0.0给出模糊图像,1.0给出原始图像,大于1.0给出锐化效果
+        # 调整这个值来增加或减少锐化的程度
+        sharp_img = enhancer.enhance(factor)
+        return PictureProcessing(im=sharp_img)
+
+    @property
+    def width(self):
+        return self.im.width
+
+    @property
+    def height(self):
+        return self.im.height
+
+    @property
+    def size(self):
+        return self.im.size
+
+    def resize(self, mode="pixel", base="width", value=0, base_im=None, percentage=0, base_by_box=None,resample=None):
+        """
+        plugins_mode # relative相对(宽度、高度、其他参考图),或  pixel绝对像素
+        base # pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+        value # 固定值,如果为百分比,则为0
+        percentage #百分比
+        base_im # 参考基于其他图 PictureProcessing 格式
+        """
+
+        # 在箱子内
+        if base_by_box:
+            mode = "relative"
+            base = "by_im"
+            base_by_box = (int(base_by_box[0]), int(base_by_box[1]))
+            base_im = Image.new("RGB", base_by_box, (255, 255, 255))
+
+        # 绝对值
+        if mode == "pixel":
+            if base == "width":
+                img = self.__to_resize(width=value)
+            if base == "high":
+                img = self.__to_resize(height=value)
+
+        # 相对值
+        if mode == "relative":
+            if base == "width":
+                img = self.__to_resize(
+                    width=self.width * percentage if not base_im else int(base_im.width * percentage))
+            if base == "height":
+                img = self.__to_resize(
+                    width=self.height * percentage if not base_im else int(base_im.height * percentage))
+
+            # by_im确保能塞进参考图内
+            if base == "by_im":
+                percentage = 1 if not percentage else percentage
+                box_width, box_height = int(base_im.width * percentage), int(base_im.height * percentage)
+                width, height = self.width, self.height
+                if box_width / box_height < width / height:
+                    scale = box_width / width
+                else:
+                    scale = box_height / height
+                img = self.get_im().resize((int(width * scale), int(height * scale)))
+
+        return PictureProcessing(im=img)
+
+    def paste_img(self, mode="pixel", top_img="", base="nw", value=(0, 0), percentage=(0, 0), margins=(0, 0, 0, 0)):
+        top_img: PictureProcessing
+        """
+        {
+            "command": "paste_img",
+            "im": 需要粘贴的图片
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": (0.5, 0.5),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        }
+        """
+        value = (int(value[0]), int(value[1]))
+        # 处理默认值
+        base = "nw" if not base else base
+        percentage = (0, 0) if not percentage else percentage
+        if margins:
+            top, down, left, right = margins
+        else:
+            top, down, left, right = 0, 0, 0, 0
+
+        if percentage != (0, 0):  # percentage 不按占比模式
+            if base in ("nw", "wn", "wc", "cw", "nc", "cn", "center"):
+                value = (int(self.width), int(self.height))
+            if base in ("sw", "ws", "sc", "cs", "center"):
+                value = (int(self.width), -1 * int(self.height))
+            if base in ("ec", "ce"):
+                value = (int(self.width), int(self.height))
+
+        if mode == "pixel":
+            # 基于右边,上下居中
+            if base == "ec" or base == "ce":
+                p_x = int(self.width - (top_img.width + value[0]))
+                p_y = int((self.height - top_img.height) / 2) + value[1]
+
+            # 基于顶部,左右居中
+            if base == "nc" or base == "cn":
+                # 顶部对齐
+                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
+                    (self.height - top_img.height) / 2)
+                p_x = deviation_x + value[0] + left
+                p_y = value[1]
+
+            # 基于右上角
+            if base == "en" or base == "ne":
+                p_x = int(self.width - (top_img.width + value[0])) + left
+                p_y = value[1]
+
+            # 基于左上角
+            if base == "nw" or base == "wn":
+                deviation_x, deviation_y = 0, 0
+                p_x, p_y = value
+
+            # 基于底部,左右居中
+            if base == "cs" or base == "sc":
+                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
+                    (self.height - top_img.height) / 2)
+
+                p_y = self.height - (top_img.height + value[1] + down)
+                p_x = deviation_x + value[0] + left
+
+            # 上下左右居中
+            if base == "center" or base == "cc":
+                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
+                    (self.height - top_img.height) / 2)
+                p_x = deviation_x + value[0] + left
+                p_y = deviation_y + value[1] + top
+
+            # 基于左下角
+            if base == "sw" or base == "ws":
+                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
+                p_x = value[0] + left
+                p_y = self.height - (top_img.height + value[1] + down)
+
+            # 基于左边,上下居中
+            if base == "wc" or base == "cw":
+                p_x = value[0] + left
+                p_y = int((self.height - top_img.height) / 2) + value[1] + top
+
+            # 基于右下角
+            if base == "es" or base == "se":
+                p_x = int(self.width - (top_img.width + value[0])) + left
+                p_y = self.height - (top_img.height + value[1] + down) + top
+
+            img = PictureProcessing(im=self.im).im
+            # top_img.get_im().show()
+            top_img_im = top_img.im
+            try:
+                img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im.convert("RGBA"))
+            except:
+                img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im)
+
+        return PictureProcessing(im=img)
+
+    def paste_img_invert(self, mode="pixel", top_img="", base="nw", value=(0, 0), percentage=(0, 0),
+                         margins=(0, 0, 0, 0)):
+        top_img: PictureProcessing
+        bg_img = top_img.im
+
+        top_img = PictureProcessing(im=self.im)
+        self.im = bg_img
+        return self.paste_img(mode=mode, top_img=top_img, base=base, value=value, percentage=percentage,
+                              margins=margins)
+
+    # 水平分布处理,一行N个
+    def horizontal_distribution(self, pp_list, bg_width=1200, margins=(0, 0, 0, 0), line_spacing=0, number_per_row=3, ):
+        """
+        pp_list
+        line_spacing:行间距
+        number_per_row:每行个数
+        margins: (0, 0, 0, 0),  # 上下左右边距
+        """
+        total_row = len(pp_list) // number_per_row
+        if len(pp_list) % number_per_row > 0:
+            total_row = total_row + 1
+        # print("total_row", total_row)
+        every_height = pp_list[0].height
+        bg_pp = PictureProcessing("RGBA", (
+            bg_width, margins[0] + every_height * total_row + line_spacing * (total_row - 1) + margins[1]),
+                                  (255, 255, 255, 0))
+
+        row_num = 0
+        y = margins[0]
+        for i in range(0, len(pp_list), number_per_row):  # 切片获取每N个元素
+            row_list = pp_list[i:i + number_per_row]  # 输出每行的元素
+            if row_list:
+                row_num += 1
+                # 计算每个元素间距
+                t_w = bg_pp.width - margins[2] - margins[3]
+                al_w = sum([x.width for x in row_list])
+                space = int((t_w - al_w) / (len(row_list) + 1))
+                x = margins[2]
+                # 粘贴每个元素
+                for pp in row_list:
+                    x = x + space
+                    bg_pp = bg_pp.paste_img(mode="pixel", top_img=pp, base="", value=(x, y))
+                    x = x + pp.width
+
+                if row_num != total_row:
+                    y += (every_height + line_spacing)
+        return bg_pp
+
+    def get_text_image_advanced(self, value=(0, 0), font="", text="", anchor=None, align="left", spacing=0,
+                                fill=(0, 0, 0),
+                                return_mode="image", margins=(0, 0, 0, 0)):
+        """
+        {
+            "command": "add_text",
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": 0, },
+            "font": "",
+            "text": "",
+            "anchor": "",  # mm 为居中 ma 为左右居中,上面居顶 rs 右边;从哪边开始输入
+            "align": "对齐方式",left  center right
+            "direction": "文本的方向",
+            "max_len_one_line": "单行长度",
+            "spacing": 10,
+            "fill": "文字颜色",
+        }
+        margins 边距像素,上下左右;只有返回min_image_high 有效
+        """
+
+        # =====
+        """
+        return_mode:image返回图片,min_image_high返回最小高度尺寸但宽度不变,min_image返回最小高度与宽度
+        """
+        if not text:
+            return self
+        # pp = PictureProcessing("RGBA", self.size, (0, 0, 0, 0))
+        pp = PictureProcessing("RGBA", self.size, (255, 255, 255, 0))
+        draw_1 = ImageDraw.Draw(pp.im)
+        # 定义字体,你需要有一个.ttf字体文件
+        spacing = 4 if not spacing else spacing
+        align = "left" if not align else align  # left, center 或 right
+
+        _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
+        value = (int(value[0]), int(value[1]))
+
+        draw_1.multiline_text((value[0], value[1] + margins[0]), text,
+                              fill=fill,
+                              font=font,
+                              anchor=anchor,
+                              spacing=spacing,
+                              align=align,
+                              direction=None,
+                              features=None,
+                              language=None,
+                              stroke_width=0,
+                              stroke_fill=None,
+                              embedded_color=False)
+
+        if return_mode == "image":
+            pp = pp.paste_img_invert(mode="pixel", top_img=self, base="nw", value=(0, 0))
+            pass
+        if return_mode == "min_image_high":
+            # pp.show()
+            bbox = pp.getbbox()
+            bbox = (0, 0, pp.width, bbox[3] + margins[1])
+            pp = pp.paste_img_invert(mode="pixel", top_img=self, base="nw", value=(0, 0))
+            pp = pp.crop(bbox)
+
+        if return_mode == "min_image":
+            bbox = pp.getbbox()
+            pp = pp.crop(bbox)
+
+        return pp
+
+    def add_text(self, mode="", base="", value="", percentage="", font="", text="", anchor="", align="", direction="",
+                 max_len_one_line="", spacing="", fill=""):
+        """
+        {
+            "command": "add_text",
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": 0, },
+            "font": "",
+            "text": "",
+            "anchor": "",  # mm 为居中 ma 为左右居中,上面居顶 rs 右边;从哪边开始输入
+            "align": "对齐方式",left  center right
+            "direction": "文本的方向",
+            "max_len_one_line": "单行长度",
+            "spacing": 10,
+            "fill": "文字颜色",
+        }
+
+        """
+        pp = PictureProcessing(im=self.im)
+        draw_1 = ImageDraw.Draw(pp.im)
+        # 定义字体,你需要有一个.ttf字体文件
+        spacing = 4 if not spacing else spacing
+        anchor = None if not anchor else anchor
+        align = "left" if not align else align  # left, center 或 right
+        _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
+
+        xy = (0, 0)
+        if mode == "pixel":
+            xy = value
+
+        draw_1.multiline_text(xy, text,
+                              fill=fill,
+                              font=font,
+                              anchor=anchor,
+                              spacing=spacing,
+                              align=align,
+                              direction=None,
+                              features=None,
+                              language=None,
+                              stroke_width=0,
+                              stroke_fill=None,
+                              embedded_color=False)
+        return pp
+
+    def crop_img(self, mode="pixel", base="nw", value=(100, 100, 10, 10), color_fill=(255, 255, 255),
+                 margins=(0, 0, 0, 0)):
+        """
+        {
+            "command": "crop_img",
+            "img": {"im": "im"},
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100, 10, 10),# 顶点,以及图片大小
+                    },
+            "color_fill": (255, 255, 255)
+        }
+
+        """
+        pp = PictureProcessing(im=self.im)
+
+        base = "nw" if not base else base
+        if margins:
+            top, down, left, right = margins
+        else:
+            top, down, left, right = 0, 0, 0, 0
+
+        out_img_size = (value[2], value[3])
+        # 默认填充色
+        if not color_fill:
+            color_fill = (0, 0, 0)
+
+        if mode == "pixel":
+            # 左上脚
+            if base == "nw" or "wn":
+                box = value
+            # 左下角
+            if base == "sw" or base == "ws":
+                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
+                box = (value[0], pp.height - (value[1] + value[3]), value[2], value[3])
+                # print(box)
+
+            # 右下角
+            if base == "se" or base == "es":
+                box = (pp.width - (value[0] + value[2]), pp.height - (value[1] + value[3]), value[2], value[3])
+                # print(box)
+
+            # 居中
+            if base == "cc":
+                # print("11-value", value)
+                x = int((pp.width + value[0] - value[2]) / 2)
+                y = int((pp.height + value[1] - value[3]) / 2)
+                box = (x,
+                       y,
+                       value[2],
+                       value[3])
+                # print("11-box", box)
+
+            box = [box[0], box[1], box[0] + box[2], box[1] + box[3]]
+
+        # print("12-box", box)
+        # print("ww-hhh", pp.width, pp.height)
+        out_img = pp.im.crop(box=box)
+        # print("out_img", out_img.width, out_img.height)
+        # print("-----")
+
+        if box[0] < 0:
+            out_img.paste(Image.new("RGB", (-1 * box[0], out_img.height), color_fill), (0, 0))
+
+        if box[1] < 0:
+            out_img.paste(Image.new("RGB", (out_img.width, -1 * box[1]), color_fill), (0, 0))
+
+        if box[2] > pp.width:
+            # print(box[2] - img.width, img.height)
+            i = Image.new("RGB", (box[2] - pp.width, out_img.height), color_fill)
+            out_img.paste(i, (pp.width - box[0], 0))
+
+        if box[3] > pp.height:
+            out_img.paste(Image.new("RGB", (out_img.width, box[3] - pp.height), color_fill),
+                          (0, pp.height - box[1]))
+
+        return PictureProcessing(im=out_img)
+
+    def crop(self, bbox=(0, 0, 0, 0), mode=None):
+        if mode == "min":
+            bbox = self.getbbox()
+        return PictureProcessing(im=self.im.crop(bbox))
+
+    def rotate(self, doge):
+        return PictureProcessing(im=self.im.rotate(doge))
+
+    def rotate_advance(self, doge, is_crop=True):
+        max_px = max(self.width, self.height)
+        max_px = max_px * 2
+        bg = PictureProcessing("RGBA", (max_px, max_px), (255, 255, 255, 0))
+        bg = bg.paste_img(top_img=self, base="cc")
+        bg = bg.rotate(doge)
+        if is_crop:
+            bg = bg.crop(mode="min")
+
+        return bg
+
+    def radius(self, mode="pixel", circular_pos=(1, 1, 1, 1), value=30, percentage=""):
+        """
+        {"command": "radius",  # radius
+             "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+             "circular_pos": (0, 1, 0, 1),  # 从左上角顺时针,记录圆角数量
+             "value": 649,  # 固定值,如果为百分比,则为0
+             "percentage": 0, }  # 百分比
+        """
+        # 单图圆角处理
+        pp = PictureProcessing(im=self.im)
+
+        radii = value
+        if radii > pp.width / 2:
+            radii = int(pp.width / 2)
+        if radii > pp.height / 2:
+            radii = int(pp.height / 2)
+
+        # 画圆(用于分离4个角)
+        circle = Image.new('L', (radii * 2, radii * 2), 0)  # 创建一个黑色背景的画布
+        draw = ImageDraw.Draw(circle)
+        draw.ellipse((0, 0, radii * 2, radii * 2), fill=255)  # 画白色圆形
+
+        # 原图
+        img = pp.im.convert("RGBA")
+        w, h = img.size
+
+        # 画4个角(将整圆分离为4个部分)
+        alpha = Image.new('L', img.size, 255)
+        _pos = circular_pos
+        if not _pos:
+            _pos = (1, 1, 1, 1)
+        for index, i in enumerate(_pos):
+            if index == 0 and i == 1:
+                alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0))  # 左上角
+            if index == 1 and i == 1:
+                alpha.paste(circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0))  # 右上角
+            if index == 2 and i == 1:
+                alpha.paste(circle.crop((radii, radii, radii * 2, radii * 2)), (w - radii, h - radii))  # 右下角
+            if index == 3 and i == 1:
+                alpha.paste(circle.crop((0, radii, radii, radii * 2)), (0, h - radii))  # 左下角
+        # alpha.show()
+        img.putalpha(alpha)  # 白色区域透明可见,黑色区域不可见
+        return PictureProcessing(im=img)
+
+    def transpose(self, mode="left_right"):
+        img = self.get_im()
+        img = img.transpose(Image.FLIP_LEFT_RIGHT)
+        return PictureProcessing(im=img)
+
+    def convert(self, mode):
+        return PictureProcessing(im=self.im.convert(mode))
+
+    def save_as_rgb(self, path):
+        self.im = self.im.convert("RGB")
+        self.im.save(path, format="JPEG")
+
+    def save_as_png(self, path):
+
+        self.im = self.im.convert("RGBA")
+        self.im.save(path)

+ 422 - 0
python/service/generate_main_image/grenerate_main_image_test.py

@@ -0,0 +1,422 @@
+import os
+import copy
+import time
+from .image_deal_base_func import *
+from PIL import Image, ImageDraw
+from blend_modes import multiply
+import os
+import settings
+from functools import wraps
+
+
+def time_it(func):
+    @wraps(func)  # 使用wraps来保留原始函数的元数据信息
+    def wrapper(*args, **kwargs):
+        start_time = time.time()  # 记录开始时间
+        result = func(*args, **kwargs)  # 调用原始函数
+        end_time = time.time()  # 记录结束时间
+        print(f"Executing {func.__name__} took {end_time - start_time:.4f} seconds.")  # 打印耗时
+        return result
+
+    return wrapper
+
+
+class GeneratePic(object):
+    def __init__(self, is_test=False):
+        # self.logger = MyLogger()
+        self.is_test = is_test
+        pass
+
+    @time_it
+    def get_mask_and_config(self, im_jpg: Image, im_png: Image):
+        """
+        步骤:
+        1、尺寸进行对应缩小
+        2、查找并设定鞋底阴影蒙版
+        3、自动色阶检查亮度
+        4、输出自动色阶参数、以及放大的尺寸蒙版
+        """
+        # ===================尺寸进行对应缩小(提升处理速度)
+        im_jpg = to_resize(im_jpg, width=800)
+        im_png = to_resize(im_png, width=800)
+        x1, y1, x2, y2 = im_png.getbbox()
+
+        cv2_png = pil_to_cv2(im_png)
+        # =====================设定鞋底阴影图的蒙版
+        # 查找每列的最低非透明点
+        min_y_values = find_lowest_non_transparent_points(cv2_png)
+        # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
+        image_high = im_jpg.height
+        print("图片高度:", image_high)
+        cv2_jpg = pil_to_cv2(im_jpg)
+        # 返回线条图片,以及最低位置
+        img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
+                                                            min_y_values=min_y_values,
+                                                            shift_amount=15,
+                                                            one_line_pos=(x1, x2),
+                                                            line_color=(0, 0, 0),
+                                                            line_thickness=20)
+
+        # 制作蒙版
+        mask_line = cv2_to_pil(img_with_shifted_line)
+        mask = mask_line.convert('L')  # 转换为灰度图
+        mask = ImageOps.invert(mask)
+        # 蒙版扩边
+        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
+
+        # ====================生成新的图片
+        bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
+        bg.paste(im_png, mask=im_png)
+        bg.paste(im_jpg, mask=mask)  # 粘贴有阴影的地方
+
+        if self.is_test:
+            _bg = bg.copy()
+            draw = ImageDraw.Draw(_bg)
+            # 定义直线的起点和终点坐标
+            start_point = (0, lowest_y)  # 直线的起始点
+            end_point = (_bg.width, lowest_y)  # 直线的结束点
+            # 定义直线的颜色(R, G, B)
+            line_color = (255, 0, 0)  # 红色
+            # 绘制直线
+            draw.line([start_point, end_point], fill=line_color, width=1)
+            # mask.show()
+            # bg = pil_to_cv2(bg)
+            # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
+            # bg = cv2_to_pil(bg)
+            _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
+            mask_line = mask_line.convert('L')  # 转换为灰度图
+            mask_line = ImageOps.invert(mask_line)
+            _bg.paste(_r, mask=mask)
+            _bg.show()
+
+        # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
+        # bg.show()
+        # ==================自动色阶处理======================
+        # 对上述拼接后的图片进行自动色阶处理
+        bg = bg.convert("RGB")
+        _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
+        # 背景阴影
+        im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
+        print("image_high lowest_y", image_high, lowest_y)
+        if lowest_y < 0 or lowest_y >= image_high:
+            lowest_y = image_high - 1
+        print("image_high lowest_y", image_high, lowest_y)
+        rows = [lowest_y]  # 需要检查的像素行
+
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.7
+        Highlight = 235
+        k = 8
+        while k:
+            k -= 1
+            Midtones += 0.1
+            if Midtones > 1:
+                Midtones = 1
+            Highlight -= 3
+            _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
+                                       OutShadow=0,
+                                       OutHighlight=255, Dim=3)
+            brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            print(brightness_list)
+
+            if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
+                break
+        print("Midtones,Highlight:", Midtones, Highlight)
+
+        im_shadow = cv2_to_pil(_im_shadow)
+
+        # ========================================================
+        # 计算阴影的亮度,用于确保阴影不要太黑
+
+        # 1、图片预处理,只保留阴影
+        only_shadow_img = im_shadow.copy()
+        only_shadow_img.paste(Image.new(mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)),
+                              mask=im_png)
+        average_brightness = calculated_shadow_brightness(only_shadow_img)
+        print("average_brightness:", average_brightness)
+
+        config = {
+            "Midtones": Midtones,
+            "Highlight": Highlight,
+            "average_brightness": average_brightness,
+        }
+
+        return mask, config
+
+    def get_mask_and_config_beifen(self, im_jpg: Image, im_png: Image, out_image_path=None):
+        """
+        步骤:
+        1、尺寸进行对应缩小
+        2、查找并设定鞋底阴影蒙版
+        3、自动色阶检查亮度
+        4、输出自动色阶参数、以及放大的尺寸蒙版
+        """
+        # ===================尺寸进行对应缩小
+        orign_x, orign_y = im_jpg.size
+
+        im_jpg = to_resize(im_jpg, width=800)
+        im_png = to_resize(im_png, width=800)
+        x1, y1, x2, y2 = im_png.getbbox()
+
+        cv2_png = pil_to_cv2(im_png)
+        # =====================设定鞋底阴影图的蒙版
+        # 查找每列的最低非透明点
+        min_y_values = find_lowest_non_transparent_points(cv2_png)
+        # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
+        cv2_jpg = pil_to_cv2(im_jpg)
+        # 返回线条图片,以及最低位置
+        img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
+                                                            min_y_values=min_y_values,
+                                                            shift_amount=15,
+                                                            one_line_pos=(x1, x2),
+                                                            line_color=(0, 0, 0),
+                                                            line_thickness=20)
+
+        # 制作蒙版
+        mask = cv2_to_pil(img_with_shifted_line)
+        mask = mask.convert('L')  # 转换为灰度图
+        mask = ImageOps.invert(mask)
+        # 蒙版扩边
+        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
+
+        # ====================生成新的图片
+        bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
+        bg.paste(im_png, mask=im_png)
+        bg.paste(im_jpg, mask=mask)  # 粘贴有阴影的地方
+
+        # bg = pil_to_cv2(bg)
+        # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
+        # bg = cv2_to_pil(bg)
+        # bg.show()
+
+        # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
+        # bg.show()
+        # ==================自动色阶处理======================
+        # 对上述拼接后的图片进行自动色阶处理
+        bg = bg.convert("RGB")
+        _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
+        # 背景阴影
+        im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
+
+        rows = [lowest_y]  # 需要检查的像素行
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.62
+        Highlight = 235
+        k = 10
+        while k:
+            k -= 1
+            Midtones += 0.1
+            if Midtones > 1:
+                Midtones = 1
+            Highlight -= 3
+            _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
+                                       OutShadow=0,
+                                       OutHighlight=255, Dim=3)
+            brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            print(brightness_list)
+            if brightness_list[0] >= 254:
+                break
+        print("Midtones,Highlight:", Midtones, Highlight)
+        config = (Midtones, Highlight)
+        im_shadow = cv2_to_pil(_im_shadow)
+        im_shadow.paste(im_png, (0, 0), im_png)  # 把原图粘贴回去,避免色差
+        if out_image_path:
+            im_shadow.save(out_image_path)
+
+        return mask, config
+
+    def my_test(self,**kwargs):
+        if "output_queue" in kwargs:
+            output_queue = kwargs["output_queue"]
+        else:
+            output_queue = None
+        time.sleep(3)
+        if output_queue is not None:
+            output_queue.put(True)
+
+    @time_it
+    def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
+            out_pic_size=1024, is_logo=True, out_process_path_1=None, out_process_path_2=None,
+            resize_mode=None, max_box=None, logo_path="", **kwargs):  # im 为cv对象
+        """
+        image_path:原始图
+        cut_image_path:抠图结果 与原始图尺寸相同
+        out_path:输出主图路径
+        image_deal_mode:图片处理模式,1表示需要镜像处理
+        image_index:图片顺序索引
+        out_pic_size:输出图片宽度大小
+        is_logo=True 是否要添加logo水印
+        out_process_path_1=None, 有阴影的图片,白底非透明
+        out_process_path_2=None, 已抠图的图片
+        resize_mode=0,1,2 主体缩小尺寸
+        """
+        if "output_queue" in kwargs:
+            output_queue = kwargs["output_queue"]
+        else:
+            output_queue = None
+
+        # ==========先进行剪切原图
+        _s = time.time()
+        orign_im = Image.open(image_path)  # 原始图
+        print("242  need_time_1:{}".format(time.time() - _s))
+
+        orign_x, orign_y = orign_im.size
+        cut_image = Image.open(cut_image_path)  # 原始图的已扣图
+        cut_image, new_box = get_mini_crop_img(img=cut_image)
+        im_shadow = orign_im.crop(new_box)  # 切图
+        new_x, new_y = im_shadow.size
+
+        # ================自动色阶处理
+        _s = time.time()
+        shadow_mask, config = self.get_mask_and_config(im_jpg=im_shadow, im_png=cut_image)
+        print("242  need_time_2:{}".format(time.time() - _s))
+
+        shadow_mask = shadow_mask.resize(im_shadow.size)
+
+        # =====抠图,形成新的阴影背景图=====
+        _new_im_shadow = Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
+        _new_im_shadow.paste(im_shadow, mask=shadow_mask)  # 粘贴有阴影的地方
+        # _new_im_shadow.show()
+
+        _new_im_shadow = pil_to_cv2(_new_im_shadow)
+        _new_im_shadow = cv2.cvtColor(_new_im_shadow, cv2.COLOR_BGR2GRAY)
+        _new_im_shadow = levels_adjust(img=_new_im_shadow,
+                                       Shadow=0,
+                                       Midtones=config["Midtones"],
+                                       Highlight=config["Highlight"],
+                                       OutShadow=0,
+                                       OutHighlight=255, Dim=3)
+
+        im_shadow = cv2_to_pil(_new_im_shadow)
+
+        # ================处理阴影的亮度==================
+        average_brightness = config["average_brightness"]
+        if config["average_brightness"] < 180:
+            # 调整阴影亮度
+            backdrop_prepped = np.asfarray(Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)))
+            im_shadow = im_shadow.convert("RGBA")
+            source_prepped = np.asfarray(im_shadow)
+            # im_shadow.show()
+
+            opacity = (average_brightness - 30) / 160
+            opacity = max(0.5, min(opacity, 1))
+
+            print("阴影透明度:{}%".format(int(opacity * 100)))
+            blended_np = multiply(backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100)
+            im_shadow = Image.fromarray(np.uint8(blended_np)).convert('RGB')
+            # im_shadow.show()
+
+        # 把原图粘贴回去,避免色差
+        im_shadow.paste(cut_image, (0, 0), mask=cut_image)
+        # _new_im_shadow.show()
+
+        # ===========处理其他====================
+
+        # 保存带有阴影的底图,没有logo
+        if out_process_path_1:
+            out_image_1 = im_shadow.copy()
+            if image_deal_mode == 1:
+                out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
+            out_image_1.save(out_process_path_1)
+
+        # 保存抠图结果,没有底图,没有logo
+        if out_process_path_2:
+            out_image_2 = cut_image.copy()
+            if image_deal_mode == 1:
+                out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
+            out_image_2.save(out_process_path_2)
+
+        # 不生成主图时直接退出
+        if not out_path:
+            return True
+
+        # im_shadow.show()
+        # =====================主图物体的缩放依据大小
+        if max_box:
+            im_shadow = to_resize(_im=im_shadow, width=max_box[0], high=max_box[1])
+            cut_image = to_resize(_im=cut_image, width=max_box[0], high=max_box[1])
+        else:
+            if resize_mode is None:
+                im_shadow = to_resize(_im=im_shadow, width=1400, high=1400)
+                cut_image = to_resize(_im=cut_image, width=1400, high=1400)
+
+            elif resize_mode == 1:
+                im_shadow = to_resize(_im=im_shadow, width=1400, high=1400)
+                cut_image = to_resize(_im=cut_image, width=1400, high=1400)
+
+            elif resize_mode == 2:
+                # todo 兼容长筒靴等,将图片大小限制在一个指定的box内
+                im_shadow = to_resize(_im=im_shadow, width=650)
+                cut_image = to_resize(_im=cut_image, width=650)
+                # 再次检查需要约束缩小到一定高度,适应长筒靴
+                _im_x, _im_y = cut_image.size
+                if _im_y > 1400:
+                    im_shadow = to_resize(_im=im_shadow, high=1400)
+                    cut_image = to_resize(_im=cut_image, high=1400)
+
+                # if im_shadow.height <= im_shadow.width * 1.2:
+                #     im_shadow = to_resize(_im=im_shadow, width=650)
+                #     cut_image = to_resize(_im=cut_image, width=650)
+                # else:
+                #     im_shadow = to_resize(_im=im_shadow, high=1400)
+                #     cut_image = to_resize(_im=cut_image, high=1400)
+
+        if image_deal_mode == 1:
+            # 翻转
+            im_shadow = im_shadow.transpose(Image.FLIP_LEFT_RIGHT)
+            cut_image = cut_image.transpose(Image.FLIP_LEFT_RIGHT)
+
+        # 创建底层背景
+        image_bg = Image.new("RGB", (1600, 1600), (255, 255, 255))
+
+        image_bg_x, image_bg_y = image_bg.size
+        image_x, image_y = im_shadow.size
+
+        _x = int((image_bg_x - image_x) / 2)
+        _y = int((image_bg_y - image_y) / 2)
+
+        image_bg.paste(im_shadow, (_x, _y))
+        image_bg.paste(cut_image, (_x, _y), cut_image)  # 再叠加原图避免色差
+
+        if "小苏" in settings.Company:
+            # 所有主图加logo
+            is_logo = True
+
+        if is_logo:
+            # logo_path = ""
+            # if settings.PROJECT == "红蜻蜓":
+            #     logo_path = r"resources\LOGO\HQT\logo.png"
+            # elif settings.PROJECT == "惠利玛":
+            #     if "小苏" in settings.Company:
+            #         logo_path = r"resources\LOGO\xiaosushuoxie\logo.png"
+            #     elif "惠利玛" in settings.Company:
+            #         logo_path = r"resources\LOGO\HLM\logo.png"
+            #     else:
+            #         pass
+            if not logo_path:
+                logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
+            else:
+                if os.path.exists(logo_path):
+                    logo_im = Image.open(logo_path)
+                else:
+                    logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
+
+            image_bg.paste(logo_im, (0, 0), logo_im)
+
+        # image_bg = image_bg.resize((out_pic_size, out_pic_size), Image.BICUBIC)
+        if settings.OUT_PIC_FACTOR > 1.0:
+            print("图片锐化处理")
+            image_bg = sharpen_image(image_bg, factor=settings.OUT_PIC_FACTOR)
+
+        if out_pic_size < 1600:
+            image_bg = image_bg.resize((out_pic_size, out_pic_size), resample=settings.RESIZE_IMAGE_MODE)
+
+        if settings.OUT_PIC_MODE == ".jpg":
+            image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+        else:
+            # quality=quality
+            image_bg.save(out_path, quality=100)
+
+        if output_queue is not None:
+            output_queue.put(True)
+        return True

+ 216 - 0
python/service/generate_main_image/image_deal_base_func.py

@@ -0,0 +1,216 @@
+import cv2
+import numpy as np
+from PIL import Image, ImageEnhance, ImageFilter, ImageOps
+import settings
+
+# 锐化图片
+def sharpen_image(img, factor=1.0):
+    # 创建一个ImageEnhance对象
+    enhancer = ImageEnhance.Sharpness(img)
+    # 应用增强,值为0.0给出模糊图像,1.0给出原始图像,大于1.0给出锐化效果
+    # 调整这个值来增加或减少锐化的程度
+    sharp_img = enhancer.enhance(factor)
+    return sharp_img
+
+
+def to_resize(_im, width=None, high=None) -> Image:
+    _im_x, _im_y = _im.size
+    if width and high:
+        if _im_x >= _im_y:
+            high = None
+        else:
+            width = None
+    if width:
+        re_x = int(width)
+        re_y = int(_im_y * re_x / _im_x)
+    else:
+        re_y = int(high)
+        re_x = int(_im_x * re_y / _im_y)
+    _im = _im.resize((re_x, re_y),resample=settings.RESIZE_IMAGE_MODE)
+    return _im
+
+
+def pil_to_cv2(pil_image):
+    # 将 PIL 图像转换为 RGB 或 RGBA 格式
+    if pil_image.mode != 'RGBA':
+        pil_image = pil_image.convert('RGBA')
+    # 将 PIL 图像转换为 numpy 数组
+    cv2_image = np.array(pil_image)
+    # 由于 PIL 的颜色顺序是 RGB,而 OpenCV 的颜色顺序是 BGR,因此需要交换颜色通道
+    cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_RGBA2BGRA)
+    return cv2_image
+
+
+def cv2_to_pil(cv_img):
+    return Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
+
+
+def get_mini_crop_img(img):
+    old_x, old_y = img.size
+    x1, y1, x2, y2 = img.getbbox()
+
+    goods_w, goods_h = x2 - x1, y2 - y1
+    _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
+    new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
+    new_x1 = 0 if new_x1 < 0 else new_x1
+    new_y1 = 0 if new_y1 < 0 else new_y1
+    new_x2 = old_x if new_x2 > old_x else new_x2
+    new_y2 = old_y if new_y2 > old_y else new_y2
+    img = img.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+    box = (new_x1, new_y1, new_x2, new_y2)
+    return img, box
+
+
+def expand_mask(mask, expansion_radius=5, blur_radius=0):
+    # 对蒙版进行膨胀处理
+    mask = mask.filter(ImageFilter.MaxFilter(expansion_radius * 2 + 1))
+    # 应用高斯模糊滤镜
+    if blur_radius > 0:
+        mask = mask.filter(ImageFilter.GaussianBlur(blur_radius))
+
+    return mask
+
+
+def find_lowest_non_transparent_points(cv2_png):
+    # cv2_png 为cv2格式的带有alpha通道的图片
+
+    alpha_channel = cv2_png[:, :, 3]
+    """使用Numpy快速查找每列的最低非透明点"""
+    h, w = alpha_channel.shape
+    # 创建一个掩码,其中非透明像素为True
+    mask = alpha_channel > 0
+    # 使用np.argmax找到每列的第一个非透明像素的位置
+    # 因为是从底部向上找,所以需要先翻转图像
+    flipped_mask = np.flip(mask, axis=0)
+    min_y_values = h - np.argmax(flipped_mask, axis=0) - 1
+    # 将全透明列的值设置为-1
+    min_y_values[~mask.any(axis=0)] = -1
+    return min_y_values
+
+
+def draw_shifted_line(image, min_y_values, shift_amount=15,
+                      one_line_pos=(0, 100),
+                      line_color=(0, 0, 0),
+                      line_thickness=20):
+    """
+    image:jpg cv2格式的原始图
+    min_y_values 透明图中,不透明区域的最低那条线
+    shift_amount:向下偏移值
+    line_color:线颜色
+    line_thickness:线宽
+    """
+    # 将最低Y值向下迁移20个像素,但确保不超过图片的高度
+    # 创建空白图片
+    image = np.ones((image.shape[0], image.shape[1], 3), dtype=np.uint8) * 255
+
+    # 对线条取转成图片
+    shifted_min_y_values = np.clip(min_y_values + shift_amount, 0, image.shape[0] - 1)
+
+    # 使用Numpy索引批量绘制直线
+    min_y_threshold = 50  # Y轴像素小于50的不处理
+    valid_x = (shifted_min_y_values >= min_y_threshold) & (shifted_min_y_values != -1)
+
+    # 对曲线取平均值
+    # # 对曲线取平均值
+    # min_y = np.max(min_y_values)
+    # min_y_values_2 = min_y_values + min_y
+    # min_y_values_2 = min_y_values_2 / 2
+    # min_y_values_2 = min_y_values_2.astype(int)
+    # shifted_min_y_values = np.clip(min_y_values_2 + shift_amount, 0, image.shape[0] - 1)
+
+    x_coords = np.arange(image.shape[1])[valid_x]
+    y_start = shifted_min_y_values[valid_x]
+    y_end = y_start + line_thickness
+
+    # 使用Numpy广播机制创建线条区域的索引
+    for x, start, end in zip(x_coords, y_start, y_end):
+        image[start:end, x, :3] = line_color  # 只修改RGB通道
+
+    # 计算整个图像的最低非透明点
+    lowest_y = np.max(min_y_values[min_y_values != -1]) if np.any(min_y_values != -1) else -1
+    # 绘制原最低非透明点处的线
+    cv2.line(image, (one_line_pos[0], lowest_y + 5), (one_line_pos[1], lowest_y + 5), line_color,
+             thickness=line_thickness)
+
+    _y = lowest_y + 18
+    if _y > image.shape[0]:  # 超过图片尺寸
+        _y = image.shape[0] - 5
+    return image, _y
+
+
+def clean_colors(img):
+    # 转成灰度图
+    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+    return img
+
+
+def calculated_shadow_brightness(img: Image):
+    # 打开图片并转换为灰度模式
+    image = img.convert('L')
+    # 将图片数据转为numpy数组
+    image_data = np.array(image)
+    # 创建布尔掩码以识别非白色区域
+    non_white_mask = image_data < 252
+
+    # 使用掩码提取非白色像素的亮度值
+    non_white_values = image_data[non_white_mask]
+
+    # print(len(non_white_values),len(image_data))
+    # 如果存在非白色像素,则计算平均亮度;否则返回0
+    if len(non_white_values) > 0:
+        average_brightness = np.mean(non_white_values)
+    else:
+        average_brightness = 0  # 没有非白色像素时的情况
+
+    return average_brightness
+
+
+def levels_adjust(img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
+    # 色阶处理
+    # img 为cv2格式
+
+    # dim = 3的时候调节RGB三个分量, 0调节B,1调节G,2调节R
+    if Dim == 3:
+        mask_shadow = img < Shadow
+        img[mask_shadow] = Shadow
+        mask_Highlight = img > Highlight
+        img[mask_Highlight] = Highlight
+    else:
+        mask_shadow = img[..., Dim] < Shadow
+        img[mask_shadow] = Shadow
+        mask_Highlight = img[..., Dim] > Highlight
+        img[mask_Highlight] = Highlight
+
+    if Dim == 3:
+        Diff = Highlight - Shadow
+        rgbDiff = img - Shadow
+        clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+        outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+        data = np.array(outClRgb * 255, dtype='uint8')
+        img = data
+    else:
+        Diff = Highlight - Shadow
+        rgbDiff = img[..., Dim] - Shadow
+        clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+        outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+        data = np.array(outClRgb * 255, dtype='uint8')
+        img[..., Dim] = data
+    return img
+
+
+def calculate_average_brightness_opencv(img_gray, rows_to_check):
+    # 二值化的图片 CV对象
+    # 计算图片亮度
+    height, width = img_gray.shape
+
+    brightness_list = []
+    for row in rows_to_check:
+        if 0 <= row < height:
+            # 直接计算该行的平均亮度
+            row_data = img_gray[row, :]
+            average_brightness = np.mean(row_data)
+            brightness_list.append(average_brightness)
+        else:
+            print(f"警告:行号{row}超出图片范围,已跳过。")
+
+    return brightness_list

+ 481 - 0
python/service/generate_main_image/module_generate_main_image.py

@@ -0,0 +1,481 @@
+"""
+步骤:
+1、抠图
+2、色阶处理,保留阴影
+3、定位商品中心,并进行抠图
+4、缩放定位,并粘贴到中心位置
+5、粘贴水印
+"""
+import copy
+import time
+import cv2
+import numpy as np
+from PIL import Image, ImageEnhance
+import os
+from module.log.log import MyLogger
+
+if __name__ != '__main__':
+    import settings
+
+
+class GeneratePic(object):
+    def __init__(self):
+        self.logger = MyLogger()
+        pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+
+    def clean_colors(self, img):
+        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+        return img
+
+    def levels_adjust(self, img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
+        # print(img)
+        # dim = 3的时候调节RGB三个分量, 0调节B,1调节G,2调节R
+        if Dim == 3:
+            mask_shadow = img < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img > Highlight
+            img[mask_Highlight] = Highlight
+        else:
+            mask_shadow = img[..., Dim] < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img[..., Dim] > Highlight
+            img[mask_Highlight] = Highlight
+
+        if Dim == 3:
+            Diff = Highlight - Shadow
+            rgbDiff = img - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img = data
+        else:
+            Diff = Highlight - Shadow
+            rgbDiff = img[..., Dim] - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img[..., Dim] = data
+        return img
+
+    def calculate_average_brightness_opencv(self, img_gray, rows_to_check):
+        # 二值化的图片 CV对象
+        height, width = img_gray.shape
+
+        brightness_list = []
+        for row in rows_to_check:
+            if 0 <= row < height:
+                # 直接计算该行的平均亮度
+                row_data = img_gray[row, :]
+                average_brightness = np.mean(row_data)
+                brightness_list.append(average_brightness)
+            else:
+                print(f"警告:行号{row}超出图片范围,已跳过。")
+
+        return brightness_list
+
+    def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
+            out_pic_size=1024, is_logo=True, out_process_path_1=None, out_process_path_2=None,
+            resize_mode=None, **kwargs):  # im 为cv对象
+        """
+        image_path:原始图
+        cut_image_path:抠图结果
+        out_path:输出文件夹
+        image_deal_mode:图片处理模式,1表示需要镜像处理
+        """
+
+        # ==========先进行剪切原图
+        im = Image.open(image_path)
+        old_x, old_y = im.size
+
+        cut_image = Image.open(cut_image_path)
+        try:
+            x1, y1, x2, y2 = cut_image.getbbox()
+            # im_shadow.crop((x1, y1, x2, y2)).show()# 切图
+        except BaseException as e:
+            print("抠图失败")
+            return False
+
+        goods_w, goods_h = x2 - x1, y2 - y1
+        _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
+        new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
+        new_x1 = 0 if new_x1 < 0 else new_x1
+        new_y1 = 0 if new_y1 < 0 else new_y1
+        new_x2 = old_x if new_x2 > old_x else new_x2
+        new_y2 = old_y if new_y2 > old_y else new_y2
+
+        im = im.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+        cut_image = cut_image.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+
+        new_x, new_y = im.size
+        # ================自动色阶处理
+
+        _im = cv2.cvtColor(np.asarray(im), cv2.COLOR_RGB2BGR)
+        # 背景阴影
+        im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
+
+        s = time.time()
+        rows = [int(new_y * 19.8 / 20)]
+
+        # draw = ImageDraw.Draw(im)
+        # for row in rows:
+        #     draw.line((0, row, new_x, row), fill=128, width=3)
+        # im.show()
+
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.52
+        Highlight = 235
+        k = 20
+        while k:
+            k -= 1
+            Midtones += 0.1
+            Highlight -= 5
+            _im_shadow = self.levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
+                                            OutShadow=0,
+                                            OutHighlight=255, Dim=3)
+            brightness_list = self.calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            print(brightness_list)
+            if brightness_list[0] >= 254:
+                break
+
+        print(time.time() - s)
+
+        im_shadow = Image.fromarray(cv2.cvtColor(_im_shadow, cv2.COLOR_BGR2RGB))
+        im_shadow.paste(cut_image, (0, 0), cut_image)  # 把原图粘贴回去,避免色差
+
+        # 保存带有阴影的底图,没有logo
+        if out_process_path_1:
+            out_image_1 = im_shadow.copy()
+            if image_deal_mode == 1:
+                out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
+            out_image_1.save(out_process_path_1)
+
+        # 保存抠图结果,没有底图,没有logo
+        if out_process_path_2:
+            out_image_2 = cut_image.copy()
+            if image_deal_mode == 1:
+                out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
+            out_image_2.save(out_process_path_2)
+
+        # im_shadow.show()
+        # 主图物体的缩放依据大小
+        if resize_mode is None:
+            im_shadow = self.to_resize(_im=im_shadow, width=1400, high=1400)
+            cut_image = self.to_resize(_im=cut_image, width=1400, high=1400)
+
+        elif resize_mode == 1:
+            im_shadow = self.to_resize(_im=im_shadow, width=1400, high=1400)
+            cut_image = self.to_resize(_im=cut_image, width=1400, high=1400)
+
+        elif resize_mode == 2:
+            if im_shadow.height <= im_shadow.width * 1.2:
+                im_shadow = self.to_resize(_im=im_shadow, width=650)
+                cut_image = self.to_resize(_im=cut_image, width=650)
+            else:
+                im_shadow = self.to_resize(_im=im_shadow, high=1400)
+                cut_image = self.to_resize(_im=cut_image, high=1400)
+
+        if image_deal_mode == 1:
+            # 翻转
+            im_shadow = im_shadow.transpose(Image.FLIP_LEFT_RIGHT)
+            cut_image = cut_image.transpose(Image.FLIP_LEFT_RIGHT)
+
+        # 创建底层背景
+        image_bg = Image.new("RGB", (1600, 1600), (255, 255, 255))
+
+        image_bg_x, image_bg_y = image_bg.size
+        image_x, image_y = im_shadow.size
+
+        _x = int((image_bg_x - image_x) / 2)
+        _y = int((image_bg_y - image_y) / 2)
+
+        image_bg.paste(im_shadow, (_x, _y))
+        image_bg.paste(cut_image, (_x, _y), cut_image)  # 再叠加原图避免色差
+
+        if is_logo:
+            logo_path = ""
+            if settings.PROJECT == "红蜻蜓":
+                logo_path = r"resources\LOGO\HQT\logo.png"
+            elif settings.PROJECT == "惠利玛":
+                if "小苏" in settings.Company:
+                    logo_path = r"resources\LOGO\xiaosushuoxie\logo.png"
+                elif "惠利玛" in settings.Company:
+                    logo_path = r"resources\LOGO\HLM\logo.png"
+                else:
+                    pass
+
+            if not logo_path:
+                logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
+            else:
+                if os.path.exists(logo_path):
+                    logo_im = Image.open(logo_path)
+                else:
+                    logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
+
+            image_bg.paste(logo_im, (0, 0), logo_im)
+
+
+        image_bg = image_bg.resize((out_pic_size, out_pic_size), Image.BICUBIC)
+
+        # image_bg.show()
+        image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+        return True
+
+
+class GeneratePicPiJu(object):
+    def __init__(self):
+        pass
+
+    def calculate_average_brightness_opencv(self, img_gray, rows_to_check):
+        # 二值化的图片 CV对象
+        height, width = img_gray.shape
+
+        brightness_list = []
+        for row in rows_to_check:
+            if 0 <= row < height:
+                # 直接计算该行的平均亮度
+                row_data = img_gray[row, :]
+                average_brightness = np.mean(row_data)
+                brightness_list.append(average_brightness)
+            else:
+                print(f"警告:行号{row}超出图片范围,已跳过。")
+
+        return brightness_list
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+
+    def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
+            out_pic_size=800, is_logo=True, out_process_path_1=None, out_process_path_2=None, max_box=None):  # im 为cv对象
+        """
+        image_path:原始图
+        cut_image_path:抠图结果
+        out_path:输出文件夹
+        image_deal_mode:图片处理模式,1表示需要镜像处理
+        """
+
+        # ==========先进行剪切原图
+        im = Image.open(image_path)
+        old_x, old_y = im.size
+
+        cut_image = Image.open(cut_image_path)
+        try:
+            x1, y1, x2, y2 = cut_image.getbbox()
+            # im_shadow.crop((x1, y1, x2, y2)).show()# 切图
+        except BaseException as e:
+            print("抠图失败")
+            return False
+
+        goods_w, goods_h = x2 - x1, y2 - y1
+        _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
+        new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
+        new_x1 = 0 if new_x1 < 0 else new_x1
+        new_y1 = 0 if new_y1 < 0 else new_y1
+        new_x2 = old_x if new_x2 > old_x else new_x2
+        new_y2 = old_y if new_y2 > old_y else new_y2
+
+        im = im.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+        cut_image = cut_image.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+
+        new_x, new_y = im.size
+        # ================自动色阶处理
+
+        _im = cv2.cvtColor(np.asarray(im), cv2.COLOR_RGB2BGR)
+        # 背景阴影
+        im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
+
+        s = time.time()
+        rows = [int(new_y * 19.8 / 20)]
+
+        # draw = ImageDraw.Draw(im)
+        # for row in rows:
+        #     draw.line((0, row, new_x, row), fill=128, width=3)
+        # im.show()
+
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.52
+        Highlight = 235
+        k = 20
+        while k:
+            k -= 1
+            Midtones += 0.1
+            Highlight -= 5
+            _im_shadow = self.levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
+                                            OutShadow=0,
+                                            OutHighlight=255, Dim=3)
+            brightness_list = self.calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            print(brightness_list)
+            if brightness_list[0] >= 254:
+                break
+
+        print(time.time() - s)
+
+        im_shadow = Image.fromarray(cv2.cvtColor(_im_shadow, cv2.COLOR_BGR2RGB))
+        im_shadow.paste(cut_image, (0, 0), cut_image)  # 把原图粘贴回去,避免色差
+
+        # 保存带有阴影的底图,没有logo
+        if out_process_path_1:
+            out_image_1 = im_shadow.copy()
+            if image_deal_mode == 1:
+                out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
+            out_image_1.save(out_process_path_1)
+
+        # 保存抠图结果,没有底图,没有logo
+        if out_process_path_2:
+            out_image_2 = cut_image.copy()
+            if image_deal_mode == 1:
+                out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
+            out_image_2.save(out_process_path_2)
+
+        # im_shadow.show()
+        if max_box:
+            im_shadow = self.to_resize(_im=im_shadow, width=max_box[0], high=max_box[1])
+            cut_image = self.to_resize(_im=cut_image, width=max_box[0], high=max_box[1])
+        else:
+            if image_index != 3:
+                im_shadow = self.to_resize(_im=im_shadow, width=1000, high=1000)
+                cut_image = self.to_resize(_im=cut_image, width=1000, high=1000)
+            else:
+                im_shadow = self.to_resize(_im=im_shadow, width=650)
+                cut_image = self.to_resize(_im=cut_image, width=650)
+
+        if image_deal_mode == 1:
+            # 翻转
+            im_shadow = im_shadow.transpose(Image.FLIP_LEFT_RIGHT)
+            cut_image = cut_image.transpose(Image.FLIP_LEFT_RIGHT)
+
+        # 创建底层背景
+        image_bg = Image.new("RGB", (1600, 1600), (255, 255, 255))
+
+        image_bg_x, image_bg_y = image_bg.size
+        image_x, image_y = im_shadow.size
+
+        _x = int((image_bg_x - image_x) / 2)
+        _y = int((image_bg_y - image_y) / 2)
+
+        image_bg.paste(im_shadow, (_x, _y))
+        image_bg.paste(cut_image, (_x, _y), cut_image)
+
+        if is_logo:
+            logo_path = ""
+
+            if settings.PROJECT == "红蜻蜓":
+                logo_path = r"resources\LOGO\HQT\logo.png"
+            elif settings.PROJECT == "惠利玛":
+                if "小苏" in settings.Company:
+                    logo_path = r"resources\LOGO\xiaosushuoxie\logo.png"
+                elif "惠利玛" in settings.Company:
+                    logo_path = r"resources\LOGO\HLM\logo.png"
+                else:
+                    pass
+
+            if not logo_path:
+                logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
+            else:
+                if os.path.exists(logo_path):
+                    logo_im = Image.open(logo_path)
+                else:
+                    logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
+
+            image_bg.paste(logo_im, (0, 0), logo_im)
+
+        image_bg = image_bg.resize((out_pic_size, out_pic_size), Image.BICUBIC)
+
+        # image_bg.show()
+        image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+        return True
+
+    def increase_brightness(self, im, brightness_factor):
+        # 打开图片
+        # 将图片转换为RGB模式
+        # image = image.convert("RGBA")
+        # 获取图片的亮度值
+        enhancer = ImageEnhance.Brightness(im)
+        brightness = enhancer.enhance(brightness_factor)
+        return brightness
+
+    def clean_colors(self, img):
+        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+        return img
+
+    def levels_adjust(self, img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
+        # print(img)
+        # dim = 3的时候调节RGB三个分量, 0调节B,1调节G,2调节R
+        if Dim == 3:
+            mask_shadow = img < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img > Highlight
+            img[mask_Highlight] = Highlight
+        else:
+            mask_shadow = img[..., Dim] < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img[..., Dim] > Highlight
+            img[mask_Highlight] = Highlight
+
+        if Dim == 3:
+            Diff = Highlight - Shadow
+            rgbDiff = img - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img = data
+        else:
+            Diff = Highlight - Shadow
+            rgbDiff = img[..., Dim] - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img[..., Dim] = data
+        return img
+
+
+if __name__ == '__main__':
+    # 测试皮具主图生成
+    for jpg_name in os.listdir("image"):
+        if "png" in jpg_name:
+            continue
+        file_name = os.path.splitext(jpg_name)[0]
+        original_image_path = r"image\{}.jpg".format(file_name)
+        original_move_bg_image_path = r"image\{}.png".format(file_name)
+        out_path = r"out\{}.jpg".format(file_name)
+        is_image_deal_mode = 1
+        resize_mode = 1
+
+        print(original_image_path)
+        GeneratePicPiJu().run(image_path=original_image_path,
+                              cut_image_path=original_move_bg_image_path,
+                              out_path=out_path,
+                              image_deal_mode=is_image_deal_mode,
+                              # resize_mode=resize_mode,
+                              out_pic_size=1600,
+                              is_logo=False,
+                              max_box=None
+                              )

+ 9 - 8
python/service/grenerate_main_image_test.py

@@ -1,7 +1,7 @@
 import os
 import copy
 import time
-from image_deal_base_func import *
+from .image_deal_base_func import *
 from PIL import Image, ImageDraw
 from blend_modes import multiply
 import os
@@ -49,7 +49,7 @@ class GeneratePic(object):
         image_high = im_jpg.height
         print("图片高度:", image_high)
         # TODO 待移除
-        settings.app.processEvents()
+        # settings.app.processEvents()
         cv2_jpg = pil_to_cv2(im_jpg)
         # 返回线条图片,以及最低位置
         print("返回线条图片,以及最低位置")
@@ -59,9 +59,9 @@ class GeneratePic(object):
                                                             one_line_pos=(x1, x2),
                                                             line_color=(0, 0, 0),
                                                             line_thickness=20,
-                                                            app=settings.app)
+                                                            app=None)
         # TODO 待移除
-        settings.app.processEvents()
+        # settings.app.processEvents()
         print("66  制作蒙版")
         # 制作蒙版
         mask_line = cv2_to_pil(img_with_shifted_line)
@@ -78,14 +78,14 @@ class GeneratePic(object):
         # raise 11
 
         # TODO 待移除
-        settings.app.processEvents()
+        # settings.app.processEvents()
         # ====================生成新的图片
         print("84  生成新的图片")
         bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
         bg.paste(im_png, mask=im_png)
         bg.paste(im_jpg, mask=mask)  # 粘贴有阴影的地方
         # TODO 待移除
-        settings.app.processEvents()
+        # settings.app.processEvents()
         if self.is_test:
             _bg = bg.copy()
             draw = ImageDraw.Draw(_bg)
@@ -129,7 +129,7 @@ class GeneratePic(object):
         while k:
             # TODO 待移除
             print("循环识别:{}".format(k))
-            settings.app.processEvents()
+            # settings.app.processEvents()
             k -= 1
             Midtones += 0.1
             if Midtones > 1:
@@ -260,6 +260,7 @@ class GeneratePic(object):
     def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
             out_pic_size=1024, is_logo=True, out_process_path_1=None, out_process_path_2=None,
             resize_mode=None, max_box=None, logo_path="", **kwargs):  # im 为cv对象
+        print("****************************处理图像核心********************************\n")
         """
         image_path:原始图
         cut_image_path:抠图结果 与原始图尺寸相同
@@ -297,7 +298,7 @@ class GeneratePic(object):
 
         # =====抠图,形成新的阴影背景图=====
         # TODO 待移除
-        settings.app.processEvents()
+        # settings.app.processEvents()
 
         _new_im_shadow = Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
         _new_im_shadow.paste(im_shadow, mask=shadow_mask)  # 粘贴有阴影的地方

+ 5 - 3
python/service/image_pic_deal.py

@@ -1,16 +1,18 @@
 """
 """
 
+
 import cv2
 import numpy as np
 from PIL import Image, ImageDraw, ImageFont
-from module.view_control.auto_deal_pics.data import DataModeAutoDealPics
+from .data import DataModeAutoDealPics
 
 
 class OnePicDeal(object):
-    def __init__(self):
+    def __init__(self,token):
         # 数据模型
-        self.data_mode_auto_deal_pics = DataModeAutoDealPics()
+        self.token = token
+        self.data_mode_auto_deal_pics = DataModeAutoDealPics(token)
 
     def check_shoe_is_right(self, im=None, image_path=None):
         # 先进行API识别左右脚

+ 123 - 0
python/service/load_plugins.py

@@ -0,0 +1,123 @@
+import os
+import importlib.util
+import sys
+
+
+# import custom_plugins
+
+
+class LoadAllPlugins(object):
+    def __init__(self, windows):
+        self.windows = windows
+        self.ignore_files = ["get_remote_has256.py", "__init__.py"]
+        self.ignore_dirs = ["other_micropython_code", "__pycache__"]
+
+    def send_log(self, text):
+        if self.windows:
+            self.windows.send_log(text)
+
+    def load_plugins(self, plugin_dir):
+        """
+        递归加载插件目录下的所有包和模块。
+        """
+        plugin_modules = {}
+        # 获取插件目录的绝对路径
+        plugin_dir_abs = os.path.abspath(plugin_dir)
+        print("22 plugin_dir_abs", plugin_dir_abs)
+        # # 如果插件目录不在sys.path中,则添加它
+        if plugin_dir_abs not in sys.path:
+            sys.path.append(plugin_dir_abs)
+
+        def load_module_from_file(module_name, file_path):
+            try:
+                spec = importlib.util.spec_from_file_location(module_name, file_path)
+                module = importlib.util.module_from_spec(spec)
+                spec.loader.exec_module(module)
+                print(f"Loaded module Successful: {module_name}")
+                self.send_log(f"Loaded module Successful: {module_name}")
+                return module
+            except Exception as e:
+                print(f"Failed to load module {module_name}: {e}")
+                self.send_log(f"Failed to load module {module_name}: {e}")
+                return None
+
+        def traverse_directory(directory):
+            for root, dirs, files in os.walk(directory):
+                f = True
+                for i in self.ignore_dirs:
+                    if i in root:
+                        f = False
+                        break
+
+                if f is False:
+                    continue
+                # 确保当前目录被视为Python包
+                init_file = os.path.join(root, '__init__.py')
+                if not os.path.isfile(init_file):
+                    open(init_file, 'a').close()  # 创建空的 __init__.py 文件
+
+                # 加载当前目录下的所有 .py 文件(不包括 __init__.py)
+                for file in files:
+                    if file in self.ignore_files:
+                        continue
+                    if file.endswith('.py'):
+                        module_name = \
+                            os.path.relpath(os.path.join(root, file), plugin_dir).replace(os.sep, '.').rsplit('.', 1)[0]
+                        module = load_module_from_file(module_name, os.path.join(root, file))
+                        if module is not None:
+                            plugin_modules[module_name] = module
+
+        # 遍历插件目录,寻找所有的包和模块
+        traverse_directory(plugin_dir_abs)
+
+        return plugin_modules
+
+    def do_load(self, root):
+        plugin_b_dir = r"{}/custom_plugins".format(os.getcwd())
+        if plugin_b_dir not in sys.path:
+            sys.path.insert(0, plugin_b_dir)
+        plugins_dict = {"detail_template": {},
+                        "ampy_scribe": {},
+                        "generate_scribe": {},
+                        }
+
+        self.load_plugins(plugin_dir="{}\custom_plugins/plugins_mode".format(root))
+        plugins = self.load_plugins(plugin_dir="{}\custom_plugins/plugins".format(root))
+        for i, py_file in plugins.items():
+            print(i)
+            if hasattr(py_file, 'plugins_name'):
+                if py_file.plugins_name == "详情模板":
+                    company_name_list = py_file.company_name_list
+                    template_name = py_file.template_name
+                    # 一个模板文件可能适合多个不同的企业
+                    if "全部" in company_name_list:
+                        company_name_list = ["全部"]
+
+                    for company_name in company_name_list:
+                        if company_name not in plugins_dict["detail_template"]:
+                            plugins_dict["detail_template"][company_name] = {}
+                        plugins_dict["detail_template"][company_name][template_name] = py_file.DetailPicGet
+
+                if py_file.plugins_name == "AMPY插件":
+                    plugins_dict["ampy_scribe"]["AMPY插件"] = py_file.RemoteUpdate
+                # if py_file.plugins_name == "主图生成与上传":
+                #     plugins_dict["generate_scribe"]["主图生成与上传"] = py_file.AutoDealPics
+
+        return plugins_dict
+
+    def load(self):
+        print("Loading plugins...")
+        plugins_dict = {}
+        try:
+            plugins_dict = self.do_load(root=os.getcwd())
+        except BaseException as e:
+            print(e)
+            self.send_log("Loading plugins. error {}".format(e))
+        # print(plugins_dict)
+        # _class = plugins_dict["detail_template"]["小苏"]["xiaosushuoxie-1"]
+        # _class()
+        return plugins_dict
+
+
+if __name__ == '__main__':
+    LoadAllPlugins(None).load()

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.