본문 바로가기

❤25기/25기 세미나

[25기 세미나] PyQt5로 그림판 만들기(1)

진행에 앞서

본 강의는 오션코딩학원의 파이썬 예제 (그림판)에 설명을 붙인 강의입니다.

원글 주소: oceancoding.blogspot.com/2019/03/blog-post.html

 

파이썬 예제 (그림판)

python PyQt5 그림판

oceancoding.blogspot.com

미숙한 저작권 의식으로 처음 글을 올렸을 때 링크와 저작자를 표기하지 않은점 정말 죄송합니다.

다시 한 번 고개숙여 사과드립니다.


PyQt5와 개발환경 세팅하기

1. PyQt5란?

솔룩스의 2학기 첫 번째 온라인 세미나가 시작되었습니다!

이번 세미나의 주제는<PyQt5로 그림판 만들기>입니다.

먼저 PyQt5에 대해 알아봅시다!

PyQt란?

PyQt는 파이썬에서 GUI(Graphical User Interface)프로그래밍을 할 때 사용하는 대표적인 패키지입니다.

GUI란 무엇일까요?

GUI란마우스로 아이콘을 클릭하여 프로그램을 작동시키는 컴퓨팅 환경을 말합니다.

우리는 오늘 마우스로 클릭하여 작동시키는 그림판을 만들기 위해 GUI프로그래밍을 이용할 것입니다!

PyQt에서 Qt는 GUI프로그램 개발에 널리 쓰이는 크로스 플랫폼 프레임워크로서 주로 C++이라는 언어를 사용해 프로그래밍 합니다.

여기서 크로스 플랫폼은 여러 종류의 컴퓨터 플랫폼에서 동작할 수 있음을 의미하며,

프레임워크는 짧게 정리하자면 자주 쓰일만한 기능들을 모아 놓은 클래스들의 모음집이라는 의미입니다.

정리하자면 Qt는 마우스로 아이콘을 클릭하여 프로그램을 작동시키는 컴퓨팅 환경을 개발하는데 널리 쓰이며, 여러 종류의 컴퓨터 플랫폼에서 동작할 수 있고, 자주 쓰일만한 기능들을 모아 놓은 클래스의 모음집입니다.

그리고 이 Qt앞에 Py가 붙어서 PyQt는 Qt의 파이썬 버전임을 의미합니다.

2. PyQt5 설치하기

* 본 세미나는 PyCharm Edu를 사용한다는 가정하에 설명되어 있습니다. 만약 컴퓨터에 파이썬과 파이참이 설치되어있지 않다면 <SOLUX 1차 온라인 세미나(1)>의 내용을 참고해주시면 감사드리겠습니다.

그럼 그림판을 만들기전에 PyQt5를 사용하기위한 개발환경부터 세팅해봅시다!

그동안 개발환경 세팅 방법을 까먹으셨더라도 아래를 따라  세팅해봅시다.

1.먼저PyCharm을 실행해주세요!

2. File -> Setting에 들어갑시다!

3. Project Interpreter -> 오른쪽에 있는 + 버튼을 클릭합시다!

4. PyQt5를 검색한 뒤 Install Package를 클릭합시다!

5. Package ‘PyQt5’ installed successful이라고 뜨신다면

축하드립니다! PyQt5 설치에 성공하셨습니다! 

 

3. 모듈 불러오기

제일 처음으로 sys모듈을 불러옵시다!

import sys

sys 모듈은 파이썬 인터프리터가 제공하는변수와 함수를 직접 제어할 수 있게 해주는 모듈입니다. 

이번 세미나에서는명령행에서 인수를 전달하는 sys.argv와강제로 스크립트를 종료하는sys.exit를 이용해 콘솔에서 입력된 각종 변수를 저장하고 종료하는 메인함수에 필요합니다.

더  자세한 사항은 뒤에서 알아봅시다!

그다음 PyQt5의 모듈을 불러옵시다!

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

먼저QtCore란 Qt의 핵심 클래스로 사용자의 이벤트에 따라 동작하는 이벤트 처리를 위해 import하였습니다.

다음으로 QtGui란 그래픽 사용자 인터페이스 구성요소로 Gui프로그램을 만들기 위해 Import 한 모듈입니다.

마지막으로 QtWidgets란 기본적인 UI 구성요소를 제공하는 위젯을 포함하고 있는 모듈입니다. 


메인 윈도우 창 구성하기

1. 클래스를 만들어 전체 폼 박스의 좌, 우 레이아웃 잡기

 그림판은 2개의 클래스로 메인 윈도우 창을 구성하는 CWidget클래스와 그림을 표시하는 CView클래스로 만듭니다!

class CWidget(QWidget):

먼저Qt의Qwidget에서 상속받은 나만의 클래스를 만듭시다.

혹시1학기때 파이썬 기초를 배우며 익혔던 클래스와 상속의 개념을 기억하고 계시나요?

다시 한번 복습해보는 시간을 갖도록 합시다!

클래스란?

먼저 연산의 중심이 되는 것을‘객체’라고 합니다.함수라는‘틀’을 만든 것처럼 객체도 세부 정보만 조금씩 다르게 지정해 만들 수 있도록‘틀’을 만들 수 있는데 이 '틀'이 바로 클래스입니다.

클래스를 과자 틀에 비유해봅시다!

클래스(class)란 똑같은 무엇인가를 계속해서 만들어 낼 수 있는 설계 도면이고(과자 틀),객체(object)란 클래스로 만든 피조물(과자 틀을 사용해 만든 과자)을 뜻합니다.

과자 틀로 만든 과자에 구멍을 뚫거나 조금 베어 먹더라도 다른 과자에는 아무 영향이 없는 것과 마찬가지로 동일한 클래스로 만든 객체들은 서로 전혀 영향을 주지 않습니다. 

즉 객체마다 고유한 성격을 가진다는 것입니다.

상속이란?

상속(Inheritance)이란"물려받다"라는 뜻으로, "재산을 상속받다"라고 할 때의 상속과 같은 의미입니다.

클래스에도 이 개념을 적용할 수 있습니다.

어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있게 만드는 것입니다.

처음PyQt에 대해 설명할 때 클래스 모음집이라고 이야기 드린 것 기억하시나요?

앞서 본 첫 줄은 나만의 클래스 즉 '틀'을 만든 다음 그 '틀'을PyQt의QWidget에서 상속받아서 기능을 사용하는 것을 의미합니다!

def __init__(self):
    super().__init__()

CWidget의 생성자 함수에서 부모 클래스(QWidget)의 생성자를super()을 통해 호출해 줍시다!

--init--는 객체가 생성될 때 객체를 기본값으로 초기화하는 특수한 메서드로 객체가 생성될 때 자동 호출됩니다!

# 전체 폼 박스
formbox = QHBoxLayout()
self.setLayout(formbox)
# 좌, 우 레이아웃박스
left = QVBoxLayout()
right = QVBoxLayout()

여기서 질문이 드는 분이 계실 텐데요.

바로 '왜 규격 즉 크기와 출력 위치를 설정하지 않지?' 라는 의문이 드실 수 있습니다.

하지만 위젯의 크기와 출력 위치를 명시적으로 설정하면 윈도우 크기가 바뀔 때 문제가 생깁니다.

이러한 문제를 해결하고자PyQt에서는레이아웃 매니저를 제공하는데요!

앞서보신 QVBoxLayout, QHBoxLayout모두 이러한 레이아웃 매니저입니다.

QHBoxLayout이란?

위젯을행 방향으로 나열하는 레이아웃 매니저입니다

QVBoxLayout?

위젯을수직 방향으로 나열하는 레이아웃 매니저입니다.

 레이아웃?

레이아웃이란 GUI프로그래밍에서위젯을 배치하기 위한 방법 및 도구입니다.

그럼setLayout()은 무엇일까요?

이 메서드는해당 레이아웃을 다이얼로그에 설정하는 역할을 합니다.

다이얼로그?

다이얼로그는 GUI프로그래밍에서 사용자와의 상호작용을 위해 사용되는 윈도우를 의미합니다!

위에서는 만들어둔 레이아웃인formbox를 윈도우에 설정하는 역할로 쓰였네요!

위에 보시는 그림이 저희가 최종적으로 구현할 그림판인데요!

큰 파란색 테두리가 가장 바깥쪽 formbox(QHBoxLayout클래스)변수이며, 

좌, 우로 보이는 빨간색 테두리 2개가 각각 left(컨트롤 배치용), right(그림그리기용)으로 구성됩니다.

이번 시간에 배운 코드를 정리해봅시다!

class CWidget(QWidget):
    
    def __init__(self):

        super().__init__()

        formbox = QHBoxLayout()
        self.setLayout(formbox)

        left = QVBoxLayout()
        right = QVBoxLayout()

2. 그리기 종류 구성하기

 그럼 본격적으로 그림판에 컨트롤할 수 있는 박스를 만들어봅시다!

위 그림처럼 첫 번째 그룹박스를 만들어봅시다!

# 그룹박스1 생성 및 좌 레이아웃 배치
gb = QGroupBox('그리기 종류')        
left.addWidget(gb)

QGroupBox 란?

QGroupBox, 즉 그룹 박스는상단 타이틀과 단축키를 제공하며,그 안에다양한 위젯들을 나타낼 수 있는 클래스입니다.

또한체크 가능하도록 설정할 수 있는데,그룹 박스의 체크 여부에 따라 그 그룹 박스 안에 있는 위젯들이 사용 가능하거나 불가능해집니다.

addWidget메서드를 이용해서 위에서 만든 그룹박스인gb를 위에서 잡아두었던left라는 공간에 배치합시다!

# 그룹박스1 에서 사용할 레이아웃
box = QVBoxLayout()
gb.setLayout(box)

앞서배운 위젯을 수직으로 나열하는 레이아웃 매니저인 QVBoxLayout 와

setLayout을 이용하여 방금 만든 그룹박스 gb에 위젯을 수직으로 나열하라고 설정해 줍시다!

text = ['line', 'Curve', 'Rectange', 'Ellipse']

우리가 만들 그림판의 선의 이름을text에 저장한 후

self.radiobtns = []
for i in range(len(text)):
    self.radiobtns.append(QRadioButton(text[i], self))
    self.radiobtns[i].clicked.connect(self.radioClicked)
    box.addWidget(self.radiobtns[i])

radiobtns 라는 리스트를 만들어줍시다. 

그 후 for반복문을 통해 방금 만들었던 박스에 추가합니다!

라디오 버튼(QRadioButton 클래스)이란?

QRadioButton이란 사용자로부터여러 가지 옵션 중 하나를 입력받을 때 주로 사용합니다.

여기서는 사용자로부터 펜의 종류를 입력받기 위해 QRadioButton을 사용했음을 알 수 있습니다!

clicked.connect(함수) ?

(위에서의 self.radiobtns[i].clicked.connect(self.radioClicked)에 해당)

버튼이 눌렀을 때 어떤 기능을 하도록 하는 코드입니다. 즉기능을 하도록 시그널을 보내는 것인데요! 

생성자 내부에 작성해야만 라디오 버튼에서 버튼을 누른다는 시그널을 보낼 수 있습니다.

self.radioClicked는 임의로 지정한 함수입니다. 깔끔함을 위해 코드 후반에 넣지만 이해를 위해 잠시 앞으로 가져오겠습니다.

def radioClicked(self):
    for i in range(len(self.radiobtns)):
        if self.radiobtns[i].isChecked():
            self.drawType = i                
            break

위에서 clicked.connect(함수)를 통해시그널을 보낸다고 설명드렸는데요.

이는 곧 그림판에서 '내가 어떤 펜을 체크했나' 라는 시그널을 보내주는 것입니다!

이 함수는 그러한 시그널이 왔을 때,

즉 그리기 종류를 변경했을 때선택된 라디오 버튼의 번호를 알아내어drawType변수에 저장하는 역할을 합니다.

바로isChecked메서드를 이용해서self.radiobtns객체 중 어디에 선택되었는지를 확인하는 것입니다!

self.radiobtns[0].setChecked(True)

위 코드를 통해 기본 그리기 종류를self.radiobtns의 첫 번째 원소인‘line’으로 지정해 줍시다!

self.drawType = 0

그리고 어떤 그리기 종류가 현재 선택되어 있는지 저장할 변수drawType을 만들어0(직선)으로 초기화 합니다!

이번 시간에 작성한 코드를 정리해봅시다!

(self.radioClicked 함수는 나중에 따로 작성합니다!)

gb = QGroupBox('그리기 종류')        
left.addWidget(gb)
 
box = QVBoxLayout()
gb.setLayout(box)        
 
text = ['line', 'Curve', 'Rectange', 'Ellipse']
self.radiobtns = []
 
for i in range(len(text)):
    self.radiobtns.append(QRadioButton(text[i], self))
    self.radiobtns[i].clicked.connect(self.radioClicked)
    box.addWidget(self.radiobtns[i])
 
self.radiobtns[0].setChecked(True)
self.drawType = 0

3. 펜 설정 구성하기

위 그림처럼 두 번째 그룹박스를 만들어줍시다!

gb = QGroupBox('펜 설정')        
left.addWidget(gb)

위와 동일하게 그룹박스2를 생성하고 배치해 줍시다.

grid = QGridLayout()      
gb.setLayout(grid)  

위와 비슷한 것 같지만 다른 부분이 있습니다!

바로QVBoxLayout가 아닌QGridLayout가 사용되었다는 점인데요.

QGridLayout도QVBoxLayout같은레이아웃 매니저이지만 다른 점이 있습니다.

바로QGridLayout은격자 형태의UI를 구성하는 데 사용한다는 것입니다.

위에 사진을 보시면 전과는 다르게 2X2 격자 형태로 만들 것이기 때문에QGridLayout를 사용합니다.

label = QLabel('선굵기')
grid.addWidget(label, 0, 0)

선 굵기로 표시되는 라벨을 추가합니다.

addWidget메서드 안에 들어가는 내용이 바뀌었습니다. 왜일까요?

위에서는addwidget으로 어떠한 공간에 넣었는데요.

지금은grid를 사용하다 보니 어디에 추가할지 위치를 지정해 줘야 하는 필요성이 생긴 것입니다.

gridLayout.addWidget(추가할 위젯, row , col , 몇 줄 점유할까,몇 칸 점유할까)

따라서 이러한 방식으로 활용하게 됩니다.

그럼 위에 코드를 해석해보면0행 0줄에label이라는 위젯을 추가한다!라고 볼 수 있겠네요.

self.combo = QComboBox()
grid.addWidget(self.combo, 0, 1)       

for i in range(1, 21):
    self.combo.addItem(str(i))

1~20범위의 값을 가지는 콤보 박스를 추가합니다.

그럼 콤보박스(QComboBox)가 무엇인지 알아봅시다!

ComboBox란?

여러 개의 선택지 중 하나의 선택지를 선택하는 위젯을 말합니다.

설명만 보면 앞에서 봤던RadioButton과 유사한 기능을 하는 것처럼 보이는데요!

ComboBox는RadioButton과 다르게여러 개의 선택지가 목록으로 제공된다는 특징이 있으며,이 항목들에는리스트처럼Index가 존재한다는 특징이 있습니다.

따라서 콤보 박스를 사용하면 컴퓨터에서 날짜를 입력할 때 아래의 목록이 뜨는 것처럼 펜의 두께를 목록으로 보며 고를 수 있습니다!

즉 위의 두 줄은self.combo라는 위젯을 콤보박스로 만들어서0행1줄에 추가한다!라는 뜻이 되겠네요.

그리고 반복문을 사용해서1~20까지의 값을 위젯에 추가해 줍니다!

바로 함수addItem(String)통해서요!

addItem(String)함수는ComboBox의 맨 뒤에 항목을 추가합니다.

Parameter(매개변수)로 추가할 항목의 글자를 입력받습니다.

위 경우에는1~20까지의 숫자를 글자로 변환해서 입력받겠네요!

그럼 선 굵기가 완성되었으니 선 색상을 구현합시다.

label = QLabel('선색상')
grid.addWidget(label, 1,0)

선 색상으로 표시되는 라벨을 추가합니다.

위와 동일하지만 이번에는1행 0줄에 배치한 것을 볼 수 있습니다.

self.pencolor = QColor(0,0,0)

선색상을 구현해야 하니 색상 정보를 가져와야겠네요!

QColor란?

색상 정보를 갖고 있는 객체를 의미합니다.

QColor(Red, Green, Blue, Alpha)으로 사용하는데요Red,Green,Blue는 각각RGB값을 의미하며0부터255사이의 정수가 들어갑니다.

마지막Alpha는 투명도를 의미하는 값입니다!

0부터225사이의 정수가 들어가며 위의 코드처럼 설정하지 않을 시 기본값인255가 들어갑니다.

self.penbtn = QPushButton()    
self.penbtn.setStyleSheet('background-color: rgb(0,0,0)')
self.penbtn.clicked.connect(self.showColorDlg)
grid.addWidget(self.penbtn,1, 1)

선 색상으로 표시되는 라벨을 추가한 후 버튼(QPushButton 클래스)의 배경색을 변경하여 해당 색을 변경해 해당 버튼이 눌러지면 색상창을 열어서 색을 정한 후 다시 버튼을 색을 바꾸어 표시되는 방식으로 구현합니다.

QPushButton란 ?

푸시 버튼(push button)또는 명령 버튼(command button)은사용자가 프로그램에 명령을 내려서 어떤 동작을 하도록 할 때 사용되는 버튼이며,GUI프로그래밍에서 가장 흔하게 사용되고 중요한 위젯입니다!

setStyleSheet()을 이용하면어플리케이션 안의 다양한 구성 요소들의 스타일을 자유롭게 꾸밀수 있는데요.

앞선 두 줄에 적용하면 첫 줄에서 self.penbtn이라는 푸시 버튼 위젯을 설정하고,  배경색을 꾸미는 내용이네요!

그 다음 줄은 그룹박스1에서 시그널이라고 소개했던clicked.connect가 쓰였는데요!

이번에도 역시 이해를 위해self.showColorDlg함수를 당장 가져오고 싶지만,

self.showColorDlg이 뒤에 붓 색상까지 연결되어있어서 붓 색상때 다시 소개하고자 합니다!

지금은 간단하게 확인 후 색을 저장해 두고 버튼의 색을 바꾸는 함수라고 생각해 줍시다.

그리고 다음으로 이렇게 만든 위젯을1행 1줄에 추가하는 코드를 볼 수 있네요! 

이번시간에 작성한 전체 코드를 정리해봅시다!

('그리기 종류 구성하기'와 마찬가지로 임의 지정한 함수는 마지막에 따로 정리 할 예정입니다.)

gb = QGroupBox('펜 설정')        
left.addWidget(gb)        

grid = QGridLayout()      
gb.setLayout(grid)        

label = QLabel('선굵기')
grid.addWidget(label, 0, 0)

self.combo = QComboBox()
grid.addWidget(self.combo, 0, 1)       

for i in range(1, 21):
    self.combo.addItem(str(i))

label = QLabel('선색상')
grid.addWidget(label, 1,0)        

self.pencolor = QColor(0,0,0)
self.penbtn = QPushButton()        
self.penbtn.setStyleSheet('background-color: rgb(0,0,0)')
self.penbtn.clicked.connect(self.showColorDlg)
grid.addWidget(self.penbtn,1, 1)

4.  붓 색상 구성하기

이번에는 붓 색상을 구성하는 그룹박스를 만들어봅시다!

사진을 보시면 아시겠지만 이번 그룹박스는 앞서 배웠던 내용의 간단한 활용입니다!

gb = QGroupBox('붓 설정')        
left.addWidget(gb)

이번에도 동일한 방법으로 그룹박스를 만들어줍시다.

hbox = QHBoxLayout()
gb.setLayout(hbox)

위젯을행 방향으로 나열하는 레이아웃 매니저인QHBoxLayout를 사용한 뒤 그룹박스에 설정해 줍시다!

label = QLabel('붓색상')
hbox.addWidget(label)

행방향으로 라벨을 만들어줍니다.

잘 모르시겠다면 '그리기 종류 구성하기'의 설명을 참고해 주세요!

열에서 행으로 바뀐 것뿐입니다!

self.brushcolor = QColor(255,255,255)

붓색상이니 색상정보를 가져옵시다.

설명은 '펜 설정 구성하기'를 참고해 주세요!

self.brushbtn = QPushButton()        
self.brushbtn.setStyleSheet('background-color: rgb(255,255,255)')
self.brushbtn.clicked.connect(self.showColorDlg)
hbox.addWidget(self.brushbtn)

전체적으로 선색상과 유사한 부분이 많습니다!

선색상에서 했던 설명은 '펜 설정 구성하기'를 참고해주시고

이번에는 self.showColorDlg라는 함수를 이해해봅시다!

def showColorDlg(self):      
    # 색상 대화상자 생성      
    color = QColorDialog.getColor()
    sender = self.sender()
    # 색상이 유효한 값이면 참, QFrame에 색 적용
  if sender == self.penbtn and color.isValid():           
        self.pencolor = color
        self.penbtn.setStyleSheet('background-color: {}'.format( color.name()))
  else:
        self.brushcolor = color
        self.brushbtn.setStyleSheet('background-color: {}'.format( color.name()))

이 함수는 선 색상 때와 붓 색상때 호출되는 함수입니다.

따라서 선 색인지 붓 색인지 구분하기 위해 시그널을 보낸sender를 찾아 구분하는 코드로 구성이 되어있는데요!

먼저 첫 줄의 컬러 다이얼로그(QColorDialog)는 색상을 선택할 수 있는 다이얼로그로

getColor()메서드를 통해색상을 저장할 수 있습니다.

위에서clicked()함수가 실행되는데clicked함수 내에서어떤 버튼이 클릭되었는지를 아는 방법으로sender()메서드를 사용할 수 있습니다.

그리고 조건문을 활용하여 선 색상인지 붓 색상인지를 나누고 선택한 color을 선 또는 붓 색상으로 저장한 뒤, setStyleSheet를 이용해서 그 색깔을 배경색으로 저장해줍니다.

이번 시간 최종 코드입니다.

(구동하도록 하는 함수는 마지막에 따로 정리합니다. 지금은 실행이 불가합니다! )

# 그룹박스3
gb = QGroupBox('붓 설정')
left.addWidget(gb)

hbox = QHBoxLayout()
gb.setLayout(hbox)

label = QLabel('붓색상')
hbox.addWidget(label)

self.brushcolor = QColor(255,255,255)
self.brushbtn = QPushButton()
self.brushbtn.setStyleSheet('background-color: rgb(255,255,255)')
self.brushbtn.clicked.connect(self.showColorDlg)
hbox.addWidget(self.brushbtn)

5. 지우개 구현하기

마지막 지우개 구현입니다!반복해서 하니 조금 알 것 같으신가요?이번에도 중복되는 내용이 많으니 연습한다고 생각하시고 따라와 주세요!

gb = QGroupBox('지우개')        
left.addWidget(gb)

이번에도 역시 그룹박스를 만듭시다!

hbox = QHBoxLayout()
gb.setLayout(hbox)

그다음 위젯을 그룹박스에 설정해 주고

self.checkbox  =QCheckBox('지우개 동작')
self.checkbox.stateChanged.connect(self.checkClicked)
hbox.addWidget(self.checkbox)

체크박스를 이용하여,체크 여부를 판단해 지우개 용도가 될 수 있도록 해줍시다!

QCheckBoxCheckBox가 선택되지 않은 상태에서 선택되거나 반대로 선택된 상태에서 선택 해제가 된 경우에stateChanged라는 시그널을 발생시킵니다.

따라서 사용자가QCheckBox위젯의 상태를 변경할 때 이를 처리하는 함수인 슬롯을stateChanged시그널과 연결합니다!

바로 stateChanged.connect(함수)로 시그널과 연결합니다.

그리고 위에 다른 그룹박스들처럼 위젯을 배치해 주면 마지막 지우개까지 완료되었습니다!

self.checkClicked는 지정해준 함수입니다!

def checkClicked(self):
    pass

딱히 함수에서 할 일이 없기 때문에pass로 적습니다!

이번 시간 최종 코드입니다.

(구동 함수는 나중에 작성합니다.)

# 그룹박스4
gb = QGroupBox('지우개')
left.addWidget(gb)

hbox = QHBoxLayout()
gb.setLayout(hbox)

self.checkbox  =QCheckBox('지우개 동작')
self.checkbox.stateChanged.connect(self.checkClicked)
hbox.addWidget(self.checkbox)

6. 박스 다듬기

이제 깔끔함을 위해 박스를 다듬을 차례입니다!

left.addStretch(1)

이 함수는 왼쪽 레이아웃 영역이 수직으로 모든 영역을 차지하는 것을 방지하기 위해 왼쪽 레이아웃에 여분의 레이아웃, 즉 빈 공간을 추가하는 코드입니다!

이 코드를 넣지 않을 시 위 사진처럼 그룹박스 안에 여백이 생기게 됩니다!

# 우 레이아웃 박스에 그래픽 뷰 추가
self.view = CView(self)       
right.addWidget(self.view)

그리기 영역을 잡아주는 부분입니다.

잡아주지 않을 시 위 사진처럼 그리기 영역이 생기지 않습니다!

# 전체 폼박스에 좌우 박스 배치
formbox.addLayout(left)
formbox.addLayout(right)
 
formbox.setStretchFactor(left, 0)
formbox.setStretchFactor(right, 1)
 
self.setGeometry(100, 100, 800, 500)

완성된 좌(컨트롤),우(그림 그리는 곳)측의 레이아웃을 전체formbox에 배치합시다!

setStretchFactor(self.QWidget, int)위젯이나레이아웃의 늘림 인자를 특정 크기로 맞추어주는 역할을 하는데요left 위젯은0, right 인자는1로 맞추어주었네요!

마지막으로setGeometry()메서드는윈도우의 크기 및 출력 위치를 변경하는 것 입니다.

self.setGeometry(x , y, w, h) 메서드의 4개의 인자로 정숫값을 넘겨주는데 순서대로 모니터에 왼쪽 상단으로부터 윈도우가 출력되는 x축 위치, y축 위치, 윈도우의 너비, 윈도우의 높이를 의미합니다!

7. 호출되는 함수 정리

마지막으로 앞서 봤던 코드에서 호출된 함수들입니다. 설명은 앞에서 다 했으니 넘어가겠습니다!

def radioClicked(self):
    for i in range(len(self.radiobtns)):
        if self.radiobtns[i].isChecked():
            self.drawType = i
            break
            
def checkClicked(self):
    pass

def showColorDlg(self):

    color = QColorDialog.getColor()
    sender = self.sender()


    if sender == self.penbtn and color.isValid():
        self.pencolor = color
        self.penbtn.setStyleSheet('background-color: {}'.format( color.name()))
    else:
        self.brushcolor = color
        self.brushbtn.setStyleSheet('background-color: {}'.format( color.name()))

8. 최종코드(1)

이제 세미나(1)에서 배웠던 코드를 정리해봅시다!

아쉽게도 현재 작성한 코드를 실행하면 위젯을 구성하는 클래스만 있을 뿐, 메인함수로 불러오지 않아서 실행이 되지 않습니다! 

세미나(2)에서 나머지 부분을 채워서 그림판을 완성합시다!

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class CWidget(QWidget):

    def __init__(self):

        super().__init__()

        # 전체 폼 박스
        formbox = QHBoxLayout()
        self.setLayout(formbox)

        # 좌, 우 레이아웃박스
        left = QVBoxLayout()
        right = QVBoxLayout()

        # 그룹박스1 생성 및 좌 레이아웃 배치
        gb = QGroupBox('그리기 종류')
        left.addWidget(gb)

        # 그룹박스1 에서 사용할 레이아웃
        box = QVBoxLayout()
        gb.setLayout(box)

        # 그룹박스 1 의 라디오 버튼 배치
        text = ['line', 'Curve', 'Rectange', 'Ellipse']
        self.radiobtns = []

        for i in range(len(text)):
            self.radiobtns.append(QRadioButton(text[i], self))
            self.radiobtns[i].clicked.connect(self.radioClicked)
            box.addWidget(self.radiobtns[i])

        self.radiobtns[0].setChecked(True)
        self.drawType = 0

        # 그룹박스2
        gb = QGroupBox('펜 설정')
        left.addWidget(gb)

        grid = QGridLayout()
        gb.setLayout(grid)

        label = QLabel('선굵기')
        grid.addWidget(label, 0, 0)

        self.combo = QComboBox()
        grid.addWidget(self.combo, 0, 1)

        for i in range(1, 21):
        self.combo.addItem(str(i))

        label = QLabel('선색상')
        grid.addWidget(label, 1,0)

        self.pencolor = QColor(0,0,0)
        self.penbtn = QPushButton()
        self.penbtn.setStyleSheet('background-color: rgb(0,0,0)')
        self.penbtn.clicked.connect(self.showColorDlg)
        grid.addWidget(self.penbtn,1, 1)

        # 그룹박스3
        gb = QGroupBox('붓 설정')
        left.addWidget(gb)

        hbox = QHBoxLayout()
        gb.setLayout(hbox)

        label = QLabel('붓색상')
        hbox.addWidget(label)

        self.brushcolor = QColor(255,255,255)
        self.brushbtn = QPushButton()
        self.brushbtn.setStyleSheet('background-color: rgb(255,255,255)')
        self.brushbtn.clicked.connect(self.showColorDlg)
        hbox.addWidget(self.brushbtn)

        # 그룹박스4
        gb = QGroupBox('지우개')
        left.addWidget(gb)

        hbox = QHBoxLayout()
        gb.setLayout(hbox)

        self.checkbox  =QCheckBox('지우개 동작')
        self.checkbox.stateChanged.connect(self.checkClicked)
        hbox.addWidget(self.checkbox)


        left.addStretch(1)

        # 우 레이아웃 박스에 그래픽 뷰 추가
        self.view = CView(self)
        right.addWidget(self.view)

        # 전체 폼박스에 좌우 박스 배치
        formbox.addLayout(left)
        formbox.addLayout(right)

        formbox.setStretchFactor(left, 0)
        formbox.setStretchFactor(right, 1)

        self.setGeometry(100, 100, 800, 500)

    def radioClicked(self):
        for i in range(len(self.radiobtns)):
            if self.radiobtns[i].isChecked():
                self.drawType = i
                break

    def checkClicked(self):
        pass

    def showColorDlg(self):

        # 색상 대화상자 생성
        color = QColorDialog.getColor()

        sender = self.sender()

        # 색상이 유효한 값이면 참, QFrame에 색 적용
        if sender == self.penbtn and color.isValid():
            self.pencolor = color
            self.penbtn.setStyleSheet('background-color: {}'.format( color.name()))
        else:
            self.brushcolor = color
            self.brushbtn.setStyleSheet('background-color: {}'.format( color.name()))