Python/2.7 for fun.
가계부 프로그램 만들기. 1차완성.
일단 기본적인 기능은 하게 완성시켰다. 처음에 만들땐 가게부? 엄청 쉬울거같은데 심심풀이로 만들어볼까? 했던 생각이... 변했다. 상당히 복잡하다. 어렵지는 않은데 복잡하다. 필요한건 그냥 gui에서 입력값을 가져와서 폴더에 보기좋게 정리하는거밖에 없다. 근데 폴더 구조를 생각하고 그러니 진짜 복잡해진다.
폴더 구조.
1. 파이썬 소스파일과 같은 폴더 안에 '가계부' 라는 이름의 폴더가 생김.
2. '가계부' 폴더 안에 이번달 날짜 폴더가 생김. 오늘은 16년 12월 25일이니까 2016-12라는 폴더를 만듬. 동일한 위치에 affordable_money.txt가 있는데 이 안에는 한달동안 자유롭게 쓸수 있는 돈이 숫자로 들어가있다.
3. 2016-12와 같은 폴더 안에는 3종류의 txt파일이 있다.
1) '한달 총 사용량.txt'. 이 안에는 2016년 12월 한달동안 총 사용한 돈의 양이 숫자로 들어있다.
2) '12월 19~25일.txt'과 같은 포맷의 파일. 말 그대로 이번주에 사용한 돈의 총량이 숫자로 들어있다. 자동으로 만들어지고 자동으로 갱신되니까 건들지 말기..
3) '2016-12-25.txt'과 같은 포맷의 파일. 말 그대로 해당 날짜에 돈을 얼마 썼고, 왜 썼는지가 적혀져 있다. 맨 아래에는 해당 날짜에 사용한 돈의 총량이 들어있다.
사용법과 폴더 보는법 등.
나름 직관적으로 보기좋게 만들려고 노력을 많이 했다.
프로그램을 실행시키고 위처럼 입력 후 '지출내역 추가' 버튼을 누르면
이번주 남은돈과 이번달 남은돈에서 차감된다.
해당 파이썬 소스와 같은 디렉터리 안에 '가계부'라는 폴더가 자동으로 생기는데 그 안에는
요렇게 해당 달의 폴더가 자동으로 생기고 affordable_money.txt가 자동으로 생긴다. 이 안에는 매달 여윳돈이 txt로 들어가있고 디폴트값은 100만원이다. (위에 써있음) 그리고 또 2016-12 폴더 안에는...
처럼 들어가 있고 프로그램으로 내역을 추가할때마다 자동으로 내역이 txt에 추가된다.
한달 총 사용량은 말 그대로 한달에 총 사용한 돈의 양이고, 12월 19~25일.txt는 저 기간동안 사용한 돈의 총량이다.
참고로 예외처리는 할수있는만큼 거의다해놓긴 했는데 txt를 마음대로 만져서 포맷이 깨지면 또 고장날 우려가 있다...
개선해야될점.
1. 저번주에 안쓴 돈 만큼을 데이터가 이월되듯이 이번주로 넘어오게 하기.
2. 지출 내역 삭제하기를 gui에 구현하기.
3. 코드가 상당히 더럽다. 깔끔하게 리팩토링 등 하기.
4. 날짜입력하기 귀찮으니 디폴트값으로 오늘 날짜 주기
5. 공통되는 변수나 함수는(ex) see_money함수) new_ui.py에 넣어서 자동으로 사용되게 하려고했는데 이걸 py2exe로 exe로 만들기만 하면 안된다. 이유를 모르겠어서 그냥 복붙 한번 더함. 코드가 더러워졌는데 나중에 좀 줄여야될듯
6. 생각....
1. 개선된 new_ui.py (상당히 길다.)
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'new.ui' # # Created: Sun Dec 18 14:41:51 2016 # by: PyQt4 UI code generator 4.11.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui import sys, os, datetime import webbrowser import re try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s try: _encoding = QtGui.QApplication.UnicodeUTF8 def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig) def see_money(money): _monthly = '' cnt = 0 for i in xrange(len(money)-1,-1,-1): if cnt%4==0: _monthly = ' '+_monthly _monthly = money[i] + _monthly cnt += 1 return _monthly def get_week(date): day_idx = date.weekday() # turn sunday into 0, monday into 1, etc. sunday = date - datetime.timedelta(days=day_idx) date = sunday for n in xrange(7): yield date date += datetime.timedelta(days=1) dt = datetime.datetime.now() start = dt - datetime.timedelta(days=dt.weekday()) end = start + datetime.timedelta(days=6) thisweek_start = int(start.strftime('%d/%b/%Y')[:2]) thisweek_end = int(end.strftime('%d/%b/%Y')[:2]) dt = datetime.date.today() thisweek_txt_list = [d.isoformat()+'.txt'.encode(sys.stdout.encoding, 'replace') for d in get_week(datetime.datetime.now().date())] dir_name = u'가계부'.encode(sys.stdout.encoding, 'replace') monthly_total_txt = u'한달 총 사용량.txt'.encode(sys.stdout.encoding, 'replace') week_spend_txt = str(datetime.date.today().month)+unicode('월 ','utf-8').encode(sys.stdout.encoding, 'replace')+str(thisweek_start)+'~'+str(thisweek_end)+'.txt'.encode(sys.stdout.encoding, 'replace')#'12월 1~7일'과 같은 format affordable = 'affordable_money.txt'.encode(sys.stdout.encoding, 'replace') #여윳돈 설정 기본 100만원. class Ui_Form(object): def setupUi(self, Form): Form.setObjectName(_fromUtf8("Form")) Form.resize(464, 322) Form.setWindowIcon(QtGui.QIcon('cal.ico')) self.label_1 = QtGui.QLabel(Form) self.label_1.setGeometry(QtCore.QRect(20, 200, 81, 16)) self.label_1.setAlignment(QtCore.Qt.AlignCenter) self.label_1.setObjectName(_fromUtf8("label_1")) self.label_2 = QtGui.QLabel(Form) self.label_2.setGeometry(QtCore.QRect(20, 140, 81, 16)) self.label_2.setAlignment(QtCore.Qt.AlignCenter) self.label_2.setObjectName(_fromUtf8("label_2")) self.label_3 = QtGui.QLabel(Form) self.label_3.setGeometry(QtCore.QRect(20, 220, 81, 16)) self.label_3.setAlignment(QtCore.Qt.AlignCenter) self.label_3.setObjectName(_fromUtf8("label_3")) self.label_4 = QtGui.QLabel(Form) self.label_4.setGeometry(QtCore.QRect(20, 160, 81, 16)) self.label_4.setAlignment(QtCore.Qt.AlignCenter) self.label_4.setObjectName(_fromUtf8("label_4")) self.label_5 = QtGui.QLabel(Form) self.label_5.setGeometry(QtCore.QRect(160, 80, 81, 16)) self.label_5.setAlignment(QtCore.Qt.AlignCenter) self.label_5.setObjectName(_fromUtf8("label_5")) self.label_6 = QtGui.QLabel(Form) self.label_6.setGeometry(QtCore.QRect(160, 140, 81, 16)) self.label_6.setAlignment(QtCore.Qt.AlignCenter) self.label_6.setObjectName(_fromUtf8("label_6")) self.label_7 = QtGui.QLabel(Form) self.label_7.setGeometry(QtCore.QRect(240, 100, 21, 16)) self.label_7.setAlignment(QtCore.Qt.AlignCenter) self.label_7.setObjectName(_fromUtf8("label_7")) self.label_8 = QtGui.QLabel(Form) self.label_8.setGeometry(QtCore.QRect(20, 280, 81, 16)) self.label_8.setAlignment(QtCore.Qt.AlignCenter) self.label_8.setObjectName(_fromUtf8("label_8")) self.label_9 = QtGui.QLabel(Form) self.label_9.setGeometry(QtCore.QRect(20, 260, 81, 16)) self.label_9.setAlignment(QtCore.Qt.AlignCenter) self.label_9.setObjectName(_fromUtf8("label_9")) self.label_10 = QtGui.QLabel(Form) self.label_10.setGeometry(QtCore.QRect(160, 20, 81, 16)) self.label_10.setAlignment(QtCore.Qt.AlignCenter) self.label_10.setObjectName(_fromUtf8("label_10")) self.label_11 = QtGui.QLabel(Form) self.label_11.setGeometry(QtCore.QRect(320, 20, 91, 16)) self.label_11.setAlignment(QtCore.Qt.AlignCenter) self.label_11.setObjectName(_fromUtf8("label_11")) self.label_12 = QtGui.QLabel(Form) self.label_12.setGeometry(QtCore.QRect(400, 40, 21, 16)) self.label_12.setAlignment(QtCore.Qt.AlignCenter) self.label_12.setObjectName(_fromUtf8("label_12")) self.lineEdit_1 = QtGui.QLineEdit(Form) self.lineEdit_1.setGeometry(QtCore.QRect(160, 40, 81, 20)) self.lineEdit_1.setObjectName(_fromUtf8("lineEdit_1")) self.lineEdit_2 = QtGui.QLineEdit(Form) self.lineEdit_2.setGeometry(QtCore.QRect(160, 100, 81, 20)) self.lineEdit_2.setObjectName(_fromUtf8("lineEdit_2")) self.textEdit = QtGui.QTextEdit(Form) self.textEdit.setGeometry(QtCore.QRect(160, 160, 91, 81)) self.textEdit.setObjectName(_fromUtf8("textEdit")) self.lineEdit_3 = QtGui.QLineEdit(Form) self.lineEdit_3.setGeometry(QtCore.QRect(320, 40, 81, 20)) self.lineEdit_3.setObjectName(_fromUtf8("lineEdit_3")) self.pushButton_1 = QtGui.QPushButton(Form) self.pushButton_1.setGeometry(QtCore.QRect(10, 20, 101, 23)) self.pushButton_1.setObjectName(_fromUtf8("pushButton_1")) self.pushButton_2 = QtGui.QPushButton(Form) self.pushButton_2.setGeometry(QtCore.QRect(10, 60, 101, 23)) self.pushButton_2.setObjectName(_fromUtf8("pushButton_2")) self.pushButton_3 = QtGui.QPushButton(Form) self.pushButton_3.setGeometry(QtCore.QRect(150, 270, 111, 23)) self.pushButton_3.setObjectName(_fromUtf8("pushButton_3")) self.pushButton_4 = QtGui.QPushButton(Form) self.pushButton_4.setGeometry(QtCore.QRect(10, 100, 101, 23)) self.pushButton_4.setObjectName(_fromUtf8("pushButton_4")) self.pushButton_5 = QtGui.QPushButton(Form) self.pushButton_5.setGeometry(QtCore.QRect(310, 80, 111, 23)) self.pushButton_5.setObjectName(_fromUtf8("pushButton_5")) self.calendarWidget = QtGui.QCalendarWidget(Form) self.calendarWidget.setGeometry(QtCore.QRect(290, 130, 168, 171)) self.calendarWidget.setObjectName(_fromUtf8("calendarWidget")) self.line_1 = QtGui.QFrame(Form) self.line_1.setGeometry(QtCore.QRect(270, 0, 20, 321)) self.line_1.setFrameShape(QtGui.QFrame.VLine) self.line_1.setFrameShadow(QtGui.QFrame.Sunken) self.line_1.setObjectName(_fromUtf8("line_1")) self.line_2 = QtGui.QFrame(Form) self.line_2.setGeometry(QtCore.QRect(120, 0, 20, 321)) self.line_2.setFrameShape(QtGui.QFrame.VLine) self.line_2.setFrameShadow(QtGui.QFrame.Sunken) self.line_2.setObjectName(_fromUtf8("line_2")) self.button_clicked(Form) self.retranslateUi(Form) QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): Form.setWindowTitle(_translate("Form", "가계부", None)) self.label_1.setText(_translate("Form", "이번달 남은돈", None)) self.label_2.setText(_translate("Form", "이번주 남은돈", None)) #self.label_3.setText(_translate("Form", "50 0000 원", None)) #self.label_4.setText(_translate("Form", "3 0000 원", None)) self.label_5.setText(_translate("Form", "쓴 돈", None)) self.label_6.setText(_translate("Form", "사유(짧게)", None)) self.label_7.setText(_translate("Form", "원", None)) #self.label_8.setText(_translate("Form", "80 0000원", None)) self.label_9.setText(_translate("Form", "매달 여윳돈", None)) self.label_10.setText(_translate("Form", "날짜(월/일)", None)) self.label_11.setText(_translate("Form", "매달 여윳돈 설정", None)) self.label_12.setText(_translate("Form", "원", None)) self.pushButton_1.setText(_translate("Form", "전체 가계부", None)) self.pushButton_2.setText(_translate("Form", "저번달 가계부", None)) self.pushButton_3.setText(_translate("Form", "지출내역 추가", None)) self.pushButton_4.setText(_translate("Form", "이번달 가계부", None)) self.pushButton_5.setText(_translate("Form", "설정", None)) def button_clicked(self, Form): self.pushButton_1.clicked.connect(self.b1) self.pushButton_2.clicked.connect(self.b2) self.pushButton_4.clicked.connect(self.b4) self.pushButton_5.clicked.connect(self.b5) self.pushButton_3.clicked.connect(self.b3) def make_messagebox(self, text): msg = QtGui.QMessageBox() msg.setIcon(QtGui.QMessageBox.Warning) msg.setWindowTitle(u'가계부') msg.setText(text) msg.exec_() def b1(self): os.startfile(dir_name) def b2(self): t = datetime.date.today() _y = t.year _m = t.month _m = int(_m) - 1 if _m < 0: _y -= 1 _m = 12 _y = str(_y) _m = str(_m) path = _y+'-'+_m if path in os.listdir(dir_name): os.startfile(dir_name+'\\'+_y+'-'+_m) else: self.make_messagebox(u'저번달 가계부 폴더가 없습니다.') def b4(self): t = datetime.date.today() _y = t.year _m = t.month _y = str(_y) _m = str(_m) path = _y+'-'+_m if path in os.listdir(dir_name): os.startfile(dir_name+'\\'+_y+'-'+_m) def b5(self): try: _text = int(self.lineEdit_3.text()) except: self.make_messagebox(u'돈은 숫자만 입력해주세요!!') return with open(dir_name+'\\affordable_money.txt','w') as f: f.write(str(_text)) self.label_8.setText(see_money(str(_text))+u'원') self.make_messagebox(u'적용되었습니다.\n여윳돈 적용은 프로그램을 재시작해야 적용됩니다.') def b3(self): try: _r = re.findall('\d{2}/\d{2}',self.lineEdit_1.text())[0].split('/') except: self.make_messagebox(u"날짜는 '02/01'형식으로 입력해주세요!") return try: spend_money = int(self.lineEdit_2.text()) except: self.make_messagebox(u"쓴 돈은 정수만 입력해주세요!!") return reason = str(self.textEdit.toPlainText()).strip() #돈쓴 사유 msg = QtGui.QMessageBox() msg.setIcon(QtGui.QMessageBox.Question) msg.setWindowTitle(u'가계부') msg.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) msg.setText(u"추가하시겠습니까?") ret = msg.exec_() if ret==1024: #예 버튼을 눌렀을 경우 t = datetime.date.today() _y = str(t.year) path = dir_name+'\\'+_y+'-'+_r[0]+'\\'+_y+'-'+_r[0]+'-'+_r[1]+'.txt' if _y+'-'+_r[0]+'-'+_r[1]+'.txt' not in os.listdir(dir_name+'\\'+_y+'-'+_r[0]): #새로 가게부 txt파일 만드는 알고리즘 msg = QtGui.QMessageBox() msg.setIcon(QtGui.QMessageBox.Question) msg.setWindowTitle(u'가계부') msg.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) msg.setText(_y+'-'+_r[0]+'-'+_r[1]+u"날짜의 가게부가 없습니다. 추가하시겠습니까?") ret = msg.exec_() if ret==1024: #추가한다고 yes하면 새로 만듬. with open(dir_name+'\\'+_y+'-'+_r[0]+'\\'+_y+'-'+_r[0]+'-'+_r[1]+'.txt','w') as f: f.write('') else: #추가 안할거면 종료 return with open(path,'r') as f: #돈쓴내역 추가 알고리즘 content = f.read().split('\n')[:-2] total = map(lambda x:int(x[:8]), content) content = '\n'.join(content) if content: content += '\n' with open(path,'w') as f: f.write(content + u'{:>8}원, 사유 : [{}]\n\n'.format(spend_money, reason)) f.write(u'{}원을 {}-{}-{}에 사용.'.format(sum(total)+spend_money,_y,_r[0],_r[1])) budget_list = os.listdir(dir_name+'\\'+_y+'-'+_r[0]) #한달 남은 사용량 변경 알고리즘. monthly_total = 0 #이번달에 쓴 총돈 week_total = 0 #이번주에 쓴 총돈 for list in budget_list: if list!=monthly_total_txt and list.startswith(_y): with open(dir_name+'\\'+_y+'-'+_r[0]+'\\'+list,'r') as f: monthly_total += sum(map(lambda x:int(x[:8]), f.read().split('\n')[:-2])) if list in thisweek_txt_list: with open(dir_name+'\\'+_y+'-'+_r[0]+'\\'+list,'r') as f: week_total += sum(map(lambda x:int(x[:8]), f.read().split('\n')[:-2])) with open(dir_name+'\\affordable_money.txt','r') as f: monthly_affordable = int(f.read()) with open(dir_name+'\\'+_y+'-'+_r[0]+'\\'+monthly_total_txt,'w') as f: f.write(str(monthly_total)) with open(dir_name+'\\'+_y+'-'+_r[0]+'\\'+week_spend_txt,'w') as f: f.write(str(week_total)) with open(dir_name+'\\'+affordable,'r') as f: #여윳돈 입력. monthly = f.read() self.label_3.setText(see_money(str(monthly_affordable - monthly_total))+u'원') self.label_4.setText(see_money(str(int(monthly)/4 - week_total))+u'원')
2. 개선된 메인함수 부분.
# -*- encoding: cp949 -*- from PyQt4 import QtGui, QtCore from new_ui import * import sys, os import datetime reload(sys) sys.setdefaultencoding('cp949') class MyForm(QtGui.QMainWindow): def __init__(self, parent=None): super(MyForm, self).__init__(parent) self.ui = Ui_Form() self.ui.setupUi(self) def see_money(money): _monthly = '' cnt = 0 for i in xrange(len(money)-1,-1,-1): if cnt%4==0: _monthly = ' '+_monthly _monthly = money[i] + _monthly cnt += 1 return _monthly def get_week(date): day_idx = date.weekday() # turn sunday into 0, monday into 1, etc. sunday = date - datetime.timedelta(days=day_idx) date = sunday for n in xrange(7): yield date date += datetime.timedelta(days=1) dt = datetime.datetime.now() start = dt - datetime.timedelta(days=dt.weekday()) end = start + datetime.timedelta(days=6) thisweek_start = int(start.strftime('%d/%b/%Y')[:2]) thisweek_end = int(end.strftime('%d/%b/%Y')[:2]) dt = datetime.date.today() thisweek_txt_list = [d.isoformat()+'.txt' for d in get_week(datetime.datetime.now().date())] dir_name = u'가계부' monthly_total_txt = u'한달 총 사용량.txt' week_spend_txt = str(datetime.date.today().month)+u'월 '+str(thisweek_start)+'~'+str(thisweek_end)+'.txt' #12월 1~7일과 같은 format affordable = 'affordable_money.txt' #여윳돈 설정 기본 100만원. t = datetime.date.today() _y = str(t.year) _m = '{:0>2}'.format(str(t.month)) _d = '{:0>2}'.format(str(t.day)) if dir_name not in os.listdir('.'): #가계부 디렉터리 만들기. os.makedirs(dir_name) if affordable not in os.listdir(dir_name): with open(dir_name+'\\'+affordable,'w') as f: f.write('1000000') dir_path = _y+'-'+_m #각 월별 디렉터리 만들기 if dir_path not in os.listdir(dir_name): os.makedirs(dir_name+'\\'+dir_path) txt_path = _y+'-'+_m+'-'+_d+'.txt' #각 월 내부의 일별 txt파일 만들기 if txt_path not in os.listdir(dir_name+'\\'+dir_path): with open(dir_name+'\\'+dir_path+'\\'+txt_path,'w') as f: f.write('') if monthly_total_txt not in os.listdir(dir_name+'\\'+dir_path): #이번달에 쓴 총돈 관련 txt파일 만들기 with open(dir_name+'\\'+dir_path+'\\'+monthly_total_txt,'w') as f: f.write('0') if week_spend_txt not in os.listdir(dir_name+'\\'+dir_path): #이번주에 쓴 돈 관련 txt파일 만들기 with open(dir_name+'\\'+dir_path+'\\'+week_spend_txt,'w') as f: f.write('0') app = QtGui.QApplication(sys.argv) myapp = MyForm() with open(dir_name+'\\'+affordable,'r') as f: #여윳돈 입력. monthly = f.read() budget_list = os.listdir(dir_name+'\\'+_y+'-'+_m) monthly_total = 0 week_total = 0 for list in budget_list: if list!=monthly_total_txt: with open(dir_name+'\\'+_y+'-'+_m+'\\'+list,'r') as f: monthly_total += sum(map(lambda x:int(x[:8]), f.read().split('\n')[:-2])) if list in thisweek_txt_list: with open(dir_name+'\\'+_y+'-'+_m+'\\'+list,'r') as f: week_total += sum(map(lambda x:int(x[:8]), f.read().split('\n')[:-2])) with open(dir_name+'\\affordable_money.txt','r') as f: monthly_affordable = int(f.read()) #see_money함수는 new_ui.py에 있음 myapp.ui.label_3.setText(see_money(str(monthly_affordable - monthly_total))+u'원') #이번달 여윳돈 텍스트 설정 myapp.ui.label_4.setText(see_money(str(int(monthly)/4 - week_total))+u'원') #이번주 여윳돈 설정 myapp.ui.label_8.setText(see_money(monthly)+u'원') #매달 여윳돈 myapp.ui.lineEdit_1.setText(_m+'/'+_d) #기본 날짜 myapp.show() app.exec_()
setup.py
#-*- coding: cp949 -*- from distutils.core import setup import py2exe, glob folder = u'가계부' setup( windows=[{'script':'hello.py','icon_resources':[(1, 'cal.ico')]}], #console=['hello.py']가 아니라 앞을 windows로 주면 보기싫은 콘솔 창이 사라짐. #data_files=[ ('C:\\Users\\Ko\\Desktop\\python exe\\'+folder+'\\imageformats',glob.glob('C:\\Python27\\Lib\\site-packages\\PyQt4\\plugins\\imageformats\\*.*')) ], options = {'py2exe': { 'bundle_files':1, 'dist_dir':'C:\\Users\\Ko\\Desktop\\python exe\\'+folder, 'includes': ['sip','PyQt4.QtNetwork'], #그냥 py2exe로 만들었을때 이러한 모듈이 없다고 에러뜸. 이거 추가. "dll_excludes": ["MSVCP90.dll"] #이거 없다고 exe가 안만들어지는데 굳이 필요 없으므로 필요 없다고 표시해둠. }}, zipfile = None )
'Python > 2.7 for fun.' 카테고리의 다른 글
점심추천 slackbot 만들기. (0) | 2017.03.20 |
---|---|
티스토리 서비스 종료 대비용 사진 가져오기. (6) | 2016.12.26 |
가계부 프로그램 만들기. (디자인만.) (2) | 2016.12.18 |
시력 테스트 자동화 스크립트 2. (0) | 2016.05.07 |
벤쿠버 유학생을 위한 craigslist에서 다운타운 방찾기 2 (0) | 2015.12.30 |
'Python/2.7 for fun.'의 다른글
- 현재글가계부 프로그램 만들기. 1차완성.