瀏覽代碼

Merge branch 'dev-python'

rambo 8 月之前
父節點
當前提交
bead704aaf
共有 62 個文件被更改,包括 19957 次插入304 次删除
  1. 5 1
      python/.gitignore
  2. 112 40
      python/api.py
  3. 1 1
      python/config.ini
  4. 16 15
      python/middleware.py
  5. 19 0
      python/models.py
  6. 726 0
      python/service/AutoDealPics.py
  7. 1007 0
      python/service/auto_deal_pics/base_deal.py
  8. 28 0
      python/service/auto_deal_pics/data.py
  9. 84 0
      python/service/auto_deal_pics/detail_temp.py
  10. 648 0
      python/service/auto_deal_pics/main_control.py
  11. 77 0
      python/service/auto_deal_pics/queren_control.py
  12. 1005 0
      python/service/auto_deal_pics/run_main.py
  13. 473 0
      python/service/auto_deal_pics/upload_pic.py
  14. 8 8
      python/service/base.py
  15. 33 33
      python/service/base_deal.py
  16. 405 4
      python/service/data.py
  17. 296 0
      python/service/data_metaclass.py
  18. 6 6
      python/service/deal_cutout.py
  19. 51 56
      python/service/deal_image.py
  20. 5 6
      python/service/deal_one_image.py
  21. 177 0
      python/service/detail_func.py
  22. 84 0
      python/service/detail_temp.py
  23. 11 11
      python/service/excel_base_func.py
  24. 313 0
      python/service/generate_goods_art_no_table/module_generate_goods_art_no_table.py
  25. 340 0
      python/service/generate_goods_no_detail_pic/ceshi.py
  26. 332 0
      python/service/generate_goods_no_detail_pic/data.py
  27. 176 0
      python/service/generate_goods_no_detail_pic/detail_func.py
  28. 574 0
      python/service/generate_goods_no_detail_pic/detail_generate_base.py
  29. 206 0
      python/service/generate_goods_no_detail_pic/detail_hlm.py
  30. 221 0
      python/service/generate_goods_no_detail_pic/detail_hlm_2.py
  31. 228 0
      python/service/generate_goods_no_detail_pic/detail_hlm_3.py
  32. 325 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie.py
  33. 612 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie2.py
  34. 489 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie3.py
  35. 458 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie4.py
  36. 586 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie5.py
  37. 552 0
      python/service/generate_goods_no_detail_pic/detail_xiaosushuoxie6.py
  38. 206 0
      python/service/generate_goods_no_detail_pic/detial_hqt.py
  39. 1177 0
      python/service/generate_goods_no_detail_pic/goods_detail_pics.py
  40. 913 0
      python/service/generate_goods_no_detail_pic/module_generate_goods_no_detail_pics.py
  41. 570 0
      python/service/generate_goods_no_detail_pic/pic_deal.py
  42. 422 0
      python/service/generate_main_image/grenerate_main_image_test.py
  43. 216 0
      python/service/generate_main_image/image_deal_base_func.py
  44. 481 0
      python/service/generate_main_image/module_generate_main_image.py
  45. 9 8
      python/service/grenerate_main_image_test.py
  46. 5 3
      python/service/image_pic_deal.py
  47. 123 0
      python/service/load_plugins.py
  48. 512 0
      python/service/manual_image_matching/goods_art_image_list.py
  49. 332 0
      python/service/manual_image_matching/image_label.py
  50. 153 0
      python/service/manual_image_matching/m_image_matching_cotrol.py
  51. 42 0
      python/service/manual_image_matching/m_image_matching_data.py
  52. 1052 0
      python/service/match_and_cutout_mode_control/base_deal_image_v2.py
  53. 1219 0
      python/service/match_and_cutout_mode_control/module_matching_photos_v2.py
  54. 8 0
      python/service/matching_photos/data.py
  55. 847 0
      python/service/matching_photos/module_matching_photos.py
  56. 2 2
      python/service/module_generate_goods_art_no_table.py
  57. 436 0
      python/service/online_request/module_online_data.py
  58. 230 0
      python/service/remove_bg_pixian.py
  59. 119 96
      python/service/run_main.py
  60. 11 10
      python/service/upload_pic.py
  61. 80 4
      python/settings.py
  62. 103 0
      python/utils/utils_func.py

+ 5 - 1
python/.gitignore

@@ -5,4 +5,8 @@ venv/
 resources/
 custom_plugins/
 *.db
-*.pdf
+*.pdf
+output/*
+qt_test/*
+*.jpg
+*.png

+ 112 - 40
python/api.py

@@ -6,15 +6,21 @@ import json
 from logger import logger
 from serial.tools import list_ports
 from model import PhotoRecord
+import settings
+
 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
 from sqlalchemy import and_, asc, desc
-
+from functools import partial
 from service.deal_image import DealImage
 from databases import DeviceConfig, SqlQuery, CRUD, select
-
+from service.run_main import RunMain
+import importlib
+from service.auto_deal_pics.upload_pic import UploadPic
+# from service.AutoDealPics import AutoDealPics
 
 @app.get("/")
 async def index():
@@ -111,43 +117,107 @@ async def forwardRequest(request: HlmForwardRequest):
 
 
 @app.post("/handle_detail")
-async def handle_detail(request: Request):
-
-    image_dir = "{}/data".format(os.getcwd())
+async def handle_detail(request: Request, params: HandlerDetail):
+    goods_art_no = params.goods_art_no
+    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())
+    for itemImg in images:
+        if not os.path.exists(image_dir + "/" + os.path.basename(itemImg.image_path)):
+            shutil.copy(itemImg.image_path, image_dir)
+    check_path(image_dir)
     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/软件-生成详情错误'
-    # }
-
+    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": "0",
+        "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
+    print("config_data", config_data)
+    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("\\", "/")
+        return {
+            "code": 0,
+            "msg": "详情处理完成",
+            "data": {"out_put_dir": out_put_dir_path},
+        }
+    else:
+        return {
+            "code": 1,
+            "msg": "处理失败,请联系管理员",
+            "data": None}
 
 @app.post("/get_device_configs", description="获取可执行程序命令列表")
 def get_device_configs(params: ModelGetDeviceConfig):
@@ -315,5 +385,7 @@ 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}

+ 1 - 1
python/config.ini

@@ -13,6 +13,7 @@ debug=true
 env=dev
 # 线程数
 works=1
+project=惠利玛
 [log]
 # 日志相关
 log_file_name=app.log 
@@ -23,7 +24,6 @@ backup_counts=3
 # 地址
 hlm_host=https://dev2.pubdata.cn
 
-project=红蜻蜓
 
 
 

+ 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):

+ 19 - 0
python/models.py

@@ -55,3 +55,22 @@ 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 HandlerDetail(BaseModel):
+    """获取可执行程序命令列表"""
+
+    goods_art_no: 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地址路径")

+ 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)

+ 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"}

+ 405 - 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,401 @@ 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
+        )
+        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):
+        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))

+ 177 - 0
python/service/detail_func.py

@@ -0,0 +1,177 @@
+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
+        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()

+ 512 - 0
python/service/manual_image_matching/goods_art_image_list.py

@@ -0,0 +1,512 @@
+import os.path
+import random
+import shutil
+import time
+
+from utils.utils_func import get_images, move_elements_inplace, check_path, get_cutout_image_info
+
+
+class GoodsArtImageList():
+    # select_clearn_all_sign = Signal(str)
+
+    def __init__(self, goods_art_no_folder_path, parent=None, windows=None,
+                 label_color="green", is_goods_art_no_folder=True):
+        super().__init__(parent)
+        # self.ui = Ui_Form()
+        # self.ui.setupUi(self)
+        self.windows = windows
+
+        # parent: QWidget
+        self.parent = parent
+        self.has_selected = False
+        # self.ui.label_5.setStyleSheet("background-color: {};".format(label_color))
+
+        self.goods_art_no_folder_path = goods_art_no_folder_path
+        self.folder_name = os.path.split(goods_art_no_folder_path)[1]
+        self.is_goods_art_no_folder = is_goods_art_no_folder
+        if is_goods_art_no_folder:
+            self.original_path = "{}/原始图".format(goods_art_no_folder_path)
+            self.cut_out_path = "{}/已扣图".format(goods_art_no_folder_path)
+        else:
+            self.original_path = goods_art_no_folder_path
+            self.cut_out_path = ""
+            # self.ui.del_folder.hide()
+
+        self.drag_start_position = None
+        # self.ui.widget.setMinimumSize(300, 200)
+        self.image_data_list = []
+        self.init_data()
+        # self.show_list()
+        # self.init()
+        # self.show()
+
+    def init(self):
+        self.setAcceptDrops(True)
+        self.ui.widget.setAcceptDrops(True)
+        self.ui.widget.dropEvent = self.list_dropEvent
+        self.ui.widget.dragEnterEvent = self.list_dragEnterEvent
+        self.ui.widget.dragMoveEvent = self.list_dragMoveEvent
+        self.ui.del_folder.mousePressEvent = self.del_folder
+        self.ui.open_folder.mousePressEvent = self.open_folder
+
+    # 删除文件夹
+    # def del_folder(self, *args):
+    #     if not self.is_goods_art_no_folder:
+    #         return
+    #     a = QMessageBox.question(self, '确认', '是否删除该文件夹',
+    #                              QMessageBox.Yes | QMessageBox.No)
+    #     if a != QMessageBox.Yes:
+    #         return
+
+    #     try:
+    #         shutil.rmtree(self.goods_art_no_folder_path)
+    #     except BaseException as e:
+    #         print("删除文件夹失败", e)
+    #     self.hide()
+    #     self.deleteLater()
+
+    # 打开本地文件夹
+    def open_folder(self, *args):
+        if os.path.exists(self.original_path):
+            os.startfile(self.original_path)
+
+    def set_new_parent(self, parent):
+        self.parent = parent
+        self.setParent(parent)
+
+    # def resizeEvent(self, event: QResizeEvent):
+    #     # print(self.size())
+    #     self.show_list()
+    #     # 当窗口大小发生变化时,更新标签文本
+    #     # 调用父类的resizeEvent以确保其他必要的调整也被执行
+    #     super().resizeEvent(event)
+
+    # 刷新展示列表
+    # def show_list(self):
+    #     self.ui.label.setText("文件夹:{}".format(self.folder_name))
+    #     if not self.image_data_list:
+    #         self.ui.widget.setMinimumHeight(20)
+    #         self.ui.widget.setMaximumHeight(20)
+    #         return
+    #     x, y = 0, 5
+    #     total_len = len(self.image_data_list)
+    #     for index, image_label in enumerate(self.image_data_list):
+    #         image_label: ImageLabel
+    #         image_label.image_index = index
+    #         image_label.move(x, y)
+    #         image_label.show_text()
+    #         image_label.show()
+    #         x += image_label.width() + 5
+    #         if x + image_label.width() > self.ui.widget.width():
+    #             x = 0
+    #             if total_len != index + 1:
+    #                 y += image_label.height() + 5
+    #     self.ui.widget.setMaximumHeight(y + image_label.height())
+    #     self.ui.widget.setMinimumHeight(y + image_label.height())
+    #     self.setMaximumHeight(y + image_label.height() + self.ui.label.height())
+
+    #     # 刷新界面命令
+    #     QApplication.processEvents()
+
+    # 初始化数据
+    def init_data(self):
+        # for index, image_data in enumerate(get_images(self.original_path)):
+        #     image_label = ImageLabel(text="",
+        #                              image_index=index,
+        #                              image_path=image_data["file_path"],
+        #                              image_name=image_data["file_name"],
+        #                              root_path=self.original_path,
+        #                              is_goods_list=True,
+        #                              parent=self.ui.widget,
+        #                              windows=self,
+        #                              top_windows=self.windows
+        #                              )
+        #     self.reconnect_sign(image_label, self)
+        #     self.image_data_list.append(image_label)
+        pass
+            # # 刷新界面命令
+            # QApplication.processEvents()
+
+    # 重新与当前父组件绑定信号
+    def reconnect_sign(self, image_label, windows):
+        try:
+            image_label.select_clearn_all_sign.disconnect()
+            image_label.selected_sign.disconnect()
+            image_label.drop_sign.disconnect()
+            image_label.drop_from_sign.disconnect()
+            image_label.delete_image_sign.disconnect()
+        except:
+            pass  # 如果没有连接则忽略错误
+
+        image_label.select_clearn_all_sign.connect(windows.clearn_all_label_select)
+        image_label.selected_sign.connect(windows.set_selected)
+        image_label.drop_sign.connect(windows.deal_image_label_to)
+        image_label.drop_from_sign.connect(windows.deal_image_label_from)
+        image_label.delete_image_sign.connect(windows.delete_image_items)
+
+    def set_image_label_parent(self, image_label):
+        image_label.set_new_parent(self.ui.widget)
+
+    # 批量删除图片
+    def delete_image_items(self, image_index_list: list):
+        image_index_list.sort(reverse=True)
+        # for image_index in image_index_list:
+        #     image_label: ImageLabel
+        #     image_label = self.image_data_list.pop(image_index)
+        #     image_label.del_image()
+        #     image_label.deleteLater()
+        # self.show_list()
+
+    # 设置有选择
+    def set_selected(self):
+        self.has_selected = True
+        self.select_clearn_all_sign.emit(self.original_path)
+
+    # 清除所有已选
+    def clearn_all_label_select(self, flag=False):
+        _f = False
+        if flag:
+            _f = True
+        else:
+            if self.has_selected:
+                _f = True
+
+        if _f:
+            for image_label in self.image_data_list:
+                # image_label: ImageLabel
+                # if image_label.selected:
+                image_label.set_selected(False)
+
+    # 手动排序
+    def re_sort_by_manual(self):
+        # 重新排序
+        pass
+
+    # 粘贴到事件;处理同文件夹内容
+    def deal_image_label_to(self, data, is_real_same_folder=False):
+        is_same_folder = data["is_same_folder"]
+        print(data)
+        # 来自同文件夹
+        if is_same_folder:
+            target_index = data["to_image_index"]
+            indices = data["from_image_index_list"]
+            if target_index in indices:
+                return
+            move_elements_inplace(lst=self.image_data_list, indices=indices, target_index=target_index,
+                                  is_real_same_folder=is_real_same_folder)
+            # 刷新文件名称
+            self.rename_image(target=self)
+            self.show_list()
+
+    # 处理不同文件夹事件
+    def deal_image_label_from(self, data):
+        # 如果是来源于不同的文件夹,则需要处理这个。
+        """
+        从原列表进行剔除
+        添加到新列表,并插入到指定位置
+        """
+        is_same_folder = data["is_same_folder"]
+        if is_same_folder is False:
+            from_image_index_list = data["from_image_index_list"]
+            from_image_index_list: list
+            from_image_index_list.sort(reverse=True)
+            to_source = data["to_source"]
+            from_source = data["from_source"]
+            to_source_len_image = len(to_source.image_data_list) - 1
+            to_image_index = data["to_image_index"]
+            indices = []
+            # 从末尾进行剔除,并添加到另外一个列表
+            temp = []
+            for from_index in from_image_index_list:
+                image_label = from_source.image_data_list.pop(from_index)
+                temp.append(image_label)
+
+            # 倒序处理
+            for image_label in temp[::-1]:
+                to_source_len_image += 1
+                to_source.image_data_list.append(image_label)
+                to_source.set_image_label_parent(image_label)
+                to_source.set_image_label_windows(image_label, to_source)
+                to_source.reconnect_sign(image_label, to_source)  # 重新绑定信号
+
+                # 设定父类
+                image_label.windows = to_source
+                indices.append(to_source_len_image)
+            # 目标文件夹处理
+            move_elements_inplace(lst=to_source.image_data_list, indices=indices, target_index=to_image_index)
+            self.dealMoveImage(to_source=to_source)
+
+            to_source.show_list()
+            # 刷新来源文件夹
+            from_source.show_list()
+
+            # 全部重命名
+            self.rename_image(target=to_source)
+
+            # 取消已选数据
+            to_source.has_selected = True
+            from_source.has_selected = True
+
+            to_source.clearn_all_label_select(flag=True)
+            from_source.clearn_all_label_select(flag=True)
+
+    def set_image_label_windows(self, image_label, windows):
+        image_label.set_image_label_windows(windows)
+
+    def dealMoveImage_beifen(self, to_source):
+        '''
+        从其他文件夹复制移动图片
+         参数例 sources = ['D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/abc.jpg', D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/ddd.jpg]
+            target = 'D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图'
+        '''
+        target = to_source.original_path
+        targetParent, _ = os.path.split(target)
+        for imageLabel in to_source.image_data_list:
+            if imageLabel.root_path == target:
+                continue
+            path, sourceFile = os.path.split(imageLabel.image_path)
+            sourceFileName, file_extension = os.path.splitext(sourceFile)
+            parentPath, _ = os.path.split(path)
+            sourceCutPath = '{}/原始图_已抠图/{}.png'.format(parentPath, sourceFileName)
+            newFileName = str(time.time()) + str(random.randint(1000000, 9999999))
+            targetName = target + '/' + newFileName + file_extension
+
+            if os.path.exists(imageLabel.image_path):
+                shutil.move(imageLabel.image_path, targetName)
+                self.upImageLabel(item=imageLabel, image_name=newFileName, image_path=targetName)
+                if os.path.exists(sourceCutPath) and os.path.exists(
+                        "{}/原始图_已抠图".format(targetParent)) and os.path.isdir("{}/原始图_已抠图".format(targetParent)):
+                    cutTargetPath = '{}/原始图_已抠图/{}.png'.format(targetParent, newFileName)
+                    shutil.move(sourceCutPath, cutTargetPath)
+
+        self.rename_image(to_source)
+
+    def dealMoveImage(self, to_source):
+        '''
+        从其他文件夹复制移动图片
+         参数例 sources = ['D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/abc.jpg', D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/ddd.jpg]
+            target = 'D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图'
+        '''
+        """
+        步骤:
+        1、获取来源的图片与已扣图图片
+        2、随机进行命名,(包括已扣图)
+        3、移动
+        以上过程需要考虑是否存在对应路径或图片
+        """
+        # original_path 为原始图路径
+        target_original_path = to_source.original_path
+        # target_original_root_path 为目标的原始图路径
+        target_goods_art_no_root_path, _ = os.path.split(target_original_path)
+
+        # target_cutout_root_path 为目标的原始图_已扣图路径
+        target_cutout_root_path = None
+        if "原始图" == target_original_path[-3:]:
+            target_cutout_root_path = target_original_path[:-3] + "原始图_已抠图"
+            if not os.path.exists(target_cutout_root_path):
+                # 创建一个已扣图路径
+                check_path(target_cutout_root_path)
+
+        # 轮询移动每个文件
+        for imageLabel in to_source.image_data_list:
+            # 来源为当前原图片文件夹,则忽略
+            if imageLabel.root_path == target_original_path:
+                continue
+
+            image_info_data = get_cutout_image_info(imageLabel.image_path)
+            if image_info_data is None:
+                continue
+
+            # 创建一个随机名称
+            new_file_name = str(int(time.time()) + random.randint(10000, 59999))
+            target_image_path = "{}/{}{}".format(target_original_path, new_file_name,
+                                                 image_info_data["source_file_extension"])
+            # 移动原始图
+            shutil.move(image_info_data["source_image_path"], target_image_path)
+            self.upImageLabel(item=imageLabel, image_name=new_file_name, image_path=target_image_path)
+
+            # 移动已扣图图片
+            print(image_info_data)
+            if target_cutout_root_path is None:
+                # 如果不存在已扣图文件夹,但是图片又存在,则需要进行文件删除
+                if image_info_data["cutout_image_path"]:
+                    if os.path.exists(image_info_data["cutout_image_path"]):
+                        os.remove(image_info_data["cutout_image_path"])
+                continue
+
+            if image_info_data["cutout_image_path"] is not None:
+                # 移动原始图
+                target_cutout_image_path = "{}/{}{}".format(target_cutout_root_path, new_file_name,
+                                                            image_info_data["cutout_image_file_extension"])
+                print("target_cutout_image_path", target_cutout_image_path)
+
+                shutil.move(image_info_data["cutout_image_path"], target_cutout_image_path)
+
+        # self.rename_image(to_source)
+
+    def rename_image_beifen(self, target):
+        """
+         重新排序,重新命名图片
+        """
+        target: GoodsArtImageList
+
+        path, _ = os.path.split(target.original_path)
+        _, artno = os.path.split(path)
+
+        imageNames = []
+        for index, item in enumerate(target.image_data_list):
+            imageNames.append(item.image_name)
+            _, image_extend = os.path.splitext(item.image_path)
+            targetPath = "{}/copy_tmp({}){}".format(item.root_path, index, image_extend)
+            if os.path.exists(item.image_path):
+                shutil.move(item.image_path, targetPath)
+                imageName = 'copy_tmp({})'.format(index)
+                self.upImageLabel(item=item, image_name=imageName, image_path=targetPath)
+
+        for index, item in enumerate(target.image_data_list):
+            _, image_extend = os.path.splitext(item.image_path)
+            imageName = '{}({})'.format(artno, index + 1)
+            targetPath = "{}/{}{}".format(item.root_path, imageName, image_extend)
+            if os.path.exists(item.image_path):
+                shutil.move(item.image_path, targetPath)
+                self.upImageLabel(item=item, image_name=imageName, image_path=targetPath)
+
+        self.dealCutImage(target, imageNames)
+
+    def rename_image_by_prefix(self, target, new_file_prefix):
+        pass
+        # for image_label in target.image_data_list:
+        #     image_label: ImageLabel
+        #     if not os.path.exists(image_label.image_path):
+        #         continue
+
+        #     if os.path.isdir(image_label.image_path):
+        #         continue
+
+        #     # 原始图信息
+        #     original_path, original_file = os.path.split(image_label.image_path)
+        #     original_file_name, original_file_extension = os.path.splitext(original_file)
+        #     new_file_name = "{}({})".format(new_file_prefix, image_label.image_index + 11)
+        #     new_original_file_name = "{}/{}{}".format(original_path,
+        #                                               new_file_name,
+        #                                               original_file_extension
+        #                                               )
+        #     # 重命名
+        #     os.rename(image_label.image_path, new_original_file_name)
+        #     self.upImageLabel(item=image_label, image_name=new_file_name, image_path=new_original_file_name)
+
+        #     # 处理已扣图的重命名
+        #     if original_path[-3:] != "原始图":
+        #         continue
+        #     cutout_original_path = original_path[:-3] + "原始图_已抠图"
+        #     cutout_image_path = "{}/{}.png".format(cutout_original_path, original_file_name)
+        #     if not os.path.exists(cutout_image_path):
+        #         continue
+
+        #     new_cutout_file_name = "{}/{}.png".format(cutout_original_path,
+        #                                               new_file_name,
+        #                                               )
+        #     # 重命名
+        #     os.rename(cutout_image_path, new_cutout_file_name)
+
+    def rename_image(self, target):
+        """
+         重新排序,重新命名图片
+        """
+        """
+        步骤:
+        1、先统一修改一个随机名称str(random.randint(60000, 99999))
+        2、再还原为指定名称
+        以上已扣图需要同步修改
+        """
+
+        target: GoodsArtImageList
+        # original_path 为原始图路径
+        # print("398            rename_image          ")
+        new_file_prefix = str(random.randint(60000, 99999))
+        self.rename_image_by_prefix(target, new_file_prefix)
+        self.rename_image_by_prefix(target, target.folder_name)
+
+    def dealCutImage(self, target, imageNames):
+        path, _ = os.path.split(target.original_path)
+        _, artno = os.path.split(path)
+        cutout_path = '{}/原始图_已抠图'.format(path)
+        if not os.path.exists(cutout_path):
+            return None
+
+        cutImages = self.getDirImageList(cutout_path)
+        for index, item in enumerate(cutImages):
+            if item['image_name'] not in imageNames:
+                shutil.move(item['image_path'], '{}/原始图_已抠图/remove{}.png'.format(path, index))
+
+        for index, item in enumerate(imageNames):
+            if os.path.exists('{}/原始图_已抠图/{}.png'.format(path, item)):
+                shutil.move('{}/原始图_已抠图/{}.png'.format(path, item),
+                            '{}/原始图_已抠图/copy_tmp({}).png'.format(path, index))
+                imageNames[index] = 'copy_tmp({})'.format(index)
+
+        for index, item in enumerate(imageNames):
+            if os.path.exists('{}/原始图_已抠图/{}.png'.format(path, item)):
+                shutil.move('{}/原始图_已抠图/{}.png'.format(path, item),
+                            '{}/原始图_已抠图/{}({}).png'.format(path, artno, index + 1))
+
+    def upImageLabel(self, item, image_name, image_path):
+        item.text = image_name
+        item.image_name = image_name
+        item.image_path = image_path
+        item.root_path, _ = os.path.split(image_path)
+        item.show_text()
+
+    def getDirImageList(self, path):
+        list = os.listdir(path)
+        result = []
+        for val in list:
+            name, _ = os.path.splitext(val)
+            result.append({
+                'image_path': '{}/{}'.format(path, val),
+                'image_name': name
+            })
+        return result
+        # print(item.image_path)
+
+    # 有且,只有目标粘贴位置才会触发该方法;粘贴到空白位置
+    def list_dropEvent(self, event):
+        # mime = event.mimeData()
+        pass
+        # if mime.hasFormat('application/x-draggable-labels'):
+        #     return_data = {"is_same_folder": True,
+        #                    "from_image_index_list": [],
+        #                    "from_source": "",
+        #                    "to_image_index": 0,
+        #                    "to_source": "",
+        #                    }
+        #     byte_array = mime.data('application/x-draggable-labels')
+        #     stream = QDataStream(byte_array, QIODevice.ReadOnly)
+        #     while not stream.atEnd():
+        #         image_index = stream.readInt32()
+        #         return_data["from_image_index_list"].append(image_index)
+
+        #     from_source = event.source()
+        #     return_data["from_source"] = from_source.windows
+        #     return_data["to_source"] = self
+        #     return_data["to_image_index"] = len(self.image_data_list)  # 添加到末尾
+
+        #     if from_source.windows == self:
+        #         return_data["is_same_folder"] = True
+        #         print("粘贴到末尾,同文件夹")
+        #     else:
+        #         return_data["is_same_folder"] = False
+        #         print("粘贴到末尾,不同文件夹")
+
+        #     if return_data["is_same_folder"] is False:
+        #         # 不同文件夹处理,处理发送到来源文件夹的操作
+        #         self.deal_image_label_from(return_data)
+        #     else:
+        #         # 同文件夹处理,发送粘贴的信号
+        #         self.deal_image_label_to(return_data, is_real_same_folder=True)
+
+    def list_dragEnterEvent(self, event):
+        event.acceptProposedAction()
+
+    def list_dragMoveEvent(self, event):
+        event.acceptProposedAction()

+ 332 - 0
python/service/manual_image_matching/image_label.py

@@ -0,0 +1,332 @@
+import json
+import os.path
+
+from import_qt_mode import *
+from UI.manual_image_matching.image_items import Ui_Form as Ui_Image_Items
+from PIL import Image
+import rawpy, io
+
+
+class ImageLabel(QLabel, Ui_Image_Items):
+    drop_sign = Signal(dict)  # 粘贴的位置
+    drop_from_sign = Signal(dict)  # 粘贴来源的位置
+    select_clearn_all_sign = Signal()
+    selected_sign = Signal()  # 有选中
+    delete_image_sign = Signal(list) # 删除通知
+
+    def __init__(self, text, image_index, image_path, image_name, root_path, is_goods_list, parent=None, windows=None,
+                 top_windows=None):
+        super().__init__(parent)
+        self.setupUi(self)
+        # self.setFixedSize(150, 160)
+        # self.ui_image.setFixedSize(150, 150)
+        self.windows = windows
+        self.top_windows = top_windows
+        self.parent = parent
+        self.text = image_name
+        self.image_index = image_index
+        self.image_path = image_path
+        self.image_name = image_name
+        self.root_path = root_path
+        self.ui_text.setText(self.text)
+
+        # 是图片列表还是普通列表
+        self.is_goods_list = is_goods_list
+        self.icon = None
+        self.set_label_image()
+        self.setMouseTracking(True)
+        self.drag_start_position = None
+        self.selected = False
+        self.setAcceptDrops(True)
+
+    def set_image_label_windows(self,windows):
+        self.windows = windows
+
+    def show_text(self):
+        self.ui_text.setText(self.text)
+
+    def crop_to_square(self, img):
+        # 打开图片
+        width, height = img.size  # 获取原始图片的宽度和高度
+        if width == height:
+            return img
+        if width > height:
+            _x = int((width - height) / 2)
+            img = img.crop((_x, 0, width - _x, height))
+        else:
+            _y = int((height - width) / 2)
+            img = img.crop((0, _y, width, height - _y))
+        return img
+
+    def set_new_parent(self, parent):
+        self.parent = parent
+        self.setParent(parent)
+
+    def show_big_image(self):
+        if self.top_windows is not None:
+            if self.icon:
+                self.top_windows.show_big_image(self.icon)
+
+    def set_label_image2(self):
+        try:
+            w, h = self.ui_image.width() * 2, self.ui_image.height() * 2
+            if "CR2" == self.image_path[-3:]:
+                _img_raw = Image.open(self.image_path)
+            else:
+                if "jpg" == self.image_path[-3:] or "png" == self.image_path[-3:]:
+                    _img_raw = Image.open(self.image_path)
+                else:
+                    with rawpy.imread(self.image_path) as raw:
+                        raw_image = raw.postprocess()
+                    _img_raw = Image.fromarray(raw_image)
+
+            size = (w, int(_img_raw.height * w / _img_raw.width))
+            _img_raw = _img_raw.resize(size)
+            _img_raw = _img_raw.convert("RGB")
+
+            bytes_io = io.BytesIO()
+            _img_raw.save(bytes_io, "JPEG")
+
+            icon = QPixmap()
+            icon.loadFromData(bytes_io.getvalue())
+            self.icon = icon
+            icon = icon.scaled(
+                self.ui_image.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui_image.setPixmap(icon)
+        except:
+            pass
+
+    def set_label_image(self):
+        try:
+            w, h = self.ui_image.width() * 2, self.ui_image.height() * 2
+            if "CR2" == self.image_path[-3:]:
+                _img_raw = Image.open(self.image_path)
+            else:
+                if "jpg" == self.image_path[-3:] or "png" == self.image_path[-3:]:
+                    _img_raw = Image.open(self.image_path)
+                else:
+                    with rawpy.imread(self.image_path) as raw:
+                        raw_image = raw.postprocess()
+                    _img_raw = Image.fromarray(raw_image)
+
+            # 取缩略图
+            _img_raw.thumbnail((w, int(_img_raw.height * w / _img_raw.width)))
+
+            size = (w, int(_img_raw.height * w / _img_raw.width))
+            _img_raw = _img_raw.resize(size)
+            _img_raw = _img_raw.convert("RGB")
+
+            bytes_io = io.BytesIO()
+            _img_raw.save(bytes_io, "JPEG")
+
+            icon = QPixmap()
+            icon.loadFromData(bytes_io.getvalue())
+            self.icon = icon
+            icon = icon.scaled(
+                self.ui_image.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui_image.setPixmap(icon)
+        except:
+            pass
+
+    def get_image_orientation(self, img):
+        # 获取EXIF数据
+        exif = img._getexif()
+        if exif is not None:
+            # EXIF标签274对应的是Orientation
+            orientation = exif.get(0x0112)
+            if orientation == 2:
+                # 水平翻转
+                img = img.transpose(Image.FLIP_LEFT_RIGHT)
+            elif orientation == 3:
+                # 旋转180度
+                img = img.rotate(180, expand=True)
+            elif orientation == 4:
+                # 垂直翻转
+                img = img.transpose(Image.FLIP_TOP_BOTTOM)
+            elif orientation == 5:
+                # 水平翻转后顺时针旋转90度
+                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
+            elif orientation == 6:
+                # 顺时针旋转90度
+                img = img.transpose(Image.ROTATE_270)
+            elif orientation == 7:
+                # 水平翻转后逆时针旋转90度
+                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
+            elif orientation == 8:
+                # 逆时针旋转90度
+                img = img.transpose(Image.ROTATE_90)
+            else:
+                pass
+
+        return img
+
+    def set_selected(self, selected):
+        self.selected = selected
+        self.setStyleSheet("background-color: yellow;" if selected else "background-color: white;")
+
+    def mouseMoveEvent(self, event):
+        if not (event.buttons() & Qt.LeftButton) or self.drag_start_position is None:
+            return
+
+        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
+            return
+
+        print("Starting drag...{}".format(self.image_name))  # 调试信息
+
+        # 创建拖拽任务
+        drag = QDrag(self)
+        mime_data = QMimeData()
+
+        # 收集所有选中的控件的数据
+        data = QByteArray()
+        stream = QDataStream(data, QIODevice.WriteOnly)
+
+        # 写入图片索引
+        for widget in self.parent.findChildren(ImageLabel):
+            if widget.selected:
+                if widget != self:
+                    stream.writeInt32(widget.image_index)
+
+        # 设置当前为选中
+        self.set_selected(True)
+
+        stream.writeInt32(self.image_index)
+
+        mime_data.setData('application/x-draggable-labels', data)
+        drag.setMimeData(mime_data)
+
+        # 可选:设置一个图标或快照来表示正在拖拽的对象
+        pixmap = QPixmap(self.size())
+        self.render(pixmap)
+        drag.setPixmap(pixmap)
+
+        # 设置热点为鼠标点击位置相对窗口的位置
+        hot_spot = event.position().toPoint()
+        drag.setHotSpot(hot_spot)
+
+        # 开始拖拽操作
+        result = drag.exec(Qt.MoveAction)
+        if result == Qt.MoveAction:
+            self.setParent(None)  # 如果成功移动,则从原位置移除
+            self.hide()
+
+        self.drag_start_position = None
+        print("end drag...{}".format(self.image_name))  # 调试信息
+
+    def mouseReleaseEvent(self, event):
+        super().mouseReleaseEvent(event)
+        # print("Mouse released.")  # 调试信息
+        self.drag_start_position = None
+
+    # 清除所有已选
+    def clear_all(self):
+        self.select_clearn_all_sign.emit()
+
+    def mousePressEvent(self, event):
+        if event.button() == Qt.LeftButton:
+            self.show_big_image()
+            self.drag_start_position = event.pos()
+            self.selected_sign.emit()  # 发送单击事件给父窗口
+            if event.modifiers() & Qt.ControlModifier:
+                # 如果按下了 Ctrl 键,则进行复选
+                self.set_selected(not self.selected)
+            else:
+                self.clear_all()
+                self.set_selected(not self.selected)
+
+    def dragEnterEvent(self, event):
+        event.acceptProposedAction()
+
+    def dragMoveEvent(self, event):
+        event.acceptProposedAction()
+
+    # 粘贴来源的位置,发送信号
+    def drop_from_event(self, return_data):
+        self.drop_from_sign.emit(return_data)
+
+    def dropEvent(self, event):
+        mime = event.mimeData()
+        if mime.hasFormat('application/x-draggable-labels'):
+
+            return_data = {"is_same_folder": True,
+                           "from_image_index_list": [],
+                           "from_source": "",
+                           "to_image_index": 0,
+                           "to_source": "",
+                           }
+            byte_array = mime.data('application/x-draggable-labels')
+            stream = QDataStream(byte_array, QIODevice.ReadOnly)
+            while not stream.atEnd():
+                image_index = stream.readInt32()
+                return_data["from_image_index_list"].append(image_index)
+
+            from_source = event.source()
+            return_data["from_source"] = from_source.windows
+            return_data["to_source"] = self.windows
+            return_data["to_image_index"] = self.image_index
+
+            if from_source.windows == self.windows:
+                return_data["is_same_folder"] = True
+                print("同文件夹")
+            else:
+                return_data["is_same_folder"] = False
+                print("不同文件夹")
+
+            if return_data["is_same_folder"] is False:
+                # 不同文件夹处理,处理发送到来源文件夹的操作
+                from_source.drop_from_event(return_data)
+            else:
+                # 同文件夹处理,发送粘贴的信号
+                self.drop_sign.emit(return_data)
+
+    def contextMenuEvent(self, event):
+        # 创建一个QMenu对象
+        menu = QMenu(self)
+
+        # 添加菜单项
+        delete_action = menu.addAction("删除图片")
+
+        # 连接菜单项到槽函数
+        action = menu.exec(self.mapToGlobal(event.pos()))
+        if action == delete_action:
+            self.delete_selected_item()
+
+    def delete_selected_item(self):
+        # 获取当前选中的项并删除它
+        print("delete_selected_item")
+        image_index_list = [self.image_index]
+        # 写入图片索引
+        for widget in self.parent.findChildren(ImageLabel):
+            if widget.selected:
+                if widget != self:
+                    image_index_list.append(widget.image_index)
+
+        if image_index_list:
+            self.delete_image_sign.emit(image_index_list)
+
+    def del_image(self):
+        """
+        删除图片
+        """
+        parentPath, _ = os.path.split(self.root_path)
+        cutImagePath = "{}/原始图_已抠图/{}.png".format(parentPath, self.image_name)
+        f = False
+        try:
+            if os.path.exists(self.image_path):
+                os.remove(self.image_path)
+                f = True
+
+            if os.path.exists(cutImagePath):
+                os.remove(cutImagePath)
+                f = True
+
+        except FileNotFoundError:
+            print(f"删除文件失败.")
+        except Exception as e:
+            print(f"发生错误: {e}")
+
+        if f:
+            self.hide()
+            del self

+ 153 - 0
python/service/manual_image_matching/m_image_matching_cotrol.py

@@ -0,0 +1,153 @@
+import os
+from import_qt_mode import *
+from UI.manual_image_matching.manual_image_matching_main import Ui_Form
+from module.view_control.manual_image_matching.m_image_matching_data import MatchingData
+from module.view_control.manual_image_matching.goods_art_image_list import GoodsArtImageList
+from module.view_control.MineQWidget import MineQWidget, LoadingMask
+
+
+class MainMatchingWindow(MineQWidget, QWidget):
+    mask_load_sign = Signal(bool)
+
+    def __init__(self, root_path=None):
+        super().__init__()
+        self.ui = Ui_Form()
+        self.ui.setupUi(self)
+        # 安装事件过滤器
+        self.installEventFilter(self)
+        self.ui.splitter.setSizes([900, 450])
+
+        # 移除 Qt.WindowStaysOnTopHint 标志以取消置顶
+        self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
+        # 默认最大化显示窗口
+        self.showMaximized()
+
+        self.matching_data = MatchingData()
+        if not root_path:
+            # root_path = r"D:\phpstudy_pro\WWW\auto_photo\output\2024-11-18"
+            root_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-12-20-2 - 副本"
+
+        self.root_path = root_path
+        print(self.size())
+        # 加载图片
+        self.state = 0
+        self.__mask = LoadingMask(self)
+        self.mask_load_sign.connect(self.mask_load)
+        self.installEventFilter(self.__mask)
+        self.__mask.is_end_sign.connect(self.hide_mask)
+
+        QTimer.singleShot(1000, self.load_images)
+        self.ui.select_path.clicked.connect(self.selectPath)
+        self.show()
+
+    def mask_load(self, flag):
+        print("mask_load")
+        if flag:
+            self.__mask.show()
+        else:
+            self.__mask.close()
+
+    def load_images(self):
+        self.show_all_goods_list(self.root_path)
+
+    def show_all_goods_list(self, root_path):
+        # for i in self.ui.action_widget_list.findChildren(GoodsArtImageList):
+        #     i.deleteLater()
+
+
+
+        self.mask_load_sign.emit(True)
+        for data in self.matching_data.get_all_folder(root_path):
+            # folder_name = data["folder_name"]
+            # original_path = data["original_path"]
+            # cut_out_path = data["cut_out_path"]
+            goods_art_image_list = GoodsArtImageList(goods_art_no_folder_path=data["goods_art_no_folder_path"],
+                                                     parent=self.ui.action_widget_list,
+                                                     windows=self,
+                                                     label_color = data["label_color"],
+                                                     )
+            self.ui.verticalLayout_4.insertWidget(self.ui.verticalLayout_4.count() - 1, goods_art_image_list)
+            goods_art_image_list.select_clearn_all_sign.connect(self.clearn_all_label_select)
+
+        self.mask_load_sign.emit(False)
+
+    # 清除所有已选
+    def clearn_all_label_select(self, original_path):
+        for goods_art_image_list_item in self.ui.action_widget_list.findChildren(GoodsArtImageList):
+            goods_art_image_list_item: GoodsArtImageList
+            if goods_art_image_list_item.original_path != original_path:
+                if goods_art_image_list_item.has_selected:
+                    goods_art_image_list_item.clearn_all_label_select()
+                    goods_art_image_list_item.has_selected = False
+
+        for goods_art_image_list_item in self.ui.action_widget_list_2.findChildren(GoodsArtImageList):
+            goods_art_image_list_item: GoodsArtImageList
+            if goods_art_image_list_item.original_path != original_path:
+                if goods_art_image_list_item.has_selected:
+                    goods_art_image_list_item.clearn_all_label_select()
+                    goods_art_image_list_item.has_selected = False
+
+    def selectPath(self):
+        # a = QMessageBox.question(
+        #     self,
+        #     "继续",
+        #     "如选择的文件夹图片很多,则此处会非常耗时",
+        #     QMessageBox.Yes | QMessageBox.No,
+        # )
+        # if a != QMessageBox.Yes:
+        #     return
+
+        path = QFileDialog.getExistingDirectory(self, "选择文件夹", "")
+        if not path:
+            return
+        if os.path.isfile(path):
+            return
+
+        _, folder_name = os.path.split(path)
+
+        for widget in self.ui.action_widget_list_2.findChildren(GoodsArtImageList):
+            widget.deleteLater()
+        self.mask_load_sign.emit(True)
+        goods_art_image_list = GoodsArtImageList(goods_art_no_folder_path=path,
+                                                 parent=self.ui.action_widget_list_2,
+                                                 is_goods_art_no_folder=False,
+                                                 )
+        goods_art_image_list.select_clearn_all_sign.connect(self.clearn_all_label_select)
+        self.ui.verticalLayout_5.insertWidget(self.ui.verticalLayout_5.count() - 1, goods_art_image_list)
+        self.mask_load_sign.emit(False)
+
+    def show_big_image(self, icon):
+        icon = icon.scaled(self.ui.show_big_image.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
+        self.ui.show_big_image.setPixmap(icon)
+
+    def eventFilter(self, obj, event):
+        if obj == self and event.type() == QEvent.WindowStateChange:
+            if self.isMaximized():
+                print("检测到窗口最大化")
+                QTimer.singleShot(1000, self.show_again)
+        return super().eventFilter(obj, event)
+
+    def show_again(self, *args):
+        print("检测到窗口最大化2")
+        print(self.size())
+        self.updateGeometry()
+        # self.resizeEvent(QResizeEvent(self.size(), QSize()))
+        # self.adjustSize()
+        self.ui.action_widget_list.updateGeometry()
+        print(self.size())
+
+        for i in self.ui.action_widget_list.findChildren(GoodsArtImageList):
+            i.repaint()
+        for i in self.ui.action_widget_list_2.findChildren(GoodsArtImageList):
+            i.repaint()
+
+    def resizeEvent(self, event):
+        super().resizeEvent(event)
+
+
+if __name__ == '__main__':
+    app = QApplication([])
+    window = MainMatchingWindow()
+    window.resize(1000, 800)
+    window.show()
+    app.exec()

+ 42 - 0
python/service/manual_image_matching/m_image_matching_data.py

@@ -0,0 +1,42 @@
+import os
+
+from utils.utils_func import list_dir
+
+
+class MatchingData(object):
+
+    def __init__(self):
+        pass
+
+    def get_all_folder(self, root_path):
+        folder_list = []
+        last_goods_no = ""
+        last_label_color = "green"
+        if os.path.exists(root_path):
+            all_folder = list_dir(root_path)
+            for folder_name in all_folder:
+                # original_path = "{}/{}/800x800".format(root_path, folder_name)
+                original_path = "{}/{}/原始图".format(root_path, folder_name)
+                cut_out_path = "{}/{}/原始图_已抠图".format(root_path, folder_name)
+                if os.path.exists(original_path) and os.path.exists(cut_out_path):
+                    if folder_name[:-1] == last_goods_no:
+                        label_color = last_label_color
+                    else:
+                        if last_label_color == "green":
+                            last_label_color = "blue"
+                        else:
+                            last_label_color = "green"
+                        last_goods_no = folder_name[:-1]
+                        label_color = last_label_color
+
+                    folder_list.append(
+                        {"root_path": root_path,
+                         "goods_art_no_folder_path": "{}/{}".format(root_path, folder_name),
+                         "original_path": original_path,
+                         "folder_name": folder_name,
+                         "cut_out_path": cut_out_path,
+                         "label_color": label_color,
+                         }
+                    )
+
+        return folder_list

+ 1052 - 0
python/service/match_and_cutout_mode_control/base_deal_image_v2.py

@@ -0,0 +1,1052 @@
+import hashlib
+import json
+import os
+import shutil
+import time
+
+# from multiprocessing import Process, Queue
+import exifread
+import requests
+
+import settings
+from ..image_pic_deal import OnePicDeal
+from ..generate_goods_art_no_table.module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+# from module.view_control.generate_main_image.module_generate_main_image import GeneratePicPiJu, GeneratePic
+from ..generate_main_image.grenerate_main_image_test import GeneratePic
+from ..matching_photos.data import DataModeMatchPhoto
+from collections import defaultdict
+from threading import Lock
+from ..remove_bg_ali import RemoveBgALi
+from PIL import Image
+from ..pic_deal import Picture
+from natsort import ns, natsorted
+from ..deal_cutout import DealCutout
+from ..data import DataModeAutoDealPics
+
+# from import_qt_mode import *
+
+_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):
+        # 对所有缺失已抠图的进行抠图处理
+        print("1windows.state", windows.state)
+        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,
+                              )
+        print("2windows.state", windows.state)
+        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:
+                    print("54  windows.state   不为1,", windows.state)
+                    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):
+        print(
+            "**********************check_folders_image_amount*****************************"
+        )
+        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):
+        # 计算单个文件夹原图数量
+        print(
+            "**********************check_one_folder_image_amount*****************************"
+        )
+        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
+                )
+
+            # todo 主图生成处理,使用进程处理耗性能操作
+            # 创建输入和输出队列
+
+            # input_queue = Queue()
+            # output_queue = Queue()
+
+            # kwargs = {"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,
+            #           "input_queue": input_queue,
+            #           "output_queue": output_queue,
+            #           }
+            #
+            # p = Process(target=generate_pic.run, kwargs=kwargs)
+            # # 启动子进程
+            # p.start()
+            # # 等待子进程结束
+            # p.join()
+            #
+            # if output_queue.empty():
+            #     is_successful = False
+            # else:
+            #     print("output_queue.get():", output_queue.get())
+
+            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 calculate_image_md5(self, filepath):
+        # image = Image.open(filepath)
+        # image_data = image.getdata()
+        # md5hash = hashlib.md5()
+        # md5hash.update(image_data)
+        # md5_value = md5hash.hexdigest()
+        # image.close()
+        # return md5_value
+        md5_hash = hashlib.md5()
+        with Image.open(filepath) as image:
+            image_data = image.tobytes()
+            md5_hash.update(image_data)
+        return md5_hash.hexdigest()
+
+    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()
+                            im = remove_pic_ins.get_image_cut(file_path=original_image_path,
+                                                              out_file_path=original_move_bg_image_path)
+                            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 + "/历史"):
+            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))
+                    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("没有任何匹配的图片~")
+            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))
+            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.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)

+ 1219 - 0
python/service/match_and_cutout_mode_control/module_matching_photos_v2.py

@@ -0,0 +1,1219 @@
+# from UI.matching_photos.matching_photos import Ui_Form as matching_photos_Ui_Form
+import json
+import os
+
+from UI.matching_photos.matching_and_cutout_photos import (
+    Ui_Form as matching_photos_Ui_Form,
+)
+
+from import_qt_mode import *
+
+import settings
+from collections import defaultdict
+
+from module.view_control.match_and_cutout_mode_control.base_deal_image_v2 import BaseDealImage
+from module.view_control.generate_goods_art_no_table.module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+from module.view_control.matching_photos.data import DataModeMatchPhoto
+from module.view_control.manual_image_matching.m_image_matching_cotrol import MainMatchingWindow
+
+from PySide6.QtGui import QPixmap
+from PIL import Image
+import io
+from functools import partial
+import threading
+import time
+from module.base_mode.pic_deal import PicDeal
+from module.base_mode.base import *
+from module.view_control.MineQWidget import MineQWidget, DialogShow, WorkerOneThread
+from threading import Event
+from UI.matching_photos.temp_item import Ui_Form as UI_temp_item
+from concurrent.futures import ThreadPoolExecutor
+import concurrent.futures
+
+"""
+照片自动货号匹配 将图片放置在指定文件夹下,并自动对应不同的货号进行整理
+"""
+
+
+class Communicate(QObject):
+    # 定义一个信号用于请求显示对话框
+    show_dialog_signal = Signal()
+    # 定义一个信号用于返回对话框的结果
+    dialog_result_signal = Signal(str)
+
+
+class MatchingPhotos(MineQWidget):
+    progress_sign = Signal(dict)
+    info_sign = Signal(str)
+    text_show = Signal(str)
+    show_dialog_sign = Signal(dict)
+    change_state_sign = Signal(int)
+
+    def __init__(self):
+        super().__init__()
+        self.image_dir = settings.PhotoImageDir
+        self.ui = matching_photos_Ui_Form()
+        self.ui.setupUi(self)
+        # 0禁用  1进行中  2已结束
+        self.state = 2
+        self.event = Event()
+        self.goods_images_count_dict = defaultdict(int)
+        self.data_match_photo = DataModeMatchPhoto()
+        self.excel_path = ""
+        # 上次使用的详情模板
+        self.last_temp = settings.MATCHING_LAST_TEMP
+        # 指定单独处理的颜色文件夹
+        self.special_goods_art_no_folder = []
+
+        self.communicator = Communicate()
+        self.dialog_result = ""
+        self.communicator.dialog_result_signal.connect(self.on_dialog_result)
+        self.event_dialog = Event()
+        self.show_dialog_sign.connect(self.show_dialog)
+
+        # 改变状态
+        self.change_state_sign.connect(self.set_state)
+
+        self.init()
+        self.show()
+
+    def init(self):
+        # 文本
+        self.ui.textBrowser.clear()
+        text = """请将capture One 加工后的图片放在指定目录下(目录为当前文件夹下的{}/data文件夹),务必确保导出的图片有拍摄时间信息。""".format(os.getcwd())
+        self.ui.textBrowser.append(text)
+        self.ui.tabWidget.setCurrentIndex(0)
+        # 页面视图1
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.text_show.connect(self.append_text_to_browser)
+        self.show_img_dir()
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.setMaximum(100)
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.hide()
+        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']))
+        self.ui.check_is_pass.setChecked(bool(settings.cutimage_dict['is_filter']))
+        print(settings.cutimage_dict)
+        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.ui.is_do_other.toggled.connect(self.change_do_special_treatment)
+        self.change_do_special_treatment()
+
+        self.progress_sign.connect(self.show_progress)
+        self.ui.run.clicked.connect(self.run_image_cut)  # 抠图与主图加工
+        self.ui.run_2.clicked.connect(self.run_matching)  # 匹配图片
+        self.ui.back.clicked.connect(self.back)
+        self.ui.stop.clicked.connect(self.to_stop)
+
+        # 页面视图2
+        self.ui.back.hide()
+        self.set_logo_selected()
+
+        # 手动匹配图片
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic_mousePressEvent
+
+    # 暂停
+    def to_stop(self, *args):
+        if self.state == 1:
+            self.set_state(state_value=99)
+
+    # 切换是否特殊处理的开关
+    def change_do_special_treatment(self, *args):
+        if self.ui.is_do_other.isChecked():
+            self.ui.show_deal_condition.show()
+        else:
+            self.ui.show_deal_condition.hide()
+
+    # 设置选择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 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 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
+
+        image_dir = self.ui.path_name.text()
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.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),
+            'is_filter': str(is_filter),
+            '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['is_filter'] = str(is_filter)
+        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["is_filter"] = is_filter
+        config_data["resize_image_view"] = resize_image_view
+        config_data["cutout_mode"] = cutout_mode
+        config_data["logo_path"] = logo_path
+        return config_data
+
+    @Slot()
+    def on_stop_clicked(self):
+        self.event.set()
+        pass
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
+        print(folder)
+        settings.set_config(data_dict={"photo_image_dir": folder})
+
+        # 清空已特殊选择等处理
+        self.ui.is_do_other.setChecked(False)
+
+        self.show_img_dir(image_dir=folder)
+
+    def show_img_dir(self, image_dir=None):
+        if image_dir:
+            self.image_dir = image_dir
+        else:
+            self.image_dir = settings.PhotoImageDir
+
+        if self.image_dir:
+            self.ui.path_name.show()
+            self.ui.path_name.setText(self.image_dir)
+        else:
+            self.ui.path_name.hide()
+
+    def show_progress(self, data):
+        progress_bar_value = data["progress_bar_value"]
+        self.ui.progressBar.setValue(progress_bar_value)
+
+    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, 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.back.hide()
+            self.ui.stop.show()
+            self.ui.stop.setEnabled(True)
+            self.ui.textBrowser_4.clear()
+            self.ui.textBrowser_4.show()
+            self.event.clear()
+
+        if self.state == 2:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.show()
+            self.ui.stop.hide()
+
+        if self.state == 99:
+            self.ui.stop.setEnabled(False)
+        print("设置state:{}".format(state_value))
+
+    def back(self):
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.ui.back.hide()
+        self.ui.run.show()
+        self.ui.stop.hide()
+        self.ui.textBrowser_4.clear()
+        self.ui.textBrowser_4.hide()
+
+    def run_matching(self, *args):
+        if settings.PROJECT == "红蜻蜓":
+            if not settings.IsLogin:
+                a = QMessageBox.question(
+                    self,
+                    "确认",
+                    "请先进行登录红蜻蜓账号",
+                    QMessageBox.Yes | QMessageBox.No,
+                )
+                return
+
+        self.set_state(state_value=1)
+        self.goods_images_count_dict = {}
+        self.goods_images_count_dict = defaultdict(int)
+        self.do_thread_run(func=self.run_by_thread, call_back=self.matchingCallBack, time_out=60)
+
+        # self.t = threading.Thread(target=self.run_by_thread, args=())
+        # self.t.start()
+
+    def matchingCallBack(self, data: dict):
+        self.set_state(2)
+        if data['code'] == 0:
+            self.show_progress_detail('处理完成')
+            button_1, button_2, button_3 = None, None, None
+            button_1 = "去抠图"
+            text = '移动完成'
+            my_dialog = DialogShow(self, text=text, button_1=button_1, button_2=button_2)
+            ret = my_dialog.exec()
+            if "去抠图" in my_dialog.flag_name:
+                self.back()
+                self.ui.tabWidget.setCurrentIndex(1)
+                path = os.getcwd() + "/" + data['target_path']
+                self.ui.path_name.setText(path)
+        else:
+            self.show_progress_detail(data['msg'])
+            text = data['msg']
+            my_dialog = DialogShow(self, text=text)
+            ret = my_dialog.exec()
+
+    def run_image_cut(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
+
+        # 先做整体校验
+        self.do_thread_run(func=self.check_for_cutout_image_first,
+                           call_back=self.check_for_cutout_image_first_call_back, time_out=30)
+
+    # 抠图前先做数据规整处理;类似详情图生成逻辑
+    def check_for_cutout_image_first(self, ):
+        return_data = {
+            "code": 99,
+            "message": "",
+            "data": {
+                "all_goods_art_no_folder_data": [],
+            },
+        }
+        image_dir = self.ui.path_name.text()
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.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),
+            'is_filter': str(is_filter),
+            '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['is_filter'] = str(is_filter)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+
+        # =================整理要处理的货号文件夹数据=================
+        # all_goods_art_no_folder_data 为数据列表,格式如下:
+        # {"folder_path": file_path,
+        #                  "folder_name": _file,
+        #                  "root_path": path,
+        #                  "to_do": True,  # 是否需要继续处理;默认为需要处理
+        #                  }
+        # 自动处理红蜻蜓货号,进行重命名
+        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
+        if self.ui.is_do_other.isChecked():
+            for i in all_goods_art_no_folder_data:
+                i["label"] = "不处理"
+            is_filter = False
+            specified_goods_art_no_folder = self.ui.special_goods_art_no_folder_line.text()
+            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 self.ui.is_do_other.isChecked():
+            if self.ui.is_clean_cutout_image.isChecked():
+                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"]
+        print("===================return_data=================")
+        print(json.dumps(return_data))
+        if code != 0:
+            # self.show_message(return_data["message"])
+            _dialog_dict = {"text": return_data["message"],
+                            "windows": self,
+                            }
+            self.show_dialog_sign.emit(_dialog_dict)
+            self.event_dialog.wait()
+            return
+
+        do_next = False
+        text = "check_for_cutout_image_first_call_back\n"
+        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))
+        # my_dialog = DialogShow(
+        #     self, text=text, button_1=button_1, button_2=button_2, button_3=button_3
+        # )
+        # ret = my_dialog.exec()
+
+        _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_dialog.wait()
+        print("self.dialog_result", self.dialog_result)
+
+        print("520  ===============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(self.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"] == "待处理"]
+            # self.set_state(state_value=1)
+            self.change_state_sign.emit(1)
+            time.sleep(1.5)
+            # 强制刷新界面命令
+            # QApplication.processEvents()
+            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,
+                      "logo_path": return_data["data"]["logo_path"], }
+            # self.t = threading.Thread(target=self.do_run2,
+            #                           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,
+            #                                   "logo_path": return_data["data"]["logo_path"],
+            #                                   })
+            # self.t.start()
+            # new_func = partial(self.do_run2,
+            #                    all_goods_art_no_folder_data=kwargs["all_goods_art_no_folder_data"],
+            #                    callback_func=kwargs["callback_func"],
+            #                    image_order_list=kwargs["image_order_list"],
+            #                    cutout_mode=kwargs["cutout_mode"],
+            #                    resize_image_view=kwargs["resize_image_view"],
+            #                    windows=kwargs["windows"],
+            #                    logo_path=kwargs["logo_path"],
+            #                    )
+            # self._w_3 = WorkerOneThread(func=new_func, name="_w_3")
+            # self._w_3.start()
+            self.do_run2(all_goods_art_no_folder_data=kwargs["all_goods_art_no_folder_data"],
+                         callback_func=kwargs["callback_func"],
+                         image_order_list=kwargs["image_order_list"],
+                         cutout_mode=kwargs["cutout_mode"],
+                         resize_image_view=kwargs["resize_image_view"],
+                         windows=kwargs["windows"],
+                         logo_path=kwargs["logo_path"], )
+
+    def do_run2(self, all_goods_art_no_folder_data,
+                callback_func,
+                image_order_list,
+                cutout_mode,
+                resize_image_view,
+                windows,
+                logo_path):
+        try:
+            print("610 BaseDealImage")
+            print(all_goods_art_no_folder_data)
+        except BaseException as e:
+            print("615", e)
+
+        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)
+
+        # 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)
+        #     # 处理完成的任务
+        #     for future in done:
+        #         if settings.IS_TEST:
+        #             result = future.result()
+
+        # ==============完成处理==============
+        self.change_state_sign.emit(2)
+        # self.set_state(state_value=2)
+        callback_func("已结束")
+
+    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 threadImageCut(self, ):
+        image_dir = self.ui.path_name.text()
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.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),
+            'is_filter': str(is_filter),
+            '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['is_filter'] = str(is_filter)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+
+        # =================整理要处理的货号文件夹数据=================
+        all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+        f = True
+        if self.ui.is_do_other.isChecked():
+            for i in all_goods_art_no_folder_data:
+                i["todo"] = False
+
+            is_filter = False
+            specified_goods_art_no_folder = self.ui.special_goods_art_no_folder_line.text()
+            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:
+                self.show_progress_detail('请手动输入文件夹名称(多选),或关闭指定文件夹模式')
+            else:
+                for i in all_goods_art_no_folder_data:
+                    if i["folder_path"] in specified_goods_art_no_folder_list:
+                        i["todo"] = True
+            # 哪些数据不合规
+            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:
+                    self.show_progress_detail('文件夹:{},在您选的目录下不存在'.format(goods_art_no_folder_name))
+                    f = False
+
+        if not f:
+            self.set_state(state_value=2)
+            return
+
+        # 清空指定文件夹的已抠图文件
+        # print("todo_goods_art_no_folder_name_list",todo_goods_art_no_folder_name_list)
+        if self.ui.is_do_other.isChecked():
+            if self.ui.is_clean_cutout_image.isChecked():
+                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)
+
+        if settings.IS_TEST:
+            result = baseDealImage.cutoutImage(
+                image_dir=image_dir,
+                all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                image_order=image_order,
+                is_check_number=is_check_number,
+                is_filter=is_filter,
+                resize_image_view=resize_image_view,
+                callback_func=self.show_progress_detail,
+                event=self.event,
+                cutout_mode=cutout_mode,
+                logo_path=logo_path,
+            )
+            if "code" in result and result['code'] != 0:
+                self.show_progress_detail(result['msg'])
+            else:
+                self.show_progress_detail('处理完成')
+        else:
+            try:
+                result = baseDealImage.cutoutImage(
+                    image_dir=image_dir,
+                    image_order=image_order,
+                    all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                    is_check_number=is_check_number,
+                    is_filter=is_filter,
+                    resize_image_view=resize_image_view,
+                    callback_func=self.show_progress_detail,
+                    event=self.event,
+                    cutout_mode=cutout_mode,
+                    logo_path=logo_path,
+                )
+
+                if "code" in result and result['code'] != 0:
+                    self.show_progress_detail(result['msg'])
+                else:
+                    self.show_progress_detail('处理完成')
+
+            except BaseException as e:
+                print(e)
+                self.show_progress_detail('程序异常')
+
+        self.set_state(state_value=2)
+
+    def show_progress_detail(self, text):
+        self.text_show.emit(text)
+
+    def append_text_to_browser(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    def run_by_thread(self):
+        image_dir = "{}/data".format(os.getcwd())
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        # result = baseDealImage.dealMoveImage(
+        #     image_dir=settings.PhotoOutputDir, callback_func=self.show_progress_detail
+        # )
+        result = baseDealImage.dealMoveImage(
+            image_dir=image_dir, callback_func=self.show_progress_detail
+        )
+        return result
+
+    def run_by_thread_backup(self):
+        if not self.check():
+            return
+
+        # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
+        files = os.listdir(self.image_dir)
+        _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE"]
+        original_photo_list = []  # 原始图片列表
+        for file in files:
+            # -----图片清洗
+            file_path = self.image_dir + "/" + file
+            if os.path.isdir(file_path):  # 忽略文件夹
+                continue
+            file_name, e = os.path.splitext(file)
+            if e not in _Type:  # 非图片进行移除
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+                continue
+
+            date_time_original = self.get_date_time_original(
+                file_path
+            )  # 获取照片拍照时间
+            if date_time_original:
+                # 基于照片的时间,与数据库匹配goods_art_no
+                _data = self.data_match_photo.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)
+                    )
+                    # shutil.move(photo_dict["file_path"], self.image_dir + "/历史/" + photo_dict["file"])
+                    continue
+            else:
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+
+        if not original_photo_list:
+            self.show_progress_detail("没有任何匹配的图片~")
+            self.set_state(state_value=2)
+            return
+
+        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.data_match_photo.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.data_match_photo.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 n != 0:
+            # if settings.MattingPics:
+            #     # 检查所有未处理的货号文件夹,查看是否有完成图片加工处理
+            #     self.deal_images()
+
+            # 自动生成一个货号表
+            print("output_path", output_path)
+            GenerateGoodsArtNoTable.deal(output_path)
+
+        # 完成处理
+        self.set_state(state_value=2)
+
+    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 deal_images(self):
+        """
+        2、压缩并上传平台获取抠图
+        3、抠图处理成白底图
+        4、做成800*800/200*200
+        :return:
+        """
+        output_path = "output"
+        """
+        扫描文档,检查有哪些需要进行抠图等处理
+        """
+        to_do_images_total = 0
+        for _date_folder in os.listdir(output_path):
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                # print(goods_art_no_folder)
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    if os.path.splitext(file)[0] not in all_moved_images:
+                        print("----------》", goods_art_no_folder, file)
+                        to_do_images_total += 1
+
+        if to_do_images_total > 0:
+            self.progress_sign.emit(
+                {"type": "处理图片 抠图、加工等", "progress_bar_value": 0}
+            )
+        else:
+            return True
+
+        """开始处理抠图等处理"""
+        n = 0
+        for _date_folder in os.listdir(output_path):
+            # 过滤非文件夹的文件
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+
+            # 扫描每个货号
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    original_image_path = "{}/{}/{}/原始图/{}".format(
+                        output_path, _date_folder, goods_art_no_folder, file
+                    )
+                    file_name = os.path.splitext(file)[0]
+                    if file_name not in all_moved_images:
+                        print("上传平台获取抠图结果----------》", file)
+                        goods_art_no_folder_path = "{}/{}/{}".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                        pic_deal = PicDeal()
+                        # 上传平台获取抠图结果
+                        original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(
+                            goods_art_no_folder_path, file_name, ".png"
+                        )
+                        if pic_deal.remove_bg(
+                                in_path=original_image_path,
+                                out_path=original_move_bg_image_path,
+                        ):
+                            self.show_progress_detail(
+                                "货号图{} 已完成抠图处理~".format(file_name)
+                            )
+                            # 抠图处理成白底图 800*800
+                            out_800_path = "{}/800x800/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.create_800image(
+                                image_path=original_move_bg_image_path,
+                                out_path=out_800_path,
+                            )
+                            # 抠图处理成白底图 200*200
+                            out_200_path = "{}/200images/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.resize_and_save(
+                                image_path=out_800_path,
+                                out_path=out_200_path,
+                                width=200,
+                            )
+                            self.show_progress_detail(
+                                "货号图{} 已完成800*800图片和200*200图片制作~".format(
+                                    file_name
+                                )
+                            )
+                        else:
+                            self.show_progress_detail(
+                                "货号图{} 抠图处理失败~".format(file_name)
+                            )
+                        # 完成处理的图片进度
+                        n += 1
+                        self.progress_sign.emit(
+                            {
+                                "type": "处理图片 抠图、加工等",
+                                "progress_bar_value": int(n / to_do_images_total * 100),
+                            }
+                        )
+
+    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 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 on_dialog_result(self, result):
+        """处理对话框结果"""
+        self.dialog_result = result
+        print("1077  处理对话框结果:{}".format(result))
+        # self.quit()  # 结束事件循
+        self.event_dialog.set()
+
+    # 弹出对话框
+    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("632  my_dialog.flag_name", my_dialog.flag_name)
+        # 根据用户的选择发出相应的信号
+        windows.communicator.dialog_result_signal.emit(my_dialog.flag_name)
+
+
+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

+ 8 - 0
python/service/matching_photos/data.py

@@ -0,0 +1,8 @@
+from ..data_metaclass import DataBaseModel
+
+
+class DataModeMatchPhoto(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+
+

+ 847 - 0
python/service/matching_photos/module_matching_photos.py

@@ -0,0 +1,847 @@
+# from UI.matching_photos.matching_photos import Ui_Form as matching_photos_Ui_Form
+import os
+
+from UI.matching_photos.matching_and_cutout_photos import (
+    Ui_Form as matching_photos_Ui_Form,
+)
+from import_qt_mode import *
+
+import settings
+from collections import defaultdict
+
+from module.service.base_deal_image import BaseDealImage
+from module.view_control.generate_goods_art_no_table.module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+from module.view_control.matching_photos.data import DataModeMatchPhoto
+from module.view_control.manual_image_matching.m_image_matching_cotrol import MainMatchingWindow
+
+from PySide6.QtGui import QPixmap
+from PIL import Image
+import io
+
+import threading
+import shutil
+import time
+from module.base_mode.pic_deal import PicDeal
+# import exifread
+from module.base_mode.base import *
+from module.view_control.MineQWidget import MineQWidget, DialogShow
+from threading import Event
+from functools import partial
+from UI.matching_photos.temp_item import Ui_Form as UI_temp_item
+
+"""
+照片自动货号匹配 将图片放置在指定文件夹下,并自动对应不同的货号进行整理
+"""
+
+
+class MatchingPhotos(MineQWidget):
+    progress_sign = Signal(dict)
+    info_sign = Signal(str)
+    text_show = Signal(str)
+
+    def __init__(self):
+        super().__init__()
+        self.image_dir = settings.PhotoImageDir
+        self.ui = matching_photos_Ui_Form()
+        self.ui.setupUi(self)
+        # 0禁用  1进行中  2已结束
+        self.state = 2
+        self.event = Event()
+        self.goods_images_count_dict = defaultdict(int)
+        self.data_match_photo = DataModeMatchPhoto()
+        self.excel_path = ""
+        # 上次使用的详情模板
+        self.last_temp = settings.MATCHING_LAST_TEMP
+        # 指定单独处理的颜色文件夹
+        self.special_goods_art_no_folder = []
+
+        self.init()
+        self.show()
+
+    def init(self):
+        # 文本
+        self.ui.textBrowser.clear()
+        text = """请将capture One 加工后的图片放在指定目录下(目录为当前文件夹下的{}/data文件夹),务必确保导出的图片有拍摄时间信息。""".format(os.getcwd())
+        self.ui.textBrowser.append(text)
+        self.ui.tabWidget.setCurrentIndex(0)
+        # 页面视图1
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.text_show.connect(self.append_text_to_browser)
+        self.show_img_dir()
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.setMaximum(100)
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.hide()
+        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']))
+        self.ui.check_is_pass.setChecked(bool(settings.cutimage_dict['is_filter']))
+        print(settings.cutimage_dict)
+        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.ui.is_do_other.toggled.connect(self.change_do_special_treatment)
+        self.change_do_special_treatment()
+
+        self.progress_sign.connect(self.show_progress)
+        self.ui.run.clicked.connect(self.run_image_cut)  # 抠图与主图加工
+        self.ui.run_2.clicked.connect(self.run_matching)  # 匹配图片
+        self.ui.back.clicked.connect(self.back)
+        # 页面视图2
+        self.ui.back.hide()
+        self.set_logo_selected()
+
+        # 手动匹配图片
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic_mousePressEvent
+
+    # 切换是否特殊处理的开关
+    def change_do_special_treatment(self, *args):
+        if self.ui.is_do_other.isChecked():
+            self.ui.show_deal_condition.show()
+        else:
+            self.ui.show_deal_condition.hide()
+
+    # 设置选择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)
+                print("111111111          image_list", image_list)
+                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 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)
+
+    @Slot()
+    def on_stop_clicked(self):
+        self.event.set()
+        pass
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
+        print(folder)
+        settings.set_config(data_dict={"photo_image_dir": folder})
+
+        # 清空已特殊选择等处理
+        self.ui.is_do_other.setChecked(False)
+
+        self.show_img_dir(image_dir=folder)
+
+    def show_img_dir(self, image_dir=None):
+        if image_dir:
+            self.image_dir = image_dir
+        else:
+            self.image_dir = settings.PhotoImageDir
+
+        if self.image_dir:
+            self.ui.path_name.show()
+            self.ui.path_name.setText(self.image_dir)
+        else:
+            self.ui.path_name.hide()
+
+    def show_progress(self, data):
+        progress_bar_value = data["progress_bar_value"]
+        self.ui.progressBar.setValue(progress_bar_value)
+
+    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.ui.stackedWidget.setCurrentIndex(0)
+        if self.state == 1:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.hide()
+            self.ui.stop.show()
+            self.ui.textBrowser_4.clear()
+            self.ui.textBrowser_4.show()
+            self.event.clear()
+
+        if self.state == 2:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.show()
+            self.ui.stop.hide()
+
+    def back(self):
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.ui.back.hide()
+        self.ui.run.show()
+        self.ui.stop.hide()
+        self.ui.textBrowser_4.clear()
+        self.ui.textBrowser_4.hide()
+
+    def check_first_call_back(self, data):
+        # 首次数据校验的信息返回
+        # self.show_message(text="22222222222222222222222")
+        # QMessageBox.critical(self, "警告", "1111111", QMessageBox.Ok)
+        code = data["code"]
+        if data["message"]:
+            button_1, button_2, button_3 = None, None, None
+            button_1 = "移除"
+            button_2 = "继续"
+            text = data["message"]
+            my_dialog = DialogShow(self, text=text, button_1=button_1, button_2=button_2)
+            ret = my_dialog.exec()
+            print(my_dialog.flag_name)
+            if "移除" in my_dialog.flag_name:
+                pass
+            if "继续" in my_dialog.flag_name:
+                pass
+
+    def check_test(self, test):
+        print(test)
+        time.sleep(3)
+        return_data = {"code": 99,
+                       "message": "messagemessagemessagemessagemessagemessagemessage",
+                       "data": {"error_folder_list": [],
+                                "goods_no_dict": {},
+                                "succeed_folder_list": [],
+                                }}
+
+        return return_data
+
+    def run_matching11111(self, *args):
+        func = partial(self.check_test, test=1111111)
+        self.do_thread_run(func=func, call_back=self.check_first_call_back, time_out=30)
+
+    def run_matching(self, *args):
+        if settings.PROJECT == "红蜻蜓":
+            if not settings.IsLogin:
+                a = QMessageBox.question(
+                    self,
+                    "确认",
+                    "请先进行登录红蜻蜓账号",
+                    QMessageBox.Yes | QMessageBox.No,
+                )
+                return
+
+        self.set_state(state_value=1)
+        self.goods_images_count_dict = {}
+        self.goods_images_count_dict = defaultdict(int)
+        self.do_thread_run(func=self.run_by_thread, call_back=self.matchingCallBack, time_out=60)
+
+        # self.t = threading.Thread(target=self.run_by_thread, args=())
+        # self.t.start()
+
+    def matchingCallBack(self, data: dict):
+        self.set_state(2)
+        if data['code'] == 0:
+            self.show_progress_detail('处理完成')
+            button_1, button_2, button_3 = None, None, None
+            button_1 = "去抠图"
+            text = '移动完成'
+            my_dialog = DialogShow(self, text=text, button_1=button_1, button_2=button_2)
+            ret = my_dialog.exec()
+            if "去抠图" in my_dialog.flag_name:
+                self.back()
+                self.ui.tabWidget.setCurrentIndex(1)
+                path = os.getcwd() + "/" + data['target_path']
+                self.ui.path_name.setText(path)
+        else:
+            self.show_progress_detail(data['msg'])
+            text = data['msg']
+            my_dialog = DialogShow(self, text=text)
+            ret = my_dialog.exec()
+
+    def run_image_cut(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
+
+        self.set_state(state_value=1)
+        self.t = threading.Thread(target=self.threadImageCut, args=())
+        self.t.start()
+
+    def threadImageCut(
+            self,
+    ):
+        image_dir = self.ui.path_name.text()
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.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),
+            'is_filter': str(is_filter),
+            '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['is_filter'] = str(is_filter)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+
+        # =================整理要处理的货号文件夹数据=================
+        is_ok = True
+        all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+        todo_goods_art_no_folder_name_list = []
+        if not self.ui.is_do_other.isChecked():
+            todo_goods_art_no_folder_name_list = [x["folder_name"] for x in all_goods_art_no_folder_data]
+        else:
+            is_filter = False
+            specified_goods_art_no_folder = self.ui.special_goods_art_no_folder_line.text()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.strip()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.replace(",", ",")
+            if not specified_goods_art_no_folder:
+                self.show_progress_detail('请手动输入文件夹名称(多选),或关闭指定文件夹模式')
+                is_ok = False
+            else:
+                specified_goods_art_no_folder_list = specified_goods_art_no_folder.split(",")
+                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:
+                        if goods_art_no_folder_name not in all_folder_name_list:
+                            self.show_progress_detail('文件夹:{},在您选的目录下不存在'.format(goods_art_no_folder_name))
+                            is_ok = False
+                        else:
+                            todo_goods_art_no_folder_name_list.append(goods_art_no_folder_name)
+
+        if not is_ok:
+            self.set_state(state_value=2)
+            return
+
+        # 清空指定文件夹的已抠图文件
+        print("todo_goods_art_no_folder_name_list",todo_goods_art_no_folder_name_list)
+        if self.ui.is_do_other.isChecked():
+            if self.ui.is_clean_cutout_image.isChecked():
+                for goods_art_no_folder_name in todo_goods_art_no_folder_name_list:
+                    goods_art_no_folder_path = "{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder_name)
+                    print("goods_art_no_folder_path",goods_art_no_folder_path)
+                    if os.path.exists(goods_art_no_folder_path):
+                        remove_all_file(goods_art_no_folder_path)
+
+        if settings.IS_TEST:
+            result = baseDealImage.cutoutImage(
+                image_dir=image_dir,
+                todo_goods_art_no_folder_name_list=todo_goods_art_no_folder_name_list,
+                image_order=image_order,
+                is_check_number=is_check_number,
+                is_filter=is_filter,
+                resize_image_view=resize_image_view,
+                callback_func=self.show_progress_detail,
+                event=self.event,
+                cutout_mode=cutout_mode,
+                logo_path=logo_path,
+            )
+            if "code" in result and result['code'] != 0:
+                self.show_progress_detail(result['msg'])
+            else:
+                self.show_progress_detail('处理完成')
+        else:
+            try:
+                result = baseDealImage.cutoutImage(
+                    image_dir=image_dir,
+                    image_order=image_order,
+                    todo_goods_art_no_folder_name_list=todo_goods_art_no_folder_name_list,
+                    is_check_number=is_check_number,
+                    is_filter=is_filter,
+                    resize_image_view=resize_image_view,
+                    callback_func=self.show_progress_detail,
+                    event=self.event,
+                    cutout_mode=cutout_mode,
+                    logo_path=logo_path,
+                )
+
+                if "code" in result and result['code'] != 0:
+                    self.show_progress_detail(result['msg'])
+                else:
+                    self.show_progress_detail('处理完成')
+
+            except BaseException as e:
+                print(e)
+                self.show_progress_detail('程序异常')
+
+        self.set_state(state_value=2)
+
+    def show_progress_detail(self, text):
+        self.text_show.emit(text)
+
+    def append_text_to_browser(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    def run_by_thread(self):
+        image_dir = "{}/data".format(os.getcwd())
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        # result = baseDealImage.dealMoveImage(
+        #     image_dir=settings.PhotoOutputDir, callback_func=self.show_progress_detail
+        # )
+        result = baseDealImage.dealMoveImage(
+            image_dir=image_dir, callback_func=self.show_progress_detail
+        )
+        return result
+
+    def run_by_thread_backup(self):
+        if not self.check():
+            return
+
+        # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
+        files = os.listdir(self.image_dir)
+        _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE"]
+        original_photo_list = []  # 原始图片列表
+        for file in files:
+            # -----图片清洗
+            file_path = self.image_dir + "/" + file
+            if os.path.isdir(file_path):  # 忽略文件夹
+                continue
+            file_name, e = os.path.splitext(file)
+            if e not in _Type:  # 非图片进行移除
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+                continue
+
+            date_time_original = self.get_date_time_original(
+                file_path
+            )  # 获取照片拍照时间
+            if date_time_original:
+                # 基于照片的时间,与数据库匹配goods_art_no
+                _data = self.data_match_photo.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)
+                    )
+                    # shutil.move(photo_dict["file_path"], self.image_dir + "/历史/" + photo_dict["file"])
+                    continue
+            else:
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+
+        if not original_photo_list:
+            self.show_progress_detail("没有任何匹配的图片~")
+            self.set_state(state_value=2)
+            return
+
+        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.data_match_photo.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.data_match_photo.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 n != 0:
+            # if settings.MattingPics:
+            #     # 检查所有未处理的货号文件夹,查看是否有完成图片加工处理
+            #     self.deal_images()
+
+            # 自动生成一个货号表
+            print("output_path", output_path)
+            GenerateGoodsArtNoTable.deal(output_path)
+
+        # 完成处理
+        self.set_state(state_value=2)
+
+    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 deal_images(self):
+        """
+        2、压缩并上传平台获取抠图
+        3、抠图处理成白底图
+        4、做成800*800/200*200
+        :return:
+        """
+        output_path = "output"
+        """
+        扫描文档,检查有哪些需要进行抠图等处理
+        """
+        to_do_images_total = 0
+        for _date_folder in os.listdir(output_path):
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                # print(goods_art_no_folder)
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    if os.path.splitext(file)[0] not in all_moved_images:
+                        print("----------》", goods_art_no_folder, file)
+                        to_do_images_total += 1
+
+        if to_do_images_total > 0:
+            self.progress_sign.emit(
+                {"type": "处理图片 抠图、加工等", "progress_bar_value": 0}
+            )
+        else:
+            return True
+
+        """开始处理抠图等处理"""
+        n = 0
+        for _date_folder in os.listdir(output_path):
+            # 过滤非文件夹的文件
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+
+            # 扫描每个货号
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    original_image_path = "{}/{}/{}/原始图/{}".format(
+                        output_path, _date_folder, goods_art_no_folder, file
+                    )
+                    file_name = os.path.splitext(file)[0]
+                    if file_name not in all_moved_images:
+                        print("上传平台获取抠图结果----------》", file)
+                        goods_art_no_folder_path = "{}/{}/{}".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                        pic_deal = PicDeal()
+                        # 上传平台获取抠图结果
+                        original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(
+                            goods_art_no_folder_path, file_name, ".png"
+                        )
+                        if pic_deal.remove_bg(
+                                in_path=original_image_path,
+                                out_path=original_move_bg_image_path,
+                        ):
+                            self.show_progress_detail(
+                                "货号图{} 已完成抠图处理~".format(file_name)
+                            )
+                            # 抠图处理成白底图 800*800
+                            out_800_path = "{}/800x800/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.create_800image(
+                                image_path=original_move_bg_image_path,
+                                out_path=out_800_path,
+                            )
+                            # 抠图处理成白底图 200*200
+                            out_200_path = "{}/200images/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.resize_and_save(
+                                image_path=out_800_path,
+                                out_path=out_200_path,
+                                width=200,
+                            )
+                            self.show_progress_detail(
+                                "货号图{} 已完成800*800图片和200*200图片制作~".format(
+                                    file_name
+                                )
+                            )
+                        else:
+                            self.show_progress_detail(
+                                "货号图{} 抠图处理失败~".format(file_name)
+                            )
+                        # 完成处理的图片进度
+                        n += 1
+                        self.progress_sign.emit(
+                            {
+                                "type": "处理图片 抠图、加工等",
+                                "progress_bar_value": int(n / to_do_images_total * 100),
+                            }
+                        )
+
+    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 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
+
+
+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

+ 2 - 2
python/service/module_generate_goods_art_no_table.py

@@ -2,10 +2,10 @@
 import threading
 import xlsxwriter
 import shutil
-from pic_deal import Picture
+from .pic_deal import Picture
 
 
-from excel_base_func import *
+from .excel_base_func import *
 
 
 class GenerateGoodsArtNoTable():

+ 436 - 0
python/service/online_request/module_online_data.py

@@ -0,0 +1,436 @@
+import requests
+import settings
+import json
+import numpy as np
+
+
+class JsonEncoder(json.JSONEncoder):
+    """Convert numpy classes to JSON serializable objects."""
+
+    def default(self, obj):
+        if isinstance(obj, (np.integer, np.floating, np.bool_)):
+            return obj.item()
+        elif isinstance(obj, np.ndarray):
+            return obj.tolist()
+        else:
+            return super(JsonEncoder, self).default(obj)
+
+
+class OnlineDataRequest(object):
+    def __init__(self,token):
+        self.s = requests.session()
+        self.token = token
+        self.post_headers = {
+            "Authorization": token,
+            #  "Origin": settings.Headers["Origin"],
+            #  "Host": settings.Headers["Host"],
+            "Content-Length": "0",
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+        }
+        print("28   Authorization:", self.post_headers["Authorization"])
+
+    def refresh_headers(self, token):
+        self.post_headers = {
+            "Authorization": token,
+            #  "Origin": settings.Headers["Origin"],
+            #  "Host": settings.Headers["Host"],
+            "Content-Length": "0",
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+        }
+
+    def auth_user(self):
+        # 用户登录
+        url = "{domain}/api/auth/user".format(
+            domain=settings.DOMAIN
+        )
+        s = requests.session()
+        _s = s.get(url=url, headers=settings.Headers)
+        response_data = _s.json()
+        return response_data
+
+    def logout(self):
+        url = "{domain}/api/auth/logout".format(
+            domain=settings.DOMAIN
+        )
+        s = requests.session()
+        _s = s.post(url=url, headers=settings.Headers)
+
+    def get_change_bar_code(self, code):
+        url = "{domain}/api/hct/open/sting_search_goods?string={code}".format(domain=settings.DOMAIN, code=code)
+        try:
+            s = requests.get(url)
+            goods_art_no = s.json()["data"]["goods_art_no"]
+            return goods_art_no
+        except BaseException as e:
+            print(e)
+            return
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None,token=None):
+        # 获取商品基础信息,入参为商品的编号
+        url = "{domain}/api/backend/produce/goods/info".format(
+            domain=settings.DOMAIN
+        )
+        if numbers_list:
+            data = {
+                'numbers': numbers_list
+            }
+            print("请求编码:", numbers_list)
+        else:
+            data = {
+                'goods_art_nos': goods_art_list
+            }
+            print("请求货号:", goods_art_list)
+            print("请求货号=====>", self.token)
+        post_headers = {
+            "Authorization": token,
+            # "Origin": settings.Headers["Origin"],
+            # "Host": settings.Headers["Host"],
+            "Content-Length": "",
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+        }
+        data = json.dumps(data)
+        post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=post_headers)
+        # _s = self.s.get(url=url, params=params, headers=settings.Headers)
+        response_data = _s.json()
+        # print(response_data)
+        # print("\n")
+
+        goods_number_data = {}
+        # ["", "", "", "", "", "", "", "", "", "", "", ]
+        if "data" not in response_data:
+            return {}
+
+        for data in response_data["data"]:
+            if numbers_list:
+                number = data["number"]
+            else:
+                number = data["goods_art_no"].upper()
+
+            goods_number_data[number] = {}
+            goods_number_data[number]["商品面料"] = data["fabric"]
+            goods_number_data[number]["商品内里"] = data["lining"]
+            goods_number_data[number]["商品鞋底"] = data["sole"]
+            goods_number_data[number]["后帮高"] = data["back_height"]
+            goods_number_data[number]["前掌宽"] = data["forefoot_width"]
+            goods_number_data[number]["鞋跟高"] = data["heel_height"]
+            goods_number_data[number]["FAB介绍"] = data["fab_info"]
+            goods_number_data[number]["编号"] = data["number"]
+            goods_number_data[number]["商品货号"] = data["goods_art_no"].upper()
+            goods_number_data[number]["款号"] = data["goods_number"].upper()
+            goods_number_data[number]["颜色名称"] = data["color"]
+            goods_number_data[number]["所属企划"] = data["projects"][0]
+            goods_number_data[number]["设计方名称"] = data["purchasing_unit"]
+            goods_number_data[number]["供应商"] = data["supplier_name"]
+            goods_number_data[number]["供应商编码"] = data["supplier_code"].lstrip('0')
+            goods_number_data[number]["供应商货号"] = data["supplier_goods_artno"]
+            goods_number_data[number]["销售工厂"] = data["sales_factory_name"]
+            goods_number_data[number]["销售组织"] = data["man_org_name"]
+            goods_number_data[number]["是否SAP"] = data["source"]
+            goods_number_data[number]["OEM报价"] = data["oem_price"]
+            goods_number_data[number]["出厂价"] = data["ex_factory_price"]
+            goods_number_data[number]["首单货期"] = data["earliest_delivery_date"]
+            goods_number_data[number]["包装"] = data["package_specification"]
+            goods_number_data[number]["创建日期"] = data["created_at"]
+            goods_number_data[number]["货号图"] = data["image"]
+        return goods_number_data
+
+    def get_on_goods_all_art(self, number):
+        # 获取商品基础信息,入参为商品的编号
+        url = "{domain}/api/backend/produce/goods/query/numbers?number={number}".format(
+            domain=settings.DOMAIN,
+            number=number
+        )
+        _s = self.s.get(url=url, headers=self.post_headers)
+        response_data = _s.json()
+        print(number, response_data)
+        """
+        14250230 {'data': {'goods_number': 'AC5200117', 'brother_goods_arts': [{'number': '14250232', 'goods_art_no': 'AC52001173', 'color': '杏色'}, {'number': '14250231', 'goods_art_no': 'AC52001172', 'color': '灰色'}, {'number': '14250230', 'goods_art_no': 'AC52001171', 'color': '黑色'}]}, 'code': 0, 'message': 'success'}
+        """
+        return response_data["data"]["goods_number"], response_data["data"]["brother_goods_arts"], \
+               response_data["data"]["goods_art_no"]
+
+    def get_views(self, image_url):
+
+        url = "http://{}/shoes_category".format(settings.VIEW_DEAL_DOMAIN)
+        data = {"train_path": "./datasets/Shoes_Dataset/Train/angle",
+                "model_filename": "./models/0320/output0320.pth",
+                "validate_path": image_url, }
+        _s = requests.post(url=url, data=json.dumps(data), )
+        response_data = _s.json()
+        return response_data["classify_result"]
+
+    def uploadImage(self, local_path: str) -> str:
+        post_headers = {"Authorization": self.token}
+        url = settings.DOMAIN + '/api/upload'
+        resultData = self.s.post(url, files={'file': open(local_path, 'rb')}, headers=post_headers).json()
+        return resultData['data']['url']
+
+    def get_current_menu(self):
+        def get_menu(_menu_dict, _data):
+            for menu in _data:
+                _menu_dict[menu["key"]] = {}
+                for mods in menu["mods_arr"]:
+                    _menu_dict[menu["key"]][mods["key"]] = mods["name"]
+                if "_child" in menu:
+                    get_menu(_menu_dict, menu["_child"])
+            return _menu_dict
+
+        url = "{domain}/api/backend/basic/get_current_menu".format(
+            domain=settings.DOMAIN,
+        )
+        _s = self.s.get(url=url, headers=settings.Headers)
+        response_data = _s.json()
+        try:
+            menu_data = response_data["data"]["pc_menu"]
+            menu_dict = {}
+            menu_dict = get_menu(menu_dict, menu_data)
+        except:
+            menu_dict = {}
+        # print(json.dumps(menu_dict,ensure_ascii=False))
+        # raise 1
+        return menu_dict
+
+    # 获取所有资源的配置
+    def get_resource_config(self):
+        url = "{domain}/api/openai/query_client_addons".format(domain=settings.DOMAIN)
+        _s = self.s.get(url=url, headers=self.post_headers, params={"type": "client_camera"})
+        response_data = _s.json()
+        return response_data
+
+    # 拍照日志上报
+    def add_auto_photo_logs(self, data):
+        url = "{domain}/api/openai/add_auto_photo_logs".format(domain=settings.DOMAIN)
+        post_data = {"goods_no": data["goods_art_no"],
+                     "take_photo_created_at": data["take_photo_created_at"],
+                     "photo_created_at": data["photo_create_time"],
+                     "image_dispose_mode": data["image_deal_mode"],
+                     "photo_serial_number": data["image_index"], }
+
+        post_data = json.dumps(post_data)
+
+        _s = self.s.post(url=url, headers=self.post_headers, data=post_data)
+        response_data = _s.json()
+        if settings.IS_TEST:
+            print("209-----拍照日志上报   add_auto_photo_logs")
+            print(response_data)
+        return response_data
+
+    def upload_pic_list_data(self, data):
+        url = "{domain}/api/backend/goods/save/images".format(
+            domain=settings.DOMAIN
+        )
+        data = json.dumps(data)
+        self.post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=self.post_headers)
+        response_data = _s.json()
+        try:
+            if response_data["code"] == 0 and response_data["message"] == "success":
+                return True
+            else:
+                print(data)
+                print(response_data)
+                return False
+        except BaseException as e:
+            print(data)
+            print(e)
+            print(response_data)
+            return False
+
+    def upload_pic(self, goods_data):
+        # 检查货号图是否存在
+        url = "{domain}/api/backend/upload".format(
+            domain=settings.DOMAIN
+        )
+        # print(url)
+        headers = {'Authorization': settings.Headers["Authorization"],
+                   'User-Agent': settings.Headers["User-Agent"],
+                   'Origin': settings.Headers["Origin"],
+                   'Host': settings.Headers["Host"], }
+
+        files = [
+            ('file',
+             (goods_data["file_path"], goods_data["image_io"], 'image/{}'.format(goods_data["e"])))
+        ]
+
+        _s = requests.post(url=url, headers=headers, files=files)
+        response_data = _s.json()
+
+        return response_data["data"]["url"]
+
+    # 查询是否已有详情图
+    def check_detail_image(self, goods_art_no,token):
+        url = "{domain}/api/backend/goods/check_detail_image?number={number}".format(domain=settings.DOMAIN,
+                                                                                     number=goods_art_no)
+        _s = self.s.get(url=url, headers=self.post_headers)
+        response_data = _s.json()
+        # print(response_data)
+        return response_data["data"]["hasDetailImage"]
+
+    # 调用API识别是否是拖鞋
+    def yolo_shoes_category(self, image_url):
+        url = "{domain}/api/ai_image/main/yolo_shoes_category".format(domain=settings.DOMAIN)
+        post_data = {"image_url": image_url,
+                     }
+
+        post_data = json.dumps(post_data)
+
+        _s = self.s.post(url=url, headers=self.post_headers, data=post_data)
+        response_data = _s.json()
+        if settings.IS_TEST:
+            print("278-----yolo_shoes_category")
+            print(response_data)
+
+        r_data = None
+        try:
+            r_data = response_data["data"]["category"]
+        except BaseException as e:
+            print("285", e)
+        return r_data
+
+    # 图片上传by IO
+    def upload_image_by_io(self, image_io) -> str:
+        post_headers = {"Authorization": settings.Authorization}
+        url = settings.DOMAIN + '/api/upload'
+        resultData = self.s.post(url, files={'file': image_io}, headers=post_headers).json()
+        return resultData['data']['url']
+
+
+class GetOnlineDataHLM(OnlineDataRequest):
+
+    def __init__(self,token):
+        super().__init__(token)
+        self.token = token
+    def upload_pic(self, goods_data):
+        # 检查货号图是否存在
+        url = "{domain}/api/backend/upload".format(
+            domain=settings.DOMAIN
+        )
+        # print(url)
+        headers = {'Authorization': self.token,
+                #    'User-Agent': settings.Headers["User-Agent"],
+                #    'Origin': settings.Headers["Origin"],
+                #    'Host': settings.Headers["Host"], 
+                   }
+
+        files = [
+            ('file',
+             (goods_data["file_path"], goods_data["image_io"], 'image/{}'.format(goods_data["e"])))
+        ]
+
+        _s = requests.post(url=url, headers=headers, files=files)
+        response_data = _s.json()
+
+        return response_data["data"]["url"]
+
+    def upload_pic_list_data(self, data):
+        url = "{domain}/api/backend/goods/save/images".format(
+            domain=settings.DOMAIN
+        )
+        data = json.dumps(data)
+        self.post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=self.post_headers)
+        response_data = _s.json()
+        try:
+            if response_data["code"] == 0 and response_data["message"] == "success":
+                return True
+            else:
+                print(response_data)
+                return False
+        except BaseException as e:
+            print(e)
+            print(response_data)
+            return False
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None,token=None):
+        # 获取商品基础信息,入参为商品的编号
+        url = "{domain}/api/backend/goods_client/goods_query".format(
+            domain=settings.DOMAIN
+        )
+        data = {
+            'goods_art_list': goods_art_list
+        }
+        print("url:", url)
+        print("请求货号:", goods_art_list)
+
+        post_headers = {
+            "Authorization": token,
+            # "Origin": settings.Headers["Origin"],
+            # "Host": settings.Headers["Host"],
+            "Content-Length": "",
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+        }
+        data = json.dumps(data)
+        print(post_headers)
+        print(data)
+        # post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=post_headers)
+        # _s = self.s.get(url=url, params=params, headers=settings.Headers)
+        response_data = _s.json()
+        print(response_data)
+        print("\n")
+
+        goods_number_data = {}
+        # ["", "", "", "", "", "", "", "", "", "", "", ]
+        if "data" not in response_data:
+            return {}
+
+        for data in response_data["data"]:
+            goods_number_data[data["goods_art_no"]] = {}
+            goods_number_data[data["goods_art_no"]]["商品货号"] = data["goods_art_no"].upper()
+            goods_number_data[data["goods_art_no"]]["款号"] = data["goods_number"].upper()
+            goods_number_data[data["goods_art_no"]]["商品面料"] = data["fabric"]
+            goods_number_data[data["goods_art_no"]]["商品内里"] = data["lining"]
+            goods_number_data[data["goods_art_no"]]["商品鞋底"] = data["sole"]
+            goods_number_data[data["goods_art_no"]]["鞋垫"] = data["insole"]
+            goods_number_data[data["goods_art_no"]]["颜色名称"] = data["color"]
+
+        return goods_number_data
+
+    def uploadImage(self, local_path: str) -> str:
+        post_headers = {"Authorization": settings.Authorization}
+        url = settings.DOMAIN + '/api/upload'
+        resultData = self.s.post(url, files={'file': open(local_path, 'rb')}, headers=post_headers).json()
+        return resultData['data']['url']
+
+    # ============pixian抠图处理==========================
+    def dispose_point(self, _type):
+        # 扣分 sub;add为增加分数,每次操作一分
+        url = "{domain}/api/ai_image/client/dispose_point".format(
+            domain=settings.DOMAIN)
+        data = {"type": _type}
+        _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=10)
+        response_data = _s.json()
+        return response_data
+
+    def send_message(self, text):
+        # 发送钉钉消息
+        url = "{domain}/api/ai_image/client/send_message".format(
+            domain=settings.DOMAIN)
+        data = {"message": text}
+        _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=10)
+        response_data = _s.json()
+        return response_data
+
+    def get_cutout_image_times(self):
+        # 获取抠图剩余次数
+        url = "{domain}/api/ai_image/client/search_company_balance".format(
+            domain=settings.DOMAIN)
+        _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
+        response_data = _s.json()
+        if "data" not in response_data:
+            return False
+        else:
+            return response_data["data"]
+
+    def get_key_secret(self):
+        # 获取抠图剩余次数
+        url = "{domain}/api/ai_image/client/get_key_serect".format(
+            domain=settings.DOMAIN)
+        _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
+        response_data = _s.json()
+        return response_data["data"]

+ 230 - 0
python/service/remove_bg_pixian.py

@@ -0,0 +1,230 @@
+import copy
+import os
+from PIL import Image
+from .remove_bg_ali import RemoveBgALi
+import requests
+from io import BytesIO
+
+
+class Segment(object):
+    def __init__(self):
+        self.k = "pxnib99dbchtmdm"
+        self.s = "ub9uj5678gs4m2bnrass1t3tn6ughlk065ianosk06akagolcr2u"
+
+    def get_no_bg_goods2(self, file_path=None, _im=None):
+        im = _im
+        img = BytesIO()
+        try:
+            im.save(img, format='JPEG')  # format: PNG or JPEG
+        except:
+            im.save(img, format='PNG')  # format: PNG or JPEG
+        img.seek(0)  # rewind to the start
+
+        # img = "https://ossimg.valimart.net/uploads/vali_ai/20241011/172864207036098.png"
+
+        response = requests.post(
+            'http://47.76.110.118:27777/api/v2/remove-background',
+            files={'image.url': img},
+            data={
+                # Add more upload options here
+            },
+            auth=(self.k, self.s)
+        )
+        # print(response.content)
+        if response.status_code == requests.codes.ok:
+            return response.content, ""
+        else:
+            return None, response.content
+
+    def get_no_bg_goods_by_url(self, url):
+        response = requests.post(
+            'https://api.pixian.ai/api/v2/remove-background',
+            data={
+                'image.url': url
+            },
+            auth=(self.k, self.s)
+        )
+
+        if response.status_code == requests.codes.ok:
+            return response.content, ""
+        else:
+            print("response.status_code:", response.status_code)
+            return None, response.content
+
+    def get_no_bg_goods(self, file_path=None, _im=None, key=None):
+        im = _im
+        img = BytesIO()
+        try:
+            im.save(img, format='JPEG')  # format: PNG or JPEG
+        except:
+            im.save(img, format='PNG')  # format: PNG or JPEG
+        img.seek(0)  # rewind to the start
+
+        if key:
+            # 切换key
+            auth = key
+            # auth = (self.k, self.s)
+        else:
+            auth = (self.k, self.s)
+
+
+        try:
+            response = requests.post(
+                'https://api.pixian.ai/api/v2/remove-background',
+                files={'image': img},
+                data={
+                    # Add more upload options here
+                },
+                auth=auth,
+                timeout=40
+            )
+        except BaseException as e:
+            data = {"im": None,
+                    "status_code": "time_out",
+                    "message":"{}".format(e)
+                    }
+            return data
+
+        # print(response.content)
+
+        data = {"im": None,
+                "status_code": response.status_code, }
+
+        if response.status_code == requests.codes.ok:
+            data["im"] = Image.open(BytesIO(response.content))
+
+        return data
+
+
+class Picture:
+    def __init__(self, in_path, im=None):
+        if im:
+            self.im = im
+        else:
+            self.im = Image.open(in_path)
+        self.x, self.y = self.im.size
+        # print(self.x, self.y)
+
+    def save_img(self, outpath, quality=90):
+        # self.im = self.im.convert("RGB")
+        self.im.save(outpath, quality=quality)
+
+    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.BICUBIC)
+        self.x, self.y = self.im.size
+
+    def resize_by_heigh(self, heigh):
+        re_y = int(heigh)
+        re_x = int(self.x * re_y / self.y)
+        self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
+        self.x, self.y = self.im.size
+
+
+class RemoveBgPiXian(object):
+    def __init__(self):
+        self.segment = Segment()
+        self.r = RemoveBgALi()
+
+    def direct_matting_image(self, image):
+        #不能超过32,000,000尺寸的数据
+        x, y = image.size
+        f = False
+        if x * y > 32000000:
+            r = 32000000 / x * y
+            image = image.resize(size=(int(x * r), int(y * r)))
+            f = True
+        pic, _ = self.segment.get_no_bg_goods(file_path=None, _im=image)
+        if not pic:
+            return None, _
+        _img_im = Image.open(BytesIO(pic))  # 阿里返回的抠图结果 已转PIL对象
+        if f:
+            _img_im = _img_im.resize(size=(x, y))
+        return _img_im, ""
+
+    def run_by_image_url(self, url):
+        pic, _ = self.segment.get_no_bg_goods_by_url(url)
+        if pic is not None:
+            _img_im = Image.open(BytesIO(pic))
+            return _img_im, None
+        else:
+            return None, _
+
+    def run_by_image_im(self, im, key):
+        return self.segment.get_no_bg_goods(_im=im, key=key)
+
+    def get_image_cut(self, file_path, out_file_path=None, original_im=None, image_preprocessing=False, is_test=False):
+        if original_im:
+            original_pic = Picture(in_path=None, im=original_im)
+        else:
+            original_pic = Picture(file_path)
+
+        if original_pic.im.mode != "RGB":
+            print("抠图图片不能是PNG")
+            return False, {"data": "抠图图片不能是PNG"}
+
+        if is_test:
+            cut_image = self.r.get_image_cut(file_path=None, out_file_path=None, original_im=original_pic.im)
+            if out_file_path:
+                cut_image.save(out_file_path)
+            return True, {}
+
+        if image_preprocessing:
+            cut_image = self.r.get_image_cut(file_path=None, out_file_path=None, original_im=original_pic.im)
+            image_deal_info = {}
+            x1, y1, x2, y2 = cut_image.getbbox()
+            image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2)
+            o_w, o_h = cut_image.size
+            image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h)
+            # 扩边处理
+            _w, _h = x2 - x1, y2 - y1
+            out_px = 0.06
+            _w, _h = int(out_px * _w), int(out_px * _h)
+            n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h
+            if n_x1 < 0:
+                n_x1 = 0
+            if n_y1 < 0:
+                n_y1 = 0
+            if n_x2 > o_w:
+                n_x2 = o_w
+            if n_y2 > o_h:
+                n_y2 = o_h
+
+            image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2)
+            cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"])
+            image_deal_info["抠图扩边后图片大小"] = cut_image.size
+            image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y)
+
+            # 使用pixian进行抠图
+            second_cut_image, _ = self.direct_matting_image(image=cut_image)
+            if not second_cut_image:
+                return False, {"data": _}
+
+            if second_cut_image.size != image_deal_info["抠图扩边后图片大小"]:
+                print("图片尺寸还原")
+                second_cut_image = second_cut_image.resize(image_deal_info["抠图扩边后图片大小"])
+
+            # 创建空白图片并粘贴回去
+            _img_im = Image.new(mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0))
+            _img_im.paste(second_cut_image, box=(image_deal_info["抠图扩边后位置"][0], image_deal_info["抠图扩边后位置"][1]))
+        else:
+            _img_im = self.direct_matting_image(image=original_pic.im)
+            pass
+
+        if out_file_path:
+            _img_im.save(out_file_path)
+        return True, {}
+
+    def download_picture(self, url, out_path):
+        response = requests.get(url)
+        pic = response.content
+        with open(out_path, 'wb') as f:
+            f.write(pic)
+
+
+if __name__ == '__main__':
+    r = RemoveBgPiXian()
+    path = r"C:\Users\gymmc\Desktop\白底部分品类45度\测试\eva_keai_1.png"
+    out_path = "{}._no_bg-out.png".format(path)
+    r.get_image_cut(path, out_file_path=out_path)

+ 119 - 96
python/service/run_main.py

@@ -1,15 +1,13 @@
 import settings
-from module.view_control.generate_goods_no_detail_pic import detail_func
+from .detail_func import *
 import json
-from 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
+from .base import *
+# from .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
+from .data import DataModeGenerateDetail
 import time
 from PIL import Image
 from io import BytesIO
@@ -17,27 +15,28 @@ import os, re
 from functools import partial
 # from multiprocessing import Process, Queue
 import pickle
-from  base_deal import BaseDealImage
+from  .base_deal import BaseDealImage
+from middleware import UnicornException
 
+class RunMain():
+    # run_end_sign = Signal(dict)
+    # show_dialog_sign = Signal(dict)
+    # show_progress_detail_sign = Signal(str)
 
-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)
+    # # 定义一个信号用于请求显示对话框
+    # show_dialog_signal = Signal()
+    # # 定义一个信号用于返回对话框的结果
+    # dialog_result_signal = Signal(str)
 
     # dialog_result_signal = Signal(str)
 
-    def __init__(self, windows):
+    def __init__(self, windows, token):
         super().__init__()
         self.windows = windows
+        self.token = token
         self.dialog_result = ""
-        self.data_mode_generate_detail = DataModeGenerateDetail()
-        self.dialog_result_signal.connect(self.on_dialog_result)
+        self.data_mode_generate_detail = DataModeGenerateDetail(token=token)
+        # self.dialog_result_signal.connect(self.on_dialog_result)
         self.event = threading.Event()
 
     def set_state(self, state_value: int):
@@ -68,11 +67,10 @@ class RunMain(QThread):
         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)
-
+        # 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)
 
@@ -101,6 +99,7 @@ class RunMain(QThread):
                     f = False
 
         if not f:
+            # raise UnicornException(return_data["message"])
             self.set_state(state_value=2)
             return
 
@@ -114,13 +113,17 @@ class RunMain(QThread):
         return_data["data"]["succeed_folder_list"] = 1
 
         # ==================检查填写的图片视角是否符合要求
-        res = BaseDealImage().getImageOrder(image_order=image_order, resize_image_view=resize_image_view)
+        res = BaseDealImage(token=self.token).getImageOrder(
+            image_order=image_order, resize_image_view=resize_image_view
+        )
         if res['code'] != 0:
             return_data["message"] += "{}\n".format(res['msg'])
+            # raise UnicornException(return_data["message"])
             return return_data
         else:
             # 图片命名顺序
             image_order_list = res['imageOrderList']
+            print("图片命名顺序", image_order_list)
             for goods_art_no_folder_data in all_goods_art_no_folder_data:
                 if goods_art_no_folder_data["label"] != "待处理":
                     continue
@@ -138,7 +141,7 @@ class RunMain(QThread):
                         goods_art_no_folder_data["label"] = "不处理"
 
         # ================检查每个货号文件夹图片数量是否符合要求
-        all_goods_art_no_folder_data, message = BaseDealImage().check_folders_image_amount(all_goods_art_no_folder_data,
+        all_goods_art_no_folder_data, message = BaseDealImage(token=self.token).check_folders_image_amount(all_goods_art_no_folder_data,
                                                                                            image_order_list)
         if message:
             return_data["message"] += "{}\n".format(message)
@@ -163,15 +166,13 @@ class RunMain(QThread):
         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
+        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,
+            #                 }
+            raise UnicornException(return_data["message"])
 
         do_next = False
         text = ""
@@ -237,11 +238,10 @@ class RunMain(QThread):
                                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,
+                               windows=None,
                                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()
+            return new_func()
 
             # self.t = threading.Thread(target=self.do_run_cutout_image,
             #                           kwargs={"all_goods_art_no_folder_data": all_goods_art_no_folder_data,
@@ -256,8 +256,10 @@ class RunMain(QThread):
             # self.t.start()
 
         else:
-            config_data["sign_text"] = "已结束抠图处理"
-            self.run_end_sign.emit(config_data)
+            # config_data["sign_text"] = "已结束抠图处理"
+            # self.run_end_sign.emit(config_data)
+            print("已结束抠图处理")
+            return True
 
     def do_run_cutout_image(self,
                             all_goods_art_no_folder_data,
@@ -268,8 +270,8 @@ class RunMain(QThread):
                             windows,
                             logo_path,
                             config_data):
-
-        BaseDealImage().run_main(all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+        print("BaseDealImage().run_main========>>>>")
+        BaseDealImage(token=self.token).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,
@@ -281,8 +283,9 @@ class RunMain(QThread):
         # ==============完成处理==============
         # self.set_state(state_value=2)
         callback_func("已结束抠图处理")
-        config_data["sign_text"] = "已结束抠图处理"
-        self.run_end_sign.emit(config_data)
+        return True
+        # 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,
@@ -360,12 +363,15 @@ class RunMain(QThread):
         is_use_excel = config_data["is_use_excel"]
         excel_path = config_data["excel_path"]
         temp_class = config_data["temp_class"]
+        goods_art_no = config_data["goods_art_no"]
         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)
+        folder_name_list = get_all_dir_info(
+            image_dir=image_dir, goods_art_no=goods_art_no
+        )
         if not folder_name_list:
             return_data["message"] += "不存在任何货号/编号文件夹\n"
             print("不存在任何货号/编号文件夹")
@@ -386,6 +392,7 @@ class RunMain(QThread):
                         _result = self.data_mode_generate_detail.get_basic_goods_art_data_by_hqt_and_hlm(
                             folder_name_list
                         )
+            # print("is_use_excel")
         else:
             keys = settings.keys
             _result = (
@@ -399,6 +406,7 @@ class RunMain(QThread):
         if _result["code"] == 0:
             remote_data = _result["data"]
         else:
+            print('_result["message"]====>', _result["message"])
             return_data["message"] += _result["message"] + "\n"
             return return_data
         # print(json.dumps(remote_data))
@@ -411,7 +419,7 @@ class RunMain(QThread):
         """
         # 获取所有文件夹基础数据内容  检查不满足要求的文件不满足要求移动到错误文件夹
         need_view_list = temp_class[temp_name].need_view
-        _all_dir_info_data = detail_func.get_all_dir_info_and_pic_info(
+        _all_dir_info_data = get_all_dir_info_and_pic_info(
             image_dir, folder_name_list, need_view_list
         )
         all_dir_info_data = {}
@@ -434,7 +442,7 @@ class RunMain(QThread):
 
         # 结合上述返回结果进行数据组合拼接
         # 返回可能存在的情况:1、存在文件夹不匹配数据,2、存在货号数据缺失
-        goods_no_dict, error_folder_name_list = detail_func.merge_local_and_remote_data(
+        goods_no_dict, error_folder_name_list = merge_local_and_remote_data(
             all_dir_info_data=all_dir_info_data, remote_data=remote_data
         )
         # 文件在系统中没有匹配的结果
@@ -579,6 +587,8 @@ class RunMain(QThread):
                             _goods_no_dict[goods_no] = value  # 需要生成的数据
                         finally_goods_no_need_temps[goods_no].append(__temp_name)
                     else:
+                        # output/2025-04-03/软件-详情图生成/huilima-2/AQN141132
+                        return_data["data"]["config_data"]["out_put_dir"] = _path
                         if detail_is_pass:
                             return_data["message"] += "\n款号:{},模板:{} 已存在".format(goods_no, __temp_name)
                         else:
@@ -637,20 +647,8 @@ class RunMain(QThread):
         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
+            raise UnicornException(data["message"])
+            return False
 
         do_next = False
         if data["message"]:
@@ -686,9 +684,9 @@ class RunMain(QThread):
                                 "button_3": button_3,
                                 "windows": self,
                                 }
-                self.show_dialog_sign.emit(_dialog_dict)
-                # 等待事件被设置
-                self.event.wait()
+                # self.show_dialog_sign.emit(_dialog_dict)
+                # # 等待事件被设置
+                # self.event.wait()
                 print("self.dialog_result", self.dialog_result)
 
                 # my_dialog = DialogShow(
@@ -731,12 +729,14 @@ class RunMain(QThread):
                                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()
+            # self._w_3 = WorkerOneThread(func=new_func, name="_w_3")
+            # self._w_3.start()
+            return new_func
             # threading.Thread(target=self.detail_run_by_thread, kwargs=kwargs).start()
         else:
             config_data["sign_text"] = "已结束详情处理"
-            self.run_end_sign.emit(config_data)
+            # self.run_end_sign.emit(config_data)
+            return 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,
@@ -763,6 +763,7 @@ class RunMain(QThread):
             print("==============_goods_no_dict  打印-end=================")
 
         all_detail_path_list = []
+        out_put_dir_resp = ""
         for goods_no, temp_name_list in finally_goods_no_need_temps.items():
             try:
                 for _temp_name in temp_name_list:
@@ -781,19 +782,27 @@ class RunMain(QThread):
                                 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))
+                    out_put_dir_resp = "{}/{}/{}".format(
+                        out_put_dir, _temp_name, goods_no
+                    )
+                    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)
+                    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,
+                        temp_class=config_data["temp_class"],
+                        target_error_folder=config_data["target_error_folder"],
+                        image_dir=config_data["image_dir"],
+                    )
             except BaseException as e:
                 self.show_progress_detail(
                     "款:{}生成详情异常:{}".format(goods_no, e))
                 print(e)
+                raise UnicornException("款:{}生成详情异常:{}".format(goods_no, e))
 
         # ==============完成处理==============
         self.set_state(state_value=2)
@@ -807,10 +816,12 @@ class RunMain(QThread):
 
         config_data["sign_text"] = "已结束详情处理"
         config_data["all_detail_path_list"] = all_detail_path_list
+        config_data["out_put_dir"] = out_put_dir_resp
+        print("out_put_dir_resp", out_put_dir_resp)
         # 打开文件夹
-        os.startfile(out_put_dir)
+        # os.startfile(out_put_dir)
 
-        self.run_end_sign.emit(config_data)
+        return 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,
@@ -864,7 +875,7 @@ class RunMain(QThread):
                                     if _key in temp_info_data:
                                         temp_info_data[_key] = _key_value
 
-                    print("temp_info_data")
+                    print("temp_info_data11111111111111111111111")
                     print("goods_no:{},_temp_name:{}".format(goods_no, _temp_name))
                     all_detail_path_list.append("{}/{}/".format(out_put_dir, _temp_name, goods_no))
                     # continue
@@ -924,11 +935,23 @@ class RunMain(QThread):
     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
+        print("text=====>", text)
+        return text
+
+    def detail_deal_one_data(
+        self,
+        goods_no,
+        value,
+        out_put_dir,
+        temp_name,
+        assigned_page_list,
+        temp_class,
+        target_error_folder,
+        image_dir,
+    ):
+        # 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
@@ -936,9 +959,9 @@ class RunMain(QThread):
         print("goods_no", goods_no)
         print("模板:", temp_name)
         print("value:", value)
-
+        print("temp_class:", temp_class)
         if settings.IS_TEST:
-            d = self.windows.temp_class[temp_name](goods_no, value,
+            temp_class[temp_name](goods_no, value,
                                                    out_put_dir=out_put_dir,
                                                    windows=self.windows,
                                                    assigned_page_list=assigned_page_list)
@@ -946,7 +969,7 @@ class RunMain(QThread):
         else:
             try:
                 # # 处理图片详情图生成
-                d = self.windows.temp_class[temp_name](goods_no, value,
+                temp_class[temp_name](goods_no, value,
                                                        out_put_dir=out_put_dir,
                                                        windows=self.windows,
                                                        assigned_page_list=assigned_page_list)
@@ -969,22 +992,22 @@ class RunMain(QThread):
                 "相关货号:{}".format([x["货号"] for x in goods_art_no_list])
             )
             # 将相关的文件夹统一移动至错误文件夹
-            detail_func.move_folders(
+            move_folders(
                 path_list=[
-                    "{}/{}".format(self.windows.image_dir, x)
+                    "{}/{}".format(image_dir, x)
                     for x in [x["文件夹名称"] for x in goods_art_no_list]
                 ],
-                target_folder=self.windows.target_error_folder,
+                target_folder=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),
-            }
-        )
+        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):

+ 11 - 10
python/service/upload_pic.py

@@ -1,6 +1,5 @@
-from import_qt_mode import *
-from module.view_control.generate_goods_no_detail_pic.data import DataModeUploadPic
-from module.view_control.MineQWidget import DialogShow, WorkerOneThread
+from .data import DataModeUploadPic
+# from module.view_control.MineQWidget import DialogShow, WorkerOneThread
 import os
 import threading
 import time
@@ -11,10 +10,10 @@ from io import BytesIO
 
 
 # 详情图上传
-class UploadPic(QObject):
-    signal_data = Signal(dict)
-    run_end_sign = Signal(dict)
-    show_progress_detail_sign = Signal(str)
+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):
         super().__init__()
@@ -36,11 +35,13 @@ class UploadPic(QObject):
     def run_by_thread(self):
         self.run_by_thread_real()
         self.config_data["sign_text"] = "结束"
-        self.run_end_sign.emit(self.config_data)
+        # self.run_end_sign.emit(self.config_data)
+        self.config_data
 
     def run(self):
-        self._w_5 = WorkerOneThread(func=self.run_by_thread, name="_w_5")
-        self._w_5.start()
+        pass
+        # self._w_5 = WorkerOneThread(func=self.run_by_thread, name="_w_5")
+        # self._w_5.start()
 
     def run_by_thread_real(self, *args):
         self.show_info("====开始处理====")

+ 80 - 4
python/settings.py

@@ -1,6 +1,34 @@
 from dotenv import load_dotenv, find_dotenv
 from pathlib import Path  # Python 3.6+ only
-import configparser
+import configparser, json
+
+from numpy import true_divide
+from databases import (
+    create_all_database,
+    DeviceConfig,
+    SysConfigs,
+    CRUD,
+    batch_insert_sys_configs,
+    SqlQuery,
+    batch_insert_device_configs,
+)
+# 初始化数据表
+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)
+# 初始化数据表---结束
+
 def get_config_by_items(config_dict):
     __config_dict = {}
     for i, k in config_dict:
@@ -8,16 +36,28 @@ def get_config_by_items(config_dict):
     return __config_dict
 
 
+def getSysConfigs(key,item,default=None):
+    session = SqlQuery()
+    crud = CRUD(SysConfigs)
+    one_item = crud.read(session, conditions={"key": key})
+    config = json.loads(one_item.value)
+    return config.get(item, default)
+
 def get_dict_value(_dict, key, default=None):
     if key in _dict:
         return _dict[key]
     else:
         return default
-MACHINE_LEVEL = "二档"
+
+MACHINE_LEVEL = getSysConfigs("other_configs", "device_speed", "二档")
 IS_TEST = False
 IS_MCU = True
 IS_LIN_SHI_TEST = False
-PhotographSeconds = float(0.3)  # 拍照停留时间
+PhotographSeconds = float(
+    getSysConfigs("take_photo_configs", "camera_delay", "0.5")
+)  # 拍照停留时间
+
+
 def moveSpeed(level: str = None):
     config = {
         "一档": {
@@ -92,7 +132,7 @@ BACKUP_COUNTS = config.get("log", "backup_counts")
 # 远程服务器地址
 HLM_HOST = config.get("log", "hlm_host")
 
-PROJECT = config.get("log", "project")
+PROJECT = config.get("app", "project")
 
 
 # ----------------------------------
@@ -116,3 +156,39 @@ NEXT_STEP = int(get_dict_value(_config_mcu_config, "next_step", 6))  # 下一步
 MOVE_UP = _mcu_config_dict["move_up"]
 MOVE_DOWN = _mcu_config_dict["move_down"]
 STOP = _mcu_config_dict["stop"]
+
+
+DOMAIN = (
+    "https://dev2.valimart.net"
+    if config.get("app", "debug") == "false"
+    else "https://dev2.pubdata.cn"
+)
+
+
+Company = "惠利玛"
+
+is_test_plugins = true_divide
+
+
+OUT_PIC_MODE = "."+getSysConfigs("basic_configs", "image_out_format", "png")  # ".png"
+
+OUT_PIC_SIZE = int(getSysConfigs("basic_configs", "main_image_size", "800"))  # 主图大小
+
+
+Mode = getSysConfigs("other_configs", "product_type", "鞋类")  # 程序执行类
+OUT_PIC_FACTOR = float(
+    getSysConfigs("basic_configs", "image_sharpening", "1")
+)  # 图片锐化
+
+ 
+RESIZE_IMAGE_MODE = 1
+
+GRENERATE_MAIN_PIC_BRIGHTNESS = 254  # 色阶是否调整到位判断
+
+RUNNING_MODE = getSysConfigs("other_configs", "running_mode", "普通模式")
+
+
+DEFAULT_CUTOUT_MODE = getSysConfigs("other_configs", "cutout_mode", "普通抠图")
+
+
+CUTOUT_MODE = 0 if getSysConfigs("other_configs", "cutout_mode", "普通抠图") =="普通抠图" else 1

+ 103 - 0
python/utils/utils_func.py

@@ -56,6 +56,7 @@ async def download_file(url, file_path):
 def check_path(_path):
     if not os.path.exists(_path):
         # 创建多级目录
+        print("创建目录", _path)
         os.makedirs(_path, exist_ok=True)
         # os.mkdir(_path)
     return True
@@ -137,3 +138,105 @@ def httpPosthandler(url, data=None, json=None, headers=None):
     except requests.exceptions.RequestException as e:
         print(f"POST请求失败: {e}")
         return None
+
+
+# 指定一个列表,指定一个需要移动的数据列表,移动到指定列表中的位置
+def move_elements_inplace(lst, indices, target_index, is_real_same_folder=False):
+    """
+    在原地将列表中指定位置的两个元素移动到目标位置。
+
+    :param lst: 要操作的列表
+    :param indices: 一个包含两个整数的元组或列表,表示要移动的元素的索引
+    :param target_index: 目标插入位置的索引
+    """
+
+    if not isinstance(indices, (list, tuple)) or len(indices) == 0:
+        raise ValueError("Indices must be a non-empty list or tuple of integers.")
+
+    # 添加到末尾
+    if target_index == len(lst):
+        if is_real_same_folder:
+            # 倒序取出所有内容,并添加到末尾
+            indices.sort(reverse=True)
+            temp = []
+            for i in indices:
+                temp.append(lst.pop(i))
+            while temp:
+                lst.append(temp.pop(-1))
+
+        # 重新写入索引
+        for index, image_label in enumerate(lst):
+            image_label.image_index = index
+        return
+
+    # 检查索引是否有效,并排序以确保按照正确的顺序处理
+    valid_indices = sorted(set(indices))  # 去重并排序
+    if not all(0 <= idx < len(lst) for idx in valid_indices):
+        raise IndexError("One or more indices are out of range.")
+
+    if not (0 <= target_index <= len(lst)):
+        raise IndexError("Target index is out of range.")
+
+    elements_to_move = [lst[idx] for idx in valid_indices]
+
+    # 如果目标位置在所有待移动元素的最大索引之后,则不需要调整目标位置
+    max_idx = max(valid_indices)
+    if max_idx < target_index:
+        pass
+    else:
+        # 计算需要减少的位置数(因为删除元素后,后续元素会向前移动)
+        shift_count = sum(1 for idx in valid_indices if idx < target_index)
+        target_index -= shift_count
+
+    # 移除元素(从大索引开始以避免影响小索引处的元素)
+    for idx in reversed(valid_indices):
+        del lst[idx]
+
+    # 插入元素到目标位置,保持它们原来的相对顺序
+    for i, element in enumerate(elements_to_move):
+        lst.insert(target_index + i, element)
+
+    # 重新写入索引
+    for index, image_label in enumerate(lst):
+        image_label.image_index = index
+
+
+# 给定一个图片路径,如果 是原始图下的则返回对应已扣图等信息。否则只返回基础信息
+def get_cutout_image_info(source_image_path):
+    if not os.path.exists(source_image_path):
+        return None
+    if os.path.isdir(source_image_path):
+        return None
+
+    data = {}
+    source_root_path, source_file = os.path.split(source_image_path)
+    source_file_name, source_file_extension = os.path.splitext(source_file)
+    print("---------------source_root_path--------------------")
+    print(source_root_path)
+    print(source_root_path[-3:])
+    # if source_root_path[-3:] != "原始图":
+    #     return None
+
+    data["source_image_path"] = source_image_path
+    data["source_root_path"] = source_root_path
+    data["source_file"] = source_file
+    data["source_file_name"] = source_file_name
+    data["source_file_extension"] = source_file_extension
+
+    cutout_image_path = "{}/原始图_已抠图/{}.png".format(
+        source_root_path[:-3], source_file_name
+    )
+    if not os.path.exists(cutout_image_path):
+        data["cutout_image_path"] = None
+        return data
+
+    cutout_image_root_path, cutout_image_file = os.path.split(cutout_image_path)
+    cutout_image_file_name, cutout_image_file_extension = os.path.splitext(
+        cutout_image_file
+    )
+    data["cutout_image_path"] = cutout_image_path
+    data["cutout_image_root_path"] = cutout_image_root_path
+    data["cutout_image_file"] = cutout_image_file
+    data["cutout_image_file_name"] = cutout_image_file_name
+    data["cutout_image_file_extension"] = cutout_image_file_extension
+    return data