본문 바로가기
일/Python

파이썬(python) - Project - Macro with GUI

by 멜랑멀리 2022. 12. 21.
SMALL

1. macro 수행을 위한 python code 생성.

2. exe형태로 만들기.

3. GUI로 Save, Load 등 수행.

 

(flow chart)

1. site 정보 / id 정보 / pw 정보 기입 (해당 위치 정보 기입)  

- 관련 정보 저장, 읽기, clear 수행 - load info, save info , clear , start, quit 버튼사용.

2. 반복 횟수, 사용하는 macro 개수 정보

- repeat num : 반복횟수

- macro num : 사용 좌표 개수

(예상 구조)

 

실제 구조)

XYlocation 정보를 확인할수 있게 추가적용

- 해상도에 따라 입력 x, y 위치가 달라지기 때문에 XYlocation 확인후 해당 위치값 사용하면 됨.

repeatnum 과 macronum 반복수행함.

- (macronum 4개는 4개의 pattern 사용, repeatnum 5개는 macronum 4개를 5번 반복 수행함.)

(실제 구조)

주요 import)

pyautogui : window내의 macro 위치 선택.

argparse : macro 개수가 defalut로 10개인데, 20개, 30개 등 argument로 바꿀수 있게 해놓음.

pyQt5 : GUI를 위해 사용

selenium : chrome website open 용

 

[ python code ]

 

import sys, os
import subprocess

import pyautogui
import argparse

from PyQt5.QtWidgets import QMessageBox, QFileDialog, QLineEdit, QCheckBox, QApplication, QPushButton, QLabel, QMainWindow, QDesktopWidget
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QCoreApplication, QDate, Qt, QTime

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import TimeoutException

macrocntroom = 10
version = "0.10(20210801)"

class MyApp(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        parser = argparse.ArgumentParser()
        parser.add_argument('--mcnt', required=False, default=10, help='macro number (max:~20)')
        args = parser.parse_args()

        macrocntroom = int(args.mcnt)

        self.statusBar()
        self.mylabel()
        self.mytext()

        self.mycheckbox(macrocntroom)
        self.mycontent(macrocntroom)
        self.idpwloc()
        self.myloc(macrocntroom)
        self.topbotton(macrocntroom)

        # window run
        self.windowdisplay(macrocntroom)

    def windowdisplay(self,macrocnt):
        ndate = QDate.currentDate()
        ntime = QTime.currentTime()
        self.setWindowTitle('Macro GUI : '+ ndate.toString(Qt.ISODate) +' ' + ntime.toString() + '  Ver:' +str(version))
        self.setWindowIcon(QIcon('windowicon.png'))
        self.setGeometry(300, 300, 800, 220+macrocnt*20 ) #loc(x,y) , size (x,y)
        self.center()
        self.show()

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def openwebpage(self,url,waittime):
        options = webdriver.ChromeOptions()
        options.add_experimental_option("excludeSwitches", ["enable-logging"])
        if getattr(sys, 'frozen', False):
            chromedriver_path = os.path.join(sys._MEIPASS, "chromedriver.exe")
            self.browser = webdriver.Chrome(chromedriver_path, options=options)

        else:
            self.browser = webdriver.Chrome('chromedriver', options=options)


        self.browser.maximize_window()
        self.browser.get(url)
        self.browser.implicitly_wait(time_to_wait=int(waittime)) #5초이내에 page가 load되면 next.

        # self.waitpage("h1") #최적화?
        self.waitpage("html", waittime)

    def waitpage(self,name,waittime):
        timeout = int(waittime)
        try:
            element_present = EC.presence_of_element_located((By.TAG_NAME, name))
            WebDriverWait(self.browser, timeout).until(element_present)
        except TimeoutException:
            #print(QTime.currentTime().toString())
            print("Timed out waiting for page to load")

    def showLoadDialog(self):
        floadname = QFileDialog.getOpenFileName(self, 'Open file', './', '*.txt')
        textvalue = 0
        if floadname[0]:
            f = open(floadname[0], 'r')
            with f:
                lines = f.read().splitlines()
                #print(lines)
                ### clear text
                for k in range(4):
                    self.leMytext[k].setText("")
                for kk in range(3):
                    self.leidpwlocX[kk].setText("")
                    self.leidpwlocY[kk].setText("")
                ###load data to lineedit text
                j=0
                for i in lines[ :3]:
                    texti, textX, textY = i.split(sep=',')
                    self.leMytext[j].setText(str(texti))
                    self.leidpwlocX[j].setText(str(textX))
                    self.leidpwlocY[j].setText(str(textY))
                    j = j + 1

                ### load data to lineedit text(repeat num)
                self.leMytext[3].setText(str(lines[3]))

                ### load data to lineedit text(macro num)
                for k in lines[4:]:
                    textvalue = textvalue + 1
                self.leMytext[4].setText(str(textvalue))
                ### clear loc
                for k in range(10):
                    self.leMylocX[k].setText("")
                    self.leMylocY[k].setText("")
                ### load data to lineedit loc(x,y)
                jj = 0
                for k in lines[4: ]:
                    splitX, splitY = k.split(sep=',')
                    self.leMylocX[jj].setText(str(splitX))
                    self.leMylocY[jj].setText(str(splitY))
                    jj = jj + 1
        else:
            QMessageBox.warning(self, "warning", "Load 파일을 선택하지 않았습니다.")

    def showSaveDialog(self):
        fsavename = QFileDialog.getSaveFileName(self, 'Save file', './', '*.txt')
        if fsavename[0] :
            f = open(fsavename[0], 'w')
            stext = {}
            sloc = {}
            for i in range(3):
                stext[i] = self.leMytext[i].text()+','+self.leidpwlocX[i].text()+','+self.leidpwlocY[i].text() + '\n'
                f.write(stext[i])
            repeatcnt = self.leMytext[3].text()
            f.write(repeatcnt + '\n')
            macrocnt = self.leMytext[4].text()
            if macrocnt :
                for i in range(int(macrocnt)):
                    sloc[i] = self.leMylocX[i].text() + ',' + self.leMylocY[i].text() + '\n'
                    f.write(sloc[i])
            f.close()
        else :
            QMessageBox.warning(self, "warning", "Save 파일을 선택하지 않았습니다.")

    def showClear(self,macrocnt):
        for i in range(5):
            self.leMytext[i].setText("")
        for i in range(3):
            self.leidpwlocX[i].setText("")
            self.leidpwlocY[i].setText("")
        for i in range(macrocnt):
            self.leMylocX[i].setText("")
            self.leMylocY[i].setText("")

    def showClose(self):
        #print("close chromedriver.exe")
        subprocess.call("taskkill /F /IM chromedriver.exe /T")

    def showStartDialog(self):
        self.showMinimized() #화면 최소화
        screenwidth, screenheight = pyautogui.size()
        waittime = 10
        if self.cbon0:
            url=self.leMytext[0].text()
            if url is not "":
                self.openwebpage(url,waittime)
                pyautogui.moveTo(int(self.leidpwlocX[0].text()), int(self.leidpwlocY[0].text()), 0.1)
                pyautogui.click()
                # self.waitpage("h1")
                self.waitpage("html", waittime)
        #else:
        #    self.openwebpage("http://google.com", waittime)
        if self.cbon1:
            id = self.leMytext[1].text()
            if id is not "":
                pyautogui.moveTo(int(self.leidpwlocX[1].text()), int(self.leidpwlocY[1].text()), 0.1)
                pyautogui.click()
                pyautogui.typewrite(id, interval=0)
        if self.cbon2:
            pw = self.leMytext[2].text()
            if pw is not "":
                pyautogui.moveTo(int(self.leidpwlocX[2].text()), int(self.leidpwlocY[2].text()) , 0.1)
                pyautogui.click()
                pyautogui.typewrite(pw, interval=0)
                #pyautogui.moveTo(self.leidpwlocX[2].text(), self.leidpwlocY[2].text()+30, 0)
                pyautogui.typewrite(['enter'], interval=0)
                #pyautogui.click()
                self.waitpage("html", waittime)
        if self.cbon3:
            if not self.cbon0 and self.cbon1 and self.cbon2:
                pyautogui.hotkey('alt', 'tap')
            repeatnum = int(self.leMytext[3].text())
            macronum = int(self.leMytext[4].text())
            if macronum is not "":
                for j in range(repeatnum):
                    for i in range(macronum):
                        pyautogui.moveTo(int(self.leMylocX[i].text()), int(self.leMylocY[i].text()), 0.1)
                        pyautogui.click()


    def macrolocation(self):
        position = pyautogui.position()
        self.leidpwlocX[4].setText(str(position.x))
        self.leidpwlocY[4].setText(str(position.y))


    def mytext(self):
        self.leMytext ={}
        for i in range(5) :
            self.leMytext[i] = QLineEdit(self)
            self.leMytext[i].resize(self.leMytext[i].sizeHint())
            self.leMytext[i].move(300, 100+20*i)
            self.leMytext[i].adjustSize()
            self.leMytext[i].setText("")

    def myloc(self,num):
        self.leMylocX = {}
        self.leMylocY = {}
        for i in range(num):
            self.leMylocX[i] = QLineEdit(self)
            self.leMylocX[i].resize(self.leMylocX[i].minimumSizeHint())
            self.leMylocX[i].move(600, 100 + 5 * 20 + i*20)
            self.leMylocX[i].setText("")
            self.leMylocY[i] = QLineEdit(self)
            self.leMylocY[i].resize(self.leMylocY[i].minimumSizeHint())
            self.leMylocY[i].move(700, 100 + 5 * 20 + i*20)
            self.leMylocY[i].setText("")

    def idpwloc(self):
        self.leidpwlocX = {}
        self.leidpwlocY = {}
        for i in range(0,3):
            self.leidpwlocX[i] = QLineEdit(self)
            self.leidpwlocX[i].resize(self.leidpwlocX[i].minimumSizeHint())
            self.leidpwlocX[i].move(600, 100 + i * 20)
            self.leidpwlocX[i].setText("")
            self.leidpwlocY[i] = QLineEdit(self)
            self.leidpwlocY[i].resize(self.leidpwlocY[i].minimumSizeHint())
            self.leidpwlocY[i].move(700, 100 + i * 20)
            self.leidpwlocY[i].setText("")
        #tmp 위치정보
        self.leidpwlocX[4] = QLineEdit(self)
        self.leidpwlocX[4].resize(self.leidpwlocX[4].minimumSizeHint())
        self.leidpwlocX[4].move(500, 50)
        self.leidpwlocX[4].setText("")
        self.leidpwlocY[4] = QLineEdit(self)
        self.leidpwlocY[4].resize(self.leidpwlocY[4].minimumSizeHint())
        self.leidpwlocY[4].move(550, 50)
        self.leidpwlocY[4].setText("")

    def mycheckbox(self,num):
        self.cb0 = QCheckBox('On/Off', self)
        self.cb0.move(100, 100 + 0 * 20)
        self.cb0.toggle()
        self.cbon0=1
        self.cb0.stateChanged.connect(self.mycheckboxinfo)
        self.cb1 = QCheckBox('On/Off', self)
        self.cb1.move(100, 100 + 1 * 20)
        self.cb1.toggle()
        self.cbon1 = 1
        self.cb1.stateChanged.connect(self.mycheckboxinfo)
        self.cb2 = QCheckBox('On/Off', self)
        self.cb2.move(100, 100 + 2 * 20)
        self.cb2.toggle()
        self.cbon2 = 1
        self.cb2.stateChanged.connect(self.mycheckboxinfo)
        self.cb3 = QCheckBox('On/Off', self)
        self.cb3.move(100, 100 + 3 * 20)
        self.cb3.toggle()
        self.cbon3 = 1
        self.cb3.stateChanged.connect(self.mycheckboxinfo)

    def mycheckboxinfo(self):
       if self.cb0.isChecked():
           self.cbon0 = 1
       else :
           self.cbon0 = 0
       if self.cb1.isChecked():
           self.cbon1 = 1
       else :
           self.cbon1 = 0
       if self.cb2.isChecked():
           self.cbon2 = 1
       else :
           self.cbon2 = 0
       if self.cb3.isChecked():
           self.cbon3 = 1
       else :
           self.cbon3 = 0

    def changeTitle(self, state):
        if state == Qt.Checked:
            self.setWindowTitle('QCheckBox')
        else:
            self.setWindowTitle(' ')

    def mylabel(self):
        label1 = QLabel('<b>On/Off Check</b>', self)
        label1.move(100, 80)
        label2 = QLabel('<b>내용</b>', self)
        label2.move(200, 80)
        label3 = QLabel('<b>정보입력</b>', self)
        label3.move(300, 80)
        label4 = QLabel('<b>좌표(X)</b>', self)
        label4.move(600, 80)
        label5 = QLabel('<b>좌표(Y)</b>', self)
        label5.move(700, 80)

    def mycontent(self,num):
        label0 = QLabel('Web Site: ', self)
        label0.move(200, 100 + 20 * 0)
        label1= QLabel('ID: ', self)
        label1.move(200, 100 + 20 * 1)
        label2 = QLabel('PW: ', self)
        label2.move(200, 100 + 20 * 2)
        label3 = QLabel('RepeatNum: ', self)
        label3.move(200, 100 + 20 * 3)
        label3 = QLabel('MacroNum: ', self)
        label3.move(200, 100 + 20 * 4)
        for i in range(num):
            label4 = QLabel('MacroPattern'+str(i)+' : ', self)
            label4.move(200, 100 + 20 * 5 + 20 * i)

    def topbotton(self,macrocnt):
        # Load 버튼
        btnl = QPushButton('Load Info.', self)
        btnl.setToolTip('정보 읽기')
        btnl.move(100, 20)
        btnl.resize(btnl.sizeHint())
        btnl.clicked.connect(self.showLoadDialog)

        # Save 버튼
        btns = QPushButton('Save Info.', self)
        btns.setToolTip('정보 쓰기')
        btns.move(200, 20)
        btns.resize(btns.sizeHint())
        btns.clicked.connect(self.showSaveDialog)

        # Clear 버튼
        btns = QPushButton('Clear', self)
        btns.setToolTip('정보 지우기')
        btns.move(300, 20)
        btns.resize(btns.sizeHint())
        btns.clicked.connect(lambda:self.showClear(macrocnt))

        # tmp 버튼
        btnt = QPushButton('XYlocation', self)
        btnt.setToolTip('XYlocation')
        btnt.move(500, 20)
        btnt.resize(btns.sizeHint())
        btnt.clicked.connect(self.macrolocation)

        # Start 버튼
        btnst = QPushButton('Start', self)
        btnst.setToolTip('동작 버튼')
        btnst.move(600, 20)
        btnst.resize(btnst.sizeHint())
        btnst.clicked.connect(self.showStartDialog)

        #Quit 버튼
        btnq = QPushButton('Quit', self)
        btnq.setToolTip('종료 버튼')
        btnq.move(700, 20)
        btnq.resize(btnq.sizeHint())
        btnq.clicked.connect(self.showClose)
        btnq.clicked.connect(QCoreApplication.instance().quit)



if __name__ == '__main__':
   app = QApplication(sys.argv)
   ex = MyApp()
   sys.exit(app.exec_())

[ exe file 만들기 ]

- chrome 사용(chromedriver.exe file 함께 사용)

- exe file size 줄이기 위 numpy, pandas 제외하여 exe 만들기.

pyinstaller -F -w --exclude pandas, --exclude numpy --onefile --add-binary "chromedriver.exe";"." macro_gui.py

 

 

LIST