ろうとるがもっと頭がよくなるために
こちらの記事「ろうとるがPythonを扱う、、(その12:回線速度checkプログラム)」の続きである。少しでもスマートになろうとしたときのメモ。
すること
- ダウンロード時の進行状況表示
- コード簡略化
先に実行状況
各サイズに相当するボタンをクリックすると、指定回数繰り返しダウンロードし、速度などを表示するとともに、CSVファイルを作成。青枠で囲った部分で示すように、ダウンロード状況(%)を表示する。
ソースコード
いつものように、ポイントとなる箇所を中心に説明。最後にコード全体を記載。
import
import logging
import logging.handlers
from datetime import datetime
import time
import urllib.request # ダウンロード用URLライブラリ
import tkinter
from tkinter import *
import tkinter.ttk as ttk
import threading # スレッド
from functools import partial # 複数ボタンをまとめて定義(後述)
説明略。
定義
w_width = 750
w_height = 400
speed_list = [1, 5, 10, 30, 100]
url_list = [
'https://www.4fgh.dr.jp/data/1MByte',
'https://www.4fgh.dr.jp/data/5MByte',
'https://www.4fgh.dr.jp/data/10MByte',
'https://www.4fgh.dr.jp/data/30MByte',
'https://www.4fgh.dr.jp/data/100MByte']
file_list = ['1Mbyte', '5Mbyte', '10Mbyte', '30Mbyte', '100Mbyte']
p_flag = 0 # for progress
- Windowサイズの定義。
- ダウンロードするファイルのURL(仮のもの)、サイズ定義など。
- ダウンロード状況表示に関連するフラグ(p_flag)。
メイン
class NetworkCheck(ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.create_widgets()
self.pack()
...(略)
## End of NetworkCheck()
if __name__ == '__main__':
master = Tk()
master.title("Repeat Download 2")
master.geometry("750x400")
# Call main part
NetworkCheck(master)
master.mainloop()
- tkinterフレームワークにて、NetworkCheckというメイン関数をCall。
- create_widgetsにて、各要素を配置。
def create_widgets(self):
...(略)
## Start of create_widgets()
# Log
log = logging.getLogger('Download')
log.setLevel(logging.DEBUG)
fh = logging.FileHandler('{:Download_Log-%Y%m%d-%H%M%S}.csv'.format(datetime.now()))
log.addHandler(fh)
header = 'Start Time, Size(Mbyte), Elapsed Time(ms), Speed(Mbps), Failure,'
log.debug(header)
# Tab
note = ttk.Notebook(self)
note.pack()
note0 = ttk.Frame(note, width=w_width, height=w_height)
note.add(note0, text=" Click Download File Size ")
cr_tab(note0)
## End of createwidgets()
- CSV化するログファイルの定義。
- タブの定義(1つしか存在しないが)。
タブ
def cr_tab(frm):
...(略)
## Start of cr_tab()
frm1 = ttk.Frame(frm, width=w_width, height=w_height)
frm1.Btn = [ttk.Button(frm1, text=file_list[i], style='W.TButton', command=partial(clk_btn, i)) \
for i in range(5)]
[frm1.Btn[i].grid(row = 0, column = i) for i in range(5)]
frm1.label = ttk.Label(frm1, text=' Repetition Number ')
frm1.label.grid(row = 0, column = 5)
frm1.text = ttk.Entry(frm1, width = 10)
frm1.text.grid(row = 0, column = 6)
frm2 = ttk.Frame(frm, width=w_width, height=w_height)
frm2.label = ttk.Label(frm2, text = 'Start Time, Size(Mbyte), Elapsed Time(ms), Speed(Mbps), Failure')
frm2.label.grid(row = 0, column = 0)
frm2.dummy = ttk.Label(frm2, text = ' ')
frm2.dummy.grid(row = 0, column = 1)
frm2.progress = ttk.Label(frm2, text = ' ')
frm2.progress.grid(row = 0, column = 2)
frm3 = ttk.Frame(frm, width=w_width, height=w_height)
frm3.prt = Text(frm3, font=("Courier New", 15), width=120, height=33)
frm3.ysc = tkinter.Scrollbar(frm3, orient=tkinter.VERTICAL, command=frm3.prt.yview)
frm3.ysc.pack(side=tkinter.RIGHT, fill="y")
frm3.prt["yscrollcommand"] = frm3.ysc.set
frm3.prt.config(state='disabled')
frm3.prt.pack()
frm1.pack()
frm2.pack()
frm3.pack()
## End of cr_tab()
- タブ内に3つのフレームを構成。
- 5つのボタン(frm1.Btn)の定義および配置。
- for文による繰り返しについては、こちらのサイト「Tkinterでオブジェクトを複数、変数に依存して作る(覚え)」参照。
- 複数ボタンクリック時のコマンド(関数)については、こちらのサイト「Python Tkinterの大量のボタンをpartialを使って楽に設置」参照。
- 繰り返し回数指定用ボックス・変数(frm1.text)の定義。
- ダウンロード進行状況表示用テキスト(frm2.progress)の定義。
- 結果表示用フィールド(frm3.prt)の定義。
ボタンクリック
## Start of clk_btn()
def clk_btn(size):
th = threading.Thread(target=download, args=(size, ))
th.start()
ダウンロード進行状況や結果をリアルタイムに表示するには、ボタンクリック時にCallされる部分をスレッド化する必要がある。これについては、こちらのサイト「tkinterで遅い処理を別スレッドに投げ画面が固まらないようにする」を参考にした。Call元にて定義された変数は、Call元に戻らないと更新されないようだ。
ダウンロード
## Start of download()
def download(arg):
num0 = frm1.text.get()
if num0 == "":
num = 1
else:
num = int(num0)
for i in range(num):
global p_flag
start_datetime = datetime.now().strftime("%Y%m%d_%H%M%S")
start_time = time.perf_counter_ns()
failure = 0
try:
urllib.request.urlretrieve(url_list[arg], file_list[arg], progress)
except:
frm2.progress["text"] = 'Error'
failure = 1
time.sleep(1)
p_flag = 0
frm2.progress["text"] = 'End'
end_time = time.perf_counter_ns()
elapsed_time = (end_time - start_time) / 1000000
speed = speed_list[arg]*8*1024/elapsed_time
output = start_datetime + ', ' + str(speed_list[arg]) + ', ' + str(elapsed_time) + ', ' + str(speed) \
+ ', ' + str(failure)
log.debug(output)
output = output + '\n'
frm3.prt.config(state='normal')
frm3.prt.insert(END, output)
frm3.prt.config(state='disabled')
frm3.prt.see('end')
time.sleep(1)
frm2.progress["text"] = ''
## End of download()
- ダウンロード繰り返し数(num0)の取得(未入力時は"1"とする)。
- ログ用の開始時刻(start_datetime)の取得。
- 開始時間(start_time)の記録。
- URLLib(urllib.request.urlretrieve)によるファイルダウンロード。
- 途中経過処理行うCallback関数はprogress(後述)。
- ダウンロードエラー時に進行状況表示を”Error”とする。
- 終了時間(end_time)の記録。
- ダウンロード時間(elapsed_time)および速度(speed)の計算。
- 結果をバッファ(output)に格納、ログに記録(log.debug)、画面表示(frm3.prt.insert)。
進行状況表示
## Start of progress()
def progress(block_count, block_size, total_size):
global p_flag
percent = 100.0 * block_count * block_size / total_size
about = round(percent)
curr = about % 5
if curr == 0 and p_flag == 0:
frm2.progress["text"] = str(about)
p_flag = 1
if curr >= 1:
p_flag = 0
## End of progress()
こちらの記事「Pythonでファイルをダウンロード&プログレスバーを表示させる」を参考に作成。
- 進行状況を%(percent)で取得。
- 5%で表示(frm2.progressのText置き換え)するため、制御用フラグ(p_flag)を設ける。
- 表示したタイミングで、p_flagを1にセット。
- 進行状況(percent)が5で割り切れないときやダウンロード終了時に、p_flagを0にセット(リセット)。
全体
import logging
import logging.handlers
from datetime import datetime
import time
import urllib.request
import tkinter
from tkinter import *
import tkinter.ttk as ttk
import threading
from functools import partial
w_width = 750
w_height = 400
speed_list = [1, 5, 10, 30, 100]
url_list = [
'https://www.4fgh.dr.jp/data/1MByte',
'https://www.4fgh.dr.jp/data/5MByte',
'https://www.4fgh.dr.jp/data/10MByte',
'https://www.4fgh.dr.jp/data/30MByte',
'https://www.4fgh.dr.jp/data/100MByte']
file_list = ['1Mbyte', '5Mbyte', '10Mbyte', '30Mbyte', '100Mbyte']
p_flag = 0 # for progress
class NetworkCheck(ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.create_widgets()
self.pack()
def create_widgets(self):
def cr_tab(frm):
## Start of progress()
def progress(block_count, block_size, total_size):
global p_flag
percent = 100.0 * block_count * block_size / total_size
about = round(percent)
curr = about % 5
if curr == 0 and p_flag == 0:
frm2.progress["text"] = str(about)
p_flag = 1
if curr >= 1:
p_flag = 0
## End of progress()
## Start of download()
def download(arg):
num0 = frm1.text.get()
if num0 == "":
num = 1
else:
num = int(num0)
for i in range(num):
global p_flag
start_datetime = datetime.now().strftime("%Y%m%d_%H%M%S")
start_time = time.perf_counter_ns()
failure = 0
try:
urllib.request.urlretrieve(url_list[arg], file_list[arg], progress)
except:
frm2.progress["text"] = 'Error'
failure = 1
time.sleep(1)
p_flag = 0
frm2.progress["text"] = 'End'
end_time = time.perf_counter_ns()
elapsed_time = (end_time - start_time) / 1000000
speed = speed_list[arg]*8*1024/elapsed_time
output = start_datetime + ', ' + str(speed_list[arg]) + ', ' + str(elapsed_time) + ', ' + str(speed) \
+ ', ' + str(failure)
log.debug(output)
output = output + '\n'
frm3.prt.config(state='normal')
frm3.prt.insert(END, output)
frm3.prt.config(state='disabled')
frm3.prt.see('end')
time.sleep(1)
frm2.progress["text"] = ''
## End of download()
## Start of clk_btn()
# https://www.haya-programming.com/entry/2019/01/20/012926
def clk_btn(size):
th = threading.Thread(target=download, args=(size, ))
th.start()
## Start of cr_tab()
frm1 = ttk.Frame(frm, width=w_width, height=w_height)
# list https://qiita.com/omossan7182t/items/a3ffb3788667de937c59
# partial https://qiita.com/yuzukaki/items/9dd3a4481e1f6e6fa0a8
frm1.Btn = [ttk.Button(frm1, text=file_list[i], style='W.TButton', command=partial(clk_btn, i)) \
for i in range(5)]
[frm1.Btn[i].grid(row = 0, column = i) for i in range(5)]
frm1.label = ttk.Label(frm1, text=' Repetition Number ')
frm1.label.grid(row = 0, column = 5)
frm1.text = ttk.Entry(frm1, width = 10)
frm1.text.grid(row = 0, column = 6)
frm2 = ttk.Frame(frm, width=w_width, height=w_height)
frm2.label = ttk.Label(frm2, text = 'Start Time, Size(Mbyte), Elapsed Time(ms), Speed(Mbps), Failure')
frm2.label.grid(row = 0, column = 0)
frm2.dummy = ttk.Label(frm2, text = ' ')
frm2.dummy.grid(row = 0, column = 1)
frm2.progress = ttk.Label(frm2, text = ' ')
frm2.progress.grid(row = 0, column = 2)
frm3 = ttk.Frame(frm, width=w_width, height=w_height)
frm3.prt = Text(frm3, font=("Courier New", 15), width=120, height=33)
frm3.ysc = tkinter.Scrollbar(frm3, orient=tkinter.VERTICAL, command=frm3.prt.yview)
frm3.ysc.pack(side=tkinter.RIGHT, fill="y")
frm3.prt["yscrollcommand"] = frm3.ysc.set
frm3.prt.config(state='disabled')
frm3.prt.pack()
frm1.pack()
frm2.pack()
frm3.pack()
## End of cr_tab()
## Start of create_widgets()
# Log
log = logging.getLogger('Download')
log.setLevel(logging.DEBUG)
fh = logging.FileHandler('{:Download_Log-%Y%m%d-%H%M%S}.csv'.format(datetime.now()))
log.addHandler(fh)
header = 'Start Time, Size(Mbyte), Elapsed Time(ms), Speed(Mbps), Failure,'
log.debug(header)
# Tab
note = ttk.Notebook(self)
note.pack()
note0 = ttk.Frame(note, width=w_width, height=w_height)
note.add(note0, text=" Click Download File Size ")
cr_tab(note0)
## End of createwidgets()
## End of NetworkCheck()
if __name__ == '__main__':
master = Tk()
master.title("Repeat Download 2")
master.geometry("750x400")
# Call main part
NetworkCheck(master)
master.mainloop()
EOF