Organizador de Arquivos em Python + tkinter

Publicado por Pedro Fernandes (última atualização em 30/04/2023)

[ Hits: 1.693 ]

Homepage: https://github.com/PedroF37

Download Organizador-de-Arquivos.7z




Projeto de automação em Python (Organizador de Arquivos)

Dada uma pasta com vários arquivos de várias extensões, aplicativo permite guardar arquivos em pastas nomeadas com a extensão; arquivos .pdf na pasta pdf, arquivos .txt na pasta txt etc.

Aplicativo permite também arquivar e comprimir arquivos, usando formato tar.xz (txz) e deletar arquivos. Projeto trabalha apenas com arquivos, não com pastas. Projeto também não faz recursão em pasta, ou seja, não procura dentro de subpastas.

Configuração é simples: arquivo extensions.py, tem a lista das extensões de arquivos reconhecidos. Basta adicionar (ou deletar) extensões. Feito.
Fonte usada: Roboto

Projeto feito para praticar (não que seja algo útil kkk)

Original:
https://github.com/PedroF37/Organizador-de-Arquivos

  



Esconder código-fonte

Arquivo: frontend.py

# -------------------------------------------------------------------------- #
# IMPORTAÇÕES


# tkinter
from tkinter import Tk, Frame, Label, Button, PhotoImage
from tkinter.messagebox import showinfo, showwarning
from tkinter.filedialog import askdirectory

from tkinter.ttk import Style, Combobox

# matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt

# pathlib
from pathlib import Path

# os
from os import chdir, getcwd

# sys
from sys import exit

# views
from views import list_directory, organize_files
from views import compress_files, compress_all_files
from views import delete_files, delete_all_files


# -------------------------------------------------------------------------- #
# CONSTANTES


# Cores
COLOR1 = '#464646'  # Fundo
COLOR2 = '#ffffff'  # Letra
COLOR3 = '#676767'  # Linha de separação

# Home do usuário
home = Path.home()


# -------------------------------------------------------------------------- #
# FUNÇÕES


def choose_folder():
    """Cuida da escolha da pasta alvo."""
    try:
        target_folder = askdirectory(initialdir=home)
    # Para não dar um erro feioso se fechar a janela principal
    except Exception as err:
        exit()
    else:
        '''
        dicionario tem como chave a extensão e como valor o número
        de arquivos com essa extensão. Em ext_list, uso do facto de que
        por padrão o dicionário retorna a chave, logo não precisa colocar
        filedict.keys(). number_list e legend_list são para o gráfico pie.
        ext_list é para os combobox. legend_list é algo como: .pdf : 35
        O "if target_folder:", trata do caso de apertar botão de cancelar.
        Quando isso acontece, dá erro de "TypeError" e mesmo com except não
        sei como tratar de forma que não apareca outra mensagem feiosa
        na tela. Logo, temos "if target_folder:" ...
        '''
        if target_folder:
            size, filedict = list_directory(target_folder)
            number_list = [number for number in filedict.values()]
            ext_list = [ext for ext in filedict]
            legend_list = [
                f'{ext} : {number}'
                for ext, number in filedict.items()
            ]

            # Aqui, é para o caso de não ter arquivos, não mostrar
            # a legenda e o título sem o gráfico (que fica feioso kk)
            if size > 0:
                pie_graph(size, number_list, legend_list)
            else:
                showinfo('', 'Pasta selecionada está vazia')
                return

            # 'insert('.*')', para dar opção de compactar/deletar
            # todos os arquivos
            ext_list.insert(0, '.*')

            delete_combobox['values'] = ext_list
            extension_combobox['values'] = ext_list

            organize_button['state'] = 'active'
            compact_button['state'] = 'active'
            delete_button['state'] = 'active'

            organize_button['command'] = lambda: organize(target_folder)
            compact_button['command'] = lambda: compress(target_folder)
            delete_button['command'] = lambda: choose_delete(target_folder)
        else:
            exit()


def organize(target_folder):
    """Cuida de chamar função que organiza arquivos."""
    organize_files(target_folder)
    showinfo('', 'Arquivos organizados com sucesso')
    reset_app()


def compress(target_folder):
    """Cuida de chamar função que compacta arquivos."""
    if not extension_combobox.get():
        showwarning('', 'Tem que selecionar uma extensão')
        return
    # TODOS os arquivos
    elif extension_combobox.get() == '.*':
        compress_all_files(target_folder)
    else:
        # Somente para a extensão escolhida
        compress_files(target_folder, extension_combobox.get())

    showinfo('', 'Arquivos comprimidos com sucesso')
    reset_app()


def choose_delete(target_folder):
    """Cuida de chamar função que deleta arquivos."""
    if not delete_combobox.get():
        showwarning('', 'Tem que selecionar uma extensão')
        return
    # Age sob TODOS os arquivos
    elif delete_combobox.get() == '.*':
        delete_all_files(target_folder)
    else:
        # Somente para a extensão escolhida
        delete_files(target_folder, delete_combobox.get())

    showinfo('', 'Arquivos deletados com sucesso')
    reset_app()


def pie_graph(size, number_list, legend_list):
    """
    Cuida de mostrar gráfico pizza
    com as extensões e número por cada
    extensão no diretório.
    """
    graph = plt.Figure(figsize=(5, 3), dpi=90)
    ax = graph.add_subplot(111)

    ax.pie(
        number_list,
        wedgeprops=dict(width=0.3),
        shadow=True, startangle=90
    )

    # Deu um trabalhão para descobrir
    # como fazer isto!! kkk
    ax.set_title(
        f'Tamanho Total: {size} MB',
        color='w', weight='bold',
        fontsize=10
    )

    ax.legend(
        legend_list, loc="lower center",
        bbox_to_anchor=(1.30, 0.50),
        title='Quantidade'
    )

    # Coloca o fundo do gráfico igual ao fundo do app
    graph.patch.set_facecolor(COLOR1)

    canvas = FigureCanvasTkAgg(graph, output_frame)
    canvas.get_tk_widget().place(x=-60, y=0)


def reset_app():
    """Cuida de resetar todo o app."""
    chdir(home)
    organize_button['state'] = 'disabled'
    compact_button['state'] = 'disabled'
    delete_button['state'] = 'disabled'
    delete_combobox['values'] = ''
    delete_combobox.set('')
    extension_combobox['values'] = ''
    extension_combobox.set('')


# -------------------------------------------------------------------------- #
# JANELA


window = Tk()
window.title('')
window.geometry('800x400')
window.resizable(width=False, height=False)
window.configure(background=COLOR1)

style = Style(window)
style.theme_use('clam')


# -------------------------------------------------------------------------- #
# FRAMES


title_frame = Frame(window, width=800, height=50, bg=COLOR1)
title_frame.grid(row=0, column=0, sticky='nw')

operation_frame = Frame(window, width=400, height=350, bg=COLOR1)
operation_frame.grid(row=1, column=0, sticky='nw')

output_frame = Frame(window, width=400, height=350, bg=COLOR1)
output_frame.place(x=400, y=50)


# -------------------------------------------------------------------------- #
# CONFIGURANDO TITLE_FRAME


logo = PhotoImage(file='icones/logo.png')
title_label = Label(
    title_frame, width=800, image=logo,
    text='   Organizador de Arquivos',
    compound='left', font=('Roboto 15 bold'),
    anchor='nw', bg=COLOR1, fg=COLOR2
)
title_label.place(x=10, y=10)

separator_label = Label(
    title_frame, width=800,
    text='', font=('Roboto 1'),
    bg=COLOR3
)
separator_label.place(x=0, y=48)


# -------------------------------------------------------------------------- #
# CONFIGURANDO OPERATION_FRAME


# Escolher pasta
folder_choose_label = Label(
    operation_frame, width=70,
    text='Selecione a pasta',
    font=('Roboto 10 bold'), anchor='nw',
    fg=COLOR2, bg=COLOR1
)
folder_choose_label.place(x=15, y=15)

folder_img = PhotoImage(file='icones/folder.png')
folder_button = Button(
    operation_frame, width=100, image=folder_img, text='PASTA',
    compound='left', font=('Roboto 8 bold'), anchor='nw',
    relief='ridge', overrelief='sunken', bd=0, borderwidth=0,
    bg=COLOR1, fg=COLOR2, activebackground=COLOR1,
    activeforeground=COLOR2, command=choose_folder
)
folder_button.place(x=10, y=40)


# Opção para organizar arquivos
organize_label = Label(
    operation_frame, width=70, text='Organizar Arquivos',
    font=('Roboto 10 bold'), anchor='nw',
    fg=COLOR2, bg=COLOR1
)
organize_label.place(x=160, y=15)

organize_img = PhotoImage(file='icones/organize.png')
organize_button = Button(
    operation_frame, width=100, image=organize_img, text='ORGANIZAR',
    compound='left', font=('Roboto 8 bold'), anchor='nw',
    relief='ridge', overrelief='sunken', bd=0, borderwidth=0,
    bg=COLOR1, fg=COLOR2, activebackground=COLOR1,
    activeforeground=COLOR2
)
organize_button['state'] = 'disabled'
organize_button.place(x=160, y=40)


# Opção para comprimirr arquivos
compact_label = Label(
    operation_frame, width=70, text='Comprimir Arquivos',
    font=('Roboto 10 bold'), anchor='nw',
    fg=COLOR2, bg=COLOR1
)
compact_label.place(x=10, y=80)

extension_combobox = Combobox(operation_frame, width=12)
extension_combobox['state'] = 'readonly'
extension_combobox.place(x=10, y=105)

compact_img = PhotoImage(file='icones/compact.png')
compact_button = Button(
    operation_frame, width=100, image=compact_img, text='COMPRIMIR',
    compound='left', font=('Roboto 8 bold'), anchor='nw',
    relief='ridge', overrelief='sunken', bd=0, borderwidth=0,
    bg=COLOR1, fg=COLOR2, activebackground=COLOR1,
    activeforeground=COLOR2
)
compact_button['state'] = 'disabled'
compact_button.place(x=160, y=105)


# Opção para deletar arquivos
delete_label = Label(
    operation_frame, width=70, text='Deletar Arquivos',
    font=('Roboto 10 bold'), anchor='nw',
    fg=COLOR2, bg=COLOR1
)
delete_label.place(x=10, y=135)

delete_combobox = Combobox(operation_frame, width=12)
delete_combobox['state'] = 'readonly'
delete_combobox.place(x=10, y=160)

delete_img = PhotoImage(file='icones/delete.png')
delete_button = Button(
    operation_frame, width=100, image=delete_img, text='DELETAR',
    compound='left', font=('Roboto 8 bold'), anchor='nw',
    relief='ridge', overrelief='sunken', bd=0, borderwidth=0,
    bg=COLOR1, fg=COLOR2, activebackground=COLOR1,
    activeforeground=COLOR2
)
delete_button['state'] = 'disabled'
delete_button.place(x=160, y=160)


# -------------------------------------------------------------------------- #
# LOOP


window.mainloop()


# -------------------------------------------------------------------------- #

#####################################################################################

Arquivo: views.py

# -------------------------------------------------------------------------- #
# IMPORTAÇÕES


# os
from os import mkdir, chdir, remove, stat
from os.path import splitext

# shutil
from shutil import move

# glob
from glob import glob

# tarfile
import tarfile

# extensions
from extensions import extension_list


# -------------------------------------------------------------------------- #
# FUNÇÕES


def list_directory(directory):
    """Cuida de fazer a contagem das extensões."""
    chdir(directory)

    # Este é para a legenda do gráfico de pizza
    extension_dict = dict()

    # Este vai somando o tamanho de cada arquivo e depois
    # 'total_size' pega o tamanho total, para usar
    # no título do gráfico de pizza.
    extension_size = dict()

    for item in glob('*.*'):
        filename, ext = splitext(item)
        if ext in extension_list:
            extension_dict.setdefault(ext, 0)
            extension_size.setdefault(ext, 0)
            extension_dict[ext] += 1
            extension_size[ext] += stat(item).st_size / (1024 * 1024)

    total_size = sum([round(value, 2) for value in extension_size.values()])
    return total_size, extension_dict


def organize_files(directory):
    """Cuida de organizar os arquivos em pastas."""
    chdir(directory)
    for item in glob('*.*'):
        filename, ext = splitext(item)
        if ext in extension_list:
            new_directory = ext[1:]
            try:
                mkdir(new_directory)
            except FileExistsError:
                pass
            finally:
                move(item, new_directory)


def compress_files(directory, extension):
    """Cuida de compactar arquivos baseado na extensão."""
    chdir(directory)
    archive = 'archive.txz'

    with tarfile.open(archive, 'w:xz') as tar:
        [tar.add(item) for item in glob(f'*{extension}')]


def compress_all_files(directory):
    """Cuida de compactar TODOS os arquivos."""
    chdir(directory)
    archive = 'archive.txz'

    with tarfile.open(archive, 'w:xz') as tar:
        for item in glob('*.*'):
            filename, ext = splitext(item)
            if ext in extension_list:
                tar.add(item)


def delete_files(directory, extension):
    """Cuida de deletar arquivos baseado na extensão."""
    chdir(directory)
    [remove(item) for item in glob(f'*{extension}')]


def delete_all_files(directory):
    """Cuida de deletar TODOS os arquivos."""
    chdir(directory)
    for item in glob('*.*'):
        filename, ext = splitext(item)
        if ext in extension_list:
            remove(item)


# -------------------------------------------------------------------------- #

###################################################################################

Arquivo: extensions.py

'''
Lista de extensões de arquivos permitidos. Coloque mais extensões
a gosto e preferência, especialmente formatos office, que não uso office
e não conheco as extensões. '.*', significa todas as extensões e não
pode ser removida da lista, pois é para a opção de compactar/deletar
todos os arquivos.
'''
extension_list = [
    '.txt', '.pdf', '.docx', '.xlsx', '.bak', '.conf', '.config',
    '.sh', '.bash', '.py', '.c', '.html', '.css', '.list', '.log',
    '.avi', '.mkv', '.mp3', '.mp4', '.x264', '.x265', '.epub', '.mobi',
    '.azw3', '.png', '.jpg'
]

Scripts recomendados

Veja a versão das principais distrubuições.

Versão das principais distribuições.

Crivo de Eratóstenes Simples em Python

Just Do It - XML Generic Editor

Par ou ímpar no Python


  

Comentários
[1] Comentário enviado por maurixnovatrento em 14/05/2023 - 20:03h


Foi um ótimo trabalho.

___________________________________________________________
Conhecimento não se Leva para o Túmulo.
https://github.com/mxnt10

[2] Comentário enviado por sabe nada em 17/05/2023 - 17:09h


[1] Comentário enviado por mauricio123 em 14/05/2023 - 20:03h


Foi um ótimo trabalho.

___________________________________________________________
Conhecimento não se Leva para o Túmulo.
https://github.com/mxnt10


Obrigado.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts