컴퓨터/Python

excel pandas pyqt5 qtablewidget email 급여명세서

풍경소리^^ 2022. 9. 5. 17:17

pyqt5_excel_qtablewidget01.py--------------------

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, QHeaderView, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import Qt
import pandas as pd # pip install pandas

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 700, 500
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('Load Excel (or CSV) data to QTableWidget')

        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        layout_v = QVBoxLayout()

        self.button = QPushButton('&Load Data')
        self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        layout_v.addWidget(self.button)
        self.table = QTableWidget()
        layout_v.addWidget(self.table)

        layout_main.addLayout(layout_v)

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df = pd.read_excel(excel_file_dir, worksheet_name)
        if df.size == 0:
            return
        # print(df.columns[4])
        df.fillna('', inplace=True)
        df_name = df[['성명']]
        # print(df_name)

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_name.shape[0])
        self.table.setColumnCount(df_name.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_name.columns)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_name.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        
if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pyqt5_excel_qtablewidget02.py--------------------

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, QHeaderView, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import Qt
import pandas as pd # pip install pandas

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 700, 500
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('Load Excel (or CSV) data to QTableWidget')

        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        layout_v = QVBoxLayout()

        self.button = QPushButton('&Load Data')
        self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        layout_v.addWidget(self.button)
        self.table = QTableWidget()
        layout_v.addWidget(self.table)

        layout_main.addLayout(layout_v)

    def loadExcelData(self, excel_file_dir, worksheet_name):
        # df = pd.read_excel(excel_file_dir, worksheet_name)
        # if df.size == 0:
        #     return
        # print(df.columns[4])
        # df.fillna('', inplace=True)
        # df_name = df[['성명']]
        # print(df_name)

        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')[['성명','이메일']]

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table.shape[0])
        self.table.setColumnCount(df_table.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table.columns)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        
if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pyqt5_excel_qtablewidget03.py--------------------

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, QHeaderView, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import Qt
import pandas as pd # pip install pandas

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 700, 500
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('Load Excel (or CSV) data to QTableWidget')

        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        layout_v = QVBoxLayout()

        self.button = QPushButton('&Load Data')
        self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        layout_v.addWidget(self.button)
        self.table = QTableWidget()
        layout_v.addWidget(self.table)

        layout_main.addLayout(layout_v)

    def loadExcelData(self, excel_file_dir, worksheet_name):
        # df = pd.read_excel(excel_file_dir, worksheet_name)
        # if df.size == 0:
        #     return
        # print(df.columns[4])
        # df.fillna('', inplace=True)
        # df_name = df[['성명']]
        # print(df_name)

        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        
if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pyqt5_excel_qtablewidget04.py--------------------

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import Qt
import pandas as pd # pip install pandas

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 400, 500
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('Load Excel (or CSV) data to QTableWidget')

        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        layout_v = QVBoxLayout()

        self.button = QPushButton('&Load Data')
        self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        layout_v.addWidget(self.button)
        self.table = QTableWidget()
        layout_v.addWidget(self.table)

        layout_main.addLayout(layout_v)

    def loadExcelData(self, excel_file_dir, worksheet_name):
        # df = pd.read_excel(excel_file_dir, worksheet_name)
        # if df.size == 0:
        #     return
        # print(df.columns[4])
        # df.fillna('', inplace=True)
        # df_name = df[['성명']]
        # print(df_name)

        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        
if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay01.py--------------------

from operator import index
import sys
from unittest import result
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QAbstractScrollArea
from PyQt5.QtCore import Qt
from PyQt5 import QtGui
import pandas as pd # pip install pandas


class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 300, 400
        # self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        layout_v = QVBoxLayout()

        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        self.table = QTableWidget()
        layout_v.addWidget(self.table)

        layout_main.addLayout(layout_v)

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        table_df = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        table_02 = table_df.set_index("성명")
        index = table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
        for idx in index:
            # print(idx)
            # print(table_02.loc[idx][0])
            print(idx, table_02.loc[idx][0])

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    def print_name(self, name):
        print(name)
    

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay02.py--------------------

from operator import index
import sys
from unittest import result
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QAbstractScrollArea
from PyQt5.QtCore import Qt
from PyQt5 import QtGui
import pandas as pd # pip install pandas


class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 300, 400
        # self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        layout_v = QVBoxLayout()

        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        self.table = QTableWidget()
        layout_v.addWidget(self.table)

        layout_main.addLayout(layout_v)

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        table_df = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.table_02 = table_df.set_index("성명")
        index = self.table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
        for idx in index:
            # print(idx)
            # print(table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.print_name(idx)

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    def print_name(self, name):
        print(name, self.table_02.loc[name][0])
    

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay03.py--------------------

from operator import index
from unittest import result
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
from PyQt5 import QtGui
import sys
import pandas as pd # pip install pandas
import os


class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        layout_main.addLayout(layout_2)


        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        index = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
        for idx in index:
            # print(idx)
            # print(table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.print_name(idx)

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    def print_name(self, name):
        print(name, self.df_table_02.loc[name][0])

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay04.py--------------------

from operator import index
from unittest import result
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
from PyQt5 import QtGui
import sys
import pandas as pd # pip install pandas
import os

import smtplib
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email import encoders
import io
import secret


class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        self.btn_send = QPushButton("보내기")
        layout_2.addWidget(self.btn_send, 7, 2, 1, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

    
        # html 파일이 존재하면
        filename = 'html0.txt'
        if os.path.isfile(filename) == True:
            html_text = open(filename,'r', encoding="UTF-8").read()
            self.textedit22.setText(html_text)

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    def fn_test(self):
        for idx in self.idx_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_print_name(idx, self.df_table_02.loc[idx][0])

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    
    def fn_print_name(self, name, email):
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit22.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit22.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br><br>'
        pretext += f'당신의 이메일은 {email} 입니다.'
        contents = self.textedit22.toPlainText()
        nexttext = f'<img src="cid:{attachment}" >'
        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            file.write(pretext)
            file.write(contents)
            file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')

    def fn_html_save(self):
        if len(self.textedit22.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit22.toPlainText()
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_send(self, name, email_i):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        msg_from = 'miero@kakao.com'
        msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        cc = ''

        # 메세지 구성
        msg = MIMEBase('multipart','mixed')
        msg['Subject'] = f'{name} 님 - 2022년 08월 급여명세서'
        msg['From'] = msg_from
        msg['To'] = msg_to
        msg['Cc'] = cc

        logo_file = r"C:\Users\newstep\Pictures\number1.jpg"

        img_f_name = logo_file.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        with io.open(img_f_name,'r') as f:
            # emailtext = f.read()
            emailtext = img_f_name
            
        # attachment = 'attachment_file_name.png'
        attachment = img_f_name

        fp = open(attachment, 'rb')
        img = MIMEImage(fp.read())
        fp.close()
        img.add_header('Content-ID', '<{}>'.format(attachment))
        msg.attach(img)

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        contents = self.textedit22.toPlainText()
        msgText = MIMEText(contents, 'html')


        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())
        server.sendmail(msg_from, msg_to, msg.as_string())
        server.quit()

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay05.py--------------------

from operator import index
from unittest import result
from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os

import smtplib
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email import encoders
import io
import secret



class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        self.textedit22.setAcceptRichText(False)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        self.btn_send = QPushButton("보내기")
        layout_2.addWidget(self.btn_send, 7, 2, 1, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

    
        # html 파일이 존재하면
        filename = 'html0.txt'
        if os.path.isfile(filename) == True:
            source_text = open(filename,'r', encoding="UTF-8").read()
            # self.textedit22.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit22.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    
    

    def fn_html_save(self):
        if len(self.textedit22.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit22.toPlainText()

            # document = self.textedit22.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit22.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit22.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit22.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"

        # document = self.textedit22.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_send(self, name, email_i):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        msg_from = 'miero@kakao.com'
        msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        cc = ''
        # print(name,email_i)

        # 메세지 구성
        msg = MIMEBase('multipart','mixed')
        msg['Subject'] = f'{name} 님 - 2022년 08월 급여명세서'
        msg['From'] = msg_from
        msg['To'] = msg_to
        msg['Cc'] = cc

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        pretext = f'수신: {name}<br/>'
        pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        pretext += '<br/>'
        msgpreText = MIMEText(pretext, 'html')
        msg.attach(msgpreText)
        contents = self.textedit22.toPlainText()
        msgText = MIMEText(contents, 'html')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # # 메일 서버를 이용하여 메일을 발송합니다.
        # # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())
        server.sendmail(msg_from, msg_to, msg.as_string())
        server.quit()

    def fn_test(self):
        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx in self.idx_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_send(idx, self.df_table_02.loc[idx][0])
            self.fn_print_name(idx, self.df_table_02.loc[idx][0])
            # self.fn_send(idx, self.df_table_02.loc[idx][0])

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay06.py--------------------

from operator import index
from unittest import result
from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os

import smtplib
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email import encoders
from email.encoders import encode_base64
import io
import secret



class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        self.textedit22.setAcceptRichText(False)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        self.btn_send = QPushButton("보내기")
        layout_2.addWidget(self.btn_send, 7, 2, 1, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

    
        # html 파일이 존재하면
        filename = 'html0.txt'
        if os.path.isfile(filename) == True:
            source_text = open(filename,'r', encoding="UTF-8").read()
            # self.textedit22.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit22.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    
    

    def fn_html_save(self):
        if len(self.textedit22.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit22.toPlainText()

            # document = self.textedit22.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit22.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit22.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit22.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"

        # document = self.textedit22.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_send(self, name, email_i):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        msg_from = 'miero@kakao.com'
        msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        cc = ''
        # print(name,email_i)

        # 메세지 구성
        msg = MIMEBase('multipart','mixed')
        msg['Subject'] = f'{name} 님 - 2022년 08월 급여명세서'
        msg['From'] = msg_from
        msg['To'] = msg_to
        msg['Cc'] = cc

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        pretext = f'수신: {name}<br/>'
        pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        pretext += '<br/>'
        msgpreText = MIMEText(pretext, 'html')
        msg.attach(msgpreText)
        contents = self.textedit22.toPlainText()
        msgText = MIMEText(contents, 'html')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일 첨부
        files = list()
        for i in range(self.listwidget21.count()):
            files.append(self.listwidget21.item(i).text())
        for f in files:
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encode_base64(part)
            part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            msg.attach(part)

        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        server.sendmail(msg_from, msg_to, msg.as_string())
        server.quit()

    def fn_test(self):
        self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        # for idx in self.idx_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])

            # self.fn_send(idx, self.df_table_02.loc[idx][0])
            # self.fn_print_name(idx, self.df_table_02.loc[idx][0])

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay07.py--------------------

from operator import index
from unittest import result
from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os

import smtplib
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email import encoders
from email.encoders import encode_base64
import io
import secret



class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        self.textedit22.setAcceptRichText(False)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        self.btn_send = QPushButton("보내기")
        layout_2.addWidget(self.btn_send, 7, 2, 1, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

    
        # html 파일이 존재하면
        filename = 'html0.txt'
        if os.path.isfile(filename) == True:
            source_text = open(filename,'r', encoding="UTF-8").read()
            # self.textedit22.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit22.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    
    

    def fn_html_save(self):
        if len(self.textedit22.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit22.toPlainText()

            # document = self.textedit22.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit22.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit22.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit22.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"

        # document = self.textedit22.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_send(self, name, email_i):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        msg_from = 'miero@kakao.com'
        msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        cc = ''
        # print(name,email_i)

        # 메세지 구성
        msg = MIMEBase('multipart','mixed')
        msg['Subject'] = f'2022년 08월 급여명세서-{name}'
        msg['From'] = msg_from
        msg['To'] = msg_to
        msg['Cc'] = cc

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        contents = self.textedit22.toPlainText()
        con_before = self.textedit22.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = con_before.replace("{성명}",f'수신: {name}')
        msgText = MIMEText(contents, 'html')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일 첨부
        files = list()
        for i in range(self.listwidget21.count()):
            files.append(self.listwidget21.item(i).text())
        for f in files:
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encode_base64(part)
            part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            msg.attach(part)

        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        server.sendmail(msg_from, msg_to, msg.as_string())
        server.quit()

    def fn_test(self):
        self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        # for idx in self.idx_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])

            # self.fn_send(idx, self.df_table_02.loc[idx][0])
            # self.fn_print_name(idx, self.df_table_02.loc[idx][0])

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay08.py--------------------

from operator import index
from unittest import result
from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os

import smtplib
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email import encoders
from email.encoders import encode_base64
import io
import secret



class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        self.textedit22.setAcceptRichText(False)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        self.btn_send = QPushButton("보내기")
        layout_2.addWidget(self.btn_send, 7, 2, 1, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

    
        # html 파일이 존재하면
        filename = 'html0.txt'
        if os.path.isfile(filename) == True:
            source_text = open(filename,'r', encoding="UTF-8").read()
            # self.textedit22.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit22.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 300)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    
    

    def fn_html_save(self):
        if len(self.textedit22.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit22.toPlainText()

            # document = self.textedit22.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit22.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit22.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit22.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"

        # document = self.textedit22.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        msg = MIMEBase('multipart','mixed')
        msg['Subject'] = f'2022년 08월 급여명세서-{name}'
        msg['From'] = 'miero@kakao.com'
        msg['To'] = email_i
        msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        contents = self.textedit22.toPlainText()
        con_before = self.textedit22.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = con_before.replace("{성명}",f'수신: {name}')
        msgText = MIMEText(contents, 'html')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일 첨부
        files = list()
        for i in range(self.listwidget21.count()):
            files.append(self.listwidget21.item(i).text())
        for f in files:
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encode_base64(part)
            part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            msg.attach(part)

        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_test(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        msg_from = 'miero@kakao.com'

        self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        self.server.quit()
        self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        msg_from = 'miero@kakao.com'

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx in self.idx_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])

            self.fn_send(idx, self.df_table_02.loc[idx][0])
            self.fn_print_name(idx, self.df_table_02.loc[idx][0])
        self.server.quit()
if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay09.py--------------------

from operator import index
from unittest import result
from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os

import smtplib
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64
import io
import secret
import xlwings as xw


class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)
        self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        self.textedit22.setAcceptRichText(False)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        self.btn_send = QPushButton("보내기")
        layout_2.addWidget(self.btn_send, 7, 2, 1, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

    
        # html 파일이 존재하면
        filename = 'html0.txt'
        if os.path.isfile(filename) == True:
            source_text = open(filename,'r', encoding="UTF-8").read()
            # self.textedit22.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit22.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    
    

    def fn_html_save(self):
        if len(self.textedit22.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit22.toPlainText()

            # document = self.textedit22.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit22.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit22.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit22.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"

        # document = self.textedit22.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        pdf_file = os.path.join(self.current_pdf_path, f"2022년08월급여명세서-{name}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        msg = MIMEBase('multipart','mixed')
        msg['Subject'] = f'2022년08월급여명세서-{name}'
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        contents = self.textedit22.toPlainText()
        con_before = self.textedit22.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = con_before.replace("{성명}",f'수신: {name}')
        msgText = MIMEText(contents, 'html')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"2022년08월급여명세서-{name}.pdf"
        pdf_file = os.path.join(self.current_pdf_path, pdf_file_name)
        if os.path.isfile(pdf_file):
            binary_pdf = open(pdf_file, 'rb')
            payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
            payload.set_payload((binary_pdf).read())
            encoders.encode_base64(payload)
            payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
            msg.attach(payload)
        for i in range(self.listwidget21.count()):
            files.append(self.listwidget21.item(i).text())
        # print(files)
        for f in files:
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encode_base64(part)
            part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_test(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.fn_pdf_create("밀1")
        self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        self.server.quit()
        self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_pdf_create(idx_name)
            self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
            self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay10.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(310)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('첨부파일')
        layout_2.addWidget(label21, 0, 0, 1, 1)
        
        self.listwidget21 = QListWidget()
        self.listwidget21.setFixedWidth(300)
        self.listwidget21.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget21, 1, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 1, 2, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 2, 2, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 6, 0, 1, 1)

        self.textedit22 = QTextEdit()
        self.textedit22.setFixedWidth(300)
        self.textedit22.setAcceptRichText(False)
        layout_2.addWidget(self.textedit22, 7, 0, 5, 1)

        self.btn_send = QPushButton("보내기")
        layout_2.addWidget(self.btn_send, 7, 2, 1, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

    
        # html 파일이 존재하면
        filename = 'html0.txt'
        if os.path.isfile(filename) == True:
            source_text = open(filename,'r', encoding="UTF-8").read()
            # self.textedit22.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit22.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget21.count()):
                    # if str(self.listwidget21.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget21.item(i).text()) == txtPath:
                    if str(self.listwidget21.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget21.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget21.count() +1
                    
                    # self.listwidget21.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget21.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget21.insertItem(cnt, f_path)
                        # self.listwidget21.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget21.currentRow()
        self.listwidget21.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget21.clearSelection()
    
    

    def fn_html_save(self):
        if len(self.textedit22.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit22.toPlainText()

            # document = self.textedit22.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit22.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit22.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit22.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit22.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return 'Remove All File'
        else:
            return 'Directory Not Found'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        pdf_file = os.path.join(self.current_pdf_path, f"2022년08월급여명세서-{name}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)
        self.msg_from = 'miero@kakao.com'
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        

        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'
        msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit22.toPlainText()
        contents_before = self.textedit22.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"2022년08월급여명세서-{name}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget21.count()):
            files.append(self.listwidget21.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_test(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create("정용만")
        # self.fn_pdf_create("정용만")
        self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
        self.server.quit()
        self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'

        # self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
        # self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
            self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay11.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        label21 = QLabel('본문 (HTML형식)')
        layout_2.addWidget(label21, 8, 0)

        self.textedit23 = QTextEdit()
        self.textedit23.setFixedWidth(400)
        self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 9, 0, 3, 1)

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 9, 1, 3, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
        else:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        if len(self.lineedit21.text())==0:
            book.save()
            app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)
        self.msg_from = 'miero@kakao.com'
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"2022년08월급여명세서-{name}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_test(self):
        try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'

            self.fn_subject_save()
            self.fn_html_save()
            
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create("정용만")
            # self.fn_pdf_create("정용만")
            self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
            self.server.quit()
            self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])
        except:    # 예외가 발생했을 때 실행됨
            print('예외가 발생했습니다.')

    def fn_run(self):
        try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'

            self.fn_subject_save()
            self.fn_html_save()

            # self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
            # self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])
            for idx_name in self.idx_name_list:
                # print(idx)
                # print(self.df_table_02.loc[idx][0])
                # print(idx, table_02.loc[idx][0])
                self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
                self.fn_pdf_create(idx_name)
                self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
            self.server.quit()
        except:    # 예외가 발생했을 때 실행됨
            print('예외가 발생했습니다.')

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay12.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 800, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 8, 0)

        hBox = QHBoxLayout()
        groupBoxBottom.setLayout(hBox)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 10, 0, 3, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 10, 0, 3, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 10, 1, 3, 1)
        self.btn_send.clicked.connect(self.fn_test) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
        else:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name,email,"전송완료")

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        if len(self.lineedit21.text())==0:
            book.save()
            app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)
        self.msg_from = 'miero@kakao.com'
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_test(self):
        try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'

            self.fn_subject_save()
            self.fn_html_save()
            
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create("정용만")
            # self.fn_pdf_create("정용만")
            self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
            self.server.quit()
            self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])
        except:    # 예외가 발생했을 때 실행됨
            print('예외가 발생했습니다.')

    def fn_run(self):
        try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'

            self.fn_subject_save()
            self.fn_html_save()

            # self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
            # self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])
            for idx_name in self.idx_name_list:
                # print(idx)
                # print(self.df_table_02.loc[idx][0])
                # print(idx, table_02.loc[idx][0])
                self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
                self.fn_pdf_create(idx_name)
                self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
            self.server.quit()
        except:    # 예외가 발생했을 때 실행됨
            print('예외가 발생했습니다.')

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay13.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1100, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 8, 0)

        hBox = QHBoxLayout()
        groupBoxBottom.setLayout(hBox)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 10, 0, 3, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 10, 0, 3, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 10, 1, 3, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
        else:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        if len(self.lineedit21.text())==0:
            book.save()
            app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)
        self.msg_from = 'miero@kakao.com'
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_result_window(self, name, email):
        self.listwidget32.clear()
        cnt = self.listwidget32.count() +1
        # print(cnt, name, email, "전송완료")
        # self.listwidget32.insertItem(cnt,name&email&"전송완료")
        self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
            self.listwidget32.clear()
            cnt = 0
            
            self.fn_subject_save()
            self.fn_html_save()
            
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create("정용만")
            # self.fn_pdf_create("정용만")
            self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
            self.server.quit()
            self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])
        except:    # 예외가 발생했을 때 실행됨
            print('예외가 발생했습니다.')
            self.listwidget32.insertItem(cnt, f'{"정용만"}-{self.df_table_02.loc["정용만"][0]}-실패')

    def fn_run(self):
        try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
            self.listwidget32.clear()
            cnt = 0

            self.fn_subject_save()
            self.fn_html_save()

            # self.fn_send("정용만", self.df_table_02.loc["정용만"][0])
            # self.fn_print_name("정용만", self.df_table_02.loc["정용만"][0])
            for idx_name in self.idx_name_list:
                # print(idx)
                # print(self.df_table_02.loc[idx][0])
                # print(idx, table_02.loc[idx][0])
                self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
                self.fn_pdf_create(idx_name)
                self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
                self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
            self.server.quit()
        except:    # 예외가 발생했을 때 실행됨
            print('예외가 발생했습니다.')
            self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay14.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 8, 0)

        hBox = QHBoxLayout()
        groupBoxBottom.setLayout(hBox)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 10, 0, 3, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 10, 0, 3, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 10, 1, 3, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        if len(self.lineedit21.text())==0:
            book.save()
            app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)
        self.msg_from = 'miero@kakao.com'
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_result_window(self, name, email):
        self.listwidget32.clear()
        cnt = self.listwidget32.count() +1
        # print(cnt, name, email, "전송완료")
        # self.listwidget32.insertItem(cnt,name&email&"전송완료")
        self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create("밀1")
        # self.fn_pdf_create("밀1")
        self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        self.server.quit()
        self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
            

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay15.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()
        label11 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label11)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 5, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxBottom1 = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxBottom1, 8, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxBottom1.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("밀1")
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom2 = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom2, 9, 0)

        hBox2 = QHBoxLayout()
        groupBoxBottom2.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 11, 0, 3, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 11, 0, 3, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 11, 1, 3, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        if len(self.lineedit21.text())==0:
            book.save()
            app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        self.server.login(secret.kakao_id, secret.kakao_pw)
        self.msg_from = 'miero@kakao.com'
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_result_window(self, name, email):
        self.listwidget32.clear()
        cnt = self.listwidget32.count() +1
        # print(cnt, name, email, "전송완료")
        # self.listwidget32.insertItem(cnt,name&email&"전송완료")
        self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
            

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay16.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("밀1")
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        if len(self.lineedit21.text())==0:
            book.save()
            app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_result_window(self, name, email):
        self.listwidget32.clear()
        cnt = self.listwidget32.count() +1
        # print(cnt, name, email, "전송완료")
        # self.listwidget32.insertItem(cnt,name&email&"전송완료")
        self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        self.listwidget32.clear()
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return

        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
            

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay17.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("밀1")
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra

        if len(self.lineedit21.text())==0:
            book.save()
            app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_result_window(self, name, email):
        self.listwidget32.clear()
        cnt = self.listwidget32.count() +1
        # print(cnt, name, email, "전송완료")
        # self.listwidget32.insertItem(cnt,name&email&"전송완료")
        self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
            

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay18.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("정용만") # 이름기본값
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra
        
        # rng = sh_paystub.range("D35")
        # sh_paystub.pictures.add(r'B:\python\vscode\excel\millstudio_logo.png', left=rng.left, top=rng.top)

        if len(self.lineedit21.text())==0:
            book.save()
            # app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        # app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    def fn_result_window(self, name, email):
        self.listwidget32.clear()
        cnt = self.listwidget32.count() +1
        # print(cnt, name, email, "전송완료")
        # self.listwidget32.insertItem(cnt,name&email&"전송완료")
        self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
            

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay19.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("정용만") # 이름기본값
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra
        
        # rng = sh_paystub.range("D35")
        # sh_paystub.pictures.add(r'B:\python\vscode\excel\millstudio_logo.png', left=rng.left, top=rng.top)

        if len(self.lineedit21.text())==0:
            book.save()
            # app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        # app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    # def fn_result_window(self, name, email):
    #     self.listwidget32.clear()
    #     cnt = self.listwidget32.count() +1
    #     # print(cnt, name, email, "전송완료")
    #     # self.listwidget32.insertItem(cnt,name&email&"전송완료")
    #     self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
                self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            # self.listwidget32.item(cnt-1).setBackground(Qt.red)
            self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
            

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay20.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("정용만") # 이름기본값
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        self.app = xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra
        
        # rng = sh_paystub.range("D35")
        # sh_paystub.pictures.add(r'B:\python\vscode\excel\millstudio_logo.png', left=rng.left, top=rng.top)

        if len(self.lineedit21.text())==0:
            book.save()
            # app.kill()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        # app.kill()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    # def fn_result_window(self, name, email):
    #     self.listwidget32.clear()
    #     cnt = self.listwidget32.count() +1
    #     # print(cnt, name, email, "전송완료")
    #     # self.listwidget32.insertItem(cnt,name&email&"전송완료")
    #     self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
                self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            # self.listwidget32.item(cnt-1).setBackground(Qt.red)
            self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        self.app.kill()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
        self.app.kill()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
        

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay21.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("정용만") # 이름기본값
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        df_table = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    def loadExcelData(self, excel_file_dir, worksheet_name):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        fname = "급여1.xlsx"
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name='4대급여')
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra
        
        # rng = sh_paystub.range("D35")
        # sh_paystub.pictures.add(r'B:\python\vscode\excel\millstudio_logo.png', left=rng.left, top=rng.top)

        if len(self.lineedit21.text())==0:
            book.save()
            book.close()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        book.close()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    # def fn_result_window(self, name, email):
    #     self.listwidget32.clear()
    #     cnt = self.listwidget32.count() +1
    #     # print(cnt, name, email, "전송완료")
    #     # self.listwidget32.insertItem(cnt,name&email&"전송완료")
    #     self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
                self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            # self.listwidget32.item(cnt-1).setBackground(Qt.red)
            self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.app.kill()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
        # self.app.kill()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
        

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay22.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("정용만") # 이름기본값
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        # df_table = self.loadExcelData(excel_file_path, worksheet_name)
        df_table = self.loadExcelData()
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    # def loadExcelData(self, excel_file_dir, worksheet_name):
    def loadExcelData(self):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        # fname = "급여1.xlsx"
        fname = excel_file_path
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name=worksheet_name)
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra
        
        # rng = sh_paystub.range("D35")
        # sh_paystub.pictures.add(r'B:\python\vscode\excel\millstudio_logo.png', left=rng.left, top=rng.top)

        if len(self.lineedit21.text())==0:
            book.save()
            book.close()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        book.close()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    # def fn_result_window(self, name, email):
    #     self.listwidget32.clear()
    #     cnt = self.listwidget32.count() +1
    #     # print(cnt, name, email, "전송완료")
    #     # self.listwidget32.insertItem(cnt,name&email&"전송완료")
    #     self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        self.fn_pdf_create(self.lineedit23.text())

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
                self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            # self.listwidget32.item(cnt-1).setBackground(Qt.red)
            self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.app.kill()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')
        QMessageBox.about(self,'전송 완료','전송 작업 완료')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
        # self.app.kill()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
        QMessageBox.about(self,'전송 완료','전송 작업 완료')
        

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay23.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("정용만") # 이름기본값
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        # df_table = self.loadExcelData(excel_file_path, worksheet_name)
        df_table = self.loadExcelData()
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    # def loadExcelData(self, excel_file_dir, worksheet_name):
    def loadExcelData(self):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        # fname = "급여1.xlsx"
        fname = excel_file_path
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name=worksheet_name)
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        
            
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra
        
        # rng = sh_paystub.range("D35")
        # sh_paystub.pictures.add(r'B:\python\vscode\excel\millstudio_logo.png', left=rng.left, top=rng.top)

        if len(self.lineedit21.text())==0:
            book.save()
            book.close()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        book.close()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f'수신: {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    # def fn_result_window(self, name, email):
    #     self.listwidget32.clear()
    #     cnt = self.listwidget32.count() +1
    #     # print(cnt, name, email, "전송완료")
    #     # self.listwidget32.insertItem(cnt,name&email&"전송완료")
    #     self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        try:
            self.fn_pdf_create(self.lineedit23.text())
        except:
            QMessageBox.about(self,'대상자 확인','유효하지 않은 이름입니다.')
            return

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
                self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            # self.listwidget32.item(cnt-1).setBackground(Qt.red)
            self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.app.kill()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')
        QMessageBox.about(self,'전송 완료','전송 작업 완료')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.addItem(f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.addItem(f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
        # self.app.kill()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
        QMessageBox.about(self,'전송 완료','전송 작업 완료')
        

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay24.py--------------------

# from operator import index
# from unittest import result
# from xml.dom.minidom import Document
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QPushButton, \
    QHeaderView, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractScrollArea, QLabel, QListWidget, \
    QFileDialog, QMessageBox, QTextEdit, QAbstractItemView, QLineEdit, QGroupBox, QRadioButton
from PyQt5.QtCore import Qt
# from PyQt5 import QtGui
from PyQt5.QtGui import QTextCursor
import sys
import pandas as pd # pip install pandas
import os
import io
import secret
import xlwings as xw
import re

import smtplib
from email.encoders import encode_base64
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # 메일의 본문 내용을 만드는 모듈 내용을 base64형식으로 변환
from email.utils import formatdate

from email.mime.application import MIMEApplication # 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.image import MIMEImage # 메일의 이미지 파일을 base64 형식으로 변환
# 출처: https://nowonbun.tistory.com/684 [명월 일지:티스토리]
from email import encoders
from email.encoders import encode_base64

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1150, 600
        self.resize(self.window_width, self.window_height)
        self.setWindowTitle('pay')

        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        # self.msg_from = 'miero@kakao.com'

        ##### layout_main #####
        layout_main = QHBoxLayout()
        self.setLayout(layout_main)

        ##### layout_1 #####
        layout_1 = QVBoxLayout()

        vBox11 = QVBoxLayout()
        groupBoxLogin11 = QGroupBox("로그인 정보")
        groupBoxLogin11.setLayout(vBox11)
        layout_1.addWidget(groupBoxLogin11)

        self.label11 = QLabel("이메일계정")
        vBox11.addWidget(self.label11)
        self.lineedit11 = QLineEdit("miero@kakao.com")
        vBox11.addWidget(self.lineedit11)

        self.label12 = QLabel("패스워드")
        vBox11.addWidget(self.label12)
        self.lineedit12 = QLineEdit()
        self.lineedit12.setEchoMode(QLineEdit.Password)
        # self.lineedit12.show()
        vBox11.addWidget(self.lineedit12)

        label13 = QLabel('직원 메일')
        # self.button = QPushButton('&Load Data')
        # self.button.clicked.connect(lambda _, xl_path=excel_file_path, sheet_name=worksheet_name: self.loadExcelData(xl_path, sheet_name))
        # layout_v.addWidget(self.button)
        layout_1.addWidget(label13)
        
        self.table = QTableWidget()
        self.table.setFixedWidth(350)
        layout_1.addWidget(self.table)

        layout_main.addLayout(layout_1)

        ##### layout_2 #####
        layout_2 = QGridLayout()

        label21 = QLabel('이메일 제목')
        layout_2.addWidget(label21, 0, 0)
        self.lineedit21 = QLineEdit()
        layout_2.addWidget(self.lineedit21, 1, 0, 1, 2)

        label22 = QLabel('첨부파일')
        layout_2.addWidget(label22, 2, 0)
        
        self.listwidget22 = QListWidget()
        # self.listwidget22.setFixedWidth(300)
        self.listwidget22.setMinimumWidth(400)
        # self.listwidget22.setSelectionMode(QAbstractItemView.ExtendedSelection)
        layout_2.addWidget(self.listwidget22, 3, 0, 3, 1)
        
        self.btn_file_add = QPushButton('파일 추가') # sendlist
        # self.btn_file_add.setMinimumHeight(80)
        layout_2.addWidget(self.btn_file_add, 3, 1, 1, 1)
        self.btn_file_add.clicked.connect(self.fn_fileadd)

        self.btn_file_del = QPushButton('파일 삭제')
        layout_2.addWidget(self.btn_file_del, 4, 1, 1, 1)
        self.btn_file_del.clicked.connect(self.fn_filedel)

        # label21 = QLabel('본문 (HTML형식)')
        # layout_2.addWidget(label21, 8, 0)
        # radio_1 ~ 4 버튼을 그룹으로 설정
        groupBoxMid = QGroupBox("테스트 메일 보내기")
        layout_2.addWidget(groupBoxMid, 6, 0, 1, 2)

        hBox1 = QHBoxLayout()
        groupBoxMid.setLayout(hBox1)

        self.label23 = QLabel("성명")
        hBox1.addWidget(self.label23)
        self.lineedit23 = QLineEdit("정용만") # 이름기본값
        hBox1.addWidget(self.lineedit23)
        self.pushbutton23 = QPushButton("테스트 보내기")
        self.pushbutton23.clicked.connect(self.fn_test)
        hBox1.addWidget(self.pushbutton23)

        groupBoxBottom = QGroupBox("본문 보기형식 선택")
        layout_2.addWidget(groupBoxBottom, 7, 0, 1, 2)

        hBox2 = QHBoxLayout()
        groupBoxBottom.setLayout(hBox2)

        self.radioBtn1 = QRadioButton("HTML")
        self.radioBtn1.setChecked(True)
        self.radioBtn1.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn1)

        self.radioBtn2 = QRadioButton("미리보기")
        self.radioBtn2.clicked.connect(self.fn_radio_select)
        hBox2.addWidget(self.radioBtn2)

        self.textedit23 = QTextEdit()
        # self.textedit23.setFixedWidth(400)
        # self.textedit23.setAcceptRichText(False)
        layout_2.addWidget(self.textedit23, 8, 0, 4, 1)
        self.textedit23.show()

        self.textedit24 = QTextEdit()
        layout_2.addWidget(self.textedit24, 8, 0, 4, 1)
        self.textedit24.hide()

        self.btn_send = QPushButton("보내기")
        self.btn_send.setMaximumHeight(500) # gridlayout button size
        layout_2.addWidget(self.btn_send, 8, 1, 4, 1)
        self.btn_send.clicked.connect(self.fn_run) #@@@@@@@@@@@@@@@@@@@@@@@@@@

        layout_main.addLayout(layout_2)

        ##### layout_3 #####
        layout_3 = QVBoxLayout()
        label31 = QLabel('발송 결과')
        layout_3.addWidget(label31)
        self.listwidget32 = QListWidget()
        # self.listwidget32.setMinimumWidth(self.listwidget32.sizeHintForColumn(0))
        # self.listwidget32.setWidth(10)
        layout_3.addWidget(self.listwidget32)
        layout_main.addLayout(layout_3)

        # subject0 파일이 존재하면
        filename_subject = 'subject0.txt'
        if os.path.isfile(filename_subject) == True:
            source_text = open(filename_subject,'r', encoding="UTF-8").read()
            # self.lineedit21.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.lineedit21.setText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

    
        # html0 파일이 존재하면
        filename_html = 'html0.txt'
        if os.path.isfile(filename_html) == True:
            source_text = open(filename_html,'r', encoding="UTF-8").read()
            # self.textedit23.setText(source_text) # TextEdit에 RichText 형식의 글을 입력합니다.
            self.textedit23.setPlainText(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.
            # self.textedit24.setHtml(source_text) # TextEdit에 PlainText 형식의 글을 새로 입력합니다.

        # table_data = self.loadExcelData(excel_file_path, worksheet_name)
        # print(table_data.keys())
        # df_table = self.loadExcelData(excel_file_path, worksheet_name)
        df_table = self.loadExcelData()
        # print(table_df["성명"][0])

        self.df_table_02 = df_table.set_index("성명")
        self.idx_name_list = self.df_table_02.index.to_list()
        # print(index)
        # for i in table_02["이메일"]:
    
    def fn_radio_select(self):
        # QRadioButton1 클릭 여부 표시
        if self.radioBtn1.isChecked():
            self.textedit23.show()
            self.textedit24.hide()
            self.hidden = False
            # self.btn_send.setText('보내기')
        elif self.radioBtn2.isChecked():
            self.fn_html_save()
            self.textedit23.hide()
            self.textedit24.show()
            self.hidden = True
            # self.btn_send.setText('미리보기')
    

    # def loadExcelData(self, excel_file_dir, worksheet_name):
    def loadExcelData(self):
        df_work_sheet = pd.read_excel(excel_file_path, worksheet_name)
        df_email_sheet = pd.read_excel(excel_file_path, emailsheet_name)
        df_table = df_work_sheet.merge(df_email_sheet, how='left')
        df_table_mail = df_table[['성명','이메일']]
        res = df_table_mail
        # res = df_table_mail.to_dict()

        # self.table.setRowCount(df.shape[0])
        # self.table.setColumnCount(df.shape[1])
        self.table.setRowCount(df_table_mail.shape[0])
        self.table.setColumnCount(df_table_mail.shape[1])
        # self.table.setHorizontalHeaderLabels(df.columns)
        self.table.setHorizontalHeaderLabels(df_table_mail.columns)
        header = self.table.horizontalHeader()
        # header.setSectionResizeMode(0, QHeaderView.Stretch)
        # header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # returns pandas array object
        # for row in df.iterrows():
        for row in df_table_mail.iterrows():
            values = row[1]
            for col_index, value in enumerate(values):
                if isinstance(value, (float, int)):
                    value = '{0:0,.0f}'.format(value)
                tableItem = QTableWidgetItem(str(value))
                if value == "nan":
                    tableItem.setBackground(Qt.red)
                self.table.setItem(row[0], col_index, tableItem)

        self.table.setColumnWidth(2, 310)
        return res

    def fn_fileadd(self):
        # f_name = QFileDialog.getOpenFileName(None, '열기', '', "All Files(*.*)")
        f_names = QFileDialog.getOpenFileNames(None, '열기', '', "All Files(*.*)")
        # txtPath = f_name[0]
        filelist = f_names[0]
        if filelist:
            for f_name in filelist:
                # ext = os.path.splitext(f_name[0])[1]
                ext = os.path.splitext(f_name)[1]   # 확장자 확인
        # if f_names[0]:
                icnt = 0
                for i in range(self.listwidget22.count()):
                    # if str(self.listwidget22.item(i).text()) == txtPath.replace("/","\\") or str(self.listwidget22.item(i).text()) == txtPath:
                    if str(self.listwidget22.item(i).text()) == f_name.replace("/","\\") or str(self.listwidget22.item(i).text()) == f_name:
                        icnt = 1
                        break
                if icnt != 1:
                    f_path = f_name
                    if ext == '.exe':
                        QMessageBox.about(self,'파일 형식 확인','exe파일은 전송할 수 없습니다.')
                        pass
                    
                    else:
                        cnt = self.listwidget22.count() +1
                    
                    # self.listwidget22.insertItem(cnt, os.path.basename(txtPath))
                    # self.listwidget22.insertItem(cnt, txtPath)
                    # f_path = f_names[0]
                        # print(f_path)
                        self.listwidget22.insertItem(cnt, f_path)
                        # self.listwidget22.insertItem(cnt, f_path.replace("/","\\"))

    def fn_filedel(self):
        currentrow = self.listwidget22.currentRow()
        self.listwidget22.takeItem(currentrow) # currentrowRight번째 항목을 삭제합니다
        self.listwidget22.clearSelection()
    
    def fn_subject_save(self):
        if len(self.lineedit21.text())>0:
            S__File = os.path.join(os.getcwd(),'subject0.txt')
            Text = self.lineedit21.text()
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)

    def fn_html_save(self):
        if len(self.textedit23.toPlainText())>0:
            # print(len(self.textedit.toPlainText()))
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','/', "Text Files (*.txt)")
            # S__File = QFileDialog.getSaveFileName(None,'문자 파일저장','html0', "Text Files (*.txt)")
            S__File = os.path.join(os.getcwd(),'html0.txt')
        
            # This will let you access the test in your QTextEdit
            Text = self.textedit23.toPlainText()
            self.textedit24.setHtml(Text)

            # document = self.textedit23.document()
            # cursor = QTextCursor(document)

            # p1 = cursor.position() # return int
            # cursor.insertImage(r"C:\Users\newstep\Pictures\number1.jpg")
            
            # This will prevent you from an error if pressed cancel on file dialog.
            # if S__File[0]: 
            if S__File: 
                # Finally this will Save your file to the path selected.
                # with open(S__File[0], 'w', encoding='utf-8') as file:
                with open(S__File, 'w', encoding='utf-8') as file:
                    file.write(Text)
            
            # txtPath = S__File[0]
            # icnt = 0
            # for i in range(self.listwidget31.count()):
            #     if str(self.listwidget31.item(i).text()) == txtPath.replace("/","\\"):
            #         icnt = 1
            #         break
            # if icnt != 1:
            #     cnt = self.listwidget31.count() +1
                # self.listwidget31.insertItem(cnt, os.path.basename(txtPath))
                # self.listwidget31.insertItem(cnt, txtPath)
        else:
            QMessageBox.about(self,'메시지 입력 확인','메시지를 입력하셔야 합니다.')

    def fn_print_name(self, name, email):
        # f_name_pre = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='50%' height='0%'></center>"
        # f_name = f_name_pre.replace("\\","/")

        # with io.open('file_name.html','r') as f:
        # with io.open(f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = f_name
        
        # attachment = 'attachment_file_name.png'
        # attachment = f_name
        # print(name, self.df_table_02.loc[name][0])
        # print(name, email)
        if len(self.textedit23.toPlainText())>0:
            S__File = os.path.join(os.getcwd(),'html0.txt')
        # print(len(self.textedit23.toPlainText()))
        # print(S__File)
        # pretext = f'{name} 님<br/><br/>'
        # pretext += f'당신의 이메일은 {email} 입니다.<br/>'
        contents = self.textedit23.toPlainText()
        # nexttext = f'<br/><img src="cid:{attachment}" >'
        # nexttext = f"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' id='millstudio logo' width='10%' height='0%'></center>"

        # document = self.textedit23.document()
        # cursor = QTextCursor(document)

        # p1 = cursor.position() # return int
        # cursor.insertImage(f_name)

        # with open(S__File[0], 'w', encoding='utf-8') as file:
        with open(S__File, 'w', encoding='utf-8') as file:
            # file.write(pretext)
            file.write(contents)
            # file.write(nexttext)
        # print(contents)
        # msgText = MIMEText(contents, 'html')
        print(name, email, "전송완료")
        # self.fn_result_window(name, email)

    def fn_DeleteAllFiles(self,filePath):
        if os.path.exists(filePath):
            for file in os.scandir(filePath):
                os.remove(file.path)
            return '폴더 내 모든 파일 지우기 완료'
        else:
            return '폴더가 없습니다.'

    def fn_pdf_create(self, idx_name):
        #####################################
        # xlwings
        self.current_pdf_path = os.getcwd()+"\pdf"
        # fname = "급여1.xlsx"
        fname = excel_file_path
        # for n in self.target_name_list:
        name = idx_name

        df_data = pd.read_excel(fname, sheet_name=worksheet_name)
        df_data = df_data.fillna(0)
        df_employee = pd.read_excel(fname, sheet_name='직원현황')

        idx_df_data = df_data.index[df_data['성명']==name].tolist()[0]
        
            
        # print("4대급여 인덱스 :",idx_df_data)
        normal = df_data.loc[idx_df_data,['기본급']][0]
        overtime = df_data.loc[idx_df_data,['연장수당']][0]
        holiday = df_data.loc[idx_df_data,['휴일수당']][0]
        holidayovertime = df_data.loc[idx_df_data,['휴일연장수당']][0]
        extra = df_data.loc[idx_df_data,['추가수당']][0]
        meals = df_data.loc[idx_df_data,['식대']][0]
        bonus = df_data.loc[idx_df_data,['상여금']][0]

        normal_calculation = df_data.loc[idx_df_data,['기']][0]
        overtime_calculation = df_data.loc[idx_df_data,['연']][0]
        holiday_calculation = df_data.loc[idx_df_data,['휴']][0]
        holidayovertime_calculation = df_data.loc[idx_df_data,['휴연']][0]
        extra_calculation = df_data.loc[idx_df_data,['추']][0]

        nationalpension = df_data.loc[idx_df_data,['국민연금']][0]
        healthinsurance = df_data.loc[idx_df_data,['건강보험']][0]
        longtermcareinsurance = df_data.loc[idx_df_data,['장기요양보험']][0]
        employmentinsurance = df_data.loc[idx_df_data,['고용보험']][0]
        healthsettlement = df_data.loc[idx_df_data,['정산건강']][0]
        longtermcaresettlement = df_data.loc[idx_df_data,['정산장기요양']][0]
        advancepayment = df_data.loc[idx_df_data,['선지급']][0]
        etc = df_data.loc[idx_df_data,['기타']][0]
        incometax = df_data.loc[idx_df_data,['소득세']][0]
        localincometax = df_data.loc[idx_df_data,['지방소득세']][0]

        idx_df_employee = df_employee.index[df_employee['성명']==name].tolist()[0]
        # print("직원현황 인덱스 :",idx_df_employee)
        jobposition = df_employee.loc[idx_df_employee,['직위']][0]
        department = df_employee.loc[idx_df_employee,['부서']][0]
        birthday = df_employee.loc[idx_df_employee,['생일']][0]

        # 엑셀 인스턴스 생성
        xw.App(visible=False)
        book = xw.Book(fname)
        sh_paystub = book.sheets('급여명세서사대보험')

        sh_paystub["B4"].value = name
        sh_paystub["D4"].value = jobposition
        sh_paystub["B5"].value = department
        sh_paystub["D5"].value = birthday

        sh_paystub["B8"].value = normal
        sh_paystub["B9"].value = overtime
        sh_paystub["B10"].value = holiday
        sh_paystub["B11"].value = holidayovertime
        sh_paystub["B12"].value = extra
        sh_paystub["B13"].value = meals

        sh_paystub["D8"].value = nationalpension
        sh_paystub["D9"].value = healthinsurance
        sh_paystub["D10"].value = longtermcareinsurance
        sh_paystub["D11"].value = employmentinsurance
        sh_paystub["D12"].value = healthsettlement
        sh_paystub["D13"].value = longtermcaresettlement
        sh_paystub["D14"].value = advancepayment
        sh_paystub["D15"].value = etc
        sh_paystub["D16"].value = incometax
        sh_paystub["D17"].value = localincometax

        sh_paystub["B23"].value = normal_calculation
        sh_paystub["B24"].value = overtime_calculation
        sh_paystub["B25"].value = holiday_calculation
        sh_paystub["B26"].value = holidayovertime_calculation
        sh_paystub["B27"].value = extra_calculation

        sh_paystub["D23"].value = normal
        sh_paystub["D24"].value = overtime
        sh_paystub["D25"].value = holiday
        sh_paystub["D26"].value = holidayovertime
        sh_paystub["D27"].value = extra
        
        # rng = sh_paystub.range("D35")
        # sh_paystub.pictures.add(r'B:\python\vscode\excel\millstudio_logo.png', left=rng.left, top=rng.top)

        if len(self.lineedit21.text())==0:
            book.save()
            book.close()
            return
        # print(len(self.lineedit21.text()))
        # pdf 로 저장하기
        # current_path = os.getcwd()   # 현재 작업중인 폴더에 저장하기
        # 절대경로로 파일 위치 입력
        subject_before = self.lineedit21.text()
    
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        pdf_file = os.path.join(self.current_pdf_path, f"{subject}.pdf")

        # PDF 로 저장할 시트 선택하기(본 예제에서는 첫 번째 시트 선택하기)
        # report_sheet = book.sheets[1]
        report_sheet = sh_paystub
        # PDF 로 저장하기
        # report_sheet.ExportAsFixedFormat(0, pdf_path)
        report_sheet.api.ExportAsFixedFormat(0, pdf_file)
        book.save()
        book.close()
        #####################################
    
    

    def fn_send(self, name, email_i):
        # # 메일 서버를 설정합니다.
        account_id = self.lineedit11.text().split("@")[0]
        emailserver = self.lineedit11.text().split("@")[1]
        # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
        smtp_server = 'smtp.'+ emailserver
        # print(account_id)
        self.server = smtplib.SMTP_SSL(smtp_server)
        # self.server.login(secret.kakao_id, secret.kakao_pw)
        self.server.login(account_id, self.lineedit12.text())
        # self.msg_from = 'miero@kakao.com'
        self.msg_from = self.lineedit11.text()
        # # server = smtplib.SMTP('smtp.com', port_number)
        # # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        # msg_to = email_i
        # cc = 'b@b.com, c@c.com'
        # cc = ''
        # print(name,email_i)

        # 메세지 구성
        # msg = MIMEBase('multipart','mixed')
        msg = MIMEMultipart()
        
        msg['From'] = self.msg_from
        msg['To'] = email_i
        msg['Date'] = formatdate(localtime=True)
        # msg['Subject'] = f'2022년08월급여명세서-{name}'

        subject_before = self.lineedit21.text()
        # 문자치환 f-string 사용하여 변수 적용하기
        subject = subject_before.replace("{성명}",f'{name}')
        msg['Subject'] = Header(s=subject, charset='utf-8')
        # msg['Subject'] = Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
        # msg['Cc'] = ""

        # 메일 본문 작성
        # # html 로 되어있던 파일을 불러 오고 거기에 파일을 붙여서 보냅니다.
        # msgText = MIMEText('<h1>%s</h1><hr>, <img src="cid:%s" >' % (emailtext, attachment), 'html')
        # contents = '<h1>%s</h1>'
        # contents += '<hr>'
        # contents += '<img src="cid:%s" >'
        # msgText = MIMEText(contents % (emailtext, attachment), 'html')

        # contents = f'<h1>{emailtext}</h1>'
        # contents += f'<h2>{msg_to} 님 메일이 도착했습니다.</h2>'
        # contents += '<hr>'
        # contents += f'<img src="cid:{attachment}" >'
        # msgText = MIMEText(contents, 'html')

        # pretext = f'수신: {name}<br/>'
        # pretext += '발신: 주식회사 밀앤아이, 주식회사 헴펠 대표이사<br/>'
        # pretext += '<br/>'
        # msgpreText = MIMEText(pretext, 'html')
        # msg.attach(msgpreText)
        # contents = self.textedit23.toPlainText()
        contents_before = self.textedit23.toPlainText()
        # 문자치환 f-string 사용하여 변수 적용하기
        contents = contents_before.replace("{성명}",f' {name}')
        # msgText = MIMEText(contents, 'plain')
        msgText = MIMEText(contents, 'html')
        # msgText = MIMEText(contents, _charset='utf-8')

        # 메세지를 메일에 붙여 줍니다.
        msg.attach(msgText)

        # 메일 제일 밑에 로고그림 붙여 줍니다.
        # logo_file = "logo.jpg"
        # img_f_name = logo_file.replace("\\","/")
        # logo_file = r"<center><img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYTHu%2FbtrKqQes3BJ%2F2UVr0HkX0ht7utKmwUqKb0%2Fimg.jpg' width='10%' height='0%'></center>"
        # img_f_name = logo_file.replace("\\","/")

        # # with io.open('file_name.html','r') as f:
        # with io.open(img_f_name,'r') as f:
        #     # emailtext = f.read()
        #     emailtext = img_f_name
            
        # # attachment = 'attachment_file_name.png'
        # attachment = img_f_name
        # attachment = logo_file

        # fp = open(attachment, 'rb')
        # img = MIMEImage(fp.read())
        # fp.close()
        # img.add_header('Content-ID', '<{}>'.format(attachment))
        # msg.attach(img)

        # 파일첨부
        files = list()
        pdf_file_name = f"{subject}.pdf"
        pdf_file = os.path.join(os.getcwd()+"\pdf", pdf_file_name)
        files.append(pdf_file)
        # if os.path.isfile(pdf_file):
        #     binary_pdf = open(pdf_file, 'rb')
        #     payload = MIMEBase('application', 'octate-stream', Name=pdf_file_name)
        #     payload.set_payload((binary_pdf).read())
        #     encoders.encode_base64(payload)
        #     payload.add_header('Content-Decomposition', 'attachment', filename=pdf_file_name)
        #     msg.attach(payload)
        for i in range(self.listwidget22.count()):
            files.append(self.listwidget22.item(i).text())
        # print(files)
        for f in files:
            # print(f)
            part = MIMEBase('application', "octet-stream")
            part.set_payload(open(f,"rb").read())
            encoders.encode_base64(part)
            fstr_name = os.path.basename(f)
            # part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            part.add_header('Content-Disposition',"attachment", filename= fstr_name, charset='utf-8')
            # [출처] 파이썬에서 지메일 보내기(w/첨부파일) 한글파일도 가능|작성자 데이터공방

            # Header(s=f'2022년08월급여명세서-{name}', charset='utf-8')
            msg.attach(part)

        
        
        # 메일 서버를 이용하여 메일을 발송합니다.
        # server.sendmail(msg_from, msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())

        self.server.sendmail(msg['From'], msg['To'], msg.as_string())
        # self.server.quit()

    # def fn_result_window(self, name, email):
    #     self.listwidget32.clear()
    #     cnt = self.listwidget32.count() +1
    #     # print(cnt, name, email, "전송완료")
    #     # self.listwidget32.insertItem(cnt,name&email&"전송완료")
    #     self.listwidget32.insertItem(cnt, f'{name}-{email}-전송완료')

    def fn_test(self):
        # try:
            # 메일 서버를 설정합니다.
            # server = smtplib.SMTP('smtp.com', port_number)
            # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
            # self.server = smtplib.SMTP_SSL('smtp.kakao.com')
            # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

            # 로그인이 필요하면 로그인 설정
            # self.server.login(secret.kakao_id, secret.kakao_pw)
            # 보내는 사람, 받는 사람 설정
            # msg_from = 'miero@kakao.com'
        
        self.listwidget32.clear()
        
        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return


        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()
        
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        try:
            self.fn_pdf_create(self.lineedit23.text())
        except:
            QMessageBox.about(self,'대상자 확인','유효하지 않은 이름입니다.')
            return

        try:
            if self.df_table_02.loc[self.lineedit23.text()][0] != "nan":
                self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
                cnt += 1
                # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
                self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        except:
            cnt += 1
            # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            # self.listwidget32.item(cnt-1).setBackground(Qt.red)
            self.listwidget32.addItem(f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-실패')
            self.listwidget32.item(cnt-1).setBackground(Qt.red)


        # self.fn_pdf_create("밀1")
        # self.fn_send(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # cnt += 1
        # self.listwidget32.insertItem(cnt, f'{self.lineedit23.text()}-{self.df_table_02.loc[self.lineedit23.text()][0]}-전송완료')
        self.server.quit()
        # self.app.kill()
        # self.fn_print_name(self.lineedit23.text(), self.df_table_02.loc[self.lineedit23.text()][0])
        # except:    # 예외가 발생했을 때 실행됨
        #     print('예외가 발생했습니다.')
        #     self.listwidget32.insertItem(cnt, f'{"밀1"}-{self.df_table_02.loc["밀1"][0]}-실패')
        QMessageBox.about(self,'전송 완료','전송 작업 완료')

    def fn_run(self):
        # 메일 서버를 설정합니다.
        # server = smtplib.SMTP('smtp.com', port_number)
        # server = smtplib.SMTP_SSL('smtp.kakao.com:465')
        # server = smtplib.SMTP_SSL('smtp.kakao.com')
        # server = smtplib.SMTP_SSL('smtp.kakao.com', 465)

        # 로그인이 필요하면 로그인 설정
        # server.login(secret.kakao_id, secret.kakao_pw)
        # 보내는 사람, 받는 사람 설정
        # msg_from = 'miero@kakao.com'
        self.listwidget32.clear()

        if len(self.lineedit11.text()) == 0:
            QMessageBox.about(self,'이메일계정 확인','이메일계정을 입력하십시요.')
            return
        
        p = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
        if p.match(self.lineedit11.text()) == None:
            # print(p.match(self.lineedit11.text()) != None)
            QMessageBox.about(self,'이메일 형식 확인','올바른 이메일 형식이 아닙니다.')
            return

        if len(self.lineedit12.text()) == 0:
            QMessageBox.about(self,'비밀번호 확인','비밀번호를 입력하십시요.')
            return
        
        cnt = 0
        if len(self.lineedit21.text()) == 0:
            QMessageBox.about(self,'제목 입력 확인','제목을 입력하십시요.')
            return
        self.fn_subject_save()
        self.fn_html_save()

        # self.fn_send("밀1", self.df_table_02.loc["밀1"][0])
        # self.fn_print_name("밀1", self.df_table_02.loc["밀1"][0])
        self.fn_DeleteAllFiles(os.getcwd()+"\pdf")
        for idx_name in self.idx_name_list:
            # print(idx)
            # print(self.df_table_02.loc[idx][0])
            # print(idx, table_02.loc[idx][0])
            self.fn_pdf_create(idx_name)
            try:
                if self.df_table_02.loc[idx_name][0] != "nan":
                    self.fn_send(idx_name, self.df_table_02.loc[idx_name][0])
                    cnt += 1
                    self.listwidget32.addItem(f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-전송완료')
            except:
                cnt += 1
                self.listwidget32.addItem(f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
                self.listwidget32.item(cnt-1).setBackground(Qt.red)
                # if self.df_table_02.loc[idx_name][0] == "nan":
                #     print(self.listwidget32.)
                #     self.listwidget32.item(cnt).setBackground(Qt.red)
                # print("예외가 발생했습니다.-fn_run",cnt,self.df_table_02.loc[idx_name][0])
            # self.fn_print_name(idx_name, self.df_table_02.loc[idx_name][0])
        self.server.quit()
        # self.app.kill()
            # print('예외가 발생했습니다.')
            # self.listwidget32.insertItem(cnt, f'{idx_name}-{self.df_table_02.loc[idx_name][0]}-실패')
        QMessageBox.about(self,'전송 완료','전송 작업 완료')
        

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    excel_file_path = '급여1.xlsx'
    worksheet_name = '4대급여'
    emailsheet_name = '이메일'

    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 17px;
        }
    ''')
    
    myApp = MyApp()
    myApp.show()

    try:
        sys.exit(app.exec())
    except SystemExit:
        print('Closing Window...')

====================

pay25.py--------------------

'컴퓨터 > Python' 카테고리의 다른 글

python 파일 합치기  (0) 2022.09.14
python selenium chromedriver 다운 안받아도 되네요  (0) 2022.09.08
excel to pdf  (0) 2022.08.28
AI코인 email  (0) 2022.08.28
코딩나우-pandas  (0) 2022.08.27