1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【実装】PC操作自動化システム

Last updated at Posted at 2021-01-10

#概要
設計検討
https://qiita.com/asahi4549/items/b63affa193a2f422586a
をもとに実装してみた。

ezgif.com-gif-maker (1).gif

#各機能実装説明
GUI画面を示す。各機能について同実装したか説明する
image.png

"Read picture"クリック時の処理コードを示す。
フォルダを選択できるダイアログを表示し、そこで選択されたフォルダのpath情報を取得する。
path情報から選択されたフォルダ内の画像ファイルをリストデータとして保存しウィジェットに表示させている

AK003_V1002.py

    def click_read_file(self):
        #ファイルオープンダイアログの表示
        #(file_path, selectedFilter) = QtWidgets.QFileDialog.getOpenFileName(self,filter="png jpeg (*.png *.jpeg)")
        #フォルダのみ選択する方法に変更
        #第2引数はダイアログのタイトル, 第3引数は初期表示したいpath
        dir_path_tmp = QtWidgets.QFileDialog.getExistingDirectory(self, 'Open Directory', "./picture")

        if(dir_path_tmp != ""):
            #path_absは絶対path
            path_abs = pathlib.Path(dir_path_tmp)
            #relative_toで相対パスに変換,引数は起点となるパス
            #path_abs.cwd()でカレントディレクトリのパスを取得
            self.dir_path_rel = path_abs.relative_to( path_abs.cwd() )      
            #pathからファイル名取得
            file_name  = os.listdir(self.dir_path_rel)
            #file_name = os.path.basename(file_path)
            print(file_name)
            #.pngファイル名をPicture listにセット
            #ファイル名をリストモデルに追加
            #self.list_model.setStringList([file_name])
            self.list_model.setStringList(file_name)
            #リストモデルをリストビューに追加
            #リストビューに追加することでウィジェットに反映される
            self.ui.listView_picture_list.setModel(self.list_model)
AK003_V1002.py
    def click_execute(self):        
        #code_text解析処理
        #code textの文字列取得
        code = self.ui.plainTextEdit_code_text.toPlainText()

        
        #codeを引数にして文法チェック関数に渡す。check_syntaxの中で改行判定を行い、命令分を抽出する
        #selfも引数で渡さないと,uiの変数が扱えない
        syntax_result = Check_syntax(self,code)

        
        #textコードに問題がなければ実行用リストの実行
        if(syntax_result != FAIL):
            self.ui.plainTextEdit_result.appendPlainText("executed successfully")
            #デスクトップ画面を表示
            msg_ret = QMessageBox.question(None,"確認","自動化プログラムを実行しますがよろしいですか?", QMessageBox.Ok,QMessageBox.Cancel )
            if(msg_ret != QMessageBox.Cancel ):       
                #実行用リストを上から実行
                pg.sleep(2)
                QMessageBox.information(None, "情報","デスクトップ画面を表示してから2sec後にこの画面が表示されているはず")
                args_cnt = 0
                
                for func in self.list_exec_func:
                    exec_handler(self,func, self.list_exec_args[args_cnt])
                    time.sleep(0.5)
                    args_cnt = args_cnt + 1
                #実行用配列をクリア
                #2回目のコード実行時に前回の命令が残らないように
                self.list_exec_func.clear()
                self.list_exec_args.clear()
            else:
                self.ui.plainTextEdit_result.appendPlainText("program cancelled")
                return
        #文法チェックがngのときエラー処理
        else:
            self.ui.plainTextEdit_result.appendPlainText("execution failure")
            return
AK003_V1002.py
    def click_save_code(self):
        #code_txtから文字列取得
        code = self.ui.plainTextEdit_code_text.toPlainText()
        
        #保存ファイル名取得
        (file_name_path, selectedFilter) = QtWidgets.QFileDialog.getSaveFileName(self, 'Open Directory', './code')
        if(file_name_path != ''):
            #pathからファイル名取得
            file_name  = os.path.basename(file_name_path)
            self.ui.plainTextEdit_result.appendPlainText(file_name+"を保存しました")
        #取得した文字列をほぞしたいファイルに書き出し
        #withを使うことで自動でcloseしてくれる
            with open('./code/'+file_name,"w" ) as save_code_file:
                save_code_file.write(code)
            
#コールバック関数
def exec_handler(self,func , *arg):
    func(self,*arg)
 

```python:AK003_V1002.py
#文法チェック関数
def Check_syntax(self,txt_code):
    
    #改行の文字列までの命令分を抽出
    list_code_tmp = []

    #特定の文字列の場所を取得して、そこまで削除を考えたが、改行でリストかする関数があったのでそちらを使う
    #改行ごとでリスト化
    list_code_tmp = txt_code.splitlines()
    print(list_code_tmp)
    
    #コードリストの中身を上から文法チェック
    for code in list_code_tmp:    #clickの文字を正規表現で抽出
        #note: matchは先頭文字からあっているか
        if(re.match('click',code)):
            #note 過去の文字を消さずに文字を追加
            #setPlainTextもあるがこれは過去の文字を無視する
            #self.ui.plainTextEdit_result.appendPlainText(code_tmp)
            #()があるか
            #note()も正規表現の一つなので\でエスケープしている
            #(から)で終わる文字の最後を抽出
            ob_recode_tmp = re.search('\(.*\)',code)
            print(ob_recode_tmp)
            #()が見つからない場合はエラー処理
            #note オブジェクトの一致判定は is をつかう
            if(ob_recode_tmp is not None):
                #group()でmatchした文字列を抽出
                code_tmp = ob_recode_tmp.group()
                #引数cod_argを抽出
                #note 0文字目と-1文字目以外を抽出
                code_arg = code_tmp[1:-1]
                print(code_arg)
                #clickの引数は画像idで与えられるので、idをlist_modelのインデックスのメンバにし、list_model内のデータを取得する
                #index指定して.dataでリストモデルに登録してるデータを取得できる
                #indexの引数はintなのでintに変換している
                code_arg = self.list_model.index(int(code_arg),0).data()
                
                #引数をselectpictureの中から探索
                #note stringListでlist_modelからリストを抽出,
                #if inより完全一致検索をかける。一文字でもあっている場合は判定したいときはmatch関数をつかう
                str_list = self.list_model.stringList()
                #str_listの中にcode_argがあるか判定
                if code_arg in str_list:
                    self.ui.plainTextEdit_result.appendPlainText("画像が見つかりました")
                    #clickを実行用リストに追加
                    
                    self.list_exec_func.append(pc_op.click)
                    self.list_exec_args.append(code_arg)
                else:
                    self.ui.plainTextEdit_result.appendPlainText("引数が正しくありません")
                    return FAIL
            else:
                self.ui.plainTextEdit_result.appendPlainText("()がありません")
                return FAIL            
        elif(re.match('dclick',code)):
            #note 過去の文字を消さずに文字を追加
            #setPlainTextもあるがこれは過去の文字を無視する
            #self.ui.plainTextEdit_result.appendPlainText(code_tmp)
            #()があるか
            #note()も正規表現の一つなので\でエスケープしている
            #(から)で終わる文字の最後を抽出
            ob_recode_tmp = re.search('\(.*\)',code)
            print(ob_recode_tmp)
            #()が見つからない場合はエラー処理
            #note オブジェクトの一致判定は is をつかう
            if(ob_recode_tmp is not None):
                #group()でmatchした文字列を抽出
                code_tmp = ob_recode_tmp.group()
                #引数cod_argを抽出
                #note 0文字目と-1文字目以外を抽出
                code_arg = code_tmp[1:-1]
                print(code_arg)
                #clickの引数は画像idで与えられるので、idをlist_modelのインデックスのメンバにし、list_model内のデータを取得する
                #index指定して.dataでリストモデルに登録してるデータを取得できる
                #indexの引数はintなのでintに変換している
                code_arg = self.list_model.index(int(code_arg),0).data()
                
                #引数をselectpictureの中から探索
                #note stringListでlist_modelからリストを抽出,
                #if inより完全一致検索をかける。一文字でもあっている場合は判定したいときはmatch関数をつかう
                str_list = self.list_model.stringList()
                #str_listの中にcode_argがあるか判定
                if code_arg in str_list:
                    self.ui.plainTextEdit_result.appendPlainText("画像が見つかりました")
                    #dclickとその引数を実行用リストに追加
                    self.list_exec_func.append(pc_op.dclick)
                    self.list_exec_args.append(code_arg)
                else:
                    self.ui.plainTextEdit_result.appendPlainText("引数が正しくありません")
                    return FAIL
            else:
                self.ui.plainTextEdit_result.appendPlainText("()がありません")
                return FAIL            
        elif(re.match('typekey',code)):
            #()があるか
            #note()も正規表現の一つなので\でエスケープしている
            #(から)で終わる文字の最後を抽出
            ob_recode_tmp = re.search('\(.*\)',code)
            #()が見つからない場合はエラー処理
            #note オブジェクトの一致判定は is をつかう
            if(ob_recode_tmp is not None):
                #group()でmatchした文字列を抽出
                code_tmp = ob_recode_tmp.group()
                #引数cod_argを抽出
                #note 0文字目と-1文字目以外を抽出
                code_arg = code_tmp[1:-1]
                print(code_arg)
                #typekey関数とその引数を実行リストに追加
                self.list_exec_func.append(pc_op.typekey)
                self.list_exec_args.append(code_arg)
            else:
                self.ui.plainTextEdit_result.appendPlainText("()がありません")
                return FAIL            
        elif(re.match('wait',code)):
            #()があるか
            #note()も正規表現の一つなので\でエスケープしている
            #(から)で終わる文字の最後を抽出
            ob_recode_tmp = re.search('\(.*\)',code)
            #()が見つからない場合はエラー処理
            #note オブジェクトの一致判定は is をつかう
            if(ob_recode_tmp is not None):
                #group()でmatchした文字列を抽出
                code_tmp = ob_recode_tmp.group()
                #引数cod_argを抽出
                #note 0文字目と-1文字目以外を抽出
                code_arg = code_tmp[1:-1]
                #typekey関数とその引数を実行リストに追加
                self.list_exec_func.append(pc_op.wait)
                self.list_exec_args.append(code_arg)
            else:
                self.ui.plainTextEdit_result.appendPlainText("()がありません")
                return FAIL                        
        elif(re.match('enter',code)):
            self.list_exec_func.append(pc_op.enter)
            code_arg = 0
            self.list_exec_args.append(code_arg)
        elif(re.match('desktop',code)):
            self.list_exec_func.append(pc_op.desktop)
            code_arg = 0
            self.list_exec_args.append(code_arg)            
        else:
            self.ui.plainTextEdit_result.appendPlainText("invalid function")
            return FAIL    

        
    return SUCCESS


AK003_V1001func.py
# -*- coding: utf-8 -*-
"""
Created on Sun Dec 27 16:27:00 2020

@author: asahi
"""
import time
import pyautogui as pg
import threading


def click(self,args):
    print("clickが呼ばれました引数{}".format(args))
    #state_search = 'STATE_SEARCH_CHROME'
    #検索画像が見つかった場所の座標系が返る
    nsec = 0
    timeout = 5
    while True:
        #state_search = 'STATE_SEARCH_CHROME'
        #検索画像が見つかった場所の座標系が返る
        #button_position = get_locate_from_filename(args)
        #pg.hotkeyがすぐ動いてくれない?
        #note
        #.はpythonファイルがあるフォルダ(カレントディレクトリ)を表しpictureフォルダ内の画像を引数とする
        #+を使うことで文字列の中に変数を展開できる
        #print(str(self.dir_path_rel)+"\\"+args)
        #print("./picture/grope_0/"+args)
        #dir_path_relはwindowsパス(¥¥)表記なので+で連結できない
        #なのでstrで文字列に直す
        button_position = get_locate_from_filename("./"+str(self.dir_path_rel)+"/"+args)
        #button_position = get_locate_from_filename("./picture/grope_0/"+args)
        #button_position = get_locate_from_filename("C:/Users/asahi/Documents/30_業務/40_改善提案/FF_kitada/00_program/AK003_V1001/picture/grope_0/chrome.png")
        if(button_position != None):
            #pg.click(button_position)
            print("button_position = {}".format(button_position) )
            pg.click(button_position)
            break
        else:
            time.sleep(0.5)
            print("もう一回探します")            
            nsec += 1
            if( nsec > timeout):
                pg.alert(text = 'タイムアウト', button ='OK')
                break

def dclick(self,args):
    print("dclickが呼ばれました引数{}".format(args))
    #state_search = 'STATE_SEARCH_CHROME'
    #検索画像が見つかった場所の座標系が返る
    nsec = 0
    timeout = 5
    print(str(self.dir_path_rel))
    while True:
        button_position = get_locate_from_filename("./"+str(self.dir_path_rel)+"/"+args)
        if(button_position != None):
            print("button_position = {}".format(button_position) )
            pg.doubleClick(button_position)
            break
        else:
            time.sleep(0.5)
            print("もう一回探します")            
            nsec += 1
            if( nsec > timeout):
                pg.alert(text = 'タイムアウト', button ='OK')
                break
def typekey(self,args):
    print("typekeyが呼ばれました引数{}".format(args))
    
    pg.typewrite(args)

def enter(self,args):
    print("enterが呼ばれました引数{}".format(args))
    pg.press('enter',presses = 2, interval = 0.5)
    
def wait(self,args):
    print("waitが呼ばれました引数{}".format(args))
    #sleepの引数はintでないといけないのでキャストしている
    time.sleep(int(args))
def press_key(self,args):
    print("press_keyが呼ばれました")
    pass
def desktop(self,args):
    print("desktopが呼ばれました")              
    pg.hotkey('win','d')
    

#画像ファイルから座標を取得する処理
def get_locate_from_filename(filename):
    locate = None
    time.sleep(1)
    #グレイスケール処理で95%一致判定
    #検索画像の座標取得
    print(filename)
    try:
        locate = pg.locateCenterOnScreen(filename,grayscale = True,confidence=0.8)
        return locate
    except:
        print("画像ありません")
        return False

1
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?