python:一部のファイルを別フォルダにコピーする

プログラミング
スポンサーリンク




こんにちは、おみです。

フォルダ内のファイルのうち、特定のパターンに該当するファイルを一気に抽出したいときが、たまにあります。

そんなとき手作業でいちいち取り出すのはとても面倒なので、フォルダとファイルのパターンを指定し、パターンに一致したファイルを別フォルダにコピーするプログラムを作成しました。

 

スポンサーリンク

画面設計

入力項目

・送信元フォルダ … コピーを行いたいファイルが格納されているファイルのパスを記述する。

・宛先フォルダ … コピーしたファイルを保存するフォルダのパスを記述する。

・ファイル名 … 抽出対象となるファイル名のパターンを記述する。(正規表現でも可能)

・拡張子名 … 抽出対象となる拡張子を記述する。

 

入力例

→[C:¥example¥hogehoge]内のファイルのうち、名前に[hogehoge]が含まれていて、拡張子が[.txt]であるファイルを[Z:¥example¥output]にコピーする。

 

ソースコード

ファイル構造は、下記になります。

・Main.py

import tkinter as tk

from src.Model import Model
from src.View import View
from src.Controller import Controller


class Application(tk.Frame):
    def __init__(self, root):
        # スーパークラスのコンストラクタを呼び出し
        super().__init__(root)

        # モデルをインスタンス化
        self.model = Model(root)

        # ビューをインスタンス化
        self.view = View(root, self.model)

        # コントローラーをインスタンス化
        self.controller = Controller(root, self.model, self.view)

        # Viewにcommandを設定
        self.view.btn_execute["command"] = self.controller.btn_execute_clicked

        # 画面の設定
        root.geometry(str(self.model.width) + "x" + str(self.model.height))
        root.title(self.model.title)


# mainメソッド
def main():
    # 画面を生成
    root = tk.Tk()

    # 画面の設定を行う
    window = Application(root)

    # 画面を表示する
    window.mainloop()


if __name__ == "__main__":
    main()

・Model.py

import configparser as c
import tkinter as tk
import os
import re
import glob
import shutil


class Model(object):
    def __init__(self, root):
        # ---------------------------------
        # 初期設定
        # ---------------------------------
        # configファイルを読み込み
        self.config = c.ConfigParser()
        self.config.read("config.ini")

        self.title = self.config["DISPLAY_INFO"]["title"]
        self.width = self.config["DISPLAY_INFO"]["width"]
        self.height = self.config["DISPLAY_INFO"]["height"]

        # 画面パーツに入力した値を格納する変数を宣言
        self.ent_sender_text = tk.StringVar()
        self.ent_destination_text = tk.StringVar()
        self.ent_file_name_text = tk.StringVar()
        self.ent_extention_text = tk.StringVar()
        self.lbl_message_text = tk.StringVar()

    # ボタンクリック時起動メソッド
    def execute(self):
        # 入力値を変数に格納する
        sender_path = self.ent_sender_text.get()
        destination_path = self.ent_destination_text.get()
        file_name = self.ent_file_name_text.get()
        extention = self.ent_extention_text.get()

        # 指定されたフォルダが存在するかを確認する
        if not sender_path:
            self.lbl_message_text.set("送信元フォルダパスが入力されていません")
            return

        if not os.path.exists(sender_path):
            self.lbl_message_text.set("送信元フォルダが存在しません")
            return

        if not destination_path:
            self.lbl_message_text.set("宛先フォルダパスが入力されていません")
            return

        if not os.path.exists(destination_path):
            self.lbl_message_text.set("宛先フォルダが存在しません")
            return

        # 入力した値の形式を調整する
        # 送信元フォルダパス ... 末尾に\を付ける
        if not re.match('\\$', sender_path):
            sender_path = sender_path + "\\"

        # 宛先フォルダパス ... 末尾に\を付ける
        if not re.match("\\$", destination_path):
            destination_path = destination_path + "\\"

        # 拡張子 ... 指定されていない場合*とする
        if not extention:
            extention = "*"

        # 先頭に.をつける
        if extention[0:1] != r".":
            extention = r"." + extention

        # ファイル名、拡張子のパターンを生成する
        ptn_file_name = re.compile(file_name)
        ptn_extention = re.compile(extention)

        # ファイル一覧を取得する
        # file_map
        # キー: ファイル名
        # 値  : ファイルパス
        file_dict = {}
        for path in glob.glob(sender_path + "\\*"):
            file_dict.setdefault(path.replace(sender_path, ""), path)

        # ファイルをコピーする
        for str in file_dict.keys():
            # パターンと一致しているかを判定
            res_file_name = ptn_file_name.search(str)
            res_extention = ptn_extention.search(str)

            # 一致している場合、コピーを行う
            if res_file_name is not None and res_extention is not None:
                shutil.copy(sender_path + str, destination_path + str)

        self.lbl_message_text.set("処理が完了しました。")

・View.py

import tkinter as tk


class View(object):
    def __init__(self, root, model):
        self.root = root
        self.model = model

        # ---------------------------------
        # 画面の部品を生成
        # ---------------------------------
        # 送信元フォルダパスラベル
        self.lbl_sender_path= tk.Label(
            root,
            text="送信元フォルダ"
        )

        # 送信元フォルダパス入力欄
        self.ent_sender_path = tk.Entry(
            root,
            textvariable=model.ent_sender_text,
            width=50
        )

        # 宛先フォルダパスラベル
        self.lbl_destination_path = tk.Label(
            root,
            text="宛先フォルダ"
        )

        # 宛先フォルダパス入力欄
        self.ent_destination_path = tk.Entry(
            root,
            textvariable=model.ent_destination_text,
            width=50
        )

        # 対象ファイル名ラベル
        self.lbl_file_name = tk.Label(
            root,
            text="ファイル名"
        )

        # 対象ファイル名入力欄
        self.ent_file_name = tk.Entry(
            root,
            textvariable=model.ent_file_name_text,
            width=50
        )
        # 拡張子名ラベル
        self.lbl_extention = tk.Label(
            root,
            text="拡張子名"
        )

        # 拡張子名入力欄
        self.ent_extention = tk.Entry(
            root,
            textvariable=model.ent_extention_text,
            width=50
        )

        # 実行ボタン
        self.btn_execute = tk.Button(
            root,
            text="実行"
        )

        # 出力欄ラベル
        self.lbl_message = tk.Label(
            root,
            textvariable=model.lbl_message_text
        )

        # ---------------------------------
        # 画面の部品を配置
        # ---------------------------------
        self.lbl_sender_path.grid(row=0, column=0)
        self.ent_sender_path.grid(row=0, column=1)
        self.btn_execute.grid(row=0, column=2)
        self.lbl_destination_path.grid(row=1, column=0)
        self.ent_destination_path.grid(row=1, column=1)
        self.lbl_file_name.grid(row=2, column=0)
        self.ent_file_name.grid(row=2, column=1)
        self.lbl_extention.grid(row=3, column=0)
        self.ent_extention.grid(row=3, column=1)
        self.lbl_message.grid(row=4, column=1)

・Controller.py

class Controller(object):
def __init__(self, root, model, view):
self.root = root
self.model = model
self.view = view

# 挨拶ボタンクリック時の処理を定義
def btn_execute_clicked(self):
# モデルのメソッドを呼び出し
self.model.execute()

・config.ini

[DISPLAY_INFO]
title = "ファイル抽出"
width = 610
height = 125

 

改善点

例えば…

・とりあえず例外処理入れておく

・ダイアログからパスを選択できるようにする

・拡張子の複数指定や、大文字小文字を区別するか指定できるようにする

コメント

  1. […] […]

タイトルとURLをコピーしました