Dandy Now!
  • [파이썬 신병 교육대] MDB로 부터 Packing List 자동출력 프로그램_3주차_상병
    2021년 12월 27일 13시 54분 29초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    파이썬 신병 교육대 3주차 상병

    https://youtu.be/6a3rO4HyxBE

    마지막 코드 수정이 될 것 같다.

    다음주는 전역식(?)이라고 한다.


    구현내용

     

    프로그램 실행화면

     

    AttributeError: Excel.Application.Workbooks 예외처리

     

    1. try except를 이용해 AttributeError 에 대한 예외처리를 했다(이전 실행된 엑셀 파일이 완전히 종료되지 않았을때 발생하는 에러).
    2. tkinter 모듈(root.iconbitmap)을 이용해서 창 아이콘을 프린터 모양으로 변경했다.
    3. tkinter 모듈(delete(0,"end"))을 이용해서 패킹리스트 출력 또는 고객사 추가 후 텍스트 입력창이 초기화 되도록 하였다. delete(0,"end")은 텍스트 입력창 내의 모든 내용을 지운다.
    4. 프로그램이 실행 중일때 mdb의 데이터가 변경되더라도 인쇄시 출력에 반영되지 않는 문제가 있었다. 이 문제를 해결하기 위해 mdb → xls로 만드는 기능을 함수로 만들고 OK버튼을 누를때마다 호출하도록 하였다. 이렇게 만든 함수는 try except에 적용하기에도 용이했다.

    작성한 코드

    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')
    
    # 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)
    
    # 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)
    
    # 입력 버튼
    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
    반응형
    댓글