Thread OK
This commit is contained in:
parent
ffe62c8ac3
commit
858217ffd7
1002
browser_control.py
1002
browser_control.py
File diff suppressed because it is too large
Load Diff
300
gui2.py
300
gui2.py
|
|
@ -1,5 +1,5 @@
|
|||
from PySide6.QtWidgets import QInputDialog, QWidget, QSpinBox, QPushButton, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy
|
||||
from PySide6.QtCore import Qt, QRect, QSettings, QTimer, QThread, Signal, Slot
|
||||
from PySide6.QtCore import Qt, Slot, QRect, QSettings, QTimer
|
||||
from toggleSwitch import ToggleSwitch
|
||||
from browser_control import BrowserController
|
||||
from whale_translator import WhaleTranslator
|
||||
|
|
@ -16,145 +16,21 @@ import logging
|
|||
import asyncio, sys
|
||||
import os, shutil, time
|
||||
|
||||
class PlaywrightWorker(QThread):
|
||||
"""Playwright 작업을 담당하는 QThread"""
|
||||
finished = Signal() # 작업 완료 시그널
|
||||
|
||||
def __init__(self, browser_controller, logger):
|
||||
super().__init__()
|
||||
self.browser_controller = browser_controller
|
||||
self.logger = logger
|
||||
|
||||
async def run_playwright_task(self):
|
||||
"""비동기 Playwright 작업을 실행"""
|
||||
try:
|
||||
await self.browser_controller.start_browser()
|
||||
self.logger.info(f"브라우저 컨트롤러 스레드 완료")
|
||||
|
||||
|
||||
|
||||
"""크롬 브라우저 실행 후 로그인"""
|
||||
self.logger.debug('크롬 브라우저를 실행합니다...')
|
||||
# await self.whale_translator.start_whale_browser()
|
||||
|
||||
await self.browser_controller.start_browser()
|
||||
|
||||
# 관리자 토글 상태에 따라 로그인
|
||||
|
||||
if self.admin_toggle.isChecked():
|
||||
admin_id = self.admin_id_input.text()
|
||||
admin_pw = self.admin_pw_input.text()
|
||||
user_id = self.user_id_input.text()
|
||||
user_pw = self.user_pw_input.text()
|
||||
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=True)
|
||||
else:
|
||||
admin_id = self.admin_id_input.text()
|
||||
admin_pw = self.admin_pw_input.text()
|
||||
user_id = self.user_id_input.text()
|
||||
user_pw = self.user_pw_input.text()
|
||||
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=False)
|
||||
|
||||
# 로그인 정보 저장
|
||||
self.save_settings()
|
||||
|
||||
# "신규 상품 등록" 페이지로 이동
|
||||
if self.toggle_states['ed_mode']:
|
||||
await self.browser_controller.go_to_registered_product_page()
|
||||
self.logger.info('등록 상품 관리 페이지로 이동 중...')
|
||||
else:
|
||||
self.logger.info('신규 상품 등록 페이지로 이동 중...')
|
||||
await self.browser_controller.go_to_new_product_page()
|
||||
|
||||
# 각 핸들러에 초기화된 page 객체 전달.
|
||||
self.optionHandler.update_page(self.browser_controller.page)
|
||||
self.optionHandler.update_whale(self.whale_translator)
|
||||
self.titleHandler.update_page(self.browser_controller.page)
|
||||
self.priceHandler.update_page(self.browser_controller.page)
|
||||
|
||||
self.translate_button.setEnabled(True)
|
||||
self.pause_button.setEnabled(True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Playwright 오류: {e}")
|
||||
finally:
|
||||
self.finished.emit() # 작업 완료 시그널 발생
|
||||
|
||||
def run(self):
|
||||
"""스레드 내에서 비동기 작업 시작"""
|
||||
asyncio.run(self.run_playwright_task())
|
||||
|
||||
async def stop_playwright(self):
|
||||
"""Playwright 및 Whale 브라우저 종료"""
|
||||
await self.browser_controller.close_browser()
|
||||
if self.whale_translator:
|
||||
self.whale_translator.close_all_virtual_desktops()
|
||||
|
||||
class AutoPercentyGUI(QWidget):
|
||||
def __init__(self, logger=None, app=None):
|
||||
super().__init__()
|
||||
self.initUI()
|
||||
self.app = app
|
||||
self.logger = logger
|
||||
self.debug = False
|
||||
|
||||
# key_path = 'leensoo1nt.json'
|
||||
self.settings = QSettings("WhenRideMycar", "TranslationApp") # QSettings 초기화
|
||||
self.locator_manager = LocatorManager()
|
||||
|
||||
# PlaywrightWorker 생성 및 browser_control 관리
|
||||
self.browser_controller = BrowserController(self, self.logger, self.locator_manager)
|
||||
self.playwright_worker = PlaywrightWorker(self, self.browser_controller, self.logger)
|
||||
|
||||
self.whale_translator = None
|
||||
self.app = app
|
||||
|
||||
self.vertexAI = VertexAITranslator(self.logger)
|
||||
self.optionHandler = None
|
||||
|
||||
# DB 파일 경로 설정
|
||||
self.base_dir = self.get_base_dir()
|
||||
self.user_db_path = os.path.join(self.base_dir, "userDB.db")
|
||||
self.initial_db_path = os.path.join(self.base_dir, "src", "initialDB.db")
|
||||
|
||||
# userDB.db 생성 (없으면 initialDB.db 복사)
|
||||
self.create_user_db_if_not_exists()
|
||||
|
||||
# # userDB.db 파일이 없으면 initialDB.db를 복사해서 생성
|
||||
# if not os.path.exists(self.user_db_path):
|
||||
# if os.path.exists(self.initial_db_path):
|
||||
# shutil.copyfile(self.initial_db_path, self.user_db_path)
|
||||
# print("initialDB.db를 userDB.db로 복사했습니다.")
|
||||
# else:
|
||||
# raise FileNotFoundError("initialDB.db 파일이 없습니다. 초기 DB 파일이 존재하는지 확인해주세요.")
|
||||
|
||||
# DatabaseManager 초기화
|
||||
self.db_manager = DatabaseManager(db_url=f"sqlite:///{self.user_db_path}", logger=self.logger)
|
||||
|
||||
self.cmb_diag = CMBSettingsDialog(parent=self, logger=self.logger, db_manager=self.db_manager, initial_db_path=self.initial_db_path, user_db_path=self.user_db_path, debug=self.debug)
|
||||
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, watermark_font_size=36, debug=self.debug)
|
||||
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.clipboardImageManager, self.logger, self.vertexAI, self.debug)
|
||||
self.priceHandler = PriceHandler(self.locator_manager, self.browser_controller, self.logger, self.optionHandler, self.vertexAI, self.cmb_diag, self.debug)
|
||||
self.titleHandler = TitleHandler(self.locator_manager, self.browser_controller, self.logger)
|
||||
self.running = False
|
||||
|
||||
# 변수 설정
|
||||
self.start_time = 0
|
||||
self.finish_time = 0
|
||||
|
||||
self.total_product_count = 0
|
||||
self.current_product_count = 0
|
||||
|
||||
self.title_count = 0
|
||||
self.option_count = 0
|
||||
self.price_count = 0
|
||||
self.detail_image_count = 0
|
||||
self.thumb_image_count = 0
|
||||
self.current_options_info = {}
|
||||
|
||||
self.current_stage_index = 0 # 현재 진행 중인 단계 인덱스
|
||||
self.login_infos={
|
||||
'admin_id' : None,
|
||||
'admin_pw' : None,
|
||||
'user_id' : None,
|
||||
'user_pw' : None,
|
||||
'is_admin' : False,
|
||||
}
|
||||
|
||||
# 토글 상태를 저장할 딕셔너리 초기화
|
||||
self.toggle_states = {
|
||||
|
|
@ -177,6 +53,46 @@ class AutoPercentyGUI(QWidget):
|
|||
'max_option_count': 20, # 최대 선택가능한 옵션 수
|
||||
}
|
||||
|
||||
|
||||
self.settings = QSettings("WhenRideMycar", "TranslationApp") # QSettings 초기화
|
||||
self.locator_manager = LocatorManager()
|
||||
self.browser_controller = BrowserController(self, self.logger, self.locator_manager, self.login_infos, self.toggle_states)
|
||||
self.vertexAI = VertexAITranslator(self.logger)
|
||||
self.optionHandler = None
|
||||
self.whale_translator = None
|
||||
|
||||
# DB 파일 경로 설정
|
||||
self.base_dir = self.get_base_dir()
|
||||
self.user_db_path = os.path.join(self.base_dir, "userDB.db")
|
||||
self.initial_db_path = os.path.join(self.base_dir, "src", "initialDB.db")
|
||||
|
||||
# userDB.db 생성 (없으면 initialDB.db 복사)
|
||||
self.create_user_db_if_not_exists()
|
||||
|
||||
# DatabaseManager 초기화
|
||||
self.db_manager = DatabaseManager(db_url=f"sqlite:///{self.user_db_path}", logger=self.logger)
|
||||
self.cmb_diag = CMBSettingsDialog(parent=self, logger=self.logger, db_manager=self.db_manager, initial_db_path=self.initial_db_path, user_db_path=self.user_db_path, debug=self.debug)
|
||||
|
||||
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, watermark_font_size=36, debug=self.debug)
|
||||
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.clipboardImageManager, self.logger, self.vertexAI, self.debug)
|
||||
self.priceHandler = PriceHandler(self.locator_manager, self.browser_controller, self.logger, self.optionHandler, self.vertexAI, self.cmb_diag, self.debug)
|
||||
self.titleHandler = TitleHandler(self.locator_manager, self.browser_controller, self.logger)
|
||||
self.running = False
|
||||
|
||||
# 변수 설정
|
||||
self.start_time = 0
|
||||
self.finish_time = 0
|
||||
self.total_product_count = 0
|
||||
self.current_product_count = 0
|
||||
self.title_count = 0
|
||||
self.option_count = 0
|
||||
self.price_count = 0
|
||||
self.detail_image_count = 0
|
||||
self.thumb_image_count = 0
|
||||
self.current_options_info = {}
|
||||
|
||||
self.current_stage_index = 0 # 현재 진행 중인 단계 인덱스
|
||||
|
||||
# 이전에 저장된 설정 불러오기
|
||||
self.load_settings()
|
||||
|
||||
|
|
@ -186,11 +102,6 @@ class AutoPercentyGUI(QWidget):
|
|||
# 프로그래스바 초기화
|
||||
self.update_total_progress(0,0)
|
||||
|
||||
async def run_async_tasks(self):
|
||||
"""비동기 작업을 실행"""
|
||||
while True:
|
||||
await asyncio.sleep(0.1) # 비동기적으로 잠시 대기하여 응답성을 유지
|
||||
|
||||
def get_base_dir(self):
|
||||
"""
|
||||
실행 환경에 따라 base_dir을 설정하는 메서드.
|
||||
|
|
@ -266,7 +177,7 @@ class AutoPercentyGUI(QWidget):
|
|||
|
||||
def initUI(self):
|
||||
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
||||
self.setGeometry(QRect(1240, 900, 280, 600))
|
||||
self.setGeometry(QRect(500, 600, 380, 700))
|
||||
self.setWindowTitle('AutoPecenty2')
|
||||
|
||||
# 로그
|
||||
|
|
@ -531,21 +442,8 @@ class AutoPercentyGUI(QWidget):
|
|||
self.log_layout.addLayout(self.stageTimeline_layout)
|
||||
self.log_layout.addWidget(self.detail_progress_bar)
|
||||
|
||||
# # 토글 레이아웃
|
||||
# self.toggle_layout = QGridLayout()
|
||||
# self.toggle_layout.addWidget(self.title_toggle,0,0)
|
||||
# self.toggle_layout.addWidget(self.optionTrnas_toggle,1,0)
|
||||
# self.toggle_layout.addWidget(self.optionIMGTrans_toggle,2,0)
|
||||
# self.toggle_layout.addWidget(self.optionAutoSelect_toggle,3,0)
|
||||
# self.toggle_layout.addWidget(self.price_toggle,0,1)
|
||||
# self.toggle_layout.addWidget(self.tag_toggle,1,1)
|
||||
# self.toggle_layout.addWidget(self.thumb_toggle,2,1)
|
||||
# self.toggle_layout.addWidget(self.detail_Option_toggle,3,1)
|
||||
# self.toggle_layout.addWidget(self.detail_IMGTrans_toggle,0,2)
|
||||
# self.toggle_layout.addWidget(self.debug_toggle,1,2)
|
||||
|
||||
# 메인 레이아웃에 버튼 레이아웃과 로그 레이아웃 추가
|
||||
# self.main_layout.addLayout(self.admin_toggle_layout,2)
|
||||
self.main_layout.addLayout(self.toggle_layout,2)
|
||||
self.main_layout.addLayout(self.button_layout,2)
|
||||
self.main_layout.addLayout(self.log_layout,5)
|
||||
|
|
@ -556,22 +454,13 @@ class AutoPercentyGUI(QWidget):
|
|||
self.on_admin_toggle_clicked(False)
|
||||
|
||||
# 버튼 이벤트 연결
|
||||
# self.start_chrome_button.clicked.connect(self.start_browser)
|
||||
self.start_chrome_button.clicked.connect(self.on_start_chrome_button_clicked)
|
||||
|
||||
# self.translate_button.clicked.connect(self.start_translation)
|
||||
self.translate_button.clicked.connect(self.on_start_translation_button_clicked)
|
||||
|
||||
self.start_chrome_button.clicked.connect(self.start_browser)
|
||||
self.translate_button.clicked.connect(self.start_translation)
|
||||
self.pause_button.clicked.connect(self.pause_translation)
|
||||
# self.exit_button.clicked.connect(self.close)
|
||||
self.cmb_button.clicked.connect(self.on_cmb_button_clicked)
|
||||
self.cmb_test_button.clicked.connect(self.on_cmb_test_button_clicked)
|
||||
|
||||
async def run_async_tasks(self):
|
||||
"""비동기 작업을 실행"""
|
||||
while True:
|
||||
await asyncio.sleep(0.1) # 비동기적으로 잠시 대기하여 응답성을 유지
|
||||
|
||||
def load_toggle_settings(self):
|
||||
"""QSettings에서 토글 상태 불러오기"""
|
||||
for key in self.toggle_states.keys():
|
||||
|
|
@ -732,22 +621,12 @@ class AutoPercentyGUI(QWidget):
|
|||
if widget:
|
||||
widget.setVisible(visible)
|
||||
|
||||
@Slot()
|
||||
def on_start_chrome_button_clicked(self):
|
||||
url = self.url_input.text()
|
||||
if not url:
|
||||
self.logger.info("URL을 입력하세요.")
|
||||
return
|
||||
|
||||
self.logger.info(f"{url}로 연결 시도 중...")
|
||||
|
||||
# 기존 스레드가 실행 중이라면 종료 후 새로 시작
|
||||
if self.playwright_worker and self.playwright_worker.isRunning():
|
||||
self.playwright_worker.stop_browser()
|
||||
|
||||
# PlaywrightWorker 생성 및 실행
|
||||
self.playwright_worker = PlaywrightWorker(self.browser_controller, url, self.logger)
|
||||
self.playwright_worker.start()
|
||||
async def on_close_button_clicked(self):
|
||||
"""크롬 실행 버튼 클릭 시 호출"""
|
||||
self.logger.debug('크롬 실행 버튼 클릭됨')
|
||||
# 비동기 함수 실행을 위해 asyncio.create_task 사용
|
||||
task = asyncio.create_task(self.close())
|
||||
await task # 작업이 완료될 때까지 대기
|
||||
|
||||
def on_cmb_test_button_clicked(self, test_cat):
|
||||
"""크무비 설정 실행 버튼 클릭 시 호출"""
|
||||
|
|
@ -763,39 +642,32 @@ class AutoPercentyGUI(QWidget):
|
|||
self.logger.debug('크무비 설정 버튼 클릭됨')
|
||||
self.cmb_diag.show()
|
||||
|
||||
async def start_browser(self):
|
||||
@Slot()
|
||||
def start_browser(self):
|
||||
"""크롬 브라우저 실행 후 로그인"""
|
||||
self.logger.debug('크롬 브라우저를 실행합니다...')
|
||||
# await self.whale_translator.start_whale_browser()
|
||||
# self.logger.debug(f'self.browser_controller.page : {self.browser_controller.page}')
|
||||
optionIMGTrans_status = self.toggle_states['optionIMGTrans']
|
||||
detail_IMGTrans_status = self.toggle_states['detail_IMGTrans']
|
||||
vd_mode_status = self.toggle_states['vd_mode']
|
||||
|
||||
await self.browser_controller.start_browser()
|
||||
if optionIMGTrans_status or detail_IMGTrans_status:
|
||||
self.logger.debug(f"optionIMGTrans_status : {optionIMGTrans_status}, detail_IMGTrans_status : {detail_IMGTrans_status}")
|
||||
self.whale_translator = WhaleTranslator(self.app, self.logger, secret_mode=True, vd_mode=vd_mode_status) # 모드 켜기
|
||||
self.whale_translator.start_whale_browser()
|
||||
|
||||
# 관리자 토글 상태에 따라 로그인
|
||||
self.login_infos['admin_id'] = self.admin_id_input.text()
|
||||
self.login_infos['admin_pw'] = self.admin_pw_input.text()
|
||||
self.login_infos['user_id'] = self.user_id_input.text()
|
||||
self.login_infos['user_pw'] = self.user_pw_input.text()
|
||||
self.login_infos['is_admin'] = self.admin_toggle.isChecked()
|
||||
|
||||
self.browser_controller.whale_browser = self.whale_translator # whale_browser 설정
|
||||
self.browser_controller.start()
|
||||
|
||||
if self.admin_toggle.isChecked():
|
||||
admin_id = self.admin_id_input.text()
|
||||
admin_pw = self.admin_pw_input.text()
|
||||
user_id = self.user_id_input.text()
|
||||
user_pw = self.user_pw_input.text()
|
||||
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=True)
|
||||
else:
|
||||
admin_id = self.admin_id_input.text()
|
||||
admin_pw = self.admin_pw_input.text()
|
||||
user_id = self.user_id_input.text()
|
||||
user_pw = self.user_pw_input.text()
|
||||
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=False)
|
||||
|
||||
# 로그인 정보 저장
|
||||
self.save_settings()
|
||||
|
||||
# "신규 상품 등록" 페이지로 이동
|
||||
if self.toggle_states['ed_mode']:
|
||||
await self.browser_controller.go_to_registered_product_page()
|
||||
self.logger.info('등록 상품 관리 페이지로 이동 중...')
|
||||
else:
|
||||
self.logger.info('신규 상품 등록 페이지로 이동 중...')
|
||||
await self.browser_controller.go_to_new_product_page()
|
||||
|
||||
# 각 핸들러에 초기화된 page 객체 전달.
|
||||
self.optionHandler.update_page(self.browser_controller.page)
|
||||
self.optionHandler.update_whale(self.whale_translator)
|
||||
|
|
@ -852,12 +724,7 @@ class AutoPercentyGUI(QWidget):
|
|||
self.detail_progress_bar.setValue(percentage)
|
||||
self.detail_progress_bar.setFormat(f"{current_value}/{total_value}개 완료 [{percentage}%]")
|
||||
|
||||
def on_start_translation_button_clicked(self):
|
||||
"""번역 작업 버튼 클릭 시 호출"""
|
||||
self.logger.debug('번역 작업 버튼 클릭됨')
|
||||
# 비동기 함수 실행을 위해 asyncio.create_task 사용
|
||||
asyncio.create_task(self.start_translation())
|
||||
|
||||
@Slot()
|
||||
async def start_translation(self):
|
||||
self.logger.debug('번역 작업을 시작합니다...')
|
||||
self.running = True # 번역 작업이 시작됨
|
||||
|
|
@ -1007,13 +874,11 @@ class AutoPercentyGUI(QWidget):
|
|||
self.logger.debug('번역 작업을 중단합니다...')
|
||||
self.running = False # 번역 작업 중단
|
||||
|
||||
def close_app(self):
|
||||
"""프로그램 종료 시 Playwright 스레드 및 리소스를 정리"""
|
||||
if self.playwright_worker and self.playwright_worker.isRunning():
|
||||
self.playwright_worker.stop_browser()
|
||||
self.logger.info("프로그램 종료 중...")
|
||||
self.save_settings()
|
||||
self.close() # GUI 닫기
|
||||
async def close(self):
|
||||
"""종료 시 모든 자원 반환 및 Playwright 종료"""
|
||||
await self.playwright_worker.stop_playwright()
|
||||
self.logger.debug('프로그램을 종료합니다.')
|
||||
super().close()
|
||||
|
||||
async def close(self):
|
||||
self.logger.debug('프로그램을 종료합니다...')
|
||||
|
|
@ -1023,7 +888,6 @@ class AutoPercentyGUI(QWidget):
|
|||
self.whale_translator.close_all_virtual_desktops()
|
||||
super().close()
|
||||
|
||||
|
||||
async def detail_trans(self):
|
||||
# 상세페이지 탭 클릭
|
||||
await self.browser_controller.click_detail_tab()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import os
|
|||
from logging.handlers import RotatingFileHandler
|
||||
from PyQt5.QtCore import pyqtSignal, QObject
|
||||
|
||||
def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, backup_count=10):
|
||||
def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, backup_count=5):
|
||||
"""로거 설정을 위한 함수"""
|
||||
formatter = logging.Formatter('%(asctime)s - %(filename)s:%(lineno)d - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import KO_EN
|
|||
import pyperclip # 클립보드 데이터를 확인하기 위한 라이브러리
|
||||
from PIL import ImageGrab
|
||||
import re
|
||||
import psutil
|
||||
|
||||
class WhaleTranslator:
|
||||
def __init__(self, app, logger, secret_mode=True, vd_mode=False, pixel_check_interval=0.1, timeout=10, color_tolerance=20):
|
||||
|
|
@ -490,7 +491,32 @@ class WhaleTranslator:
|
|||
time.sleep(1) # 페이지 로딩 대기
|
||||
|
||||
|
||||
|
||||
def close_whale_window_if_exists(self):
|
||||
"""윈도우 핸들을 사용하여 웨일 창을 닫음"""
|
||||
|
||||
try:
|
||||
if not self.whale_hwnd:
|
||||
self.logger.debug("웨일 창 핸들이 설정되지 않았습니다.")
|
||||
return
|
||||
|
||||
# 핸들이 유효한지 확인
|
||||
if win32gui.IsWindow(self.whale_hwnd):
|
||||
self.logger.debug(f"웨일 창 핸들을 찾았습니다: {self.whale_hwnd}. 종료 중...")
|
||||
win32gui.PostMessage(self.whale_hwnd, win32con.WM_CLOSE, 0, 0) # 창을 종료하는 메시지 전송
|
||||
time.sleep(1) # 잠시 대기하여 창 종료 확인
|
||||
if not win32gui.IsWindow(self.whale_hwnd):
|
||||
self.logger.debug("웨일 창이 성공적으로 종료되었습니다.")
|
||||
else:
|
||||
self.logger.debug("웨일 창 종료에 실패했습니다.")
|
||||
else:
|
||||
self.logger.debug("유효하지 않은 웨일 창 핸들입니다.")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"핸들을 사용한 웨일 창 종료 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
|
||||
def close_whale_window_if_exists_ori(self):
|
||||
"""웨일 브라우저 창을 프로세스 ID(pid)로 찾아 종료"""
|
||||
try:
|
||||
if not self.whale_pid:
|
||||
|
|
|
|||
Loading…
Reference in New Issue