pyinstaller
This commit is contained in:
parent
c41b669dd8
commit
c95c671294
|
|
@ -7,8 +7,9 @@ from translatepy import Translator
|
||||||
from translatepy.translators.google import GoogleTranslate
|
from translatepy.translators.google import GoogleTranslate
|
||||||
|
|
||||||
class DatabaseManager:
|
class DatabaseManager:
|
||||||
def __init__(self, db_path, logger):
|
def __init__(self, base_path, db_path, logger):
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
self.base_path = base_path
|
||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
self.conn = sqlite3.connect(db_path, check_same_thread=False) # 스레드 간 공유 허용
|
self.conn = sqlite3.connect(db_path, check_same_thread=False) # 스레드 간 공유 허용
|
||||||
self.logger.info("Database connection established.")
|
self.logger.info("Database connection established.")
|
||||||
|
|
@ -38,7 +39,7 @@ class DatabaseManager:
|
||||||
filter_cat.ini 파일에서 제외 카테고리를 읽어옵니다.
|
filter_cat.ini 파일에서 제외 카테고리를 읽어옵니다.
|
||||||
"""
|
"""
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
ini_path = os.path.join(os.getcwd(), "filter_cat.ini")
|
ini_path = os.path.join(self.base_path, "filter_cat.ini")
|
||||||
|
|
||||||
if not os.path.exists(ini_path):
|
if not os.path.exists(ini_path):
|
||||||
self.logger.warning(f"filter_cat.ini not found at {ini_path}. No categories will be excluded.")
|
self.logger.warning(f"filter_cat.ini not found at {ini_path}. No categories will be excluded.")
|
||||||
|
|
|
||||||
BIN
img/5_0.jpg
BIN
img/5_0.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
BIN
img/6_1.jpg
BIN
img/6_1.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
BIN
img/6_2.jpg
BIN
img/6_2.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
img/6_3.jpg
BIN
img/6_3.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
|
|
@ -10,7 +10,8 @@ import numpy as np
|
||||||
import sys, os, random
|
import sys, os, random
|
||||||
|
|
||||||
class BaiduImageSearcher:
|
class BaiduImageSearcher:
|
||||||
def __init__(self, sources=None, image_downloader=None, db_manager=None, logger=None):
|
def __init__(self, base_path, sources=None, image_downloader=None, db_manager=None, logger=None):
|
||||||
|
self.base_path = base_path
|
||||||
self.filtered_sources = set(sources) if sources else {'淘宝', 'tmall', '1688'}
|
self.filtered_sources = set(sources) if sources else {'淘宝', 'tmall', '1688'}
|
||||||
self.image_downloader = image_downloader
|
self.image_downloader = image_downloader
|
||||||
self.db_manager = db_manager
|
self.db_manager = db_manager
|
||||||
|
|
@ -26,16 +27,9 @@ class BaiduImageSearcher:
|
||||||
|
|
||||||
self.playwright = sync_playwright().start()
|
self.playwright = sync_playwright().start()
|
||||||
|
|
||||||
|
browser_path = os.path.join(self.base_path, 'src', 'browsers', 'chromium-1112', 'chrome-win','chrome.exe')
|
||||||
# cx_Freeze로 패키징된 경우와 일반 Python 실행 환경 구분하여 경로 설정
|
browser_webkit_path = os.path.join(self.base_path, 'src', 'browsers', 'webkit-2083', 'Playwright.exe')
|
||||||
if getattr(sys, 'frozen', False):
|
user_data_dir = os.path.join(self.base_path, 'src', 'browsers', 'user_data')
|
||||||
browser_path = os.path.join(os.path.dirname(sys.executable), 'src', 'browsers', 'chromium-1112', 'chrome-win','chrome.exe')
|
|
||||||
# browser_path = os.path.join(os.path.dirname(sys.executable), 'src', 'browsers', 'webkit-2083', 'Playwright.exe')
|
|
||||||
user_data_dir = os.path.join(os.path.dirname(sys.executable), 'src', 'browsers', 'user_data')
|
|
||||||
else:
|
|
||||||
browser_path = os.path.join(os.path.dirname(__file__), 'src', 'browsers', 'chromium-1112', 'chrome-win','chrome.exe')
|
|
||||||
# browser_path = os.path.join(os.path.dirname(__file__), 'src', 'browsers', 'webkit-2083', 'Playwright.exe')
|
|
||||||
user_data_dir = os.path.join(os.path.dirname(__file__), 'src', 'browsers', 'user_data')
|
|
||||||
|
|
||||||
self.logger.debug(f"브라우저 경로: {browser_path}")
|
self.logger.debug(f"브라우저 경로: {browser_path}")
|
||||||
self.logger.debug(f"사용자 폴더 경로: {user_data_dir}")
|
self.logger.debug(f"사용자 폴더 경로: {user_data_dir}")
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,23 @@ def setup_logger(log_file='app.log', log_level=logging.DEBUG):
|
||||||
logger = logging.getLogger("MainLogger")
|
logger = logging.getLogger("MainLogger")
|
||||||
logger.setLevel(log_level)
|
logger.setLevel(log_level)
|
||||||
|
|
||||||
|
# 기존 핸들러 제거
|
||||||
|
if logger.hasHandlers():
|
||||||
|
logger.handlers.clear()
|
||||||
|
|
||||||
# 콘솔 핸들러 설정
|
# 콘솔 핸들러 설정
|
||||||
console_handler = logging.StreamHandler()
|
console_handler = logging.StreamHandler()
|
||||||
console_handler.setLevel(log_level)
|
console_handler.setLevel(log_level)
|
||||||
console_format = logging.Formatter('[%(levelname)s] %(message)s')
|
console_format = logging.Formatter('[%(levelname)s] %(message)s')
|
||||||
console_handler.setFormatter(console_format)
|
console_handler.setFormatter(console_format)
|
||||||
console_handler.stream.reconfigure(encoding='utf-8') # 콘솔 핸들러에서 UTF-8로 인코딩 설정
|
|
||||||
|
|
||||||
# 파일 핸들러 설정
|
# 파일 핸들러 설정
|
||||||
file_handler = logging.FileHandler("app.log", encoding="utf-8")
|
log_file_path = os.path.join(os.getcwd(), log_file)
|
||||||
|
file_handler = logging.FileHandler(log_file_path, encoding="utf-8")
|
||||||
file_handler.setLevel(log_level)
|
file_handler.setLevel(log_level)
|
||||||
file_format = logging.Formatter('%(asctime)s - %(name)s - [%(levelname)s] - %(message)s')
|
file_format = logging.Formatter('%(asctime)s - %(name)s - [%(levelname)s] - %(message)s')
|
||||||
file_handler.setFormatter(file_format)
|
file_handler.setFormatter(file_format)
|
||||||
file_handler.stream.reconfigure(encoding='utf-8') # 콘솔 핸들러에서 UTF-8로 인코딩 설정
|
|
||||||
|
|
||||||
# 핸들러 추가
|
# 핸들러 추가
|
||||||
logger.addHandler(console_handler)
|
logger.addHandler(console_handler)
|
||||||
logger.addHandler(file_handler)
|
logger.addHandler(file_handler)
|
||||||
|
|
|
||||||
26
main.py
26
main.py
|
|
@ -7,15 +7,30 @@ from logger_module import setup_logger
|
||||||
from mainWindow import MainWindow
|
from mainWindow import MainWindow
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
def get_base_path():
|
||||||
|
"""
|
||||||
|
실행 파일이 위치한 경로를 반환.
|
||||||
|
- PyInstaller로 패키징된 경우와 개발 환경 모두 지원.
|
||||||
|
"""
|
||||||
|
if getattr(sys, 'frozen', False): # PyInstaller로 패키징된 실행 환경
|
||||||
|
return os.path.dirname(sys.executable) # 실행 파일 위치
|
||||||
|
return os.path.dirname(os.path.abspath(__file__)) # 개발 환경
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# 로그 설정
|
# 로그 설정
|
||||||
logger = setup_logger(log_file='SellFree_Soucer.log', log_level=logging.DEBUG)
|
base_path = get_base_path()
|
||||||
|
log_file = os.path.join(base_path, 'SellFree_Soucer.log')
|
||||||
|
logger = setup_logger(log_file=log_file, log_level=logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
# 폴더 및 파일 경로 설정
|
# 폴더 및 파일 경로 설정
|
||||||
excel_folder = os.path.join(os.getcwd(), 'xls')
|
excel_folder = os.path.join(base_path, 'xls') # xls 폴더
|
||||||
img_folder = os.path.join(os.getcwd(), 'img')
|
img_folder = os.path.join(base_path, 'img') # img 폴더
|
||||||
db_path = os.path.join(os.getcwd(), 'search_products.db')
|
db_path = os.path.join(base_path, 'search_products.db') # 데이터베이스 파일
|
||||||
|
|
||||||
|
# 폴더가 없으면 생성
|
||||||
|
os.makedirs(excel_folder, exist_ok=True)
|
||||||
|
os.makedirs(img_folder, exist_ok=True)
|
||||||
|
|
||||||
# 프로그램 시작 시 이미지 폴더와 데이터베이스 파일 정리
|
# 프로그램 시작 시 이미지 폴더와 데이터베이스 파일 정리
|
||||||
print("Clearing image folder and database file...")
|
print("Clearing image folder and database file...")
|
||||||
|
|
@ -28,14 +43,13 @@ def main():
|
||||||
os.remove(db_path)
|
os.remove(db_path)
|
||||||
print(f"Deleted database file: {db_path}")
|
print(f"Deleted database file: {db_path}")
|
||||||
|
|
||||||
|
|
||||||
# PySide6 앱 초기화
|
# PySide6 앱 초기화
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
main_window = MainWindow()
|
main_window = MainWindow()
|
||||||
|
|
||||||
# MainProcessor와 QThread 설정
|
# MainProcessor와 QThread 설정
|
||||||
processor_thread = QThread()
|
processor_thread = QThread()
|
||||||
processor = MainProcessor(excel_folder, db_path, img_folder, main_window, logger)
|
processor = MainProcessor(base_path, excel_folder, db_path, img_folder, main_window, logger)
|
||||||
processor.moveToThread(processor_thread)
|
processor.moveToThread(processor_thread)
|
||||||
|
|
||||||
# ProductViewer 생성
|
# ProductViewer 생성
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
from PyInstaller.utils.hooks import collect_data_files
|
||||||
|
import os
|
||||||
|
|
||||||
|
# src 폴더의 모든 파일 포함
|
||||||
|
src_data = []
|
||||||
|
src_folder = "src"
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(src_folder):
|
||||||
|
for file in files:
|
||||||
|
# 파일 경로를 src 내부 상대 경로로 추가
|
||||||
|
full_path = os.path.join(root, file)
|
||||||
|
relative_path = os.path.relpath(full_path, src_folder)
|
||||||
|
src_data.append((full_path, os.path.join('src', relative_path)))
|
||||||
|
|
||||||
|
# 분석 설정
|
||||||
|
a = Analysis(
|
||||||
|
['main.py'], # 메인 스크립트 파일
|
||||||
|
pathex=[], # 추가 탐색 경로 (필요하면 추가 가능)
|
||||||
|
binaries=[], # 바이너리 파일 (필요시 추가)
|
||||||
|
datas=[
|
||||||
|
('filter_cat.ini', '.'), # ini 파일
|
||||||
|
('xls', 'xls'), # xls 폴더
|
||||||
|
('img', 'img'), # img 폴더
|
||||||
|
], # 추가할 데이터 파일 및 폴더
|
||||||
|
hiddenimports=['skimage.exposure'],
|
||||||
|
hookspath=[], # 추가 hook 파일 경로
|
||||||
|
runtime_hooks=[], # 런타임 hook
|
||||||
|
excludes=[], # 제외할 모듈
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False, # 압축하지 않음
|
||||||
|
)
|
||||||
|
|
||||||
|
# 압축 관련 설정
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
# 실행 파일 설정
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='SellFree소서', # 생성된 실행 파일 이름
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True, # UPX 압축 사용
|
||||||
|
console=False, # 콘솔 창 숨기기
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 파일 수집 설정
|
||||||
|
coll = COLLECT(
|
||||||
|
exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
name='SellFreeSourcer', # 생성된 디렉터리 이름
|
||||||
|
)
|
||||||
|
|
@ -6,7 +6,7 @@ from imgSearcher import BaiduImageSearcher
|
||||||
from resultDiag import ProductViewer
|
from resultDiag import ProductViewer
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from PySide6.QtCore import QObject, Signal, QThread
|
from PySide6.QtCore import QObject, Signal
|
||||||
import xlwings as xw
|
import xlwings as xw
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -15,31 +15,18 @@ class MainProcessor(QObject):
|
||||||
log_message_signal = Signal(str) # 로그 메시지
|
log_message_signal = Signal(str) # 로그 메시지
|
||||||
finished_signal = Signal() # 작업 완료 시그널
|
finished_signal = Signal() # 작업 완료 시그널
|
||||||
|
|
||||||
def __init__(self, excel_folder, db_path, img_folder, main_window, logger):
|
def __init__(self, base_path, excel_folder, db_path, img_folder, main_window, logger):
|
||||||
super().__init__() # QObject 초기화
|
super().__init__() # QObject 초기화
|
||||||
|
self.base_path = base_path
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.excel_reader = ExcelReader(excel_folder, logger)
|
self.excel_reader = ExcelReader(excel_folder, logger)
|
||||||
self.db_manager = DatabaseManager(db_path, logger)
|
self.db_manager = DatabaseManager(self.base_path, db_path, logger)
|
||||||
self.resultViewer = ProductViewer(self.db_manager, logger)
|
self.resultViewer = ProductViewer(self.db_manager, logger)
|
||||||
self.image_downloader = ImageDownloader(img_folder, logger)
|
self.image_downloader = ImageDownloader(img_folder, logger)
|
||||||
self.image_searcher = BaiduImageSearcher(sources=['淘宝', 'tmall', '1688'], image_downloader=self.image_downloader, db_manager=self.db_manager, logger=logger)
|
self.image_searcher = BaiduImageSearcher(self.base_path, sources=['淘宝', 'tmall', '1688'], image_downloader=self.image_downloader, db_manager=self.db_manager, logger=logger)
|
||||||
self.logger.info("MainProcessor initialized.")
|
self.logger.info("MainProcessor initialized.")
|
||||||
|
|
||||||
def clean_up_files(self):
|
|
||||||
img_folder = os.path.join(os.getcwd(), 'img')
|
|
||||||
xls_folder = os.path.join(os.getcwd(), 'xls')
|
|
||||||
|
|
||||||
for folder in [img_folder, xls_folder]:
|
|
||||||
for filename in os.listdir(folder):
|
|
||||||
file_path = os.path.join(folder, filename)
|
|
||||||
try:
|
|
||||||
if os.path.isfile(file_path):
|
|
||||||
os.remove(file_path)
|
|
||||||
self.logger.info(f"Deleted file: {file_path}")
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error deleting file {file_path}: {e}")
|
|
||||||
|
|
||||||
def process_all_products(self):
|
def process_all_products(self):
|
||||||
try:
|
try:
|
||||||
# Spinbox의 가격 필터링 기준값 가져오기
|
# Spinbox의 가격 필터링 기준값 가져오기
|
||||||
|
|
@ -180,7 +167,7 @@ class MainProcessor(QObject):
|
||||||
|
|
||||||
# 날짜와 파일명 설정
|
# 날짜와 파일명 설정
|
||||||
date_str = datetime.now().strftime('%Y%m%d')
|
date_str = datetime.now().strftime('%Y%m%d')
|
||||||
base_filename = os.path.join(os.getcwd(), "src", "BaseXLS.xlsx")
|
base_filename = os.path.join(self.base_path, "src", "BaseXLS.xlsx")
|
||||||
|
|
||||||
if not os.path.exists(base_filename):
|
if not os.path.exists(base_filename):
|
||||||
self.logger.error(f"BaseXLS.xlsx 파일이 존재하지 않습니다: {base_filename}")
|
self.logger.error(f"BaseXLS.xlsx 파일이 존재하지 않습니다: {base_filename}")
|
||||||
|
|
@ -244,4 +231,4 @@ class MainProcessor(QObject):
|
||||||
self.log_message_signal.emit(f"{output_filename} 파일에 데이터 50개 저장 완료.")
|
self.log_message_signal.emit(f"{output_filename} 파일에 데이터 50개 저장 완료.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error exporting to Excel with template: {e}", exc_info=True)
|
self.logger.error(f"Error exporting to Excel with template: {e}", exc_info=True)
|
||||||
Binary file not shown.
55
setup.py
55
setup.py
|
|
@ -1,55 +0,0 @@
|
||||||
from cx_Freeze import setup, Executable
|
|
||||||
import os
|
|
||||||
|
|
||||||
# 파일 및 폴더 포함 설정
|
|
||||||
include_files = [
|
|
||||||
'filter_cat.ini', # 필수 설정 파일
|
|
||||||
'src/', # src 폴더 전체
|
|
||||||
'xlwings32-0.31.10.dll', # 필수 DLL
|
|
||||||
'xlwings64-0.31.10.dll', # 필수 DLL
|
|
||||||
('img', 'img'), # 빈 img 폴더 생성
|
|
||||||
('xls', 'xls'), # 빈 xls 폴더 생성
|
|
||||||
]
|
|
||||||
|
|
||||||
# 모듈 제외 및 추가 설정
|
|
||||||
excludes = [
|
|
||||||
"tkinter", "unittest", "PyQt4", "PyQt5", "scipy",
|
|
||||||
"matplotlib", "numba", "sklearn", "pytest"
|
|
||||||
]
|
|
||||||
packages = [
|
|
||||||
"os", "sys", "requests", "numpy", "pandas", "sqlite3",
|
|
||||||
"PySide6", "playwright", "bs4", "PIL", "xlwings"
|
|
||||||
]
|
|
||||||
includes = [
|
|
||||||
"logging", "time", "re", "configparser"
|
|
||||||
]
|
|
||||||
include_files += [
|
|
||||||
"C:/Windows/System32/api-ms-win-crt-runtime-l1-1-0.dll",
|
|
||||||
"C:/Windows/System32/api-ms-win-crt-string-l1-1-0.dll",
|
|
||||||
"C:/Windows/System32/api-ms-win-crt-stdio-l1-1-0.dll"
|
|
||||||
]
|
|
||||||
|
|
||||||
# 실행 파일 설정
|
|
||||||
executables = [
|
|
||||||
Executable(
|
|
||||||
"main.py",
|
|
||||||
base="Win32GUI" if os.name == "nt" else None, # GUI 애플리케이션이라면 Win32GUI 사용
|
|
||||||
target_name="my_application.exe",
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# setup.py 구성
|
|
||||||
setup(
|
|
||||||
name="MyApplication",
|
|
||||||
version="1.0",
|
|
||||||
description="Description of My Application",
|
|
||||||
options={
|
|
||||||
"build_exe": {
|
|
||||||
"includes": includes,
|
|
||||||
"packages": packages,
|
|
||||||
"include_files": include_files,
|
|
||||||
"excludes": excludes,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
executables=executables
|
|
||||||
)
|
|
||||||
Loading…
Reference in New Issue