Python/PyQt5

[PyQt5] 5. layout

컴닥 2021. 5. 19. 14:18
반응형

레이아웃은 위젯들을 담아두는 틀입니다. 

레이아웃을 잘 이용하면 
자동으로 줄 맞춤이 되고, 
좌표를 하드 코딩할 필요가 없어 
유연한 GUI를 만들 수 있기 때문에, 
무척 편합니다. 

하드 코딩은 데이터를 직접 코드 내에 넣어두는 것을 말합니다. 
레이아웃을 사용하면 (좌표에 관련된) 숫자는 거의 사용하지 않습니다. 

최상위 위젯을 QMainWindow에서 QWidget으로 바꿨습니다.
이유는 마지막에... 

QVBoxLayout 

QVBoxLayout이라는 클래스가 보입니다.
V는 Vertical(세로)에서 나왔다는 걸 알 수 있죠. 

QVBoxLayout 클래스로 layout 인스턴스를 만들어서
layout 인스턴스에 위젯들을 하나씩 addWidget 해 주었습니다. 

addWidget으로 상위 위젯에 add해주기 때문에
하위 위젯의 두 번째 인수로 상위 위젯(self 등)을
일일이 작성하지 않아도 됩니다.  

마무리로 self.setLayout(layout) 해 주었고요. 

 

from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QWidget


class MyWidget(QWidget):  # QMainWindow 에서 QWidget 으로 바꿨습니다.
    def __init__(self):
        super().__init__()

        self.label = QLabel('label')
        self.button1 = QPushButton('button1')
        self.button1.clicked.connect(self.button1_clicked)
        self.button2 = QPushButton('button2')
        self.button2.clicked.connect(self.button2_clicked)

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        self.setLayout(layout)

    def button1_clicked(self):
        self.label.setText('Clicked')

    def button2_clicked(self):
        self.label.setText('')


app = QApplication([])
my_window = MyWidget()
my_window.show()
app.exec_()

https://github.com/pycrawling/pyqt5_study/blob/main/pyqt5_study_05-1.py

결과는 다음과 같습니다.  

줄이 잘 맞습니다. 

 

이런 레이아웃들이 준비되어 있습니다.  

  • QVBoxLayout: 위젯들을 세로(위 -> 아래) 순서대로 배열합니다.
  • QHBoxLayout: 위젯들을 가로(좌 -> 우) 순서대로 배열합니다.
  • QGridLayout: 위젯들을 Grid의 지정된 Row, Column에 배열합니다.
  • QFormLayout: 두 개의 위젯을 하나의 Row에 넣어줍니다. 폼 만들 때 편하겠죠?

 

QHBoxLayout

from PyQt5.QtWidgets import *


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()

        layout = QHBoxLayout()
        layout.addWidget(QPushButton('AAA'))
        layout.addWidget(QPushButton('BBB'))
        layout.addWidget(QPushButton('CCC'))
        self.setLayout(layout)


app = QApplication([])
my_window = MyWindow()
my_window.show()
app.exec_()

https://github.com/pycrawling/pyqt5_study/blob/main/pyqt5_study_05-2.py

 

QGridLayout

from PyQt5.QtWidgets import *


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()

        layout = QGridLayout()
        layout.addWidget(QPushButton('AAA'), 0, 0)
        layout.addWidget(QPushButton('BBB'), 0, 1)
        layout.addWidget(QPushButton('CCC'), 1, 1)
        self.setLayout(layout)


app = QApplication([])
my_window = MyWindow()
my_window.show()
app.exec_()

https://github.com/pycrawling/pyqt5_study/blob/main/pyqt5_study_05-3.py

 

QFormLayout

from PyQt5.QtWidgets import *


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()

        layout = QFormLayout()
        layout.addRow(QPushButton('AAA'), QPushButton('BBB'))
        layout.addRow(QPushButton('CCC'), QPushButton('DDD'))
        self.setLayout(layout)


app = QApplication([])
my_window = MyWindow()
my_window.show()
app.exec_()

https://github.com/pycrawling/pyqt5_study/blob/main/pyqt5_study_05-4.py

 

레이아웃(Layout)의 중첩

계산기를 만들어 보겠습니다.

layout을 위아래로 나눠서,
layout_upper에는 숫자 창(LineEdit)과 C 버튼을 
layout_lower에는 나머지 버튼들을 넣었습니다. 

layout_upper는 위젯을 가로로 배열하는 QHBoxLayout을 사용했고,
layout_lower에는 위젯을 Grid로 배열을 해주는 QGridLayout을 사용했고, 
전체적인 레이아웃(layout)에는 위젯을 세로로 배열하는 QVBoxLayout을 사용했습니다.

layout을 상위 layout에 합칠 때는 addLayout을 씁니다.

from PyQt5.QtWidgets import *


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()

        self.line_edit = QLineEdit('0')
        self.button0 = QPushButton('0')
        self.button1 = QPushButton('1')
        self.button2 = QPushButton('2')
        self.button3 = QPushButton('3')
        self.button4 = QPushButton('4')
        self.button5 = QPushButton('5')
        self.button6 = QPushButton('6')
        self.button7 = QPushButton('7')
        self.button8 = QPushButton('8')
        self.button9 = QPushButton('9')
        self.button_add = QPushButton('+')
        self.button_sub = QPushButton('-')
        self.button_mul = QPushButton('*')
        self.button_div = QPushButton('/')
        self.button_clear = QPushButton('C')
        self.button_dot = QPushButton('.')
        self.button_equal = QPushButton('=')

        layout_upper = QHBoxLayout()
        layout_upper.addWidget(self.line_edit)
        layout_upper.addWidget(self.button_clear)

        layout_lower = QGridLayout()
        layout_lower.addWidget(self.button7, 0, 0)
        layout_lower.addWidget(self.button8, 0, 1)
        layout_lower.addWidget(self.button9, 0, 2)
        layout_lower.addWidget(self.button4, 1, 0)
        layout_lower.addWidget(self.button5, 1, 1)
        layout_lower.addWidget(self.button6, 1, 2)
        layout_lower.addWidget(self.button1, 2, 0)
        layout_lower.addWidget(self.button2, 2, 1)
        layout_lower.addWidget(self.button3, 2, 2)
        layout_lower.addWidget(self.button0, 3, 0)
        layout_lower.addWidget(self.button_dot, 3, 1)
        layout_lower.addWidget(self.button_equal, 3, 2)
        layout_lower.addWidget(self.button_sub, 0, 3)
        layout_lower.addWidget(self.button_div, 1, 3)
        layout_lower.addWidget(self.button_mul, 2, 3)
        layout_lower.addWidget(self.button_add, 3, 3)
    
        layout = QVBoxLayout()
        layout.addLayout(layout_upper)
        layout.addLayout(layout_lower)
        self.setLayout(layout)


app = QApplication([])
my_window = MyWidget()
my_window.show()
app.exec_()

https://github.com/pycrawling/pyqt5_study/blob/main/pyqt5_study_05-5.py

 

QMainWindow

QMainWindow를 Designer에서 보면
다른 최상위 위젯과 달리
지울 수 없는 QWidget 클래스의 centralwidget 객체(이름은 변경 가능)가 있습니다. 

그래서 코드를 직접 작성할 때는 다음과 같이 해주셔야 합니다. 

QWidget의 객체를 만들고,
central_widget = QWidget()

레이아웃을 작성하고

이 객체에 레이아웃을 등록하고
central_widget.setLayout(layout)

마지막에는 setCentralWidget으로 그 위젯을 등록합니다. 
self.setCentralWidget(central_widget)

from PyQt5.QtWidgets import (QApplication, QLabel, QMainWindow,
                             QPushButton, QVBoxLayout, QWidget)


class MyWidget(QMainWindow):
    def __init__(self):
        super().__init__()

        self.label = QLabel('label')
        self.button1 = QPushButton('button1')
        self.button1.clicked.connect(self.button1_clicked)
        self.button2 = QPushButton('button2')
        self.button2.clicked.connect(self.button2_clicked)

        central_widget = QWidget()
        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

    def button1_clicked(self):
        self.label.setText('Clicked')

    def button2_clicked(self):
        self.label.setText('')


app = QApplication([])
my_window = MyWidget()
my_window.show()
app.exec_()

https://github.com/pycrawling/pyqt5_study/blob/main/pyqt5_study_05-6-1.py

아래 방식으로 작성해도 됩니다만...
전 위 방식이 더 편합니다. 

        central_widget = QWidget()
        layout = QVBoxLayout(central_widget)  # 이렇게 작성해도 됩니다만
        layout.addWidget(self.label)
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        self.setCentralWidget(central_widget)

https://github.com/pycrawling/pyqt5_study/blob/main/pyqt5_study_05-6.py

 

대규모의 GUI를 작성하거나 
상태바와 메뉴가 필요할 때는 
이런 구조가 편리하겠으나..

간단한 구조의 GUI를 만들 때는 
굳이 필요한 것 같지 않습니다. 

반응형