수정중
|
|
@ -9,24 +9,28 @@ class DatabaseManager:
|
|||
self._create_tables()
|
||||
|
||||
def _create_tables(self):
|
||||
# 상품 테이블과 검색 결과 테이블 생성
|
||||
try:
|
||||
with self.conn:
|
||||
# products 테이블 생성
|
||||
self.conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS products (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT,
|
||||
image_url TEXT,
|
||||
tag TEXT,
|
||||
price INTER,
|
||||
price INTEGER,
|
||||
category TEXT,
|
||||
percent_category TEXT,
|
||||
selected_search_result_id INTEGER, -- 선택된 search_results ID
|
||||
percenty_category TEXT,
|
||||
selected_search_result_id INTEGER,
|
||||
saved_img_path TEXT, -- 이미지 저장 경로 필드 추가
|
||||
FOREIGN KEY(selected_search_result_id) REFERENCES search_results(id)
|
||||
)
|
||||
''')
|
||||
|
||||
# search_results 테이블에 고유 ID 추가
|
||||
self.conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS search_results (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
product_id INTEGER,
|
||||
title TEXT,
|
||||
source TEXT,
|
||||
|
|
@ -34,7 +38,8 @@ class DatabaseManager:
|
|||
imgurl TEXT,
|
||||
encrypted_url TEXT,
|
||||
original_url TEXT,
|
||||
is_selected BOOLEAN DEFAULT 0, -- 선택 여부 필드
|
||||
saved_img_path TEXT, -- 이미지 저장 경로 필드 추가
|
||||
is_selected BOOLEAN DEFAULT 0,
|
||||
FOREIGN KEY (product_id) REFERENCES products (id)
|
||||
)
|
||||
''')
|
||||
|
|
@ -49,11 +54,10 @@ class DatabaseManager:
|
|||
with self.conn:
|
||||
for product in products:
|
||||
cursor = self.conn.execute('''
|
||||
INSERT INTO products (name, image_url, tag, price, category, percent_category)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
''', (product['name'], product['image_url'], product['tag'], product['price'], product['category'], product['percent_category']))
|
||||
INSERT INTO products (name, image_url, tag, price, category, percenty_category, saved_img_path)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
''', (product['name'], product['image_url'], product['tag'], product['price'], product['category'], product['percenty_category'], product.get('saved_img_path')))
|
||||
|
||||
# 각 삽입된 행의 product_id 추출
|
||||
product_id = cursor.lastrowid
|
||||
product_ids.append(product_id)
|
||||
self.logger.debug(f"Inserted product with ID: {product_id}")
|
||||
|
|
@ -64,36 +68,49 @@ class DatabaseManager:
|
|||
self.logger.error(f"Error inserting products: {e}", exc_info=True)
|
||||
return []
|
||||
|
||||
def update_product_image_path(self, product_id, image_path):
|
||||
"""특정 상품의 saved_img_path 필드만 업데이트"""
|
||||
try:
|
||||
with self.conn:
|
||||
self.conn.execute('''
|
||||
UPDATE products
|
||||
SET saved_img_path = ?
|
||||
WHERE id = ?
|
||||
''', (image_path, product_id))
|
||||
self.logger.info(f"Updated saved_img_path for Product ID [{product_id}]")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error updating saved_img_path for Product ID [{product_id}]: {e}", exc_info=True)
|
||||
|
||||
def insert_search_results(self, product_id, search_results):
|
||||
# 검색 결과 중 상위 5개만 DB에 저장
|
||||
try:
|
||||
top_results = search_results[:5] # 상위 5개로 제한
|
||||
with self.conn:
|
||||
self.conn.executemany('''
|
||||
INSERT INTO search_results (product_id, title, source, price, imgurl, encrypted_url, original_url)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
''', [(product_id, result['title'], result['source'], result['price'], result['imgurl'], result['encrypted_url'], result['original_url']) for result in top_results])
|
||||
INSERT INTO search_results (product_id, title, source, price, imgurl, saved_img_path, encrypted_url, original_url)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', [(product_id, result['title'], result['source'], result['price'], result['imgurl'], result['saved_img_path'], result['encrypted_url'], result['original_url']) for result in top_results])
|
||||
self.logger.info(f"Product ID [{product_id}]: {len(top_results)} search results inserted into database.")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error inserting search results for Product ID [{product_id}]: {e}", exc_info=True)
|
||||
|
||||
|
||||
def select_search_result_for_product(self, product_id, search_result_id):
|
||||
# products 테이블에 선택된 search_result_id 업데이트
|
||||
try:
|
||||
with self.conn:
|
||||
# products 테이블의 selected_search_result_id 업데이트
|
||||
self.conn.execute('''
|
||||
UPDATE products
|
||||
SET selected_search_result_id = ?
|
||||
WHERE id = ?
|
||||
''', (search_result_id, product_id))
|
||||
|
||||
# search_results 테이블의 is_selected 업데이트
|
||||
# search_results 테이블의 선택된 항목 업데이트
|
||||
self.conn.execute('''
|
||||
UPDATE search_results
|
||||
SET is_selected = 1
|
||||
WHERE id = ?
|
||||
''', (search_result_id,))
|
||||
|
||||
SET is_selected = CASE WHEN id = ? THEN 1 ELSE 0 END
|
||||
WHERE product_id = ?
|
||||
''', (search_result_id, product_id))
|
||||
|
||||
self.logger.info(f"Product ID [{product_id}] selected search result ID [{search_result_id}].")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error selecting search result for product ID [{product_id}]: {e}", exc_info=True)
|
||||
|
|
@ -123,7 +140,7 @@ class DatabaseManager:
|
|||
try:
|
||||
query = '''
|
||||
SELECT p.name as 상품명, sr.original_url as 원본링크, sr.price as 가격,
|
||||
sr.source as 출처, p.percent_category as 퍼센티카테고리,
|
||||
sr.source as 출처, p.percenty_category as 퍼센티카테고리,
|
||||
sr.title as 이미지검색결과의상품명, sr.imgurl as 이미지URL
|
||||
FROM products p
|
||||
LEFT JOIN search_results sr ON p.id = sr.product_id
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ class ImageDownloader:
|
|||
"Cache-Control": "max-age=0"
|
||||
}
|
||||
|
||||
|
||||
|
||||
def download_image(self, image_url, product_id):
|
||||
# 임시 폴더에 이미지 다운로드
|
||||
try:
|
||||
|
|
@ -35,3 +33,19 @@ class ImageDownloader:
|
|||
self.logger.warning(f"Failed to download image: {e}", exc_info=True)
|
||||
|
||||
|
||||
def download_image_for_searchResult(self, img_url, product_id, index):
|
||||
"""이미지 URL에서 이미지를 다운로드하고 로컬 경로를 반환합니다."""
|
||||
try:
|
||||
response = requests.get(img_url, stream=True)
|
||||
if response.status_code == 200:
|
||||
file_path = os.path.join(self.temp_folder, f"{product_id}_{index}.jpg")
|
||||
with open(file_path, "wb") as file:
|
||||
for chunk in response.iter_content(1024):
|
||||
file.write(chunk)
|
||||
self.logger.info(f"Downloaded image for product ID {product_id} at index {index}")
|
||||
return file_path
|
||||
else:
|
||||
self.logger.warning(f"Failed to download image from {img_url}, status code {response.status_code}")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error downloading image for search result: {e}")
|
||||
return None
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -4,10 +4,10 @@ import re
|
|||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
class BaiduImageSearcher:
|
||||
def __init__(self, sources=None, logger=None):
|
||||
def __init__(self, sources=None, image_downloader=None, logger=None):
|
||||
self.filtered_sources = set(sources) if sources else {'淘宝', 'tmall', '1688'}
|
||||
self.image_downloader = image_downloader
|
||||
self.logger = logger
|
||||
self.browser = None
|
||||
self.page = None
|
||||
|
|
@ -35,6 +35,8 @@ class BaiduImageSearcher:
|
|||
"Cache-Control": "max-age=0"
|
||||
})
|
||||
|
||||
|
||||
|
||||
self.initial_url = 'https://graph.baidu.com/pcpage/index?tpl_from=pc'
|
||||
# upload_button_xpath = '//*[@id="app"]/div/div[1]/div[7]/div/span[1]/span[1]'
|
||||
|
||||
|
|
@ -82,18 +84,15 @@ class BaiduImageSearcher:
|
|||
# 첫 번째 검색과 이후 검색의 선택자를 다르게 설정
|
||||
# if self.is_first_search:
|
||||
# if self.is_first_search:
|
||||
# self.logger.info("is_first_search")
|
||||
# upload_button_xpath = '//*[@id="app"]/div/div[1]/div[7]/div/span[1]/span[1]'
|
||||
# upload_input_xpath = '//*[@id="app"]/div/div[1]/div[7]/div/div/div[2]/div[2]/div/form/input'
|
||||
# # self.is_first_search = False # 이후 검색에서는 일반 선택자를 사용
|
||||
self.logger.info("is_first_search")
|
||||
upload_button_xpath = '//*[@id="app"]/div/div[1]/div[7]/div/span[1]/span[1]'
|
||||
upload_input_xpath = '//*[@id="app"]/div/div[1]/div[7]/div/div/div[2]/div[2]/div/form/input'
|
||||
self.is_first_search = False # 이후 검색에서는 일반 선택자를 사용
|
||||
# else:
|
||||
# self.logger.info("another search")
|
||||
# upload_button_xpath = '//*[@id="app"]/div/div[1]/div/div[1]/div/div/div[1]/span[1]/span[1]'
|
||||
# upload_input_xpath = '//*[@id="app"]/div/div[1]/div/div[1]/div/div/div[1]/div/div[2]/div[2]/div/form/input'
|
||||
|
||||
upload_button_xpath = '//*[@id="app"]/div/div[1]/div[7]/div/span[1]/span[1]'
|
||||
upload_input_xpath = '//*[@id="app"]/div/div[1]/div[7]/div/div/div[2]/div[2]/div/form/input'
|
||||
|
||||
# 이미지 업로드 버튼 클릭 및 파일 업로드
|
||||
self.page.wait_for_selector(upload_button_xpath)
|
||||
self.page.click(upload_button_xpath)
|
||||
|
|
@ -129,16 +128,14 @@ class BaiduImageSearcher:
|
|||
return False
|
||||
return True
|
||||
|
||||
def extract_product_data(self):
|
||||
def extract_product_data(self, product_id):
|
||||
# 검색 결과 페이지에서 JSON 데이터 추출
|
||||
if self.check_capcha():
|
||||
return False
|
||||
content = None
|
||||
self.logger.info("검색 결과 페이지에서 JSON 데이터 추출")
|
||||
content = self.page.content()
|
||||
|
||||
self.page.go_back() # 뒤로 가기
|
||||
|
||||
|
||||
soup = BeautifulSoup(content, 'html.parser')
|
||||
script_tag = soup.select_one("html > head > script:nth-of-type(2)")
|
||||
|
||||
|
|
@ -154,7 +151,7 @@ class BaiduImageSearcher:
|
|||
product_info = []
|
||||
|
||||
# 필터링된 출처에 따라 데이터 추출
|
||||
for card in data:
|
||||
for idx, card in enumerate(data):
|
||||
if card.get("cardName") == "product":
|
||||
products = card["tplData"]["list"]
|
||||
for product in products:
|
||||
|
|
@ -167,16 +164,21 @@ class BaiduImageSearcher:
|
|||
# 출처 필터링
|
||||
if source in self.filtered_sources:
|
||||
original_url = self.get_original_url(buyurl)
|
||||
|
||||
# 이미지 다운로드 및 saved_img_path 설정
|
||||
saved_img_path = self.image_downloader.download_image_for_searchResult(imgurl, product_id=product_id, index=products.index(product))
|
||||
|
||||
product_info.append({
|
||||
"title": title,
|
||||
"source": source,
|
||||
"price": price,
|
||||
"imgurl": imgurl,
|
||||
"saved_img_path": saved_img_path,
|
||||
"encrypted_url": buyurl,
|
||||
"original_url": original_url
|
||||
})
|
||||
self.logger.info("product_info 추출 완료")
|
||||
self.logger.info(f"추출된 정보 \n{product_info}")
|
||||
self.logger.info(f"{product_info}")
|
||||
|
||||
return product_info
|
||||
except json.JSONDecodeError as e:
|
||||
|
|
|
|||
13
main.py
|
|
@ -3,19 +3,28 @@ from mainProcessor import MainProcessor
|
|||
from logger_module import setup_logger
|
||||
import logging
|
||||
|
||||
from PySide6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QDialog
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 로그 설정
|
||||
logger = setup_logger(log_file='app.log', log_level=logging.DEBUG)
|
||||
|
||||
# 폴더 및 파일 경로 설정
|
||||
excel_folder = os.path.join(os.getcwd(), 'xls')
|
||||
img_folder = os.path.join(os.getcwd(), 'img')
|
||||
db_path = os.path.join(os.getcwd(), 'products.db')
|
||||
temp_folder = os.path.join(os.getcwd(), 'temp')
|
||||
|
||||
# products.db 파일이 이미 존재할 경우 삭제
|
||||
if os.path.exists(db_path):
|
||||
os.remove(db_path)
|
||||
print(f"{db_path} 파일이 삭제되었습니다.")
|
||||
|
||||
# 메인 프로세서 실행
|
||||
logger.info("Starting main process.")
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
processor = MainProcessor(excel_folder, db_path, temp_folder, logger)
|
||||
processor = MainProcessor(excel_folder, db_path, img_folder, logger)
|
||||
|
||||
processor.process_all_products()
|
||||
logger.info("process_all_products Completed.")
|
||||
|
|
|
|||
105
mainProcessor.py
|
|
@ -3,16 +3,36 @@ from xlsReader import ExcelReader
|
|||
from databaseManager import DatabaseManager
|
||||
from imageDownloader import ImageDownloader
|
||||
from imgSearcher import BaiduImageSearcher
|
||||
from resultDiag import ProductViewer
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
|
||||
class MainProcessor:
|
||||
def __init__(self, excel_folder, db_path, temp_folder, logger):
|
||||
def __init__(self, excel_folder, db_path, img_folder, logger):
|
||||
self.logger = logger
|
||||
self.excel_reader = ExcelReader(excel_folder, logger)
|
||||
self.db_manager = DatabaseManager(db_path, logger)
|
||||
self.image_downloader = ImageDownloader(temp_folder, logger)
|
||||
self.image_searcher = BaiduImageSearcher(sources=['淘宝', 'tmall', '1688'], logger=logger)
|
||||
self.logger.debug("1")
|
||||
self.resultViewer = ProductViewer(db_path)
|
||||
self.logger.debug("2")
|
||||
self.image_downloader = ImageDownloader(img_folder, logger)
|
||||
self.image_searcher = BaiduImageSearcher(sources=['淘宝', 'tmall', '1688'], image_downloader=self.image_downloader, logger=logger)
|
||||
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):
|
||||
# 모든 엑셀 파일을 읽어와 DB에 저장
|
||||
products = self.excel_reader.read_excel_files()
|
||||
|
|
@ -20,30 +40,6 @@ class MainProcessor:
|
|||
|
||||
self.image_searcher.start_browser()
|
||||
|
||||
# # 각 상품에 대해 이미지 검색 수행
|
||||
# for product, product_id in zip(products, product_ids):
|
||||
# try:
|
||||
# if product_id is None:
|
||||
# self.logger.error("Failed to insert product into database.")
|
||||
# continue
|
||||
|
||||
# # 이미지 다운로드 및 검색 실행
|
||||
# image_path = self.image_downloader.download_image(product['image_url'], product_id)
|
||||
# is_success_upload_image = self.image_searcher.upload_image(image_path)
|
||||
# is_success_expand_results = self.image_searcher.expand_results()
|
||||
|
||||
# # 검색 결과를 추출하여 DB에 저장
|
||||
# search_results = self.image_searcher.extract_product_data()
|
||||
# self.db_manager.insert_search_results(product_id, search_results)
|
||||
|
||||
# os.remove(image_path)
|
||||
# self.logger.debug(f"Processed product ID: {product_id}")
|
||||
# time.sleep(1)
|
||||
# except Exception as e:
|
||||
# self.logger.warning(f"Failed to process product ID: {product_id} - {e}", exc_info=True)
|
||||
|
||||
|
||||
|
||||
# 각 상품에 대해 이미지 검색 수행
|
||||
for product, product_id in zip(products, product_ids):
|
||||
try:
|
||||
|
|
@ -53,6 +49,9 @@ class MainProcessor:
|
|||
|
||||
# 이미지 다운로드 및 검색 실행
|
||||
image_path = self.image_downloader.download_image(product['image_url'], product_id)
|
||||
product['saved_img_path'] = image_path # 이미지 경로 설정
|
||||
# self.db_manager.insert_products([product])
|
||||
self.db_manager.update_product_image_path(product_id, image_path)
|
||||
|
||||
# 재시도 횟수 설정
|
||||
max_retries = 3
|
||||
|
|
@ -82,7 +81,7 @@ class MainProcessor:
|
|||
continue # 재시도 시 루프를 다시 시작
|
||||
|
||||
# 검색 결과 추출
|
||||
search_results = self.image_searcher.extract_product_data()
|
||||
search_results = self.image_searcher.extract_product_data(product_id)
|
||||
# if search_results == []: # 빈 리스트일 경우만 실패로 간주
|
||||
# attempt += 1
|
||||
# self.logger.warning(f"Extract product data failed for Product ID [{product_id}]. Retry {attempt}/{max_retries}")
|
||||
|
|
@ -101,8 +100,9 @@ class MainProcessor:
|
|||
self.logger.debug(f"Insert DB: {product_id}")
|
||||
|
||||
self.db_manager.insert_search_results(product_id, search_results)
|
||||
# input("로그를 확인한 후 아무 키나 눌러서 계속하세요...")
|
||||
|
||||
os.remove(image_path)
|
||||
# os.remove(image_path)
|
||||
self.logger.debug(f"Processed product ID: {product_id}")
|
||||
time.sleep(1)
|
||||
|
||||
|
|
@ -112,16 +112,47 @@ class MainProcessor:
|
|||
def show_results(self):
|
||||
# 검색 결과 출력
|
||||
try:
|
||||
with self.db_manager.conn as conn:
|
||||
cursor = conn.execute('''
|
||||
SELECT p.name, s.title, s.source, s.price, s.original_url
|
||||
FROM products p
|
||||
JOIN search_results s ON p.id = s.product_id
|
||||
''')
|
||||
for row in cursor:
|
||||
print(f"상품명: {row[0]}, 검색 결과: {row[1]}, 출처: {row[2]}, 가격: {row[3]}, 원본 링크: {row[4]}")
|
||||
self.resultViewer.show()
|
||||
self.logger.debug(f"show_results Completed")
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Failed to show_results - {e}", exc_info=True)
|
||||
|
||||
|
||||
def export_to_xls(self):
|
||||
# 현재 날짜와 시간을 사용하여 파일 이름 생성
|
||||
date_str = datetime.now().strftime('%Y%m%d')
|
||||
file_index = 1
|
||||
|
||||
try:
|
||||
with self.db_manager.conn as conn:
|
||||
cursor = conn.execute('''
|
||||
SELECT s.original_url, p.name, p.tag, p.percenty_category
|
||||
FROM products p
|
||||
JOIN search_results s ON p.id = s.product_id
|
||||
''')
|
||||
|
||||
data = cursor.fetchall()
|
||||
|
||||
# 50개씩 데이터를 나누어 출력
|
||||
for i in range(0, len(data), 50):
|
||||
# 50개 데이터를 추출하여 DataFrame으로 변환
|
||||
chunk = data[i:i + 50]
|
||||
df = pd.DataFrame(chunk, columns=["original_url", "name", "tag", "percenty_category"])
|
||||
|
||||
# 엑셀 파일 생성
|
||||
excel_filename = f"출력데이터_{date_str}_{file_index}.xlsx"
|
||||
with pd.ExcelWriter(excel_filename) as writer:
|
||||
df.to_excel(writer, index=False, startrow=3, startcol=1, sheet_name="multi_ss")
|
||||
|
||||
# 셀 배치
|
||||
worksheet = writer.sheets['multi_ss']
|
||||
for row_idx, row_data in enumerate(chunk, start=4):
|
||||
worksheet[f"B{row_idx}"] = row_data[0] # original_url
|
||||
worksheet[f"C{row_idx}"] = row_data[1] # name
|
||||
worksheet[f"F{row_idx}"] = row_data[2] # tag
|
||||
worksheet[f"G{row_idx}"] = row_data[3] # percent_category
|
||||
|
||||
self.logger.info(f"{excel_filename} 파일에 데이터 50개 저장 완료.")
|
||||
file_index += 1
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error exporting to Excel: {e}", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ class ProductCard(QWidget):
|
|||
self.info2_label.setStyleSheet("font-weight: bold; font-size: 16px;")
|
||||
|
||||
self.image_label = QLabel()
|
||||
self.image_label.setFixedSize(250, 250)
|
||||
# self.image_label.setFixedWidth(250)
|
||||
self.image_label.setFixedSize(200, 250)
|
||||
|
||||
self.name_label = QLabel("상품명:")
|
||||
self.name_value = QLabel("")
|
||||
|
|
@ -68,7 +69,7 @@ class ProductCard(QWidget):
|
|||
self.cat_value.setText("")
|
||||
self.image_label.clear()
|
||||
|
||||
def set_data(self, name, price, tag, cat, img_url):
|
||||
def set_data(self, name, price, tag, cat, product_img_path):
|
||||
"""카드에 상품 데이터를 설정합니다."""
|
||||
self.name_value.setText(name)
|
||||
# self.price_value.setText(str(price))
|
||||
|
|
@ -78,14 +79,19 @@ class ProductCard(QWidget):
|
|||
self.cat_value.setText(cat)
|
||||
# self.price_value.setText(str(price))
|
||||
|
||||
# 이미지 설정
|
||||
pixmap = QPixmap()
|
||||
img_data = self.download_image_data(img_url)
|
||||
if img_data:
|
||||
pixmap.loadFromData(img_data)
|
||||
self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
|
||||
else:
|
||||
self.image_label.clear()
|
||||
|
||||
pixmap = QPixmap(product_img_path)
|
||||
self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
|
||||
|
||||
|
||||
# # 이미지 설정
|
||||
# pixmap = QPixmap()
|
||||
# img_data = self.download_image_data(img_url)
|
||||
# if img_data:
|
||||
# pixmap.loadFromData(img_data)
|
||||
# self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
|
||||
# else:
|
||||
# self.image_label.clear()
|
||||
|
||||
def download_image_data(self, img_url):
|
||||
"""이미지 URL에서 데이터를 다운로드합니다."""
|
||||
|
|
|
|||
BIN
products.db
|
|
@ -5,10 +5,16 @@ import sys
|
|||
from productCard import ProductCard # ProductCard 클래스 임포트
|
||||
from searchResultCard import SearchResultCard # SearchResultCard 클래스 임포트
|
||||
|
||||
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class ProductViewer(QWidget):
|
||||
def __init__(self, db_path):
|
||||
super().__init__()
|
||||
self.conn = sqlite3.connect(db_path)
|
||||
self.cursor = self.conn.cursor() # 여기에 cursor를 올바르게 초기화
|
||||
self.product_index = 0
|
||||
self.products = self.load_products()
|
||||
self.total_products = len(self.products)
|
||||
|
|
@ -16,7 +22,7 @@ class ProductViewer(QWidget):
|
|||
|
||||
# UI 초기화
|
||||
self.init_ui()
|
||||
self.load_product_data()
|
||||
# self.load_product_data()
|
||||
|
||||
def init_ui(self):
|
||||
self.setWindowTitle("Product Viewer")
|
||||
|
|
@ -30,7 +36,7 @@ class ProductViewer(QWidget):
|
|||
self.middle_layout = QHBoxLayout()
|
||||
self.search_result_cards = [SearchResultCard(i+1) for i in range(4)]
|
||||
for card in self.search_result_cards:
|
||||
self.middle_layout.addWidget(card, 2)
|
||||
self.middle_layout.addWidget(card,2)
|
||||
# card.setFixedHeight(300) # 각 카드의 크기를 고정
|
||||
|
||||
main_layout.addLayout(self.middle_layout)
|
||||
|
|
@ -55,34 +61,42 @@ class ProductViewer(QWidget):
|
|||
|
||||
def load_products(self):
|
||||
# DB에서 products 테이블 데이터를 로드
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("SELECT * FROM products")
|
||||
products = cursor.fetchall()
|
||||
|
||||
self.cursor.execute("SELECT * FROM products")
|
||||
products = self.cursor.fetchall()
|
||||
return products
|
||||
|
||||
def load_product_data(self):
|
||||
# 현재 상품 데이터를 로드하여 ProductCard에 설정
|
||||
product = self.products[self.product_index]
|
||||
product_id = product[0] # products 테이블의 ID
|
||||
product_name = product[1]
|
||||
product_img_url = product[2]
|
||||
product_img_path = product[8]
|
||||
# print(f"product_img_path:{product_img_path}")
|
||||
product_tag = product[3]
|
||||
product_price = product[4]
|
||||
product_cat = product[5]
|
||||
|
||||
self.product_card.set_data(name=product_name, price=product_price, tag=product_tag, cat=product_cat, img_url=product_img_url)
|
||||
self.product_card.set_data(
|
||||
name=product_name,
|
||||
price=product_price,
|
||||
tag=product_tag,
|
||||
cat=product_cat,
|
||||
product_img_path=product_img_path
|
||||
)
|
||||
|
||||
# SearchResultCard 초기화 및 데이터 로드
|
||||
self.reset_search_result_cards() # 이전 카드 데이터 리셋
|
||||
search_results = self.load_search_results(product[0])
|
||||
# SearchResultCard 초기화 및 데이터 설정
|
||||
search_results = self.load_search_results(product_id)
|
||||
for i, result in enumerate(search_results):
|
||||
if i < len(self.search_result_cards):
|
||||
title = result[1]
|
||||
price = result[3]
|
||||
source = result[2]
|
||||
img_url = result[4]
|
||||
is_selected = result[5] # DB에서 is_selected 값을 가져옴
|
||||
self.search_result_cards[i].set_data(name=title, price=price, source=source, img_url=img_url, is_selected=is_selected, cursor=self.cursor)
|
||||
|
||||
title, source, price, saved_img_path, is_selected, search_result_id = result[1], result[2], result[3], result[4], result[5], result[0]
|
||||
self.search_result_cards[i].set_data(
|
||||
name=title, price=price, source=source, saved_img_path=saved_img_path, is_selected=is_selected
|
||||
)
|
||||
self.search_result_cards[i].product_id = product_id
|
||||
self.search_result_cards[i].search_result_id = search_result_id
|
||||
self.search_result_cards[i].cursor = self.cursor
|
||||
|
||||
# 페이지 버튼 텍스트 업데이트
|
||||
self.page_button.setText(f"{self.product_index + 1}/{self.total_products}")
|
||||
|
||||
|
|
@ -94,17 +108,21 @@ class ProductViewer(QWidget):
|
|||
|
||||
def load_search_results(self, product_id):
|
||||
# DB에서 search_results 테이블의 데이터를 로드
|
||||
self.cursor.execute("SELECT * FROM search_results WHERE product_id=?", (product_id,))
|
||||
# self.cursor.execute("SELECT * FROM search_results WHERE product_id=?", (product_id,))
|
||||
self.cursor.execute("SELECT id, title, source, price, saved_img_path, is_selected FROM search_results WHERE product_id=?", (product_id,))
|
||||
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def previous_product(self):
|
||||
if self.product_index > 0:
|
||||
self.product_index -= 1
|
||||
self.reset_search_result_cards()
|
||||
self.load_product_data()
|
||||
|
||||
def next_product(self):
|
||||
if self.product_index < self.total_products - 1:
|
||||
self.product_index += 1
|
||||
self.reset_search_result_cards()
|
||||
self.load_product_data()
|
||||
|
||||
def show_product_dialog(self):
|
||||
|
|
@ -127,13 +145,19 @@ class ProductViewer(QWidget):
|
|||
elif event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
|
||||
self.show_product_dialog()
|
||||
|
||||
def select_card(self, index, cursor):
|
||||
def select_card(self, index):
|
||||
# 선택된 카드의 UI 갱신 및 데이터베이스 업데이트
|
||||
print(f"{index}번째 카드 선택")
|
||||
for i, card in enumerate(self.search_result_cards):
|
||||
if i == index:
|
||||
card.set_selected(True)
|
||||
print(f"i = {i} | index = {index} - True")
|
||||
print(f"card.index : {card.index}")
|
||||
card.update_selection(True) # 선택 상태로 업데이트
|
||||
else:
|
||||
card.set_selected(False)
|
||||
print(f"i = {i} | index = {index} - False")
|
||||
print(f"card.index : {card.index}")
|
||||
card.update_selection(False) # 선택 해제 상태로 업데이트
|
||||
|
||||
|
||||
class ProductDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
208
resultGUI.py
|
|
@ -1,208 +0,0 @@
|
|||
from PySide6.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QPushButton, QDialog, QMessageBox
|
||||
)
|
||||
from PySide6.QtGui import QPixmap, QKeyEvent
|
||||
from PySide6.QtCore import Qt
|
||||
import sys
|
||||
|
||||
class ProductSelectionApp(QWidget):
|
||||
def __init__(self, products, search_results, logger):
|
||||
super().__init__()
|
||||
self.products = products # products 테이블의 데이터를 포함한 리스트
|
||||
self.search_results = search_results # search_results 테이블의 데이터를 포함한 리스트
|
||||
self.logger = logger
|
||||
self.current_product_index = 0
|
||||
self.selected_card_index = None
|
||||
|
||||
self.setWindowTitle("상품 선택 GUI")
|
||||
self.resize(800, 600)
|
||||
|
||||
self.init_ui()
|
||||
self.update_ui()
|
||||
|
||||
def init_ui(self):
|
||||
# 전체 레이아웃
|
||||
self.layout = QVBoxLayout()
|
||||
|
||||
# 상단 레이아웃 - 제품 정보
|
||||
self.top_layout = QHBoxLayout()
|
||||
self.image_label = QLabel() # 이미지 표시용
|
||||
self.product_info_label = QLabel() # 상품 정보 표시용
|
||||
self.top_layout.addWidget(self.image_label)
|
||||
self.top_layout.addWidget(self.product_info_label)
|
||||
self.layout.addLayout(self.top_layout, stretch=45)
|
||||
|
||||
# 중간 레이아웃 - 검색 결과
|
||||
self.middle_layout = QHBoxLayout()
|
||||
self.result_cards = []
|
||||
for i in range(5): # 최대 5개의 검색 결과
|
||||
card_layout = QGridLayout()
|
||||
card_widget = QWidget()
|
||||
card_widget.setLayout(card_layout)
|
||||
|
||||
# 상품 카드에 표시할 위젯 구성
|
||||
card_number = QLabel(f"{i + 1}")
|
||||
card_number.setAlignment(Qt.AlignCenter)
|
||||
card_image = QLabel() # 이미지 표시용
|
||||
card_title = QLabel("상품명:")
|
||||
card_title_value = QLabel()
|
||||
card_price = QLabel("가격:")
|
||||
card_price_value = QLabel()
|
||||
card_url = QLabel("원본URL:")
|
||||
card_url_value = QLabel()
|
||||
card_status = QLabel("미선택")
|
||||
card_status.setAlignment(Qt.AlignCenter)
|
||||
|
||||
# QGridLayout에 위젯 배치
|
||||
card_layout.addWidget(card_number, 0, 0, 1, 2)
|
||||
card_layout.addWidget(card_image, 1, 0, 1, 2)
|
||||
card_layout.addWidget(card_title, 2, 0)
|
||||
card_layout.addWidget(card_title_value, 2, 1)
|
||||
card_layout.addWidget(card_price, 3, 0)
|
||||
card_layout.addWidget(card_price_value, 3, 1)
|
||||
card_layout.addWidget(card_url, 4, 0)
|
||||
card_layout.addWidget(card_url_value, 4, 1)
|
||||
card_layout.addWidget(card_status, 5, 0, 1, 2)
|
||||
|
||||
# 카드 레이아웃 추가
|
||||
self.middle_layout.addWidget(card_widget)
|
||||
self.result_cards.append({
|
||||
"widget": card_widget,
|
||||
"number": card_number,
|
||||
"image": card_image,
|
||||
"title": card_title_value,
|
||||
"price": card_price_value,
|
||||
"url": card_url_value,
|
||||
"status": card_status
|
||||
})
|
||||
|
||||
self.layout.addLayout(self.middle_layout, stretch=45)
|
||||
|
||||
# 하단 레이아웃 - 탐색 버튼 및 상태
|
||||
self.bottom_layout = QHBoxLayout()
|
||||
self.prev_button = QPushButton("←")
|
||||
self.prev_button.clicked.connect(self.prev_product)
|
||||
self.status_button = QPushButton("1/1")
|
||||
self.status_button.clicked.connect(self.show_db_dialog)
|
||||
self.next_button = QPushButton("→")
|
||||
self.next_button.clicked.connect(self.next_product)
|
||||
self.bottom_layout.addWidget(self.prev_button)
|
||||
self.bottom_layout.addWidget(self.status_button)
|
||||
self.bottom_layout.addWidget(self.next_button)
|
||||
self.layout.addLayout(self.bottom_layout, stretch=10)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def update_ui(self):
|
||||
# 제품 정보 업데이트
|
||||
product = self.products[self.current_product_index]
|
||||
self.product_info_label.setText(f"상품명: {product['name']}\n가격: {product['price']}\n카테고리: {product['category']}\n태그: {product['tag']}")
|
||||
|
||||
# 상단 이미지 설정
|
||||
pixmap = QPixmap() # 실제 이미지를 로드해서 사용
|
||||
self.image_label.setPixmap(pixmap)
|
||||
|
||||
# 중간 검색 결과 카드 설정
|
||||
results = self.search_results.get(product['id'], [])
|
||||
for i, card in enumerate(self.result_cards):
|
||||
if i < len(results):
|
||||
result = results[i]
|
||||
card["title"].setText(result['title'])
|
||||
card["price"].setText(result['price'])
|
||||
card["url"].setText(result['original_url'])
|
||||
pixmap = QPixmap() # 실제 imgurl 로드해서 사용
|
||||
card["image"].setPixmap(pixmap)
|
||||
card["status"].setText("미선택")
|
||||
card["status"].setStyleSheet("color: black; font-weight: bold;")
|
||||
else:
|
||||
card["widget"].hide()
|
||||
|
||||
self.status_button.setText(f"{self.current_product_index + 1}/{len(self.products)}")
|
||||
self.update_navigation_buttons()
|
||||
|
||||
def update_navigation_buttons(self):
|
||||
self.prev_button.setEnabled(self.current_product_index > 0)
|
||||
self.next_button.setEnabled(self.current_product_index < len(self.products) - 1)
|
||||
|
||||
def prev_product(self):
|
||||
if self.current_product_index > 0:
|
||||
self.current_product_index -= 1
|
||||
self.update_ui()
|
||||
|
||||
def next_product(self):
|
||||
if self.current_product_index < len(self.products) - 1:
|
||||
self.current_product_index += 1
|
||||
self.update_ui()
|
||||
|
||||
def show_db_dialog(self):
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("DB 조회")
|
||||
dialog.setGeometry(100, 100, 400, 300)
|
||||
layout = QVBoxLayout()
|
||||
layout.addWidget(QLabel("DB 내용 표시"))
|
||||
dialog.setLayout(layout)
|
||||
dialog.exec_()
|
||||
|
||||
def keyPressEvent(self, event: QKeyEvent):
|
||||
if event.key() in [Qt.Key_1, Qt.Key_2, Qt.Key_3, Qt.Key_4, Qt.Key_5]:
|
||||
index = event.key() - Qt.Key_1
|
||||
if index < len(self.result_cards) and self.result_cards[index]["widget"].isVisible():
|
||||
self.select_card(index)
|
||||
elif event.key() == Qt.Key_Left:
|
||||
self.prev_product()
|
||||
elif event.key() == Qt.Key_Right:
|
||||
self.next_product()
|
||||
else:
|
||||
QMessageBox.warning(self, "경고", "유효하지 않은 키입니다.", QMessageBox.Ok, QMessageBox.Ok)
|
||||
|
||||
def select_card(self, index):
|
||||
# 선택된 카드 업데이트
|
||||
for i, card in enumerate(self.result_cards):
|
||||
if i == index:
|
||||
card["status"].setText("선택")
|
||||
card["status"].setStyleSheet("color: red; font-weight: bold;")
|
||||
self.selected_card_index = index
|
||||
else:
|
||||
card["status"].setText("미선택")
|
||||
card["status"].setStyleSheet("color: black; font-weight: bold;")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# 샘플 데이터
|
||||
products = [
|
||||
{"id": 1, "name": "앞유리 썬팅 필름", "price": 26800, "category": "생활/건강-자동차용품", "tag": "썬팅필름"},
|
||||
{"id": 2, "name": "집게형 속눈썹 고데기", "price": 26900, "category": "화장품/미용-뷰티소품", "tag": "아이소품"},
|
||||
{"id": 3, "name": "실험실 주사기 펌프", "price": 118200, "category": "의료용품", "tag": "주사기펌프"},
|
||||
{"id": 4, "name": "대파 절단기", "price": 49200, "category": "주방용품", "tag": "절단기"},
|
||||
{"id": 5, "name": "여성용 스포츠 바지", "price": 18900, "category": "패션/잡화", "tag": "스포츠 바지"},
|
||||
]
|
||||
|
||||
# 샘플 search_results 데이터
|
||||
search_results = {
|
||||
1: [
|
||||
{"title": "UV 차단 썬팅 필름", "price": "¥44.50", "original_url": "https://item.taobao.com/item.htm?id=834618258372", "imgurl": "https://imgurl1.com/suntint.jpg"},
|
||||
{"title": "자동차 창문 썬팅 필름", "price": "¥52.30", "original_url": "https://item.taobao.com/item.htm?id=925497238601", "imgurl": "https://imgurl2.com/autotint.jpg"},
|
||||
],
|
||||
2: [
|
||||
{"title": "내추럴 속눈썹 고데기", "price": "¥33.99", "original_url": "https://item.taobao.com/item.htm?id=817709004314", "imgurl": "https://imgurl1.com/eyelash.jpg"},
|
||||
],
|
||||
3: [
|
||||
{"title": "마이크로 스테핑기", "price": "¥1,182.00", "original_url": "https://item.taobao.com/item.htm?id=675573208809", "imgurl": "https://imgurl1.com/micropump.jpg"},
|
||||
{"title": "페달 소형 실린지펌프", "price": "¥1,250.00", "original_url": "https://item.taobao.com/item.htm?id=834618254321", "imgurl": "https://imgurl2.com/pedalpump.jpg"},
|
||||
{"title": "정밀 실린지펌프", "price": "¥1,320.00", "original_url": "https://item.taobao.com/item.htm?id=763797457223", "imgurl": "https://imgurl3.com/precisepump.jpg"},
|
||||
],
|
||||
4: [
|
||||
{"title": "대파 슬라이서", "price": "¥49.50", "original_url": "https://item.taobao.com/item.htm?id=735171561855", "imgurl": "https://imgurl1.com/slicer.jpg"},
|
||||
{"title": "다용도 야채 절단기", "price": "¥53.80", "original_url": "https://item.taobao.com/item.htm?id=817809254314", "imgurl": "https://imgurl2.com/vegcutter.jpg"},
|
||||
],
|
||||
5: [
|
||||
{"title": "운동용 여성 바지", "price": "¥18.90", "original_url": "https://item.taobao.com/item.htm?id=731571561855", "imgurl": "https://imgurl1.com/sportspants.jpg"},
|
||||
{"title": "여성 스포츠 쇼츠", "price": "¥21.50", "original_url": "https://item.taobao.com/item.htm?id=871709004314", "imgurl": "https://imgurl2.com/sportshorts.jpg"},
|
||||
{"title": "헬스용 레깅스", "price": "¥24.30", "original_url": "https://item.taobao.com/item.htm?id=871739004318", "imgurl": "https://imgurl3.com/leggings.jpg"},
|
||||
],
|
||||
}
|
||||
|
||||
window = ProductSelectionApp(products, search_results, logger=None)
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
|
@ -4,13 +4,21 @@ from PySide6.QtCore import Qt
|
|||
import requests
|
||||
|
||||
class SearchResultCard(QWidget):
|
||||
def __init__(self, index=1):
|
||||
def __init__(self, index=1, product_id=None, search_result_id=None, is_selected=False, cursor=None):
|
||||
super().__init__()
|
||||
self.index = index # 인덱스 속성 추가
|
||||
self.product_id = product_id # 연결된 product의 ID
|
||||
self.search_result_id = search_result_id # search_results 테이블의 고유 ID
|
||||
self.cursor = cursor # DB 커서
|
||||
self.init_ui()
|
||||
self.load_selection_from_db(is_selected) # 초기 선택 상태 반영
|
||||
|
||||
def init_ui(self):
|
||||
self.setFixedSize(160, 300)
|
||||
self.layout = QGridLayout(self)
|
||||
|
||||
# UI 요소 초기화
|
||||
self.index_label = QLabel(str(index))
|
||||
self.index_label = QLabel(str(self.index))
|
||||
self.index_label.setAlignment(Qt.AlignCenter)
|
||||
self.index_label.setStyleSheet("font-weight: bold; font-size: 16px;")
|
||||
|
||||
|
|
@ -51,25 +59,29 @@ class SearchResultCard(QWidget):
|
|||
self.price_value.setText("")
|
||||
self.source_value.setText("")
|
||||
self.image_label.clear()
|
||||
self.set_selected(False, None) # 기본 선택 해제 상태로 초기화
|
||||
self.select_label.setText("미선택")
|
||||
self.select_label.setStyleSheet("font-weight: bold; color: black;")
|
||||
self.setStyleSheet("border: 1px solid grey;")
|
||||
|
||||
def set_data(self, name, price, source, img_url, is_selected, cursor):
|
||||
def set_data(self, name, price, source, saved_img_path, is_selected):
|
||||
"""카드에 검색 결과 데이터를 설정하고 선택 상태를 적용합니다."""
|
||||
self.name_value.setText(name)
|
||||
self.price_value.setText(f"{price}원")
|
||||
self.price_value.setText(f"{price}")
|
||||
self.source_value.setText(source)
|
||||
self.load_selection_from_db(is_selected) # 초기 선택 상태 설정
|
||||
|
||||
# 이미지 설정
|
||||
pixmap = QPixmap()
|
||||
img_data = self.download_image_data(img_url)
|
||||
if img_data:
|
||||
pixmap.loadFromData(img_data)
|
||||
self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
|
||||
else:
|
||||
self.image_label.clear()
|
||||
pixmap = QPixmap(saved_img_path)
|
||||
self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
|
||||
|
||||
# 선택 상태에 따라 UI 업데이트
|
||||
self.set_selected(is_selected, cursor)
|
||||
|
||||
# # 이미지 설정
|
||||
# pixmap = QPixmap()
|
||||
# img_data = self.download_image_data(img_url)
|
||||
# if img_data:
|
||||
# pixmap.loadFromData(img_data)
|
||||
# self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
|
||||
# else:
|
||||
# self.image_label.clear()
|
||||
|
||||
def download_image_data(self, img_url):
|
||||
"""이미지 URL에서 데이터를 다운로드합니다."""
|
||||
|
|
@ -81,9 +93,8 @@ class SearchResultCard(QWidget):
|
|||
print(f"Image download error: {e}")
|
||||
return None
|
||||
|
||||
def set_selected(self, selected, cursor):
|
||||
"""카드의 선택 상태를 설정하고 데이터베이스를 업데이트합니다."""
|
||||
# 선택 상태에 따라 스타일 및 텍스트 변경
|
||||
def update_selection(self, selected):
|
||||
"""사용자의 선택을 반영하여 UI와 데이터베이스를 업데이트합니다."""
|
||||
if selected:
|
||||
self.select_label.setText("선택")
|
||||
self.select_label.setStyleSheet("font-weight: bold; color: red;")
|
||||
|
|
@ -94,9 +105,27 @@ class SearchResultCard(QWidget):
|
|||
self.setStyleSheet("border: 1px solid grey;")
|
||||
|
||||
# 데이터베이스에 선택 상태 업데이트
|
||||
if cursor is not None:
|
||||
print(f"선택카드 DB 업데이트")
|
||||
print(f"self.cursor = {self.cursor}, self.search_result_id = {self.search_result_id}")
|
||||
# if self.cursor is not None and self.product_id is not None:
|
||||
if self.cursor and self.search_result_id is not None:
|
||||
try:
|
||||
cursor.execute("UPDATE search_results SET is_selected = ? WHERE id = ?", (1 if selected else 0, self.index))
|
||||
cursor.connection.commit() # 변경 사항 커밋
|
||||
self.cursor.execute(
|
||||
"UPDATE search_results SET is_selected = ? WHERE id = ?",
|
||||
(1 if selected else 0, self.search_result_id)
|
||||
)
|
||||
self.cursor.connection.commit() # 변경 사항 커밋
|
||||
print(f"Updated selection in DB for search_result_id: {self.search_result_id}, selected: {selected}")
|
||||
except Exception as e:
|
||||
print(f"Error updating selection in database: {e}")
|
||||
print(f"Error updating selection in database: {e}", exc_info=True)
|
||||
|
||||
def load_selection_from_db(self, is_selected):
|
||||
"""DB에서 가져온 선택 상태를 UI에만 반영합니다."""
|
||||
if is_selected:
|
||||
self.select_label.setText("선택")
|
||||
self.select_label.setStyleSheet("font-weight: bold; color: red;")
|
||||
self.setStyleSheet("border: 2px solid red;")
|
||||
else:
|
||||
self.select_label.setText("미선택")
|
||||
self.select_label.setStyleSheet("font-weight: bold; color: black;")
|
||||
self.setStyleSheet("border: 1px solid grey;")
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 465 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
|
@ -0,0 +1,48 @@
|
|||
import cv2
|
||||
import numpy as np
|
||||
|
||||
# 이미지 로드
|
||||
image_path = '1.png'
|
||||
image = cv2.imread(image_path)
|
||||
|
||||
# 이미지 로드 체크
|
||||
if image is None:
|
||||
print(f"Error: 이미지 파일을 찾을 수 없습니다. 경로를 확인하세요: {image_path}")
|
||||
else:
|
||||
# Step 1: GrabCut을 위한 초기 설정
|
||||
mask = np.zeros(image.shape[:2], np.uint8)
|
||||
bgdModel = np.zeros((1, 65), np.float64)
|
||||
fgdModel = np.zeros((1, 65), np.float64)
|
||||
|
||||
# 이미지 가장자리에서 10 픽셀씩 떨어진 사각형을 전경 영역으로 지정
|
||||
height, width = image.shape[:2]
|
||||
rect = (10, 10, width - 20, height - 20) # 가장자리 10px을 배경으로 설정
|
||||
|
||||
# Step 2: GrabCut 알고리즘 적용
|
||||
cv2.grabCut(image, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)
|
||||
|
||||
# 확정된 전경 및 배경 마스크 설정
|
||||
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
|
||||
result = image * mask2[:, :, np.newaxis]
|
||||
|
||||
# Step 3: Contour Detection을 통해 객체 경계 찾기
|
||||
# 그레이스케일 변환
|
||||
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# 이진화 처리 (전경 객체만 남김)
|
||||
_, thresh = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)
|
||||
|
||||
# 윤곽선 찾기
|
||||
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
# 가장 큰 윤곽선 기준으로 경계 영역 잘라내기
|
||||
if contours:
|
||||
cnt = max(contours, key=cv2.contourArea)
|
||||
x, y, w, h = cv2.boundingRect(cnt)
|
||||
final_cropped = result[y:y+h, x:x+w]
|
||||
|
||||
# 최종 결과 저장
|
||||
cv2.imwrite('D:/py/baidu_web/test/ip/image_final_cropped.png', final_cropped)
|
||||
print("이미지 처리 완료: 최종 결과가 저장되었습니다.")
|
||||
else:
|
||||
print("경계를 찾을 수 없습니다.")
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import sqlite3
|
||||
|
||||
def add_id_column_to_search_results(db_path):
|
||||
# 데이터베이스 연결
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
# 기존 테이블 이름 변경
|
||||
cursor.execute("ALTER TABLE search_results RENAME TO search_results_old")
|
||||
|
||||
# 새로운 테이블 생성 (id 필드 추가)
|
||||
cursor.execute('''
|
||||
CREATE TABLE search_results (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
product_id INTEGER,
|
||||
title TEXT,
|
||||
source TEXT,
|
||||
price TEXT,
|
||||
imgurl TEXT,
|
||||
encrypted_url TEXT,
|
||||
original_url TEXT,
|
||||
is_selected BOOLEAN DEFAULT 0,
|
||||
FOREIGN KEY (product_id) REFERENCES products(id)
|
||||
)
|
||||
''')
|
||||
|
||||
# 기존 테이블의 데이터를 새 테이블로 복사
|
||||
cursor.execute('''
|
||||
INSERT INTO search_results (product_id, title, source, price, imgurl, encrypted_url, original_url, is_selected)
|
||||
SELECT product_id, title, source, price, imgurl, encrypted_url, original_url, is_selected
|
||||
FROM search_results_old
|
||||
''')
|
||||
|
||||
# 변경 사항 저장
|
||||
conn.commit()
|
||||
|
||||
# 기존 테이블 삭제
|
||||
cursor.execute("DROP TABLE search_results_old")
|
||||
|
||||
conn.commit()
|
||||
print("Successfully added 'id' column to search_results.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
finally:
|
||||
# 연결 종료
|
||||
conn.close()
|
||||
|
||||
# 데이터베이스 파일 경로를 지정하고 함수 호출
|
||||
add_id_column_to_search_results("products.db")
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import sqlite3
|
||||
|
||||
def rename_column(db_path):
|
||||
# 데이터베이스에 연결
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
# 기존 테이블을 새로운 테이블로 변경
|
||||
cursor.execute('''
|
||||
ALTER TABLE products RENAME TO products_old;
|
||||
''')
|
||||
|
||||
# 새로운 테이블 생성, 필드 이름을 변경한 상태로 만듦
|
||||
cursor.execute('''
|
||||
CREATE TABLE products (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT,
|
||||
image_url TEXT,
|
||||
tag TEXT,
|
||||
price INTEGER,
|
||||
category TEXT,
|
||||
percenty_category TEXT,
|
||||
selected_search_result_id INTEGER,
|
||||
FOREIGN KEY(selected_search_result_id) REFERENCES search_results(id)
|
||||
);
|
||||
''')
|
||||
|
||||
# 기존 테이블에서 데이터를 복사
|
||||
cursor.execute('''
|
||||
INSERT INTO products (id, name, image_url, tag, price, category, percenty_category, selected_search_result_id)
|
||||
SELECT id, name, image_url, tag, price, category, percent_category, selected_search_result_id
|
||||
FROM products_old;
|
||||
''')
|
||||
|
||||
# 기존 테이블 삭제
|
||||
cursor.execute('''
|
||||
DROP TABLE products_old;
|
||||
''')
|
||||
|
||||
conn.commit()
|
||||
print("Column renamed successfully.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
conn.rollback()
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
# 실행
|
||||
rename_column("products.db")
|
||||
43
xlsReader.py
|
|
@ -8,6 +8,46 @@ class ExcelReader:
|
|||
self.logger = logger
|
||||
self.logger.info("ExcelReader initialized.")
|
||||
|
||||
def read_excel_files_for(self):
|
||||
# 엑셀 파일을 순회하며 모든 데이터 수집
|
||||
try:
|
||||
data = []
|
||||
seen_items = set() # 중복을 확인할 집합
|
||||
|
||||
for file_name in os.listdir(self.folder_path):
|
||||
if file_name.endswith('.xlsx') or file_name.endswith('.xls'):
|
||||
file_path = os.path.join(self.folder_path, file_name)
|
||||
self.logger.debug(f"Reading file: {file_path}")
|
||||
xls = pd.ExcelFile(file_path)
|
||||
|
||||
for sheet_name in xls.sheet_names:
|
||||
df = pd.read_excel(xls, sheet_name=sheet_name)
|
||||
|
||||
# 컬럼 이름을 영문으로 변경하고 'NaN'을 None으로 변환
|
||||
df = df.rename(columns={
|
||||
'상품명': 'name',
|
||||
'이미지URL': 'image_url',
|
||||
'태그': 'tag',
|
||||
'가격': 'price',
|
||||
'카테고리': 'category',
|
||||
'퍼센티카테고리': 'percenty_category'
|
||||
}).replace({np.nan: None})
|
||||
|
||||
for record in df.to_dict('records'):
|
||||
# 상품명과 이미지 URL을 이용해 중복 체크
|
||||
unique_key = (record['name'], record['image_url'])
|
||||
if unique_key not in seen_items:
|
||||
seen_items.add(unique_key)
|
||||
data.append(record)
|
||||
else:
|
||||
self.logger.debug(f"Duplicate found and skipped: {unique_key}")
|
||||
|
||||
self.logger.info("read_excel_files successfully.")
|
||||
self.logger.debug(f"{data}")
|
||||
return data
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error in read_excel_files: {e}", exc_info=True)
|
||||
|
||||
def read_excel_files(self):
|
||||
# 엑셀 파일을 순회하며 모든 데이터 수집
|
||||
try:
|
||||
|
|
@ -26,7 +66,7 @@ class ExcelReader:
|
|||
'태그': 'tag',
|
||||
'가격': 'price',
|
||||
'카테고리': 'category',
|
||||
'퍼센티카테고리': 'percent_category'
|
||||
'퍼센티카테고리': 'percenty_category'
|
||||
}).replace({np.nan: None})
|
||||
data.extend(df.to_dict('records'))
|
||||
self.logger.info("read_excel_files successfully.")
|
||||
|
|
@ -34,3 +74,4 @@ class ExcelReader:
|
|||
return data
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error read_excel_files: {e}", exc_info=True)
|
||||
|
||||
|
|
|
|||