2015. 9. 25. 01:21

0052 - 대화 창 더 알아 보기

참고 영상 (약 4분 부터)

지난 시간에 연결해서 확인 했던 대화 창에 대해 조금 더 알아보겠습니다.

1. 프로젝트 열기

지난 시간에 받았던 파일을 사용합니다.

005_MyWindows_Start.zip

2. centralWiget 설정하기

Mainwindow 에는 최소한 정보를 표현할 수 있는 센트럴 위젯(Central Widget) 이라고 불리는 하나의 위젯이 있는 것이 좋습니다. 지정하지 않아도 0 (NULL) 으로 지정되어 문제는 안됩니다만 문서에는 있어야 한다고 표시를 하니 다른 특별한 이유가 있어 보입니다.

정보를 표시할 위젯을 센트럴 위젯으로 지정하기 위해서는 QMainWidow setCentralWidget 메소드를 이용하면 됩니다. 센트롤 위젯으로 지정이 되면 창 크기에 따라 위젯의 크기가 자동으로 변경되는 등의 혜택을 입게 됩니다.

폼 편집기에서 Widget Box 에 있는 Text Edit 을 드래그 앤 드롭으로 MainWindow 안에 추가합니다.


오브젝트 인스펙터를 보면 추가된 위젯에 대한 정보가 나옵니다. textEdit 라는 이름의 QTextEdit 형 위젯이 추가가 되었습니다.

실행 버튼(Ctrl + R ) 을 눌러 실행을 하면 텍스트 위젯이 자동으로 추가되어 보이지만, 크기나 위치가 어정쩡한 상태로 나오게 됩니다.

이 추가된 textEdit 을 센트럴 위젯으로 지정해 보겠습니다.

MainWindow 생성자에서 this->setCentralWidget(ui->textEdit) 을 입력하여 QTextEdit 형의 textEdit 위젯을 센트롤 위젯으로 지정합니다.

1
2
3
4
5
6
7
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setCentralWidget(ui->textEdit);
}
cs

실행을 하면 textEdit 가 MainWindow 창을 가득 채우는 형태로 등장하게 됩니다.

센트럴 위젯은 QT 고유 속성이므로 따로 신경 써줘야 하는 불편함이 있지만, 그에 걸맞는 보상을 줍니다.

3. 모달 / 모달리스 설정하기

1) setModal 메소드로 모달 속성 변경하기

on_actionNewWindow_triggered() 안의 선언된 MyDialog (QDialog) 에서 지원하는 setModal 메소드를 이용하여 모달(Modal) 속성 을 지정할 수 있습니다.

기본은 모달 (true) 이며, setModal(false) 를 입력함으로써, 모달리스(Modaless) 로 속성을 지정할 수 있습니다.

2) exec 메소드를 show  메소드로 변경하기

주의할 것은 API 문서에 따르면 exec 메소드의 특성상 QCoreApplication 의 exec 와 같은 이벤트 루프 성격을 띄므로 사용자가 명시적으로 닫기 전까지 다른 창들의 이동을 막으면서 동작하는 블로킹 (Blocking) 방식으로 설계가 되어 있습니다. 따라서 모달인지 모달리스인지 속성과 관계 없이 구현상 모달로 지정되게 됩니다.

아래와 같은 코드를 mainwindow.cpp 에 작성하고 실행하고 Application Output 창을 확인 하면 New Window 를 눌렀을 때 Start Dialog 가 표시되고, 창을 닫았을 때 Close Dialog 가 나옴을 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mydialog.h"
#include <QDebug>
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::on_actionNew_Window_triggered()
{
    MyDialog mDialog;
    qDebug() << "\n Start Dialog \n";
    mDialog.exec();
    qDebug() << "\n Close Dialog \n";
}
 
cs

따라서 exec 메소드를 쓰지 말고 show 메소드를 사용하여 창을 표시하는 게 좋습니다.

show 메소드는 변수가 실행 범위에서 사라지는 조건에서는 파괴자에 의해 자동으로 사라지게 됩니다.

현재 MyDialog.setModal(true) MyDialog.show() 만으로 대화창을 실행하면 창이 나오긴 하지만, MyDialog 가 지역 변수 형태이므로 곧 범위를 벗어나면 파괴자에 의해 사라지는 현상을 볼 수 있습니다.

1
2
3
4
5
6
7
8
void MainWindow::on_actionNew_Window_triggered()
{
    MyDialog mDialog;
    qDebug() << "\n Start Dialog \n";
    mDialog.setModal(true);
    mDialog.show();
    qDebug() << "\n Close Dialog \n";
}
cs

mDialog 를 on_actionNew_Window_triggered() 메소드 호출 후에도 메모리에 남아 있게 변경하면 해결이 됩니다.

3) 대화 상자 객체 mDialog 를 관리하기

- 제일 간단한 방법은 정적(스태틱, static) 변수 형태로 변경하면 됩니다. static 은 프로그램이 실행하기 전에 Data Segment 공간에 저장되므로 on_actionNew_Window_triggered() 에서의 실행 여부와 관계없이 메모리에 저장되어 있습니다.

1
2
3
4
5
6
void MainWindow::on_actionNew_Window_triggered()
{
    static MyDialog mDialog;
    mDialog.setModal(false);
    mDialog.show();
}
cs

mDialog 가 아닌 MainWindow 로 접근할 수 있습니다.

- 위 방법으로 모달리스를 만들 수 있지만 몇가지 어색한 점이 있습니다만 대표적으로 다음과 같은 문제가 있습니다.

  • 상위 창에서 New Window 를 입력시 Focus 이동이 되지 않습니다.
  • 상위 윈도우를 닫으면 대화창이 종료 되지 않습니다
  • 모달리스의 장점이 될 수 있는 여러개의 창이 나오지 않습니다.

4) 상위 윈도우 지정하기

mDialog 의 생성자를 MainWindow 클래스 자신을 가리키는 this 를 넣어서 사용하면 자동으로 상위(parent) 윈도우 관계 속에서 대화 상자가 열리게 됩니다.

주의 할 것은 QDialog 에서 제공하는 setParent 메소드를 이용해서 직접 MainWindow 를 지정하기엔 정보가 많이 부족하므로 정상적으로 지정되지 않습니다.

4. 여러개 모달리스 창을 띄우기

모달리스 창의 특징 중 하나는 같은 항목을 표시하는 여러 창을 띄울 수 있다는 점입니다. 호출시 QDialog 형의 객체를 생성해 표시하는 방식으로 구현하면 됩니다.

MainWindow 클래스에 MyDialog 를 참조하는 포인터를 추가를 합니다. 메소드에 넣어도 되지만, 그렇게 하면 나중에 어떤 위젯이 이 윈도우에 포함되는지 파악하기가 어렵기 때문에 관리 목적으로 습관처럼 여기에 넣어 줍니다. 다른 객체에서 MyDialog 를 직접 부를 일이 없기 때문에 프라이빗(private) 영역에 추가 작업을 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// mainwindow.h
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
private slots:
    void on_actionNew_Window_triggered();
 
private:
    Ui::MainWindow *ui;
    MyDialog *mDialog;
};
cs

New Window 를 눌렀을 때 적용되는 on_actionNew_Window_triggered() 메소드에서는 mDialog 를 힙 (Heap) 영역에 두어서, 해당 실행 범위를 벗어나도 계속 유지가 되게 합니다.

1
2
3
4
5
6
// mainwindow.cpp
void MainWindow::on_actionNew_Window_triggered()
{    
    mDialog = new MyDialog(this);
    mDialog->show();
}
cs

작업 파일

005_MyWindows_Second.zip