249 lines
11 KiB
Python
249 lines
11 KiB
Python
import sys
|
|
import asyncio
|
|
from tkinter import EXCEPTION
|
|
import qasync
|
|
from PyQt5.QtWidgets import QApplication, QGridLayout, QSizePolicy, QProgressDialog, QMessageBox, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtGui import QPixmap
|
|
from web_scraper_async import WebScraper
|
|
|
|
class MainApp(QWidget):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.initUI()
|
|
self.scraper = WebScraper()
|
|
self.results_widget = None # 초기에는 결과 위젯이 없음
|
|
|
|
|
|
def initUI(self):
|
|
searchLayout = QHBoxLayout()
|
|
self.searchLabel = QLabel('검색어 입력:', self)
|
|
|
|
self.searchButton = QPushButton('검색 실행', self)
|
|
# 검색 버튼 클릭 시 start_search를 비동기로 실행
|
|
self.searchButton.clicked.connect(self.on_search_button_clicked)
|
|
self.searchButton.setEnabled(False) # 초기에 버튼을 비활성화합니다.
|
|
|
|
self.searchLineEdit = QLineEdit(self)
|
|
# 엔터 키를 누를 때 start_search를 비동기로 실행
|
|
self.searchLineEdit.returnPressed.connect(self.on_search_button_clicked)
|
|
# self.searchLineEdit.setEnabled(False) # 초기에 입력 필드도 비활성화
|
|
|
|
|
|
searchLayout.addWidget(self.searchLabel)
|
|
searchLayout.addWidget(self.searchLineEdit)
|
|
searchLayout.addWidget(self.searchButton)
|
|
|
|
logLayout = QVBoxLayout()
|
|
self.logTextEdit = QTextEdit(self)
|
|
self.logTextEdit.setReadOnly(True)
|
|
|
|
logLayout.addWidget(self.logTextEdit)
|
|
|
|
mainLayout = QVBoxLayout()
|
|
mainLayout.addLayout(searchLayout)
|
|
mainLayout.addLayout(logLayout)
|
|
|
|
self.setLayout(mainLayout)
|
|
self.setWindowTitle('Web Scraper')
|
|
self.setGeometry(300, 300, 300, 400)
|
|
|
|
def start_search_process(self):
|
|
# 검색 프로세스 시작 시 QProgressDialog 설정
|
|
self.progress_dialog = QProgressDialog("검색 진행 중... 잠시만 기다려 주세요.", "취소", 0, 100, self)
|
|
self.progress_dialog.setWindowTitle("검색 진행 상태")
|
|
self.progress_dialog.setWindowModality(Qt.WindowModal)
|
|
self.progress_dialog.setAutoClose(True)
|
|
self.progress_dialog.setAutoReset(True)
|
|
self.progress_dialog.canceled.connect(self.cancel_search)
|
|
self.progress_dialog.show()
|
|
|
|
# 검색 시작
|
|
asyncio.ensure_future(self.perform_search(self.searchLineEdit.text()))
|
|
|
|
async def perform_search(self, term):
|
|
# 검색어를 사용하여 실제 검색 수행을 시뮬레이션합니다.
|
|
for i in range(101):
|
|
if self.progress_dialog.wasCanceled():
|
|
break
|
|
self.progress_dialog.setValue(i) # 진행 상황 업데이트
|
|
await asyncio.sleep(0.05) # 검색 시간을 시뮬레이션
|
|
if not self.progress_dialog.wasCanceled():
|
|
self.progress_dialog.setValue(100) # 작업 완료
|
|
|
|
def cancel_search(self):
|
|
# 검색 취소 로직
|
|
print("검색이 사용자에 의해 취소되었습니다.")
|
|
|
|
def on_search_button_clicked(self):
|
|
asyncio.ensure_future(self.start_search())
|
|
|
|
async def prepare_search(self):
|
|
# setup_browser에서 페이지 로딩을 관리하므로 여기서는 로딩 완료를 기다립니다.
|
|
print(f"prepare_search Start : is_page_loaded : {self.scraper.is_page_loaded}")
|
|
while not self.scraper.is_page_loaded:
|
|
print(f"is_page_loaded : {self.scraper.is_page_loaded}")
|
|
await asyncio.sleep(1) # 로딩 상태를 주기적으로 체크합니다.
|
|
|
|
self.searchButton.setEnabled(True)
|
|
# self.searchLineEdit.setEnabled(True)
|
|
|
|
async def start_search(self):
|
|
term = self.searchLineEdit.text()
|
|
if not term:
|
|
self.logTextEdit.append("검색어를 입력해주세요.")
|
|
QMessageBox.warning(self, "경고", "검색어를 입력해주세요.")
|
|
return
|
|
|
|
self.logTextEdit.append("검색을 시작합니다...")
|
|
try:
|
|
results = await self.scraper.search_for_term(term)
|
|
if results:
|
|
self.logTextEdit.append("검색 완료. 결과를 처리합니다...")
|
|
await self.show_results(results)
|
|
else:
|
|
self.logTextEdit.append("검색 결과가 없거나 오류가 발생했습니다.")
|
|
QMessageBox.information(self, "검색 결과 없음", "검색 결과가 없으므로 지재권에 안심하시면 됩니다.", QMessageBox.Ok)
|
|
except Exception as e:
|
|
self.logTextEdit.append(f"검색 중 오류 발생: {str(e)}")
|
|
|
|
async def show_results(self, results):
|
|
try:
|
|
# 이전 결과 위젯이 있으면 닫기
|
|
if self.results_widget is not None:
|
|
self.results_widget.close() # 기존 위젯 닫기
|
|
self.results_widget = None # 참조 제거
|
|
|
|
# 새 결과 위젯 생성
|
|
self.results_widget = QWidget()
|
|
|
|
layout = QVBoxLayout()
|
|
self.results_widget.setLayout(layout)
|
|
total_count = results['total_count']
|
|
if total_count is not int:
|
|
total_count = int(results['total_count'])
|
|
set_count = min(total_count, 10)
|
|
grid_layout = QGridLayout()
|
|
layout.addLayout(grid_layout)
|
|
grid_index = 0
|
|
grid_columns = 5
|
|
|
|
for i in range(1, set_count + 1):
|
|
result_key = f"result_{i}"
|
|
if result_key in results:
|
|
result = results[result_key]
|
|
print(f"result num {i}: {result}")
|
|
|
|
# 테두리 설정
|
|
border_style = ''
|
|
if result['admin_status'] == '등록':
|
|
border_style = 'border: 4px solid red;'
|
|
elif result['admin_status'] == '공고':
|
|
border_style = 'border: 3px solid black;'
|
|
|
|
item_layout = QVBoxLayout()
|
|
item_widget = QWidget()
|
|
item_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)
|
|
print("item_widget set")
|
|
|
|
try:
|
|
image_label = QLabel()
|
|
image_label.setFixedSize(150, 150)
|
|
image_data = await self.scraper.fetch_image_data(result['IDimageURL'])
|
|
print(f"image_data : {image_data}")
|
|
|
|
pixmap = QPixmap()
|
|
if image_data:
|
|
pixmap.loadFromData(image_data)
|
|
scaled_pixmap = pixmap.scaled(image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
|
image_label.setPixmap(scaled_pixmap)
|
|
image_label.setAlignment(Qt.AlignCenter)
|
|
image_label.setScaledContents(True)
|
|
print(f"image URL : [{result['IDimageURL']}]")
|
|
print("image set")
|
|
horizontal_layout = QHBoxLayout()
|
|
horizontal_layout.addWidget(image_label)
|
|
horizontal_layout.setAlignment(Qt.AlignCenter)
|
|
item_layout.addLayout(horizontal_layout)
|
|
print("horizontal_layout set")
|
|
except Exception as e:
|
|
print(f" 이미지 데이터 처리 중 에러 : {e}")
|
|
|
|
info_text = f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>상표권명: {result['title']}</span><br>\n" \
|
|
f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>등록상태: {result['admin_status']}</span><br>\n" \
|
|
f"<span style='font-size: 9pt;'>카테고리: {result['product_category']}</span><br>\n" \
|
|
f"<span style='font-size: 9pt;'>권리자: {result['applicant']}</span><br>\n" \
|
|
f"<span style='font-size: 9pt;'> {result['publication_date']}</span><br>\n" \
|
|
f"<span style='font-size: 9pt;'> {result['registration_date']}</span>"
|
|
|
|
print(f"info_text : {info_text}")
|
|
info_label = QLabel(info_text)
|
|
print(f"result['category_description'] : {result['category_description']}")
|
|
tooltip_text = self.wrap_text(result['category_description'], 50)
|
|
info_label.setToolTip(tooltip_text)
|
|
image_label.setToolTip(tooltip_text)
|
|
item_layout.addWidget(info_label)
|
|
|
|
image_label.setStyleSheet(border_style)
|
|
info_label.setStyleSheet(border_style)
|
|
|
|
print("item_layout set")
|
|
|
|
# 레이아웃에 위젯 추가
|
|
row = grid_index // grid_columns
|
|
col = grid_index % grid_columns
|
|
grid_layout.addLayout(item_layout, row, col)
|
|
# grid_layout.addLayout(item_layout, grid_index // grid_columns, grid_index % grid_columns)
|
|
grid_index += 1
|
|
|
|
close_button = QPushButton("Close")
|
|
close_button.clicked.connect(self.close_results_widget)
|
|
layout.addWidget(close_button)
|
|
print("close_button set")
|
|
|
|
self.results_widget.setGeometry(300, 300, 800, 600)
|
|
self.results_widget.setWindowTitle('Search Results')
|
|
print("Search Results results_widget set")
|
|
self.results_widget.show()
|
|
|
|
except Exception as e:
|
|
print(f"Error displaying results: {e}")
|
|
|
|
def wrap_text(self, text, width=40):
|
|
"""주어진 너비에 맞게 텍스트를 줄바꿈합니다."""
|
|
words = text.split()
|
|
wrapped_text = ''
|
|
line_length = 0
|
|
|
|
for word in words:
|
|
if line_length + len(word) + 1 > width:
|
|
wrapped_text += '\n'
|
|
line_length = 0
|
|
wrapped_text += word + ' '
|
|
line_length += len(word) + 1
|
|
|
|
return wrapped_text.strip()
|
|
|
|
|
|
def close_results_widget(self):
|
|
self.results_widget.close()
|
|
|
|
|
|
def closeEvent(self, event):
|
|
asyncio.create_task(self.scraper.close_browser())
|
|
super().closeEvent(event) # QWidget의 기본 closeEvent 처리
|
|
|
|
if __name__ == '__main__':
|
|
app = qasync.QApplication(sys.argv)
|
|
ex = MainApp()
|
|
loop = qasync.QEventLoop(app)
|
|
asyncio.set_event_loop(loop)
|
|
ex.show()
|
|
|
|
# `setup_browser`가 완료될 때까지 기다립니다.
|
|
loop.create_task(ex.scraper.setup_browser())
|
|
loop.create_task(ex.prepare_search())
|
|
|
|
# `loop.run_forever()`를 사용하여 이벤트 루프를 시작합니다.
|
|
loop.run_forever()
|