PyQt 예제 3.1.1

이번 3절에서는 메인 윈도우를 다루어 보는 예제를 보겠다.


위의 그림처럼 레이아웃을 변경하고 메뉴도 만들어 볼 것이다. 그리고 대화 상자에서는 최대화 단추가 없었지만, 메인 위도우가 되었기 때문에 이제 창을 최대화 시킬 수 있다.

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    app.exec_()
먼저, 실행 부분이다. 지난 번에서는 MainDialog()를 호출해서 show를 하였지만, 이번에는 MainWindow()를 호출한 후 show를 호출했다.

그 다음으로 MainWindow 클래스를 보겠다.
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        ### Buttons ###
        buttonGroup = QGroupBox()
        buttonGroup.setTitle("버튼")
        buttonLayout = QFormLayout()
        buttonGroup.setLayout(buttonLayout)

        # Push Button
        mainButtonLabel = QLabel("버튼: ")
        self.mainButton = QPushButton("버튼(&B)")
        buttonMessageBox = QMessageBox(QMessageBox.Information, "버튼 메시지",
                            "버튼이 클릭 됨", QMessageBox.Ok, self)
        self.connect(self.mainButton, SIGNAL("clicked()"), buttonMessageBox.open)

        # Check Box
        mainCheckBoxLabel = QLabel("체크 상자: ")
        self.mainCheckBox = QCheckBox("체크(&K)")
        
        buttonLayout.addRow(mainButtonLabel, self.mainButton)
        buttonLayout.addRow(mainCheckBoxLabel, self.mainCheckBox)

        ### Input Widgets ###
        inputGroup = QGroupBox()
        inputGroup.setTitle("입력 위젯")
        inputLayout = QFormLayout()
        inputGroup.setLayout(inputLayout)
        
        # Combo Box
        mainComboBoxLabel = QLabel("콤보 상자(&C): ")
        self.mainComboBox = QComboBox()
        mainComboBoxLabel.setBuddy(self.mainComboBox)
        self.mainComboBox.addItems(["항목 1", "항목 2", "항목 3", "항목 4"])

        # Line Edit
        mainLineEditLabel = QLabel("라인 편집(&I): ")
        self.mainLineEdit = QLineEdit("편집 내용")
        mainLineEditLabel.setBuddy(self.mainLineEdit)

        # Spin Box
        mainSpinBoxLabel = QLabel("스핀 상자(&S): ")
        self.mainSpinBox = QSpinBox()
        mainSpinBoxLabel.setBuddy(self.mainSpinBox)
        self.mainSpinBox.setRange(-1000, 1000)
        self.mainSpinBox.setValue(0)
        self.mainSpinBox.setPrefix("Prefix ")
        self.mainSpinBox.setSuffix(" Suffix")
        self.mainSpinBox.setAccelerated(True)

        # Dial
        mainDialLabel = QLabel("다이얼(&D): ")
        self.mainDial = QDial()
        mainDialLabel.setBuddy(self.mainDial)
        self.mainDial.setMinimumSize(100, 100)
        self.mainDial.setRange(-100, 100)
        self.mainDial.setValue(0)
        self.mainDial.setNotchesVisible(True)

        # Slider
        mainSliderLabel = QLabel("슬라이더(&L): ")
        self.mainSlider = QSlider(Qt.Horizontal)
        mainSliderLabel.setBuddy(self.mainSlider)
        self.mainSlider.setRange(-100, 100)
        self.mainSlider.setValue(0)
        self.mainSlider.setTickPosition(QSlider.TicksBelow)

        inputLayout.addRow(mainComboBoxLabel, self.mainComboBox)
        inputLayout.addRow(mainLineEditLabel, self.mainLineEdit)
        inputLayout.addRow(mainSpinBoxLabel, self.mainSpinBox)
        inputLayout.addRow(mainDialLabel, self.mainDial)
        inputLayout.addRow(mainSliderLabel, self.mainSlider)

        ### Display Widgets ###
        displayGroup = QGroupBox()
        displayGroup.setTitle("표시 위젯")
        displayLayout = QFormLayout()
        displayGroup.setLayout(displayLayout)

        mainLabelLabel = QLabel("레이블: ")
        self.mainLabel = QLabel("레이블 내용")

        displayLayout.addRow(mainLabelLabel, self.mainLabel)

        ### Actions ###
        # Simple Dialog Open
        simpleDialogAction = QAction("단순 대화상자", self)
        simpleDialogAction.setShortcut("Ctrl+S")
        simpleDialogHelp = "단순한 대화 상자를 엽니다"
        simpleDialogAction.setToolTip(simpleDialogHelp)
        simpleDialogAction.setStatusTip(simpleDialogHelp)
        self.connect(simpleDialogAction, SIGNAL("triggered()"),
                        lambda : sscDlg.SimpleDialog(self).exec_())

        # Signal Dialog Open
        signalDialogAction = QAction("시그널 대화상자", self)
        signalDialogAction.setShortcut("Ctrl+G")
        signalDialogHelp = "여러 시그널로 된 대화 상자를 엽니다"
        signalDialogAction.setToolTip(signalDialogHelp)
        signalDialogAction.setStatusTip(signalDialogHelp)
        self.connect(signalDialogAction, SIGNAL("triggered()"),
                        lambda : sscDlg.SignalDialog(self).exec_())

        # Connect Dialog Open
        connectDialogAction = QAction("Connect 대화상자", self)
        connectDialogAction.setShortcut("Ctrl+E")
        connectDialogHelp = "여러 연결 방식을 갖는 버튼 대화 상자를 엽니다"
        connectDialogAction.setToolTip(connectDialogHelp)
        connectDialogAction.setStatusTip(connectDialogHelp)
        self.connect(connectDialogAction, SIGNAL("triggered()"),
                        lambda : sscDlg.ConnectDialog(self).exec_())

        # Dumb Dialog Open
        dumbDialogAction = QAction("Dumb 대화상자", self)
        dumbDialogAction.setShortcut("Ctrl+M")
        dumbDialogHelp = "Dumb 및 Modal 대화 상자를 엽니다"
        dumbDialogAction.setToolTip(dumbDialogHelp)
        dumbDialogAction.setStatusTip(dumbDialogHelp)
        self.connect(dumbDialogAction, SIGNAL("triggered()"), self.DumbCall)

        # Standard Dialog Open
        standardDialogAction = QAction("Standard 대화상자", self)
        standardDialogAction.setShortcut("Ctrl+R")
        standardDialogHelp = "Standard 및 Modal 대화 상자를 엽니다"
        standardDialogAction.setToolTip(standardDialogHelp)
        standardDialogAction.setStatusTip(standardDialogHelp)
        self.connect(standardDialogAction, SIGNAL("triggered()"), self.StandardCall)

        # Smart Dialog Open
        smartDialogAction = QAction("Smart 대화상자", self)
        smartDialogAction.setShortcut("Ctrl+M")
        smartDialogHelp = "Smart 및 Modaless 대화 상자를 엽니다"
        smartDialogAction.setToolTip(smartDialogHelp)
        smartDialogAction.setStatusTip(smartDialogHelp)
        self.connect(smartDialogAction, SIGNAL("triggered()"), self.SmartCall)

        # Live Dialog Open
        liveDialogAction = QAction("Live 대화상자", self)
        liveDialogAction.setShortcut("Ctrl+L")
        liveDialogHelp = "Live 및 Modaless 대화 상자를 엽니다"
        liveDialogAction.setToolTip(liveDialogHelp)
        liveDialogAction.setStatusTip(liveDialogHelp)
        self.connect(liveDialogAction, SIGNAL("triggered()"), self.LiveCall)

        ### Menu Bar ###
        # 대화 상자
        dialogMenu = self.menuBar().addMenu("대화 상자(&A)")
        dialogMenuHelp = "여러 대화 상자 종류들을 포함합니다"
        dialogMenu.addAction(simpleDialogAction)
        dialogMenu.addAction(signalDialogAction)
        dialogMenu.addAction(connectDialogAction)
        dialogMenu.addSeparator()
        dialogMenu.addAction(dumbDialogAction)
        dialogMenu.addAction(standardDialogAction)
        dialogMenu.addAction(smartDialogAction)
        dialogMenu.addAction(liveDialogAction)

        ### Status Bar ###
        status = self.statusBar()
        status.showMessage("실행 완료", 5000)
        
        ### Main ###
        self.liveDialog = None

        mainLayout = QHBoxLayout()
        mainLayout.addWidget(buttonGroup)
        mainLayout.addWidget(inputGroup)
        mainLayout.addWidget(displayGroup)

        centralWidget = QWidget()
        centralWidget.setLayout(mainLayout)
        self.setCentralWidget(centralWidget)

        self.setWindowTitle("Main Window")
지난번까지는 QDialog를 상속했지만 이제 QMainWindow를 상속해서 창을 띄우게 된다. 그 다음으로, 지난 번처럼 위젯의 레이아웃을 설정해주면 된다.

84번째 줄까지가 위젯을 설정해주는 부분이다. 위젯을 생성하고, 속성을 지정한 후에 필요하면 connect 메소드로 연결까지 해주었다. 그리고 각 위젯을 구분하기 위해서 GroupBox에 넣어주었다.

이렇게 위젯 배치를 하는 이유는 Qt 디자이너에 있는 위젯을 나열하기 위함이다. Qt 디자이너에 존재하는 위젯들을 한 번씩 다루어 보기 위해서 GroupBox로 나누어서 배치를 하였다.

GroupBox를 생성한 후에 setLayout을 통해서 레이아웃을 추가해주고, 이 레이아웃에는 위젯을 배치하면 GroupBox 내에 위젯을 추가해줄 수 있다.


그 다음으로 Action이라는 것이 처음 나온다. Action은 메뉴가 어떤 행동을 수행할 지 지정해주는 것이다. 여기서는 지금까지 만든 대화 상자 7개를 호출하는 액션들을 추가할 것이다.

QAction 을 통해서 객체를 생성하고, setShortcut을 통해서 단축키를 설정할 수 있다. 그런 다음, setToolTip과 setStatusTip에 원하는 도움말을 추가해줄 수 있다.

툴팁은 메뉴 위에 마우스를 올려두었을 때 나타나는 팁이고, Status 팁은 상태표시줄에 나타나는 팁이다.

그런 다음 제일 중요한 것으로 액션과 메소드를 연결해주어야 한다. 어떤 메뉴에 액션이 설정되어 있고, 그 액션을 눌렀을 때 triggered() 시그널이 발생한다. 이를 받아서 대화 상자를 호출해주는 메소드를 실행해주면 된다.

1절에서 만든 대화 상자들을 lambda 를 통해서 바로 실행해주도록 하였고, 2절에서 만든 대화 상자들은 더 복잡하기 때문에 따로 메소드를 만든 후에 그 메소드를 호출하도록 하였다.


액션을 144번째 줄까지 설정하였고, 이제 메뉴를 설정해줄 차례이다. MainWindow 클래스에는 메뉴가 이미 존재하는데, 어떠한 메뉴도 추가하지 않아서 보이지 않을 뿐이다. 따라서 먼저 메뉴를 추가해주면 메뉴가 생성이 된다.

self 를 통해서 MainWindow 클래스를 부르고, menuBar() 메소드를 통해서 메뉴 객체를 받는다. 그리고 이 메뉴 객체에 addMenu를 통해서 자신이 추가하고자 하는 메뉴를 추가해준다.

메뉴 이름은 '대화 상자'이다. 그리고 메뉴에 액션들을 추가해주어야 하므로 addAction 메소드를 통해서 위에서 만든 액션들을 추가해주면 된다. 이때, addSeperator를 통해서 액션 사이에 구분자(얇은 실선)를 추가해줄 수 있다.

이제 160번째 줄에서 상태 표시줄을 추가해준다. 이미 MainWindow 클래스에 상태표시줄이 존해하므로, self.statusBar를 통해서 상태표시줄을 받고, showMessage를 통해서 메시지를 보여줄 수 있다. 첫번째 인자는 보여줄 메시지 내용이고, 두번째 인자는 보여줄 시간이다. 여기서 단위는 ms이므로 5000 은 5초를 의미한다.


마지막으로, 메인 윈도우의 가운데에 위젯을 추가해주어야 한다. 메인 윈도우는 아래 그림과 같이 영역이 구분되어 있다.

가운데에는 레이아웃이 아닌 위젯이 추가되어야 하므로, 어떤 특정 위젯이 추가해되어야 한다.

만약, 편집기 프로그램을 만든다면 QEditor와 같은 위젯이 추가될 수 있을 것이다. 그러나 여기서는 그룹 박스들의 묶음이 하나의 레이아웃에 포함되어 있으므로, 가능하다면 레이아웃을 추가해주면 좋을 것이다.

그래서 약간의 편법을 사용해서 QWidget이라는 모든 위젯들의 부모 클래스를 사용하여 이 위젯에 레이아웃을 추가한 다음, 이 위젯을 메인 윈도우의 중심 위젯으로 배치해줄 것이다.

이것이 166 ~ 173의 코드이다. 레이아웃에 GruopBox를 배치한 다음, 위젯에 레이아웃을 배치하고, setCenteralWidget을 통해서 중심 위젯을 추가해주었다.

그리고 마지막으로 메인 윈도우의 제목을 지정하기 위해서 setWindowTitle을 사용하여 제목을 지정해주었다.


이로써, 메인 윈도우가 완성이 되었다. 이제 나머지는 위에서 설정한 액션들에 대한 메소드 내용이다. 이 부분은 지난 번과 비슷하게 대화 상자를 호출하는 메소드들로 구성되어 있다. 따라서 추가적인 설명은 하지 않겠다.
def DumbCall(self):
        dialog = introDlg.DumbDialog(self)
        dialog.dumbLineEdit.setText(self.mainLabel.text())
        dialog.dumbComboBox.clear()
        for idx in range(len(self.mainComboBox)):
            dialog.dumbComboBox.addItem(self.mainComboBox.itemText(idx))
        dialog.dumbComboBox.setCurrentIndex(self.mainComboBox.currentIndex())
        dialog.dumbSlider.setMaximum(self.mainSlider.maximum())
        dialog.dumbSlider.setMinimum(self.mainSlider.minimum())
        dialog.dumbSlider.setValue(self.mainSlider.value())
        
        if dialog.exec_():
            self.mainLabel.setText(dialog.dumbLineEdit.text())
            self.mainComboBox.setCurrentIndex(
                            dialog.dumbComboBox.currentIndex())
            self.mainSlider.setValue(dialog.dumbSlider.value())

    def StandardCall(self):
        mainComboBoxItems = [self.mainComboBox.itemText(idx) for idx in
                                range(len(self.mainComboBox))]
        values = {"labelText": self.mainLabel.text(),
                "comboBoxItems": mainComboBoxItems,
                "comboBoxIndex": self.mainComboBox.currentIndex(),
                "dialValue": self.mainDial.value(),
                "dialMaximum": self.mainDial.maximum(),
                "dialMinimum": self.mainDial.minimum()}
        dialog = introDlg.StandardDialog(values, self)

        if dialog.exec_():
            values = dialog.getValues()
            self.mainLabel.setText(values["labelText"])
            self.mainComboBox.setCurrentIndex(values["comboBoxIndex"])
            self.mainDial.setValue(values["dialValue"])

    def SmartCall(self):
        mainComboBoxItems = [self.mainComboBox.itemText(idx) for idx in
                                range(len(self.mainComboBox))]
        def update():
            self.mainLabel.setText(self.values["labelText"])
            self.mainComboBox.setCurrentIndex(self.values["comboBoxIndex"])
            self.mainSlider.setValue(self.values["sliderValue"])
        self.values = {"labelText": self.mainLabel.text(),
                    "comboBoxItems": mainComboBoxItems,
                    "comboBoxIndex": self.mainComboBox.currentIndex(),
                    "sliderValue": self.mainSlider.value(),
                    "sliderMaximum": self.mainSlider.maximum(),
                    "sliderMinimum": self.mainSlider.minimum()}

        dialog = introDlg.SmartDialog(self.values, self)
        self.connect(dialog, SIGNAL("changed"), update)
        dialog.show()

    def LiveCall(self):
        mainComboBoxItems = [self.mainComboBox.itemText(idx) for idx in
                                range(len(self.mainComboBox))]
        def update():
            self.mainLabel.setText(self.values["labelText"])
            self.mainComboBox.setCurrentIndex(self.values["comboBoxIndex"])
            self.mainSlider.setValue(self.values["sliderValue"])
        self.values = {"labelText": self.mainLabel.text(),
                    "comboBoxItems": mainComboBoxItems,
                    "comboBoxIndex": self.mainComboBox.currentIndex(),
                    "sliderValue": self.mainSlider.value(),
                    "sliderMaximum": self.mainSlider.maximum(),
                    "sliderMinimum": self.mainSlider.minimum()}

        if self.liveDialog is None:
            self.liveDialog = introDlg.LiveDialog(self.values, update, self)
        self.liveDialog.show()
        self.liveDialog.raise_()
        self.liveDialog.activateWindow()

댓글 없음:

댓글 쓰기