Dandy Now!
  • [파이썬 신병 교육대] MDB로 부터 Packing List 자동출력 프로그램_4주차_병장_수료식_주특기 시범_최종코드
    2022년 01월 04일 14시 02분 58초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    파이썬 신병 교육대 4주차 수료식

    https://youtu.be/SAEkIRYvxNg

    4주차에는 수료식이 진행되었다.

    수료하기 위해서는 주특기 시범을 해야한다.

    Google Meet에서 발표를 진행하였다.

    1:00:33초 부터 발표(병장 나무명)를 하였다.

     

    앞선 발표에서는 대학원생들의 빅데이터분석과 비주얼라이제이션이 주를 이루었다.

    파이썬이 최근 빅데이터 분야에서 활발하게 이용되고 있음을 체감했고,

    내가 작성한 Windows용 응용프로그램이 오히려 희소하게 여겨졌다!

    그렇지만 현업에서 실무에 적용했다는 점에서 좋은 반응을 얻었다.

     

    특별히 AttributeError로 인해 애먹었던 사연에 대해 김왼손님이 코멘트해주셨다.

    당시에는 미치도록 괴롭혔던 이슈였는데,

    오히려 나를 성장시켜주었다는 생각이든다!


    최종 코드

    import pandas as pd # pip install pandas
    from openpyxl import load_workbook # pip install openpyxl
    from openpyxl.styles import Font, Alignment
    import os
    import win32com.client # pip install pywin32
    import pyodbc # pip install pyodbc
    import xlwt, xlrd # pip install xlwt, pip install xlrd
    import tkinter
    from math import *
    from tkinter import *
    from tkinter import messagebox
    
    def mdb_xls(): # mdb > xls 함수 선언함. 프로그램 실행 중일때 mdb 변경될 경우를 고려하여 OK버튼 클릭시 mdb > xls 과정을 재수행하기 위함(3주차)
        # mdb를 xls로 변환
        MDB = os.getcwd()+'/IDCMAINDB.mdb'
        DRV = '{Microsoft Access Driver (*.mdb, *.accdb)}'
        PWD = ''
    
        # connect to db
        con = pyodbc.connect('DRIVER={};DBQ={};PWD={}'.format(DRV,MDB,PWD))
        cur = con.cursor()
    
        # run a query and get the results 
        SQL = """SELECT * FROM TWEIGHT""" # your query goes here
        rows = cur.execute(SQL).fetchall()
        cur.close()
        con.close()
    
        wb = xlwt.Workbook()
        ws = wb.add_sheet("Weighing Data") # use table name for worksheet name
        cols = ['WDATE', 'SEQ_NO', 'FIELD1', 'NET_WEIGHT', 'TARE_WEIGHT', 'GROSS_WEIGHT', 'UNIT_WEIGHT', 'BARCODE', 'FIELD2', 'FIELD3', 'FIELD4', 'OPERATOR'] # renamed colum headings
        wbRow = 0 # counter for workbook row
        i=0
        while i<12:
            ws.write(wbRow, i, cols[i]) # write column heading to first row
            i+=1
    
        for row in rows:
            wbRow += 1 # increment workbook row counter
            i=0
            while i<12:
                ws.write(wbRow, i, row[i])
                i+=1
    
        # wb.save(os.path.abspath('./pk.xls'))
        wb.save(os.getcwd()+'/pk.xls')
    
    # Packing list 작성
    def pkPrint(lot,company):
        xl = pd.read_excel(os.getcwd()+'/pk.xls')
    
        xl_mask = xl['FIELD2'] == lot
        filtered_xl = xl[xl_mask]
        pack = filtered_xl.groupby(['FIELD2','FIELD3','NET_WEIGHT'])['GROSS_WEIGHT'].agg(['count','sum'])
    
        GROSS_WEIGHT=filtered_xl.groupby(['FIELD2','FIELD3'])['GROSS_WEIGHT'].agg(['count','sum'],as_index=False).mean()
    
        print(GROSS_WEIGHT)
    
        path = os.path.abspath(os.getcwd()+'/pk_result.xlsx')
    
        with pd.ExcelWriter(path) as writer:
            pack.to_excel(writer,'packing', startcol=0,startrow=2)
            GROSS_WEIGHT.to_excel(writer,'packing',header=False, startcol=6,startrow=2)
    
        wb = load_workbook(filename = path, read_only=False, data_only=False)
        ws = wb['packing']
    
        ws.merge_cells('A1:H1')
        ws['A1'] = 'Packing List'
        ws['A2'] = '고객사 : '
        ws['B2'] = company
    
        ca1 = ws['A1']
        ca1.font = Font(size=15, bold=True)
        wb.font = Font(size=15, bold=True)
        ca1.alignment = Alignment(horizontal='center', vertical='center')
    
        wb.save(path)
    
        # xlsx to PDF 
        excel = win32com.client.Dispatch("Excel.Application") # Microsoft Excel 열기
        
        # Excel 파일 읽기
        sheets = excel.Workbooks.Open(os.getcwd()+'/pk_print.xlsm')
        sheets.Worksheets[0]
        work_sheets = sheets.Worksheets[0]
        work_sheets.ExportAsFixedFormat(0,os.getcwd()+'/pk_result.pdf') # Convert into PDF File(이 과정이 생략되면 인쇄 안됨)
        excel.Quit() # 엑셀 종료(이 과정이 생략되면 다시 실행시 에러 발생)
    
    # 로트번호 유무 확인(2주차)
    def lot_chk(lot):
        filename = os.getcwd()+'/pk.xls'
        wb = xlrd.open_workbook(filename)
        ws = wb.sheet_by_index(0)
    
        nrow=ws.nrows
    
        cnt = 0
        for i in range(nrow):
            if ws.col_values(8)[i] == lot:
                cnt += 1
                if cnt >= 1:
                    return True
    
    # OK 출력하기 버튼 이벤트 핸들러
    def okClick():
        try:
            mdb_xls() # mdb > xls 수행(3주차)
            lot = txt.get()
            SelectList = listbox.curselection()
            company=''.join([listbox.get(i) for i in SelectList]) # 리스트 값 문자열 변환
            if lot_chk(lot) == True:
                pkPrint(lot,company)
                txt.delete(0,"end") # 출력 후 입력된 로트번호 entry 삭제(3주차)
            else:
                tkinter.messagebox.showwarning("알림", "로트번호를 다시 확인하세요!") # 경고용 메시지 상자(2주차)
        except:
            tkinter.messagebox.showwarning("알림", "실행된 엑셀 창을 닫아주세요!") # AttributeError: Excel.Application.Workbooks 처리(3주차)
        
    # 고객 리스트 정보 추가 버튼 이벤트 핸들러
    def add_click():
        ctm = add_txt.get()
    
        # 고객 리스트 정보 추가
        f = open(os.getcwd()+'/customer.txt', 'a')
        f.write(ctm+'\n')
        f.close()
        add_txt.delete(0,"end") # 고객 리스트 추가후 입력된 고객명 entry 삭제(3주차)
    
        # 리스트 박스 새로고침
        listbox.delete(0, END)
        f = open(os.getcwd()+'/customer.txt', 'r')
        customerList = f.read().splitlines()
        f.close()
    
        for customer in customerList: listbox.insert(END, customer)
    
    # 고객사 리스트 정보 삭제 버튼 이벤트 핸들러(2주차)
    def del_click():
        # 고객 리스트 정보 삭제
        SelectList = listbox.curselection()
        company=''.join([listbox.get(i) for i in SelectList]) # 리스트 값 문자열 변환
    
        with open(os.getcwd()+'/customer.txt', 'r') as f:
            lines = f.readlines()
        with open(os.getcwd()+'/customer.txt', 'w') as f:
            for line in lines:
                if line.strip('\n') != company:
                    f.write(line)
    
        # 리스트 박스 새로고침
        listbox.delete(0, END)
        f = open(os.getcwd()+'/customer.txt', 'r')
        customerList = f.read().splitlines()
        f.close()
    
        for customer in customerList: listbox.insert(END, customer)
    
    # tkinter GUI적용
    root=Tk()
    root.title("PACKING LIST 출력")
    root.geometry("300x300+100+100")
    root.resizable(False, False)
    root.iconbitmap(os.getcwd()+'/print.ico') # 창 아이콘 설정(3주차)
    
    # 로트번호 입력
    lotLab = Label(root, text="로트번호")
    lotLab.place(x=20, y=20)
    txt = Entry(root)
    txt.place(x=80, y=20, height=25)
    
    # 고객사 추가
    add_customerLab = Label(root, text="고객추가")
    add_customerLab.place(x=20, y=50)
    add_txt = Entry(root)
    add_txt.place(x=80, y=50, height=25)
    
    # 고객사 리스트 스크롤바
    customerLab = Label(root, text="고객사")
    customerLab.place(x=20, y=80)
    
    frame=tkinter.Frame(root)
    
    scrollbar=tkinter.Scrollbar(frame, orient="vertical")
    scrollbar.pack(side="right", fill="y")
    
    # 고객 리스트 정보 customer.txt 가져오기
    f = open(os.getcwd()+'/customer.txt', 'r')
    customerList = f.read().splitlines()
    f.close()
    
    listbox=tkinter.Listbox(frame, yscrollcommand = scrollbar.set)
    for customer in customerList: listbox.insert(END, customer)
    listbox.pack()
    
    scrollbar["command"]=listbox.yview
    
    frame.place(x=80, y=80, height=140)
    
    # 회사 UI 적용
    image = tkinter.PhotoImage(file=os.getcwd()+'/UI_100.png')
    
    ui = tkinter.Label(root, image=image)
    ui.place(x = 95, y = 270)
    
    # 입력 버튼
    btn = Button(root, text="OK", width=15, command=okClick)
    btn.place(x=90, y=230)
    
    # 고객 추가 버튼
    add_btn = Button(root, text="추가", width=5, command=add_click)
    add_btn.place(x=240, y=50)
    
    # 고객 삭제 버튼(2주차)
    add_btn = Button(root, text="삭제", width=5, command=del_click)
    add_btn.place(x=240, y=80)
    
    root.mainloop()

     

    ※ 가독성을 위해 선언된 함수를 호출 부분과 분리하는 작업을 하였다.

    728x90
    반응형
    댓글