pyinstaller

This commit is contained in:
9700X_PC 2024-11-14 23:52:38 +09:00
parent c41b669dd8
commit c95c671294
18 changed files with 111 additions and 98 deletions

View File

@ -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/1.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

BIN
img/2.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

BIN
img/3.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

BIN
img/4.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

BIN
img/5.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

BIN
img/6.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -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}")

View File

@ -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
View File

@ -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 생성

69
main.spec Normal file
View File

@ -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', # 생성된 디렉터리 이름
)

View File

@ -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.

View File

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