ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • PyQt를 사용하여 원형 진행바(Round ProgressBar) 만들기
    PyQt Protect/SmartFac: PyQt를 이용한 스마트 팩토리 인터페이스 개발 2023. 6. 21. 22:45

    이걸 만들어 볼꺼에요.

     

    Goal : 원형 진행바(Round ProgressBar) 위젯을 만들어봅시다.

     

    Purpose : 

    •  Smart Factory에서 자동화 프로세스가 어느정도 진행되었는지 알려줄 목적으로 개발합니다. Round ProcessBar는 시간의 흐름을 시각적으로 표현하는데 있어서 강점을 가지고 있습니다. 특히나, Smart Factory에서는 특정 공정의 진행 상황을 운전원에게 보여줌으로써 운전원이 취해야할 다음 조치 또는 운전의 계획을 세우는데 도움될 것입니다.

     

    Code Devleopment Process :

     

    1) 간단한 Window 위젯을 만들어줍시다.

    import sys
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setWindowTitle('Round ProcessBar')
            self.setGeometry(0, 0, 200, 200)
            
    if __name__ == '__main__':
       app = QApplication(sys.argv)
       ex = Window()
       ex.show()
       sys.exit(app.exec_())

    가로 200, 세로 200 크기를 가진 간단한 윈도우가 생성되었습니다.

     

    2) Painter 이벤트를 사용해서 뒤에 배경을 채워봅시다.

    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setWindowTitle('Round ProcessBar')
            self.setGeometry(300, 300, 200, 200)
            
        def paintEvent(self, a0: QPaintEvent) -> None:
            painter = QPainter(self)
            # 1. 배경 색칠하기
            painter.setBrush(QBrush(QColor(19, 33, 65, 255), Qt.SolidPattern))
            painter.drawRect(self.rect())
            painter.end()

     

    • paintEvent는 PyQt 및 기타 Qt 기반 프레임워크에서 그래픽 렌더링을 관리하는 중요한 메소드입니다. PyQt에서 위젯은 자신의 모양을 그리는 데 사용되는 paint event를 받습니다. paintEvent 메소드는 이 이벤트가 발생할 때 호출되며, 이 메소드 내에서 그래픽 렌더링 코드를 정의합니다.
    • 위에 코드에서 특별히 창 크기의 변화가 발생하였을때 이를 감지하기 위해서 self.rect()을 사용합니다. self.rect()은 Window 위젯의 사각형 크기를 반환합니다.
    • 창은 사각형이라는 가정하에 배경을 색칠합니다. 색은 RGB (19, 33, 65) 로하고 투명도는 255를 주어 불투명하게 색칠하였습니다. 그리고 아래 Ref를 참조하여 Qt.SolidPattern 로 채워줍시다.

     

    Ref [https://doc.qt.io/qt-6/qbrush.html]
    아직은 멍청해요.

     

    3) Painter 이벤트를 사용해서 원을 그려줍니다.

        def paintEvent(self, a0: QPaintEvent) -> None:
            painter = QPainter(self)
            # 1. 배경 색칠하기
            painter.setBrush(QBrush(QColor(19, 33, 65, 255), Qt.SolidPattern))
            painter.drawRect(self.rect())
            # 2. 원 그린다음 책칠하기
            painter.setRenderHint(QPainter.Antialiasing)
            tkEllipseLine = 5 # 원 두께
            painter.setPen(QPen(QColor(37, 177, 228, 255), tkEllipseLine))
            new_rect = QRect(tkEllipseLine, tkEllipseLine, self.rect().width() - tkEllipseLine*2, self.rect().height() - tkEllipseLine*2)
            painter.drawEllipse(new_rect)
            painter.end()

     

    • 개발과정에서 약간에 단점을 발견했는데, 원을 그리는 painter.drawEllipse()에서 self.rect()을 입력하면 원이 잘리는 현상이 발생합니다. 이를 보완하기 위해서 원의 두께를 나타내는 tkEllipseLine 변수를 하나 만들어 주고 new_rect을 만들어줍시다. new_rect은 새롭게 범위를 지정하고 지정한 범위 내에 원을 그리기 위해서 만들었습니다.
    • painter.setRenderHint(QPainter.Antialiasing)을 주어 선을 부드럽게 그려줍시다.

     

    조금 나아졌어요.

     

    3) 이제 Progress 원을 Arc를 사용하여 그려줍시다.

    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setWindowTitle('Round ProcessBar')
            self.setGeometry(300, 300, 200, 200)
            self.value = 50 # [0 ~ 100%]
            
        def paintEvent(self, a0: QPaintEvent) -> None:
            painter = QPainter(self)
            # 1. 배경 색칠하기
            painter.setBrush(QBrush(QColor(19, 33, 65, 255), Qt.SolidPattern))
            painter.drawRect(self.rect())
            # 2. 원 그린다음 책칠하기
            painter.setRenderHint(QPainter.Antialiasing)
            tkEllipseLine = 5 # 원 두께
            painter.setPen(QPen(QColor(37, 177, 228, 255), tkEllipseLine))
            new_rect = QRect(tkEllipseLine, tkEllipseLine, self.rect().width() - tkEllipseLine*2, self.rect().height() - tkEllipseLine*2)
            painter.drawEllipse(new_rect)
            # 3. Progress 원 그리기
            start_angle = 90 * 16 # [0 ~ 5760]
            span_angle = int(self.value * 3.6 * 16) # [0 ~ 5760]
            painter.setPen(QPen(QColor(255, 255, 255, 255), tkEllipseLine))
            painter.drawArc(new_rect, start_angle, -span_angle)
            painter.end()

     

    • self.value 라는 값을 만들어줍시다. 이 값은 0 ~ 100% 사이값을 가집니다.
    • Arc의 각도의 범위는 0 ~ 5760을 가집니다. 이를 각도로 환산하면 "원하는 각도 * 16" 입니다. 이 범위를 0~100 사이로 표현하기 위해서 span_angle은 self.value * 3.6 * 16 을 통해 계산된 값으로 사용합니다.

     

    이제 좀 나아지긴했습니다.

     

    4) 화면 가운데에 현재 값이 나올 수 있도록 text를 집어 넣습니다.

    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setWindowTitle('Round ProcessBar')
            self.setGeometry(300, 300, 200, 200)
            self.value = 50 # [0 ~ 100%]
            
        def paintEvent(self, a0: QPaintEvent) -> None:
            painter = QPainter(self)
            # 1. 배경 색칠하기
            painter.setBrush(QBrush(QColor(19, 33, 65, 255), Qt.SolidPattern))
            painter.drawRect(self.rect())
            # 2. 원 그린다음 책칠하기
            painter.setRenderHint(QPainter.Antialiasing)
            tkEllipseLine = 5 # 원 두께
            painter.setPen(QPen(QColor(37, 177, 228, 255), tkEllipseLine))
            new_rect = QRect(tkEllipseLine, tkEllipseLine, self.rect().width() - tkEllipseLine*2, self.rect().height() - tkEllipseLine*2)
            painter.drawEllipse(new_rect)
            # 3. Progress 원 그리기
            start_angle = 90 * 16 # [0 ~ 5760]
            span_angle = int(self.value * 3.6 * 16) # [0 ~ 5760]
            painter.setPen(QPen(QColor(255, 255, 255, 255), tkEllipseLine))
            painter.drawArc(new_rect, start_angle, -span_angle)
            # 4. Value 값 text 표시하기
            painter.setFont(QFont("Arial", 25, QFont.Bold, False))
            painter.drawText(new_rect, Qt.AlignmentFlag.AlignCenter, f'{self.value}%')
            painter.end()

     

    • 아래 2줄을 추가합니다. 
      • painter.setFont(QFont("Arial", 25, QFont.Bold, False))
        • 폰트 타입은 "Arial" 입니다.
        • 폰트 크기는 25 입니다.
        • 폰트에 Bold 를 주기위해 QFont.Bold 를 사용합니다.
        • Italic 체를 안쓰기 위해 False를 줍니다.
      • painter.drawText(new_rect, Qt.AlignmentFlag.AlignCenter, f'{self.value}%')
        • new_rect 크기 만큼 텍스트 영역을 지정합니다.
        • Qt.AlignmentFlag.AlignCenter로 해서 사각형의 중앙에 배치합니다.
        • f'{self.value}%' 으로 값을 표기합니다.

     

    * 아래 테이블은 폰트 Bold와 관련한 정보입니다.

    QFont::Thin 100 100
    QFont::ExtraLight 200 200
    QFont::Light 300 300
    QFont::Normal 400 400
    QFont::Medium 500 500
    QFont::DemiBold 600 600
    QFont::Bold 700 700
    QFont::ExtraBold 800 800
    QFont::Black 900 900

    완성했습니다!

     

    5) 마지막으로 키보드 입력을 넣어서 한번 동작시켜봅시다.

        def keyPressEvent(self, a0: QKeyEvent) -> None:
            if a0.key() == Qt.Key.Key_Up:
                self.value = self.value + 1 if 0 <= self.value < 100 else self.value
            if a0.key() == Qt.Key.Key_Down:
                self.value = self.value - 1 if 0 < self.value <= 100 else self.value
            self.update()

     

    • keyPressEvent 함수를 작성합니다. 키보드에서 Key_Up을 누르면 self.value 를 +1 합니다. 반면 Key_Down을 누르면 -1 을 합니다.
    • 값이 변경된 것을 반영하기 위해서 self.update()를 수행합니다.

     


    잘동작합니다.

     


    • 전체 코드
    더보기
    import sys
    from PyQt5 import QtGui
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setWindowTitle('Round ProcessBar')
            self.setGeometry(300, 300, 200, 200)
            self.value = 50 # [0 ~ 100%]
            
        def paintEvent(self, a0: QPaintEvent) -> None:
            painter = QPainter(self)
            # 1. 배경 색칠하기
            painter.setBrush(QBrush(QColor(19, 33, 65, 255), Qt.SolidPattern))
            painter.drawRect(self.rect())
            # 2. 원 그린다음 책칠하기
            painter.setRenderHint(QPainter.Antialiasing)
            tkEllipseLine = 5 # 원 두께
            painter.setPen(QPen(QColor(37, 177, 228, 255), tkEllipseLine))
            new_rect = QRect(tkEllipseLine, tkEllipseLine, self.rect().width() - tkEllipseLine*2, self.rect().height() - tkEllipseLine*2)
            painter.drawEllipse(new_rect)
            # 3. Progress 원 그리기
            start_angle = 90 * 16 # [0 ~ 5760]
            span_angle = int(self.value * 3.6 * 16) # [0 ~ 5760]
            painter.setPen(QPen(QColor(255, 255, 255, 255), tkEllipseLine))
            painter.drawArc(new_rect, start_angle, -span_angle)
            # 4. Value 값 text 표시하기
            painter.setFont(QFont("Arial", 25, QFont.Bold, False))
            painter.drawText(new_rect, Qt.AlignmentFlag.AlignCenter, f'{self.value}%')
            painter.end()
        
        def keyPressEvent(self, a0: QKeyEvent) -> None:
            if a0.key() == Qt.Key.Key_Up:
                self.value = self.value + 1 if 0 <= self.value < 100 else self.value
            if a0.key() == Qt.Key.Key_Down:
                self.value = self.value - 1 if 0 < self.value <= 100 else self.value
            self.update()
                    
    if __name__ == '__main__':
       app = QApplication(sys.argv)
       ex = Window()
       ex.show()
       sys.exit(app.exec_())
Designed by Tistory.