PyQt 예제 2.2.1

이제 실제 Dumb 대화 상자에 대한 예제를 보겠다. 먼저, 메인 프로그램에 대한 레이아웃에서 많은 변경이 있었다.


이 부분은 단순한 레이아웃 설정이므로 간략한 설명만 하겠다.

지난번에서 버튼 3개와 다른 위젯들이 추가 되었다. 버튼 3개는 2버전에서 알아볼 대화 상자들이다. 그리고 위젯들은 이 대화 상자들로부터 조작될 위젯들이다. 즉, 대화 상자에서 값을 설정하면 이 위젯들에 대화 상자 값들이 적용되는 것이다.
(자세한 코드는 https://github.com/bluekyu/PyQt-Examples/commit/70429b13a69da8e098717e89bf65e6b559fb5c5d 부분을 보기 바란다. 2.1.1에서 2.2.1로 변경된 사항이 나와있다. +가 추가된 부분이고 -가 제거된 부분이다.)


그러면 본격적인 Dumb 대화 상자 코드를 보겠다. 먼저, 대화 상자를 호출하는 코드부터 보겠다.
def DumbCall(self):
        dialog = DumbDialog(self)
        comboBoxIndex = dialog.comboBox.findText(self.comboBoxLabel.text())
        if comboBoxIndex == -1:
            comboBoxIndex = 0
        dialog.comboBox.setCurrentIndex(comboBoxIndex)
        dialog.textLineEdit.setText(self.textLabel.text())
        dialog.slider.setValue(self.spinBox.value())
        
        if dialog.exec_():
            self.comboBoxLabel.setText(dialog.comboBox.currentText())
            self.textLabel.setText(dialog.textLineEdit.text())
            self.spinBox.setValue(dialog.slider.value())

메인 윈도우에서 버튼을 클릭하면 이 함수를 호출하게 된다. 먼저, 대화 상자 클래스에서 대화 상자를 생성한다. 이때, self 를 인자로 넘기면서 부모 윈도우를 지정해주게 된다.

그리고 3 ~ 5 라인은 단순히 값을 가져오는 부분이고, 그 아래에서 대화 상자에 값을 적용한다. 대화 상자를 띄우기 전에 현재 위젯들(콤보 박스, 라인 에디터, 슬라이더)의 값을 대화 상자에 적용 해주어야, 현재 값들은 무엇인지 알 수 있기 때문이다.

그리고 dialog.exec_() 메소드를 통해서 대화 상자를 띄우게 된다. 이 메소드가 호출 되면서 대화 상자가 모달 형식으로 나타나게 되고, 부모 윈도우의 사용이 중지되게 된다.

마지막으로, 대화 상자의 값을 적용하겠다고 하면, 수락하는 값이 리턴되면서 이를 참으로 판단하여 if 문을 실행하게 된다. if 문에서는 대화 상자에서 변경된 값들을 현재 위젯들에 모두 적용하는 작업을 수행한다.

만약, 대화 상자에서 취소를 하게 되면 거절(reject) 하는 값이 리턴되고, 이를 거짓으로 판단하여 if 문을 실행하지 않게 된다. 따라서 현재 위젯들의 값은 유지되게 된다.

참고로, 3 ~5라인에서 comboBoxIndex 가 -1인 경우를 판단하는 이유는, 콤보 박스에 없는 값을 지정했을 경우 -1을 리턴하기 때문이다. 따라서 없는 값은 그냥 맨 처음 값으로 하기 위해서 보정을 하는 것이다.


그러면 Dumb 대화 상자를 호출하는 클래스 부분의 코드를 보겠다. 딱히 특별한 것은 없고, 메인 대화 상자를 작성한 것과 비슷하게 작성을 하면 된다.
class DumbDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)

        textEditLabel = QLabel("Main 레이블 변경: ")
        self.textLineEdit = QLineEdit("Hello, PyQt!")
        
        valueEditLabel = QLabel("Main 값 변경: ")
        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(0, 100)

        comboBoxLabel = QLabel("Combo Box(&C): ")
        self.comboBox = QComboBox()
        comboBoxLabel.setBuddy(self.comboBox)
        for item in ["item1", "item2", "item3", "item4", "item5"]:
            self.comboBox.addItem(item)

        widgetLayout = QFormLayout()
        widgetLayout.addRow(textEditLabel, self.textLineEdit)
        widgetLayout.addRow(comboBoxLabel, self.comboBox)
        widgetLayout.addRow(valueEditLabel, self.slider)

        okButton = QPushButton("확인(&O)")
        cancelButton = QPushButton("취소")
        buttonBox = QDialogButtonBox()
        buttonBox.addButton(okButton, QDialogButtonBox.AcceptRole)
        buttonBox.addButton(cancelButton, QDialogButtonBox.RejectRole)
        okButton.setDefault(True)
        self.connect(buttonBox, SIGNAL("accepted()"), self, SLOT("accept()"))
        self.connect(buttonBox, SIGNAL("rejected()"), self, SLOT("reject()"))

        layout = QVBoxLayout()
        layout.addLayout(widgetLayout)
        layout.addWidget(buttonBox) 

        self.setLayout(layout)
        self.setWindowTitle("Dumb Dialog") 

먼저, __init__ 바로 아래에서, 상속한 클래스의 생성자를 호출해서 부모 윈도우를 지정해주어야 Modal 형식으로 사용을 할 수 있다.

레이아웃 설정에서, 레이블에 &를 사용하였는데, 이는 Alt 키와 사용하는 단축키를 지정하기 위함이다. 위에서 확인(&O)는 Alt + O키를 누르게 되면 확인 버튼이 눌리게 된다. 그런데 레이블은 이러한 작업이 의미가 없다. 따라서 setBuddy를 사용해서 단축키가 눌렸을 때 반응할 위젯을 설정할 수 있다.

메소드 이름 그대로 친구를 지정해주는 것이다. 각 레이블이 나타내는 위젯이 있으므로 그 위젯에 setBuddy 를 해주면, 단축키를 눌렀을 때 그 위젯으로 포커스가 이동하게 된다.

그리고 처음으로 FormLayout을 사용하였는데, 이 레이아웃은 위젯의 배치를 "레이블 : 필드" 형식으로 배치를 하게 된다. 예를 들어, 이름을 적는 라인 에디터와 그 라인 에디터를 나타내는 레이블을 함께 나타낼 때, QHBoxLayout으로 나타낼 수도 있지만 QFormLayout을 통해서 "이름: 라인에디터" 형식으로 위젯을 배치할 수 있다.

사용 방식은 addRow 메소드에 (레이블, 위젯) 형식으로 추가를 해주면 추가 순서대로 위젯이 배치된다. 더 자세한 사용법은 레퍼런스를 참고하면 된다.

QDialogButtonBox는 대화 상자 버튼을 쉽게 배치해주는 위젯이다. 윈도우, 맥, 리눅스, 그리고 리눅스 내에서도 Gnome, KDE에 따라서도 버튼의 순서나 배치가 매우 다르다. 이를 일일이 플랫폼마다 변경할 수는 없으므로 Qt에서 배치를 자동으로 변경시키도록 해둔 것이 이 위젯이다. 위젯을 생성한 다음, 사용하고자 하는 버튼을 addButton 메소드를 사용해서 추가해주면 된다.

이때, 2가지 방법이 있는데, 표준 버튼을 사용하는 방법과 직접 생성한 버튼을 사용하는 방법이 있다. 표준 버튼을 사용하는 방법은 단지, 버튼 역할을 선택해서 넘겨주기만 하면, 자동으로 버튼을 추가해준다. 예를 들어, QDialogButtonBox(QDialogButtonBox.OK | QDialogButtonBox.Cancle)을 해주면 표준 버튼들을 바로 추가해준다. 그러나 이 방식을 사용하면 버튼의 이름 변경이 불가능하기 때문에, 영문으로 나온다는 점이 있다.

따라서 위와 같이 직접 버튼을 생성해서 addButton 메소드로 버튼 역할과 함께 추가해주는 방법을 사용할 수도 있다.

마지막으로 connect 메소드에서 확인과 취소를 눌렀을 때, 보내야할 슬롯으로써, accept 와 reject를 사용하면 된다. 그러면 위에서 설명한 것처럼 이를 True와 False로 판단하게 된다.

ps. 2.1.1 글을 쓴 지 오래되서 그런지 무엇에 대해 설명을 해야 하는지 많이 잊어버렸다... 그리고 예전에 썼던 글도, 이번 설명 부분부터는 책에 있는 내용을 많이 옮기지 않고, 직접 썼기 때문에 자세한 내용을 설명하지는 못했다.

레퍼런스를 많이 참고하길 바란다...... 또는 코드에 대한 설명 요구는 댓글로 남겨주길 바란다.

댓글 2개:

  1. DumbCall 함수
    comboBoxIndex = dialog.comboBox.findText(self.comboBoxLabel.text()) 에서 comboBoxLabel 속성이 없다고 나오는데 .. 혹시 전체 코드를 볼수 있을까요?

    답글삭제
  2. ㄴ레이아웃을 추가한 것에 대한 설명이 없었네요.

    https://github.com/bluekyu/PyQt-Examples/commit/70429b13a69da8e098717e89bf65e6b559fb5c5d

    에 보시면 2.1.1 에서 2.2.1로 넘어가면서 변경됨 점이 나와 있습니다. + 부분이 추가된 부분으로, 52번째 줄에 원하시는 부분이 있습니다.

    답글삭제