Qt를 처음 배우시는 분들을 위한, 간단한 GUI 프로그램 만들기 - HandyToonView

HDNua의 이미지

솔직히 Mac OS X에서 Qt가 잘 설치되었다면
이것보다는 깔끔하게 만들 수 있었을 것 같습니다.

Ubuntu에 Qt를 설치하고 여러 가지 라이브러리 함수를 테스트하고자
만들었던 간단한 만화 보기 프로그램이었습니다.
구현이 안 된 부분도 많고 여러 모로 강좌란에 올리기 부끄러운 작품이지만
제가 Qt를 처음 배우면서 한 삽질을, 이 글이 필요한 입문자 분들은
그나마 좀 덜 할 수 있으면 좋겠다는 생각이 들어 올려봅니다.

프로그램으로 사용하진 마시고, 이런 기능을 구현하려할 때
이런 함수를 쓴다는 정도만 알아가시면 딱 적당하리라 생각합니다.

-----

main.cpp

#include <QtGui/QApplication>
#include "mainwindow.h"
/***************************************************************************
  // 프로그램 동작 방식
  1. 사용자가 만화 파일이 들어있는 경로를 설정합니다.
  2. 이미지 파일의 갯수를 세어 정렬된 순서대로 동적 할당한 배열에 넣습니다.
  3. 프로그램 종료 시에 배열을 할당 해제합니다.
  ---   프로그램은 실제로 다음과 같이 동작합니다.
  1. 사용자가 이미지 파일을 클릭합니다.
  2. QStringList 멤버 변수에 해당 폴더에 있는 모든 파일의 이름을 넣습니다.
  3. ., ..과 같은 이름을 QStringList에서 삭제한 다음 sort() 메서드를 이용해 정렬합니다.
      (근데 sort()메서드는 인자 없이 어떻게 정렬을 했을까요.. 자동으로 오름차순 정렬인가.)
  4. 선택한 이미지 파일을 창에 뿌립니다.
 
  // 프로그램의 문제점
  1. 동적 할당한 변수를 제대로 해제하지 않고 있습니다.
 
  // 특징
  - 좌우 방향키를 이용해 페이지를 이동할 수 있다.
  - 상하 방향키를 이용해 프로그램을 최소화/최대화할 수 있다.
  - 페이지 기억 기능을 지원한다. 텍스트 파일에 경로명만 집어넣으면 끝.
  --- 3번째는 구현하지 않았습니다.
  *****************************************************************************/
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
 
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
#include <QtGui>
class QDir;
class QLabel;
class QImage;
class QMenuBar;
class QMenu;
class QFileDialog;
class QString;
class QStringList;
class MainWindow : public QMainWindow
{
    Q_OBJECT
private:
    // 화면 관리
    // 메뉴 관리
    QMenuBar *_menuBar;
    // 파일 관리
    QDir _dir;
    QString _fileName;
    QFileDialog *_fd;
    QStringList _filters;
    // 이미지 목록 관리
    int _first;
    int _last;
    int _index;
    QImage **_list;
    QStringList _entryList;
    // 이미지 출력
    QLabel *_node;
    QImage *_image;
private:
    // 초기화 함수
    void BasicValueSetting();
    void MenuSetting();
    void ImageListInit();
    void ImagePrintInit();
 
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    // 추가 메서드
    void glance();
    void printImage();
    void changeImageToLeft();
    void changeImageToRight();
    // 이벤트 처리
    void keyPressEvent(QKeyEvent *event);
//    void resizeEvent(QResizeEvent *event);
public:
    // 슬롯에 대한 함수
    void process_open();
    void print_developer();
public slots:
    // 슬롯 함수
    void open();    // 파일 오픈 슬롯
    void developer();
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <Qt>
#include <QtCore>
#include <QtGui>
#include <iostream>
#define BIG(X, Y) ( (X)>(Y)?(X):(Y) )
// 한 번의 초기화 이후에 다시 그 값을 설정하지 않음
#define ADD_ACTION_TO_MENU(MENU, ACTION_NAME, SIG, SLT) \
     action = new QAction(tr(ACTION_NAME), this);       \
     connect(action, SIGNAL(SIG), this, SLOT(SLT));     \
     MENU->addAction(action)
#define ADD_ACTION_TO_MENU_WITH_SHORTCUT(MENU, ACTION_NAME, SIG, SLT, SC) \
    ADD_ACTION_TO_MENU(MENU, ACTION_NAME, SIG, SLT); action->setShortcut(SC)
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 기본 값 설정
    this->BasicValueSetting();
    // 메뉴
    this->MenuSetting();
    // 이미지 목록 관리
    this->ImageListInit();
    // 이미지 출력
    this->ImagePrintInit();
}
MainWindow::~MainWindow()
{
}
// 내부 메서드
void MainWindow::BasicValueSetting()
{
    _dir = "/home";
    _fd = new QFileDialog(this);
    _fd->setDirectory(_dir);
    // 확장자 설정
    _filters << "Image files (*.png *.xpm *.jpg)";
    _fd->setNameFilters(_filters);
}
void MainWindow::MenuSetting()
{
    QMenu *menu;
    QAction *action;
    // 메뉴 초기화
    _menuBar = menuBar();
    menu = new QMenu(tr("&File"));
    _menuBar->addMenu(menu);
    ADD_ACTION_TO_MENU_WITH_SHORTCUT(menu, "&Open", triggered(), open(), QKeySequence::Open);
    ADD_ACTION_TO_MENU_WITH_SHORTCUT(menu, "E&xit", triggered(), close(), QKeySequence::Quit);
    menu->addSeparator();
    menu = new QMenu(tr("&Setting"));
    _menuBar->addMenu(menu);
    ADD_ACTION_TO_MENU(menu, "De&veloper", triggered(), developer());
}
void MainWindow::ImageListInit()
{
    _first = -1;
    _last = -1;
    _index = -1;
    _list = 0;
}
void MainWindow::ImagePrintInit()
{
    _node = new QLabel(this);
    _image = new QImage();
}
// 추가 메서드
void MainWindow::glance()
{
    // 본래 목적은 이미지를 읽어오기 전에 한 번 훑어봄으로써
    // 이미지를 로드하는 속도를 높이려는 것이었는데,
    // 그 목적 대신 폴더에서 이미지를 가져와서
    // _entryList라는 리스트에 넣는 함수가 되어버렸다.
    int i;
    _entryList = _dir.entryList();
    std::cout<<"First"<<std::endl;
    for (i=0; i<_entryList.count(); ++i)
        std::cout<<_entryList[i].toStdString()<<std::endl;
    // ., ..과 같은 것도 경로로 치는 모양이다.
    _entryList.removeOne(".");
    _entryList.removeOne("..");
    _entryList.sort();
    std::cout<<"Second"<<std::endl;
    for (i=0; i<_entryList.count(); ++i)
        std::cout<<_entryList[i].toStdString()<<std::endl;
    _last  = _entryList.count() - 1;
    _index = _entryList.indexOf(_fileName);
    _first = 0;
    std::cout<<"First: "<<_first<<std::endl;
    std::cout<<"Index: "<<_index<<std::endl;
    std::cout<<"Last : "<<_last<<std::endl;
}
void MainWindow::printImage()
{
    QString fileName = _entryList.value(_index);
    fileName = _dir.absoluteFilePath(fileName);
    if( _image->load(fileName) )
    {
        double d_width = _image->width();
        double d_height = _image->height();
        double ratio = d_width/d_height;
        d_height = this->geometry().height(); // geometry() 메서드는 현재 창의 크기를 가져온다.
        d_width = ratio * d_height;
        int width = d_width;
        int height = d_height;
        // 이건.. 도움말 보고 짜맞춘 겁니다. 3, 4번째 인자는 레퍼런스를 참조하세요.
        QImage image = _image->scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
        // 이미지를 창에 뿌리기 전에 현재 창의 크기를 이미지에 맞게 조절합니다.
        this->setGeometry(0, 0, this->geometry().width(), this->geometry().height());
        // 이미지를 가져와서 이미지의 크기를 조절합니다.
        _node->setPixmap(QPixmap::fromImage(image));
        _node->setGeometry(this->geometry().width()/2 - width/2,
                           this->geometry().height()/2 - height/2,
                           width,
                           height);
        // 창에 이미지를 뿌립니다.
        _node->show();
    }
}
void MainWindow::changeImageToLeft()
{
    if( --_index<_first )
        _index = _last;
    std::cout<<"First: "<<_first<<std::endl;
    std::cout<<"Index: "<<_index<<std::endl;
    std::cout<<"Last : "<<_last<<std::endl;
    this->printImage();
}
void MainWindow::changeImageToRight()
{
    if( ++_index>_last)
        _index = _first;
    std::cout<<"First: "<<_first<<std::endl;
    std::cout<<"Index: "<<_index<<std::endl;
    std::cout<<"Last : "<<_last<<std::endl;
    this->printImage();
}
// 이벤트 처리
void MainWindow::keyPressEvent(QKeyEvent *event)
{
    int key = event->key();
    switch (key)
    {
    // 방향키에 대한 이벤트 설정
    case Qt::Key_Up:
//        std::cout<<"Up"<<std::endl;
        if( this->isMaximized() )
            this->showNormal();
        else
            this->showMaximized();
        break;
    case Qt::Key_Right:
//        std::cout<<"Right"<<std::endl;
        this->changeImageToRight();
        break;
    case Qt::Key_Down:
//        std::cout<<"Down"<<std::endl;
        this->showMinimized();
        break;
    case Qt::Key_Left:
//        std::cout<<"Left"<<std::endl;
        this->changeImageToLeft();
        break;
    // 기타 키에 대한 이벤트
    case Qt::Key_Return:
    case Qt::Key_Enter:
//        std::cout<<"Enter"<<std::endl;
        this->process_open();
        break;
    }
}
/* : 메인 윈도우의 창 크기가 변경될 때 호출되는 이벤트입니다.
void MainWindow::resizeEvent(QResizeEvent *event)
{
    QSize size = event->size();
    std::cout<<size.width()<<std::endl;
    std::cout<<size.height()<<std::endl;
    // (1215, 747) / (1215, 776)
}*/
// 슬롯에 대한 함수
void MainWindow::process_open()
{
    QString filePath = _fd->getOpenFileName(this);
    if( !filePath.isNull() )
    {
        QDir dir = QFileInfo(filePath).absoluteDir();
        _dir = dir;
        _fileName = _dir.relativeFilePath(filePath);
        this->glance();
        this->printImage();
    }
}
void MainWindow::print_developer()
{
    // 개발하다 말았습니다.
}
// 슬롯 메서드
void MainWindow::open()
{
    this->process_open();
}
void MainWindow::developer()
{
    this->print_developer();
}

-----

봐주셔서 감사합니다.

Forums: 
익명 사용자의 이미지

이제막 Qt를 새로시작하게 됐습니다.

좋은 정보 감사합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.