0
2

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 1 year has passed since last update.

Pythonを利用したexcelファイルの集計です。

Posted at

エクセルのシート集計です。

エクセルファイルが、月別や、年度別、または営業所別に分かれてしまっている事って多いですよね。
作る側としては、その方が作りやすいのですが、一括で見たい場合は、全部開けるの面倒です。

エクセルのVBAなどで、合成させても良いと思いますが、python使ってやってみました。

機能を簡単に説明すると、任意のフォルダーのエクセルファイルを
1.全ファイルをシート名別に集計する。
2.全ファイルの任意のシートだけを集計する。
3.1と同時に、全ファイル内の全シートをファイル毎に集計する。
というものです。
なお、集計と言っても、合計が出るわけでなく、全部の行をがっちゃんこするだけです。

ファイルの中に、支店ごとにシートが分かれているみたいな場合は、3の機能で全支店のがっちゃんこが出来ます。(ふつうはファイルのサマリシートがあると思いますが、資産管理台帳みたいな場合は分かれたままの場合があります。)
月毎のファイルの中に、例えば、「受注」、「売上」、みたいにシートが分かれている場合、通期の「受注」がっちゃんこや、複数年度のがっちゃんこが出来ます。

もちろん、中身を適切に解析しているわけではなくpandas任せなので、基本は単純ながっちゃんこです。
タイトルの文字に変更などがあると、うまく集計できませんし、エクセルは、xlsxのみの対応です。
(HELP内でxls→xlsxの変換にも触れてますが、今のところ非公開です。)

また、tkinterの使い方が不慣れで、なんとなくぎこちない感じになってます。

でも、実用的にはそこそこ使えてますので、公開することにしました。

ソースコードは以下の通りです。

例によって、コメントがたくさんついてます。
また、ライブラリをそこそこ使ってますが、ほぼ一般的なものかと思います。

P.S.
VS codeのデバッグモードでしか動かしたこと何のですが、なぜか、終了してもちゃんと終わらないみたいで、毎回赤い四角ボタンを押して、終了してます。何が悪いのかわかる方いたら教えてください。

# %%
# 汎用版エクセルファイル集計
# 指定したフォルダ内のエクセルファイルを1つのファイルに集計する。

# 同じ形式のファイルが月別に分かれてしまっているような場合に一つにするために使用する。
# PowerQueryで高機能なファイル結合が出来るようになったが、それなりに面倒だし、ちょっとしたことでエラーになる。
# このプログラムは、pandasの機能を使って、完全に同じでない場合もなんとなく結合してくれる。
# そのため、変な結合の場合もあるが、その場合は元ファイルを修正してやり直せるよう、結合ファイルに元のファイル名とシート名を入れている。
# また、全データを結合するので、合計行があればそれも結合してしまう。
# そのため、結合後のファイルは手作業で修正が必要な場合が多い。
# システムから出力された正規化されたファイルの結合は、エクセルの機能を使った方が楽だが、実際には正規化されていないデータが多いので、これを作成した。

# 仕様
# データは、1行目にタイトル行がある事が望ましい。
# 同様に、最終行に合計などはない方が望ましい。(合計行もデータとして集計します。)
# それ以外の形だと、おかしな集計になることがあるが、pandasライブラリの仕様なので詳細は不明。

# 基本的に、同じつくりのエクセルを集計する事が多い。
# そのための集計方法は、指定されたフォルダの全エクセルファイルに対して、
# ①「ファイルの集計」=指定フォルダ内の、全ファイルの各シートを「シート別」に集計する。
# (例えば、ファイルが月別に分かれてしまっていて、それぞれのファイルに、「受注」「売上」等シートなどが分かれているケース等に使う。)
# ②「シートの集計」=指定フォルダ内の、各ファイル内で全てのシートを「1つのシート」に集計する。
# (例えば、支店別のファイルで、月別にシートが分かれてしまっていているケース等に使う。)
# ③「特定シートだけの集計」=指定フォルダ内の、全ファイルの「特定のシートだけ」を集計する。
# (例えば、、ファイルに集計に不要なシートが多数あるケースなどに使う。)
# の3つがある。(初めは③だけ作った。)

# 設計条件
# ①のケースは、全ファイルでシート名が同じシートのフォーマットが同一であれば、シート名毎にフォーマットは違っても問題ない。
# ②のケースは、同一ファイル内の全てのシートが同じフォーマットとする。
# ③のケースは、シートの指定は「シート番号」、「シート名」のいずれも可能にするが、フォーマットは共通とする。

# 上記を前提として、

# ①は、シート名毎に集計を行うことになるが、全てのファイルは同じシート名が使われている(例えば、「受注」、「売上」みたいな感じ)、と仮定する。
# シート名に「XX月分」などファイル別に名称を入れるなど、全ファイルでバラバラだと集計できないが、上記①を、シート名統一する目的にも使える。
# ②は、集計を全ファイル分を一つのファイルにまとめるか、各ファイル別にシートをまとめるか、の2通りの集計が考えられる。
# ③は、指定したシートが存在しない場合があることに留意する。

# 上記①②③を共通化出来るようにプログラムのUIと構造を考える。

# ■UIを考える
# まず、最初に①②③を選択するようにしたが、①②は同時に行う事が出来るので、
# 「シートの指定がない場合は、①②の全てのシートを集計し、指定があった場合は③とする。」
# とした。

# ■集計ロジックを考える。
# ①は、まず1つ目のファイルを読み込んで、1シート毎にDataFrameに取り込んで、出力ファイルに同じ名前のシートを作成する。
# 2つ目以降のシート以降は、出力ファイルの同じシートにアペンドしていく。という動きにする。
# とするつもりだったけど、pandasが優秀で、一気にできそうなので、そうした。
# 具体的には、すでにあったら追加するという方法で、1ファイル目から全ファイル処理することにした。

# ②は、ファイルを1つずつ読み込んで、その中の全てのシートをDataFrameに取り込んで、
# ファイルの全シートを読み込んでから「全シート集計」というシートに集約して、出力フォルダ内に同一名ファイルを作成する事を繰り返す。

# ③は、最初に作ったのプログラムで、②に近い動きをしてるけど、ファイル書き込みタイミングとかが違う。
# 具体的には、ファイルを1つずつ読み込んで、その中の指定のシートをDataFrameに取り込んでいく。
# 全ファイルの指定シートを読み込んだら、出力フォルダ内に同一名ファイルを作成して終わり。

# こうしてみると、ループと書き込みタイミングがそれぞれ違うな。
# GUIだけ共通化して、処理部分は、①②③別にした方が良いかも?→最終的に①②は同時に処理することにした。

# 以下は、画面に出す予定はない。(HELPには、出すかな。)
# 例えば、支店別ファイルに、「4月受注シート、4月売上シート、5月受注シート、5月売上シート、6月受注シート、6月売上シート」
# みたいに複数のフォーマットが複合してで保存をされてしまっているケースでは、
# 受注と売上を自動で判定して、全月受注、全月売上として、集計するのは、かなり困難で、現実的ではない。
# このケースでは、②のシート別集計は可能だが、それ以上の集計は自動では困難。
# そのため、結果的に、全支店分の4月受注シート、全支店分の4月売上シート、全支店分の5月受注シート、、、という集計までとする。

# なお、このプログラムは、excel,word,pdf,ppt等からテキストを抽出してワードクラウドを作成するプログラムをもとにしているため、
# エクセルだけの集計としては、若干冗長なつくりとなっている。

from glob import glob

# フルパスから、ファイル名だけを抜き出す、ファイル存在チェック等に使う。
import datetime
import os

# 画面用にtkinterライブラリを使う。
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox

# 画面に途中経過を表示するために、マルチスレッドを使う事にした。
import threading

# これらのライブラリで、エクセルドキュメントを取り込む。
import numpy as np
import pandas as pd


def xlsx2xlsx_12(scan_folder, out_folder, collect_sheet_name, sum_file, sheet_sum):
    """エクセルドキュメントから集計
    ①指定フォルダ内の、全ファイルの各シートを「シート別」に集計する。
    設計条件:全ファイルで共通のシート名が使われていて、シートごとにフォーマットは違うと考える。
    処理概要:全ファイル、1シートずつDataFrameに取り込んで、出力ファイルに、同一シート名で追加書き込みを繰り返す。
    (例えば、ファイルが月別に分かれてしまっていて、それぞれのファイルに、「受注」「売上」等シートなどが分かれているケース等に使う。)
    ②(sheet_sumがTrueの時のみ)指定フォルダ内の、各ファイル内で全てのシートを「1つのシート」に集計する。
    (例えば、支店別のファイルで、月別にシートが分かれてしまっていているケース等に使う。)

    Args:
        scan_folder (str): スキャンするフォルダー
        out_folder (str): テキスト分析の結果を保存するフォルダー
        collect_sheet_name:集計対象シート名(0は1シート目の意味)
        sum_file:結果出力ファイル名
        sheet_sum:各ファイル毎に、ファイル内の全シートを結合するかどうか

    Returns:
        int: 集計したファイルの数、シートの数、スキップしたファイルの数
    """

    # 他のサブルーチンとパラメータを合わせるため、ここで拡張子を追加することにした。
    sum_file = sum_file+".xlsx"
    number_of_files = 0
    number_of_sheets = 0
    number_of_skipfiles = 0

    # 集計するファイルが存在していると、2回目以降、そのファイルに追加していってしまうので、最初に集計ファイルを削除する。
    if os.path.exists(out_folder + sum_file):
        try:
            os.remove(out_folder + sum_file)
        except:
            print('出力ファイルが開かれているため、結果を出力できません。閉じてから再実行してください。')
            return (number_of_files, number_of_sheets, number_of_skipfiles)

    scan_path = scan_folder + '*.xlsx'
    filepaths = glob(scan_path)  # globで一気に全ファイル名を取得できる。

    print("指定フォルダ内の、各ファイル内で全てのシートを「1つのシート」に集計しながら、全ファイルの各シートを「シート別」に集計します。")
    print("■これから、エクセルファイルのすべてのシートを読んで、解析します。")
    print("■解析中のファイル名、シート名を表示します。")

    # pandasのデータフレーム化
    df = pd.DataFrame()
    df_all_sheets = pd.DataFrame()
    data_number_dict = {}

    for filepath in filepaths:

        print(filepath)
        t = os.path.getmtime(filepath)
        d = datetime.datetime.fromtimestamp(t)
        print(f'ファイル更新日時:{d}')

        try:
            # sheet_name=Noneとすると、全てのシートがディクショナリ形式で読み込まれる。
            df_all_sheets = pd.read_excel(filepath, sheet_name=None)
        except:
            # 読み込めなかった場合
            number_of_skipfiles += 1
            print('★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★')
            print('★★★' + filepath + 'は開けない(多分PW付)のでスキップします。★★★')
            print('★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★')
            continue

        number_of_files += 1

        # 全シートを読みこむと、ディクショナリ形式になるらしい。
        all_sheet = []
        # シート名をキーとしたディクショナリ形式なので、keysでシート毎に処理できる。
        for sheet_key in df_all_sheets.keys():
            number_of_sheets += 1
            df = df_all_sheets[sheet_key].replace(np.nan, '')
            # データフレームの長さがデータ行数になる。
            data_number = len(df)
            collect_sheet_name = sheet_key
            df = df.assign(FileName=filepath, FileTimeStamp=d,
                           SheetName=collect_sheet_name)
            # insertを使うとフラグメンテーションが発生し、パフォーマンスが著しくダウンする警告が出たので、止めた。
            # df.insert(0, 'FileName', filepath)
            # df.insert(1, 'SheetName', collect_sheet_name)
            print(f'集計シート名:{collect_sheet_name} --- {data_number}')
            all_sheet.append(df)
            all_df = pd.concat(all_sheet, ignore_index=True)

            if not sheet_key in data_number_dict:
                data_number_dict[sheet_key] = 0
            # 全体集計用のファイルに、全てのシートを追加していきます。
            # もし、出力用エクセルが存在したら、該当のシートだけ追加またはリプレースする。
            if os.path.exists(out_folder + sum_file):
                if data_number_dict[sheet_key] == 0:
                    # 最初にシートを作る時は、ヘッダ付き、リプレース
                    with pd.ExcelWriter(out_folder + sum_file, engine="openpyxl", mode='a', if_sheet_exists='replace') as writer:
                        df.to_excel(
                            writer, startrow=data_number_dict[sheet_key], header=True, sheet_name=str(collect_sheet_name))
                    data_number_dict[sheet_key] = data_number_dict[sheet_key] + \
                        data_number + 1  # 「+1」はヘッダ分
                else:
                    # シートに追加する場合は、ヘッダなし、追加行を指定して、追加
                    with pd.ExcelWriter(out_folder + sum_file, engine="openpyxl", mode='a', if_sheet_exists='overlay') as writer:
                        df.to_excel(
                            writer, startrow=data_number_dict[sheet_key], header=False, sheet_name=str(collect_sheet_name))
                    data_number_dict[sheet_key] = data_number_dict[sheet_key] + \
                        data_number
            else:
                # ファイルを作成するときもヘッダ付き
                df.to_excel(out_folder + sum_file,
                            sheet_name=str(collect_sheet_name))
                data_number_dict[sheet_key] = data_number_dict[sheet_key] + \
                    data_number + 1  # 「+1」はヘッダ分

        # ファイル内のシートを1つのシートに結合する場合は、以下を実施する。
        if sheet_sum == True:
            # 1つのファイルの全てのシートをデータフレームに取り込んだので、エクセルに書き出します。
            # ファイル名は読み込んだファイルに(シート集約)を付け足した名前にします。
            collect_sheet_name = "全シート集計"
            all_sum_file, _ = os.path.splitext(
                os.path.basename(filepath))
            all_sum_file = all_sum_file + "(シート集約).xlsx"

           # もし、出力用エクセルが存在したら、該当のシートだけ追加またはリプレースする。
            if os.path.exists(out_folder + all_sum_file):
                with pd.ExcelWriter(out_folder + all_sum_file, engine="openpyxl", mode='a', if_sheet_exists='replace') as writer:
                    all_df.to_excel(writer, sheet_name=str(collect_sheet_name))
            else:
                all_df.to_excel(out_folder + all_sum_file,
                                sheet_name=str(collect_sheet_name))
    # 上記を全てのファイル分繰り返します。

    return (number_of_files, number_of_sheets, number_of_skipfiles)


def xlsx2xlsx_3(scan_folder, out_folder, collect_sheet_name, sum_file):
    """エクセルドキュメントから集計
    指定フォルダ内の、特定シートを1つのシートに集計する。
    設計条件:シートの指定は「シート番号」、「シート名」のいずれも可能にする。
    処理概要:ファイルを1つずつ読み込んで、その中の指定のシートをDataFrameに取り込んでいく。
    全ファイルの指定シートを読み込んだら、出力フォルダ内に同一名ファイルを作成して終わり。

    Args:
        scan_folder (str): スキャンするフォルダー
        out_folder (str): テキスト分析の結果を保存するフォルダー
        collect_sheet_name:集計対象シート名(0は1シート目の意味)
        sum_file:結果出力ファイル名

    Returns:
        int: 集計したファイルの数、シートの数、スキップしたファイルの数
    """
    scan_path = scan_folder + '*.xlsx'
    number_of_files = 0
    number_of_sheets = 0
    number_of_skipfiles = 0

    filepaths = glob(scan_path)  # globで一気に全ファイル名を取得できる。
    print("指定フォルダ内の、全ファイルの「特定のシートだけ」を集計します。")
    print("■解析中のファイル名、シート名を表示します。")

    # pandasのデータフレーム化
    df = pd.DataFrame()

    for filepath in filepaths:

        print(filepath)
        t = os.path.getmtime(filepath)
        d = datetime.datetime.fromtimestamp(t)
        print(f'ファイル更新日時:{d}')

        try:
            pandas_work = pd.ExcelFile(filepath)
        except:
            number_of_skipfiles += 1
            print('★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★')
            print('★★★' + filepath + 'は開けない(多分PW付)のでスキップします。★★★')
            print('★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★')
            continue

        number_of_files += 1

        sheet_names = pandas_work.sheet_names
        if isinstance(collect_sheet_name, int):
            if len(pandas_work.sheet_names) < collect_sheet_name + 1:
                print('★★★' + filepath + 'には「' +
                      str(len(pandas_work.sheet_names)) + '枚」しかシートがありません★★★')
                print('★★★シート番号は0から始まるのですが「' + str(collect_sheet_name) +
                      '番」のシートは無いため、スキップします。★★★')
                continue
            now_sheet_name = pandas_work.sheet_names[collect_sheet_name]
        else:
            now_sheet_name = collect_sheet_name

        if not now_sheet_name in sheet_names:
            print('★★★' + filepath + 'には「' + now_sheet_name +
                  '」という名のシートがないのでスキップします。★★★')
            continue

        # 上記チェックで読み込めると判断できるので、読み込んで、データフレームに蓄積します。
        number_of_sheets += 1

        df2 = pd.read_excel(filepath, sheet_name=collect_sheet_name)
        df2 = df2.replace(np.nan, '')
        data_number = len(df2)
        print(f'集計シート名:{now_sheet_name} --- {data_number}')
        # ファイルには、読み込んだシートの番号でなく、名前を記録します。
        df2 = df2.assign(FileName=filepath, FileTimeStamp=d,
                         SheetName=now_sheet_name)
        df = pd.concat([df, df2])

    # 全てのファイルからデータフレームに取り込んだので、エクセルに書き出します。
    # その際、シート名は集計指示した物(シート番号の場合は番号)を使います。
    # もし、出力用エクセルが存在したら、該当のシートだけ追加またはリプレースする。
    if os.path.exists(out_folder + sum_file + "-" + str(collect_sheet_name) + ".xlsx"):
        with pd.ExcelWriter(out_folder + sum_file + "-" + str(collect_sheet_name) + ".xlsx", engine="openpyxl", mode='a', if_sheet_exists='replace') as writer:
            df.to_excel(writer, sheet_name=str(collect_sheet_name))
    else:
        df.to_excel(out_folder + sum_file + "-" + str(collect_sheet_name) +
                    ".xlsx", sheet_name=str(collect_sheet_name))

    return (number_of_files, number_of_sheets, number_of_skipfiles)


def help_screen():
    """説明画面
    この画面は、それほど邪魔な画面とは思えないので、常に表示することにしました。
    """
    sub_window = tk.Toplevel()
    sub_window.title("仕様などの説明")
    sub_window.geometry("1000x600")
# 大きさを変えたら、help_txのwidth,heightも変えないと見た目がおかしくなることがある。

# サブフレームの作成と設置
    sub_frame = ttk.Frame(sub_window)
    sub_frame.grid(column=0, row=0, sticky=tk.NSEW, padx=5, pady=10)

    help_tx = tk.Text(sub_frame, width=150, height=50,
                      padx=10, pady=10, wrap=tk.NONE)

    help_hsb = tk.Scrollbar(sub_frame, orient='h', command=help_tx.xview)
    help_tx.configure(xscrollcommand=help_hsb.set)    # 水平スクロールバーをアタッチ
    help_vsb = tk.Scrollbar(sub_frame, orient='v', command=help_tx.yview)
    help_tx.configure(yscrollcommand=help_vsb.set)    # 垂直スクロールバーをアタッチ

    help_tx.grid(column=0, row=5, sticky=('e', 'w'))    # 密着するよう配置
    help_hsb.grid(column=0, row=6, sticky=('e', 'w'))
    help_vsb.grid(column=1, row=5, sticky=('n', 's', 'w'))

    help_tx.insert('1.0', '''
※このアプリの説明

【一つのフォルダに集めた、同じ形式のエクセルシートを合成する。】

  【使い方】

   1.集計したい、同じ形式のエクセルを1つのフォルダに集めておく。
     (パスワード付きのファイルは、集計時にスキップします。
      合成したいファイルは、パスワードを解除しておいてください。)

   2.このプログラムで、上記フォルダを指定する。
     (入力に使うファイルを上書きしたり、削除することはありません。
      ただし、このフォルダ内に、結果出力フォルダとファイルを作成するので、書き込み権限が必要です。)
   
   3.全てのシートを集計したい場合は、シート指定はブランクのままにする。
     特定のシートだけを集計する場合は、「シート名」、または集計したい「シート番号」を指定する。
     (1シート目の番号は、0なので注意!)
    ■シート指定が無ければ
    ・指定フォルダ内の、全ファイルの各シートを「一つのファイルにシート名別」に集計する。
     また、「各ファイル毎に、ファイル内の全シートを結合する。」にチェックを入れた場合は、以下も同時に行う。
    ・指定フォルダ内の、各ファイル内で全てのシートを「それぞれファイル毎に1つのシート」として集計する。
    ■シート指定があれば
    ・指定フォルダ内の、各ファイル内の指定シートだけを「1つのファイル、1つのシート」として集計する。
      (該当するシートが無いファイルはスキップします。
       非表示シートも集計対象になるので、シート番号を指定した場合、見た目と番号が一致しないことがあります。)

   4.上記を指定したら「集計開始」ボタンを押す。

   5.指定したフォルダに「output_merge」というフォルダが出来て、そこに集計結果が集計される。


  【仕様】

   入力のファイルは、なにも変更しません。
   集計結果は常に上書きします。

   なお、本プログラムは、「.xlsx形式」のエクセルしか参照しません。
   したがって、古い形式のエクセルは、あらかじめ新しくしておく必要があります。
   そのため、別途、「xls2xlsx.xlsm」というエクセルマクロも用意しています。
   使い方は、簡単で、変換したいファイルがあるフォルダに上記のマクロエクセルを
   コピーして実行すると、同じフォルダ内の旧エクセル形式を新形式にします。

   P.S.この画面は、それほど邪魔な画面とは思えないので、常に表示することにしました。
     この画面だけを✖で閉じても、動作に影響はありません。
    '''[1:-1])


class Application():
    """これが、メインルーティンに相当する。
    勉強のため、クラス記述してみたけど、クラスにした意味はない。
    """

    def __init__(self):

        self.root = tk.Tk()

# メインウィンドウの設定
        self.root.title("【エクセル集計】")
        self.root.geometry("800x300")

# メインフレームの作成と設置
        self.frame = ttk.Frame(self.root)
        self.frame.grid(column=0, row=0, sticky=tk.NSEW, padx=5, pady=10)

# 各種ウィジェットの作成
        self.label_folder = ttk.Label(self.frame, text="集計フォルダ指定:")
        self.label_folder.grid(row=2, column=0, padx=10, pady=2, sticky=tk.E)

        self.entry_folder = tk.StringVar()
        self.entry_exec_folder = ttk.Entry(
            self.frame, textvariable=self.entry_folder, width=80)
        self.entry_exec_folder.grid(row=2, column=1, sticky=tk.W)

        self.button_folder = ttk.Button(
            self.frame, text="参照", command=self.ask_folder)
        self.button_folder.grid(row=2, column=2, sticky=tk.E)


# シート番号または、シート名を入力すると、特定シートのみを集計する。

        self.label_comment1 = ttk.Label(
            self.frame, text="------------")
        self.label_comment1.grid(row=3, column=1, padx=10, pady=2, sticky=tk.W)

# ここにチェックボタンを作ろう。sheet_sum

        # チェックボックスの作成
        self.check_value = tk.BooleanVar(value=False)
        self.check = ttk.Checkbutton(self.frame,
                                     text="各ファイル毎に、ファイル内の全シートを結合する。",    # チェックボックスの表示名
                                     variable=self.check_value  # チェックの状態を設定する
                                     )
        self.check.grid(row=4, column=1, padx=10, pady=2, sticky=tk.W)

        self.label_comment2 = ttk.Label(
            self.frame, text="「特定のシートだけ」を集計する場合は、「シート名」、または「シート番号」を以下に入力してください")
        self.label_comment2.grid(row=8, column=1, padx=10, pady=2, sticky=tk.W)

        self.label_sheet_name = ttk.Label(
            self.frame, text="※「シート番号」は 0から始まります。また、非表示シートも1枚と数えます。")
        self.label_sheet_name.grid(
            row=20, column=1, padx=10, pady=2, sticky=tk.W)

        self.entry_sheet_name = tk.StringVar()
        self.entry_sheet_name = ttk.Entry(
            self.frame, textvariable=self.entry_sheet_name, width=60)
        self.entry_sheet_name.grid(row=30, column=1, sticky=tk.W)
        # self.entry_sheet_name.insert(0, 0)

        self.button_execute = ttk.Button(
            self.frame, text="集計開始", command=self.click_execute)
        self.button_execute.grid(row=40, column=1, pady=10)

        help_screen()

        self.root.mainloop()

    def ask_folder(self):
        """
        entry_folderにチェックする親のディレクトリをセットする。
        """
        if now_processing:  # 実行中は無効にする。
            return
        fld = filedialog.askdirectory()
        self.entry_folder.set(fld)

    def click_execute(self):
        """実行ボタンで実行するメソッド(関数)
        フォルダー名を画面からgetし、実行可能な状態かチェックし、
        OKであれば、別スレッドとして集計・出力(main_syuukei)を起動する。
        時間がかかるプログラムは、別のスレッドとする。
        画面と別のスレッドにしないと、タイムアウトすることがあるらしい。
        """

        if now_processing:  # 実行中は無効にする。
            return

        scan_folder = self.entry_exec_folder.get() + '/'
        out_folder = scan_folder + 'output_merge' + '/'
        collect_sheet_name = self.entry_sheet_name.get()

        if str.isdigit(collect_sheet_name):
            collect_sheet_name = int(collect_sheet_name)

        if scan_folder == "/":
            messagebox.showerror('エラー', 'フォルダ指定が必要です。')
            return

        if not os.path.exists(scan_folder):    # ディレクトリがない場合もエラー
            messagebox.showerror('エラー', 'フォルダ指定が存在しません。')
            return

        # チェックの状態を取得(チェックされているときはTrue)
        sheet_sum_check = self.check_value.get()

        self.button_execute["text"] = "集計中..."
        self.label_exec = ttk.Label(self.frame, text="集計を実施中です。")
        self.label_exec.grid(row=100, column=1, pady=10)

        self.progress_bar = ttk.Progressbar(
            self.frame, maximum=10, value=0, length=100, mode='indeterminate')
        self.progress_bar.grid(
            row=100, column=1, sticky=(tk.N, tk.E, tk.S, tk.W))
        self.progress_bar.start(200)

        main_thread = threading.Thread(target=self.main_syuukei, args=(
            [scan_folder, out_folder, collect_sheet_name, sheet_sum_check], ))
        main_thread.start()

    def main_syuukei(self, list_args):
        """実際のファイルをすべて調べるメインの部分。
        afterを使うときは、パラメータは1つしか渡せないので、対応できるようLIST形式にしている。

        Args:
            list_args (list): スキャンするフォルダのパス、結果を出力するフォルダ
        """

        global now_processing
        now_processing = True

        scan_folder = list_args[0]
        out_folder = list_args[1]
        collect_sheet_name = list_args[2]
        sheet_sum = list_args[3]

        if not os.path.exists(out_folder):    # 出力用ディレクトリがない場合、作成する
            print("出力用ディレクトリを作成します")
            os.makedirs(out_folder)

        self.button_execute["text"] = " エクセル集計中..."
        self.root.update()

        print(collect_sheet_name)

        # シート指定が無ければすべてのシートを集計する。
        if collect_sheet_name == "":
            number_of_all_read = xlsx2xlsx_12(scan_folder,
                                              out_folder, collect_sheet_name, '全エクセル集計(シート別集約)', sheet_sum)
        else:
            number_of_all_read = xlsx2xlsx_3(scan_folder,
                                             out_folder, collect_sheet_name, '指定シート集計')

        print(f'集計したファイルの数={number_of_all_read[0]}')
        print(f'集計したシートの数={number_of_all_read[1]}')
        if number_of_all_read[2] != 0:
            print(f'読み込みエラーでスキップしたファイル(多分パスワード付き)の数={number_of_all_read[2]}')
        print("finish!")

        self.progress_bar.grid_remove()
        self.button_execute["text"] = "集計完了"
        self.label_exec = ttk.Label(
            self.frame, text="                集計を完了しました。               ")
        self.label_exec.grid(row=100, column=1, pady=10)

        end_message = 'お待たせしました。' + \
            str(number_of_all_read[0]) + 'ファイル(' + \
            str(number_of_all_read[1]) + 'シート)を集計しました。               '
        if not number_of_all_read[2] == 0:
            end_message = end_message + '\n' + \
                '' + str(number_of_all_read[2]) + \
                'ファイルは、読み取り不可(多分PW付)だったためスキップしました。\n   詳細は、コンソール画面を確認ください。'
        messagebox.showinfo('終了', end_message)
        now_processing = False
        self.root.destroy()


# これが、python流のメインを呼ぶ儀式
# 直接起動した場合にメインルーティンを起動するようになっている。
if __name__ == "__main__":
    now_processing = False
    sheet_sum = False

    app = Application()

# %%
0
2
0

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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?