티스토리 뷰

728x90

QT의 다양한 클래스를 사용하여 프로그램을 작성하다 보면 나름 잘 정리된 매뉴얼과 풍부한 예제 코드 덕분에 편리하게 코딩을 진행할 수 있는 것이 사실이다. 그런데, 다양한 대화창을 통해서 사용자와 소통해야 하는 상황이라면 단순한 날 코딩만으로 대화창을 구성하고 수정하는 것은 너무나 비효율적이다. QT에서 제공하는 QT 디자이너를 활용하는 것이 지혜롭다. 다만, QT 디자이너를 사용하여 대화창을 작성 및 관리하는 체계를 활용한다면 UI 디자인 코드와 개발자의 코드가 섞이지 않도록 하는 것이 좋다.

 

 

QT 디자이너를 활용하여 대화창을 작성하는 과정을 정리하면 위의 그림과 같다. QT 디자이너를 사용하여 대화창을 작성하면 해당 디자인 내용은 *.ui 파일에 저장한다. 사용자 인터페이스 디자인 내용을 UIC(User Interface Compiler)로 컴파일하면 C++ 클래스가 생성되는데, 이 파일을 사용자 코드에 #include 하여 사용하면 사용자 코드와 QT 도구들이 자동적으로 생성하는 코드가 섞이지 않도록 할 수 있다. 최종적으로 QT 클래스를 사용하는 코드들은 MOC(Meta Object Compiler)로 컴파일하는 과정을 한번 더 거친 다음 본격적인 C++ 빌드에 들어가면 된다. 좀 더 상세하게 이 과정을 살펴보고자 한다.

 

sudo apt-get install qttools5-dev-tools

 

QT5와 QT6이 다르지만 QT5 기준으로는 우분투 계열 리눅스에서 위와 같이 간편하게 QT 디자이너와 연관도구들을 시스템에 설치할 수 있다.

 

우선 QT 디자이너에서 대화창을 작성한다. QDialog 템플릿을 이용하면 손쉽게 작성을 시작할 수 있다.

 

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>dlgPou</class>
 <widget class="QDialog" name="dlgPou">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>500</width>
    <height>198</height>
   </rect>
  </property>
  <property name="font">
   <font>
    <family>나눔고딕</family>
   </font>
  </property>
  <property name="windowTitle">
   <string>Create a new POU</string>
  </property>
  <property name="windowIcon">
   <iconset resource="dytoplc.qrc">
    <normaloff>:/images/new.png</normaloff>:/images/new.png</iconset>
  </property>
  <widget class="QWidget" name="formLayoutWidget">
......

 

QT 디자이너로 작성한 *.ui 파일의 내용은 위의 그림처럼 XML 파일 형태이다. XML 파일을 코드 형태로 바꾸어 주는 것이 UIC(User Interface Compiler) 도구이다.

 

$ uic -h
Usage: /usr/lib/qt5/bin/uic [options] [uifile]
Qt User Interface Compiler version 5.12.8

Options:
  -h, --help                    Displays this help.
  -v, --version                 Displays version information.
  -d, --dependencies            Display the dependencies.
  -o, --output <file>           Place the output into <file>
  -p, --no-protection           Disable header protection.
  -n, --no-implicit-includes    Disable generation of #include-directives.
  -s, --no-stringliteral        Deprecated. The use of this option won't take any effect.
  --postfix <postfix>           Postfix to add to all generated classnames.
  --tr, --translate <function>  Use <function> for i18n.
  --include <include-file>      Add #include <include-file> to <file>.
  -g, --generator <java|cpp>    Select generator.
  --idbased                     Use id based function for i18n

Arguments:
  [uifile]                      Input file (*.ui), otherwise stdin.


uic의 사용법은 위와 같다. 여러 옵션이 있지만 단순하게는 "uic  -o ui_dlgPou.h dlgPou.ui" 처럼 *.ui 파일을 컴파일하여 *.h로 출력하는 형태로 사용할 수 있다.

 

/********************************************************************************
** Form generated from reading UI file 'dlgPou.ui'
**
** Created by: Qt User Interface Compiler version 5.12.8
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_DLGPOU_H
#define UI_DLGPOU_H

#include <QtCore/QVariant>
#include <QtGui/QIcon>
#include <QtWidgets/QApplication>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialog>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_dlgPou
{
public:
    QWidget *formLayoutWidget;
    QFormLayout *formLayout;
    QLabel *label_2;
    QHBoxLayout *horizontalLayout_4;
    QLineEdit *PouName;
    QLabel *label_3;
    QHBoxLayout *horizontalLayout_2;
    QComboBox *PouType;
    QLabel *label_4;
    QComboBox *Language;
    QPushButton *but_ok;
    QPushButton *but_cancel;

    void setupUi(QDialog *dlgPou)
    {
        if (dlgPou->objectName().isEmpty())
            dlgPou->setObjectName(QString::fromUtf8("dlgPou"));
        dlgPou->resize(500, 198);
        QFont font;
//......

 

uic가 *.ui 파일을 가지고 자동적 만들어내는 코드는 위의 예제와 같다. 코드에서 우선하여 주목하여 볼 것은 uic가 자동으로 만들어 내는 파일이므로 사용자 코드를 이곳에 두지 말라는 주석 안내문이고, 개발자가 오브젝트의 이름으로 지정한 것에 "Ui_"를 앞에 붙여서 클래스를 만들어 놓았다는 점과 추후 UI 생성에 사용할 setupUi() 메서드이다.

 

#ifndef DLGPOU_H
#define DLGPOU_H
#include "ui_dlgPou.h"

class dlgPou : public QDialog
{
    Q_OBJECT

public:
    dlgPou(QWidget *pwnd);
    ~dlgPou();
    
private slots:
    void OnOK();
    void OnTypeChanged(int idx);

private:
    Ui::dlgPou *ui;
};
#endif //DLGPOU_H

 

위의 코드는 사용자 인터페이스 클래스를 포함시켜서 제작한 사용자 클래스로 주목하여 볼 것은 헤더 파일 #include와 QDialog를 상속하는 클래스 정의, Q_OBJECT 기술, 사용자 인터페이스 클래스 선언부이다. 시그널과 슬롯 연결 또한 QT 디자이너에서 수행하므로 사용자 클래스에서는 해당 슬롯을 정의하여 기술하기만 하면 된다.

 

#include "dlgPou.h"

dlgPou::dlgPou(QWidget *pwnd) : QDialog(pwnd), ui(new Ui::dlgPou)
{
    ui->setupUi(this);
    ui->Language->insertItem(0, "IL");
    ui->Language->insertItem(1, "ST");
}

dlgPou::~dlgPou()
{
    delete ui;
}

 

사용자 클래스 정의 헤더를 포함시킨 소스 코드에서는 클래스 생성 시 사용자 인터페이스 클래스의 인스턴스를 만들고 상위 클래스인 QDialog 생성자를 호출한 다음 setupUi() 메서드를 호출하는 것으로 사용자 인터페이스를 생성한다. 인터페이스 내부의 개별 오브젝트 또한 QT 디자이너에서 지정한 이름으로 접근할 수 있다. 클래스 종료 시 사용자 인터페이스의 인스턴스도 삭제해 주면 된다.

 

$ moc -h
Usage: /usr/lib/qt5/bin/moc [options] [header-file] [@option-file]
Qt Meta Object Compiler version 67 (Qt 5.12.8)

Options:
  -h, --help                  Displays this help.
  -v, --version               Displays version information.
  -o <file>                   Write output to file rather than stdout.
  -I <dir>                    Add dir to the include path for header files.
  -F <framework>              Add Mac framework to the include path for header files.
  -E                          Preprocess only; do not generate meta object code.
  -D <macro[=def]>            Define macro, with optional definition.
  -U <macro>                  Undefine macro.
  -M <key=value>              Add key/value pair to plugin meta data
  --compiler-flavor <flavor>  Set the compiler flavor: either "msvc" or "unix".
  -i                          Do not generate an #include statement.
  -p <path>                   Path prefix for included file.
  -f <file>                   Force #include <file> (overwrite default).
  -b <file>                   Prepend #include <file> (preserve default include).
  --include <file>            Parse <file> as an #include before the main source(s).
  -n <which>                  Do not display notes (-nn) or warnings (-nw).
                              Compatibility option.
  --no-notes                  Do not display notes.
  --no-warnings               Do not display warnings (implies --no-notes).
  --ignore-option-clashes     Ignore all options that conflict with compilers,
                              like -pthread conflicting with moc's -p option.

Arguments:
  [header-file]               Header file to read from, otherwise stdin.
  [@option-file]              Read additional options from option-file.

 

코드 빌드 이전의 마지막 작업은 Q_OBJECT를 기술하고 있는 클래스에 대한 moc 컴파일을 거치는 것으로 moc의 사용법은 위와 같다. 여러 옵션이 있지만 일반적인 사용법은 "moc dlgPou.h > moc_dlgPou.cpp" 처럼 Q_OBJECT를 기술하고 있는 사용 클래스 헤더 파일을 입력으로 하고 그 결과를 소스 코드 파일로 저장하여 빌드에 포함시키면 된다.

 

QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_dlgPou_t {
    QByteArrayData data[5];
    char stringdata0[31];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_dlgPou_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_dlgPou_t qt_meta_stringdata_dlgPou = {
    {
QT_MOC_LITERAL(0, 0, 6), // "dlgPou"
QT_MOC_LITERAL(1, 7, 4), // "OnOK"
QT_MOC_LITERAL(2, 12, 0), // ""
QT_MOC_LITERAL(3, 13, 13), // "OnTypeChanged"
QT_MOC_LITERAL(4, 27, 3) // "idx"

    },
    "dlgPou\0OnOK\0\0OnTypeChanged\0idx"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_dlgPou[] = {
 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
       2,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount ......

 

moc 컴파일 결과로 만들어지는 소스 코드는 위의 예제처럼 개발자가 직접 다루지 않는 QT 내부 코드라고 인식하는 것이 좋다. 빌드에만 사용하는 임시 파일에 해당하므로 "make clean"을 정의한다면 moc_*.cpp로 만들어지는 파일들도 삭제에 포함시킨다.

 

 

728x90