1. 동기화(Synchronization)란?

  • 다중 프로그래밍(multi-programming) 시스템에서는 다수의 프로세스(스레드 포함)가 동시에 실행
  • 동시에 실행되는 프로세스간에 공유하는 자원이나 데이터가 존재할 경우 문제가 발생할 수 있음
  • 프로세스가 서로 비동기적이라고 함은 프로세스가 서로의 존재를 모르고 독립적으로 동작중임을 의미한다.
  • 프로세스의 실행 순서를 맞추거나 공유 자원에 대한 접근 정책을 설정여 문제를 해결하는 것을 동기화라고 한다.

 

 

2. 상호 배제(Mutual Exclusion)

  • 코드에서 여러 프로세스가 공유하는 데이터에 접근하는 부분을 임계 영역(Critical Section)라고 한다.
  • 임계 영역에 여러 프로세스가 동시에 들어가지 않도록 하는 것을 상호 배제(Mutual Exclusion)라고 한다.

  • 기본 연산
    • Lock
      • 임계영역에 진입하기 위한 연산
      • 임계영역에 진입한 다른 프로세스가 존재하지 않을 경우 진입
      • 진입하는 즉시 다른 프로세스가 들어오지 못하도록 함

    • Unlock
      • 임계영역에서의 작업을 마치고 빠져나오는 연산
      • 임계영역에 다른 프로세스가 들어올 수 있도록 비어있음을 알림

  • 상호배제 구현의 조건
    • 상호 배제(Mutual Exclusion)
      • 말그대로 임계영역에 진입한 프로세스가 있다면 다른 프로세스는 진입할 수 없도록 해야함

    • 진행(Progress)
      • 임계영역에 진입한 프로세스가 없는 상태이고 하나 이상의 프로세스가 임계영역에 진입하고자
        하는 상황일 경우 어느 프로세스를 임계영역에 진입시킬지 정해진 시간 내에 선택해야함

    • 한정된 대기(Bounded Waiting)
      • 프로세스의 임계영역으로의 진입까지의 대기시간은 유한해야한다.

  • 상호배제 구현의 방식
    • 소프트웨어적 해결책(SW Solution)
      • 개발자가 소프트웨어 수준에서 직접 상호배제를 보장하는 코드를 작성하는 방식
      • Dekker's Algorithm, Peterson's Algorithm, Dijkstra's Algorithm 등이 있다.

    • 하드웨어적 해결책(HW Solution)
      • 하드웨어 수준에서 자체적으로 상호배제를 보장하도록 설계하는 방식
      • TestAndSet(TAS) 구조 등이 있다.

    • OS에서 지원하는 소프트웨어적 해결책(OS Supported SW Solution)
      • 운영체제가 자체적으로 지원하는 소프트웨어적 해결책
      • 스핀 락(Spin Lock), 뮤텍스(Mutex), 세마포어(Semaphore) 등이 있다.

    • 언어 수준에서의 해결책(Language-Level Solution)
      • 프로그래밍 언어에서 자체적으로 지원하는 소프트웨어적 해결책
      • 자바의 모니터(Monior)가 대표적이다.

'CS > 운영체제' 카테고리의 다른 글

#6 동기화(Synchronization) 3 - HW Solution  (0) 2021.12.24
#5 동기화(Synchronization) 2 - SW Solution  (0) 2021.12.23
#3 프로세스 스케줄링(Scheduling)  (0) 2021.12.21
#2 스레드(Thread)  (0) 2021.12.20
#1 프로세스(Process)  (0) 2021.12.14

1. 성능(Performance) 판단의 지표 (용어정리)

  • CPU 사용률(CPU Utilization)
    • 단위 시간동안 프로세서가 작업중인 시간의 비율
    • 사용률이 높을수록 CPU를 효율적으로 사용하고 있는 상태

  • 처리율(Throughput)
    • 단위 시간당 완료하는 작업의 수

  • 반환 시간(Turnarount Time)
    • 요청된 작업이 완료될 때 까지의 시간
    • 종료 시간 - 도착 시간

  • 대기 시간(Waiting Time)
    • 작업이 Ready Queue 에서 대기하는 시간
    • 시작 시간 - 도착 시간

  • 반응 시간(Response Time)
    • 요청된 작업에 반응이 돌아올 때 까지의 시간
    • 첫 응답이 돌아온 시간 - 도착 시간
  • 실행 시간(Burst Time)
    • 요청된 작업이 실제로 실행된 시간
    • 종료 시간 - 시작 시간

 

 

2. 스케줄링의 기준

  • 스케줄링 기법의 선택을 위해 고려해야할 항목

  • 프로세스의 특성
    • 프로세서의 작업비중과 입출력(I/O) 작업비중 중에서 어느쪽이 높은지를 고려

  • 시스템의 특성
    • 실시간, 대화형 시스템의 경우 반응 시간이 핵심 지표가 된다.
    • 일괄 처리 시스템(batch system) 등의 경우 단위 시간당 처리율(Throughput)이 핵심지표가 된다.

  • 프로세스의 긴급성(Urgency)
    • hard-realtime : 지정된 응답시간 내에 반응에 실패할 경우 심각한 피해가 발생하는 시스템
    • soft-realtime : 지정된 응답시간 내에 반응에 실패할 경우 성능저하가 발생할뿐 동작엔 문제가 없는 시스템
    • non-realtime : 실시간 시스템이 아닌 경우

  • 프로세스의 우선순위(Priority)

  • 프로세스의 실행시간(Service Time)

 

 

3. 스케줄링의 단계

  • Short-Term 스케줄링
    • Ready Queue에서 대기중인 프로세스중 어느 프로세스에게 프로세서(CPU)를 할당할지 결정
    • 일반적으로 말하는 스케줄링은 단기 스케줄러에 의한 스케줄링을 의미함
    • 가장 자주 발생하는 스케줄링이기 때문에 성능에 미치는 영향도 매우큼 => 속도가 매우 빨라야한다.

  • Mid-Term 스케줄링
    • 너무 많은 프로세스에게 메모리를 할당하여 시스템 성능이 저하될 경우
      메모리에 적재된 프로세스의 수를 유동적으로 조절하

    • 메모리에 적재될 프로세스중 일부를 선택하여 컨텍스트를 디스크의 스왑 영역에
      저장해두고 메모리를 해제한다. 이를 스왑 아웃(Swap Out)이라 한다.

  • Long-Term 스케줄링
    • 메모리에 동시 적재 가능한 프로세스의 수(multi-programming degree) 조절
      => Ready Queue 에 들어갈 프로세스의 수 조절

    • 현대의 시분할 시스템이나 실시간/대화형 시스템의 경우 장기 스케줄러를 두지 않거나 최소화시킴
      • 시분할 시스템의 경우 모든 프로세스에 공평하게 정해진 시간동안 자원을 분배하기 위해,
        실시간/대화형 시스템의 경우 요청을 받았을 때 정해진 시간 내에 응답하기 위해 바로 메모리를
        할당하고 Ready Queue에 넣어야하기 때문에 Long-Term 스케줄링의 의미가 없기 때문
    • 일괄 처리 시스템(batch system)에 적합하다.

 

 

4. 스케줄링 정책

  • 비선점 스케줄링(Non-preemptive Scheduling)
    • 한번 자원을 할당받고나면 실행이 완료되고 이를 반납하기 전까지 사용할 수 있음

    • 보다 우선순위가 높은 프로세스가 도착하더라도 자신의 작업은 끝까지 수행할 수 있음

    • 한 작업이 모두 수행되기 전까지 다른 작업에 대해 응답할 수 없기에 평균 응답시간이 길어짐

    • 우선순위가 높은 작업이 먼저 실행되기 시작한 우선순위가 낮은 작업에 밀리는 경우가 자주 발생

    • 하나의 프로세스가 실행을 마칠 때 까지 Context Switching이 발생하지 않아 오버헤드가 적음

  • 선점 스케줄링(Preemptive Scheduling)
    • 자원을 할당받은 후 실행이 아직 완료되지 않았어도 보다 우선순위가 높은 프로세스가 도착하거나
      자신의 사용시간이 만료되면 자원을 반납해야함

    • 선점에 의한 Context Switching 이 자주 발생 => 오버헤드가 큼
    • 시분할 시스템이나 실시간/대화형 시스템에는 이 방식이 적합

  • 우선순위(Priority)의 결정
    • 정적 우선순위(Static Priority)
      • 프로세스 생성시 변하지 않는 우선순위를 지정
      • 구현이 쉽고 overhead가 적음
      • 상황 변화에 따른 유연한 대처 불가

    • 동적 우선순위(Dynamic Priority)
      • 프로세스의 상태 변화에 따라 Priority 변경
      • 구현이 복잡하고 Priority 계산을 위한 overhead가 발생
      • 상황 변화에 따른 유연한 대처 가능

 

 

5. 스케줄링 알고리즘

 ① 비선점형 스케줄링 알고리즘

  • FCFS(First Come First Served)
    • 대기열에 먼저 들어온 프로세스를 우선적으로 실행하는 알고리즘

    • 장점
      • 구현이 매우 간단하며 오버헤드 또한 거의 없음
      • 작업이 종료될 때를 제외하면 Context Switching이 발생하지 않아 CPU 사용률이 매우 높다
      • 도착한 작업은 자신의 순서가 오면 반드시 실행되기에 starvation(기아) 이 발생하지 않는다.

    • 단점
      • 평균 응답시간이 매우 길다
      • 하나의 오래걸리는 작업으로 인해 다른 작업들의 대기시간이 길어지는 문제

  • SJF(Shortest Job First)
    • 실행시간(Burst Time)이 가장 적은 프로세스가 우선적으로 실행되는 알고리즘
    • 장점
      • 평균 대기시간 최소화
      • 시스템 내 프로세스의 수를 최소화(실행시간이 짧은 작업들이 빠르게 종료되기 때문)

    • 단점
      • 프로세스의 실행시간을 정확히 예측하는 것이 현실적으로 어려움
      • 실행시간이 긴 프로세스가 계속해서 우선순위에서 밀리는 starvation(기아)이 발생

  • HRRN(Highest Response Ratio Next)
    • Response Ratio 공식에 따라 계산된 우선순위가 높은 프로세스를 우선적으로 실행하는 알고리즘
    • Response Ratio = (Service Time + Waiting Time) / Service Time
    • 서비스 시간이 짧을수록, 대기시간이 길수록 우선순위가 높아지는 방식
    • 대기시간의 증가에 따라 우선순위가 높아져 starvation이 발생하지 않도록 하는 에이징(aging) 기법을 사용

 

 ② 선점형 스케줄링 알고리즘

  • RR(Round Robin)
    • FCFS의 선점형 버전
    • 기본적으로는 대기열에 가장 먼저 도착한 프로세스를 먼저 처리
    • 대신 자원의 사용 시간에 제한을 두어 시간이 만료되면 자원을 반납하고 대기열의 맨 뒤로 간다

    • 장점
      • 특정 프로세스에 의한 자원 독점의 방지
      • 평균 응답시간이 길어지는 문제 해결

    • 단점
      • 잦은 Context Switch로 인한 오버헤드가 큼
    • 시분할 시스템이나 실시간/대화형 시스템에 적합
  • SRT(Shortest Remaining Time)
    • SJF의 선점형 버전 
    • 남은 실행시간이 가장 적은 프로세스를 먼저 처리
    • 남은 실행시간이 더 적은 프로세스가 대기열에 들어오면 그 프로세스에 의해 자원이 선점당함

    • 장점
      • SJF의 장점(대기시간의 최소화, 시스템 내 프로세스 수의 최소화)을 극대화

    • 단점
      • SJF와 마찬가지로 실행시간(Burst Time)의 측정이 어려움
      • 남은 실행시간(Remaining Time)을 지속적으로 추적해야함

  • MLQ(Multi Level Queue)
    • 프로세스들을 그룹화하여 그룹마다 독자적인 큐(대기열)를 사용하는 스케줄링 기법
    • 프로세스는 작업의 특성(작업의 중요도, 빠른 응답시간이 요구되는지 등)에 따라 분류
    • 각 큐는 그 특성에 맞는 독자적인 스케줄링 기법을 적용
    • 한번 배정된 큐에서는 이동할 수 없음
    • 큐 사이에는 우선순위 기반의 스케줄링 사용

    • 장점
      • 작업의 특성에 따라 적합한 스케줄링 기법을 독립적으로 적용 가능

    • 단점
      • 여러개의 큐를 관리해야하는 만큼 스케줄링의 오버헤드가 보다 큼
      • 우선순위가 낮은 큐의 starvation이 발생할 수 있음
      • 처음에 프로세스를 적절한 큐에 배정하지 못하면 이동이 불가능하기에 프로세스에 대한 사전정보가 필요

  • MFQ(Multi Level Feedback Queue)
    • 프로세스가 처음 배정된 큐에서 다른 큐로 이동 가능해진 MLQ 방식
    • Feedback 을 통해 우선순위를 조정
    • 프로세스에 대한 사전정보를 알지 못해도 HRN과 비슷한 효과를 볼 수 있음

    • 장점
      • MLQ가 그러했듯이 각 큐마다 별도의 스케줄링 정책을 사용하는 것으로
        프로세스의 특성에 적합한 스케줄링 방식 적용이 가능

      • 입출력 위주(I/O Bounded)의 프로세스를 상위 큐로 이동
        => I/O 위주의 프로세스는 CPU 사용시간이 짧기 때문(적은 service time)

      • 대기시간이 길어진 프로세스를 상위 큐로 이동(Aging)
        => starvation 의 방지

    • 단점
      • 복잡한 설계와 구현
      • 다중 큐의 관리로 인한 오버헤드

 

'CS > 운영체제' 카테고리의 다른 글

#5 동기화(Synchronization) 2 - SW Solution  (0) 2021.12.23
#4 동기화(Synchronization) 1 - 상호 배제의 개념  (0) 2021.12.23
#2 스레드(Thread)  (0) 2021.12.20
#1 프로세스(Process)  (0) 2021.12.14
#0 컴퓨터의 구조  (0) 2021.12.13

1. 스타일 시트 적용

import sys

from PySide6.QtWidgets import QApplication

from widgets import main_window

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyleSheet("TableView { border: 1px solid; font-size: 15px; margin: 0px; padding: 0px; }"
                      "QHeaderView::section:horizontal {"
                      " font-size: 15px;"
                      " font-weight: bold;"
                      " border: 1px solid;"
                      " margin: 0px;"
                      " border-bottom: 2px solid "
                      "}"
                      "QHeaderView::section:horizontal:last {"
                      " font-size: 15px;"
                      " font-weight: bold;"
                      " border: 1px solid;"
                      " margin: 0px;"
                      " border-bottom: 2px solid;"
                      " border-right: 2px solid }"
                      "QHeaderView::section:vertical {"
                      " font-size: 15px;"
                      " font-weight: bold;"
                      " border: 1px solid;"
                      " margin: 0px;"
                      " border-right: 2px solid }"
                      "QTableCornerButton::section {"
                      " border: 1px solid;"
                      " margin: 0px;"
                      "}"
                      )
    screen = main_window.MainWindow()
    sys.exit(app.exec())
  • main.py 에서 QApplication 인스턴스에 위와 같이 스타일시트를 설정하여 앱 내부의 해당 위젯 전체에 적용

 

 

2. 헤더 크기 조정

# 일일 정산서 계산서 위젯
class DayCal(QWidget):
    # 생성자
    def __init__(self):
        super().__init__()
        # 화주명단, 화주별 데이터, 기타 데이터, 결과 데이터를 DB 에서 읽어온다
        self.owner_list = actions.get_daycal_owner_list()
        self.owner_values = actions.get_daycal_owner_values()
        self.other_values = actions.get_daycal_other_values()
        self.result = actions.get_daycal_result()

        # 입력 테이블 생성(화주별 데이터)
        self.input_table = TableView(0)
        self.data_model = DayCalTableModel(self, actions.get_daycal_owner_list(), actions.get_daycal_owner_values())
        self.input_table.setModel(self.data_model)
        self.input_table.verticalHeader().setMinimumWidth(170)

        # 기타 테이블 생성(기타 데이터)
        self.other_table = TableView(1)
        self.other_data_model = DayCalOthersTableModel(self, actions.get_daycal_other_values())
        self.other_table.setModel(self.other_data_model)
        self.other_table.verticalHeader().setMinimumWidth(170)
        self.other_table.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)

        # 결과 테이블 생성
        self.result_table = TableView(2)
        self.result_data_model = DayCalResultTableModel(self, actions.get_daycal_result())
        self.result_table.setModel(self.result_data_model)
        self.result_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

        self.init_ui()

	''' 생략 '''
  • input_table과 other_table에 대해 각각 setMinimumWidth 를 호출하여 수평 헤더의 너비를 동일하게 맞춤

  • other_table, result_table에 대해 각각 setSectionResizeMode(QHeaderView.Stretch) 를 호출하여
    오른쪽, 아래쪽으로 남는공간이 없도록 헤더 사이즈를 맞춘다.

  • 같은 작업을 DayCalQueryResult 위젯에서도 적용하여 과거 데이터 조회시에도 같은 형태가 되도록 한다.

 

디자인 변경 전

 

디자인 변경 후

위 작업들을 마치면 위와 같이 테이블의 디자인이 변경된 것을 확인할 수 있다.

'개인 프로젝트 > Accounting Program' 카테고리의 다른 글

#20 데이터 조회 - 수정기능 구현  (0) 2021.12.07
#19 데이터 조회기능 구현  (0) 2021.12.01
#18 저장기능 구현  (0) 2021.11.25
#17 QTableView  (0) 2021.11.22
#16 화주 삭제, 화주 이름 변경  (0) 2021.11.02

1. 스레드(Thread)란?

  • 프로세스 내에서 실행되는 작업의 단위
  • 운영체제의 스케줄러에 의해 관리될 수 있는 최소 작업단위
  • 하나의 프로세스에는 다수의 스레드가 존재할 수 있다.
  • 하나의 프로세스에 속한 스레드들은 프로세스가 운영체제로부터 할당받은 자원을 공유한다.
  • 단, Thread ID(tid)나 PC(Program Counter), SP(Stack Pointer) 등의 Register Set 등의
    제어요소나
    스택 영역의 경우 공유하지 않고 각 스레드가 독립적으로 가지게된다.
  • 경량 프로세스(Light Weight Process)라고도 불린다.

 

 

2. 스레드의 장점

  • 응답 속도 증가
    • 일부 스레드의 처리가 지연되더라도 다른 스레드가 작업을 계속할 수 있음
  • 자원 공유
    • 제어요소나 스택영역을 제외한 자원을 공유하기에 Context Switch에 의한 오버헤드가 대폭 감소
    • 프로세스를 새로 생성하는 것에 비해 경제적

  • 멀티 프로세서 활용
    • CPU 내의 여러개의 코어를 동시에 활용하여 병렬적으로 작업을 처리하는것으로 성능 향상

 

 

3. 스레드의 구현

  • 사용자 수준 스레드(User-Level Thread)
    • 스레드의 생성, 스케줄링 등을 사용자 영역의 라이브러리로 구현
    • 커널과는 무관하게 구현되어있기 때문에 커널의 개입을 받지않음

    • 장점
      • 생성, 관리(스케줄링, 동기화 등)에 커널이 개입하지 않기에 오버헤드가 적음
      • 라이브러리 형태로 지원되는 기능인만큼 이식성이 좋다

    • 단점
      • IO가 발생한 스레드가 하나라도 존재할 경우 모든 스레드가 대기상태가 된다.

  • 커널 수준 스레드(Kernel-Level Thread)
    • 커널에서 직접 스레드를 관리
    • 스케줄링의 최소단위가 스레드가 됨

    • 장점 
      • 각 스레드가 커널에 의해 개별적으로 관리되기에 하나의 스레드가 대기상태가 되어도
        다른 스레드는 정상적으로 작업을 수행할 수 있음

    • 단점
      • 스레드의 생성, 관리에 커널이 개입하기에 사용자 수준 스레드에 비해 오버헤드가 크다.

 

 

4. 멀티 스레드 모델

  • n:1 모델
    • 다중 사용자 스레드 : 단일 커널 스레드
    • 사용자 스레드만을 사용하는 모델

  • 1:n 모델
    • 단일 사용자 스레드 : 다중 커널 스레드
    • 커널 스레드만을 사용하는 모델

  • n:m 모델
    • 다중 사용자스레드 : 다중 커널 스레드
    • 사용자 스레드와 커널 스레드를 모두 사용하는 혼합형 모델
    • 사용자 입장에서는 원하는 만큼의 스레드를 생성하여 사용
    • 커널 입장에서는 다수의 사용자 스레드를 그보다 적은 수의 커널 스레드가 분할하여 담당
    • 실제 OS에서는 이 모델이 사용되고있다.

 

1. 프로세스(Process)란?

  • 하나, 또는 여러개의 스레드(thread)로 실행되는 작업의 단위
  • 컴퓨터 프로그램의 인스턴스(instance)
  • 효율적인 실행을 위해 운영체제의 커널(kernal)에 의해 관리
  • 운영체제로부터 PCB(Process Control Block)를 할당받고 이를 통해 운영체제의 관리를 받게된다.
  • 프로세스의 작업은 필요한 프로세서(CPU), 메모리, 주변기기 등의 자원을 운영체제에 요청, 할당받아 실행된다. 

 

 

2. 프로세스 제어 블록(Process Control Block)

  • 운영체제가 프로세스를 제어하기 위해 프로세스에게 할당하는 데이터 블록
  • PCB의 정보를 어떻게 참조하고 갱신하냐에 따라 OS의 성능이 크게 갈린다.

 

  • 프로세스 실행에 필요한 데이터를 보관
    • PID(Process IDentifider)
      • 프로세스의 고유 식별자

    • 프로세스의 상태(Process State)
      • 생성, 실행, 준비, 대기, 종료 등 프로세스의 상태 정보

    • PC(Program Counter)
      • 다음에 실행될 명령어의 위치를 나타내는 포인터

    • 레지스터 정보(Register Inforamation)
      • 프로세스가 실행중이었을 때의 CPU의 레지스터 정보를 저장해두는 영역

    • 스케줄링 정보(Scheduling Information)
      • 프로세스의 우선순위 등 스케줄링에 필요한 정보

    • 메모리 관리 정보(Memory Management Information)
      • 페이지 테이블, 세그먼트 테이블, 메모리 제한 등의 메모리 관리를 위한 정보

    • 입출력 정보(I/O Information)
      • 프로세스가 할당받은 주변장치나 파일에 대한 정보

    • 계정 정보(Accounting Information)
      • CPU 사용시간, 시간제한, 실행 ID 등의 정보

 

  • 모든 프로세스는 생성시에 고유의 PCB를 할당받고 이는 프로세스의 종료시 반환된다.
    PCB의 일부 정보는 프로세스가 종료된 후에도 잠시동안 소멸되지 않고 남아있다.

 

  • Context Switching
    • 인터럽트가 발생할 경우 실행중인 프로세스의 처리를 중단하고 PCB에 프로세스의
      상태를 저장(Context Saving)하고 다음으로 실행될 프로세스의 정보를 레지스터에
      적재(Context Restoring)하는 과정

    • 인터럽트의 발생 요인
      • 기계적 고장, 전원부족 등 프로그램 외적인 결함(External Interrupt)
      • CPU 사용시간 만료(Timeout), 입출력 요청의 완료 등 외부로부터의 인터럽트 요청
      • 입출력 요청(I/O Request)이나 자식 프로세스 생성(Fork Child) 등의 커널 서비스 요청(System Call)
      • 프로그램의 내부적 결함, 즉 예외(Exception)
    • Context Switching의 오버헤드
      • Context Switching 이 발생하는 동안 CPU는 아무 작업도 실행하지 않음
      • 자주 발생할 수록 오버헤드로 인해 성능 저하를 가져올 수 있음

 

 

3. 프로세스의 상태(Process State)와 전이

 ① 프로세스의 상태

  • 생성(New / Create) : 프로세스가 생성되어 PCB를 할당받은 상태
  • 준비(Ready) : 프로세스가 실행 준비를 마치고 CPU만 할당받으면 언제든 실행 가능한 상태
  • 실행(Run) : 프로세스가 CPU를 할당받아 실행되고있는 상태
  • 대기(Block / Wait) : 프로세스가 인터럽트의 처리가 완료되길 대기하고있는 상태
  • 종료(Terminated) : 프로세스의 실행이 종료된 상태

 

 ② 프로세스의 상태 전이(Process State Transition)

출처: https://www.d.umn.edu/~gshute/os/processes-and-threads.xhtml

  • Creation
    • 생성(New)된 프로세스가 준비 상태(Ready)가 되어 대기열에 들어감

  • Dispatch
    • 준비상태(Ready)의 프로세스가 CPU를 할당받아 실행상태(Run)에 들어감

  • Timeout
    • 실행(Run)중이던 프로세스가 운영체제로부터 할당받은 CPU 사용시간을 모두 소진하여
      준비상태(Ready)로 되돌아감

  • Blocking
    • 실행(Run)중이던 프로세스가 입출력 요청, 시그널 대기 등으로 인해 발생한 인터럽트의 처리가
      완료될 때 까지 대기상태(Block)에 들어감

  • Unblocking / Wake up
    • 인터럽트의 처리가 완료되어 대기상태(Block)였던 프로세스가 다시 준비상태(Ready)가 되어
      대기열에 들어감

  • Termination
    • 실행(Run)중이던 프로세스가 작업을 마치고 종료상태(Terminated)에 들어감

1. 프로세서(Processor) - CPU

 ① CPU(Central Processing Unit)

  • 컴퓨터의 중앙 프로세서
  • 컴퓨터의 모든 장치의 동작을 제어

 

 ② 레지스터(Register)

  • 프로세서 내부의 메모리
  • 프로세서가 사용하기 위한 데이터를 저장
  • 컴퓨터를 구성하는 메모리중 가장 빠름
  • 사용자 가시 레지스터
    • 데이터 레지스터(Data Register)
      • 함수 연산에 필요한 데이터를 저장
      • 산술/논리 연산에 사용하며 연산 결과로 플래그값 저장

    • 주소 레지스터
      • 주소나 유효 주소를 계산하기 위해 필요한 주소의 일부분을 저장
      • 주소 레지스터에 저장한 값을 사용하여 산술연산 가능

  • 사용자 불가시 레지스터
    • 프로그램 카운터(PC - Program Counter)
      • 다음에 실행할 명령어의 주소를 보관하는 레지스터
      • 실행할 명령어를 메모리에서 읽어오면 명령어 길이만큼 증가, 다음명령어를 가리킴
      • 분기명령어는 목적 주소로 갱신

    • 령어 레지스터(IR - Instruction Register)
      • 현재 실행중인 명령어를 보관하는 레지스터

    • 누산기(ACC - Accumulator)
      • 데이터를 일시적으로 저장하는 레지스터

    • 메모리 주소 레지스터(MAR - Memory Address Register)
      • 프로세서가 참조하려는 데이터의 주소를 명시하여 메모리에 접근하는 버퍼 레지스터

    • 메모리 버퍼 레지스터(MBR - Memory Buffered Register)
      • 프로세서가 메모리에서 읽거나 메모리에 저장할 데이터를 보관하는 버퍼 레지스터
      • 메모리 데이터 레지스터(MDR - Memory Data Register)라고도 함

  • 프로세서는 위의 다양한 레지스터들과 산술/논리 연산장치인 ALU(Arithmetic Logic Unit)
    사용하여 연산을 수행하고 시스템 버스로 연결된 입출력장치나 메모리와 데이터를 주고받는다.

  • 운영체제는 사용자가 실행한 응용프로그램들이 효율적으로 동작하도록 CPU 자원을 적절히
    할당하고 회수하는 등의 작업을 수행한다.

 

 

2. 메모리(Memory)

  • 데이터를 저장하기 위한 기억장치

  • 컴퓨터를 구성하는 메모리의 종류 - 아래로 내려갈수록 느리지만 값이 싸고 용량이 크다
    • 레지스터(Register)
      • 프로세서 내부의 가장 빠른 메모리
    • 캐시(Cache)
      • 프로세스 내부의 L1, L2 캐시 등의 메모리
      • CPU와 메인메모리 사이의 병목현상 해소

    • 메인 메모리(Main Memory)
      • 프로세서가 수행할 프로그램과 데이터 저장
      • CPU 처리속도에 비해 너무 느린 디스크 입출력 속도로 인한 병목 현상을 해소

    • 보조 기억 장치(Auxiliary Memory)
      • 데이터를 사용하기 위해서는 메인 메모리로 가져와야하는 보조 메모리
      • 디스크나 SSD 등이 대표적

  • 캐시의 동작
    • 일반적으로 하드웨어적으로 관리
    • 캐시 히트(Cache Hit)
      • 필요한 데이터 블록이 캐시메모리 내에 존재
      • 바로 가져와 사용

    • 캐시 미스(Cache Miss)
      • 필요한 데이터 블록이 캐시메모리 내에 존재하지 않음
      • 메인 메모리에서 데이터를 가져와야함

    • 지역성(Locality)
      • 공간적 지역성(Spatial Locality)
        • 참조한 주소와 인접한 주소를 참조하는 특성
        • 순차적 프로그램 수행 등

      • 시간적 지역성(Temporal Locality)
        • 한 번 참조한 주소를 곧 다시 참조하는 특성
        • 반복문 등의 순환문

      • 지역성을 고려하여 캐싱 알고리즘을 적절히 구성하면 높은 캐시 적중률을 얻을 수 있다

  • 운영체제는 응용프로그램이 필요로하는 만큼의 메모리를 효율적으로 할당해주는 작업을 수행한다.

 

 

3. 주변 장치

  • 입출력 장치(I/O Device) : 키보드, 마우스, 모니터 등 데이터를 입력, 출력하기 위한 장치

  • 저장 장치(Storage) : 하드디스크, SSD, USB, CD 등의 보조기억장치

  • 운영체제는 응용프로그램의 입출력 요청이나 저장 장치에 대한 읽고쓰기 요청 등을
    효율적으로 처리하여 그 결과를 응용프로그램에게 전달해주는 작업을 수행한다.

 

 

1. DayCalQueryResult 위젯 수정

# 날짜로 조회한 데이터를 표시하기 위한 위젯
# 기본적으로 DayCal 위젯과 거의 동일한 구조를 가짐
class DayCalQueryResult(QDialog):
    submitted = Signal()

    # 생성자
    def __init__(self, parent, owner_values, other_values, result, today):
        super().__init__(parent)
        # 화주별 데이터, 기타 데이터, 결과 데이터, 조회날짜를 인자로 받아온다
        self.owner_values = owner_values
        self.other_values = other_values
        self.result = result
        self.save = QPushButton('저장')
        self.save.clicked.connect(lambda: self.submitted.emit())
        self.today = today

        # 화주별 데이터를 기반으로 임시 화주 명단을 생성
        self.owner_list = []
        for values in self.owner_values:
            self.owner_list.append(DayCalOwner(values.get_owner_name(), values.get_owner_type(), values.get_owner_id()))

        # 입력 테이블 생성(화주별 데이터)
        self.input_table = TableView()
        self.data_model = DayCalTableModel(self, self.owner_list, self.owner_values)
        self.input_table.setModel(self.data_model)

        # 기타 테이블 생성(기타 데이터)
        self.other_table = TableView()
        self.other_data_model = DayCalOthersTableModel(self, self.other_values)
        self.other_table.setModel(self.other_data_model)

        # 결과 테이블 생성
        self.result_table = TableView()
        self.result_data_model = DayCalResultTableModel(self, self.result)
        self.result_table.setModel(self.result_data_model)

        self.init_ui()

    # ui 초기화
    def init_ui(self):
        # 그리드 레이아웃
        grid = QGridLayout()

        # 테이블위젯 추가
        grid.addWidget(self.input_table, 0, 0)
        grid.addWidget(self.other_table, 1, 0)
        grid.addWidget(self.result_table, 0, 1, 2, 1)
        grid.addWidget(self.save, 2, 1)
        grid.setRowStretch(0, 5)
        grid.setColumnStretch(0, 5)

        # 레이아웃 세팅
        self.setLayout(grid)

        # 초기 윈도우사이즈 설정
        self.setGeometry(100, 100, 1446, 620)

        # 스타일 설정
        self.setStyleSheet("background-color: #FFFFFF")

        # 타이틀 설정
        self.setWindowTitle("일일정산서 계산서: " + self.today)
  • save 버튼을 생성, 클릭시 submitted 시그널을 발생시키도록 설정
  • 버튼을 레이아웃에 추가

 

 

2. date_query 함수 수정

# 날짜로 데이터 조회
def date_query(parent, tab):
    date_select = DateSelect(parent)
    date_select.show_modal()

    if not date_select.canceled:
        if tab == 0:
            today = date_select.calendar.selectedDate().toString('yyyy-MM-dd')
            result = DayCalQueryResult(parent, get_daycal_owner_values(today), get_daycal_other_values(today), get_daycal_result(today), today)
            result.submitted.connect(save)
            result.show()
  • date_query 함수에서 DayCalQueryResult 위젯을 생성했을 때 submitted 시그널에 save 함수를 연결
  • save 함수에서는 db 세션에 커밋을 실행하여 변경된 데이터를 저장

 

위 두가지 작업을 수행하는 것 만으로 간단하게 조회한 과거 데이터에 대한 수정기능을 구현할 수 있다.

다만, 화주의 경우 과거 데이터의 화주는 현재 화주 리스트 테이블과 일치하지 않기때문에 당연히 수정할 수 없다.

'개인 프로젝트 > Accounting Program' 카테고리의 다른 글

#21 디자인 변경  (0) 2021.12.20
#19 데이터 조회기능 구현  (0) 2021.12.01
#18 저장기능 구현  (0) 2021.11.25
#17 QTableView  (0) 2021.11.22
#16 화주 삭제, 화주 이름 변경  (0) 2021.11.02

1. SSL

  • HTTP는 기본적으로 플레인 텍스트로 전송하기에 보안에 취약

  • TLS(Transport Layer Security), 혹은 SSL(Secure Socket Layer) 이라 불리는 프로토콜을 사용하여
    전송계층간의 통신시에 데이터를 암호화하는 것으로 데이터 유출을 방지한다.

  • TCP의 3-way handshaking 절차 이후에 SSL handshaking 과정을 거쳐 암호화 통신이 가능한 상태가 된다.

  • SSL handshaking
    • Client Hello
      • 클라이언트는 서버에 사용 가능한 암호화 방식들과 랜덤 데이터를 전송

    • Server Hello
      • 서버는 클라이언트가 보낸 암호화 방식중 하나를 선택
      • 선택한 암호화 방식을 자신이 생성한 랜덤 데이터와 함께 클라이언트에게 전송

    • Certificate
      • 서버는 CA(인증기관)에서 발급받은 SSL 인증서를 클라이언트에게 전송
      • 이때, 인증서 내에 자신이 발급한 공개키를 첨부(개인키는 자신이 보관)

    • Server Key Exchange
      • Certificate 과정에서 클라이언트에게 보낸 인증서에 서버의 공개키가 포함되지 않았다면 직접 클라이언트에게 공개키를 전송

    • Server Hello Done
      • 서버가 행동을 마쳤음을 클라이언트에게 알림

    • Client Key Exchange
      • 클라이언트는 CA의 공개키를 사용하여 서버가 보낸 인증서를 검증
      • 인증서가 검증되면 실제로 데이터를 암호화하기 위한 대칭키를 생성
      • 대칭키를 서버가 보낸 공개키로 암호화하여 서버에게 전송
      • 전송된 대칭키는 중간에 가로채더라도 서버의 공개키로 암호화되어있어 서버의 개인키 없이는 복호화가 불가능하므로 서버와 클라이언트는 안전하게 암호화키를 공유하게 됨

    • Change Cipher Spec / Finished
      • 클라이언트와 서버가 암호화 통신을 위한 준비가 완료되었음을 알리기 위해 서로 번갈아가며 패킷을 전송.

 

 

2. HTTPS

  • 기존의 HTTP에 SSL 프로토콜을 추가하여 보안성을 추가한 확장 프로토콜
  • 443번 포트를 사용

'CS > 네트워크' 카테고리의 다른 글

#10 네트워크 보안 1 - 암호화  (0) 2021.12.06
#9 Wireless Network & Mobile Network  (0) 2021.11.30
#8 Link Layer(Data Link Layer + Physical Layer)  (0) 2021.11.25
#7 Network Layer  (0) 2021.11.18
#6 Transport Layer  (0) 2021.11.16

1. 대칭키 암호화(Private Key Cyptography)

  • 하나의 비밀키를 사용하여 plain text 를 암호화하고 복호화하는 방식
  • 구조가 매우 단순하여 구현이 쉬움
  • 키를 공유하는 과정에서 유출될 경우 보안이 무의미해짐

 

 

2. 공개키 암호화(Public Key Cyptography)

  • 남에게 공개하지 않는 개인키와 모두에게 공개하는 공개키를 사용
  • 어떤 사람의 공개키를 사용하여 암호화된 데이터는 그 사람의 개인키를 사용해야 복호화할 수 있음
  • 어떤 사람의 개인키를 사용하여 암호화된 데이터는 그 사람의 공개키를 사용해야 복호화할 수 있음
  • 키를 교환할 필요 없이 상대의 공개키를 사용하여 암호화한 뒤 데이터를 전송하면 보안이 보장됨
  • 암호화/복호화에 걸리는 시간이 대칭키 암호화방식에 비해 훨씬 길다 
  • DSA, RSA, ECC 등의 알고리즘이 있다.

 

 

3. 인증(Authentication) - 전자서명

  • 통신중인 상대가 정말 본인이 맞는지 확인이 필요
  • 공개키 암호화를 응용하여 본인을 인증
  • 상대에게 랜덤 데이터를 보내고 상대는 이를 자신의 개인키로 암호화하여 전송
  • 상대의 공개키로 이 데이터가 정상적으로 복호화된다면 상대가 본인임을 인증할 수 있음

 

 

4. 인증기관(Certificate Authority)

  • 공개키 암호화를 사용한 인증방식에도 문제가 있음
  • 만약 상대가 보낸 암호화된 랜덤데이터와 공개키 모두 전혀 다른사람의 것이어도 알아챌 수 없음
  • 이를 보장해줄 신뢰성있는 제 3자가 필요 => 인증기관의 필요성
  • 인증기관(CA)은 자신의 비밀키로 암호화된 인증서를 제공하고 사용자는 이를 CA의 공개키로 복호화,
    인증기관이 발행한 인증서임을 확인함과 동시에 인증서를 통해 식별하고자 하는 상대방의 공개키와
    정보를 얻게된다. 이를 통해 전자서명을 통한 상대방의 본인 여부를 확인할 수 있다.

 

 

'CS > 네트워크' 카테고리의 다른 글

#11 네트워크 보안 2 - HTTPS  (0) 2021.12.07
#9 Wireless Network & Mobile Network  (0) 2021.11.30
#8 Link Layer(Data Link Layer + Physical Layer)  (0) 2021.11.25
#7 Network Layer  (0) 2021.11.18
#6 Transport Layer  (0) 2021.11.16

이번에는 달력 위젯에서 날짜를 선택하여 해당 날짜의 데이터를 조회하는 기능을 구현한다. 

 

1. DayCalOwnerValues 모델 변경

# 화주별 일일정산 데이터 모델
class DayCalOwnerValues(Base):
    __tablename__ = 'daycal_owner_values'
    date = Column(Date, primary_key=True)
    owner_id = Column(Integer, primary_key=True)
    owner_name = Column(String, nullable=False)
    owner_type = Column(Integer, nullable=False)
    kd_total = Column(Integer, nullable=False)
    kd_fare = Column(Integer, nullable=False)
    kd_drop = Column(Integer, nullable=False)
    kd_fee4 = Column(Integer, nullable=False)
    after_deduction = Column(Integer, nullable=False)
    match_fee5 = Column(Integer, nullable=False)
    owner_fare = Column(Integer, nullable=False)
    owner_drop = Column(Integer, nullable=False)
    listing_fee4 = Column(Integer, nullable=False)
    kd_pre = Column(Integer, nullable=False)
    deduction_total = Column(Integer, nullable=False)
    total_include_pre = Column(Integer, nullable=False)

    def __init__(self, date, owner_id, owner_name, owner_type):
        self.date = date
        self.owner_id = owner_id
        self.owner_name = owner_name
        self.owner_type = owner_type
        self.kd_total = 0
        self.kd_fare = 0
        self.kd_drop = 0
        self.kd_fee4 = 0
        self.after_deduction = 0
        self.match_fee5 = 0
        self.owner_fare = 0
        self.owner_drop = 0
        self.listing_fee4 = 0
        self.kd_pre = 0
        self.deduction_total = 0
        self.total_include_pre = 0

    def get(self, idx):
        if idx == 0:
            return self.kd_total
        elif idx == 1:
            return self.kd_fare
        elif idx == 2:
            return self.kd_drop
        elif idx == 3:
            return self.kd_fee4
        elif idx == 4:
            return self.after_deduction
        elif idx == 5:
            return self.match_fee5
        elif idx == 6:
            return self.owner_fare
        elif idx == 7:
            return self.owner_drop
        elif idx == 8:
            return self.listing_fee4
        elif idx == 9:
            return self.kd_pre
        elif idx == 10:
            return self.deduction_total
        elif idx == 11:
            return self.total_include_pre

    def set(self, idx, val):
        if idx == 0:
            self.kd_total = val
        elif idx == 1:
            self.kd_fare = val
        elif idx == 2:
            self.kd_drop = val
        elif idx == 3:
            self.kd_fee4 = val
        elif idx == 4:
            self.after_deduction = val
        elif idx == 5:
            self.match_fee5 = val
        elif idx == 6:
            self.owner_fare = val
        elif idx == 7:
            self.owner_drop = val
        elif idx == 8:
            self.listing_fee4 = val
        elif idx == 9:
            self.kd_pre = val
        elif idx == 10:
            self.deduction_total = val
        elif idx == 11:
            self.total_include_pre = val

    def get_owner_id(self):
        return self.owner_id

    def get_owner_name(self):
        return self.owner_name

    def get_owner_type(self):
        return self.owner_type
  • 예전 데이터를 가져올 경우 지금은 삭제된 화주가 그 당시 데이터에는 존재할 수도 있기 때문에
    화주 리스트를 DayCalOwnerValues 리스트를 토대로 임시로 생성해야한다.

  • 그러기 위해서는 DayCalOwner 객체의 정보가 DayCalOwnerValues 에도 존재해야하기 때문에
    DayCalOwnerValues 모델에 owner_type 필드를 추가하고 생성자도 수정해준다.

  • 물론 해당 모델의 인스턴스를 생성하는 메소드에서도 모두 매개변수로 화주 타입을 넘겨주도록 수정한다.

 

 

2. TimeLabel 위젯 수정

# 시간레이블 클래스(QLabel 상속)
# 1초마다 현재 날짜/시간을 갱신하여 표시하는 레이블
class TimeLabel(QLabel):
    clicked = Signal()

    def __init__(self):
        super().__init__()
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.timeout)
        self.timer.start(100)
        self.init_ui()

    def init_ui(self):
        self.setText(QDateTime.currentDateTime().toString('yyyy년 MM월 dd일 ddd hh:mm:ss'))

    def timeout(self):
        self.setText(QDateTime.currentDateTime().toString('yyyy년 MM월 dd일 ddd hh:mm:ss'))

    def mouseReleaseEvent(self, ev: QMouseEvent) -> None:
        self.clicked.emit()
  • 상태표시줄에 표시된 날짜/시간 부분을 클릭하면 데이터를 조회할 날짜를 선택하기 위한 달력 위젯이 표시되도록
    하기 위해 먼저 TimeLabel의 mouseReleaseEvent 를 오버라이드하여 clicked 시그널을 구현해준다.

 

 

3. MainWindow 수정

# 시간 레이블 추가
time_label = TimeLabel()
time_label.clicked.connect(lambda: actions.date_query(self, self.central_widget.get_selected_tab()))
status_bar.addPermanentWidget(time_label)
  • MainWindow의 time_label을 상태표시줄에 추가하는 부분에서 time_label의 clicked 시그널에 date_query를
    호출하는 람다함수를 연결해준다.  이 때, central_widget의 get_selected_tab 을 호출하여 현재 선택된 탭 정보를
    받아와 매개변수로 넘겨준다. 

 

 

4. CentralWidget 수정

# 중앙 위젯
class CentralWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.doc_tab = DocTab()
        self.doc_tab.setParent(self)
        self.init_ui()

    def init_ui(self):
        self.setStyleSheet("background-color: #FFFFFF")

        # 그리드 레이아웃
        grid = QGridLayout()

        # 탭 위젯 추가
        grid.addWidget(self.doc_tab, 0, 0)

        # 중앙 위젯에 그리드 레이아웃 적용
        self.setLayout(grid)

    def get_selected_tab(self):
        cur = self.doc_tab.currentWidget()
        return 0 if cur == self.doc_tab.tab1 else 1 if self.doc_tab.tab2 else 2
  • CentralWidget에 선택된 탭이 몇번 탭인지 반환하는 get_selected_tab 메소드를 구현한다

  • tab1이라면 0, tab2라면 1, tab3라면 2를 반환한다.

 

 

5. date_query 함수 작성

# 날짜로 데이터 조회
def date_query(parent, tab):
    date_select = DateSelect(parent)
    date_select.show_modal()

    if not date_select.canceled:
        if tab == 0:
            today = date_select.calendar.selectedDate().toString('yyyy-MM-dd')
            result = DayCalQueryResult(parent, get_daycal_owner_values(today), get_daycal_other_values(today), get_daycal_result(today), today)
            result.show()
  • 호출시에 매개변수로 전달받은 parent 를 부모 위젯으로 하여 DataSelect 다이얼로그를 생성, modal하게 표시한다
    => DataSelect 는 달력 위젯을 사용하여 유저가 선택한 날짜를 가져오기 위한 위젯이다.
  • DataSelect 다이얼로그가 정상적으로 제출되었을 경우 받아온 날짜를 기준으로 DayCalOwnerValues, 
    DayCalOtherValues, DayCalResult 정보를 DB에서 읽어온다.

  • 읽어온 정보를 기반으로 DayCalQueryResult 위젯을 생성하여 표시한다.
    => DayCalQueryResult 는 조회된 데이터를 테이블로 나타내주기 위한 위젯이다.

 

 

6. DataSelect 다이얼로그 구현

# 날짜로 데이터 조회를 위한 달력 위젯
# 다이얼로그(QDialog 상속)
class DateSelect(QDialog):
    def __init__(self, parent):
        super().__init__(parent)
        self.calendar = QCalendarWidget()
        self.submit = QPushButton('조회')
        self.submit.clicked.connect(self.success)
        self.canceled = True
        self.init_ui()

    def init_ui(self):
        grid = QGridLayout()
        grid.addWidget(self.calendar, 0, 0, 1, 3)
        grid.addWidget(self.submit, 1, 0, 1, 1)
        self.setLayout(grid)

    def show_modal(self):
        return super().exec_()

    def success(self):
        self.canceled = False
        self.close()
  • QDialog 를 상속하여 구현
  • 날짜선택을 위한 달력(QCalendarWidget)과 선택된 날짜정보를 제출하기 위한 버튼(QPushButton)을 가진 위젯.

 

 

7. DayCalQueryResult 구현

# 날짜로 조회한 데이터를 표시하기 위한 위젯
# 기본적으로 DayCal 위젯과 거의 동일한 구조를 가짐
class DayCalQueryResult(QDialog):
    # 생성자
    def __init__(self, parent, owner_values, other_values, result, today):
        super().__init__(parent)
        # 화주별 데이터, 기타 데이터, 결과 데이터, 조회날짜를 인자로 받아온다
        self.owner_values = owner_values
        self.other_values = other_values
        self.result = result
        self.today = today

        # 화주별 데이터를 기반으로 임시 화주 명단을 생성
        self.owner_list = []
        for values in self.owner_values:
            self.owner_list.append(DayCalOwner(values.get_owner_name(), values.get_owner_type(), values.get_owner_id()))

        # 입력 테이블 생성(화주별 데이터)
        self.input_table = TableView()
        self.data_model = DayCalTableModel(self, self.owner_list, self.owner_values)
        self.input_table.setModel(self.data_model)

        # 기타 테이블 생성(기타 데이터)
        self.other_table = TableView()
        self.other_data_model = DayCalOthersTableModel(self, self.other_values)
        self.other_table.setModel(self.other_data_model)

        # 결과 테이블 생성
        self.result_table = TableView()
        self.result_data_model = DayCalResultTableModel(self, self.result)
        self.result_table.setModel(self.result_data_model)

        self.init_ui()

    # ui 초기화
    def init_ui(self):
        # 그리드 레이아웃
        grid = QGridLayout()

        # 테이블위젯 추가
        grid.addWidget(self.input_table, 0, 0)
        grid.addWidget(self.other_table, 1, 0)
        grid.addWidget(self.result_table, 0, 1, 2, 1)
        grid.setRowStretch(0, 5)
        grid.setColumnStretch(0, 5)

        # 레이아웃 세팅
        self.setLayout(grid)

        # 초기 윈도우사이즈 설정
        self.setGeometry(100, 100, 1446, 620)

        # 스타일 설정
        self.setStyleSheet("background-color: #FFFFFF")

        # 타이틀 설정
        self.setWindowTitle("일일정산서 계산서: " + self.today)
  • 날짜로 조회한 데이터를 테이블의 형태로 표시해주기 위한 위젯

  • 기본적으로 DayCal 위젯과 거의 동일한 구조를 가진다.

  • 다른 점이라면 과거의 데이터를 가져오는 것이기 때문에 화주를 추가/삭제할 일이 없으며
    이미 삭제된 화주나 추가된 화주는 여기에 영향을 미치지 않는다

  • 테이블 모델에서 필요로하는 owner_list는 owner_values를 기반으로 임시로 생성한다.

 

 

전날의 데이터를 조회하면 테이블 형태로 어제의 데이터가 표시되는 것을 볼 수 있다. 
화주2는 오늘 추가되었기 때문에 전날의 데이터에는 나타나지 않는다.

'개인 프로젝트 > Accounting Program' 카테고리의 다른 글

#21 디자인 변경  (0) 2021.12.20
#20 데이터 조회 - 수정기능 구현  (0) 2021.12.07
#18 저장기능 구현  (0) 2021.11.25
#17 QTableView  (0) 2021.11.22
#16 화주 삭제, 화주 이름 변경  (0) 2021.11.02

+ Recent posts