MENU

Python-seleniumではてなブログ自動投稿プログラムを作ってみる⑤~結合編その2~

こんにちは、のちたままです。

はてなブログ自動投稿プログラム製作の第5回です。

今回はGUIを完成させましょう。

コード全体

import PySimpleGUI as sg
import calendar
import hatenaprogram
import json

sg.theme('Dark Blue 3')

def mod_day(year, month, day):
    if year or month or day:
        if int(day) > int(calendar.monthrange(int(year),int(month))[1]):
            print('日付が正しくありません')
            exit()
    month='0'+month
    day='0'+day
    s=year[-4:]+'-'+month[-2:]+'-'+day[-2:]
    return s

def mod_time(time, minutes):
    if time or minutes:
        if int(time)>24 or int(time)<0:
            print('時刻が正しくありません')
            exit()
    time='0'+time
    minutes='0'+minutes
    return time[-2:]+':'+minutes[-2:]

def userjson_save(user,userid,year,month,day,time,minutes,checkid,checkday,checktime):
    if checkid:
        user['Id']=userid
    if checkday:
        user['Daytime']['Year']=year
        user['Daytime']['Month']=month
        user['Daytime']['Day']=day
    if checktime:
        user['Daytime']['Time']=time
        user['Daytime']['Minutes']=minutes
    user['Check']['Id']=checkid
    user['Check']['Day']=checkday
    user['Check']['Time']=checktime
    f=open('user.json','w')
    json.dump(user,f)
    f.close()

def main():
    dict1={1:'公開',2:'予約投稿',3:'下書き保存'}
    f=open('user.json','r')
    user=json.load(f)
    f.close()
    layout=[
        [sg.Text('ID',size=(15,1)),sg.InputText(default_text=user['Id'], key='ID'),sg.Checkbox('保存', key='ID_CHECK',default=user['Check']['Id'])],
        [sg.Text('Password',size=(15,1)),sg.InputText(key='PW')],
        [sg.Text('ファイル',size=(15,1)),sg.InputText(key='FILE'),sg.FileBrowse()],
        [sg.Text('公開設定',size=(15,1)),sg.Radio(dict1[1],'RADIO1',default=True,key='1'),sg.Radio(dict1[2],'RADIO1',key='2'),sg.Radio(dict1[3],'RADIO1',key='3')],
        [sg.Text('日付',size=(15,1)),sg.InputText(size=(5,1),default_text=user['Daytime']['Year'],key='YEAR'),sg.Text('年'),sg.InputText(size=(5,1),default_text=user['Daytime']['Month'],key='MONTH'),sg.Text('月'),sg.InputText(size=(5,1),default_text=user['Daytime']['Day'],key='DAY'),sg.Text('日'),sg.Checkbox('日付を保存する',default=user['Check']['Day'],key='DAY_CHECK')],
        [sg.Text('時刻',size=(15,1)),sg.InputText(size=(5,1),default_text=user['Daytime']['Time'],key='TIME'),sg.Text('時'),sg.InputText(size=(5,1),default_text=user['Daytime']['Minutes'],key='MINUTES'),sg.Text('分'),sg.Text(size=(8,1)),sg.Checkbox('時刻を保存する',default=user['Check']['Time'],key='TIME_CHECK')],
        [sg.Submit(button_text='実行する')]
    ]

    window=sg.Window('hatena',layout)

    while True:
        event, value=window.read()
        if event=='実行する':
            option=1 if value['1'] else 2 if value['2'] else 3
            if not value['FILE']:
                show_message='ファイルが選択されていません'
                sg.popup(show_message)
                exit()
            show_message=value['FILE']
            show_message+='を'+dict1[option]+'します。\n'
            show_message+='OKを押してください。\n'
            day=mod_day(value['YEAR'],value['MONTH'],value['DAY'])
            time=mod_time(value['TIME'],value['MINUTES'])
            userjson_save(user,value['ID'], value['YEAR'],value['MONTH'],value['DAY'], value['TIME'], value['MINUTES'], value['ID_CHECK'], value['DAY_CHECK'], value['TIME_CHECK'])
            sg.popup(show_message)
            hatenaprogram.main(value['ID'], value['PW'], day, time, option, value['FILE'])
        
        if event is None:
            print('exit')
            break
    window.close()

if __name__ == '__main__':
    main()

細かい説明

各関数での処理を説明していきます。

mod_day関数について

mod_dayは入力された年月日の成形をしています。

def mod_day(year, month, day):
    if year or month or day:
        if int(day) > int(calendar.monthrange(int(year),int(month))[1]):
            print('日付が正しくありません')
            exit()
    month='0'+month
    day='0'+day
    s=year[-4:]+'-'+month[-2:]+'-'+day[-2:]
    return s
引数:year(年),month(月),day(日)
返り値:成形された年月日(文字列)

calendar.monthrange()は引数に年と月を入れることで、その月の初めの日(1日)の曜日とその月が何日あるかを返します。

ここで欲しい返り値はその月が何日あるかなので末尾に[1]をつけています。

厳密にやるなら、年月日の欄に何も入力されていない場合、入力された年月日のいづれかがおかしな値をとっている場合の処理も必要ですが、自分で使う分には特に問題ありません。

返り値としてはてなブログ用に成形された年月日の文字列を返し、これをmainの中でスクリプト側に渡します。

mod_time関数について

目的はmod_day()と同じです。

こちらでは時刻の成形を行っています。

def mod_time(time, minutes):
    if time or minutes:
        if int(time)>24 or int(time)<0:
            print('時刻が正しくありません')
            exit()
        if int(minutes)>59 or int(minutes)<0:
            print('時刻が正しくありません')
            exit()
    time='0'+time
    minutes='0'+minutes
    return time[-2:]+':'+minutes[-2:]
引数:time(時)、minutes(分)
返り値:整形された時刻(文字列)

これもmain関数内で返り値をスクリプトに渡します。

userjson_save関数について

こちらについては前回紹介したので割愛します。

以下の記事を御覧ください。

nochitamama.hatenablog.com

main関数と残りの部分について

def main():
    dict1={1:'公開',2:'予約投稿',3:'下書き保存'}
    f=open('user.json','r')
    user=json.load(f)
    f.close()
    layout=[
        [sg.Text('ID',size=(15,1)),sg.InputText(default_text=user['Id'], key='ID'),sg.Checkbox('保存', key='ID_CHECK',default=user['Check']['Id'])],
        [sg.Text('Password',size=(15,1)),sg.InputText(key='PW')],
        [sg.Text('ファイル',size=(15,1)),sg.InputText(key='FILE'),sg.FileBrowse()],
        [sg.Text('公開設定',size=(15,1)),sg.Radio(dict1[1],'RADIO1',default=True,key='1'),sg.Radio(dict1[2],'RADIO1',key='2'),sg.Radio(dict1[3],'RADIO1',key='3')],
        [sg.Text('日付',size=(15,1)),sg.InputText(size=(5,1),default_text=user['Daytime']['Year'],key='YEAR'),sg.Text('年'),sg.InputText(size=(5,1),default_text=user['Daytime']['Month'],key='MONTH'),sg.Text('月'),sg.InputText(size=(5,1),default_text=user['Daytime']['Day'],key='DAY'),sg.Text('日'),sg.Checkbox('日付を保存する',default=user['Check']['Day'],key='DAY_CHECK')],
        [sg.Text('時刻',size=(15,1)),sg.InputText(size=(5,1),default_text=user['Daytime']['Time'],key='TIME'),sg.Text('時'),sg.InputText(size=(5,1),default_text=user['Daytime']['Minutes'],key='MINUTES'),sg.Text('分'),sg.Text(size=(8,1)),sg.Checkbox('時刻を保存する',default=user['Check']['Time'],key='TIME_CHECK')],
        [sg.Submit(button_text='実行する')]
    ]

    window=sg.Window('hatena',layout)

    while True:
        event, value=window.read()
        if event=='実行する':
            option=1 if value['1'] else 2 if value['2'] else 3
            if not value['FILE']:
                show_message='ファイルが選択されていません'
                sg.popup(show_message)
                exit()
            show_message=value['FILE']
            show_message+='を'+dict1[option]+'します。\n'
            show_message+='OKを押してください。\n'
            day=mod_day(value['YEAR'],value['MONTH'],value['DAY'])
            time=mod_time(value['TIME'],value['MINUTES'])
            userjson_save(user,value['ID'], value['YEAR'],value['MONTH'],value['DAY'], value['TIME'], value['MINUTES'], value['ID_CHECK'], value['DAY_CHECK'], value['TIME_CHECK'])
            sg.popup(show_message)
            hatenaprogram.main(value['ID'], value['PW'], day, time, option, value['FILE'])
        
        if event is None:
            print('exit')
            break
    window.close()

if __name__ == '__main__':
    main()

main()内の上半分は前回説明したので割愛します。

今回はwhile文の中身を説明します。

if event=='実行する':というのは「実行すると書かれたボタンが押されたら」という意味になります。

ボタンが押されるとoption=1 if value['1'] else 2 if value['2'] else 3で公開設定を読み取ります。

この書き方は三項演算子という書き方になります。

これは以下のものと同じ意味です。

if value['1']:
   option=1
elif value['2']:
   option=2
else:
   option=3

ifが2つ以上あると三項演算子は可読性が下がるのであまりおすすめはしません。

続いて、show_messageがいくつも連なっている部分ですが、ファイルが指定されていない場合にプログラムを終了するif文と、ファイルを指定した際にポップアップを表示させるコードです。

特に詳しく説明するところでもないです。

あとは先程説明したmod_day,mod_timeに値を渡して返り値をday,timeに入れています。

これをスクリプトの関数、今回はスクリプトをhatenaprogram.pyという名前にしたので、そのなかのmain関数(hatenaprogram.main)に各値を渡しています。

最後のif __name__=='__main__'については以下の記事が参考になると思います。

note.nkmk.me

最後に

長々と読んでいただきありがとうございます。

これでGUI側は完成です。

色々と荒い部分はありますが、趣味で作る程度ならこれくらいでも良いと自分では思います。

60点でもいいからまずは完成させる方が大切です。

あとはスクリプト(hatenaprogram.py)の修正をすれば完成になります。

それではまた次回をお待ちください。