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

    파이썬 신병 교육대 2주차 일등병

    https://youtu.be/HaOwoUK1Pjs

    유튜버 김왼손의 왼손코딩에서 주관하는 파이썬 신병 교육대 2주차가 되었다.

    1주차 코딩 결과에 대한 내용을 주특기 훈련장에 제출했는데

    김왼손님이 2주차 영상에서 언급해 주셨다.

    2주차가 되어 일등병으로 진급했다.


    구현내용

    프로그램 실행화면
    잘못된 로트 번호 입력시 처리

     

    1. tkinter 모듈로 리스트 고객사 삭제 버튼을 추가하고, 리스트박스에서 선택된 고객사를 customer.txt에서 삭제하는 기능을 구현했다.
    2. xlrd 모듈을 이용해 잘못된 로트 번호 입력을 체크하는 lot_chk함수를 생성하고, OK버튼의 이벤트 핸들러인 okClick함수에 lot_chk 함수로 부터 True를 받지 못하면 "알림"을 띄우도록 구현했다.
    3. OS 모듈os.getcwd()를 모든 경로에 적용하여 현재 실행 중인 경로를 라우팅했다.

    작성한 코드

    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
    
    # 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)
    
    # 로트번호 입력
    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():
        lot = txt.get()
        SelectList = listbox.curselection()
        company=''.join([listbox.get(i) for i in SelectList]) # 리스트 값 문자열 변환
        if lot_chk(lot) == True:
            pkPrint(lot,company)
        else:
            tkinter.messagebox.showwarning("알림", "로트번호를 다시 확인하세요!") # 경고용 메시지 상자(2주차)
    
    # 고객 리스트 정보 추가 버튼 이벤트 핸들러
    def add_click():
        ctm = add_txt.get()
    
        # 고객 리스트 정보 추가
        f = open(os.getcwd()+'/customer.txt', 'a')
        f.write(ctm+'\n')
        f.close()
    
        # 리스트 박스 새로고침
        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()

    개선사항

    1. AttributeError 에 대한 예외처리(이전 실행된 엑셀 파일이 완전히 종료되지 않았을때 발생하는 에러)
    2. 창 아이콘 변경(현재는 tkinter 기본 아이콘 적용 중)
    3. 고객추가 후 텍스트 입력창 초기화
    728x90
    반응형
    댓글