Python(正規表現を利用したファイル整理ツール)

正規表現については以前の投稿
Python(正規表現)
を参照してください。

正規表現とtkinterを組み合わせて、正規表現を確認することができる簡単なGUIツールは
正規表現(Regular Expression)に慣れよう
で紹介されています。

今回はファイルの拡張子でファイルの種類を分けて、フォルダにまとめることができるGUIツールを作成しました。
ダウンロードはこちら

企画概要
  • 「ファイル整理ツール」
    フォルダ内の各ファイルを拡張子で種類分けし、同じディレクトリ内に抽出したファイルを格納するフォルダを作成してまとめる。
仕様
  • メニューからディレクトリの指定、リロード、実行、ファイルの種類選択、終了が可能。

  • ファイルの種類はテキスト、エクセル、スライド、画像、音、動画、圧縮、プログラムで大別し、選択が可能。
    FILE_TYPES= ['text', 'excel', 'slide', 'image', 'music', 'movie', 'compress', 'program']

  • 大別した種類ごとに拡張子を登録しており、拡張子毎に選択が可能。
    TEXT_TYPES= ['doc', 'docx', 'odt', 'rtf', 'pdf', 'txt']
    EXCEL_TYPES= ['xlsx', 'csv', 'tsv', 'xls']
    SLIDE_TYPES= ['pptx', 'ppt', 'odp']
    IMAGE_TYPES = ['gif', 'png', 'bmp', 'jpeg', 'jpg', 'tga', 'tif', 'ppm']
    MUSIC_TYPES = ['mp3', 'wav', 'wma', 'asf', 'aac', 'flac', 'm4a']
    MOVIE_TYPES = ['mp4', 'mpg', 'mpeg', 'wmv', 'avi', 'mkv']
    COMPRESS_TYPES = ['zip', 'lzh', '7z', 'cab', 'tar', 'rar']
    PROGRAM_TYPES = ['c', 'cpp', 'h', 'cs', 'py', 'js', 'bat', 'php', 'java']

  • メニューのHelpまたはiボタンからツールの使い方を確認することが可能。

  • エントリーウィジェットからディレクトリの指定、拡張子検索、フォルダ名の入力が可能。

  • ディレクトリの指定はファイルダイアログ、使い方や実行確認はメッセージボックスを使用。

  • ディレクトリを指定すると、対象のディレクトリを別ウィンドウで表示。

  • チェックした種類のファイルをスクロールリストボックスウィジェットでファイル名を一覧表示。

  • 一覧のファイルは拡張子を選択チェックし、ReLoadすることで更新可能。

  • 一覧の中からマウス左クリックでファイル選択を行い、選択状態で右クリックまたはPickUpボタンでPickUpリストに入れることが可能。

  • ExecutionボタンでPickUpリスト内のファイルを入力したフォルダ名内に格納してまとめる。
    その際に確認ダイアログを表示して実行確認を行う。
    フォルダ名が未入力では、Executionボタンを押しても警告ダイアログを表示して実行できない。

  • 拡張子検索は正規表現で行う。

使用ライブラリフレームワーク
  1. re
  2. os
  3. subprocess
  4. tkinter
  5. PyInstaller

実際にプログラムしたコードは以下のようになります。

import re
import os
import subprocess
import tkinter as Tk
import tkinter.filedialog as D
import tkinter.messagebox as M

FILE_TYPES= ['text', 'excel', 'slide', 'image', 'music', 'movie', 'compress', 'program']
TEXT_TYPES= ['doc', 'docx', 'odt', 'rtf', 'pdf', 'txt']
EXCEL_TYPES= ['xlsx', 'csv', 'tsv', 'xls']
SLIDE_TYPES= ['pptx', 'ppt', 'odp']
IMAGE_TYPES = ['gif', 'png', 'bmp', 'jpeg', 'jpg', 'tga', 'tif', 'ppm']
MUSIC_TYPES = ['mp3', 'wav', 'wma', 'asf', 'aac', 'flac', 'm4a']
MOVIE_TYPES = ['mp4', 'mpg', 'mpeg', 'wmv', 'avi', 'mkv']
COMPRESS_TYPES = ['zip', 'lzh', '7z', 'cab', 'tar', 'rar']
PROGRAM_TYPES = ['c', 'cpp', 'h', 'cs', 'py', 'js', 'bat', 'php', 'java']
GEO_MAIN = '750x400+20+20'

class Frame(Tk.Frame):

    def __init__(self, master=None):
        Tk.Frame.__init__(self, master)
        self.master.title('Organize Files')
        self.master.geometry(GEO_MAIN)

        ### Menu
        menu_bar = Tk.Menu(self, tearoff=0)
        # File
        menu_file = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="File", menu=menu_file,  underline=0)
        menu_file.add_command(label="Browse Dir.", command=self.browse, underline=0, accelerator = 'Ctrl-O')
        menu_file.add_command(label="ReLoad", command=self.load_dir, underline=0, accelerator = 'Ctrl-R')
        menu_file.add_command(label="Execution", command=self.execution, underline=0, accelerator = 'Ctrl-S')        
        menu_file_type = Tk.Menu(menu_file, tearoff=0)
        menu_file.add_cascade(label="File Type", menu=menu_file_type, underline=0)
        menu_file.add_separator()
        menu_file.add_command(label="Exit", command=self.exit, underline=0 , accelerator = 'Ctrl-Q')

        menu_text_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="text", menu=menu_text_type, underline=0)
        menu_excel_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="excel", menu=menu_excel_type, underline=0)
        menu_slide_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="slide", menu=menu_slide_type, underline=0)
        menu_image_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="image", menu=menu_image_type, underline=0)
        menu_music_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="music", menu=menu_music_type, underline=0)
        menu_movie_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="movie", menu=menu_movie_type, underline=0)
        menu_compress_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="compress", menu=menu_compress_type, underline=0)
        menu_program_type = Tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="program", menu=menu_program_type, underline=0)
        menu_bar.add_command(label="Help", command=self.show_info, underline=0)

        # short-cuts
        self.master.bind('<Control-KeyPress-o>', self.browse)
        self.master.bind('<Control-KeyPress-s>', self.execution)
        self.master.bind('<Control-KeyPress-r>', self.load_dir)
        self.master.bind('<Control-KeyPress-q>', self.exit) 

        # check buttons in menu_file_type
        self.var_type = dict()
        for i, file_type in enumerate(FILE_TYPES):
            self.var_type[file_type] = Tk.IntVar()
            menu_file_type.add_checkbutton(label=file_type, variable=self.var_type[file_type])
            menu_file_type.invoke(i)

        # check buttons in menu_text_type
        self.var_text_type = dict()
        for i, text_type in enumerate(TEXT_TYPES):
            self.var_text_type[text_type] = Tk.IntVar()
            menu_text_type.add_checkbutton(label=text_type, variable=self.var_text_type[text_type])
            menu_text_type.invoke(i)

        # check buttons in menu_excel_type
        self.var_excel_type = dict()
        for i, excel_type in enumerate(EXCEL_TYPES):
            self.var_excel_type[excel_type] = Tk.IntVar()
            menu_excel_type.add_checkbutton(label=excel_type, variable=self.var_excel_type[excel_type])
            menu_excel_type.invoke(i)

        # check buttons in menu_slide_type
        self.var_slide_type = dict()
        for i, slide_type in enumerate(SLIDE_TYPES):
            self.var_slide_type[slide_type] = Tk.IntVar()
            menu_slide_type.add_checkbutton(label=slide_type, variable=self.var_slide_type[slide_type])
            menu_slide_type.invoke(i)

        # check buttons in menu_image_type
        self.var_image_type = dict()
        for i, image_type in enumerate(IMAGE_TYPES):
            self.var_image_type[image_type] = Tk.IntVar()
            menu_image_type.add_checkbutton(label=image_type, variable=self.var_image_type[image_type])
            menu_image_type.invoke(i)

        # check buttons in menu_music_type
        self.var_music_type = dict()
        for i, music_type in enumerate(MUSIC_TYPES):
            self.var_music_type[music_type] = Tk.IntVar()
            menu_music_type.add_checkbutton(label=music_type, variable=self.var_music_type[music_type])
            menu_music_type.invoke(i)

        # check buttons in menu_movie_type
        self.var_movie_type = dict()
        for i, movie_type in enumerate(MOVIE_TYPES):
            self.var_movie_type[movie_type] = Tk.IntVar()
            menu_movie_type.add_checkbutton(label=movie_type, variable=self.var_movie_type[movie_type])
            menu_movie_type.invoke(i)

        # check buttons in menu_compress_type
        self.var_compress_type = dict()
        for i, compress_type in enumerate(COMPRESS_TYPES):
            self.var_compress_type[compress_type] = Tk.IntVar()
            menu_compress_type.add_checkbutton(label=compress_type, variable=self.var_compress_type[compress_type])
            menu_compress_type.invoke(i)

        # check buttons in menu_program_type
        self.var_program_type = dict()
        for i, program_type in enumerate(PROGRAM_TYPES):
            self.var_program_type[program_type] = Tk.IntVar()
            menu_program_type.add_checkbutton(label=program_type, variable=self.var_program_type[program_type])
            menu_program_type.invoke(i) 

        # add menu bar
        try:
            self.master.config(menu=menu_bar)     # this required to show the menu bar
        except AttributeError:
            self.master.Tk.call(master, "config", "-menu", menu_bar)

        self.create_widgets()
        self.pack(fill=Tk.BOTH, expand=1)

    def create_widgets(self):
        f=Tk.Frame(self)
        f.pack(fill=Tk.X)
        f_dir = Tk.LabelFrame(f, text='Directory')
        f_dir.pack(side=Tk.LEFT, padx=10, pady=2)
        self.e_dir = Tk.Entry(f_dir, width=70)
        self.e_dir.pack(side=Tk.LEFT)
        self.e_dir.bind('<Return>', self.load_dir)
        self.b_dir = Tk.Button(f_dir, text='Browse', command=self.browse)
        self.b_dir.pack(side=Tk.LEFT, padx=2)        
        b_info=Tk.Button(f, bitmap='info', width=25, command=self.show_info)
        b_info.pack(side=Tk.LEFT, padx=20, pady=10)        

        f_type = Tk.LabelFrame(self, text='File Type')
        f_type.pack(anchor=Tk.W,padx=10, pady=2)
        self.e_type = Tk.Entry(f_type, width=10)
        self.e_type.pack(side=Tk.LEFT)
        self.e_type.bind('<Return>', self.load_dir)
        self.b_re = Tk.Button(f_type, text='ReLoad', command=self.load_dir)
        self.b_re.pack(side=Tk.LEFT, padx=2)    
        for file_type in FILE_TYPES:
            cb = Tk.Checkbutton(f_type, text=file_type, variable=self.var_type[file_type],
                                relief=Tk.FLAT, justify = Tk.LEFT)
            cb.pack(side=Tk.LEFT, padx=5)  

        f_folder = Tk.LabelFrame(self, text='New folder name')
        f_folder.pack(anchor=Tk.W, padx=10, pady=2)
        self.e_folder = Tk.Entry(f_folder, width=30)
        self.e_folder.pack(side=Tk.LEFT)
        self.e_folder.bind('<Return>', self.execution)
        self.b_folder = Tk.Button(f_folder, text='Execution', command=self.execution)
        self.b_folder.pack(side=Tk.LEFT, padx=2)

        f_list = Tk.LabelFrame(self, text='Files list')
        f_list.pack(side=Tk.LEFT, anchor=Tk.NW, padx=10, pady=2)
        self.once = False
        self.listbox = Tk.Listbox(f_list,selectmode=Tk.EXTENDED)
        self.listbox.pack(side=Tk.LEFT, fill=Tk.Y, expand=1)
        self.listbox.bind("<3>", self.pick_up)         
        self.scrollbar = Tk.Scrollbar(f_list, orient=Tk.VERTICAL,command=self.listbox.yview)
        self.listbox['yscrollcommand'] = self.scrollbar.set
        self.scrollbar.pack(side=Tk.RIGHT, fill=Tk.Y, expand=1)
        self.b_pick = Tk.Button(f_list, text='Pick up', command=self.pick_up)
        self.b_pick.pack(side=Tk.BOTTOM,padx=2)    

        f_pick = Tk.LabelFrame(self, text='Pick up')
        f_pick.pack(side=Tk.LEFT, anchor=Tk.NW, padx=10, pady=2)
        self.pickups=[]
        self.listbox2 = Tk.Listbox(f_pick,selectmode=Tk.EXTENDED)
        self.listbox2.pack(side=Tk.LEFT, fill=Tk.Y, expand=1)
        self.listbox2.bind("<3>", self.remove)       
        self.scrollbar2 = Tk.Scrollbar(f_pick, orient=Tk.VERTICAL,command=self.listbox2.yview)
        self.listbox2['yscrollcommand'] = self.scrollbar2.set
        self.scrollbar2.pack(side=Tk.RIGHT, fill=Tk.Y, expand=1)        
        self.b_pick = Tk.Button(f_pick, text='Remove', command=self.remove)
        self.b_pick.pack(side=Tk.BOTTOM,padx=2)

    ## functions ------------------------------------------------
    def make_regexp(self,types):        
        self.make_compile_list(types)
        string = "\.("
        for k in self.compiles:
            string += k + '|'
        string = string[0:-1] + ')$'
        return re.compile(string, re.I)

    def make_compile_list(self,types):
        self.compiles=[]
        self.compiles.append(self.e_type.get())
        for k, v in types.items():
            if v.get():
                if k == "text":
                    self.add_compile_list(self.var_text_type)
                elif k == "excel":
                    self.add_compile_list(self.var_excel_type)
                elif k == "slide":
                    self.add_compile_list(self.var_slide_type)
                elif k == "image":
                    self.add_compile_list(self.var_image_type)
                elif k == "music":
                    self.add_compile_list(self.var_music_type)
                elif k == "movie":
                    self.add_compile_list(self.var_movie_type)
                elif k == "compress":
                    self.add_compile_list(self.var_compress_type)
                elif k == "program":
                    self.add_compile_list(self.var_program_type)

    def add_compile_list(self,types):
        for k, v in types.items():
            if v.get():
                self.compiles.append(k)

    def update(self, dir_path, types):
        if self.once:
            self.listbox.delete(0, Tk.END)
            self.renew()

        self.once=True
        pat = self.make_regexp(types)
        os.chdir(dir_path)
        self.file_list=[]
        for f in os.listdir():
            if pat.search(f):
                self.file_list.append(f)
        self.listbox.insert(Tk.END, *self.file_list)                

    def browse(self, event=None):
        dir_path = D.askdirectory()
        if dir_path:
            self.e_dir.delete(0, Tk.END)
            self.e_dir.insert(0, dir_path)
            subprocess.Popen(['explorer', dir_path.replace("/", "\\")])            
            self.load_dir()

    def load_dir(self, event=None):
        self.update(self.e_dir.get(), self.var_type)

    def renew(self):
        if self.pickups:
            self.pickups.clear()
            self.listbox2.delete(0, Tk.END)        

    def pick_up(self, event=None):
        self.renew()
        selected = [ int(x) for x in self.listbox.curselection()]
        if len(selected) == 0:
            return None

        for i in selected:
            self.pickups.append(self.file_list[i])
            self.listbox2.insert(Tk.END,self.file_list[i])
        self.listbox.selection_clear(min(selected), max(selected))

    def remove(self, event=None):
        if not self.pickups:
            return None

        selected2 = [ int(x) for x in self.listbox2.curselection()]
        if len(selected2) == 0:
            return None
        elif len(selected2) > 1:
            M.showwarning(u"警告",
                          u"削除の複数選択には対応しておりません。\n"
                          u"1つずつ選択してRemoveしてください。")
            return None

        for i in selected2:
            self.pickups.remove(self.pickups[i])
            self.listbox2.delete(i)
        self.listbox2.selection_clear(min(selected2), max(selected2))

    def execution(self,event=None):
        if not self.pickups:
            M.showerror(u"注意",
                        u"ファイルを選択してください。")
            return None

        name = self.e_folder.get()
        if name:
            m = M.askokcancel(u"確認",
                            u"選択したファイルをフォルダにまとめて良いですか?")
            if m:
                for file in self.pickups:
                    os.renames(file,name+"\\"+file)
                M.showinfo("完了","フォルダ分けしました。")
                self.renew()
                self.e_folder.delete(0,Tk.END)
            else:
                return None
        else:
            M.showerror(u"注意",
                        u"フォルダ名を入力してください。")

    def exit(self,event=None):
        self.master.destroy()

    def show_info(self):
        M.showinfo(u"<使い方>",
                   u"Entry か Browseボタンでディレクトリを選択してください。\n"
                   u"そのディレクトリに含まれるファイルの一覧が表示されます。\n"
                   u"別窓でディレクトリが展開されます。\n"
                   u"一覧に表示するファイルの種類をメニューバーのチェックボタンで変えることができます。\n\n"
                   u"~~ファイル整理の方法~~\n"
                   u"リストボックスからファイルをマウス左ボタンで選択してください。\n"
                   u"マウス左ボタンのドラッグ、Shift + マウス左ボタン、Ctrl + マウス左ボタン などで複数の選択が可能です。\n"
                   u"選択が終わったら、マウス右ボタンまたはPick upボタンをクリックしてください。\n" 
                   u"選択されたファイルが右側に表示されます。\n"
                   u"New folder nameを入力してEnterまたはExecutionを押してください。\n"
                   u"確認ダイアログでOKを押すと、選択したファイルをまとめたフォルダが作成されます。")

##------------------------------------------------ 
if __name__ == '__main__':
    f = Frame()
    f.pack()
    f.mainloop()

このアプリケーションはこちらで紹介されているものを参考にさせてもらいました。
python ファイル名に応じたフォルダ分け(ファイル整理術)

def make_regexp(self,types)
拡張子のリストを引数に渡し、正規表現の文字列を生成してコンパイルしたものを返り値にしています。
このようにすることでファイル検索用の正規表現を事前に作成し、大量のファイルをチェックする負荷を下げるようにしています。
def make_compile_list(self,types)
チェックボタンウィジェットの選択されているファイルの種類を確認し、該当するファイル毎にリストに追加するようにしています。
また、エントリーボックスに入力されている拡張子もこのときリストに追加されます。
def add_compile_list(self,types)
ファイルの種類ごとにチェックの有無を確認して、拡張子をリストに登録しています。
def update(self, dir_path, types)
ディレクトリ選択時やリロードされた時に実行され、選択されているディレクトリ内の検索対象となっているファイルをリストボックスに反映して表示します。
該当するディレクトリやファイルがない場合、リストボックスには何も表示されません。
def browse(self, event=None)
ファイルダイアログを開いて選択されたディレクトリをエントリーボックスに入力しています。
また、その際に選択されたディレクトリをsubprocessモジュールを利用して別ウィンドウで展開されるようにしています。
def load_dir(self, event=None)
エントリーに入力されているディレクトリを読み取り、updateメソッドを実行します。
ファイルダイアログを使用せずに直接ディレクトリを入力し、Enterキーを押した場合やReload処理はこれを実行しています。
def renew(self)
ピックアップされているファイルを削除し、新規の状態に戻すメソッドです。
updateやpickup、executionメソッドが実行される場合、pickupリストは、新規状態に戻す仕様となっています。
def pick_up(self, event=None)
リストボックスのチェックを確認し、該当するファイルをpickupリストに登録し、pickup用リストボックスに表示します。
また、実行後にリストボックスの選択は解除されます。
def remove(self, event=None)
pickupリストから選択されたファイルを取り除きます。
pickupリストの内容が変わる都合上、複数選択はできないように1つずつ取り除く仕様にしています。
def execution(self,event=None)
選択されたファイルを同じディレクトリ内に新規フォルダとしてまとめます。
pickupリストにファイルがあるか、新規フォルダ名が入力されているかどうかを確認し、できていない場合はエラーをダイアログで表示します。
実行していいかOKかCancelの確認をダイアログで表示し、Cancelの場合は何もしません。
OKで実行した場合はフォルダに選択したファイルがまとめられ、確認用のダイアログが表示されます。
def exit(self,event=None)
アプリケーションを終了させます。
def show_info(self)
使い方をダイアログで表示します。

subprocess モジュールについて詳しくはこちら
subprocess --- サブプロセス管理
Pythonで特定のファイルをアプリケーションで開く方法とフォルダを開く方法

organizefiles.exe アプリケーション説明

今回作成したGUIアプリは以下からダウンロードして、Windows環境で使用することができます。
ファイル整理ツール
zip圧縮されていますので、ダウンロード後は解凍して「~.exe」の実行ファイルをダブルクリックしてください。

<使用方法>

  1. Browseボタンでディレクトリを選択してください。
  2. 一覧に表示するファイルの種類をメニューバーのチェックボタンで変えることができます。
  3. リストボックスからファイルをマウス左ボタンで選択してください。
  4. 選択が終わったら、マウス右ボタンまたはPick upボタンをクリックしてください。
  5. New folder nameを入力してEnterまたはExecutionを押してください。
  6. 確認ダイアログでOKを押すと、選択したファイルをまとめたフォルダが作成されます。

フォルダ内のファイル整理が苦手だって人は活用してみてください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください