アメグラ2号のブログ

1980年代後半の古き良きビデオゲームのほか、オッサンの個人的備忘録

python 外部サイトから気象情報取得 ⇒ウィジェット(スクリプト)

f:id:game-allergy:20210611094841p:plain

天気情報のウィジェットスクリプトを記載しておく

関連記事は以下

game-allergy.hatenablog.com

◆gitは以下

github.com

スクリプト(念のため)

# ==================================================
# library
# ==================================================
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
from tkinter import filedialog

import sqlite3

import pandas as pd

import datetime
import os
import csv
import sys

import matplotlib.pyplot as plt
import japanize_matplotlib

import requests
import bs4

"""
趣旨:自分の住んでいる場所の今後の天気情報/予報を「直感的」に把握したい。
経緯:「気温、湿度、風速、雨量」を把握し、出かける際の服装・持ち物を決めたい。

情報源:tenki.jp
上記より情報を取得し、データベースに格納
データベースから引き出した情報をウィジェット内ツリービューに表示
matplotチャートで出力・表示

そのほか:csv読み込み、書き出し

"""

# =============================================================================================
# class / bs4
# =============================================================================================
class BS4:
    def __init__(self,URL):
        self.r = requests.get(URL)
        self.s = bs4.BeautifulSoup(self.r.text, 'html.parser')

    def scraping(self,a,b,c): 
        """
        bs4で抽出したテキストからさらに絞り込み
        引数:htmlのクラス、要素など
        戻値:リスト
        a = 'tr'
        b = 'head'
        c = 'p'
        """
        list1= []
        for j in self.s.find_all(a , class_ = b):
            for i in j.find_all(c):
                list1.append(i.text)

        return list1
  

# =============================================================================================
# bs4
# =============================================================================================
def ProcessScraping(URL,a,b,c):
    """
    ///Webスクレイピングのプロセス
    """
    bs4 = BS4(URL)
    r = bs4.scraping(a,b,c)
    return r


# ====================================================================================================
# ウィジェット
# ====================================================================================================
class Application(tk.Frame):
    def __init__(self, master=None):
        """
        ///ウィジェット親クラス///
        """
        super().__init__(master)
        self.master = master

        # 全体ウィンドウサイズ、配置位置
        self.master.geometry("1300x900+0+0")

        # ウィンドウのタイトル
        self.master.title("tenki.jpの天気予報")

        # 上記を反映する
        self.pack()

        # ウィンドウ内のウィジェットを配置
        self.create_widgets()

    def create_widgets(self):
        """
        ///ウィジェットの配置///
        ///この中でウィジェット部品を配置する///
        """
        # ウィジェットのスタイル設定
        self.style = ttk.Style()

        fontsize_t1 = 13
        fontsize_t2 = 10

        # Treeview headingのフォント
        self.style.configure("Treeview.Heading",font=("",fontsize_t1))

        # Treeview内のフォント
        self.style.configure("Treeview",font=("",fontsize_t2))

        # -------------------------
        # DB / テーブル有無チェック
        # -------------------------
        # DB / DB名
        dbname = "tenki.db"
        # DB / table名
        tableName="WeatherTable"

        # テーブル有無確認、無ければ作成
        TableExsistenceCheckAndCreate(dbname,tableName)

        # -------------------------
        # master / frame
        # -------------------------
        #tcl_isOk = self.register(IsOk)
        self.frame0 = tk.Frame(self, width=800, height=100)  
        self.frame1 = tk.Frame(self, width=800, height=100)   
        self.frame2 = tk.Frame(self, width=800, height=300)
        self.frame3 = tk.Frame(self, width=800, height=100)
        self.frame4 = tk.Frame(self, width=800, height=100)
        self.frame5 = tk.Frame(self, width=800, height=100)
        self.frame6 = tk.Frame(self, width=800, height=100)
        self.frame0.grid(column=0,row=0,padx=5,pady=5)
        self.frame1.grid(column=0,row=1,padx=5,pady=5)
        self.frame2.grid(column=0,row=2,padx=5,pady=5)
        self.frame3.grid(column=0,row=3,padx=5,pady=5)
        self.frame4.grid(column=0,row=4,padx=5,pady=5)
        self.frame5.grid(column=0,row=5,padx=5,pady=5)
        self.frame6.grid(column=0,row=6,padx=5,pady=5)

        # --------------------------------------------------
        # 【変数】ウィジェット間距離など
        # --------------------------------------------------
        pad_x1 = 10
        pad_y1 = 3
        width1 = 10
        width2 = 10
        Lbl_name1 = "ID"
        Lbl_name2 = "hour"
        Lbl_name3 = "weather"
        Lbl_name4 = "temperature"
        Lbl_name5 = "humidity"
        Lbl_name6 = "windblow"
        Lbl_name7 = "wind-speed"
        Lbl_name8 = "precipitation"
        fontsize1 = 15
        fontsize2 = 12
        width_treeview1 = 100

        # Entry欄のheight
        ipady_1 = 15

        # 情報取得したいURL
        URL_yokohama ='https://tenki.jp/forecast/3/17/4610/14100/1hour.html'
        URL_saku ='https://tenki.jp/forecast/3/23/4820/20217/1hour.html' 
        URL_nagoya ='https://tenki.jp/forecast/5/26/5110/23100/1hour.html' 
        URL_fukuoka='https://tenki.jp/forecast/9/43/8210/40130/1hour.html' 
        URL_kagoshima ='https://tenki.jp/forecast/9/49/8810/46201/1hour.html' 
        URL_sapporo ='https://tenki.jp/forecast/1/2/1400/1100/1hour.html' 
        URL_fukushima = 'https://tenki.jp/forecast/2/10/3610/7201/1hour.html'
        URL_aomori = 'https://tenki.jp/forecast/2/5/3110/2201/1hour.html'
        URL_osaka = 'https://tenki.jp/forecast/6/30/6200/27100/1hour.html'
        URL_onomichi ='https://tenki.jp/forecast/7/37/6710/34205/1hour.html'
        URL_kouchi = 'https://tenki.jp/forecast/8/42/7410/39201/1hour.html'

        # --------------------------------------------------
        # frame0 / button
        # --------------------------------------------------
        t1 = '横浜市'
        t2 = '佐久市'
        t3 = '名古屋市'
        t4 = '福岡市'
        t5 = '鹿児島市'
        t6 = '札幌市'
        t7 = '福島市'
        t8 = '青森市'
        t9 = '大阪市'
        t10 = '尾道市'
        t11 = '高知市'

        self.btn_20= tk.Button(self.frame0, text = t1, command=lambda:self.Push2(URL_yokohama,dbname,tableName,t1),font=("",fontsize1))
        self.btn_20.grid(column=0,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_21= tk.Button(self.frame0, text = t2, command=lambda:self.Push2(URL_saku,dbname,tableName,t2),font=("",fontsize1))
        self.btn_21.grid(column=1,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_22= tk.Button(self.frame0, text = t3, command=lambda:self.Push2(URL_nagoya,dbname,tableName,t3),font=("",fontsize1))
        self.btn_22.grid(column=2,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_23= tk.Button(self.frame0, text = t4, command=lambda:self.Push2(URL_fukuoka,dbname,tableName,t4),font=("",fontsize1))
        self.btn_23.grid(column=3,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_24= tk.Button(self.frame0, text = t5, command=lambda:self.Push2(URL_kagoshima,dbname,tableName,t5),font=("",fontsize1))
        self.btn_24.grid(column=4,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_25= tk.Button(self.frame0, text = t6, command=lambda:self.Push2(URL_sapporo,dbname,tableName,t6),font=("",fontsize1))
        self.btn_25.grid(column=5,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_26= tk.Button(self.frame0, text = t7, command=lambda:self.Push2(URL_fukushima,dbname,tableName,t7),font=("",fontsize1))
        self.btn_26.grid(column=6,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_27= tk.Button(self.frame0, text = t8, command=lambda:self.Push2(URL_aomori,dbname,tableName,t8),font=("",fontsize1))
        self.btn_27.grid(column=7,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_28= tk.Button(self.frame0, text = t9, command=lambda:self.Push2(URL_osaka,dbname,tableName,t9),font=("",fontsize1))
        self.btn_28.grid(column=8,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_29= tk.Button(self.frame0, text = t10, command=lambda:self.Push2(URL_onomichi,dbname,tableName,t10),font=("",fontsize1))
        self.btn_29.grid(column=9,row=0,padx=pad_x1,pady=pad_y1)

        self.btn_30= tk.Button(self.frame0, text = t11, command=lambda:self.Push2(URL_kouchi,dbname,tableName,t11),font=("",fontsize1))
        self.btn_30.grid(column=10,row=0,padx=pad_x1,pady=pad_y1)

        # --------------------------------------------------
        # frame1 / button
        # --------------------------------------------------
        self.btn_4= tk.Button(self.frame1, text = 'DB読込み', command=lambda:self.Push4(dbname,tableName),font=("",fontsize1))
        self.btn_4.grid(column=2,row=1,padx=pad_x1,pady=pad_y1)

        self.btn_6= tk.Button(self.frame1, text = 'テーブル削除/作成', command=lambda:self.Push6(dbname,tableName),font=("",fontsize1))
        self.btn_6.grid(column=3,row=1,padx=pad_x1,pady=pad_y1)

        self.btn_9= tk.Button(self.frame1, text = 'CSV読み込み', command=self.Push10,font=("",fontsize1))
        self.btn_9.grid(column=5,row=1,padx=pad_x1,pady=pad_y1)

        self.btn_10= tk.Button(self.frame1, text = 'CSV出力', command=lambda:self.Push11(dbname,tableName),font=("",fontsize1))
        self.btn_10.grid(column=6,row=1,padx=pad_x1,pady=pad_y1)

        self.btn_11= tk.Button(self.frame1, text = '表jpg出力', command=lambda:self.Push12(dbname,tableName),font=("",fontsize1))
        self.btn_11.grid(column=7,row=1,padx=pad_x1,pady=pad_y1)

        self.btn_12= tk.Button(self.frame1, text = 'チャート出力', command=lambda:self.Push13(dbname,tableName),font=("",fontsize1))
        self.btn_12.grid(column=7,row=1,padx=pad_x1,pady=pad_y1)

        # --------------------------------------------------
        # frame2 / Label,Entry
        # -------------------------------------------------- 
        # 選択したデータの表示場所&入力箇所
        self.Label_1 = tk.Label(self.frame2, text = Lbl_name1, width = width1, bg='gray',fg='white',font=("",fontsize1))
        self.Label_1.grid(column=0,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_1 = tk.Entry(self.frame2, width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_1.grid(column=0,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)      

        self.Label_2 = tk.Label(self.frame2, text = Lbl_name2, width = width1, bg='gray',fg='white',font=("",fontsize1))
        self.Label_2.grid(column=1,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_2 = tk.Entry(self.frame2,width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_2.grid(column=1,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)

        self.Label_3 = tk.Label(self.frame2, text = Lbl_name3, width = width1, bg='gray',fg='white',font=("",fontsize1))
        self.Label_3.grid(column=2,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_3 = tk.Entry(self.frame2,width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_3.grid(column=2,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)

        self.Label_4 = tk.Label(self.frame2, text = Lbl_name4, width = width1, bg='gray', fg='white',font=("",fontsize1))
        self.Label_4.grid(column=3,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_4 = tk.Entry(self.frame2, width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_4.grid(column=3,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)

        self.Label_5 = tk.Label(self.frame2, text = Lbl_name5, width = width1, bg='gray', fg='white',font=("",fontsize1))
        self.Label_5.grid(column=4,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_5 = tk.Entry(self.frame2, width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_5.grid(column=4,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)

        self.Label_6 = tk.Label(self.frame2, text = Lbl_name6, width = width1, bg='gray', fg='white',font=("",fontsize1))
        self.Label_6.grid(column=5,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_6 = tk.Entry(self.frame2, width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_6.grid(column=5,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)

        self.Label_7 = tk.Label(self.frame2, text = Lbl_name7, width = width1, bg='gray', fg='white',font=("",fontsize1))
        self.Label_7.grid(column=6,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_7 = tk.Entry(self.frame2, width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_7.grid(column=6,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)
    
        self.Label_8 = tk.Label(self.frame2, text = Lbl_name8, width = width1, bg='gray', fg='white',font=("",fontsize1))
        self.Label_8.grid(column=7,row=1,padx=pad_x1,pady=pad_y1)
        self.entry_8 = tk.Entry(self.frame2, width=width2, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_8.grid(column=7,row=2,padx=pad_x1,pady=pad_y1,ipady=ipady_1)

        
        # --------------------------------------------------
        # frame3 / button
        # --------------------------------------------------
        # 書き込みボタン
        self.btn_1= tk.Button(self.frame3, text = '書き込み', command=lambda:self.Push1(dbname,tableName),width=30,font=("",fontsize1))
        self.btn_1.grid(column=1,row=0,padx=pad_x1,pady=pad_y1)

        # 削除ボタン
        self.btn_3= tk.Button(self.frame3, text = '削除処理', command=lambda:self.Push3(dbname,tableName),width=30,font=("",fontsize1))
        self.btn_3.grid(column=2,row=0,padx=pad_x1,pady=pad_y1)

        # --------------------------------------------------
        # frame4 / entry
        # --------------------------------------------------
        self.entry_9 = tk.Entry(self.frame4, width=80, bg='white',fg='black',justify='center',font=("",fontsize2))
        self.entry_9.grid(column=0,row=0,padx=pad_x1,pady=pad_y1,ipady=ipady_1)

        # -------------------------
        # frame5 / treeview
        # -------------------------
        self.tree = ttk.Treeview(self.frame5, height = 20, style='Treeview')

        # treeの設定
        self.tree["columns"] = (1,2,3,4,5,6,7,8)
        self.tree["show"] = "headings"
        self.tree.column(1, width=width_treeview1,anchor=tk.CENTER)
        self.tree.column(2, width=width_treeview1,anchor=tk.CENTER)
        self.tree.column(3, width=width_treeview1,anchor=tk.CENTER)
        self.tree.column(4, width=width_treeview1,anchor=tk.CENTER)
        self.tree.column(5, width=width_treeview1,anchor=tk.CENTER)
        self.tree.column(6, width=width_treeview1,anchor=tk.CENTER)
        self.tree.column(7, width=width_treeview1,anchor=tk.CENTER)
        self.tree.column(8, width=width_treeview1,anchor=tk.CENTER)
        self.tree.heading(1, text=Lbl_name1)
        self.tree.heading(2, text=Lbl_name2)
        self.tree.heading(3, text=Lbl_name3)
        self.tree.heading(4, text=Lbl_name4)
        self.tree.heading(5, text=Lbl_name5)
        self.tree.heading(6, text=Lbl_name6)
        self.tree.heading(7, text=Lbl_name7)
        self.tree.heading(8, text=Lbl_name8)
        
        # --------------------------------------------------
        # frame6 / Quit button
        # --------------------------------------------------
        self.btn_2= tk.Button(self.frame6, text="閉じる", fg="black",command=self.master.destroy,font=("",fontsize1))
        self.btn_2.grid(column=0,row=0,padx=pad_x1,pady=pad_y1)

        # --------------------------------------------------
        # Entry入力不可状態の処理
        # --------------------------------------------------
        AllEntryDisable(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)

        # --------------------------------------------------
        #ツリー内をマウスで選択した時
        # --------------------------------------------------
        self.tree.bind("<<TreeviewSelect>>", self.OnTreeSelect)

        # pack
        self.tree.pack()

        # DB/Tableの有無のチェック、無ければ作成する
        TableExsistenceCheckAndCreate(dbname,tableName)

    def OnTreeSelect(self,event):     
        """
        ///ツリービュー内の情報を選択したときの処理///
        """
        # Entryの入力可能処理
        AllEntryEnable(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)

        for id in self.tree.selection():
            #Dicionary形式で抽出
            self.tree.set(id)

        mydic = self.tree.set(id)
        d1 = mydic['1']
        d2 = mydic['2']
        d3 = mydic['3']
        d4 = mydic['4']
        d5 = mydic['5']
        d6 = mydic['6']
        d7 = mydic['7']
        d8 = mydic['8']


        # 中身をいったん消してから/データ挿入
        AllEntryInfoErase(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)

        # データを入れる
        self.entry_1.insert(tk.END,d1)
        self.entry_2.insert(tk.END,d2)
        self.entry_3.insert(tk.END,d3)
        self.entry_4.insert(tk.END,d4)
        self.entry_5.insert(tk.END,d5)
        self.entry_6.insert(tk.END,d6) 
        self.entry_7.insert(tk.END,d7)
        self.entry_8.insert(tk.END,d8)


    def Push1(self,dbname,tableName):
        """
        ///書き込みボタン押下時の処理///
        ///Entry内の情報を読み取り、DBへ登録する///
        """
        # ------------------------------
        # 実行前の確認処理
        # ------------------------------
        res = PopUp4()

        # yesで抜けて進む
        if res =="yes":
            pass

        # noで終了処理
        else:
            return      

        # ------------------------------
        # 処理の開始
        #-------------------------------
        # Entry欄の情報取得
        e1 = self.entry_1.get()
        e2 = self.entry_2.get()
        e3 = self.entry_3.get()
        e4 = self.entry_4.get()
        e5 = self.entry_5.get()
        e6 = self.entry_6.get()
        e7 = self.entry_7.get()
        e8 = self.entry_8.get()

        # DB接続し、
        dbc = DBconnection(dbname)

        # UPDATE/INSERTの振り分け(ID有無で振り分け)
        if e1 != "":

            # データ/UPDATE
            dbc.Updatedata(tableName,e1,e2,e3,e4,e5,e6,e7,e8)

        else:
            # データ/INSERT
            dbc.InsertData(tableName,e2,e3,e4,e5,e6,e7,e8)

        # DB COMMIT/CLOSE
        dbc.Commit()
        dbc.Close()

        # DBを再読み込み
        data = ProcessGetData(dbname,tableName)

        # データ挿入
        TreeDataImport(self.tree,data)

        # ポップアップ通知
        if e1 != "":
            PopUp1(e1)
        else:    
            PopUp2()

    def Push2(self,URL,dbname,tableName,prefecture):
        """
        ///Webスクレイピング処理///
        """
        # Webのタグ情報を収集する
        r1,r2,r3,r4,r5,r6,r7,r8 = ProcessAllTagInfo(URL)

        # pandas
        df = ToPandasDataFrame2(r2,r3,r4,r5,r6,r7,r8)
        pd.set_option('display.max_rows', None)

        # DB/table drop
        TableDropAndCreate(dbname,tableName)

        # DB/ inseert
        dbc = DBconnection(dbname)
        df.to_sql("WeatherTable",dbc.connection,if_exists='append',index=None)
        dbc.Commit
        dbc.Close

        # DB/select、Treeview
        data = ProcessGetData(dbname,tableName)
        TreeDataImport(self.tree,data)

        # Entryの入力制限
        AllEntryEnable(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)
       
        # entryをすべて消す
        AllEntryInfoErase(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)

        # entryに「●●市」と入れる
        self.entry_9.insert(tk.END,prefecture + "のお天気情報")

        # entryを入力不可
        # Entryの入力制限
        AllEntryDisable(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)

        # 通知
        PopUp0()
        
    def Push3(self,dbname,tableName):
        """
        ///1つのレコードを削除///
        Entry情報を読み取る
        IDがない場合、「できない」処理
        IDありの場合、「いいですか?」
        ID指定で、削除処理、完了通知
        """

        e1 = self.entry_1.get()
        e2 = self.entry_2.get()
        e3 = self.entry_3.get()
        e4 = self.entry_4.get()
        e5 = self.entry_5.get()
        e6 = self.entry_6.get()

        # IDが無い場合
        if e1 == "":
            PopUp3()
            return
        
        # IDがある場合
        else:
            # 処理前の確認
            res = PopUp4()

            # yesで抜けて進む
            if res =="yes":
                pass

            # noで終了処理
            else:
                return
        
        # DB接続
        dbc = DBconnection(dbname)
        #実行処理
        dbc.DeleteRecordData(tableName,e1)

        # COMMIT/CLOSE  
        dbc.Commit()
        dbc.Close()

       # DBを再読み込み
        df = ProcessGetData(dbname,tableName)

        # データ挿入
        TreeDataImport(self.tree,df)

        # 通知
        PopUp5()

    def Push4(self,dbname,tableName):
        """
        ///再読み込みボタン///
        ///DB読込みをしてツリービューに表示する///
        ツリービューのデータ削除、読み込み
        Entryのデータ削除
        """
        # DBを再読み込み
        data = ProcessGetData(dbname,tableName)

        # データ挿入
        TreeDataImport(self.tree,data)

        # ポップアップ通知
        PopUp6()



    def Push6(self,dbname,tableName):
        """
        ///テーブル削除、新規でテーブル作成する
        """
        # ------------------------------
        # 実行前の確認処理
        # ------------------------------
        res = PopUp4()

        # yesで抜けて進む
        if res =="yes":
            pass

        # noで終了処理
        else:
            return

        # テーブル削除、作成
        TableDropAndCreate(dbname,tableName)

        # DB/SELECT、ツリー表示
        # DBを再読み込み
        data = ProcessGetData(dbname,tableName)

        # データ挿入
        TreeDataImport(self.tree,data)

        # Entryの入力制限
        AllEntryEnable(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)
       
       # Entry欄の情報削除
        AllEntryInfoErase(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)

        # Entryの入力制限
        AllEntryDisable(self.entry_1,self.entry_2,self.entry_3,self.entry_4,self.entry_5,self.entry_6,self.entry_7,self.entry_8,self.entry_9)

        # 通知
        PopUp7()



    def Push10(self):
        """
        ///CSVの読み込み
        """
        # CSVファイルの保存先のチェック
        s1,s2,s3 = CheckCsvFolderExists()

        # ファイルダイヤログ表示、ファイル選択ができるように。
        filePath = OpenFileDialog(s1)

        # ダイヤログでキャンセルボタン押下の場合の処理
        if filePath == "":
            return

        # CSV読み込み / List型
        data = CsvOpenAndReader(filePath)

        # ツリーへデータ挿入
        TreeDataImport(self.tree,data)

        # 通知
        PopUp0()

    def Push11(self,dbname,tableName):
        """
        ///CSVの出力
        ///DBデータを抽出し、CSVファイルとして出力する
        """
        # CSVファイルの保存先のチェック
        s1,s2,s3 = CheckCsvFolderExists()

        # DB接続、データをSELECT*で引っ張る
        # DBを再読み込み
        data = ProcessGetData(dbname,tableName)

        # Pandas / Dataframeにする
        df = ToPandasDataFrame(data)

        # Timestamp
        Tstamp = GetTimeStamp()

        # CSVのファイルにする
        FldName = s2
        csvName = "_out.csv"
        dirName = FldName + "\\"    
        fileSavePath = dirName + Tstamp + csvName

        # CSVへ出力
        df.to_csv(fileSavePath,index=False,encoding='utf_8_sig')
        
        # 完了通知
        PopUp0()

    def Push12(self,dbname,tableName):
        """
        ///Matplotlib.tableの表jpgを出力、保存
        """
        # jpg保存先フォルダ有無のチェック
        s1,s2,s3 = CheckCsvFolderExists()

        # DB接続、データをSELECT*で引っ張る
        # DBを再読み込み
        data = ProcessGetData(dbname,tableName)

        # Pandas / Dataframeにする
        df = ToPandasDataFrame(data)

        # チェック/dfがない場合はSTOP
        if len(df)==0:
            PopUp11()
            return

        # Timestamp
        Tstamp = GetTimeStamp()

        # matplotに渡す
        fig = CreatePltTable(df)
        
        # Save as jpg
        SaveTablefig(fig,s3)

        # Matplotlib.table終了処理
        plt.clf()
        plt.close()

        # 通知
        PopUp0()

    def Push13(self,dbname,tableName):
        """
        ///MATPLOTチャートを出力し、表示する
        """
        # チャート用のタイトルテキストを取得
        e9 = self.entry_9.get()

        # DBからデータをもらう
        data = ProcessGetData(dbname,tableName)

        # DFにする
        df = ToPandasDataFrame(data)

        # DF ⇒ MATPLOT
        chart = CreatPltChart(df,e9)
        plt.show()

def CreatPltChart(df,charttitle):
    """
    ///Matplotの表を作成する
    """
    # グラフ上限値を決めておく(5℃~25℃)
    tempMin = 10
    tempMax = 35
    humidMin = 0
    humidMax = 100
    windMin = 0
    windMax = 15
    precipiMin = 0
    precipiMax = 30

    x1 = df.hour
    y1 = df.temperature
    x2 = df.hour
    y2 = df.humidity
    x3 = df.hour
    y3 = df.windspeed
    x4 = df.hour
    y4 = df.precipitation

    # 日付情報取得
    td2 = GetTimeStamp()

    # fig1-------------------------------
    fig1 = plt.figure(charttitle,figsize=(8,8))

    # (data)----------------------------
    chart1 = fig1.add_subplot(2,2,1)
    chart1.plot(x1, y1)
    plt.ylim(tempMin, tempMax)
    # グラフ内のラベル表記
    chart1.set_title(td2 + " / Temperature")
    chart1.set_xlabel("Time")
    chart1.set_ylabel("Temperature(℃)")

    # fig2-------------------------------
    #fig2 = plt.figure(td2 + "_01")
    # (data)----------------------------
    chart2 = fig1.add_subplot(2,2,2)
    chart2.plot(x2, y2)
    plt.ylim(humidMin, humidMax)
    # グラフ内のラベル表記
    chart2.set_title(td2 + " / Humidity")
    chart2.set_xlabel("Time")
    chart2.set_ylabel("Humidity(%)")

    # fig3-------------------------------
    #fig3 = plt.figure(td2 + "_01")
    # (data)----------------------------
    chart3 = fig1.add_subplot(2,2,3)
    chart3.plot(x3, y3)
    plt.ylim(windMin, windMax)
    # グラフ内のラベル表記
    chart3.set_title(td2 + " / WindSpeed")
    chart3.set_xlabel("Time")
    chart3.set_ylabel("WindSpeed(m/h)")

    # fig3-------------------------------
    #fig4 = plt.figure(td2 + "_01")
    # (data)----------------------------
    chart4 = fig1.add_subplot(2,2,4)
    chart4.plot(x4, y4)
    plt.ylim(windMin, windMax)
    # グラフ内のラベル表記
    chart4.set_title(td2 + " / Precipitation")
    chart4.set_xlabel("Time")
    chart4.set_ylabel("Precipitation(mm/h)")

    #-----------------------------
    # グラフの設定
    #-----------------------------
    fig1.tight_layout()

    #-----------------------------
    # 戻り値
    #-----------------------------
    fig_all = (fig1)
    return fig_all

# ====================================================================================================
# データベース
# ====================================================================================================
# ///基本情報:
# データベース名:butterfly.db
# テーブル名:SanagiTable
# カラム名:id,name,sanagi,prediction,birth,dif

class DBconnection:
    """
    データベース関係:
    """
    def __init__(self,dbname):
        """
        ///データベース接続///
        """
        self.connection = sqlite3.connect(dbname)
        self.cur = self.connection.cursor()

    def Commit(self):
        """
        ///データコミット///
        """
        r = self.connection.commit()
        return r

    def Close(self):
        """
        ///データベースクローズ///
        """
        r = self.cur.close()
        return r

    def Getdata(self,tableName):
        """
        ///データベースから情報取得///
        """
        r = self.cur.execute("SELECT * FROM %s" % tableName)
        r = r.fetchall()
        return r

    def Updatedata(self,tableName,e1,e2,e3,e4,e5,e6,e7,e8):
        """
        ///データベースに既存情報を書き換え・更新///
        """
        self.cur.execute("UPDATE %s SET hour=?,weather=?,temperature=?,humidity=?,windblow=? ,windspeed=?,precipitation=? WHERE id=?" % tableName,(e2,e3,e4,e5,e6,e7,e8,e1))

    def InsertData(self,tableName,e2,e3,e4,e5,e6,e7,e8):
        """
        ///データベースに、新規情報をインサートする///
        変数:テーブル名は%s、VALUESは?で。
        """
        self.cur.execute("INSERT INTO %s(hour,weather,temperature,himidity,windblow,windspeed,precipitation) VALUES(?,?,?,?,?,?,?)" % tableName,(e2,e3,e4,e5,e6,e7,e8))

    def DeleteRecordData(self,tableName,e1):
        """
        ///データベースから、特定の1つのレコードを削除する///
        注意:引数は(e1,)とタプルで渡すこと。(e1)だと文字列で誤解する

        """
        self.cur.execute("DELETE FROM %s WHERE id=%s" %(tableName,e1))

    def DropTable(self,tableName):
        """
        ///テーブル削除///
        """
        self.cur.execute("DROP TABLE %s" % tableName)

    def CreateTable(self,tableName):
        """
        ///Tableの作成///
        """
        sql = 'CREATE TABLE %s(id INTEGER PRIMARY KEY AUTOINCREMENT, hour INTEGER, weather STRING, temperature REAL, humidity REAL, windblow STRING, windspeed INTEGER, precipitation INTEGER )' % tableName
        self.cur.execute(sql)
    
    def CheckTableExistence(self,tableName):
        """
        ///DB内のテーブル名有無をチェック
        """
        r = self.cur.execute('SELECT COUNT(*) FROM sqlite_master WHERE TYPE="table" AND NAME="%s"' % tableName)
        return r



# ====================================================================================================
# function群
# 
# ====================================================================================================
# ==================================================
# function / データベース関係
# ==================================================
def ProcessGetData(dbname,tableName):
    """
    ///一連タスク:DB接続、SELECTで抽出、DataFrameでreturn///
    """
    dbc = DBconnection(dbname)
    dbc.connection
    dbc.cur
    data = dbc.Getdata(tableName)
    return data

# ==================================================
# function / Treeview関係
# ==================================================
def TreeDataImport(tree,data):
    """
    ///Treeviewへデータ挿入///
    ///SQLから抽出したリストを、Treeviewへinsert
    """
    # まずはツリー表示のデータをクリア
    tree.delete(*tree.get_children())

    # データ挿入
    a = len(data)

    for i in range(a):
        tree.insert("", "end", values=(data[i][0], data[i][1],data[i][2],data[i][3],data[i][4],data[i][5],data[i][6],data[i][7]))
# ==================================================
# function / popup
# ==================================================
def PopUp0():
    """
    ///処理完了をユーザーへ通知する(汎用型)///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info','処理完了')


def PopUp1(id):
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info','書き込み完了しましたよん', detail="【ID】:" + id)

def PopUp2():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '新規の書き込み完了~♪')

def PopUp3():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '削除したい箇所を選択してね')

def PopUp4():
    """
    ///実行していいですか?///
    """
    res = messagebox.askquestion("注意", "実行していいですか?")
    return res

def PopUp5():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '削除完了')

def PopUp6():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '再読み込み完了')

def PopUp7():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', 'DBテーブル削除、新規作成完了')

def PopUp8():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '入力スペースに入力後、書き込みボタンを押してね')

def PopUp9():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', 'サナギになった日を入れてから、書き込みボタンを押してね')

def PopUp10():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '数字8文字でお願い~\n2020/5/20の場合は、\n20210520って感じで~')

def PopUp11():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', 'DB/テーブルがからっぽ\n処理できないっす')


def PopUp12():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '未入力だよ~')

def PopUp13():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '数字で入力しておくれ')

def PopUp14():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '数字を8文字で~~')

def PopUp15():
    """
    ///処理完了をユーザーへ通知する///
    """
    # メッセージボックス(情報) 
    messagebox.showinfo('Info', '入力チェック完了')

# ==================================================
# function / データベース関係
# ==================================================
def TableExsistenceCheckAndCreate(dbname,tableName):
    """
    ///テーブル有無チェック後、テーブルを作る
    """
    dbc = DBconnection(dbname)
    cur = dbc.CheckTableExistence(tableName)

    # Check the Existence of table and Create the table
    if cur.fetchone() == (0,):
        print("Table is not existed then created")
        dbc.CreateTable(tableName)
    
    else:
        print('Table exists')

    dbc.Commit()
    dbc.Close()

def TableDropAndCreate(dbname,tableName):
    """
    ///テーブル削除、作成
    """
    dbc = DBconnection(dbname)

    # テーブル削除
    dbc.DropTable(tableName)
    dbc.Commit()
    
    # 新規テーブル作成
    dbc.CreateTable(tableName)
    dbc.Commit()

    # CLOSE  
    dbc.Close()

# ==================================================
# function / datetime関係
# ==================================================
def StrToDateTime(str):
    """
    ///文字列をDatetime型にする
    ///戻り値:datetime
    """
    r = datetime.datetime.strptime(str, '%Y%m%d')
    return r

def IntToTimedelta(n):
    """
    ///整数INTをtimedeltaにする(加減算用)
    ///戻り値:timedelta
    """
    r = datetime.timedelta(days = n)
    return r


# ==================================================
# function / Entry欄
# ==================================================
def AllEntryDisable(entry_1,entry_2,entry_3,entry_4,entry_5,entry_6,entry_7,entry_8,entry_9):
    """
    ///Entry欄の入力を全て不可にする
    """
    entry_1.configure(state='disabled')
    entry_2.configure(state='disabled')
    entry_3.configure(state='disabled')
    entry_4.configure(state='disabled')
    entry_5.configure(state='disabled')
    entry_6.configure(state='disabled')
    entry_7.configure(state='disabled')
    entry_8.configure(state='disabled')
    entry_9.configure(state='disabled')

def AllEntryEnable(entry_1,entry_2,entry_3,entry_4,entry_5,entry_6,entry_7,entry_8,entry_9):
    """
    ///Entry欄の入力を全て可能にする
    """
    entry_1.configure(state='normal')
    entry_2.configure(state='normal')
    entry_3.configure(state='normal')
    entry_4.configure(state='normal')
    entry_5.configure(state='normal')
    entry_6.configure(state='normal')
    entry_7.configure(state='normal')
    entry_8.configure(state='normal')
    entry_9.configure(state='normal')


def AllEntryInfoErase(entry_1,entry_2,entry_3,entry_4,entry_5,entry_6,entry_7,entry_8,entry_9):
    """
    ///Entry欄の情報を全て削除する
    """
    entry_1.delete(0, tk.END) 
    entry_2.delete(0, tk.END) 
    entry_3.delete(0, tk.END) 
    entry_4.delete(0, tk.END) 
    entry_5.delete(0, tk.END) 
    entry_6.delete(0, tk.END)
    entry_7.delete(0, tk.END) 
    entry_8.delete(0, tk.END)
    entry_9.delete(0, tk.END) 


# ==================================================
# function / フォルダ
# ==================================================
def CheckCsvFolderExists():
    """
    ///CSVファイルの保存先フォルダ有無チェック、作成///
    ///戻り値:フォルダ名(string)
    """
    # 探すべきフォルダ名
    s1 = "csv_read_fld"
    s2 = "csv_out_fld"
    s3 = "table_out_fld"

    s_array = (s1,s2,s3)

    for s in s_array:
        if not os.path.exists(s):
            # ディレクトリが存在しない場合、ディレクトリを作成する
            print("Not exists, and created")
            os.makedirs(s)

    return s1,s2,s3

# ==================================================
# function / dataframe
# ==================================================
def ToPandasDataFrame(data):
    """
    ///Pandas dataframeにする
    引数:data(DB)
    戻値:データフレーム
    """
    col1 = "id"
    col2 = "hour"
    col3 = "weather"
    col4 = "temperature"
    col5 = "humidity"
    col6 = "windblow"
    col7 = "windspeed"
    col8 = "precipitation"

    df = pd.DataFrame(data,
                    columns=[
                    col1,
                    col2,
                    col3,
                    col4,
                    col5,
                    col6,
                    col7,
                    col8
                    ])
    return df

def ToPandasDataFrame2(r2,r3,r4,r5,r6,r7,r8):
    """
    ///Pandas dataframeにする
    引数:リスト
    戻値:データフレーム
    """
    col2 = "hour"
    col3 = "weather"
    col4 = "temperature"
    col5 = "humidity"
    col6 = "windblow"
    col7 = "windspeed"
    col8 = "precipitation"

    df = pd.DataFrame({
                        col2:r2,
                        col3:r3,
                        col4:r4,
                        col5:r5,
                        col6:r6,
                        col7:r7,
                        col8:r8
                        })
    return df

# =============================================================================================
# function
# =============================================================================================
def CreateHourList(l):
    """
    ///hourの値を調整
    「0~24、0~24、0~24」の塊になっているので「0~72」にする
    """
    r=[]
    loopnum=len(l)

    for i in range(0,loopnum,1):
        if i<=23:
            r.append(int(l[i]))
        elif i>=24 and i<=47:
            r.append(int(l[i])+24)
        elif i>=48:
            r.append(int(l[i])+48)
    return r

def ChangeTypeStrToFloat(l):
    """
    ///文字列型をFLOAT型に変換する
    引数:リスト
    戻値:リスト
    """
    r = [float(s) for s in l]
    return r

def ChangeTypeStrToInt(l):
    """
    ///文字列型をFLOAT型に変換する
    引数:リスト
    戻値:リスト
    """
    r = [int(s) for s in l]
    return r

def ProcessAllTagInfo(URL):
    """
    ///htmlから必要なタグ情報だけを抽出
    引数:URL
    戻値:タグ情報(リスト型)
    """
    # --------------
    # 1.日時(3日分=3こ)
    # --------------
    a = 'tr'
    b = 'head'
    c = 'p'
    r1 = ProcessScraping(URL,a,b,c)

    # --------------
    # 2.時刻
    # --------------
    a = 'tr'
    b = 'hour'
    c = 'span'
    r2 = ProcessScraping(URL,a,b,c)

    # [0~24]×3 ⇒ [0~72]にする
    r2 = CreateHourList(r2)

    # Str⇒intに変換
    r2 = ChangeTypeStrToInt(r2)

    # --------------
    # 3.天気 / 日本語
    # --------------
    a = 'tr'
    b = 'weather'
    c = 'p'
    r3 = ProcessScraping(URL,a,b,c)

    # --------------
    # 4.気温
    # --------------
    a = 'tr'
    b = 'temperature'
    c = 'span'
    r4 = ProcessScraping(URL,a,b,c)

    # Str⇒floatに変換
    r4 = ChangeTypeStrToFloat(r4)

    # --------------
    # 5.湿度
    # --------------
    a = 'tr'
    b = 'humidity'
    c = 'td'
    r5 = ProcessScraping(URL,a,b,c)

    # Str⇒floatに変換
    r5 = ChangeTypeStrToFloat(r5)

    # --------------
    # 6.風向き / 日本語
    # --------------
    a = 'tr'
    b = 'wind-blow'
    c = 'p'
    r6 = ProcessScraping(URL,a,b,c)

    # --------------
    # 7.風速
    # --------------
    a = 'tr'
    b = 'wind-speed'
    c = 'span'
    r7 = ProcessScraping(URL,a,b,c)

    # Str⇒intに変換
    r7 = ChangeTypeStrToInt(r7)

    # --------------
    # 8.雨量
    # --------------
    a = 'tr'
    b = 'precipitation'
    c = 'span'
    r8 = ProcessScraping(URL,a,b,c)

    # Str⇒intに変換
    r8 = ChangeTypeStrToInt(r8)

    return r1,r2,r3,r4,r5,r6,r7,r8

# ==================================================
# function / ファイルダイヤログ
# ==================================================
def OpenFileDialog(s1):
    """
    ///ファイルダイヤログを開き、ファイル選択可能な状態に
    ///戻り値:ファイルパス
    """
    #dirName = "csv_read_fld/"
    dirName = s1 + "/"
    typeList = [('CSVファイル','*.csv')] 
    filename = filedialog.askopenfilename(
                title = "画像ファイルを開く",
                filetypes = typeList,
                initialdir = dirName
                )
    
    return filename

def CsvOpenAndReader(filePath):
    """
    ///CSV読込み、リストにして戻す
    ///戻り値:リスト
    """
    with open(filePath,'r',encoding="utf-8") as f:
        reader = csv.reader(f)

        # 1行ごとに読み込み、リストにする
        l = [row for row in reader]

        # 0行目(ラベル情報)のため削除
        l.pop(0)

    return l

def GetTimeStamp():
    """
    ///ファイル保存用のタイムスタンプ文字列
    ///戻り値:文字列
    """
    dt = datetime.datetime.now()
    dt = dt.strftime('%Y_%m%d_%H%M_%S')
    return dt

# ====================================================================================================
# ウィジェット起動/メイン
# ====================================================================================================
root = tk.Tk()
app = Application(master=root)
app.mainloop()