diff --git a/database_with_mongo.py b/database_with_mongo.py index 89826c2..c8a22ad 100644 --- a/database_with_mongo.py +++ b/database_with_mongo.py @@ -151,10 +151,11 @@ class AutoPercentyProductsDB: 'tag_modification': 'complete', 'option_modification': 'complete', 'detail_page_modification': 'complete', - 'thumbnail_modification': 'complete' - # 'thumbnail_modification': 'complete', + 'thumbnail_modification': 'complete', # 'price_modification': 'complete', - # 'title_modification': 'complete' + # 'title_modification': 'complete', + # 'trans_detailImage': 'complete', + # 'upload_to_market': 'complete', } # 현재 시간 diff --git a/edit/trans_image.py b/edit/trans_image.py new file mode 100644 index 0000000..6ed5bd0 --- /dev/null +++ b/edit/trans_image.py @@ -0,0 +1,17 @@ +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.common.keys import Keys +import time, re, random +from edit.action_elements import click_element, return_element, click_and_confirm_tab +from edit.price_cal import calculate_margin_and_price + +# from ai.compare import find_most_similar_image_by_one +import logging + +# 로거 인스턴스 가져오기 +logger = logging.getLogger('default_logger') + +def tran_detail_image(driver, product_infos): + return None diff --git a/edit/uploadMarket.py b/edit/uploadMarket.py new file mode 100644 index 0000000..731b6ff --- /dev/null +++ b/edit/uploadMarket.py @@ -0,0 +1,17 @@ +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.common.keys import Keys +import time, re, random +from edit.action_elements import click_element, return_element, click_and_confirm_tab +from edit.price_cal import calculate_margin_and_price + +# from ai.compare import find_most_similar_image_by_one +import logging + +# 로거 인스턴스 가져오기 +logger = logging.getLogger('default_logger') + +def upload_to_market(driver, product_infos): + return None \ No newline at end of file diff --git a/login_widget.py b/login_widget.py index eec0190..06336f5 100644 --- a/login_widget.py +++ b/login_widget.py @@ -3,6 +3,8 @@ from PyQt5.QtCore import Qt, QSettings from PyQt5 import QtWidgets, QtCore, QtWidgets from datetime import datetime from credentials import load_credentials, save_credentials +from src.toggleSwitch import ToggleSwitch # toggle_switch.py에서 ToggleSwitch 클래스를 import + import sys import logging @@ -30,14 +32,43 @@ class LoginWidget(QtWidgets.QWidget): self.per_mode = False self.set_num_modify = 0 + self.alwaysOnTopSwitch = True + self.whether_shutdownAfterComplete = False + self.whether_modifyProductName = False + self.whether_modifyProductOptions = True + self.whether_modifyProductTag = True + self.whether_modifyProductPrice = False + self.whether_modifyProductThumb = True + self.whether_modifyProductDetail = True + self.whether_modifyImageTanslation = False + self.whether_uploadToMarket = False + + self.check_for_updates(self.login_db, self.CURRENT_VERSION) def initUI(self): - self.setGeometry(780, 420, 240, 200) + # self.setGeometry(780, 420, 240, 200) - layout = QtWidgets.QVBoxLayout() + # 위젯의 크기를 설정합니다. + self.resize(800, 800) + self.center() # 화면 가운데에 위치시키는 메서드 + + # 레이아웃 설정 + layout = QtWidgets.QHBoxLayout() + + login_info_frame = QtWidgets.QFrame() + login_layout = QtWidgets.QVBoxLayout() + login_info_frame.setLayout(login_layout) + # 선택사항 영역 + setting_frame = QtWidgets.QFrame() + setting_layout = QtWidgets.QVBoxLayout() + setting_frame.setLayout(setting_layout) + + # 레이아웃 설정 + layout.addWidget(login_info_frame) + layout.addWidget(setting_frame) self.signupButton = QtWidgets.QPushButton("회원 가입") self.signupButton.clicked.connect(self.showSignUpWindow) @@ -76,7 +107,7 @@ class LoginWidget(QtWidgets.QWidget): self.infoLabel3 = QtWidgets.QLabel("=====================================") # 토글 버튼 추가 - self.changeModeButton = QtWidgets.QPushButton("관리자용용모드") + self.changeModeButton = QtWidgets.QPushButton("관리자용 모드") self.changeModeButton.setCheckable(True) self.changeModeButton.clicked.connect(self.toggleMode) # 초기 토글 상태 설정 @@ -94,27 +125,204 @@ class LoginWidget(QtWidgets.QWidget): self.loginButton = QtWidgets.QPushButton("로그인") self.loginButton.clicked.connect(self.login) + self.alwaysOnTopSwitch = ToggleSwitch(self) + self.alwaysOnTopSwitch.move(10, 10) + self.alwaysOnTopSwitch.setChecked(True) # 기본 상태는 ON + self.alwaysOnTopSwitch.clicked.connect(self.toggleAlwaysOnTop) + self.alwaysOnTopSwitch_layout = self.create_label_and_switch("항상 위에 표시", self.alwaysOnTopSwitch) + + self.shutdownSwitch = ToggleSwitch(self) + self.shutdownSwitch.move(10, 50) + self.shutdownSwitch.setChecked(False) # 기본 상태는 OFF + self.shutdownSwitch.clicked.connect(self.shutdownAfterComplete) + self.shutdownSwitch_layout = self.create_label_and_switch("작업 종료 후 PC 종료", self.shutdownSwitch) + + + self.modifyProductNameSwitch = ToggleSwitch(self) + self.modifyProductNameSwitch.move(10, 90) + self.modifyProductNameSwitch.setChecked(False) # 기본 상태는 OFF + self.modifyProductNameSwitch.clicked.connect(self.modifyProductName) + self.modifyProductNameSwitch_layout = self.create_label_and_switch("상품명 수정", self.modifyProductNameSwitch) + + + self.modifyProductOptionsSwitch = ToggleSwitch(self) + self.modifyProductOptionsSwitch.move(10, 90) + self.modifyProductOptionsSwitch.setChecked(True) # 기본 상태는 OFF + self.modifyProductOptionsSwitch.clicked.connect(self.modifyProductOptions) + self.modifyProductOptionsSwitch_layout = self.create_label_and_switch("옵션명 수정", self.modifyProductOptionsSwitch) + + + self.modifyProductTagSwitch = ToggleSwitch(self) + self.modifyProductTagSwitch.move(10, 90) + self.modifyProductTagSwitch.setChecked(True) # 기본 상태는 OFF + self.modifyProductTagSwitch.clicked.connect(self.modifyProductTag) + self.modifyProductTagSwitch_layout = self.create_label_and_switch("태그 수정", self.modifyProductTagSwitch) + + + self.modifyProductPriceSwitch = ToggleSwitch(self) + self.modifyProductPriceSwitch.move(10, 90) + self.modifyProductPriceSwitch.setChecked(False) # 기본 상태는 OFF + self.modifyProductPriceSwitch.clicked.connect(self.modifyProductPrice) + self.modifyProductPriceSwitch_layout = self.create_label_and_switch("가격 수정", self.modifyProductPriceSwitch) + + + self.modifyProductThumbSwitch = ToggleSwitch(self) + self.modifyProductThumbSwitch.move(10, 90) + self.modifyProductThumbSwitch.setChecked(True) # 기본 상태는 OFF + self.modifyProductThumbSwitch.clicked.connect(self.modifyProductThumb) + self.modifyProductThumbSwitch_layout = self.create_label_and_switch("썸네일 수정", self.modifyProductThumbSwitch) + + + self.modifyProductDetailSwitch = ToggleSwitch(self) + self.modifyProductDetailSwitch.move(10, 90) + self.modifyProductDetailSwitch.setChecked(True) # 기본 상태는 OFF + self.modifyProductDetailSwitch.clicked.connect(self.modifyProductDetail) + self.modifyProductDetailSwitch_layout = self.create_label_and_switch("상세페이지 수정", self.modifyProductDetailSwitch) + + + self.modifyImageTranslationSwitch = ToggleSwitch(self) + self.modifyImageTranslationSwitch.move(10, 90) + self.modifyImageTranslationSwitch.setChecked(False) # 기본 상태는 OFF + self.modifyImageTranslationSwitch.clicked.connect(self.modifyImageTranslation) + self.modifyImageTranslationSwitch_layout = self.create_label_and_switch("이미지 번역", self.modifyImageTranslationSwitch) + + + self.uploadToMarketSwitch = ToggleSwitch(self) + self.uploadToMarketSwitch.move(10, 90) + self.uploadToMarketSwitch.setChecked(False) # 기본 상태는 OFF + self.uploadToMarketSwitch.clicked.connect(self.uploadToMarket) + self.uploadToMarketSwitch_layout = self.create_label_and_switch("마켓 업로드", self.uploadToMarketSwitch) + self.infoLabel4 = QtWidgets.QLabel(" 퍼센티 자동화 by 리앤수Int.") + - layout.addWidget(self.signupButton) - layout.addWidget(self.infoLabel) - layout.addWidget(self.emailInput) - layout.addWidget(self.passwordInput) - layout.addWidget(self.infoLabel2) - layout.addWidget(self.per_emailInput) - layout.addWidget(self.per_em_emailInput) - layout.addWidget(self.per_em_passwordInput) - layout.addWidget(self.per_passwordInput) - layout.addWidget(self.changeModeButton) - layout.addWidget(self.set_num_modifyInput) - layout.addWidget(self.infoLabel5) - layout.addWidget(self.infoLabel3) - layout.addWidget(self.showPasswordCheckBox) - layout.addWidget(self.loginButton) - layout.addWidget(self.infoLabel4) + login_layout.addWidget(self.infoLabel) + login_layout.addWidget(self.signupButton) + login_layout.addWidget(self.infoLabel) + login_layout.addWidget(self.emailInput) + login_layout.addWidget(self.passwordInput) + login_layout.addWidget(self.infoLabel2) + login_layout.addWidget(self.per_emailInput) + login_layout.addWidget(self.per_em_emailInput) + login_layout.addWidget(self.per_em_passwordInput) + login_layout.addWidget(self.per_passwordInput) + login_layout.addWidget(self.changeModeButton) + login_layout.addWidget(self.set_num_modifyInput) + login_layout.addWidget(self.infoLabel5) + login_layout.addWidget(self.infoLabel3) + login_layout.addWidget(self.showPasswordCheckBox) + login_layout.addWidget(self.loginButton) + login_layout.addWidget(self.infoLabel4) + + # labels = ["항상 위", "작업완료 후 PC종료", "상품명 수정", "옵션 수정", "가격 수정", "태그 수정", "썸네일 수정", "상세페이지 수정", "이미지 번역", "마켓 업로드"] + # for i, label_text in enumerate(labels, start = 1): + # label = QtWidgets.QLabel(label_text) + # setting_layout.addWidget(label) # 설명 라벨은 2열(인덱스 1)에 배치 + + setting_layout.addLayout(self.modifyProductNameSwitch_layout) + setting_layout.addLayout(self.modifyProductOptionsSwitch_layout) + setting_layout.addLayout(self.modifyProductPriceSwitch_layout) + setting_layout.addLayout(self.modifyProductTagSwitch_layout) + setting_layout.addLayout(self.modifyProductThumbSwitch_layout) + setting_layout.addLayout(self.modifyProductDetailSwitch_layout) + setting_layout.addLayout(self.modifyImageTranslationSwitch_layout) + setting_layout.addLayout(self.uploadToMarketSwitch_layout) + setting_layout.addLayout(self.alwaysOnTopSwitch_layout) + setting_layout.addLayout(self.shutdownSwitch_layout) + self.setLayout(layout) self.setWindowTitle('AutoPercenty') + + def create_label_and_switch(self, label_text, target_switch): + layout = QtWidgets.QHBoxLayout() + label = QtWidgets.QLabel(label_text) + + layout.addWidget(label) + layout.addWidget(target_switch) + + return layout + + def center(self): + # 화면 중앙에 위치시키기 위한 계산 + qr = self.frameGeometry() # 위젯의 직사각형 정보를 얻습니다. + cp = QtWidgets.QDesktopWidget().availableGeometry().center() # 사용 가능한 화면의 중앙 위치를 얻습니다. + qr.moveCenter(cp) # 직사각형의 중심을 화면의 중심 위치로 이동시킵니다. + self.move(qr.topLeft()) # 위젯의 시작점을 직사각형의 시작점으로 이동시켜 화면 중앙에 위치시킵니다. + + + def toggleAlwaysOnTop(self, checked): + if checked: + self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) + else: + self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint) + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def shutdownAfterComplete(self, checked): + if checked: + self.whether_shutdownAfterComplete = True + else: + self.whether_shutdownAfterComplete = False + + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def modifyProductName(self, checked): + if checked: + self.whether_modifyProductName = True + else: + self.whether_modifyProductName = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def modifyProductOptions(self, checked): + if checked: + self.whether_modifyProductOptions = True + else: + self.whether_modifyProductOptions = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def modifyProductTag(self, checked): + if checked: + self.whether_modifyProductTag = True + else: + self.whether_modifyProductTag = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def modifyProductPrice(self, checked): + if checked: + self.whether_modifyProductPrice = True + else: + self.whether_modifyProductPrice = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def modifyProductThumb(self, checked): + if checked: + self.whether_modifyProductThumb = True + else: + self.whether_modifyProductThumb = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def modifyProductDetail(self, checked): + if checked: + self.whether_modifyProductDetail = True + else: + self.whether_modifyProductDetail = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + def modifyImageTranslation(self, checked): + if checked: + self.whether_modifyImageTanslation = True + else: + self.whether_modifyImageTanslation = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + + + def uploadToMarket(self, checked): + if checked: + self.whether_uploadToMarket = True + else: + self.whether_uploadToMarket = False + self.show() # 변경된 윈도우 플래그를 적용하기 위해 위젯을 다시 보여줘야 함 + def togglePasswordVisibility(self): if self.showPasswordCheckBox.isChecked(): diff --git a/main.py b/main.py index dea3d00..cf619cf 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ from selenium.webdriver.chrome.service import Service #웨일 import time import ctypes import atexit +import platform, os, sys from login import login import sys @@ -56,6 +57,27 @@ def send_exit_message(): # 이 함수에서는 사용자에게 프로그램 종료 메시지를 보내는 로직을 구현해야 합니다. # 여기서는 예시로 로깅만 수행합니다. logger.info("프로그램이 종료되었습니다. 안녕히 가세요.") + +def shutdown_system(): + """ + 시스템을 종료시키는 메서드. 운영 체제를 확인하고 적절한 종료 명령을 실행합니다. + """ + logger.info("프로그램이 종료되었습니다. 1초 후 컴퓨터가 종료됩니다. 안녕히 가세요.") + try: + operating_system = platform.system() + + if operating_system == "Windows": + os.system("shutdown /s /t 1") # Windows에서 시스템 종료 + elif operating_system == "Linux" or operating_system == "Darwin": + os.system("shutdown -h now") # Linux와 macOS에서 시스템 종료 + else: + print("Unsupported operating system.") + sys.exit(1) + + print("System shutdown command executed.") + except Exception as e: + print(f"Failed to shutdown the system: {e}") + sys.exit(1) def DB_setting(): QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts) @@ -85,16 +107,19 @@ def DB_setting(): return login_info, result, mongo_config def main(): - + login_info, app_result, mongo_config = DB_setting() # 로그인 정보와 QApplication 실행 결과를 받음 + # 화면 꺼짐과 절전 모드 방지 기능 활성화 prevent_sleep_mode() # 프로그램 종료 시 호출될 함수 등록 atexit.register(restore_sleep_mode) atexit.register(record_logout_time) - atexit.register(send_exit_message) - login_info, app_result, mongo_config = DB_setting() # 로그인 정보와 QApplication 실행 결과를 받음 + if login_info.whether_shutdownAfterComplete: + atexit.register(shutdown_system) + + atexit.register(send_exit_message) if not login_info["login_result"]: logger.debug("사용자 인증 실패로 프로그램을 종료합니다.") diff --git a/mainwindow.py b/mainwindow.py new file mode 100644 index 0000000..c6c1a68 --- /dev/null +++ b/mainwindow.py @@ -0,0 +1,71 @@ +from PyQt5 import QtWidgets, QtCore +from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QLabel, QPushButton, QTextEdit +from selenium import webdriver +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.chrome.service import Service #웨일 +# from webdriver_manager.chrome import ChromeDriverManager #웨일 +import time +import ctypes +import atexit + +from login import login +import sys +from navigate import navigate_to_new_product_registration +from modify_products import modify_products +from database import setup_database +from utils import log +from config import WEBSITE_URL +from credentials import load_credentials, save_credentials +from ai.gemini import ImageDescriptionGenerator +from login_widget import LoginWidget +from mongo_config import MongoConfig +from PyQt5 import QtCore, QtWidgets +from logger_module import setup_logger +import logging +class MainWindow(QWidget): + def __init__(self, mongo_config, logger, CURRENT_VERSION): + super().__init__() + self.mongo_config = mongo_config + self.logger = logger + self.CURRENT_VERSION = CURRENT_VERSION + self.initUI() + + def initUI(self): + self.setWindowTitle('AutoPercenty by 리앤수인터') + self.setGeometry(300, 300, 600, 500) # 위치와 크기 설정 + + grid = QGridLayout() + self.setLayout(grid) + + # 로그인 정보와 작업 상태 정보 라벨 추가 + self.loginInfoLabel = QLabel('로그인 정보가 여기 표시됩니다.') + grid.addWidget(self.loginInfoLabel, 0, 0, 1, 2) + + self.workStatusLabel = QLabel('작업 상태 정보가 여기 표시됩니다.') + grid.addWidget(self.workStatusLabel, 0, 2, 1, 1) + + # 로그인 버튼 추가 + self.loginButton = QPushButton('로그인') + self.loginButton.clicked.connect(self.openLoginWidget) + grid.addWidget(self.loginButton, 1, 0) + + # 로그 박스 추가 + self.logBox = QTextEdit() + self.logBox.setReadOnly(True) + grid.addWidget(self.logBox, 2, 0, 3, 3) # 2행 0열부터 4행 2열까지 + + def openLoginWidget(self): + # 여기서 LoginWidget 인스턴스를 생성하고 표시합니다. + self.login_widget = LoginWidget(self.mongo_config, self.CURRENT_VERSION) + self.login_widget.show() + +def main(): + CURRENT_VERSION = "1.0.1" + app = QApplication(sys.argv) + mongo_config = MongoConfig() + logger = setup_logger('default_logger', 'application.log', level=logging.DEBUG) + main_window = MainWindow(mongo_config, logger,CURRENT_VERSION) + main_window.show() + sys.exit(app.exec_()) diff --git a/modify_products.py b/modify_products.py index 4160f93..e84d1d0 100644 --- a/modify_products.py +++ b/modify_products.py @@ -14,6 +14,8 @@ from edit.options import modify_option_page from edit.price import modify_price_page from edit.title import modify_product_title from edit.thumbnail import modify_thumb_page +from edit.trans_image import * +from edit.uploadMarket import * from edit.action_elements import click_element, return_element, wait_element import logging @@ -63,6 +65,20 @@ logger = logging.getLogger('default_logger') # 'title_modification': modify_product_title, # } +def should_execute_step(step_name, login_info): + if step_name == 'tag_modification': + return login_info.whether_modifyProductTag + elif step_name == 'thumbnail_modification': + return login_info.whether_modifyProductThumb + elif step_name == 'option_modification': + return login_info.whether_modifyProductOptions + elif step_name == 'detail_page_modification': + return login_info.whether_modifyProductDetail + elif step_name == 'price_modification': + return login_info.whether_modifyProductPrice + else: + return True + def perform_step(db, step_name, action, current_user, *args): """ 단계별 작업을 수행하는 함수. @@ -171,10 +187,24 @@ def modify_products(driver, gemini, mongo_config, login_info, set_num_modify): ('thumbnail_modification', modify_thumb_page, (driver, product_infos[i-1])), ('option_modification', modify_option_page, (driver, product_infos[i-1])), ('detail_page_modification', modify_detail_page, (driver, product_infos[i-1], gemini, delv_collection, json_naver_codes)), - # ('price_modification', modify_price_page, (driver, product_infos[i-1])), - # 'title_modification' 단계와 관련된 함수 호출은 여기에 추가합니다. + ('price_modification', modify_price_page, (driver, product_infos[i-1])), + ('title_modification', modify_product_title, (driver, product_infos[i-1])), + ('trans_detailImage', tran_detail_image, (driver, product_infos[i-1])), + ('upload_to_market', upload_to_market, (driver, product_infos[i-1])), ] + # 상품 수정 관련 작업을 조건에 따라 수행하는 로직 + step_conditions = { + 'tag_modification': login_info.whether_modifyProductTag, + 'thumbnail_modification': login_info.whether_modifyProductThumb, + 'option_modification': login_info.whether_modifyProductOptions, + 'detail_page_modification': login_info.whether_modifyProductDetail, + 'price_modification': login_info.whether_modifyProductPrice, + 'title_modification': login_info.whether_modifyProductName, + 'trans_detailImage': login_info.whether_modifyImageTanslation, + 'upload_to_market': login_info.whether_uploadToMarket, + } + # 상품ID 복사 try: product_id_element = return_element(driver,"XPATH",f"//div[{i}]/li/div/div/div[2]/div/div/div/div[3]/div[3]/span[2]/span",10) @@ -205,9 +235,11 @@ def modify_products(driver, gemini, mongo_config, login_info, set_num_modify): time.sleep(2) for step_name, action, args in steps_and_actions: if step_name in incomplete_steps: - perform_step(autoPercentyProductsDB, step_name, action, current_user, *args) - autoPercentyProductsDB.finalize_product_processing(product_id, current_user, product_infos[i-1]) - logger.info(f"상품 ID {product_id}의 미완성 목록을 완성했습니다.") + # 각 단계별 실행 여부 판단 + if step_conditions.get(step_name, False): + perform_step(autoPercentyProductsDB, step_name, action, current_user, *args) + autoPercentyProductsDB.finalize_product_processing(product_id, current_user, product_infos[i-1]) + logger.info(f"상품 ID {product_id}의 미완성 목록을 완성했습니다.") pass else: # 여기에는 incomplete_steps가 False이거나 리스트의 길이가 6인 경우. @@ -356,7 +388,9 @@ def modify_products(driver, gemini, mongo_config, login_info, set_num_modify): # 각 단계별로 수정 작업 수행 for step_name, action, args in steps_and_actions: - perform_step(autoPercentyProductsDB, step_name, action, current_user, *args) + # 각 단계별 실행 여부 판단 + if step_conditions.get(step_name, False): + perform_step(autoPercentyProductsDB, step_name, action, current_user, *args) # 모든 단계가 완료되었다면, 상품 처리 최종화 autoPercentyProductsDB.finalize_product_processing(product_infos[i-1].id, current_user, product_infos[i-1]) diff --git a/src/_init.py b/src/_init.py new file mode 100644 index 0000000..e69de29 diff --git a/src/toggleSwitch.py b/src/toggleSwitch.py new file mode 100644 index 0000000..5c1d185 --- /dev/null +++ b/src/toggleSwitch.py @@ -0,0 +1,69 @@ +from PyQt5.QtCore import Qt, QRect, QPropertyAnimation, pyqtProperty, pyqtSignal, QPoint +from PyQt5.QtGui import QPainter, QColor +from PyQt5.QtWidgets import QWidget + +class ToggleSwitch(QWidget): + clicked = pyqtSignal(bool) + + def __init__(self, parent=None): + super(ToggleSwitch, self).__init__(parent) + self.setFixedSize(60, 30) + self._checked = False + self._circle_color_checked = QColor('red') + self._circle_color_unchecked = QColor('gray') + self._background_color = QColor('white') + self._circle_pos = QPoint(0, 0) # Circle's initial position. + self.animation = QPropertyAnimation(self, b"circle_pos") + self.animation.setDuration(250) + + self._init_position() + + @pyqtProperty(QPoint) + def circle_pos(self): + return self._circle_pos + + @circle_pos.setter + def circle_pos(self, pos): + self._circle_pos = pos + self.update() + + def _init_position(self): + if self._checked: + self._circle_pos.setX(30) + else: + self._circle_pos.setX(0) + + def mousePressEvent(self, event): + if event.button() == Qt.LeftButton: + self._checked = not self._checked + self.clicked.emit(self._checked) + self._update_animation() + self.update() + super(ToggleSwitch, self).mousePressEvent(event) + + def _update_animation(self): + if self._checked: + self.animation.setStartValue(QPoint(0, 0)) + self.animation.setEndValue(QPoint(30, 0)) + else: + self.animation.setStartValue(QPoint(30, 0)) + self.animation.setEndValue(QPoint(0, 0)) + self.animation.start() + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + painter.setPen(Qt.NoPen) + painter.setBrush(self._background_color) + painter.drawRoundedRect(QRect(0, 0, 60, 30), 15, 15) + + circle_color = self._circle_color_checked if self._checked else self._circle_color_unchecked + + painter.setBrush(circle_color) + painter.drawEllipse(self._circle_pos.x(), self._circle_pos.y(), 30, 30) + + def setChecked(self, checked): + if self._checked != checked: + self._checked = checked + self._update_animation() + self.update()