가게부 - 토이프로젝트

지환·2023년 6월 6일
0

팀프로젝트

목록 보기
3/4

참고 ; http://doc.qt.io/qt-5/qwidget.html#mouseTracking-prop

http://doc.qt.io/qt-5/qwidget.html#mouseMoveEvent

http://doc.qt.io/qt-5/qmouseevent.html

프로그램 설계

도메인 설계

class Ui_mainWindow(object):

메서드

  • def setupUi(self, mainWindow):
    가계부 메인 UI

  • def table_to_csv(self, cnt_row, date, inout, category, money):
    테이블에 insert될 때 마다 csv에 한줄 한줄 저장

  • def csv_insertItem(self, date, inout, category, money):

  • def csv_load(self):
    이전에 저장된 csv file load

  • def insertItem(self, date, inout, category, money):
    수입 지출에 대한 tableInsert

  • def setTableWidgetData(self):
    tableWidget dataInit

  • def inputInc(self):
    지출금액 기입을 위한 버튼액션

  • def delList(self):
    수입/지출 목록 삭제를 위한 버튼액션

def setupUi(self, mainWindow):

    def setupUi(self, mainWindow):
        expCat = ['식비', '주거', '통신', '의복', '건강', '교통', '오락', '세금', '기타']
        incCat = ['주수입', '부수입', '기타수입']

        mainWindow.setObjectName("mainWindow")
        mainWindow.setWindowTitle("가계부")
        mainWindow.resize(804, 600)
        mainWindow.setMouseTracking(False)
        self.centralwidget = QtWidgets.QWidget(mainWindow)
        self.centralwidget.setObjectName("centralwidget")
  • 두개의 파라미터를 받는다. (self,mainwindow)

  • expCat, inCat 변수는 사용자가 입력할 때 정보값을 제공한다.

  • mainwindow는 객체로 정의되고 밑에 init에 정의되어있다.

  • mainWindow.setObjectName 객체에 대한 메서드를 실행한다.

  • 부모를 mainWindow로 하는 Qtwidgets모듈에서 Qwidget 클래스를 불러온다. self.centralwidget은 QtWidgets.QWidget의 인스턴스를 참조하는 변수로, 이를 통해 QWidget의 속성과 메서드를 사용할 수 있다.

  • self.centralwidget은 클래스 내에서 정의된 인스턴스 변수로, 메인 창의 중앙 위젯을 참조

  • self.centralwidget.setObjectName("centralwidget") 줄은 중앙 위젯의 객체 이름을 "centralwidget"으로 설정하여 애플리케이션에서 위젯을 식별

step2[달력 UI]

# 달력 UI
        self.calendarWidget = QtWidgets.QCalendarWidget(self.centralwidget)
        self.calendarWidget.setEnabled(True)
        self.calendarWidget.setGeometry(QtCore.QRect(10, 10, 344, 236))
        self.calendarWidget.setObjectName("calendarWidget")
  • QtWidgets.QCalendarWidget 클래스의 인스턴스인 calendarWidget 객체를 생성합니다. 이 위젯은 self.centralwidget을 부모 위젯으로 설정하여 중앙 위젯에 속하도록 한다.

  • self.centralwidget은 부모 위젯으로 사용될 QtWidgets.QWidget 인스턴스다. 따라서 QtWidgets.QCalendarWidget(self.centralwidget) 코드는 self.centralwidget을 부모로 하는 QtWidgets.QCalendarWidget 인스턴스를 생성한다.

    이렇게 생성된 QCalendarWidget 인스턴스는 self.calendarWidget 변수에 할당되어 해당 변수를 통해 이후에 위젯을 조작하고 설정할 수 있습니다.

  • self.calendarWidget.setEnabled(True): calendarWidget 위젯을 활성화합니다. setEnabled(True)를 호출하여 사용자 상호작용을 가능하게한다. 다시 말하면, 이 코드는 QtWidgets.QCalendarWidget.setEnabled(True)를 의미한다.

    [setEnabled은 버튼을 활성화 시켜주는 인스턴스다.]

  • self.calendarWidget.setGeometry(QtCore.QRect(10, 10, 344, 236)): calendarWidget 위젯의 위치와 크기를 설정 + QtCore.QRect 클래스의 인스턴스를 생성하여 (10, 10) 위치에서 시작하여 가로 344픽셀, 세로 236픽셀의 크기를 가진다. [setGeometry는 출력할 위치를 결정]

  • calendarWidget 위젯의 객체 이름을 "calendarWidget"으로 설정

step3[수입 UI]

 # 수입 UI
        self.incLb = QtWidgets.QLabel(self.centralwidget)
        self.incLb.setGeometry(QtCore.QRect(20, 250, 41, 41))
        self.incLb.setObjectName("incLb")
        self.incLb.setText("수입")
        
        self.incComBx = QtWidgets.QComboBox(self.centralwidget)
        self.incComBx.setGeometry(QtCore.QRect(30, 290, 71, 41))
        self.incComBx.setObjectName("incComBx")
        for value in incCat:
            self.incComBx.addItem(value)
        
        self.incLine = QtWidgets.QLineEdit(self.centralwidget)
        self.incLine.setGeometry(QtCore.QRect(120, 290, 131, 41))
        self.incLine.setValidator(QIntValidator(0, 9999999))
        self.incLine.setObjectName("incLine")

        self.won1 = QtWidgets.QLabel(self.centralwidget)
        self.won1.setGeometry(QtCore.QRect(260, 300, 21, 16))
        self.won1.setObjectName("won1")
        self.won1.setText("\\")

        self.incBtn = QtWidgets.QPushButton(self.centralwidget)
        self.incBtn.setGeometry(QtCore.QRect(290, 290, 61, 41))
        self.incBtn.clicked.connect(self.inputInc)
        self.incBtn.setObjectName("incBtn")
        self.incBtn.setText("기입")
  • self.incLb = QtWidgets.QLabel(self.centralwidget): QtWidgets.QLabel 클래스의 인스턴스인 incLb 객체를 생성
    QtWidgets모듈에 있는 QLabel 클래스 참조 -> incLb 객체생성

  • 이는 텍스트를 표시하는 레이블 위젯을 나타냄. self.centralwidget을 부모 위젯으로 설정하여 중앙 위젯에 설정

  • self.incLb.setGeometry(QtCore.QRect(20, 250, 41, 41)): incLb 레이블 위젯의 위치와 크기를 설정 (20, 250) 위치에서 시작하여 가로 41픽셀, 세로 41픽셀의 크기를 설정

  • self.incLb.setObjectName("incLb"): incLb 레이블 위젯의 객체 이름을 "incLb"로 설정

  • self.incLb.setText("수입"): incLb 레이블 위젯에 텍스트를 설정하여 "수입"이라고 표시

self.incComBx = QtWidgets.QComboBox(self.centralwidget)
self.incComBx.setGeometry(QtCore.QRect(30, 290, 71, 41))
self.incComBx.setObjectName("incComBx")
	for value in incCat:
 	 	self.incComBx.addItem(value)
  • self.incComBx = QtWidgets.QComboBox(self.centralwidget): QtWidgets.QComboBox 클래스의 인스턴스인 incComBx 객체를 생성 이는 선택할 수 있는 드롭다운 목록을 나타내는 콤보 박스 위잿 self.centralwidget을 부모 위젯으로 설정하여 중앙 위젯에 속함

    [QComboBox는 작은 공간을 차지하면서, 여러 옵션들을 제공하고 그 중 하나의 옵션을 선택할 수 있도록 해주는 위젯]

  • self.incComBx.setGeometry(QtCore.QRect(30, 290, 71, 41)): incComBx 콤보 박스 위젯의 위치와 크기를 설정

  • self.incComBx.setObjectName("incComBx"): incComBx 콤보 박스 위젯의 객체 이름을 "incComBx"로 설정

  • for value in incCat: self.incComBx.addItem(value): incCat 리스트의 각 값을 incComBx 콤보 박스에 아이템으로 추가 incCat = ['주수입', '부수입', '기타수입']
self.incLine = QtWidgets.QLineEdit(self.centralwidget)
self.incLine.setGeometry(QtCore.QRect(120, 290, 131, 41))
self.incLine.setValidator(QIntValidator(0, 9999999))
self.incLine.setObjectName("incLine")
  • self.incLine = QtWidgets.QLineEdit(self.centralwidget): QtWidgets.QLineEdit 클래스의 인스턴스인 incLine 객체를 생성합니다. 이는 사용자로부터 텍스트 입력을 받을 수 있는 한 줄 텍스트 상자 위젯 [QLineEdit이란, 한줄짜리 글자를 입력받을 수 있는 입력위젯]

    QtWidgets모듈에 있는 QLineEdit 클래스 참조하여 incLine에 할당

  • self.incLine.setValidator(QIntValidator(0, 9999999)): incLine 텍스트 상자 위젯에 입력되는 값의 유효성을 검사하기 위해 QIntValidator를 설정 사용자는 0에서 9999999 사이의 정수만 입력 가능

    Qt는 QIntValidator, QDoubleValidor, QRegExpValidator 등의 3 종류의 밸리데이터 클래스가 있음. 사용하는 방법은 밸리데이터를 생성한 후 QLineEdit나 QComboBox의 setValidator(validator) 함수를 호출해 주면 된다.

    QIntValidator와 QDoubleValidator는 생성자 또는 setRange() 함수로 최소, 최대, 소수점(QDoubleValidator인 경우에만)을 동시에 설정

  • self.incLine.setObjectName("incLine"): incLine 텍스트 상자 위젯의 객체 이름을 "incLine"으로 설정

self.incBtn = QtWidgets.QPushButton(self.centralwidget)
self.incBtn.setGeometry(QtCore.QRect(290, 290, 61, 41))
self.incBtn.clicked.connect(self.inputInc)
self.incBtn.setObjectName("incBtn")
self.incBtn.setText("기입")
  • QtWidgets모듈로부터 QPushButton 클래스의 인스턴스인 incBtn 객체를 생성
  • self.incBtn.clicked.connect(self.inputInc)
    incBtn 버튼 위젯이 클릭되었을 때 호출할 메서드를 inputInc로 연결합니다. 즉, inputInc 메서드는 incBtn 버튼을 클릭했을 때 실행

def inputInc(self)[수입버튼액션]

    # 수입금액 기입을 위한 버튼액션
    def inputInc(self):
        date = self.calendarWidget.selectedDate()
        date = str(date.toString())
        money = str(self.incLine.text()) # 수입금액
        category = str(self.incComBx.currentText()) # 수입 분류
        current_inc = int(str(self.totalIncLb.text()).split(' ')[2])
        print(current_inc)
        current_inc += int(money)
        self.totalIncLb.setText("총 수입: " + str(current_inc))
        self.insertItem(date, "수입", category, money)
  • date = self.calendarWidget.selectedDate(): calendarWidget 위젯에서 선택된 날짜를 가져와서 date 변수에 저장

  • self.calendarWidget는 QCalendarWidget 클래스의 인스턴스인 calendarWidget 객체를 나타냅니다. selectedDate는 QCalendarWidget 클래스에서 제공되는 메서드

  • date = str(date.toString()): date 변수를 문자열로 변환하여 저장
    date.toString()은 PyQt에서 사용되는 메서드로, QDate 객체인 date의 문자열 표현을 반환 그 후에 str() 함수를 사용하여 PyQt에서 반환된 문자열을 파이썬의 문자열로 변환

step3[지출 UI]

    # 지출 UI
        self.expLb = QtWidgets.QLabel(self.centralwidget)
        self.expLb.setGeometry(QtCore.QRect(20, 350, 41, 41))
        self.expLb.setObjectName("expLb")
        self.expLb.setText("지출")

        self.expComBx = QtWidgets.QComboBox(self.centralwidget)
        self.expComBx.setGeometry(QtCore.QRect(30, 390, 71, 41))
        self.expComBx.setObjectName("expComBx")
        for value in expCat:
            self.expComBx.addItem(value)

        self.expLine = QtWidgets.QLineEdit(self.centralwidget)
        self.expLine.setGeometry(QtCore.QRect(120, 390, 131, 41))
        self.expLine.setValidator(QIntValidator(0, 9999999))
        self.expLine.setObjectName("expLine")

        self.won2 = QtWidgets.QLabel(self.centralwidget)
        self.won2.setGeometry(QtCore.QRect(260, 400, 21, 16))
        self.won2.setObjectName("won2")
        self.won2.setText("\\")

        self.expBtn = QtWidgets.QPushButton(self.centralwidget)
        self.expBtn.setGeometry(QtCore.QRect(290, 390, 61, 41))
        self.expBtn.clicked.connect(self.inputExp)
        self.expBtn.setObjectName("expBtn")
        self.expBtn.setText("기입")

        self.explainLb = QtWidgets.QLabel(self.centralwidget)
        self.explainLb.setGeometry(QtCore.QRect(20, 450, 101, 16))
        self.explainLb.setObjectName("explainLb")
        self.explainLb.setText("세부 설명")

        self.expText = QtWidgets.QTextEdit(self.centralwidget)
        self.expText.setGeometry(QtCore.QRect(30, 480, 321, 91))
        self.expText.setObjectName("expText")

    # 이번 달 소비, 지출 금액 합계 UI
        self.totalLb = QtWidgets.QLabel(self.centralwidget)
        self.totalLb.setGeometry(QtCore.QRect(380, 10, 170, 16))
        self.totalLb.setObjectName("totalLb")
        self.totalLb.setText("이번 달 소비 / 지출 합계")

        self.totalExpLb = QtWidgets.QLabel(self.centralwidget)
        self.totalExpLb.setGeometry(QtCore.QRect(410, 40, 250, 16))
        self.totalExpLb.setObjectName("totalExpLb")
        self.totalExpLb.setText("총 지출  ") # 지출 총 합 뒤에 + 로 붙이기

        self.totalIncLb = QtWidgets.QLabel(self.centralwidget)
        self.totalIncLb.setGeometry(QtCore.QRect(410, 140, 250, 16))
        self.totalIncLb.setObjectName("totalIncLb")
        self.totalIncLb.setText("총 수입  ") # 수입 총 합 뒤에 + 로 붙이기

    # 소비, 지출 리스트 UI
        self.listLb = QtWidgets.QLabel(self.centralwidget)
        self.listLb.setGeometry(QtCore.QRect(380, 260, 130, 16))
        self.listLb.setObjectName("listLb")
        self.listLb.setText("수입 / 지출 리스트")

        self.delBtn = QtWidgets.QPushButton(self.centralwidget)
        self.delBtn.setGeometry(QtCore.QRect(710, 250, 61, 41))
        self.delBtn.clicked.connect(self.delList)
        self.delBtn.setObjectName("expBtn")
        self.delBtn.setText("삭제")

        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(390, 290, 381, 281))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setRowCount(1)
        self.tableWidget.setColumnCount(4)
        self.setTableWidgetData()
        mainWindow.setCentralWidget(self.centralwidget)

        self.statusbar = QtWidgets.QStatusBar(mainWindow)
        self.statusbar.setObjectName("statusbar")
        mainWindow.setStatusBar(self.statusbar)

        QtCore.QMetaObject.connectSlotsByName(mainWindow)

expComBx

       self.expComBx = QtWidgets.QComboBox(self.centralwidget)
       self.expComBx.setGeometry(QtCore.QRect(30, 390, 71, 41))
       self.expComBx.setObjectName("expComBx")
       for value in expCat:
           self.expComBx.addItem(value)
  • self.expComBx = QtWidgets.QComboBox(self.centralwidget): QtWidgets.QComboBox 클래스의 인스턴스인 expComBx 객체를 생성 드롭다운 목록을 가진 콤보 박스 위젯을 나타냄
  • 드롭다운 목록을 가진 콤보 박스 위젯을 나타냄 || (30, 390) 위치에서 시작-> 가로 71픽셀, 세로 41픽셀의 크기를 가진다.
  • expCat 리스트에 있는 각각의 값을 가져와서 expComBx 콤보 박스 위젯에 아이템으로 추가

expLine & Won2

       self.expLine = QtWidgets.QLineEdit(self.centralwidget)
       self.expLine.setGeometry(QtCore.QRect(120, 390, 131, 41))
       self.expLine.setValidator(QIntValidator(0, 9999999))
       self.expLine.setObjectName("expLine")
       
       self.won2 = QtWidgets.QLabel(self.centralwidget)
       self.won2.setGeometry(QtCore.QRect(260, 400, 21, 16))
       self.won2.setObjectName("won2")
       self.won2.setText("\\")        
  • self.expLine = QtWidgets.QLineEdit(self.centralwidget): QtWidgets.QLineEdit 클래스의 인스턴스인 expLine 객체를 생성 이는 텍스트를 입력할 수 있는 상자냄.
  • self.expLine.setGeometry(QtCore.QRect(120, 390, 131, 41)): expLine 텍스트 상자 위젯의 위치와 크기를 설정
  • self.expLine.setValidator(QIntValidator(0, 9999999)): expLine 텍스트 상자 위젯에 입력된 값이 유효한 정수 범위 내에 있는지를 검사하는 유효성 검사기(validator)를 설정
  • self.expLine.setObjectName("expLine"): expLine 텍스트 상자 위젯의 객체 이름을 "expLine"
        self.expBtn = QtWidgets.QPushButton(self.centralwidget)
        self.expBtn.setGeometry(QtCore.QRect(290, 390, 61, 41))
        self.expBtn.clicked.connect(self.inputExp)
        self.expBtn.setObjectName("expBtn")
        self.expBtn.setText("기입")

        self.explainLb = QtWidgets.QLabel(self.centralwidget)
        self.explainLb.setGeometry(QtCore.QRect(20, 450, 101, 16))
        self.explainLb.setObjectName("explainLb")
        self.explainLb.setText("세부 설명")

        self.expText = QtWidgets.QTextEdit(self.centralwidget)
        self.expText.setGeometry(QtCore.QRect(30, 480, 321, 91))
        self.expText.setObjectName("expText")
  • QtWidgets.QPushButton 클래스의 인스턴스인 expBtn 객체를 생성
    expBtn 버튼 위젯의 클릭 이벤트와 inputExp 메서드를 연결
  • self.explainLb.setGeometry(QtCore.QRect(20, 450, 101, 16)): explainLb 레이블 위젯의 위치와 크기를 설정
  • self.explainLb.setObjectName("explainLb"): explainLb 레이블 위젯의 객체 이름을 "explainLb"으로 설정
  • self.explainLb.setText("세부 설명"): explainLb 레이블 위젯의 텍스트를 "세부 설명"으로 설정

이번 달 소비, 지출 금액 합계 UI

        self.totalLb = QtWidgets.QLabel(self.centralwidget)
        self.totalLb.setGeometry(QtCore.QRect(380, 10, 170, 16))
        self.totalLb.setObjectName("totalLb")
        self.totalLb.setText("이번 달 소비 / 지출 합계")

        self.totalExpLb = QtWidgets.QLabel(self.centralwidget)
        self.totalExpLb.setGeometry(QtCore.QRect(410, 40, 250, 16))
        self.totalExpLb.setObjectName("totalExpLb")
        self.totalExpLb.setText("총 지출  ") # 지출 총 합 뒤에 + 로 붙이기

        self.totalIncLb = QtWidgets.QLabel(self.centralwidget)
        self.totalIncLb.setGeometry(QtCore.QRect(410, 140, 250, 16))
        self.totalIncLb.setObjectName("totalIncLb")
        self.totalIncLb.setText("총 수입  ") # 수입 총 합 뒤에 + 로 붙이기

위 동작 원리와 동일하다.

소비, 지출 리스트 UI

        self.listLb = QtWidgets.QLabel(self.centralwidget)
        self.listLb.setGeometry(QtCore.QRect(380, 260, 130, 16))
        self.listLb.setObjectName("listLb")
        self.listLb.setText("수입 / 지출 리스트")

        self.delBtn = QtWidgets.QPushButton(self.centralwidget)
        self.delBtn.setGeometry(QtCore.QRect(710, 250, 61, 41))
        self.delBtn.clicked.connect(self.delList)
        self.delBtn.setObjectName("expBtn")
        self.delBtn.setText("삭제")

        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(390, 290, 381, 281))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setRowCount(1)
        self.tableWidget.setColumnCount(4)
        self.setTableWidgetData()
        mainWindow.setCentralWidget(self.centralwidget)

        self.statusbar = QtWidgets.QStatusBar(mainWindow)
        self.statusbar.setObjectName("statusbar")
        mainWindow.setStatusBar(self.statusbar)

        QtCore.QMetaObject.connectSlotsByName(mainWindow)
  • QtWidgets.QTableWidget(self.centralwidget): QtWidgets.QTableWidget 클래스의 인스턴스인 tableWidget 객체를 생성

  • self.tableWidget.setGeometry(QtCore.QRect(390, 290, 381, 281)): tableWidget 테이블 위젯의 위치와 크기를 설정

  • self.tableWidget.setObjectName("tableWidget"): tableWidget 테이블 위젯의 객체 이름을 "tableWidget"으로 설정

  • self.tableWidget.setRowCount(1): tableWidget 테이블 위젯의 행(row) 개수를 1로 설정

  • self.tableWidget.setColumnCount(4): tableWidget 테이블 위젯의 열(column) 개수를 4로 설정

  • self.setTableWidgetData(): setTableWidgetData() 메서드를 호출하여 테이블 위젯의 초기 데이터를 설정

    #tableWidget dataInit    
    def setTableWidgetData(self):
        column_headers = ['날짜', '수입/지출', '카테고리', '금액']
        self.tableWidget.setHorizontalHeaderLabels(column_headers)
        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()
  • column_headers = ['날짜', '수입/지출', '카테고리', '금액']: 테이블 위젯의 열 헤더에 해당하는 문자열 리스트를 생성

    self.tableWidget.setHorizontalHeaderLabels(column_headers): column_headers

    tableWidget 객체의 setHorizontalHeaderLabels() 메서드를 호출하여 테이블 위젯의 수평 방향 헤더에 column_headers 리스트에 저장된 열 헤더를 설정

  • self.tableWidget.resizeColumnsToContents(): 테이블 위젯의 열 너비를 열에 맞게 자동 조정

  • self.tableWidget.resizeRowsToContents(): 테이블 위젯의 행 높이를 행에 맞게 자동 조정

 QtCore.QMetaObject.connectSlotsByName(mainWindow)
  • QtCore.QMetaObject : QtCore 모듈에 속한 QMetaObject 클래스 클래스는 객체의 메타 정보를 관리하고, 시그널과 슬롯을 연결하는 기능이다.
  • connectSlotsByName(mainWindow): QMetaObject 클래스의 connectSlotsByName() 메서드를 호출 / mainWindow: mainWindow은 가계부 애플리케이션의 메인 창

step3[csv_load]

[저장된 csv file load]

    # 이전에 저장된 csv file load
    def csv_load(self):
        f = open('household_ledger.csv', 'r', encoding = 'utf-8')
        rdr =  csv.reader(f)
        income = 0
        outcome = 0
        for line in rdr:
            self.csv_insertItem(line[1], line[2], line[3], line[4])
            if '수입' in line[2]:
                income += int(line[4])
            elif '지출' in line[2]:
                outcome += int(line[4])
        self.totalIncLb.setText("총 수입: " + str(income))
        self.totalExpLb.setText("총 지출: " + str(outcome))
        f.close()
    
    
    #테이블에 insert될 때 마다 csv에 한줄 한줄 저장
    def table_to_csv(self, cnt_row, date, inout, category, money):
        f = open("household_ledger.csv", 'a', encoding = "utf-8", newline = '')
        wr = csv.writer(f)
        wr.writerow([cnt_row, date, inout, category, money])
        f.close()        
       
  • f = open('household_ledger.csv', 'r', encoding='utf-8'): 'household_ledger.csv' 파일을 읽기 모드('r')로 열고 파일 객체 + 파일은 UTF-8 인코딩으로 처리

  • rdr = csv.reader(f): csv.reader() 함수를 사용하여 파일 객체를 CSV 리더 객체로 변환

  • income = 0 및 outcome = 0: 수입과 지출을 계산하기 위한 변수를 초기화

  • for line in rdr:: rdr은 csv.reader 객체로 생성된 것으로, 이를 반복문으로 순회하면서 CSV 파일의 각 줄을 처리한다.

  • self.csv_insertItem(line[1], line[2], line[3], line[4]): csv_insertItem 메서드를 호출하여 테이블 위젯에 새로운 행을 추가합니다. line[1], line[2], line[3], line[4]은 CSV 파일의 해당 줄에서 각 열의 데이터를 나타냅니다. 이 데이터를 인자로 전달하여 새로운 행을 추가하는 작업을 수행한다.

  • if '수입' in line[2]:: line[2]는 CSV 파일의 해당 줄에서 세 번째 열의 데이터를 나타냅니다. 이 부분에서 '수입'이라는 텍스트가 포함되어 있는지 확인합니다.

  • income += int(line[4]): '수입'이 포함되어 있다면, line[4]를 정수로 변환하여 income 변수에 더합니다. 이는 수입의 총합을 계산하기 위한 작업 + 지출도 동일하다.

  • self.totalIncLb.setText("총 수입: " + str(income)): 화면에 총 수입을 나타내는 레이블에 계산된 income 값을 설정

  • self.totalExpLb.setText("총 지출: " + str(outcome)): 화면에 총 지출을 나타내는 레이블에 계산된 outcome 값을 설정

리더객체란?

CSV 리더 객체는 csv.reader() 함수에 의해 생성되는 객체다. 이 객체는 CSV 파일을 행 단위로 읽을 수 있는 기능을 제공한다. CSV 파일은 쉼표(,)로 구분된 데이터를 포함하고 있는 파일 형식이다.

CSV 리더 객체를 생성하기 위해서는 파일 객체가 필요합니다. 파일 객체는 open() 함수를 사용하여 파일을 열고 반환된 객체입니다. 이 파일 객체를 csv.reader() 함수에 전달하여 CSV 리더 객체를 생성합니다. CSV 리더 객체는 파일을 읽어 각 행을 리스트로 반환하며, 이를 반복하여 파일의 내용을 처리할 수 있습니다.

각 행은 CSV 파일의 한 줄에 해당하며, 리스트 내의 요소는 해당 줄의 데이터 열을 나타낸다.

table_to_csv

테이블에 insert될 때 마다 csv에 한줄 한줄 저장

    #테이블에 insert될 때 마다 csv에 한줄 한줄 저장
    def table_to_csv(self, cnt_row, date, inout, category, money):
        f = open("household_ledger.csv", 'a', encoding = "utf-8", newline = '')
        wr = csv.writer(f)
        wr.writerow([cnt_row, date, inout, category, money])
        f.close() 
  • "household_ledger.csv" 파일을 열고 파일 객체 f를 생성합니다. 'a' 모드는 파일을 이어쓰기 모드로 열기 위한 것입니다.

  • wr = csv.writer(f): csv.writer 객체 wr을 생성합니다. 이 객체를 사용하여 CSV 파일에 데이터를 작성할 수 있습니다.

  • wr.writerow([cnt_row, date, inout, category, money]): wr.writerow() 메서드를 사용하여 리스트 형태의 데이터를 CSV 파일의 한 줄로 작성합니다.
    [cnt_row, date, inout, category, money]는 CSV 파일에 저장될 데이터입니다. cnt_row, date, inout, category, money는 각각 테이블 위젯에서 가져온 값입니다. 이를 한 줄로 만들어서 CSV 파일에 저장

  • table_to_csv() 메서드는 테이블 위젯에 행이 추가될 때마다 해당 행을 CSV 파일에 저장하는 역할

step4 csv_insertItem / insertItem

[테이블 위젯에 새로운 행을 추가하고 데이터를 설정]


    def csv_insertItem(self, date, inout, category, money):
        cnt_row = self.tableWidget.rowCount()

        self.tableWidget.setItem(cnt_row - 1, 0, QtWidgets.QTableWidgetItem(date))
        self.tableWidget.setItem(cnt_row - 1, 1, QtWidgets.QTableWidgetItem(inout))
        self.tableWidget.setItem(cnt_row - 1, 2, QtWidgets.QTableWidgetItem(category))
        self.tableWidget.setItem(cnt_row - 1, 3, QtWidgets.QTableWidgetItem(money))
        self.tableWidget.setRowCount(cnt_row + 1)
        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()

    #수입 지출에 대한 tableInsert
    def insertItem(self, date, inout, category, money):
        cnt_row = self.tableWidget.rowCount()
        
        if "수입" in inout:
            self.table_to_csv(cnt_row, date, inout, category, money)
            self.tableWidget.setItem(cnt_row - 1, 0, QtWidgets.QTableWidgetItem(date))
            self.tableWidget.setItem(cnt_row - 1, 1, QtWidgets.QTableWidgetItem("수입"))
            self.tableWidget.setItem(cnt_row - 1, 2, QtWidgets.QTableWidgetItem(category))
            self.tableWidget.setItem(cnt_row - 1, 3, QtWidgets.QTableWidgetItem(money))
            self.tableWidget.setRowCount(cnt_row + 1)

        elif "지출" in inout:
            self.table_to_csv(cnt_row, date, inout, category, money)                       
            self.tableWidget.setItem(cnt_row - 1, 0, QtWidgets.QTableWidgetItem(date))
            self.tableWidget.setItem(cnt_row - 1, 1, QtWidgets.QTableWidgetItem("지출"))
            self.tableWidget.setItem(cnt_row - 1, 2, QtWidgets.QTableWidgetItem(category))
            self.tableWidget.setItem(cnt_row - 1, 3, QtWidgets.QTableWidgetItem(money))
            self.tableWidget.setRowCount(cnt_row + 1)

        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()
  • csv_insertItem() 메서드는 테이블 위젯에 새로운 행을 추가하고 데이터를 설정하는 역할 + 이 메서드는 date, inout, category, money 매개변수다.

  • cnt_row = self.tableWidget.rowCount(): rowCount() 메서드를 사용하여 현재 테이블 위젯의 열 개수를 가져옵니다. cnt_row 변수에 저장

  • inout 값이 "수입"인지 "지출"인지 확인

 def insertItem(self, date, inout, category, money):
        cnt_row = self.tableWidget.rowCount()
        
        if "수입" in inout:
            self.table_to_csv(cnt_row, date, inout, category, money)
            self.tableWidget.setItem(cnt_row - 1, 0, QtWidgets.QTableWidgetItem(date))
            self.tableWidget.setItem(cnt_row - 1, 1, QtWidgets.QTableWidgetItem("수입"))
            self.tableWidget.setItem(cnt_row - 1, 2, QtWidgets.QTableWidgetItem(category))
            self.tableWidget.setItem(cnt_row - 1, 3, QtWidgets.QTableWidgetItem(money))
            self.tableWidget.setRowCount(cnt_row + 1)
  • "수입"인 경우
  1. self.table_to_csv(cnt_row, date, inout, category, money): table_to_csv() 메서드를 호출하여 CSV 파일에 해당 행을 저장 테이블 위젯의 각 셀에 데이터를 설정
  2. self.tableWidget.setRowCount(cnt_row + 1): 테이블 위젯의 행 개수를 증가
        elif "지출" in inout:
            self.table_to_csv(cnt_row, date, inout, category, money)                       
            self.tableWidget.setItem(cnt_row - 1, 0, QtWidgets.QTableWidgetItem(date))
            self.tableWidget.setItem(cnt_row - 1, 1, QtWidgets.QTableWidgetItem("지출"))
            self.tableWidget.setItem(cnt_row - 1, 2, QtWidgets.QTableWidgetItem(category))
            self.tableWidget.setItem(cnt_row - 1, 3, QtWidgets.QTableWidgetItem(money))
            self.tableWidget.setRowCount(cnt_row + 1)

        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()
  • "지출"인 경우:
  1. self.table_to_csv(cnt_row, date, inout, category, money): table_to_csv() 메서드를 호출하여 CSV 파일에 해당 행을 저장

  2. self.tableWidget.setRowCount(cnt_row + 1): 테이블 위젯의 행 개수를 증가시킨다.

  • self.tableWidget.resizeColumnsToContents(): 테이블 위젯의 열 너비를 콘텐츠에 맞게 조정

  • self.tableWidget.resizeRowsToContents(): 테이블 위젯의 행 높이를 콘텐츠에 맞게 조정

  • 셀에 날짜, 지출/수입 표시, 카테고리, 금액을 설정하여 테이블에 데이터를 표시하는 역할

step5[setTableWidgetData]

    #tableWidget dataInit    
    def setTableWidgetData(self):
        column_headers = ['날짜', '수입/지출', '카테고리', '금액']
        self.tableWidget.setHorizontalHeaderLabels(column_headers)
        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()

def inputInc / inputExp

수입금액 기입을 위한 버튼액션

    # 수입금액 기입을 위한 버튼액션
    def inputInc(self):
        date = self.calendarWidget.selectedDate()
        date = str(date.toString())
        money = str(self.incLine.text()) # 수입금액
        category = str(self.incComBx.currentText()) # 수입 분류
        current_inc = int(str(self.totalIncLb.text()).split(' ')[2])
        print(current_inc)
        current_inc += int(money)
        self.totalIncLb.setText("총 수입: " + str(current_inc))
        self.insertItem(date, "수입", category, money)
        
    # 지출금액 기입을 위한 버튼액션
    def inputExp(self):
        date = self.calendarWidget.selectedDate()
        date = str(date.toString())
        money = str(self.expLine.text()) # 지출 금액
        category = str(self.expComBx.currentText()) # 지출 분
        current_out = int(str(self.totalExpLb.text()).split(' ')[2])
        current_out += int(money)
        self.totalExpLb.setText("총 지출: " + str(current_out))
        self.insertItem(date, "지출", category, money)

def setTableWidgetData

tableWidget dataInit

    #tableWidget dataInit    
    def setTableWidgetData(self):
        column_headers = ['날짜', '수입/지출', '카테고리', '금액']
        self.tableWidget.setHorizontalHeaderLabels(column_headers)
        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()
  • setHorizontalHeaderLabels 함수를 사용하여 테이블 위젯의 가로 헤더에 열 제목을 설정 ->테이블 위젯의 각 열의 제목은 '날짜', '수입/지출', '카테고리', '금액'으로 설정
  • resizeColumnsToContents 함수는 각 열의 너비를 열 내용에 맞게 조정 resizeRowsToContents 함수는 각 행의 높이를 행 내용에 맞게 조정

def inputInc

수입금액 기입을 위한 버튼액션

    def inputInc(self):
        date = self.calendarWidget.selectedDate()
        date = str(date.toString())
        money = str(self.incLine.text()) # 수입금액
        category = str(self.incComBx.currentText()) # 수입 분류
        current_inc = int(str(self.totalIncLb.text()).split(' ')[2])
        print(current_inc)
        current_inc += int(money)
        self.totalIncLb.setText("총 수입: " + str(current_inc))
        self.insertItem(date, "수입", category, money) 

date = self.calendarWidget.selectedDate(): calendarWidget 위젯에서 선택된 날짜를 가져와서 date 변수에 저장
(self는 자기자신을 뜻하지만, 객체의 고유한 속성값을 의미한다.)

self.calendarWidget는 QCalendarWidget 클래스의 인스턴스인 calendarWidget 객체를 나타냅니다. selectedDate는 QCalendarWidget 클래스에서 제공되는 메서드

date = str(date.toString()): date 변수를 문자열로 변환하여 저장
date.toString()은 PyQt에서 사용되는 메서드로, QDate 객체인 date의 문자열 표현을 반환 그 후에 str() 함수를 사용하여 PyQt에서 반환된 문자열을 파이썬의 문자열로 변환

    # 지출금액 기입을 위한 버튼액션
    def inputExp(self):
        date = self.calendarWidget.selectedDate()
        date = str(date.toString())
        money = str(self.expLine.text()) # 지출 금액
        category = str(self.expComBx.currentText()) # 지출 분
        current_out = int(str(self.totalExpLb.text()).split(' ')[2])
        current_out += int(money)
        self.totalExpLb.setText("총 지출: " + str(current_out))
        self.insertItem(date, "지출", category, money)
        
  • money : expLine은 지출 금액이 입력되는 줄 편집 위젯입니다. text 메서드를 사용하여 텍스트 값을 가져와서 문자열로 변환
  • category : expComBx는 지출 분류가 선택되는 콤보 박스 위젯

  • self.totalExpLb.text(): totalExpLb 레이블 위젯의 텍스트 값을 가져옵니다. text()는 해당 위젯의 텍스트 값을 반환하는 메서드다. + str(...): 텍스트 값을 문자열로 변환

    -> .split(' '): 문자열을 공백을 기준으로 분할

    -> split 함수는 인자로 전달된 구분자를 기준으로 문자열을 분할하고, 분할된 부분 문자열을 리스트로 반환합니다. 위 코드에서는 공백을 구분자로 사용하여 텍스트를 분할하고, 분할된 부분 문자열 중에서 두 번째 부분을 선택

    -> [2]: 리스트에서 두 번째 요소를 선택합니다. 리스트의 인덱스는 0부터 시작하므로, [2]는 세 번째 요소를 의미합니다. 위 코드에서는 split 함수로 분할된 부분 문자열 중에서 세 번째 요소를 선택

  • 요약하면, self.totalExpLb 레이블 위젯의 텍스트 값을 가져와서 공백으로 분할한 후, 그 중에서 세 번째 요소를 정수로 변환하여 current_out 변수에 저장

  • 현재 총 지출 금액을 업데이트한다. totalExpLb는 총 지출 금액을 표시하는 레이블 위젯

self.insertItem(date, "지출", category, money)
  • insertItem 메서드를 호출하여 지출 정보를 테이블 위젯에 삽입합니다. date, "지출", category, money는 각각 날짜, 수입/지출, 카테고리, 금액을 나타냅니다

step6[delList]

수입/지출 목록 삭제를 위한 버튼액션

    # 수입/지출 목록 삭제를 위한 버튼액션
    def delList(self):        
        # print("cnt_row" ,  cnt_row)
        if self.tableWidget.rowCount() > 1:   
            self.tableWidget.removeRow(self.tableWidget.currentRow())
            cnt_row = self.tableWidget.rowCount()
            f = open("household_ledger.csv", 'w', encoding = 'utf-8', newline = '')
            f.close()
            income, outcome = 0, 0
            for i in range(0, cnt_row - 1):
                date = self.tableWidget.item(i, 0).text()
                inout = self.tableWidget.item(i, 1).text()
                category = self.tableWidget.item(i, 2).text()
                money = self.tableWidget.item(i, 3).text()
                if '수입' in inout:
                    income += int(money)
                elif '지출' in inout:
                    outcome += int(money)
                self.table_to_csv(i, date, inout, category, money)

            self.totalIncLb.setText("총 수입: " + str(income))
            self.totalExpLb.setText("총 지출: " + str(outcome))
  • 행(row) 개수가 1보다 큰 경우에만 삭제 작업을 수행한다. [최소한 1개의 행을 유지하려는 작업이다.]

    self.tableWidget.removeRow(self.tableWidget.currentRow())는 현재 선택된 행을 테이블 위젯에서 삭제 + self.tableWidget.currentRow()는 현재 선택된 행의 인덱스를 반환

  • cnt_row = self.tableWidget.rowCount()는 삭제 후 남은 행의 개수를 cnt_row 변수에 저장

  • f = open("household_ledger.csv", 'w', encoding='utf-8', newline='')는 'household_ledger.csv' 파일을 쓰기 모드로 연다. 이 파일은 가계부 정보를 저장하는 용도다.

  • for i in range(0, cnt_row - 1):은 0부터 cnt_row - 1까지의 범위에서 반복문을 실행한다. 이 범위는 삭제 후 남은 행의 개수를 기준으로 한다.

  • 반복문 내부에서는 각 행의 날짜, 수입/지출 내용, 카테고리, 금액을 변수에 저장하고, 해당 정보를 CSV 파일에 기록 self.table_to_csv() 함수를 사용하여 테이블의 정보를 CSV 파일로 저장

  • self.totalIncLb.setText("총 수입: " + str(income))과 self.totalExpLb.setText("총 지출: " + str(outcome))은 수입과 지출의 총합을 나타내는 레이블을 갱신합니다. setText() 함수를 사용하여 총 수입과 총 지출을 문자열로 설정

  • 위의 코드는 주어진 테이블 위젯에서 선택된 행을 삭제하고, 남은 행들의 정보를 CSV 파일에 다시 저장하며, 수입과 지출의 총합을 계산하여 화면에 출력하는 역할

Step7) if name == "main"

if __name__ == "__main__": # 현재 모듈의 이름이 저장되는 내장 변수  이 한 줄의 코드를 통해 프로그램이 직접 실행되는지 혹은 모듈을 통해 실행되는지를 확인
    app = QtWidgets.QApplication(sys.argv) #모든 PyQt5 어플리케이션은 어플리케이션 객체를 생성해야한다.
    mainWindow = QtWidgets.QMainWindow()
    ui = Ui_mainWindow()#인스턴스 생성
    ui.setupUi(mainWindow)#ui라는 객체에서 setupui메서드 실행 + mainwindow라는 객체를 전달한다. 
    mainWindow.show()
    ui.csv_load()#메서드 실행 
    sys.exit(app.exec_())
    
  • QtWidgets.QApplication(sys.argv) 부분은 QtWidgets이라는 모듈에서 QApplication 클래스를 가져와 app이라는 객체를 생성했다.
    이 부분은 인스턴스가 아니라 모듈에서 클래스를 가져와 객체를 생성한 부분이다. 헷갈리기 x

  • mainWindow도 동일하다. QtWidgets.QMainWindow() 부분도 QtWidgets 모듈에서 QMainWindow() 클래스를 참조해 mainwindow라는 객체를 생성했다.

  • ui는 위 클래스인 Ui_mainWindow() 인스턴스를 생성했다.

  • ui.setupUi(mainWindow)는 ui라는 객체에서 setupUi메서드 실행 -> mainwindow는 객체다 [상속 x]

  • 이 코드에서는 setupUi() 메서드가 생성자 역할이다.

    생성자는 클래스가 인스턴스화될 때 자동으로 호출되는 메서드로서, 클래스의 초기화 작업을 담당한다.

    Ui_mainWindow 클래스의 setupUi() 메서드는 mainWindow 객체를 전달받아 UI를 초기화하고 구성 요소를 설정한다.

    이 메서드 내에서 UI 요소들이 생성되고 초기 설정이 이루어진다. 그래서 setupUi() 메서드가 생성자 역할을 하며, mainWindow 객체를 전달하여 UI를 초기화하는 역할을 수행한다.

profile
아는만큼보인다.

0개의 댓글