박영선(youngsun@i-top.co.kr)
날짜: 1999.12.26
컴파일러: gcc
언어 : C++ (QT 1.44 Library 사용)
환경 : 알짜리눅스 6.1 XWindow환경
===============================================================
<들어가기전에...>
안녕하세요.
(주)아이탑에서 개발연구원으로 일하고 있는 박영선이라고 합니다.
우선, 간단하게 이 자료의 내용을 소개한 뒤 시작하기로 하죠.
제가 리눅스를 처음 접하고, 또 Gtk와 QT를 만나게 되면서, 실제 저
희회사 제품개발당시 힘들었던 점과 개발 후에 느꼈던 미비한 점등
을 참고해서 이 프로젝트를 계획하게 되었습니다.
이 프로젝트는 저희회사 개발원들이 개발했던 Topflash라는, 네
트워크, 프락시, 방화벽, 메신저등을 종합적으로 관리할 수 있는
통합인터넷솔루션중 제가 개발한 TopmanagerX를 기반으로 만들어졌
습니다.
이는 QT라이브러리와, ANSI-C기반의 file처리루틴을 라이브러리로
묶어 구현한 내용입니다.
사정상 이 중에서 네트워크관리를 QT를 사용하여 구현해보도록 하겠
습니다.
다음에는 Gtk를 이용한 버젼을 다른 처리 내용의 글로 올리겠습니다.
문의사항이나 개선점을 발견하신분들은 위의 E-mail주소로 꼭 연락
해주시기 바랍니다.
그리고 Gtk의 글이 끝나면 마지막으로 Kernel분석에 관한 자료를 만
들까 생각중입니다.
많은 관심과 격려 부탁드립니다.
===============================================================
<목차>
0. RedHat Linux 6.1의 System관리File에 관하여...
0.1. /etc디렉토리에 있는 file들
0.1.1. /etc/conf.modules
0.1.2. /etc/sysconfig/network-scripts/ifcfg-eth0
0.1.3. /etc/sysconfig/network
1. 이중연결리스트를 이용한 File처리 루틴에 관하여...
1.1. 이중연결리스트(Double LinkedList) 구현
1.2. file처리 라이브러리 구현
1.3. 개선되어야할 내용
2. QT 기본 다지기
2.1. QT의 Class계층정보
2.2. Widget이란?
2.2.1. 위젯(QWidget), 프레임(QFrame), 버튼(QButton)
2.2.2. Makefile 만들기
2.2.3. 라벨(QLabel)
2.2.4. 편집박스(QLineEdit)
2.2.4.1 QObject::connect, SIGNAL과 SLOT
2.2.5. 콤보박스(QComboBox)
2.2.6. 리스트(QListView)
2.2.7. 체크박스(QCheckBox), 라디오버튼(QRadioButton)
2.2.8. 메뉴(QMenu)
2.2.9. 툴바(QToolBar)와 메인윈도우(QMainWindow)
2.3. QT를 얻으려면?
< 한마디 >.
3. 기본 Format 작성하기
3.1. 메뉴 만들기
3.2. 툴바 집어넣기
3.3. Network관련 라이브러리 구현하기
3.4. Class만들기
3.4.1 일반네트워크관리
3.4.2 DNS관리
3.4.3 IP-Aliasing관리
< *** 전체 Source *** >
3.5. 개선되어야할 내용
4. 프로젝트를 마치고...
===============================================================
< 0. RedHat Linux 6.1의 System관리File에 관하여... >
0.1. /etc디렉토리에 있는 file들
0.1.1. /etc/conf.modules
이 file은 하드웨어에 새로운 디바이스가 추가되었을 때 해당 디바이스모
듈을 알기 쉬운 이름으로 속이는 역할을 한다.
예를들어 Lan Card의 Chipset이름이 3c59x라면 /etc/conf.modules에는 다
음과 같은 내용이 들어가야한다.
alias eth0 3c59x
여기서 3c59x는 엄밀히 말하자면 Chipset의 이름이 아니라 리눅스에서 제
공하는 Network Module의 이름이다. 리눅스의 제공 Network Module들은
각각 Chipset이름에 1:1 매치된다.(여기서의 Module이란 Windows에서의
드라이버개념과 비슷하다.)
Module file들은 *.o로 끝나며 이들에 대한 정보는
/lib/modules/2.2.12-20kr/net 디렉토리에 있다.
위의 "alias eth0 3c59x"의 의미는 3c59x모듈을 eth0로 aliasing해서 쓰겠
다는 뜻이다.
만약 Lan Card가 두장이상 시스템에 설치되어있다면 아래와 같은 정보가 추
가되어있을 것이다.
alias eth1 '모듈이름'
alias eth2 '모듈이름'
만약 카드가 ISA라면 이 file안에서 io와 irq번호를 수동으로 설정해주어야
한다. 원래 io, irq또한 설정하는 루틴도 같이 넣으려고 했지만, 요즈음
90% 이상 PCI카드를 사용하고 있으므로 이 설정은 뺐다.
0.1.2. /etc/sysconfig/network-scripts/ifcfg-eth0
위 작업을 마쳤다면 다음 file의 내용을 scan해보자.
cat /etc/sysconfig/network-scripts/ifcfg-eth0
다음과 같은 내용이 출력될 것이다.
DEVICE=eth0
BROADCAST=172.31.255.255
IPADDR=172.31.0.202
NETMASK=255.255.0.0
NETWORK=172.31.0.0
ONBOOT=yes
물론 IP, Netmask, Network, Broadcast등은 이렇게 되어있지는 않을 것이다.
맨 마지막줄의 "ONBOOT=yes"의 뜻은 시스템의 부팅시에 모듈을 올릴 것인지에
대한 옵션이다.
이 file이 없다면 에디터를 써서 file을 생성시키기 바란다.
이렇게 setting을 하고, 만약 Lan Card의 Chipset이 3c59x에 해당되는 모듈에
매치된다면 다음 명령어를 실행해보라.
/etc/rc.d/init.d/network restart
이 명령어를 실행하게 되면 모듈올리기에 성공하는지 실패하는지를 나타내준다.
만약 성공한다면 마지막으로 다음 명령어를 실행해본다.
ifconfig
이 명령어에 대한 필자의 시스템에서의 결과값은 다음과 같다.
eth0 Link encap:Ethernet HWaddr 00:90:27:A2:70:DE
inet addr:172.31.0.1 Bcast:172.31.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:88055 errors:0 dropped:0 overruns:0 frame:0
TX packets:119306 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:17 Base address:0xef00
eth1 Link encap:Ethernet HWaddr 00:90:27:57:17:27
inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:16 Base address:0xef40
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:3924 Metric:1
RX packets:146 errors:0 dropped:0 overruns:0 frame:0
TX packets:146 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
위의 결과값에서 볼 수 있듯이 필자의 IP(가상)은 "inet addr:172.31.0.1"에
명시되어 있고 나머지 Broadcast와 Netmask에 대한 정보 또한 나타나있다.
여기서 eth1에 대한 정보는 두번째 Lan Card를 장착했을 때, 또 그 Card를
OS에서 올바르게 인식했을 때 나타나는 정보이다. 설정 file들에 대한 정보는
eth0와 흡사하다.
출력결과중 세번째 단락의 "lo"는 localhost에 대한 정보이다. 이 결과값은
kernel에서 default로 setting하여주므로 별로 신경쓸 필요가 없다.
0.1.3. /etc/sysconfig/network
이 file은 호스트네임과 게이트웨이를 설정하는 file이다.
이 file을 cat해보자.
NETWORKING=yes
FORWARD_IPV4=false
HOSTNAME=superbug.i-top.co.kr
GATEWAY=172.31.0.200
위의 처음 두줄은 특별한 셋팅이 필요한 경우를 제외하고는 별로 설정값을
바꿀 일이 없으리라 생각한다.
레드헷 6.x이전 버젼까지는 이곳에 DOMAINNAME과 GATEWAYDEV항목이 들어있
었으나 6.x이후부터는 그 부분이 빠졌다. 이 항목들은 있어도 그만, 없어도
그만이므로 신경쓸 필요는 없다. DOMAINNAME항목은 HOSTNAME에 포함이 되었
고, GATEWAYDEV는 디폴트로 eth0를 셋팅하도록 되어있다.
< 1. 이중연결리스트를 이용한 File처리 루틴에 관하여... >
-> File처리루틴에 이중연결리스트를 이용한 목적:
File처리루틴은 이중연결리스트를 이용한 방법을 선택하였다.
연결리스트를 선택한 가장 중요한 이유는 확장성의 용이함때문이다.
가장 흔한 File처리방법은 DB(ISAM, Postgre sql, mysql기타등등)를
이용하는 방법이 있고, 혹은 File에서 한개의 문자씩 읽어들여 처리
하는 방법등 여러가지가 있다.
하지만 DB를 이용하는 방법은 대용량 데이터를 처리할 때, 그리고
데이터 포맷이 정형화되어있을 때 쓰는 방법이고, 한개의 문자씩 읽
어들여 처리하는 방법은 한두개정도의 File을 처리할 때나 유용한
방법이므로 범용적이지 못하다.
Linux의 Network 관련 File들은 각각의 정보가 모두 정형화되어있지도
않고 또한 자주 업그레이드되는 정보들이므로 범용적인 처리루틴이 필
요하다.
다른 좋은 방법들도 있겠지만, 그리고 문자처리방식을 확장시키는 방
법도 있겠지만, 손쉽게 File을 처리하기 위해, 메모리관리를 확실하게
하기위해 연결리스트를 사용했다. 여러분께서 더 좋은 방법이나 혹은
더 좋은 루틴을 가지고 계신분은 질타와 가르침을 주시기 바란다.
===============================================================
1.1. 이중연결리스트(Double LinkedList) 구현
이중연결리스트에 대한 source는 인터넷상에 널려있고,
학부과정에서도 지겹게 다루는 부분이므로 이 부분에 대
한 설명은 접어두기로 하고 file내용만 보여주도록 하겠다.
--- < source 1.1.1 Double LinkedList. Filename:procfile.h > ---
#include <stdio.h>
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#ifndef SUCCESS
#define SUCCESS 1
#define FAIL 0
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef _DOUBLE_LINKLIST_
#define _DOUBLE_LINKLIST_
typedef struct tagDOUBLELINK {
void *pzKey;
struct tagDOUBLELINK *next;
struct tagDOUBLELINK *prev;
} DList;
void initDL(void);
DList *searchDL(void *pzSearch);
DList *insertAsFirstDL(void *pzInsert);
DList *insertDL(void *pzInsert, DList *pIns); /* insert front node p */
DList *insertDLAsKey(void *pzInsert, void *pzSearch);
DList *insertDLAsSort(void *pzInsert);
int deleteDL(DList *pDel);
int deleteDLAsKey(void *pzSearch);
void deleteAllDL(void);
void deletePerfectDL(void);
void *printDL(DList *pPrint);
#endif
#endif
----------------------------------------------------------------
--- < source 1.1.2 Double LinkedList. Filename:procfile.c > ---
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include "proc_file.h"
DList *dblhead;
DList *dbltail;
void initDL(void)
{
dblhead = (DList *)malloc(sizeof(DList));
dbltail = (DList *)malloc(sizeof(DList));
dblhead->prev = dblhead;
dblhead->next = dbltail;
dbltail->prev = dblhead;
dbltail->next = dbltail;
}
DList *searchDL(void *pzSearch)
{
DList *s;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzSearch, sizeof(pzSearch)) == 0)
break;
s = s->next;
}
return s;
}
DList *insertAsFirstDL(void *pzInsert)
{
DList *p;
p = (DList *)malloc(sizeof(DList));
p->pzKey = pzInsert;
p->next = dblhead->next;
dblhead->next->prev = p;
dblhead->next = p;
p->prev = dblhead;
return p;
}
DList *insertAsLastDL(void *pzInsert)
{
insertDL(pzInsert, dbltail);
return (DList *)NULL;
}
DList *insertDL(void *pzInsert, DList *pIns) /* insert front node pIns */
{
DList *s;
if (pIns == dblhead)
return NULL;
s = (DList *)malloc(sizeof(DList));
s->pzKey = pzInsert;
pIns->prev->next = s;
s->prev = pIns->prev;
s->next = pIns;
pIns->prev = s;
return s;
}
DList *insertDLAsKey(void *pzInsert, void *pzSearch)
{ /* insert insk front findk */
DList *s;
DList *r = NULL;
s = searchDL(pzSearch);
if (s != dbltail)
{
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
}
return r;
}
DList *insertDLAsSort(void *pzInsert)
{
DList *s;
DList *r;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzInsert, sizeof(pzInsert)) >= 0)
break;
s = s->next;
}
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
return r;
}
int deleteDL(DList *pDel)
{
if (pDel == dblhead || pDel == dbltail)
return FAIL;
pDel->prev->next = pDel->next;
pDel->next->prev = pDel->prev;
free(pDel);
return SUCCESS;
}
int deleteDLAsKey(void *pzSearch)
{
DList *s;
s = searchDL(pzSearch);
if (s != dbltail)
{
s->prev->next = s->next;
s->next->prev = s->prev;
free(s);
return SUCCESS;
}
return FAIL;
}
void deleteAllDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
dblhead->next = dbltail;
dbltail->prev = dblhead;
}
void deletePerfectDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
free(dblhead);
free(dbltail);
}
void *printDL(DList *pPrint)
{
pPrint = dblhead->next;
while (pPrint != dbltail)
{
printf("%s", (char *)(pPrint->pzKey));
pPrint = pPrint->next;
}
return pPrint->pzKey;
}
------------------------------------------------------------------------
1.2. file처리 라이브러리 구현
자, 이젠 이중연결리스트를 이용하여 File처리를 해보도록 하자.
가장 기본적인 algorithm은 다음과 같다.
< 연결리스트를 이용한 file처리 algorithm 1.2.1 >
1) file을 읽기모드로 열고
2) 한 라인을 읽어들인다.
3) 읽어들인 라인이 Search 조건에 맞는지 검사한다.
3-1) 조건에 맞으면 4)로 이동.
3-2) 맞지 않으면 연결리스트의 node에 삽입하고 2)로 이동
4) 조건에 따라 연결리스트의 node에 처리한(삽입 혹은 삭제, 변경등)
문자열을 삽입한다.
5) 처리가 다 끝나면 file을 닫는다.
6) file을 쓰기모드로 연다.
7) 메모리에 적재하고 있는 연결리스트의 각 노드들을 순서대로 file에
print한다.
8) file을 닫는다.
이렇게만 써놓으면 잘 이해가 가지 않을 것이다.
그러므로 이제부터 source와 그에 따른 algorithm을 토대로 설명하겠다.
우선 algorithm 1.2.1의 2)과정에 해당하는 함수를 만들어보자.
-------- < source 1.2.1 readOneLineSearchFile > -------------------
int readOneLineSearchFile(char pszBuffer[], FILE *fp)
{
int i = 0;
char c = '\0';
do
{
c = getc(fp); /* file에서 한 문자를 읽어들인다. */
pszBuffer[i++] = c;
/* 실제 file처리함수에서 사용하게될 buffer에 읽 */
/* 어들인 문자를 삽입한다. */
if (c == EOF || c == '\n')
/* 문자가 file의 끝이거나 캐리지리턴값이면 */
break;
/* loop를 빠져나간다. 즉, 개행문자를 만나면 */
} while (1); /* 그때까지의 문자를 모두 버퍼에 저장하고 */
/* loop를 끝낸다. */
pszBuffer[i] = '\0'; /* 버퍼에 들어있는 개행문자나 EOF를 삭제한다. */
nLine++; /* file의 line수를 체크하기 위한 외부변수이다. */
/* 한 라인을 읽고나서 1씩 증가시킨다. */
return c; /* 개행문자'\n', 혹은 EOF가 return될 것이다. */
}
-------------------------------------------------------------------
다음으로 연결리스트의 내용을 file에 프린트하는 함수를 구현해보도록 하자.
연결리스트에 관한 함수들은 위에서 언급한 바와 같다.
간단하므로 이해하기도 쉬울 것이다.
--------------- < source 1.2.2 printToFileDL > -------------------
void *printToFileDL(DList *pPrint, FILE *fp)
{
pPrint = dblhead->next; /* node pPrint를 초기화한다. */
while (pPrint != dbltail) /* pPrint가 마지막이 될 때까지 반복해서 */
{
fprintf(fp, pPrint->pzKey); /* file에 프린트한다. */
pPrint = pPrint->next; /* node를 다음으로 이동 */
}
return pPrint->pzKey;
}
-------------------------------------------------------------------
아래 소개할 source 1.2.3이 바로 위에서 언급한 algorithm에 의한 루틴이다.
Code line 우측에 각각 해당하는 algorithm의 번호를 표시해놓겠다.
이 함수는 file(pszFileName)에서 특정 문자열(pszSearch)이 들어있는 라인
을 찾아 그 라인을 통째로 삽입할 문자열(pszInsert)로 바꾸는 함수이다.
만약 검색할 문자열이 발견되지 않으면 file은 아무 변화도 일어나지 않는다.
--------------- < source 1.2.3 insertStrToFile > -------------------
void insertStrToFile(char *pszInsert, char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
/* nMaxLine과 nMaxCol을 인자로 두는 이유는 메모리를 관리하기 */
/* 위해서이다. 즉 pszBuffer 이중포인터변수를 전역변수로 잡지 */
/* 않기 위해서인데, 만약 이 변수를 전역변수로 잡게되면 프로 */
/* 그램은 항상 nMaxLine*nMaxCol byte만큼의 메모리를 차지하게 */
/* 되므로 메모리의 막대한 낭비를 초래할 수 있는 문제점이 있다.*/
/* 이 루틴에서는 file의 최대 line수와 최대 coloum수를 인자로 */
/* 받아서 그 인자 만큼에 해당되는 메모리를 할당하여 준다. */
/* 이렇게 처리했을 때의 메모리의 사용량은, 이 루틴이 실행되었 */
/* 을 때만 nMaxLine*nMaxCol byte만큼 메모리에 할당이 되기때문 */
/* 에, 기본메모리가 큰 linux시스템에서는 별 무리 없이 사용할 */
/* 수 있다. */
nLine = 0;
fp = fopen(pszFileName, "r"); /* algor 1) file을 읽기모드로 연다. */
if (fp == NULL) /* file이 존재하지 않는다면 아무처리 없이 끝낸다. */
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL(); /* double linkedlist를 초기화한다. */
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
/* algor 2) file로부터 한라인(pszBuffer[i])씩 읽어들인다. */
{
/* algor 3-2) 읽어들인 라인(pszBuffer[i])이 검색할 문자열 */
/* (pszSearch)을 포함하고 있지 않다면 */
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
/* algor 3-2) node에 삽입한 후 for (i = 0; ~)로 이동해 다음라인 */
/* 을 읽어들인다.(algor 2) */
insertAsLastDL(pszBuffer[i]);
else /* algor 3-1) 검색할 문자열이 포함되어있다면 */
/* algor 4) 검색한 문자열이 포함된 line대신 삽입할 문자열을 연 */
/* 결리스트에 삽입한다. */
insertAsLastDL(pszInsert);
}
/* 만약 위 for 루틴에서 검색할 문자열이 발견되지 않는다면 연결리스트 */
/* 에는 file의 모든 라인을 가지고 있는 node들만이 있을 것이다. */
/* 이런 상태에서 바로 아래쪽의 printToFileDL함수를 호출하게 되면 */
/* file의 내용이 아무것도 변하지 않으면서 그 file자체에 내용을 복사 */
/* 하게 되는 것이다. */
fclose(fp); /* algor 5) file을 닫는다. */
fp = fopen(pszFileName, "w"); /* algor 6) file을 쓰기모드로 연다. */
printToFileDL(dblhead->next, fp); /* algor 7) 메모리에 적재하고 */
/* 있던 연결리스트의 모든 노드 */
/* 들을 순서대로 file에 print한다.*/
deletePerfectDL(); /* 연결리스트 초기화시 할당했던 메모리를 회수한다.*/
fclose(fp); /* algor 8) file을 닫는다. */
for (i = 0; i < nMaxLine; i++) /* 버퍼에 할당했던 메모리를 해제(회수)*/
free(pszBuffer[i]);
}
-------------------------------------------------------------------
이중연결리스트의 구조를 알고있다면 이해하기가 상당히 쉬울 것이다.
이 함수를 이해하지 못했다면 다음으로 넘어가지 못하므로 철저하게 이해하기
바란다. 이 함수의 쓰임새는 두가지가 있다.
첫번째, 특정문자열 바꾸기.
두번째, 특정문자열이 속한 라인 지우기.
여기서 두번째쓰임새의 내용을 보라. 특정문자열(pszSearch)이 속한 line을 지
우려면 어떻게 해야할까? 너무도 쉬운 내용이다.
인자 pszInsert에 "", 즉, 공백문자열을 넘겨주면 된다.
이 함수는 특정 file을 임시파일로 저장하는 과정 없이 바로 그 file을 갱신한
다. 임시파일로 저장하는 것과 메모리에 저장하는 것은 서로 장단점이 있는데
그에 대한 설명은 언급하지 않아도 잘 아시리라 믿는다.
strstr함수에 관한 내용은 man페이지를 참조하거나 library 메뉴얼을 참고하기
바란다.
다음으로 구현할 함수는 위의 함수와 거의 비슷하지만 약간 다르다. 위의 함수
에서는 검색문자열(pszSearch)이 없을 경우 아무일도 일어나지 않지만 이 함수
는 문자열이 검색되지 않으면 file의 맨 끝에 삽입할 문자열(pszInsert)을 끼
워넣게 된다.
검색할 문자열이 만약 검색된다면 바로 아무일을 하지 않고 루틴을 끝낸다.
대부분의 과정은 위에서 설명했으므로 새로운 주목해야할 부분만을 Comment
하겠다.
--------------- < source 1.2.4 insertStrToFileLast > -------------------
void insertStrToFileLast(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else /* 검색할 문자열(pszSearch)이 검색 된다면 루틴을 끝낸다. */
{
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
fclose(fp);
deletePerfectDL();
return;
}
}
/* 만약 문자열(pszSearch)이 검색되지 않았다면 이부분으로 넘어오게 */
/* 된다. 이 경우 바로 아래와 같이 삽입할 문자열을 연결리스트의 맨 */
/* 끝노드에 삽입을 시키고 file로 프린트한다. */
insertAsLastDL(pszInsert);
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
}
-------------------------------------------------------------------
이번에 설명할 함수는 file에서 특정문자열을 찾아 그 문자열을 원하는
문자열로 바꾸어주는 함수이다.
위에서 설명한 insertStrToFile함수와는 다른 역할을 한다.
즉, insertStrToFile함수는 line단위로 문자열을 삽입, 변경하는 반면
changeStrToOneLineToFile함수는 문자열단위로 변경하는 함수이다.
기본 algorithm은 < algorithm 1.2.1 >에 의거한다.
만약 위의 과정을 모두 이해했다면 이 함수도 별 어려움이 없을 것이다.
이 함수는 changeSearchStr이라는 함수를 call하는데, changeSearchStr
함수는 어떤 주어진 문자열(pszLine)에서 특정문자열(pszSearch)를 찾아
삽입할 문자열(pszIns)로 바꾸는 역할을 한다. 한 라인(pszLine)에 여러
개의 특정문자열(pszSearch)가 있을 수 있으므로 재귀호출(recursive
call)을 사용했다. 기본적인 C문법을 알고있다면 루틴 자체는 이해하기
쉬울 것이라 믿는다.
위와 마찬가지로 중복되는 Comment는 생략하겠다.
-------- < source 1.2.5 changeStrToOneLineToFile > -----------
void changeSearchStr(char *pszIns, char *pszSearch, char *pszLine,
char *pszSave, int nMaxCol)
/* 여기서 포인터 인자인 pszSave는 바뀐 라인을 저장할 공간이다. */
{
int i = 0;
char *pszPos;
int nPos;
/* 라인(pszLine)에서 검색문자열(pszSearch)의 위치를 계산하기 */
/* 위해 검색문자열의 pszLine에서의 포인터 위치를 저장해둔다. */
pszPos = (char *)strstr(pszLine, pszSearch);
/* 검색문자열의 위치를 계산한다. */
nPos = pszPos-pszLine;
/* 검색문자열의 위치(nPos)까지 pszSave에 pszLine의 내용을 복사 */
for (i = 0; i < nPos; i++)
pszSave[i] = pszLine[i];
/* 저장할 변수(pszSave)에 바꿀 문자열(pszIns)를 붙힌다.*/
strcat(pszSave, pszIns);
/* 저장할 변수(pszSave)에 그 이후의 문자열을 붙힌다. */
strcat(pszSave, pszLine+nPos+strlen(pszSearch));
/* 더이상 검색문자열(pszSearch)가 */
pszPos = (char *)strstr(pszSave, pszSearch);
/* 존재하지 않는다면 루틴을 끝낸다. */
if (pszPos == NULL)
return;
memset(pszLine, '\0', nMaxCol);
/* 바뀐 라인(pszSave)를 원래 라인에 복사한다. */
strcpy(pszLine, pszSave);
/* 재귀호출을 위해 pszSave를 초기화한다. */
memset(pszSave, '\0', nMaxCol);
/* 첫번째 만나는 검색문자열을 바꾸었으므로 두번째, 세번째등등 */
/* 의 문자열을 바꾸기 위해 재귀호출을 한다. */
changeSearchStr(pszIns, pszSearch, pszLine, pszSave, nMaxCol);
}
/* 이제 한 라인에 대한 문자열변경루틴은 끝냈으므로 위 함수에 실제 */
/* file에서 한 라인씩 읽어들여 인자로 넘겨주면 된다. */
void changeStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0, j = 0;
FILE *fp;
/* pszLine은 바뀐 문자열을 저장할 공간이다. */
char *pszLine[nMaxLine];
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
pszLine[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszLine[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
/* 한 라인씩 읽어들여서 */
{
/* 검색문자열(pszSearch)이 없다면 연결리스트에 삽입한다.*/
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
/* 검색문자열이 있다면 changeSearchStr함수를 이용해 검색문자열이 */
/* 포함된 라인을 수정하고 수정된 라인을 연결리스트에 삽입한다. */
else
{
changeSearchStr(pszIns, pszSearch, pszBuffer[i], pszLine[j],
nMaxCol);
insertAsLastDL(pszLine[j]);
j++;
}
}
fclose(fp);
/* file 쓰기 */
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
{
free(pszBuffer[i]);
free(pszLine[i]);
}
}
-------------------------------------------------------------------
예상외로 간단할 것이다. 특정 기능(예를들어, 삭제, 삽입, 변경등)에
대한 함수들의 구현은 위 방법과 구조가 항상 같다.
이는 연결리스트를 사용했을때의 장점이 유감없이 발휘된 특징중 하나
이다. 즉, 이해하기 쉽고 구현이 용이하고, 또 가장 중요한 범용성이
있다는 점이다.
이번엔 위의 source들의 구조를 약간 응용해서 구조화된 file의 처리
루틴을 작성해보기로 하자.
아래의 함수는 검색문자열(pszSearch)이 file(pszFileName)에 없다면
삽입할 문자열(pszInsert)을 라인으로 삽입하고 검색문자열이 존재한
다면 그 검색문자열을 포함한 라인을 삽입할 문자열로 바꾸는 함수이
다. 위의 함수들과 다른점이 있다면 이 함수에는 flag를 두었다는 점
이다. 이 flag는 file에 검색된 라인을 삽입문자열로 바꿀 것인지 아
니면 그냥 삽입문자열을 라인단위로 삽입할 것인지를 결정한다.
만약 문자열이 검색되지 않는다면 flag는 TRUE로 setting된다.
-------- < source 1.2.6 addOnNotExistChangeOnExist > -----------
void addOnNotExistChangeOnExist(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
/* 삽입할 지 혹은 변경할 지의 flag(flgAdd) */
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
/* 만약 검색문자열(pszSearch)이 검색된다면 */
else
{
/* 삽입문자열(pszIns)을 node에 삽입하고 */
insertAsLastDL(pszIns);
/* flag를 FALSE로 reset시킨다. */
flgAdd = FALSE;
}
}
fclose(fp);
/* flag가 TRUE라면 검색문자열이 발견되지 않았다는 것이므로 */
/* 링크리스트의 맨 끝node에 삽입문자열을 삽입한다. */
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
/* file 쓰기 */
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
-------------------------------------------------------------------
간단하게 몇줄만 추가 혹은 변경시켜 기능을 바꾸어놓았다.
앞으로 설명하게될 모든 file처리관련 함수들이 다 이런식이다.
이 routine을 응용하여 갖가지 source들을 재생성해보시기를...
이제부터는 위의 routine들은 물론 필자가 구현해놓은 모든 procedure들
을 한꺼번에 보여주기로 하겠다. 그리고 설명은 되도록 기능 위주로 하
겠다. 위 과정까지 모두 이해했다면 별다른 Comment없이도 이해할 수
있을 것이다.
-------- < source 1.2.7 total functions > -----------
/**************** proc_file.h *****************/
#include <stdio.h>
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#ifndef SUCCESS
#define SUCCESS 1
#define FAIL 0
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef _DOUBLE_LINKLIST_
#define _DOUBLE_LINKLIST_
typedef struct tagDOUBLELINK {
void *pzKey;
struct tagDOUBLELINK *next;
struct tagDOUBLELINK *prev;
} DList;
void initDL(void);
DList *searchDL(void *pzSearch);
DList *insertAsFirstDL(void *pzInsert);
DList *insertDL(void *pzInsert, DList *pIns); /* insert front node p */
DList *insertDLAsKey(void *pzInsert, void *pzSearch);
DList *insertDLAsSort(void *pzInsert);
int deleteDL(DList *pDel);
int deleteDLAsKey(void *pzSearch);
void deleteAllDL(void);
void deletePerfectDL(void);
void *printDL(DList *pPrint);
void deleteStrFromOneLine(char *pszSave, char *pszDel, char *pszLine);
void insertStrForeSearch(char *pszSave, char *pszInsert,
char *pszSearch, char *pszLine, char *pszTempLine);
void insertStrToOneLine(char *pszSave, char *pszIns, char *pszLine);
void changeSearchStr(char *pszIns, char *pszSearch, char *pszLine,
char *pszSave, int nMaxCol);
void *printToFileDL(DList *pPrint, FILE *fp);
int readOneLineSearchFile(char pszBuffer[], FILE *fp);
void insertStrToFile(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertStrToFileLast(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
int countAllLineToFile(char *pszFileName, int nMaxLine, int nMaxCol);
int countSearchLineToFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol);
int saveAllLineToFile(char *pszSaveListStr[], char *pszFileName,
int nMaxLine, int nMaxCol);
int saveSearchLineToFile(char *pszSearch, char *pszSaveListStr[],
char *pszFileName, int nMaxLine, int nMaxCol);
int saveSearchLineToFileWithoutComment(char *pszSearch,
char *pszSaveListStr[], char cComment,
char *pszFileName, int nMaxLine, int nMaxCol);
void changeStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void deleteStrToOneLineToFile(char *pszDel, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
int searchStrInFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol);
void addOnNotExistChangeOnExist(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void addOnNotExistChangeOnExistWithoutComment(char *pszIns,
char *pszSearch, char cComment, char *pszFileName,
int nMaxLine, int nMaxCol);
void addOnNotExistChangeOnExist2Search(char *pszIns,
char *pszFirstSearch, char *pszSecondSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertForeSearchToFile(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void attatchStrToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void saveStrIdxToFileLine(char *pszSave, char *pszSearch,
char *pszFileName, int nStartIdx, int nEndIdx,
int nMaxLine, int nMaxCol);
void changeStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nStartIdx, int nEndIdx,
int nMaxLine, int nMaxCol);
void attatchStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nIdx, int nMaxLine, int nMaxCol);
void attatchStrToFileIdxLine(char *pszInsert, char *pszFileName,
int nLineIdx, int nColIdx, int nMaxLine, int nMaxCol);
void saveBetweenStrToFile(char *pszSaveLine[], char *pszStartLine,
char *pszEndLine, char *pszFileName,
int nMaxLine, int nMaxCol);
void changeBetweenStrToFile(char *pszInsert[], int nInsertNum,
char *pszStartLine, char *pszEndLine,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertAfterStrToFile(char *pszInsert, char *pszStartLine,
char *pszFileName, int nMaxLine, int nMaxCol);
#endif
#endif
/**********************************************/
/**************** proc_file.c *****************/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include "proc_file.h"
DList *dblhead;
DList *dbltail;
void initDL(void)
{
dblhead = (DList *)malloc(sizeof(DList));
dbltail = (DList *)malloc(sizeof(DList));
dblhead->prev = dblhead;
dblhead->next = dbltail;
dbltail->prev = dblhead;
dbltail->next = dbltail;
}
DList *searchDL(void *pzSearch)
{
DList *s;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzSearch, sizeof(pzSearch)) == 0)
break;
s = s->next;
}
return s;
}
DList *insertAsFirstDL(void *pzInsert)
{
DList *p;
p = (DList *)malloc(sizeof(DList));
p->pzKey = pzInsert;
p->next = dblhead->next;
dblhead->next->prev = p;
dblhead->next = p;
p->prev = dblhead;
return p;
}
DList *insertAsLastDL(void *pzInsert)
{
insertDL(pzInsert, dbltail);
return (DList *)NULL;
}
DList *insertDL(void *pzInsert, DList *pIns) /* insert front node pIns */
{
DList *s;
if (pIns == dblhead)
return NULL;
s = (DList *)malloc(sizeof(DList));
s->pzKey = pzInsert;
pIns->prev->next = s;
s->prev = pIns->prev;
s->next = pIns;
pIns->prev = s;
return s;
}
DList *insertDLAsKey(void *pzInsert, void *pzSearch)
{ /* insert insk front findk */
DList *s;
DList *r = NULL;
s = searchDL(pzSearch);
if (s != dbltail)
{
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
}
return r;
}
DList *insertDLAsSort(void *pzInsert)
{
DList *s;
DList *r;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzInsert, sizeof(pzInsert)) >= 0)
break;
s = s->next;
}
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
return r;
}
int deleteDL(DList *pDel)
{
if (pDel == dblhead || pDel == dbltail)
return FAIL;
pDel->prev->next = pDel->next;
pDel->next->prev = pDel->prev;
free(pDel);
return SUCCESS;
}
int deleteDLAsKey(void *pzSearch)
{
DList *s;
s = searchDL(pzSearch);
if (s != dbltail)
{
s->prev->next = s->next;
s->next->prev = s->prev;
free(s);
return SUCCESS;
}
return FAIL;
}
void deleteAllDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
dblhead->next = dbltail;
dbltail->prev = dblhead;
}
void deletePerfectDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
free(dblhead);
free(dbltail);
}
void *printDL(DList *pPrint)
{
pPrint = dblhead->next;
while (pPrint != dbltail)
{
printf("%s", (char *)(pPrint->pzKey));
pPrint = pPrint->next;
}
return pPrint->pzKey;
}
/****** FILE procedure ******/
/* 한 라인에서 검색문자열(pszDel)을 지운다. */
void deleteStrFromOneLine(char *pszSave, char *pszDel, char *pszLine)
{
char *pszPnt;
int i, nFirstLen;
if ((pszPnt = (char *)strstr(pszLine, pszDel)) == NULL)
return;
nFirstLen = pszPnt-pszLine;
for (i = 0; i < nFirstLen; i++)
pszSave[i] = pszLine[i];
for (i = nFirstLen; pszLine[i+strlen(pszDel)] != '\0'; i++)
pszSave[i] = pszLine[i+strlen(pszDel)];
pszSave[i] = '\0';
}
/* 한 라인에서 검색문자열(pszSearch)앞에 삽입문자열(pszInsert) */
/* 을 삽입한다. */
void insertStrForeSearch(char *pszSave, char *pszInsert,
char *pszSearch, char *pszLine, char *pszTempLine)
{
int i = 0;
int nLen;
char *pszPos;
if ((char *)strstr(pszLine, pszInsert) != NULL)
{
strcat(pszSave, pszTempLine);
return;
}
pszPos = (char *)strstr(pszLine, pszSearch);
nLen = pszPos-pszLine;
for (i = 0; i < nLen; i++)
pszSave[i] = pszLine[i];
strcat(pszSave, pszInsert);
strcat(pszSave, strstr(pszLine, pszSearch));
}
/* 한 라인에서 검색문자열(pszIns)이 있다면 return하고 없다면 */
/* 라인 끝에 검색문자열을 삽입한다. */
void insertStrToOneLine(char *pszSave, char *pszIns, char *pszLine)
{
int nFirstPos, nSecondPos;
if ((char *)strstr(pszLine, pszIns) != NULL)
return;
for (nFirstPos = 0; nFirstPos < strlen(pszLine); nFirstPos++)
pszSave[nFirstPos] = pszLine[nFirstPos];
for (nSecondPos = 0; nSecondPos < strlen(pszIns); nSecondPos++)
pszSave[nSecondPos+nFirstPos] = pszIns[nSecondPos];
pszSave[nSecondPos+nFirstPos] = '\0';
}
void changeSearchStr(char *pszIns, char *pszSearch, char *pszLine,
char *pszSave, int nMaxCol)
{
int i = 0;
char *pszPos;
int nPos;
pszPos = (char *)strstr(pszLine, pszSearch);
nPos = pszPos-pszLine;
for (i = 0; i < nPos; i++)
pszSave[i] = pszLine[i];
strcat(pszSave, pszIns);
strcat(pszSave, pszLine+nPos+strlen(pszSearch));
pszPos = (char *)strstr(pszSave, pszSearch);
if (pszPos == NULL)
return;
memset(pszLine, '\0', nMaxCol);
strcpy(pszLine, pszSave);
memset(pszSave, '\0', nMaxCol);
changeSearchStr(pszIns, pszSearch, pszLine, pszSave, nMaxCol);
}
int nLine;
void *printToFileDL(DList *pPrint, FILE *fp)
{
pPrint = dblhead->next;
while (pPrint != dbltail)
{
fprintf(fp, pPrint->pzKey);
pPrint = pPrint->next;
}
return pPrint->pzKey;
}
int readOneLineSearchFile(char pszBuffer[], FILE *fp)
{
int i = 0;
char c = '\0';
do
{
c = getc(fp);
pszBuffer[i++] = c;
if (c == EOF || c == '\n')
break;
} while (1);
pszBuffer[i] = '\0';
nLine++;
return c;
}
void insertStrToFileLast(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
fclose(fp);
deletePerfectDL();
return;
}
}
insertAsLastDL(pszInsert);
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
}
/* file의 라인수를 return한다. */
int countAllLineToFile(char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, nCount = 0;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return 0;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (nCount = 0;
(readOneLineSearchFile(pszBuffer[nCount], fp) != EOF) &&
(nCount < nMaxLine); nCount++);
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return nCount;
}
/* file에서 검색문자열(pszSearch)가 있는 line 수를 return한다. */
int countSearchLineToFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0;
int nCount = 0;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return 0;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
nCount++;
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return nCount;
}
/* file의 모든 라인을 이중포인터(pszSaveListStr)에 저장한다. */
int saveAllLineToFile(char *pszSaveListStr[], char *pszFileName,
int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return 0;
for (j = 0; j < nMaxLine; j++)
{
pszBuffer[j] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[j], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
strcpy(pszSaveListStr[i], pszBuffer[i]);
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
return i;
}
/* file에서 검색문자열(pszSearch)이 있는 line에 Comment(cComment)가 */
/* 없다면 이중포인터(pszSaveListStr)에 저장한다. */
int saveSearchLineToFileWithoutComment(char *pszSearch,
char *pszSaveListStr[], char cComment,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
int j = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
if ((fp = fopen(pszFileName, "r")) == NULL)
return FALSE;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL &&
pszBuffer[i][0] != cComment)
{
strcpy(pszSaveListStr[j], pszBuffer[i]);
j++;
}
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return TRUE;
}
/* file에서 검색문자열(pszSearch)이 있는 line을 Comment(cComment)에 상 */
/* 관없이 이중포인터(pszSaveListStr)에 저장한다. */
int saveSearchLineToFile(char *pszSearch, char *pszSaveListStr[],
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
int j = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
if ((fp = fopen(pszFileName, "r")) == NULL)
return FALSE;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
strcpy(pszSaveListStr[j], pszBuffer[i]);
j++;
}
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return TRUE;
}
void changeStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0, j = 0;
FILE *fp;
char *pszLine[nMaxLine];
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
pszLine[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszLine[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
changeSearchStr(pszIns, pszSearch, pszBuffer[i],
pszLine[j], nMaxCol);
insertAsLastDL(pszLine[j]);
j++;
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
{
free(pszBuffer[i]);
free(pszLine[i]);
}
}
/* 검색문자열(pszSearch)이 있는 라인에서 검색문자열 바로 */
/* 앞에 삽입문자열(pszIns)을 삽입한다. */
void insertStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char pszLine[nMaxCol];
char *pszBuffer[nMaxLine];
memset(pszLine, '\0', nMaxCol);
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
insertStrForeSearch(pszLine, pszIns, pszSearch, pszBuffer[i],
pszBuffer[i]);
insertAsLastDL(pszLine);
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* file에서 검색문자열(pszSearch)을 찾아 그 문자열만 지워준다. */
void deleteStrToOneLineToFile(char *pszDel, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char pszLine[nMaxCol];
char *pszBuffer[nMaxLine];
memset(pszLine, '\0', nMaxCol);
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
deleteStrFromOneLine(pszLine, pszDel, pszBuffer[i]);
insertAsLastDL(pszLine);
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
void insertStrToFile(char *pszInsert, char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
insertAsLastDL(pszInsert);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* file에서 검색문자열(pszSearch)이 있으면 TRUE, */
/* 없으면 FALSE를 return한다. */
int searchStrInFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return FALSE;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
fclose(fp);
return TRUE;
}
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return FALSE;
}
void addOnNotExistChangeOnExist(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
insertAsLastDL(pszIns);
flgAdd = FALSE;
}
}
fclose(fp);
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* Comment(cComment)가 없을 때, 검색문자열(pszSearch)이 */
/* 없으면 file 맨 끝에 삽입문자열(pszIns)를 라인으로 삽입 */
/* 하고, 있으면 검색문자열이 있는 라인을 삽입문자열로 바 */
/* 꾼다. */
void addOnNotExistChangeOnExistWithoutComment(char *pszIns,
char *pszSearch, char cComment,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL ||
pszBuffer[i][0] == cComment)
insertAsLastDL(pszBuffer[i]);
else
{
insertAsLastDL(pszIns);
flgAdd = FALSE;
}
}
fclose(fp);
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 두개의 검색문자열(pszFirstSearch, pszSecondSearch)이 모두 있으면 */
/* 그 라인을 삽입문자열(pszIns)로 바꾸고, 둘 중 하나라도 없으면 */
/* 삽입문자열을 file맨 끝에 라인으로 삽입한다. */
void addOnNotExistChangeOnExist2Search(char *pszIns, char *pszFirstSearch,
char *pszSecondSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszFirstSearch) != NULL &&
(char *)strstr(pszBuffer[i], pszSecondSearch) != NULL)
{
insertAsLastDL(pszIns);
flgAdd = FALSE;
}
else
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)를 찾아 그 라인의 바로 앞에 삽입 */
/* 문자열을 라인으로 삽입한다. */
void insertForeSearchToFile(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
insertAsLastDL(pszBuffer[i]);
fclose(fp);
insertDLAsKey(pszInsert, pszSearch);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)을 찾아 그 라인의 끝위치에 삽입문자열 */
/* (pszInsert)을 삽입한다. */
void attatchStrToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
pszBuffer[i][strlen(pszBuffer[i])-1] = '\0';
strcat(pszBuffer[i], pszInsert);
pszBuffer[i][strlen(pszBuffer[i])] = '\n';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)를 찾아 그라인의 특정Coloum(nStartIdx)에서 */
/* 특정Coloum(nEndIdx)까지의 문자열을 Save(pszSave)한다. */
void saveStrIdxToFileLine(char *pszSave, char *pszSearch,
char *pszFileName, int nStartIdx, int nEndIdx,
int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
char pszEndBuffer[nMaxCol];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
memset(pszEndBuffer, '\0', nMaxCol);
strcpy(pszEndBuffer, pszBuffer[i]+nStartIdx);
for (j = 0; j < nEndIdx-nStartIdx; j++)
pszSave[j] = pszEndBuffer[j];
}
}
fclose(fp);
}
/* 검색문자열(pszSearch)를 찾아 그라인의 특정Coloum(nStartIdx)에서 */
/* 특정Coloum(nEndIdx)까지의 문자열을 삽입문자열(pszInsert)로 교체한다. */
void changeStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nStartIdx,
int nEndIdx, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
char pszEndBuffer[nMaxCol];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
memset(pszEndBuffer, '\0', nMaxCol);
strcpy(pszEndBuffer, pszBuffer[i]+nEndIdx);
pszBuffer[i][nStartIdx] = '\0';
strcat(pszBuffer[i], pszInsert);
strcat(pszBuffer[i], pszEndBuffer);
pszBuffer[i][strlen(pszBuffer[i])] = '\0';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)를 찾아 그라인의 특정Coloum(nIdx) 이후의 문자열 */
/* 을 모두 없애고, 삽입문자열(pszInsert)을 붙힌다. */
void attatchStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nIdx, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
pszBuffer[i][nIdx] = '\0';
strcat(pszBuffer[i], pszInsert);
pszBuffer[i][strlen(pszBuffer[i])] = '\n';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 특정라인(nLineIdx)의 특정Coloum(nColIdx) 이후의 문자열 */
/* 을 모두 없애고, 삽입문자열(pszInsert)을 붙힌다. */
void attatchStrToFileIdxLine(char *pszInsert, char *pszFileName,
int nLineIdx, int nColIdx, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if (i == nLineIdx-1)
{
pszBuffer[i][nColIdx] = '\0';
strcat(pszBuffer[i], pszInsert);
pszBuffer[i][strlen(pszBuffer[i])] = '\n';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszStartLine)을 찾아 그 라인의 바로 다음라인에 */
/* 삽입문자열(pszInsert)을 라인으로 삽입한다. */
void insertAfterStrToFile(char *pszInsert, char *pszStartLine,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j, k;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
insertAsLastDL(pszBuffer[i]);
if ((char *)strstr(pszBuffer[i], pszStartLine) != NULL)
{
insertAsLastDL(pszInsert);
insertAsLastDL("\n");
for (j = i+1;
readOneLineSearchFile(pszBuffer[j], fp) != EOF; j++)
insertAsLastDL(pszBuffer[j]);
break;
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return;
}
/* 첫번째 검색문자열(pszStartLine)과 두번째 검색문자열(pszEndLine) */
/* 을 찾아 그 사이에 있는 line들을 저장(pszSaveLine)한다. */
void saveBetweenStrToFile(char *pszSaveLine[], char *pszStartLine,
char *pszEndLine, char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszStartLine) != NULL)
{
for (j = i+1;
readOneLineSearchFile(pszBuffer[j], fp) != EOF; j++)
{
if ((char *)strstr(pszBuffer[j], pszEndLine) == NULL)
strcpy(pszSaveLine[j-(i+1)], pszBuffer[j]);
else
break;
}
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
return;
}
}
}
/* 첫번째 검색문자열(pszStartLine)과 두번째 검색문자열(pszEndLine) */
/* 을 찾아 그 사이에 있는 line들을 삽입문자열(pszInsert)로 대체 */
/* 시킨다. */
void changeBetweenStrToFile(char *pszInsert[], int nInsertNum,
char *pszStartLine, char *pszEndLine,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j, k;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
insertAsLastDL(pszBuffer[i]);
if ((char *)strstr(pszBuffer[i], pszStartLine) != NULL)
{
for (j = i+1;
readOneLineSearchFile(pszBuffer[j], fp) != EOF; j++)
if ((char *)strstr(pszBuffer[j], pszEndLine) != NULL)
break;
for (k = 0; k < nInsertNum; k++)
insertAsLastDL(pszInsert[k]);
insertAsLastDL(pszBuffer[j]);
for (k = j+1;
readOneLineSearchFile(pszBuffer[k], fp) != EOF; k++)
insertAsLastDL(pszBuffer[k]);
break;
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return;
}
-------------------------------------------------------
지금까지 링크리스트를 이용한 file처리에 관한 내용을 다루어보았다.
그런데, 왜 굳이 Simple LinkedList를 쓰지 않고, Double LinkedList
를 사용했는지 궁금할 것이다.
나는 후에라도 이 Source를 더욱 강화시키기위해 확장성이 좋은 이중
연결리스트를 사용한 것이다. 물론 단순연결리스트를 사용한다면 메
모리를 약간 절약할 수는 있겠지만 확장성에서는 훨씬 떨어진다.
따라서, Library를 후에 강화시킬 일이 있다면 이중연결리스트를 써
야한다고 생각한다.
1.3. 개선되어야할 내용
앞서 말했듯이 위의 Source는 특정 Routine이 수행될 때 순간적으로
Resource를 크게 차지할 수도 있는 단점이 있다.
이를 보완하려면 LinkList의 node에 key값을 삽입할 때 모든 file의
내용을 전부 LinkList에 삽입할 것이 아니라 조건에 맞을 때만 file
의 내용을 변경하도록 하는 것이 좋다.
이러한 개선점을 생각해 위의 Source들을 한번 수정해보시기를 바란
다.
===============================================================
< 2. QT 기본 다지기 >
-> 필자는 Ansi-C를 사용하는 원시 프로그래머였다.
주로 다뤘던 분야는 Network 관련 프로그램이었고 개발 환경또한
X-window가 아닌 text-mode에서였다. text-mode에서의 GUI구축에
온 힘을 쏟았고 그 결과로 Curses나 Slang보다도 더 좋다고 내세
울 수 있는 Library를 개발했다. 나는 내 자신이 상당한 수준에
올라와 있다고 착각하기 시작했고 점차 자만에 빠지게 되었다.
하지만 점차 Linux가 업그레이드 되면서 굉장한 수준의 그래픽환
경이 제공되면서 그 자만은 깨지기 시작했다. 그 충격이란 엄청
난 것이었다. 나와 비슷한 분들이 한분이라도 이 글을 읽는 분들
중에 계시다면 이 자료를 올린 보람이 있을 것이다.
기타등등의 이유로 C++에 대한 클래스 개념조차도 거의 모른 상태
에서 약 3개월 전부터 QT를 시작했다.
어떠한 자료도 없이 Alzza 리눅스에 포함되어있는 QT 1.44 의 예제
Source만으로 C++과 QT를 공부하자니 무척이나 힘이 들었다.
이렇게 힘들게 공부하면서 QT에 빨리 접근할 수 있는 방법을 저절
로 터득하게 되었는데 이 방법을 여러분께 가장 좋다고 생각하는
과정에 의거하여 글을 쓰겠다. 짧은 실력으로 글을 올리니 여러분
께 죄책감이 먼저 앞서지만 Source공유라는 차원에서 귀엽게 보아
주시기 바란다.
2.1. QT의 Class계층정보
우선 본격적인 QT 제공 Class들을 공부하기에 앞서 Class계층에
대해 눈으로 익혀두기 바란다. 이 계층정보는 상당히 중요하지만
외울 필요는 없다. 그냥 필요할 때 찾아보는 정도로 활용하기 바
란다. 아래의 표는 /usr/lib/qt/html/hierarchy.html 에서 발췌한
내용중 앞으로 쓰게될지도 모를 주요 클래스만을 정리한 것이다.
-------- < 표 2.1.1 QT Class 계층정보 > ---------
QBrush
QCollection
QGList
QList
QStrList
QStrIList
QColor
QColorGroup
QConnection
QCursor
QEvent
QChildEvent
QCloseEvent
QCustomEvent
QDragMoveEvent
QDropEvent
QFocusEvent
QKeyEvent
QMouseEvent
QMoveEvent
QPaintEvent
QResizeEvent
QTimerEvent
QFont
QFontInfo
QFontMetrics
QGArray
QArray
QByteArray
QString
QPointArray
QIconSet
QImage
QListBoxItem
QListBoxPixmap
QListBoxText
QListViewItem
QCheckListItem
QMenuData
QMenuBar
QPopupMenu
QMovie
QObject
QAccel
QApplication
QXtApplication
QDragObject
QImageDrag
QStoredDrag
QTextDrag
QUrlDrag
QLayout
QBoxLayout
QHBoxLayout
QVBoxLayout
QGridLayout
QSignal
QToolTipGroup
QValidator
QDoubleValidator
QIntValidator
QWidget
QButton
QCheckBox
QPushButton
QRadioButton
QToolButton
QComboBox
QDialog
QFileDialog
QMessageBox
QPrintDialog
QTabDialog
QFrame
QGroupBox
QButtonGroup
QLCDNumber
QLabel
QMenuBar
QProgressBar
QScrollView
QListView
QSpinBox
QSplitter
QTableView
QHeader
QListBox
QMultiLineEdit
QPopupMenu
QWidgetStack
QLineEdit
QMainWindow
QNPWidget
QScrollBar
QSemiModal
QProgressDialog
QSlider
QStatusBar
QTabBar
QToolBar
QWindow
QXtWidget
QPaintDevice
QPicture
QPixmap
QBitmap
QPrinter
QWidget
QPaintDeviceMetrics
QPainter
QPalette
QPen
QPixmapCache
QPoint
QRangeControl
QScrollBar
QSlider
QSpinBox
QRect
QSize
QTextStream
QTime
QToolTip
QWhatsThis
QWMatrix
-----------------------------------------
2.2. Widget(QWidget)이란?
-> 모든 사용자 인터페이스(User Interface)의 기본 Class이다.
위의 표와 같이 버튼, 프레임, 편집박스등 Graphic Interface를
주도하는 Class들중 최 상위에 위치해있다.
모든 Widget들에 대한 공부는 철저하게 예제위주로 하겠다.
2.2.1. 위젯(QWidget), 프레임(QFrame), 버튼(QButton)
---------- < source 2.2.1.1. testFrameButton.cpp > -----------
#include <qapplication.h> /* QApplication을 위한 header file */
#include <qwidget.h> /* QWidget을 위한 header file */
int main(int argc, char* argv[])
{
/* 이 부분은 QT 프로그램의 main함수에서 항상 써주어야 */
/* 하는 부분이다. */
QApplication myapp(argc, argv);
/* 인자가 없음에 주의 */
QWidget *wdgtTest = new QWidget();
/* setGeometry(x좌표, y좌표, 넓이, 높이) */
wdgtTest->setGeometry(100, 100, 200, 100);
/* application의 메인 윈도우를 wdgtTest로 정의 */
myapp.setMainWidget(wdgtTest);
/* wdgtTest를 보여준다. */
wdgtTest->show();
/* qt를 실행시킨다. 만약 이 부분이 없다면 윈도우가 뜨지 */
/* 않을 것이다. 항상 들어가야할 부분이다. */
return myapp.exec();
}
-----------------------------------------------------------
위의 Source에서 관심있게 보아야할 부분은 QWidget, QFrame,
QPushButton Class의 인자들이다.
우선 header file을 살펴보자. header file에 관한 정보는
/usr/lib/qt/include/ 에서 볼 수 있다.
public:
QWidget( QWidget *parent=0, const char *name=0, WFlags f=0 );
private: // Disabled copy constructor and operator=
#if defined(Q_DISABLE_COPY)
QWidget( const QWidget & );
QWidget &operator=( const QWidget & );
#endif
위 부분은 header file(/usr/lib/qt/include/qwidget.h)에 들어있
는 QWidget의 생성자 부분이다.
여기서 유심히 보아야할 부분은 인자들의 정의 부분이다. 즉,
QWidget *parent = 0, const char *name = 0, WFlags f = 0
이렇게 클래스정의부분에서 인자값까지도 정의할 수 있다는 것은
C++을 한번쯤 다뤄본 분이라면 모두 아실 것이다.
C에서 NULL값은 #define문으로 0으로 선언되어있다. 즉 parent,
name, f 모두 NULL로 정의를 했다는 뜻인데 그냥 초기화시켰다
는 의미로 받아들이자. 물론 이 부분은 class 생성자(constructor)
부분에서 직접 정의할 수도 있다.
이는 C++의 특권이라고 할 수 있겠다. 이런 방법은 여러곳에서 상
당히 유용하게 쓸 수 있는 기능이다. 자세한 예는 차차 이야기하
도록 하겠다.
이젠 인자들에 대해 살펴보자.
-> QWidget *parent: parent widget을 뜻한다.
위의 header file에 나타난 class선언에서도 보았
듯이 디폴트로 NULL(0)으로 설정되어있는데 이는
자신을 최상위 윈도우로 정하겠다는 뜻이다.
즉 parent가 정의되지 않은(인자가 없는) widget은
부모class가 없이 독립적인 윈도우 체제를 가진다.
parent인자에 만약 다른 widget값을 넘겨준다면
parent인자에 종속되게 된다. 자세한 내용은
source 2.2.1.2에서 다시 다루겠다.
-> const char *name:이것은 widget에 대한 식별자 역할을 한다.
이 식별자는 프로그래머가 거의 사용할 일이 없다.
library에서 자체적으로 compile시 debugging작업
을 할 때, 혹은 사용자가 debugging tool을 사용할
때 특정 부분에 대한 정보를 보고 싶을 때 name()
함수를 사용하여 정보를 추출할 수 있다.
필자는 거의 debugging tool을 사용하지 않으므로
이 인자에 대한 정의는 한번도 한적이 없다.
따라서 이 인자에 대한 설명은 생략하기로 하겠다.
-> WFlags f: 거의 사용할 일이 없으므로 디폴트값을 따르기로
하자. 설명도 생략하겠다. 자세한 정보는
/usr/lib/qt/html/qwidget.html file을 참조하기
바란다.
---------- < source 2.2.1.2. testFrameButton.cpp > -----------
#include <qapplication.h>
#include <qwidget.h>
#include <qframe.h> /* QFrame을 위한 header file */
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
QWidget *wdgtTest = new QWidget();
wdgtTest->setGeometry(100, 100, 200, 100);
/* parent 인자를 wdgtTest로 설정한다. */
QFrame *frTest = new QFrame(wdgtTest);
/* parent가 wdgtTest이므로 frTest에 대한 속성 */
/* 들(x좌표, y좌표)의 좌표값은 wdgtTest에 대한 */
/* 상대적인 좌표로 변환된다. */
frTest->setGeometry(10, 10, 150, 60);
/* frame의 형태를 정한다. */
frTest->setFrameStyle(QFrame::Box | QFrame::Raised |
QFrame::Plain);
myapp.setMainWidget(wdgtTest);
wdgtTest->show();
return myapp.exec();
}
-----------------------------------------------------------
QFrame에 대한 class선언 정보는
/usr/lib/qt/include/qframe.h에 정의되어있다. 다음과 같이
public과 private양쪽에 선언되어있다.
public:
QFrame( QWidget *parent=0, const char *name=0, WFlags f=0,
bool allowLines=TRUE );
private: // Disabled copy constructor and operator=
#if defined(Q_DISABLE_COPY)
QFrame( const QFrame & );
QFrame &operator=( const QFrame & );
#endif
public member와 private member에 대해서는 굳이 언급하지 않아도 되
겠지만 참조범위에 대해 아주 간단하게 설명을 하겠다.
-> public member: 하위, 상위, 혹은 형제 class에서 이 member를 참조할
수 있다.
-> private member: 다른 어떤 class도 이 member를 참조할 수 없다.
인자에 대해 알아보도록 하자.
-> QWidget *parent: 위에서 언급한 QWidget의 parent인자와 같다.
-> const char *name: " "
-> WFlags f: " "
-> bool allowLines: 만약 FALSE로 setting된다면 수평라인과 수직라인
을 쓰지 않는다.
---------- < source 2.2.1.3. testFrameButton.cpp > -----------
#include <qapplication.h>
#include <qwidget.h>
#include <qframe.h>
#include <qpushbutton.h> /* QPushButton을 위한 header file */
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
QWidget *wdgtTest = new QWidget();
wdgtTest->setGeometry(100, 100, 200, 100);
QFrame *frTest = new QFrame(wdgtTest);
frTest->setGeometry(10, 10, 150, 60);
/* 형태에 주의하여 보자. */
frTest->setFrameStyle(QFrame::Box | QFrame::Raised |
QFrame::Plain);
/* text가 "테스트", parent가 frTest로 설정되어있다. */
QPushButton *bttnTest = new QPushButton("테스트", frTest);
/* frTest와 마찬가지로 bttnTest 또한 frTest에 대한 상대 */
/* 적인 좌표로 설정된다. */
bttnTest->setGeometry(20, 20, 100, 30);
// bttnTest->resize(100, 30);
myapp.setMainWidget(wdgtTest);
wdgtTest->show();
return myapp.exec();
}
-----------------------------------------------------------
/usr/lib/qt/include/qframe.h에 보면 setFrameStyle에 대해 다음
과 같이 쓰여져 있다.
void setFrameStyle(int);
인자부분을 보면 integer형인데 이곳에 들어갈 수 있는 인자 또한
header file에 잘 나타나있다.
enum { NoFrame = 0, // no frame
Box = 0x0001, // rectangular box
Panel = 0x0002, // rectangular panel
WinPanel = 0x0003, // rectangular panel (Windows)
HLine = 0x0004, // horizontal line
VLine = 0x0005, // vertical line
MShape = 0x000f,
Plain = 0x0010, // plain line
Raised = 0x0020, // raised shadow effect
Sunken = 0x0030, // sunken shadow effect
MShadow = 0x00f0 };
enumeration 되어진 member들을 주의깊게 보면 숫자가 끊어져있는 것
을 알 수 있다. 즉 0x0001~0x0005, 0x000f, 0x0010, 0x0020, 0x0030,
0x00f0. 이 16진수들을 2진수로 풀어보자.
0x0001 => 0000 0000 0000 0001
0x0002 => 0000 0000 0000 0010
0x0003 => 0000 0000 0000 0011
0x0004 => 0000 0000 0000 0100
0x0005 => 0000 0000 0000 0101
0x000f => 0000 0000 0000 1111
0x0010 => 0000 0000 0001 0000
0x0020 => 0000 0000 0010 0000
0x0030 => 0000 0000 0011 0000
0x00f0 => 0000 0000 1111 0000
위의 2진수 값에서도 볼 수 있듯이 bit operation을 적당하게만 쓴
다면 여러 모양의 Frame Style을 나타낼 수 있는 조합이 나오게 된다.
예를 들어 [ Box | WinPanel | Sunken ]을 하게 되면 윈도우즈 스타
일의 움푹 들어간 입체 라인을 나타내준다.
/usr/lib/qt/include/qpushbutton.h에는 다음과 같이 class가 선
언되어있다.
public:
QPushButton( QWidget *parent=0, const char *name=0 );
QPushButton( const char *text, QWidget *parent=0,
const char *name=0 );
private: // Disabled copy constructor and operator=
#if defined(Q_DISABLE_COPY)
QPushButton( const QPushButton & );
QPushButton &operator=( const QPushButton & );
#endif
혹여 궁금하게 생각할 수도 있는 부분
QPushButton &operator=( const QPushButton & );
은 다음 보여드릴 source 2.2.1.4에서 설명하겠다.
보는 바와 같이 QPushButton의 인자에는 문자열 "테스트"와
frTest가 들어가있다. 종속성에 대한 설명은 위해서 이미 했
으므로 여기서는 생략하겠다. 각 widget들의 속성에 대한 설정은
setGeometry로 할 수도 있고, resize라는 함수로도 설정이 가능
하다. 이 두 함수는 설정값에서 약간의 차이가 있다.
setGeometry는 좌표와 크기에 대한 설정이 모두 들어가지만 resize
함수는 x좌표와 y좌표만이 인자로 들어간다. 이 두 함수는 widget
들에 대한 속성 설정이므로 QWidget의 하위 class들의 public함수
에 모두 선언이 되어있다. 물론 QWidget도 이들 함수를 포함하고 있다.
2.2.1.1. Class를 생성하여 만들기
이제부터는 위에서 설명한 source들을 사용하여 분할 컴파일하는 방
법을 알아보자. 우선 source들을 각 특성단위로 분리시켜야한다.
다음은 이렇게 분리된 source code들이다.
--------- < source 2.2.1.1.1 clssTestWidget.h > -----------
#include <qwidget.h> /* 아래쪽의 public QWidget 부분에 대한 header */
/* 아래쪽에 있는 frTest와 bttnTest를 선언하기 위한 class 선언. */
/* 일반함수 선언과 비슷함에 유의 */
class QFrame;
class QPushButton;
/* clssTestWidget class는 QWidget의 모든 public변수 혹은 class를 */
/* 공유할 수 있다. 즉 clssTestWidget class는 QWidget으로부터 '상속' */
/* 받은 class이다. */
class clssTestWidget : public QWidget
{
/* Qt library를 쓰는 class라면 항상 이렇게 선언해놓아야 한다. */
Q_OBJECT
public:
clssTestWidget(); /* 생성자 */
~clssTestWidget(); /* 소멸자 */
/* widget(bttnTest)의 특정 SIGNAL이 발생했을 때, event를 받아 */
/* 원하는 처리를 해줄 수 있는 slot function 선언 */
public slots:
void printMessage();
/* 후에 설명하겠다. 우선은 이런 format도 있다는 정도로 알아두자. */
protected:
private:
QFrame *frTest;
QPushButton *bttnTest;
};
-----------------------------------------------------------
--------- < source 2.2.1.1.2 clssTestWidget.cpp > ---------
#include <qframe.h> /* frTest를 위한 header 선언 */
#include <qpushbutton.h> /* bttnTest를 위해 */
#include <stdio.h>
#include "clssTestWidget.h"
clssTestWidget::clssTestWidget()
{
QFrame *frTest = new QFrame(this);
frTest->setGeometry(10, 10, 150, 60);
frTest->setFrameStyle(QFrame::Box | QFrame::Raised | QFrame::Plain);
QPushButton *bttnTest = new QPushButton("테스트", frTest);
bttnTest->setGeometry(20, 20, 100, 30);
/* 버튼이 'clicked()'되었을 때 printMessage()함수를 호출하라는 */
/* 행동 지침 */
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(printMessage()));
}
clssTestWidget::~clssTestWidget()
{
}
/* 버튼이 눌렸을 때 처리되는 함수 */
void clssTestWidget::printMessage()
{
emit printf("버튼이 눌렸습니다.\n");
}
-----------------------------------------------------------
--------- < source 2.2.1.1.3 main_test1.cpp > ---------
#include <qapplication.h> /* QApplication을 위한 header file */
#include "clssTestWidget.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
/* class 생성 */
clssTestWidget *wdgtTest = new clssTestWidget();
/* class 속성정의 */
wdgtTest->setGeometry(100, 100, 200, 100);
/* main으로 쓸 widget 정의 */
myapp.setMainWidget(wdgtTest);
/* widget을 보여줌 */
wdgtTest->show();
return myapp.exec();
}
-----------------------------------------------------------
이 source들에 앞서 설명한 source와의 차이점은, file들이 분리
되었고 class를 정의하여 그 class를 main에서 호출한다는 것
말고는 거의 없다. 따라서 더이상의 설명은 생략하고 다음으로
Maikfile에 대한 간단한 설명과 함께 이 분할 module들을 compile
하는 방법에 대해 알아보겠다.
2.2.2. Makefile 만들기
2.2.1.에서의 source들을 compile하는 방법에 대해서는 아직 언
급하지 않았다. Qt 2.0이상부터는 제법 GUI Tool이 괜찮아진 모
양이던데 필자도 한번은 사용을 해 보았지만 버그가 몇 가지 있
는 것 같아 지금은 전혀 사용하지 않고 있다. Makefile을 만드는
방법에 있어서도 요샌 tmake라는 GUI Tool을 사용한다고 들었다.
필자는 이 Tool도 사용하지 않는다. 이유는 간단하다. Makefile
을 마음대로 주무르기 위해서이다.
Makefile을 완전하게 이해하고 쉽게 쓸 수 있는 수준이라면 tmake
를 사용하는 것도 나쁘지는 않을 것이다.
아래의 code는 2.2.1.1의 source들을 분할 Compile하기 위한
Makefile이다.
---------------- < Makefile 2.2.2 > ---------------------
PROGS = main_test1 ### 보통 실행file의 이름을 적는다.
### X-window 환경에 관한 library들을 포함시킨다. (Qt 포함)
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
### Compiler를 정의한다.
CC = g++
GCC = gcc
### Header file들을 포함시킨다.
INCLUDE = -I/usr/lib/qt/include
### Compile option을 정의한다.
C_FLAG = -pipe -DNO_DEBUG -O2
### Header file에 대한 option과 Compile option을 합한다.
CFLAGS = $(INCLUDE) $(C_FLAG)
### 실행file생성을 위한 Linker를 정의한다. 여기서는 g++이 된다.
SYSCONF_LINK = $(CC)
### class를 분리시켜놓은 Header file을 .cpp source file과 연동
### 시키며 시스템에서 초기화된 graphic device에 관한 setting을
### 시키기 위한 특수한 cpp file인 moc file을 생성시키는 tool이
### 다. moc file에 대한 분석은 별로 필요없는 부분이므로 생략하
### 겠다.
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
### main함수가 들어있는 source file을 Link시킬 때 필요한 Object file
### 들을 후에 참조하기 편하도록 Label로 묶어놓는다.
MNU_OBJS = \
main_test1.o \
clssTestWidget_moc.o \
clssTestWidget.o
### 'make all'은 이 Makefile을 실행한다. (= make)
all: $(PROGS)
### 'make clean'은 make를 실행시켰을때 새로 생성되는 모든 file
### 을 지우는 역할을 한다. clean, all, 모두 사용자가 어떻게 정
### 의하는가에 따라 역할이 틀려지게 된다.
clean:
rm -f *.o
rm -f main_test1
rm -f *_moc.*
############# COMPILE #############
### main file을 컴파일한다.
main_test1.o: main_test1.cpp
$(CC) -c main_test1.cpp $(CFLAGS) -o $@
### Header file로부터 moc file을 만들어낸다.
clssTestWidget_moc.cpp: clssTestWidget.h
$(MOC) clssTestWidget.h -o clssTestWidget_moc.cpp
### 만들어진 moc file을 컴파일한다.
clssTestWidget_moc.o: clssTestWidget_moc.cpp
$(CC) -c clssTestWidget_moc.cpp $(CFLAGS) -o $@
### 새로 분리된 module file을 컴파일한다.
clssTestWidget.o: clssTestWidget.cpp
$(CC) -c clssTestWidget.cpp $(CFLAGS) -o $@
### 맨 윗부분의 PROG정의부에 있는 이름을 Label로 쓴다.
main_test1: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
------------------------------------------------------------
shell상에서 'make' 혹은 'make all' 을 실행시키면 자동적으로
compile 및 link를 시켜준다. make는 object file의 생성시간, 즉
compile된 시점을 기준으로 source에 변화가 생겼을 때 그 변화된
source module만을 compile함으로써 전체를 Compile하는 시간을 크
게 줄여주는 효과가 있다. 물론 source module이 두세개 정도로 작
다면 굳이 Makefile을 만들어줄 필요가 없다.
하지만 보통 실제 project들의 경우 상당한 수의 source module을
필요로 하기때문에 Makefile의 생성은 필수적이라 할 수 있다.
다음으로 소개하는 source들에 대해서는 모든 source의 Makefile
을 source바로 밑에 주석없이 소개하도록 하겠다. Makefile을 사용
하는데 있어 적게라도 도움이 되었으면 한다.
2.2.3. 라벨(QLabel)
이제부터 2.2.10까지의 모든 예제는 앞에서 사용한 분할 module compile
방법을 따르기로 하겠다. 물론 기존에 설명했던 부분들은 모두 생략한다.
우선 source를 보도록 하자.
-------------- < source 2.2.3.1 clssTestLabel.h > ---------------
#include <qwidget.h>
class QLabel;
class clssTestLabel : public QWidget
{
Q_OBJECT
public:
clssTestLabel();
~clssTestLabel();
public slots:
protected:
private:
QLabel *lblTest1;
QLabel *lblTest2;
QLabel *lblTest3;
};
-------------------------------------------------------------------
-------------- < source 2.2.3.2 clssTestLabel.cpp > ---------------
#include <qlabel.h>
#include <qframe.h>
#include "clssTestLabel.h"
clssTestLabel::clssTestLabel()
{
QLabel *lblTest1 = new QLabel(this);
lblTest1->setText("테스트 1");
lblTest1->setGeometry(20, 20, 100, 30);
/* QLabel에서 setFrameStyle함수를 호출했다는 것을 명심하자. */
lblTest1->setFrameStyle(QFrame::Box | QFrame::Panel | QFrame::Raised);
QLabel *lblTest2 = new QLabel(this);
lblTest2->setText("테스트 2");
lblTest2->setGeometry(20, 60, 100, 30);
lblTest2->setFrameStyle(QFrame::Box | QFrame::Panel | QFrame::Sunken);
QLabel *lblTest3 = new QLabel(this);
lblTest3->setText("테스트 3");
lblTest3->setGeometry(20, 100, 100, 30);
lblTest3->setFrameStyle(QFrame::Box | QFrame::Panel | QFrame::Plain);
}
clssTestLabel::~clssTestLabel()
{
}
-------------------------------------------------------------------
위의 Source는 총 세가지 모양의 Label format을 나타내준다.
특이할 만한 점은 QLabel에서 setFrameStyle을 호출했다는 것인데
/usr/lib/qt/include/qlabel.h에서는 setFrameStyle함수가 선언되어있지
않다. qframe.h file에서만이 선언되어있는데 QLabel은 QFrame class에서
파생되어 나온 자식 class이므로 QFrame class에서 선언되어있는 모든
public함수들을 공유하여 쓸 수 있다. 물론 enumeration된 Box, Panel등
을 쓸 때는 QLabel의 member들이 아니므로 QFrame::과 같이 소속을 적어
주어야 Compiler가 알아들을 수 있다.
-------------- < source 2.2.3.3 main_test2.cpp > ---------------
#include <qapplication.h>
#include "clssTestLabel.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestLabel *clssTest = new clssTestLabel();
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.3.1 > ----------------------
PROGS = main_test2
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test2.o \
clssTestLabel_moc.o \
clssTestLabel.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test2
rm -f *_moc.*
############# COMPILE #############
main_test2.o: main_test2.cpp
$(CC) -c main_test2.cpp $(CFLAGS) -o $@
clssTestLabel_moc.cpp: clssTestLabel.h
$(MOC) clssTestLabel.h -o clssTestLabel_moc.cpp
clssTestLabel_moc.o: clssTestLabel_moc.cpp
$(CC) -c clssTestLabel_moc.cpp $(CFLAGS) -o $@
clssTestLabel.o: clssTestLabel.cpp
$(CC) -c clssTestLabel.cpp $(CFLAGS) -o $@
main_test2: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.4. 편집박스(QLineEdit)
-------------- < source 2.2.4.1 clssTestLineEdit.h > ---------------
#include <qwidget.h>
class QLineEdit;
class QPushButton;
class clssTestLineEdit : public QWidget
{
Q_OBJECT
public:
clssTestLineEdit();
~clssTestLineEdit();
public slots:
void testClick();
protected:
private:
QLineEdit *leTest1;
QLineEdit *leTest2;
QPushButton *bttnTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.4.2 clssTestLineEdit.cpp > ---------------
#include <qlineedit.h>
#include <qpushbutton.h>
#include <stdio.h>
#include "clssTestLineEdit.h"
clssTestLineEdit::clssTestLineEdit()
{
leTest1 = new QLineEdit(this);
leTest1->setGeometry(20, 20, 100, 30);
leTest2 = new QLineEdit(this);
leTest2->setGeometry(20, 60, 100, 30);
leTest2->setText("환영!");
/* setEnabled는 편집을 할 수 있게 할 것인지 아닌지를 결정한다. */
/* true->편집가능, false->편집불가능 */
leTest2->setEnabled(false);
bttnTest = new QPushButton("확 인", this);
bttnTest->setGeometry(20, 100, 100, 30);
/* 버튼(bttnTest)이 눌렸(clicked())을 때 testClick()함수를 실행하라 */
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(testClick()));
}
clssTestLineEdit::~clssTestLineEdit()
{
}
void clssTestLineEdit::testClick()
{
/* leTest1->text()는 현재 leTest1 편집박스안에 표시되어있는 */
/* text를 가져온다. */
emit printf("편집박스1의 텍스트는 < %s >입니다.\n",
leTest1->text());
emit printf("편집박스2의 텍스트는 < %s >입니다.\n",
leTest2->text());
}
-------------------------------------------------------------------
QLineEdit class는 윈도우즈의 edit box와 같은 기능을 하는 class이다.
특수키(예: 방향키, insert, delete, home, end등)들을 쓸 수 있으며
X-window와의 호환도 이루어진다. 즉 편집박스에 있는 문자열은 마우스
로 < copy and paste > 할 수 있다는 이야기다. 가장 자주 쓰이는 Widget
이니 꼭 기억해두자.
-------------- < source 2.2.4.3 main_test3.cpp > ---------------
#include <qapplication.h>
#include "clssTestLineEdit.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestLineEdit *clssTest = new clssTestLineEdit();
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.4 > ----------------------
PROGS = main_test3
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test3.o \
clssTestLineEdit_moc.o \
clssTestLineEdit.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test3
rm -f *_moc.*
############# COMPILE #############
main_test3.o: main_test3.cpp
$(CC) -c main_test3.cpp $(CFLAGS) -o $@
clssTestLineEdit_moc.cpp: clssTestLineEdit.h
$(MOC) clssTestLineEdit.h -o clssTestLineEdit_moc.cpp
clssTestLineEdit_moc.o: clssTestLineEdit_moc.cpp
$(CC) -c clssTestLineEdit_moc.cpp $(CFLAGS) -o $@
clssTestLineEdit.o: clssTestLineEdit.cpp
$(CC) -c clssTestLineEdit.cpp $(CFLAGS) -o $@
main_test3: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.4.1 QObject::connect, SIGNAL과 SLOT
/usr/lib/qt/include/qobject.h에 선언되어있는 connect함수를 보자.
static bool connect( const QObject *sender, const char *signal,
const QObject *receiver, const char *member );
bool connect( const QObject *sender, const char *signal,
const char *member ) const;
위와 같이 두가지로 선언되어있는데 밑에 있는 bool type의 형태에서
빠져있는 const QObject *receiver 는 default로 < this >로 정해져있다.
즉 const char *member 는 this class 의 member라야한다.
마찬가지로 첫번째의 static bool type의 형태에서는 member가
< const QObject *receiver >의 member이어야 한다.
각 인자들을 보면 이 함수가 하는 기능이 무엇인지 알 수 있다.
const QObject *sender : signal을 보내줄 class 객체.
const char *signal : '사용자가 취한 행동'이다.
const QObject *receiver : signal을 받아서 처리할 class 객체.
const char *member : 보통 receiver class의 public member 함수를
쓴다.
const char *signal에 대해 좀더 자세히 알아보자.
예제 2.2.4.2의 Source중
QObject::connect(bttnTest, SIGNAL(clicked()), this, SLOT(testClick()));
부분에서 보면 const char *signal 부분을 SIGNAL(clicked())로 해놓았다.
QPushButton의 header file을 보면 다음과 같이 signal 함수들이 선언되어
있는 것을 알 수 있다.
signals:
void pressed();
void released();
void clicked();
void toggled( bool );
각각의 함수들은 함수 자체의 이름에 해당되는 사용자의 행동이 일어났을
때 부합하는 signal을 'this'라는 class에 전해주게 된다. 여기서 this는
class clssTestLineEdit를 말하며, 따라서 connect함수의 마지막 인자인
const char *member부분에는 clssTestLineEdit의 member함수인 testClick
함수가 오게 된 것이다.
< SLOT >이 하는 일은 차차 이야기 하겠다. 우선 그냥 이렇게 쓴다고 문법
처럼 생각을 해두자. member 인자부분은 만약 member 함수를 쓸 경우 반드
시 SLOT안에 member를 두어야하며 또한 이 member 함수는 public slots:
혹은 private slots: 에 선언되어있어야 한다.
2.2.5. 콤보박스(QComboBox)
-------------- < source 2.2.5.1 clssTestComboBox.h > ---------------
#include <qwidget.h>
class QComboBox;
class QPushButton;
class clssTestComboBox : public QWidget
{
Q_OBJECT
public:
clssTestComboBox();
~clssTestComboBox();
public slots:
void testClick();
protected:
private:
QComboBox *cbTest; /* 콤보박스에 대한 class */
QPushButton *bttnTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.5.2 clssTestComboBox.cpp > ---------------
#include <qcombobox.h>
#include <qpushbutton.h>
#include <stdio.h>
#include "clssTestComboBox.h"
clssTestComboBox::clssTestComboBox()
{
cbTest = new QComboBox(this);
cbTest->setGeometry(20, 20, 100, 30);
/* ComboBox에 item들을 넣는다. */
cbTest->insertItem("첫번째", 0);
cbTest->insertItem("두번째", 1);
cbTest->insertItem("세번째", 2);
cbTest->insertItem("네번째", 3);
cbTest->setCurrentItem(0);
bttnTest = new QPushButton("확 인", this);
bttnTest->setGeometry(20, 100, 100, 30);
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(testClick()));
}
clssTestComboBox::~clssTestComboBox()
{
}
void clssTestComboBox::testClick()
{
/* cbTest->currentText()는 현재 ComboBox에서 보이는 */
/* text를 return한다. */
emit printf("콤보박스1의 텍스트는 < %s >입니다.\n",
cbTest->currentText());
}
-------------------------------------------------------------------
틀린부분이 거의 없으므로 QComboBox에 대해서만 설명하겠다.
/usr/lib/qt/include/qcombobox.h를 열어보자.(주의: 만약 당신이 Qt를
다운받아 다른 곳에 인스톨 시켰다면 path가 틀려질 것임.)
public:
QComboBox( QWidget *parent=0, const char *name=0 );
QComboBox( bool rw, QWidget *parent=0, const char *name=0 );
몇몇 특정 widget들을 제외하고는 거의 인자가 비슷하다. 인자들에 관
한 설명은 앞서 이야기했던 부분들과 같으므로 설명을 생략하겠다.
그러면 이번에는 < cbTest->insertItem("첫번째", 0); >부분을 보자.
header file에는 insertItem함수가 다음과 같이 선언되어있다.
이 함수는 콤보박스에 item을 추가할 때 사용하는 함수이다.
void insertItem( const char *text, int index=-1 );
void insertItem( const QPixmap &pixmap, int index=-1 );
첫번째 선언문을 보자.
const char *text는 콤보박스에 추가시킬 text이고 int index는 그 text
의 순번이다. index는 0번부터 시작한다. -1로 초기화되어있다는 것은
콤보박스에 아무 item도 들어있지 않다는 뜻이다. Compile해서 실행시켜
보기 바란다. 좀더 확실하게 이해가 될 것이다.
다음으로 cbTest->currentText()부분을 보자.
header file의 선언을 보면 다음과 같다.
const char *currentText() const;
이 함수는 현재 콤보박스에 나타나있는 text를 return한다.
콤보박스의 현재 text를 setting하는 함수는
void setCurrentItem( int index );
이다. 여기서의 index의 의미는 insertItem의 index의 의미와 동일하다.
< cbTest->setCurrentItem(0); >이 뜻하는 것은 첫번째 item을 현재 text
로 보여주겠다는 이야기다.
-------------- < source 2.2.5.3 main_test4.cpp > ---------------
#include <qapplication.h>
#include "clssTestComboBox.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestComboBox *clssTest = new clssTestComboBox();
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.5 > ----------------------
PROGS = main_test4
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test4.o \
clssTestComboBox_moc.o \
clssTestComboBox.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test4
rm -f *_moc.*
############# COMPILE #############
main_test4.o: main_test4.cpp
$(CC) -c main_test4.cpp $(CFLAGS) -o $@
clssTestComboBox_moc.cpp: clssTestComboBox.h
$(MOC) clssTestComboBox.h -o clssTestComboBox_moc.cpp
clssTestComboBox_moc.o: clssTestComboBox_moc.cpp
$(CC) -c clssTestComboBox_moc.cpp $(CFLAGS) -o $@
clssTestComboBox.o: clssTestComboBox.cpp
$(CC) -c clssTestComboBox.cpp $(CFLAGS) -o $@
main_test4: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.6. 리스트(QListView)
-------------- < source 2.2.6.1 clssTestListView.h > ---------------
#include <qwidget.h>
class QListView;
class QListViewItem;
class QLabel;
class QFrame;
class clssTestListView : public QWidget
{
Q_OBJECT
public:
clssTestListView();
~clssTestListView();
public slots:
void appearItem();
protected:
private:
QFrame *frTest;
QListView *lvTest;
QListViewItem *lviTest[10];
QLabel *lblTest1;
QLabel *lblTest2;
QLabel *lblTest3;
};
-------------------------------------------------------------------
-------------- < source 2.2.6.2 clssTestListView.cpp > ---------------
#include <qlistview.h>
#include <qlabel.h>
#include <qframe.h>
#include <stdio.h>
#include "clssTestListView.h"
clssTestListView::clssTestListView()
{
int i;
char szTest1[50], szTest2[50], szTest3[50];
/* List의 가장자리선의 처리를 위해 frame을 그린다. */
frTest = new QFrame(this);
frTest->setGeometry(20, 20, 200, 200);
frTest->setFrameStyle(QFrame::Box | QFrame::Sunken |
QFrame::WinPanel);
lvTest = new QListView(frTest);
/* frame에서 약 3정도 사방으로 적은 크기가 가장 적당하다. */
lvTest->setGeometry(3, 3, 194, 194);
/* 각 field의 title을 정한다. */
lvTest->addColumn(" 첫번째 ");
lvTest->addColumn(" 두번째 ");
lvTest->addColumn(" 세번째 ");
/* tree장식을 할 것인지 아닌지를 결정한다. */
lvTest->setRootIsDecorated(true);
for (i = 0; i < 9; i++)
{
memset(szTest1, '\0', 50);
memset(szTest2, '\0', 50);
memset(szTest3, '\0', 50);
sprintf(szTest1, "환영 %d", i);
sprintf(szTest2, "리눅스 %d", i);
sprintf(szTest3, "만세 %d", i);
/* 첫번째 두번째 세번째*/
lviTest[i] = new QListViewItem(lvTest, szTest1, szTest2, szTest3);
}
/* List(lvTest)에서 item(lviTest[])이 선택(selectionChanged()) */
/* 되어질 때 this class(clssTestListView)의 member function */
/* (appearItem())을 수행해라. */
QObject::connect(lvTest, SIGNAL(selectionChanged()), this,
SLOT(appearItem()));
lblTest1 = new QLabel(this);
lblTest1->setGeometry(20, 230, 150, 20);
lblTest2 = new QLabel(this);
lblTest2->setGeometry(20, 260, 150, 20);
lblTest3 = new QLabel(this);
lblTest3->setGeometry(20, 290, 150, 20);
}
clssTestListView::~clssTestListView()
{
}
void clssTestListView::appearItem()
{
/* 선택된 item의 첫번째 field를 Label로 나타냄 */
lblTest1->setText(lvTest->currentItem()->text(0));
/* 선택된 item의 두번째 field를 Label로 나타냄 */
lblTest2->setText(lvTest->currentItem()->text(1));
/* 선택된 item의 세번째 field를 Label로 나타냄 */
lblTest3->setText(lvTest->currentItem()->text(2));
}
-------------------------------------------------------------------
/usr/lib/qt/include/qlistview.h file의 선언을 보자.
public:
QListView( QWidget * parent = 0, const char * name = 0 );
다른 widget들과 별 차이가 없으므로 설명은 생략한다.
virtual int addColumn( const char * label, int size = -1);
field의 제목(const char *label)을 지정한다.
size에 대해서는 값정의가 되어있으므로 써도 그만 안써도 그만이라는
이야기인데 흔히 field의 size를 임의로 정해서 해당 field의 값을 찾
아내는데 쓰이는 인자이다. 다시 이야기할 기회가 있을 지는 모르겠지
만 그렇게 중요한 인자는 아니므로 설명은 이쯤 해 두겠다.
virtual void setRootIsDecorated( bool );
만약 인자값을 false로 준다면 첫번째 field의 item들 앞에는 아무것
도 없게 된다.
현재 tree장식이 되어있는지 아닌지를 검사하고 싶다면 다음 함수를 써라.
bool rootIsDecorated() const;
이번에는 실제 item을 List에 삽입하는 방법에 대해 알아보자.
header file에는 다음과 같이 QListViewItem을 선언하고 있다.
public:
QListViewItem( QListView * parent );
QListViewItem( QListViewItem * parent );
QListViewItem( QListView * parent, QListViewItem * after );
QListViewItem( QListViewItem * parent, QListViewItem * after );
QListViewItem( QListView * parent,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
QListViewItem( QListViewItem * parent,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
QListViewItem( QListView * parent, QListViewItem * after,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
QListViewItem( QListViewItem * parent, QListViewItem * after,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
약간 복잡하다고 생각할 지도 모르지만 상당히 간단한 선언이다.
우선 parent인자부터 살펴보자.
위에서 선언된 parent인자의 type은 < QListView * > 와 < QListViewItem * >
이 두가지이다.
< QListView * >를 parent인자로 받는다는 것은 현재 생성되어있는 List
에 < const char * > 형태의 text를 item으로 삽입한다는 뜻이다.
< QListViewItem * >를 parent인자로 받는다는 것은 List의 특정 item(parent)
을 root로 하는 tree구조로서 그 item의 자식 node를 삽입한다는 뜻이다.
언뜻 이해가 가지 않을 것이다. 위 source에서
< lviTest[i] = new QListViewItem(lvTest, szTest1, szTest2, szTest3); >
이 포함되어있는 for loop문을 다음과 같이 바꾼 후 Compile하여 실행시켜
보라.
lviTest[0] = new QListViewItem(lvTest,
"테스트1-1", "테스트1-2", "테스트1-3");
lviTest[1] = new QListViewItem(lviTest[0],
"테스트2-1", "테스트2-2", "테스트2-3");
실행시켜보면 위에서 필자가 이야기했던 부분들이 쉽게 이해가 갈 것이다.
다음은 List에서 삽입된 text를 얻는 방법에 대한 설명이다.
lblTest1->setText(lvTest->currentItem()->text(0));
QListViewItem의 text를 얻는 방법은 간단하다.
만약 QListViewItem type의 class가 lviTest1이라면, lviTest1의 첫번째
field의 text를 얻으려면 < lviTest1->text(0); >이라고 하면 될 것이다.
ComboBox와 비슷하지만 index가 의미하는 것이 틀리므로 유의하기 바란다.
위에서 lvTest->currentItem()이 의미하는 것은 현재 List에서 선택된
Item set을 말한다. header file에는 currentItem()함수가 다음과 같이
선언되어 있다.
QListViewItem * currentItem() const;
type을 보면 QListViewItem이다. 이는 currentItem()이 QListViewItem
class의 모든 member들을 참조할 수 있다는 이야기가 된다.
따라서 현재 List에서 선택된 Item의 두번째 field의 text를 알고 싶다면
다음과 같이 하면 된다.
lblTest1->setText(lvTest->currentItem()->text(1));
이번엔 QListView에서 발생가능한 Signal에 대해 알아보도록 하자.
header file을 보면 다음과 같이 총 6개의 signal함수가 있다.
signals:
void selectionChanged();
void selectionChanged( QListViewItem * );
void currentChanged( QListViewItem * );
void doubleClicked( QListViewItem * );
void returnPressed( QListViewItem * );
void rightButtonClicked( QListViewItem *, const QPoint&, int );
void rightButtonPressed( QListViewItem *, const QPoint&, int );
각 Signal들의 발생요인은 위함수의 제목과 동일하다.
-------------- < source 2.2.6.3 main_test5.cpp > ---------------
#include <qapplication.h>
#include "clssTestListView.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestListView *clssTest = new clssTestListView();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.6 > ----------------------
PROGS = main_test5
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test5.o \
clssTestListView_moc.o \
clssTestListView.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test5
rm -f *_moc.*
############# COMPILE #############
main_test5.o: main_test5.cpp
$(CC) -c main_test5.cpp $(CFLAGS) -o $@
clssTestListView_moc.cpp: clssTestListView.h
$(MOC) clssTestListView.h -o clssTestListView_moc.cpp
clssTestListView_moc.o: clssTestListView_moc.cpp
$(CC) -c clssTestListView_moc.cpp $(CFLAGS) -o $@
clssTestListView.o: clssTestListView.cpp
$(CC) -c clssTestListView.cpp $(CFLAGS) -o $@
main_test5: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.7. 체크박스(QCheckBox), 라디오버튼(QRadioButton)
-------------- < source 2.2.7.1 clssTestButton.h > ---------------
#include <qwidget.h>
class QButtonGroup;
class QRadioButton;
class QCheckBox;
class QPushButton;
class clssTestButton : public QWidget
{
Q_OBJECT
public:
clssTestButton();
~clssTestButton();
public slots:
void procButton();
protected:
private:
/* QRadioButton을 묶기 위한 Group class 선언 */
QButtonGroup *bgTest;
QRadioButton *rbTest1;
QRadioButton *rbTest2;
QCheckBox *chkbTest1;
QCheckBox *chkbTest2;
QPushButton *bttnTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.7.2 clssTestButton.cpp > ---------------
#include <qradiobutton.h>
#include <qpushbutton.h>
#include <qcheckbox.h>
#include <qbuttongroup.h>
#include <stdio.h>
#include "clssTestButton.h"
clssTestButton::clssTestButton()
{
/* radio button은 어느 한 버튼이 활성화되면 다른 버튼들은 */
/* 비활성화되어야 한다. 이런 기능을 위해 QButtonGroup class */
/* 를 정의한다. */
bgTest = new QButtonGroup(this);
bgTest->setGeometry(20, 20, 300, 100);
/* radio button들을 bgTest에 종속시킨다. */
rbTest1 = new QRadioButton("리눅스 라디오", bgTest);
rbTest1->setGeometry(20, 20, 100, 20);
rbTest2 = new QRadioButton("만세 라디오", bgTest);
rbTest2->setGeometry(150, 20, 100, 20);
/* radio button들이 단지 하나의 버튼만이 선택되게 하기 위해 */
/* 마지막으로 QButtonGroup class변수에 삽입시킨다. */
bgTest->insert(rbTest1, 0);
bgTest->insert(rbTest2, 1);
/* 체크박스 정의 */
chkbTest1 = new QCheckBox("리눅스 체크", this);
chkbTest1->setGeometry(20, 160, 100, 20);
chkbTest2 = new QCheckBox("만세 체크", this);
chkbTest2->setGeometry(150, 160, 100, 20);
bttnTest = new QPushButton("확 인", this);
bttnTest->setGeometry(60, 250, 100, 30);
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(procButton()));
}
clssTestButton::~clssTestButton()
{
}
void clssTestButton::procButton()
{
if (rbTest1->isChecked())
emit printf("리눅스 라디오 버튼이 체크되어 있습니다.\n");
else if (rbTest2->isChecked())
emit printf("만세 라디오 버튼이 체크되어 있습니다.\n");
else
emit printf("체크되어있는 라디오 버튼이 없습니다.\n");
if (chkbTest1->isChecked())
emit printf("리눅스 체크 박스가 체크되어 있습니다.\n");
if (chkbTest2->isChecked())
emit printf("만세 체크 박스가 체크되어 있습니다.\n");
if (chkbTest1->isChecked() == false &&
chkbTest2->isChecked() == false)
emit printf("체크되어있는 체크박스가 없습니다.\n");
}
-------------------------------------------------------------------
QButtonGroup부터 보도록하자. /usr/lib/qt/include/qbuttongroup.h에는
다음과 같이 선언되어있다.
public:
QButtonGroup( QWidget *parent=0, const char *name=0 );
QButtonGroup( const char *title, QWidget *parent=0,
const char *name=0 );
인자들에 대한 설명은 별로 필요없으리라 생각하므로 생략하겠다.
생성자부분보다는 insert하는 부분의 설명이 필요하리라 생각한다.
int insert( QButton *, int id=-1 );
int id 는 group에 포함되는 버튼들의 id를 지정해주는 역할을 한다.
순서에는 상관 없으나 값은 고유값을 가져야 한다.
다음으로 checkbox의 header file /usr/lib/qt/include/qradiobutton.h 에
나타난 생성자부분을 보도록 하자.
public:
QRadioButton( QWidget *parent=0, const char *name=0 );
QRadioButton( const char *text, QWidget *parent=0,
const char *name=0 );
QRadioButton의 < const char *text > 인자에 문자열을 넘겨주면 그 문자열이
이 radiobutton의 title이 된다. 체크가 되어있는지를 검사하는 함수는
isChecked()이다. 인위적으로 checking하는 함수는 header file에 이렇게 선
언되어있다.
void setChecked( bool check );
isChecked(), setChecked(bool check) 두 함수 모두 QRadioButton과
QCheckBox에 똑같이 선언되어있고 또한 기능도 같다.
-------------- < source 2.2.7.3 main_test6.cpp > ---------------
#include <qapplication.h>
#include "clssTestButton.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestButton *clssTest = new clssTestButton();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.7 > ----------------------
PROGS = main_test6
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test6.o \
clssTestButton_moc.o \
clssTestButton.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test6
rm -f *_moc.*
############# COMPILE #############
main_test6.o: main_test6.cpp
$(CC) -c main_test6.cpp $(CFLAGS) -o $@
clssTestButton_moc.cpp: clssTestButton.h
$(MOC) clssTestButton.h -o clssTestButton_moc.cpp
clssTestButton_moc.o: clssTestButton_moc.cpp
$(CC) -c clssTestButton_moc.cpp $(CFLAGS) -o $@
clssTestButton.o: clssTestButton.cpp
$(CC) -c clssTestButton.cpp $(CFLAGS) -o $@
main_test6: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.8. 메뉴(QMenu)
-------------- < source 2.2.8.1 clssTestMenu.h > ---------------
#include <qwidget.h>
class QMenuBar;
class QPopupMenu;
class clssTestMenu : public QWidget
{
Q_OBJECT
public:
clssTestMenu();
~clssTestMenu();
public slots:
void funcTest1();
void funcTest2();
void funcTest3();
void funcTest4();
void funcTest5();
void funcTest6();
protected:
private:
/* Pulldown 메뉴를 위한 메뉴바 */
QMenuBar *mnuMainBar;
/* Popup 메뉴를 위한 메뉴 */
QPopupMenu *mnuTest1;
QPopupMenu *mnuTest2;
QPopupMenu *mnuTest3;
};
-------------------------------------------------------------------
-------------- < source 2.2.8.2 clssTestMenu.cpp > ---------------
#include <qapplication.h>
/* hot key를 위한 header. 여기서는 hot key를 쓰지 않는다. */
/* 한 번 시험삼아 hot key기능까지 넣어보기 바란다. */
#include <qkeycode.h>
/* QMenuBar를 위한 header */
#include <qmenubar.h>
/* QPopupMenu를 위한 header */
#include <qpopupmenu.h>
#include <stdio.h>
#include "clssTestMenu.h"
clssTestMenu::clssTestMenu()
{
/* 전체적인 window의 형태를 Microsoft Windows의 형태로 맞춘다. */
qApp->setStyle(WindowsStyle);
mnuTest1 = new QPopupMenu;
/* Popup menu에 아이템을 넣는다. */
mnuTest1->insertItem("테스트1메뉴의 서브메뉴1", this,
SLOT(funcTest1()));
mnuTest1->insertItem("테스트1메뉴의 서브메뉴2", this,
SLOT(funcTest2()));
mnuTest1->insertItem("테스트1메뉴의 서브메뉴3", this,
SLOT(funcTest3()));
mnuTest2 = new QPopupMenu;
mnuTest2->insertItem("테스트2메뉴의 서브메뉴1", this,
SLOT(funcTest4()));
mnuTest2->insertItem("테스트2메뉴의 서브메뉴2", this,
SLOT(funcTest5()));
mnuTest3 = new QPopupMenu;
mnuTest3->insertItem("테스트3메뉴의 서브메뉴1", this,
SLOT(funcTest6()));
/* 각 popup menu들을 pulldown menu에 삽입시킨다. */
mnuMainBar = new QMenuBar(this);
mnuMainBar->insertItem(" 테스트1 ", mnuTest1);
mnuMainBar->insertItem(" 테스트2 ", mnuTest2);
mnuMainBar->insertItem(" 테스트3 ", mnuTest3);
}
clssTestMenu::~clssTestMenu()
{
}
void clssTestMenu::funcTest1()
{
emit printf("test1 메뉴\n");
}
void clssTestMenu::funcTest2()
{
emit printf("test2 메뉴\n");
}
void clssTestMenu::funcTest3()
{
emit printf("test3 메뉴\n");
}
void clssTestMenu::funcTest4()
{
emit printf("test4 메뉴\n");
}
void clssTestMenu::funcTest5()
{
emit printf("test5 메뉴\n");
}
void clssTestMenu::funcTest6()
{
emit printf("test6 메뉴\n");
}
-------------------------------------------------------------------
위의 결과치를 미리 나타내자면 다음과 같다.
---------------------------------------------------------
| 테스트1 | 테스트2 | 테스트3 |
|---------------------------------------------------------|
|| 테스트1메뉴의 서브메뉴1 | |
|| 테스트1메뉴의 서브메뉴2 | |
|| 테스트1메뉴의 서브메뉴3 | |
| ------------------------- |
| |
| |
| -------------------------- |
---------------- ---------------
뭐 그렇게 특별한 내용은 없다. 한번만 훑어보면 금방 이해가 갈 것이다.
/usr/lib/qt/include/qpopupmenu.h를 살펴보자.
public:
QPopupMenu( QWidget *parent=0, const char *name=0 );
별로 설명할 필요는 없으리라 생각한다. 중요한 것은 insertItem함수인데
header file을 찾아보면 알겠지만 이 함수가 member로 선언되어있지 않을
것이다. 그러면 어디에 선언이 되어 있을까?
QPopupMenu class의 선언부를 보자.
class Q_EXPORT QPopupMenu : public QTableView, public QMenuData
여기서 보면 QPopupMenu class는 QMenuData class로부터 상속을 받았다.
즉, QMenuData class의 public member들을 모두 QPopupMenu도 접근할 수
있다는 이야기다. 그러면 다시 /usr/lib/qt/include/pmenudata.h를 열어
보자. insertItem함수에 대한 선언이 다음과 같이 여러개가 선언되어있다.
int insertItem( const char *text,
const QObject *receiver, const char *member,
int accel=0 );
int insertItem( const QPixmap &pixmap,
const QObject *receiver, const char *member,
int accel=0 );
int insertItem( const QPixmap &pixmap, const char *text,
const QObject *receiver, const char *member,
int accel=0 );
int insertItem( const char *text,
const QObject *receiver, const char *member,
int accel, int id, int index = -1 );
int insertItem( const QPixmap &pixmap,
const QObject *receiver, const char *member,
int accel, int id, int index = -1 );
int insertItem( const QPixmap &pixmap, const char *text,
const QObject *receiver, const char *member,
int accel, int id, int index = -1 );
int insertItem( const char *text, int id=-1, int index=-1 );
int insertItem( const char *text, QPopupMenu *popup,
int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, QPopupMenu *popup,
int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, const char *text,
int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, const char *text,
QPopupMenu *popup,
int id=-1, int index=-1 );
이제는 밑도끝도 없이 insertItem이라는 함수가 어디서 튀어나왔는지 이
해가 될 것이다. 앞으로도 member function이 이런 식으로 호출되는 경우
가 가끔씩 있을 것이다. 당황하지 말고 차근차근 이해하기 바란다.
insertItem 함수에 대한 설명을 하자. 바로 위에 있는 모든 경우를 다 설
명할 필요는 없을 것 같다. source 2.2.8.2에서 쓰인 insertItem함수에
대한 설명만을 하겠다. 그 source에서 쓰인 함수는 바로 이 함수이다.
int insertItem( const char *text,
const QObject *receiver, const char *member,
int accel=0 );
각 인자에 대해 설명하겠다.
첫번째 인자 < const char *text >는 Popup menu에 들어갈 Item의 이름이다.
두번째 인자 < const QObject *receiver >는 Item의 이름이 삽입되는 class
이다.
세번째 인자 < const char *member >는 이 Item이 사용자에 의해 선택되어
click되었을 때 실행할 receiver의 member function이다.
네번째 인자 < int accel >은 header source 에서 잠깐 설명한 hot-key(혹
은 accel-key)를 말한다. accel-key에 대해 약간만 더 알아보자.
/usr/lib/qt/include/qkeycode.h에 보면 모든 key값들이 이런식으로 선언
혹은 정의되어있다.
.
.
.
const uint SHIFT = 0x00002000; // accelerator modifiers
const uint CTRL = 0x00004000;
const uint ALT = 0x00008000;
const uint ASCII_ACCEL = 0x10000000;
#define Key_Escape 0x1000 // misc keys
#define Key_Tab 0x1001
#define Key_Backtab 0x1002
#define Key_Backspace 0x1003
#define Key_Return 0x1004
#define Key_Enter 0x1005
#define Key_Insert 0x1006
#define Key_Delete 0x1007
#define Key_Pause 0x1008
#define Key_Print 0x1009
#define Key_SysReq 0x100a
#define Key_Home 0x1010 // cursor movement
#define Key_End 0x1011
#define Key_Left 0x1012
.
.
.
위에서 보면 SHIFT, CTRL, ALT, ASCII_ACCEL등은 4byte unsigned int
형태로 정의되어있고 나머지 key값들은 모두 2byte형태로 선언되어있
다. 이는 특수확장키인 SHIFT, CTRL, ALT key와 나머지 key들을 조합
하여 쓸수 있다는 이야기인데 예를 들어 다시 한번 설명하겠다.
만약 사용자가 < 테스트1메뉴의 서브메뉴1 >을 단축키 ALT+ENTER로
설정하고 싶다면 다음과 같이 하면 된다.
mnuTest1->insertItem("테스트1메뉴의 서브메뉴1", this,
SLOT(funcTest1()), ALT|ENTER);
이젠 QMenuBar에 대해 살펴보자.
/usr/lib/qt/include/qmenubar.h에는 다음과 같이 선언하고 있다.
class Q_EXPORT QMenuBar : public QFrame, public QMenuData
{
friend class QPopupMenu;
Q_OBJECT
public:
QMenuBar( QWidget *parent=0, const char *name=0 );
.
.
.
};
아무리 찾아보아도 insertItem함수를 찾을 수 없다. 하지만 QPopupMenu
의 경우와 마찬가지로 QMenuData의 모든 public member들을 상속받고
있다.
그러므로 이에 대한 설명은 더이상 하지 않아도 되리라 믿는다.
-------------- < source 2.2.8.3 main_test7.cpp > ---------------
#include <qapplication.h>
#include "clssTestMenu.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestMenu *clssTest = new clssTestMenu();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.8 > ----------------------
PROGS = main_test7
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test7.o \
clssTestMenu_moc.o \
clssTestMenu.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test7
rm -f *_moc.*
############# COMPILE #############
main_test7.o: main_test7.cpp
$(CC) -c main_test7.cpp $(CFLAGS) -o $@
clssTestMenu_moc.cpp: clssTestMenu.h
$(MOC) clssTestMenu.h -o clssTestMenu_moc.cpp
clssTestMenu_moc.o: clssTestMenu_moc.cpp
$(CC) -c clssTestMenu_moc.cpp $(CFLAGS) -o $@
clssTestMenu.o: clssTestMenu.cpp
$(CC) -c clssTestMenu.cpp $(CFLAGS) -o $@
main_test7: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.9. 툴바(QToolBar), MainWindow(QMainWindow)
-------------- < source 2.2.9.1 clssTestToolBar.h > ---------------
#include <qmainwindow.h>
class QToolBar;
/* 이부분 < : public QMainWindow >을 주의 깊게 보기 바란다. */
class clssTestToolBar : public QMainWindow
{
Q_OBJECT
public:
clssTestToolBar();
~clssTestToolBar();
public slots:
void funcTest1();
void funcTest2();
void funcTest3();
protected:
private:
QToolBar *tbTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.9.2 clssTestToolBar.cpp > ---------------
#include <qapplication.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qpixmap.h>
#include <stdio.h>
#include "clssTestToolBar.h"
#ifndef TEST1_TOOLTIP
/* ToolButton에 마우스를 갖다대었을 때 나올 풍선도움말을 선언한다. */
#define TEST1_TOOLTIP "첫번째 테스트"
#define TEST2_TOOLTIP "두번째 테스트"
#define TEST3_TOOLTIP "세번째 테스트"
#endif
clssTestToolBar::clssTestToolBar()
{
/* Icon를 저장할 QPixmap변수 선언 */
QPixmap iconTest1, iconTest2, iconTest3;
qApp->setStyle(WindowsStyle);
/* ToolBar에 들어갈 image들을 선언한 QPixmap변수에 Loading한다. */
iconTest1.load("./test1.gif");
iconTest2.load("./test2.gif");
iconTest3.load("./test3.gif");
tbTest = new QToolBar(this);
/* ToolBar의 방향은 수평이다. */
tbTest->setOrientation(tbTest->Horizontal);
/* ToolButton을 생성한다. */
QToolButton *tbttnTest1 = new QToolButton(iconTest1,
TEST1_TOOLTIP, 0, this, SLOT(funcTest1()),
tbTest, "test1");
QToolButton *tbttnTest2 = new QToolButton(iconTest2,
TEST2_TOOLTIP, 0, this, SLOT(funcTest2()),
tbTest, "test2");
QToolButton *tbttnTest3 = new QToolButton(iconTest3,
TEST3_TOOLTIP, 0, this, SLOT(funcTest3()),
tbTest, "test3");
}
clssTestToolBar::~clssTestToolBar()
{
}
void clssTestToolBar::funcTest1()
{
emit printf("첫번째 단축아이콘\n");
}
void clssTestToolBar::funcTest2()
{
emit printf("두번째 단축아이콘\n");
}
void clssTestToolBar::funcTest3()
{
emit printf("세번째 단축아이콘\n");
}
-------------------------------------------------------------------
/usr/lib/qt/include/qtoolbar.h를 살펴보자.
public:
QToolBar( const char * label,
QMainWindow *, QMainWindow::ToolBarDock = QMainWindow::Top,
bool newLine = FALSE, const char * name = 0 );
QToolBar( const char * label, QMainWindow *, QWidget *,
bool newLine = FALSE, const char * name = 0, WFlags f = 0 );
QToolBar( QMainWindow * parent = 0, const char * name = 0 );
여기서 주의깊게 보아야 할 부분이 한군데 있다. 바로 QMainWindow *parent
인자 부분이다. 이제까지 다뤘던 widget들은 parent 의 type이 모두 QWidget
이었다. 하지만 여기서는 QMainWindow이다. 그러면 계층정보를 좀 보자.
QMainWindow와 QToolBar 모두 QWidget의 하위 class로서 서로 형제간이다.
형제간에는 서로 종속될 수 있으므로 QToolBar의 parent를 QMainWindow로 정
의할 수 있다. QMenuBar또한 마찬가지이다. 이렇듯 class계층정보를 바탕으
로 상하관계를 설정해준다면 명확한 프로그래밍이 가능하게 된다.
/usr/lib/qt/include/qtoolbutton.h를 보자.
public:
QToolButton( QWidget * parent = 0, const char * name = 0 );
QToolButton( const QPixmap & pm, const char * textLabel,
const char * grouptext,
QObject * receiver, const char * slot,
QToolBar * parent, const char * name = 0 );
QToolButton( QIconSet s, const char * textLabel,
const char * grouptext,
QObject * receiver, const char * slot,
QToolBar * parent, const char * name = 0 );
source 2.2.9.2에서 쓰인 QToolButton 함수와 비교해서 설명하겠다.
QToolButton *tbttnTest1 = new QToolButton(iconTest1,
TEST1_TOOLTIP, 0, this, SLOT(funcTest1()),
tbTest, "test1");
이 함수는 header file에 선언된 세개의 함수중 두번째 함수가 쓰인
것이다.
두번째 함수의 첫번째 인자는 icon을 나타낸다. Qt에서는 여러가지
image file들 즉, bmp, gif, jpg, xpm 등등을 지원한다. 각 file들을
loading하는 방법은 약간씩 다르다. 이런 file들을 loading하는 방법
은 header file이나 혹은 Qt에서 제공하는 예제 프로그램들을 보면
금방 알 수 있을 것이다. gif와 jpg는 압축file이므로 먼저 Linux의
X-window에서 사용할 만한 format으로 변환시켜야한다. X-window에서
사용하는 file format은 xpm file인데 다음은 간단한 xpm file의 내
부를 vi로 열어본 것이다.
static char * Ant_xpm[] = {
"48 48 60 1",
" c None",
". c #492449244924",
"X c #618561856185",
"o c #9E799E799E79",
"O c #208120812081",
"+ c #514455555144",
"@ c #410341034103",
"# c #965896589658",
"$ c #082008200820",
"% c #8E388A288E38",
"& c #69A669A669A6",
"* c #71C675D671C6",
"= c #861782078617",
"- c #28A228A228A2",
"; c #104014511040",
": c #10400C300000",
"> c #410324921040",
", c #082004100000",
"< c #000000000000",
"1 c #71C63CF32081",
"2 c #8E38492428A2",
"3 c #208114511040",
"4 c #30C234D330C2",
"5 c #208110400820",
"6 c #492424921040",
"7 c #51442CB21861",
"8 c #38E320811040",
"9 c #104008200000",
"0 c #208114510820",
"q c #28A214510820",
"w c #79E741032081",
"e c #596530C21861",
"r c #10400C300820",
"t c #618534D32081",
"y c #30C218611040",
"u c #28A218610820",
"i c #79E7451428A2",
"p c #9658514430C2",
"a c #18610C300820",
"s c #186110400820",
"d c #30C21C711040",
"f c #49242CB21861",
"g c #104010400820",
"h c #59652CB21861",
"j c #492428A21861",
"k c #618538E32081",
"l c #30C21C710820",
"z c #69A634D32081",
"x c #20811C711861",
"c c #30C224921861",
"v c #30C22CB228A2",
"b c #186110401040",
"n c #410328A21861",
"m c #8617451428A2",
"M c #38E31C711040",
"N c #61855D755965",
"B c #69A638E32081",
"V c #410320811040",
"C c #51443CF330C2",
"Z c #514430C22081",
" . ",
" X. ",
" oOo ",
" +@ ",
" # $% ",
" %+ O ",
" &* =O ",
" O* =- ",
" o; =O ",
" &@ =-=X+= ",
" O. X:>,--# ",
" #<# *<12>3* = ",
" &4- ;56789<&$# ",
" o+$-= *0q<6,w,$;= o#",
" %;4* +8wq,ee<<<% *;%",
" %<O.#o #Xr8>tw<<<; o#.;+# ",
" %4% #+;<O% .9y<<u>,08<<<<<$-4# ",
" X;*$% &O<@5ipa<<<sd:O% ",
" +$% +$* *;fp8<,$$<<Xo ",
" @$% o$;o X4gh5<<7$#* %++-# ",
" #.$% oO. ;5jk9<<ld$ #X-;;.## ",
" #*.-<;;% o;.<2zu<<5y<* ;;+# ",
" .O.X=# =%=&X+6ia<,j4$;4# +- ",
" *@;<<$O+*x0<97yX=4<$#o=<= ",
" +;O4+&+O9<<<<ae0<= @<-.$O ",
" *$o %c8etd<<<vb<+ &<<$o ",
" %$* &nt2pe<<<$X@& &;+ ",
" o$- =07t2e<<5aOOX<@ %+ ",
" oO$o xt2qae,,79O<#X<+ ",
" @<= &>mtd<Mtjq<@< 4<* ",
" o&<+ NB7B<<<se<$+< o$;% ",
" +;4= :waa1,9jV5+X$ =<Oo ",
" #@$.# o9e9<d5j5,C -- @4 ",
"-<+# <7<,ue5<$% <& &$o ",
"o -5<8q:Z+o $ &;# ",
" o4$--&o +@ &$% ",
" o4$* <# ",
" =;O% %< ",
" o.<4o =4 ",
" o$<& ++ ",
" XO% -X ",
" ;o O# ",
" X@ $ ",
" ;= & ",
" &- ",
" -= ",
" %; ",
" %+ "};
약간 의외일 것이다. xpm file은 이처럼 C에서 바로 쓸 수 있는 char
형 pointer 변수인 것이다. 약간 감이 잡힐 것이다.
즉 압축 file인 gif를 QPixmap으로 Loading을 하게 되면 Loader는
이러한 char형 pointer로 변환을 해주고 그를 memory에 loading하게
된다. Windows System과 비슷하지 않은가? Windows에서도 이런 과정
을 거쳐 그림을 화면에 뿌려준다는 것은 웬만한 programmer라면 알
고 있을 것이다. 즉, gif나 jpg format을 Windows에서 접근 가능한
bmp로 변환시켜 memory에 loading한 다음 그를 화면에 나타낸다.
이런 bmp의 역할을 하는 것이 바로 Pixmap file인 xpm format file들
인 것이다. 이해가 됐을 지 모르겠다.
두번째 인자인 char 형 pointer인 textlabel은 Mouse Pointer가
ToolButton에 위치했을 때 나타낼 수 있는 Tooltip(풍선도움말)이다.
나머지 인자들은 지금까지 착실하게 source들을 이해했다면 설명할
필요가 없을 것 같아 생략하겠다.
-------------- < source 2.2.9.3 main_test8.cpp > ---------------
#include <qapplication.h>
#include "clssTestToolBar.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestToolBar *clssTest = new clssTestToolBar();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.9 > ----------------------
PROGS = main_test8
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test8.o \
clssTestToolBar_moc.o \
clssTestToolBar.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test8
rm -f *_moc.*
############# COMPILE #############
main_test8.o: main_test8.cpp
$(CC) -c main_test8.cpp $(CFLAGS) -o $@
clssTestToolBar_moc.cpp: clssTestToolBar.h
$(MOC) clssTestToolBar.h -o clssTestToolBar_moc.cpp
clssTestToolBar_moc.o: clssTestToolBar_moc.cpp
$(CC) -c clssTestToolBar_moc.cpp $(CFLAGS) -o $@
clssTestToolBar.o: clssTestToolBar.cpp
$(CC) -c clssTestToolBar.cpp $(CFLAGS) -o $@
main_test8: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.3. QT Library를 다운받으려면?
여러 Site가 있지만 가장 대표적인 Url은
< http://www.troll.no >
에서 다운받아 사용할 수 있다.
< 한마디 >.
지금까지 하나의 Project를 꾸미기 위해 그리 짧지만은 않은 과정을 거쳐왔습
니다.
기초 과정은 모두 끝난 것입니다. 아직 기초과정에 속한 network관련 Library
에 대한 설명의 화두도 나오지 않은 상태이지만 Project를 구현하기에는 충분한
실력을 쌓았다고 할 수 있겠죠. 기초 과정은 대략 설명이 끝났으므로 앞으로
나올 기초 부분에 연관된 code들에 대해서는 되도록이면 설명을 생략하도록 하
겠습니다.
===============================================================
날짜: 1999.12.26
컴파일러: gcc
언어 : C++ (QT 1.44 Library 사용)
환경 : 알짜리눅스 6.1 XWindow환경
===============================================================
<들어가기전에...>
안녕하세요.
(주)아이탑에서 개발연구원으로 일하고 있는 박영선이라고 합니다.
우선, 간단하게 이 자료의 내용을 소개한 뒤 시작하기로 하죠.
제가 리눅스를 처음 접하고, 또 Gtk와 QT를 만나게 되면서, 실제 저
희회사 제품개발당시 힘들었던 점과 개발 후에 느꼈던 미비한 점등
을 참고해서 이 프로젝트를 계획하게 되었습니다.
이 프로젝트는 저희회사 개발원들이 개발했던 Topflash라는, 네
트워크, 프락시, 방화벽, 메신저등을 종합적으로 관리할 수 있는
통합인터넷솔루션중 제가 개발한 TopmanagerX를 기반으로 만들어졌
습니다.
이는 QT라이브러리와, ANSI-C기반의 file처리루틴을 라이브러리로
묶어 구현한 내용입니다.
사정상 이 중에서 네트워크관리를 QT를 사용하여 구현해보도록 하겠
습니다.
다음에는 Gtk를 이용한 버젼을 다른 처리 내용의 글로 올리겠습니다.
문의사항이나 개선점을 발견하신분들은 위의 E-mail주소로 꼭 연락
해주시기 바랍니다.
그리고 Gtk의 글이 끝나면 마지막으로 Kernel분석에 관한 자료를 만
들까 생각중입니다.
많은 관심과 격려 부탁드립니다.
===============================================================
<목차>
0. RedHat Linux 6.1의 System관리File에 관하여...
0.1. /etc디렉토리에 있는 file들
0.1.1. /etc/conf.modules
0.1.2. /etc/sysconfig/network-scripts/ifcfg-eth0
0.1.3. /etc/sysconfig/network
1. 이중연결리스트를 이용한 File처리 루틴에 관하여...
1.1. 이중연결리스트(Double LinkedList) 구현
1.2. file처리 라이브러리 구현
1.3. 개선되어야할 내용
2. QT 기본 다지기
2.1. QT의 Class계층정보
2.2. Widget이란?
2.2.1. 위젯(QWidget), 프레임(QFrame), 버튼(QButton)
2.2.2. Makefile 만들기
2.2.3. 라벨(QLabel)
2.2.4. 편집박스(QLineEdit)
2.2.4.1 QObject::connect, SIGNAL과 SLOT
2.2.5. 콤보박스(QComboBox)
2.2.6. 리스트(QListView)
2.2.7. 체크박스(QCheckBox), 라디오버튼(QRadioButton)
2.2.8. 메뉴(QMenu)
2.2.9. 툴바(QToolBar)와 메인윈도우(QMainWindow)
2.3. QT를 얻으려면?
< 한마디 >.
3. 기본 Format 작성하기
3.1. 메뉴 만들기
3.2. 툴바 집어넣기
3.3. Network관련 라이브러리 구현하기
3.4. Class만들기
3.4.1 일반네트워크관리
3.4.2 DNS관리
3.4.3 IP-Aliasing관리
< *** 전체 Source *** >
3.5. 개선되어야할 내용
4. 프로젝트를 마치고...
===============================================================
< 0. RedHat Linux 6.1의 System관리File에 관하여... >
0.1. /etc디렉토리에 있는 file들
0.1.1. /etc/conf.modules
이 file은 하드웨어에 새로운 디바이스가 추가되었을 때 해당 디바이스모
듈을 알기 쉬운 이름으로 속이는 역할을 한다.
예를들어 Lan Card의 Chipset이름이 3c59x라면 /etc/conf.modules에는 다
음과 같은 내용이 들어가야한다.
alias eth0 3c59x
여기서 3c59x는 엄밀히 말하자면 Chipset의 이름이 아니라 리눅스에서 제
공하는 Network Module의 이름이다. 리눅스의 제공 Network Module들은
각각 Chipset이름에 1:1 매치된다.(여기서의 Module이란 Windows에서의
드라이버개념과 비슷하다.)
Module file들은 *.o로 끝나며 이들에 대한 정보는
/lib/modules/2.2.12-20kr/net 디렉토리에 있다.
위의 "alias eth0 3c59x"의 의미는 3c59x모듈을 eth0로 aliasing해서 쓰겠
다는 뜻이다.
만약 Lan Card가 두장이상 시스템에 설치되어있다면 아래와 같은 정보가 추
가되어있을 것이다.
alias eth1 '모듈이름'
alias eth2 '모듈이름'
만약 카드가 ISA라면 이 file안에서 io와 irq번호를 수동으로 설정해주어야
한다. 원래 io, irq또한 설정하는 루틴도 같이 넣으려고 했지만, 요즈음
90% 이상 PCI카드를 사용하고 있으므로 이 설정은 뺐다.
0.1.2. /etc/sysconfig/network-scripts/ifcfg-eth0
위 작업을 마쳤다면 다음 file의 내용을 scan해보자.
cat /etc/sysconfig/network-scripts/ifcfg-eth0
다음과 같은 내용이 출력될 것이다.
DEVICE=eth0
BROADCAST=172.31.255.255
IPADDR=172.31.0.202
NETMASK=255.255.0.0
NETWORK=172.31.0.0
ONBOOT=yes
물론 IP, Netmask, Network, Broadcast등은 이렇게 되어있지는 않을 것이다.
맨 마지막줄의 "ONBOOT=yes"의 뜻은 시스템의 부팅시에 모듈을 올릴 것인지에
대한 옵션이다.
이 file이 없다면 에디터를 써서 file을 생성시키기 바란다.
이렇게 setting을 하고, 만약 Lan Card의 Chipset이 3c59x에 해당되는 모듈에
매치된다면 다음 명령어를 실행해보라.
/etc/rc.d/init.d/network restart
이 명령어를 실행하게 되면 모듈올리기에 성공하는지 실패하는지를 나타내준다.
만약 성공한다면 마지막으로 다음 명령어를 실행해본다.
ifconfig
이 명령어에 대한 필자의 시스템에서의 결과값은 다음과 같다.
eth0 Link encap:Ethernet HWaddr 00:90:27:A2:70:DE
inet addr:172.31.0.1 Bcast:172.31.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:88055 errors:0 dropped:0 overruns:0 frame:0
TX packets:119306 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:17 Base address:0xef00
eth1 Link encap:Ethernet HWaddr 00:90:27:57:17:27
inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:16 Base address:0xef40
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:3924 Metric:1
RX packets:146 errors:0 dropped:0 overruns:0 frame:0
TX packets:146 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
위의 결과값에서 볼 수 있듯이 필자의 IP(가상)은 "inet addr:172.31.0.1"에
명시되어 있고 나머지 Broadcast와 Netmask에 대한 정보 또한 나타나있다.
여기서 eth1에 대한 정보는 두번째 Lan Card를 장착했을 때, 또 그 Card를
OS에서 올바르게 인식했을 때 나타나는 정보이다. 설정 file들에 대한 정보는
eth0와 흡사하다.
출력결과중 세번째 단락의 "lo"는 localhost에 대한 정보이다. 이 결과값은
kernel에서 default로 setting하여주므로 별로 신경쓸 필요가 없다.
0.1.3. /etc/sysconfig/network
이 file은 호스트네임과 게이트웨이를 설정하는 file이다.
이 file을 cat해보자.
NETWORKING=yes
FORWARD_IPV4=false
HOSTNAME=superbug.i-top.co.kr
GATEWAY=172.31.0.200
위의 처음 두줄은 특별한 셋팅이 필요한 경우를 제외하고는 별로 설정값을
바꿀 일이 없으리라 생각한다.
레드헷 6.x이전 버젼까지는 이곳에 DOMAINNAME과 GATEWAYDEV항목이 들어있
었으나 6.x이후부터는 그 부분이 빠졌다. 이 항목들은 있어도 그만, 없어도
그만이므로 신경쓸 필요는 없다. DOMAINNAME항목은 HOSTNAME에 포함이 되었
고, GATEWAYDEV는 디폴트로 eth0를 셋팅하도록 되어있다.
< 1. 이중연결리스트를 이용한 File처리 루틴에 관하여... >
-> File처리루틴에 이중연결리스트를 이용한 목적:
File처리루틴은 이중연결리스트를 이용한 방법을 선택하였다.
연결리스트를 선택한 가장 중요한 이유는 확장성의 용이함때문이다.
가장 흔한 File처리방법은 DB(ISAM, Postgre sql, mysql기타등등)를
이용하는 방법이 있고, 혹은 File에서 한개의 문자씩 읽어들여 처리
하는 방법등 여러가지가 있다.
하지만 DB를 이용하는 방법은 대용량 데이터를 처리할 때, 그리고
데이터 포맷이 정형화되어있을 때 쓰는 방법이고, 한개의 문자씩 읽
어들여 처리하는 방법은 한두개정도의 File을 처리할 때나 유용한
방법이므로 범용적이지 못하다.
Linux의 Network 관련 File들은 각각의 정보가 모두 정형화되어있지도
않고 또한 자주 업그레이드되는 정보들이므로 범용적인 처리루틴이 필
요하다.
다른 좋은 방법들도 있겠지만, 그리고 문자처리방식을 확장시키는 방
법도 있겠지만, 손쉽게 File을 처리하기 위해, 메모리관리를 확실하게
하기위해 연결리스트를 사용했다. 여러분께서 더 좋은 방법이나 혹은
더 좋은 루틴을 가지고 계신분은 질타와 가르침을 주시기 바란다.
===============================================================
1.1. 이중연결리스트(Double LinkedList) 구현
이중연결리스트에 대한 source는 인터넷상에 널려있고,
학부과정에서도 지겹게 다루는 부분이므로 이 부분에 대
한 설명은 접어두기로 하고 file내용만 보여주도록 하겠다.
--- < source 1.1.1 Double LinkedList. Filename:procfile.h > ---
#include <stdio.h>
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#ifndef SUCCESS
#define SUCCESS 1
#define FAIL 0
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef _DOUBLE_LINKLIST_
#define _DOUBLE_LINKLIST_
typedef struct tagDOUBLELINK {
void *pzKey;
struct tagDOUBLELINK *next;
struct tagDOUBLELINK *prev;
} DList;
void initDL(void);
DList *searchDL(void *pzSearch);
DList *insertAsFirstDL(void *pzInsert);
DList *insertDL(void *pzInsert, DList *pIns); /* insert front node p */
DList *insertDLAsKey(void *pzInsert, void *pzSearch);
DList *insertDLAsSort(void *pzInsert);
int deleteDL(DList *pDel);
int deleteDLAsKey(void *pzSearch);
void deleteAllDL(void);
void deletePerfectDL(void);
void *printDL(DList *pPrint);
#endif
#endif
----------------------------------------------------------------
--- < source 1.1.2 Double LinkedList. Filename:procfile.c > ---
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include "proc_file.h"
DList *dblhead;
DList *dbltail;
void initDL(void)
{
dblhead = (DList *)malloc(sizeof(DList));
dbltail = (DList *)malloc(sizeof(DList));
dblhead->prev = dblhead;
dblhead->next = dbltail;
dbltail->prev = dblhead;
dbltail->next = dbltail;
}
DList *searchDL(void *pzSearch)
{
DList *s;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzSearch, sizeof(pzSearch)) == 0)
break;
s = s->next;
}
return s;
}
DList *insertAsFirstDL(void *pzInsert)
{
DList *p;
p = (DList *)malloc(sizeof(DList));
p->pzKey = pzInsert;
p->next = dblhead->next;
dblhead->next->prev = p;
dblhead->next = p;
p->prev = dblhead;
return p;
}
DList *insertAsLastDL(void *pzInsert)
{
insertDL(pzInsert, dbltail);
return (DList *)NULL;
}
DList *insertDL(void *pzInsert, DList *pIns) /* insert front node pIns */
{
DList *s;
if (pIns == dblhead)
return NULL;
s = (DList *)malloc(sizeof(DList));
s->pzKey = pzInsert;
pIns->prev->next = s;
s->prev = pIns->prev;
s->next = pIns;
pIns->prev = s;
return s;
}
DList *insertDLAsKey(void *pzInsert, void *pzSearch)
{ /* insert insk front findk */
DList *s;
DList *r = NULL;
s = searchDL(pzSearch);
if (s != dbltail)
{
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
}
return r;
}
DList *insertDLAsSort(void *pzInsert)
{
DList *s;
DList *r;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzInsert, sizeof(pzInsert)) >= 0)
break;
s = s->next;
}
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
return r;
}
int deleteDL(DList *pDel)
{
if (pDel == dblhead || pDel == dbltail)
return FAIL;
pDel->prev->next = pDel->next;
pDel->next->prev = pDel->prev;
free(pDel);
return SUCCESS;
}
int deleteDLAsKey(void *pzSearch)
{
DList *s;
s = searchDL(pzSearch);
if (s != dbltail)
{
s->prev->next = s->next;
s->next->prev = s->prev;
free(s);
return SUCCESS;
}
return FAIL;
}
void deleteAllDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
dblhead->next = dbltail;
dbltail->prev = dblhead;
}
void deletePerfectDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
free(dblhead);
free(dbltail);
}
void *printDL(DList *pPrint)
{
pPrint = dblhead->next;
while (pPrint != dbltail)
{
printf("%s", (char *)(pPrint->pzKey));
pPrint = pPrint->next;
}
return pPrint->pzKey;
}
------------------------------------------------------------------------
1.2. file처리 라이브러리 구현
자, 이젠 이중연결리스트를 이용하여 File처리를 해보도록 하자.
가장 기본적인 algorithm은 다음과 같다.
< 연결리스트를 이용한 file처리 algorithm 1.2.1 >
1) file을 읽기모드로 열고
2) 한 라인을 읽어들인다.
3) 읽어들인 라인이 Search 조건에 맞는지 검사한다.
3-1) 조건에 맞으면 4)로 이동.
3-2) 맞지 않으면 연결리스트의 node에 삽입하고 2)로 이동
4) 조건에 따라 연결리스트의 node에 처리한(삽입 혹은 삭제, 변경등)
문자열을 삽입한다.
5) 처리가 다 끝나면 file을 닫는다.
6) file을 쓰기모드로 연다.
7) 메모리에 적재하고 있는 연결리스트의 각 노드들을 순서대로 file에
print한다.
8) file을 닫는다.
이렇게만 써놓으면 잘 이해가 가지 않을 것이다.
그러므로 이제부터 source와 그에 따른 algorithm을 토대로 설명하겠다.
우선 algorithm 1.2.1의 2)과정에 해당하는 함수를 만들어보자.
-------- < source 1.2.1 readOneLineSearchFile > -------------------
int readOneLineSearchFile(char pszBuffer[], FILE *fp)
{
int i = 0;
char c = '\0';
do
{
c = getc(fp); /* file에서 한 문자를 읽어들인다. */
pszBuffer[i++] = c;
/* 실제 file처리함수에서 사용하게될 buffer에 읽 */
/* 어들인 문자를 삽입한다. */
if (c == EOF || c == '\n')
/* 문자가 file의 끝이거나 캐리지리턴값이면 */
break;
/* loop를 빠져나간다. 즉, 개행문자를 만나면 */
} while (1); /* 그때까지의 문자를 모두 버퍼에 저장하고 */
/* loop를 끝낸다. */
pszBuffer[i] = '\0'; /* 버퍼에 들어있는 개행문자나 EOF를 삭제한다. */
nLine++; /* file의 line수를 체크하기 위한 외부변수이다. */
/* 한 라인을 읽고나서 1씩 증가시킨다. */
return c; /* 개행문자'\n', 혹은 EOF가 return될 것이다. */
}
-------------------------------------------------------------------
다음으로 연결리스트의 내용을 file에 프린트하는 함수를 구현해보도록 하자.
연결리스트에 관한 함수들은 위에서 언급한 바와 같다.
간단하므로 이해하기도 쉬울 것이다.
--------------- < source 1.2.2 printToFileDL > -------------------
void *printToFileDL(DList *pPrint, FILE *fp)
{
pPrint = dblhead->next; /* node pPrint를 초기화한다. */
while (pPrint != dbltail) /* pPrint가 마지막이 될 때까지 반복해서 */
{
fprintf(fp, pPrint->pzKey); /* file에 프린트한다. */
pPrint = pPrint->next; /* node를 다음으로 이동 */
}
return pPrint->pzKey;
}
-------------------------------------------------------------------
아래 소개할 source 1.2.3이 바로 위에서 언급한 algorithm에 의한 루틴이다.
Code line 우측에 각각 해당하는 algorithm의 번호를 표시해놓겠다.
이 함수는 file(pszFileName)에서 특정 문자열(pszSearch)이 들어있는 라인
을 찾아 그 라인을 통째로 삽입할 문자열(pszInsert)로 바꾸는 함수이다.
만약 검색할 문자열이 발견되지 않으면 file은 아무 변화도 일어나지 않는다.
--------------- < source 1.2.3 insertStrToFile > -------------------
void insertStrToFile(char *pszInsert, char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
/* nMaxLine과 nMaxCol을 인자로 두는 이유는 메모리를 관리하기 */
/* 위해서이다. 즉 pszBuffer 이중포인터변수를 전역변수로 잡지 */
/* 않기 위해서인데, 만약 이 변수를 전역변수로 잡게되면 프로 */
/* 그램은 항상 nMaxLine*nMaxCol byte만큼의 메모리를 차지하게 */
/* 되므로 메모리의 막대한 낭비를 초래할 수 있는 문제점이 있다.*/
/* 이 루틴에서는 file의 최대 line수와 최대 coloum수를 인자로 */
/* 받아서 그 인자 만큼에 해당되는 메모리를 할당하여 준다. */
/* 이렇게 처리했을 때의 메모리의 사용량은, 이 루틴이 실행되었 */
/* 을 때만 nMaxLine*nMaxCol byte만큼 메모리에 할당이 되기때문 */
/* 에, 기본메모리가 큰 linux시스템에서는 별 무리 없이 사용할 */
/* 수 있다. */
nLine = 0;
fp = fopen(pszFileName, "r"); /* algor 1) file을 읽기모드로 연다. */
if (fp == NULL) /* file이 존재하지 않는다면 아무처리 없이 끝낸다. */
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL(); /* double linkedlist를 초기화한다. */
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
/* algor 2) file로부터 한라인(pszBuffer[i])씩 읽어들인다. */
{
/* algor 3-2) 읽어들인 라인(pszBuffer[i])이 검색할 문자열 */
/* (pszSearch)을 포함하고 있지 않다면 */
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
/* algor 3-2) node에 삽입한 후 for (i = 0; ~)로 이동해 다음라인 */
/* 을 읽어들인다.(algor 2) */
insertAsLastDL(pszBuffer[i]);
else /* algor 3-1) 검색할 문자열이 포함되어있다면 */
/* algor 4) 검색한 문자열이 포함된 line대신 삽입할 문자열을 연 */
/* 결리스트에 삽입한다. */
insertAsLastDL(pszInsert);
}
/* 만약 위 for 루틴에서 검색할 문자열이 발견되지 않는다면 연결리스트 */
/* 에는 file의 모든 라인을 가지고 있는 node들만이 있을 것이다. */
/* 이런 상태에서 바로 아래쪽의 printToFileDL함수를 호출하게 되면 */
/* file의 내용이 아무것도 변하지 않으면서 그 file자체에 내용을 복사 */
/* 하게 되는 것이다. */
fclose(fp); /* algor 5) file을 닫는다. */
fp = fopen(pszFileName, "w"); /* algor 6) file을 쓰기모드로 연다. */
printToFileDL(dblhead->next, fp); /* algor 7) 메모리에 적재하고 */
/* 있던 연결리스트의 모든 노드 */
/* 들을 순서대로 file에 print한다.*/
deletePerfectDL(); /* 연결리스트 초기화시 할당했던 메모리를 회수한다.*/
fclose(fp); /* algor 8) file을 닫는다. */
for (i = 0; i < nMaxLine; i++) /* 버퍼에 할당했던 메모리를 해제(회수)*/
free(pszBuffer[i]);
}
-------------------------------------------------------------------
이중연결리스트의 구조를 알고있다면 이해하기가 상당히 쉬울 것이다.
이 함수를 이해하지 못했다면 다음으로 넘어가지 못하므로 철저하게 이해하기
바란다. 이 함수의 쓰임새는 두가지가 있다.
첫번째, 특정문자열 바꾸기.
두번째, 특정문자열이 속한 라인 지우기.
여기서 두번째쓰임새의 내용을 보라. 특정문자열(pszSearch)이 속한 line을 지
우려면 어떻게 해야할까? 너무도 쉬운 내용이다.
인자 pszInsert에 "", 즉, 공백문자열을 넘겨주면 된다.
이 함수는 특정 file을 임시파일로 저장하는 과정 없이 바로 그 file을 갱신한
다. 임시파일로 저장하는 것과 메모리에 저장하는 것은 서로 장단점이 있는데
그에 대한 설명은 언급하지 않아도 잘 아시리라 믿는다.
strstr함수에 관한 내용은 man페이지를 참조하거나 library 메뉴얼을 참고하기
바란다.
다음으로 구현할 함수는 위의 함수와 거의 비슷하지만 약간 다르다. 위의 함수
에서는 검색문자열(pszSearch)이 없을 경우 아무일도 일어나지 않지만 이 함수
는 문자열이 검색되지 않으면 file의 맨 끝에 삽입할 문자열(pszInsert)을 끼
워넣게 된다.
검색할 문자열이 만약 검색된다면 바로 아무일을 하지 않고 루틴을 끝낸다.
대부분의 과정은 위에서 설명했으므로 새로운 주목해야할 부분만을 Comment
하겠다.
--------------- < source 1.2.4 insertStrToFileLast > -------------------
void insertStrToFileLast(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else /* 검색할 문자열(pszSearch)이 검색 된다면 루틴을 끝낸다. */
{
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
fclose(fp);
deletePerfectDL();
return;
}
}
/* 만약 문자열(pszSearch)이 검색되지 않았다면 이부분으로 넘어오게 */
/* 된다. 이 경우 바로 아래와 같이 삽입할 문자열을 연결리스트의 맨 */
/* 끝노드에 삽입을 시키고 file로 프린트한다. */
insertAsLastDL(pszInsert);
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
}
-------------------------------------------------------------------
이번에 설명할 함수는 file에서 특정문자열을 찾아 그 문자열을 원하는
문자열로 바꾸어주는 함수이다.
위에서 설명한 insertStrToFile함수와는 다른 역할을 한다.
즉, insertStrToFile함수는 line단위로 문자열을 삽입, 변경하는 반면
changeStrToOneLineToFile함수는 문자열단위로 변경하는 함수이다.
기본 algorithm은 < algorithm 1.2.1 >에 의거한다.
만약 위의 과정을 모두 이해했다면 이 함수도 별 어려움이 없을 것이다.
이 함수는 changeSearchStr이라는 함수를 call하는데, changeSearchStr
함수는 어떤 주어진 문자열(pszLine)에서 특정문자열(pszSearch)를 찾아
삽입할 문자열(pszIns)로 바꾸는 역할을 한다. 한 라인(pszLine)에 여러
개의 특정문자열(pszSearch)가 있을 수 있으므로 재귀호출(recursive
call)을 사용했다. 기본적인 C문법을 알고있다면 루틴 자체는 이해하기
쉬울 것이라 믿는다.
위와 마찬가지로 중복되는 Comment는 생략하겠다.
-------- < source 1.2.5 changeStrToOneLineToFile > -----------
void changeSearchStr(char *pszIns, char *pszSearch, char *pszLine,
char *pszSave, int nMaxCol)
/* 여기서 포인터 인자인 pszSave는 바뀐 라인을 저장할 공간이다. */
{
int i = 0;
char *pszPos;
int nPos;
/* 라인(pszLine)에서 검색문자열(pszSearch)의 위치를 계산하기 */
/* 위해 검색문자열의 pszLine에서의 포인터 위치를 저장해둔다. */
pszPos = (char *)strstr(pszLine, pszSearch);
/* 검색문자열의 위치를 계산한다. */
nPos = pszPos-pszLine;
/* 검색문자열의 위치(nPos)까지 pszSave에 pszLine의 내용을 복사 */
for (i = 0; i < nPos; i++)
pszSave[i] = pszLine[i];
/* 저장할 변수(pszSave)에 바꿀 문자열(pszIns)를 붙힌다.*/
strcat(pszSave, pszIns);
/* 저장할 변수(pszSave)에 그 이후의 문자열을 붙힌다. */
strcat(pszSave, pszLine+nPos+strlen(pszSearch));
/* 더이상 검색문자열(pszSearch)가 */
pszPos = (char *)strstr(pszSave, pszSearch);
/* 존재하지 않는다면 루틴을 끝낸다. */
if (pszPos == NULL)
return;
memset(pszLine, '\0', nMaxCol);
/* 바뀐 라인(pszSave)를 원래 라인에 복사한다. */
strcpy(pszLine, pszSave);
/* 재귀호출을 위해 pszSave를 초기화한다. */
memset(pszSave, '\0', nMaxCol);
/* 첫번째 만나는 검색문자열을 바꾸었으므로 두번째, 세번째등등 */
/* 의 문자열을 바꾸기 위해 재귀호출을 한다. */
changeSearchStr(pszIns, pszSearch, pszLine, pszSave, nMaxCol);
}
/* 이제 한 라인에 대한 문자열변경루틴은 끝냈으므로 위 함수에 실제 */
/* file에서 한 라인씩 읽어들여 인자로 넘겨주면 된다. */
void changeStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0, j = 0;
FILE *fp;
/* pszLine은 바뀐 문자열을 저장할 공간이다. */
char *pszLine[nMaxLine];
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
pszLine[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszLine[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
/* 한 라인씩 읽어들여서 */
{
/* 검색문자열(pszSearch)이 없다면 연결리스트에 삽입한다.*/
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
/* 검색문자열이 있다면 changeSearchStr함수를 이용해 검색문자열이 */
/* 포함된 라인을 수정하고 수정된 라인을 연결리스트에 삽입한다. */
else
{
changeSearchStr(pszIns, pszSearch, pszBuffer[i], pszLine[j],
nMaxCol);
insertAsLastDL(pszLine[j]);
j++;
}
}
fclose(fp);
/* file 쓰기 */
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
{
free(pszBuffer[i]);
free(pszLine[i]);
}
}
-------------------------------------------------------------------
예상외로 간단할 것이다. 특정 기능(예를들어, 삭제, 삽입, 변경등)에
대한 함수들의 구현은 위 방법과 구조가 항상 같다.
이는 연결리스트를 사용했을때의 장점이 유감없이 발휘된 특징중 하나
이다. 즉, 이해하기 쉽고 구현이 용이하고, 또 가장 중요한 범용성이
있다는 점이다.
이번엔 위의 source들의 구조를 약간 응용해서 구조화된 file의 처리
루틴을 작성해보기로 하자.
아래의 함수는 검색문자열(pszSearch)이 file(pszFileName)에 없다면
삽입할 문자열(pszInsert)을 라인으로 삽입하고 검색문자열이 존재한
다면 그 검색문자열을 포함한 라인을 삽입할 문자열로 바꾸는 함수이
다. 위의 함수들과 다른점이 있다면 이 함수에는 flag를 두었다는 점
이다. 이 flag는 file에 검색된 라인을 삽입문자열로 바꿀 것인지 아
니면 그냥 삽입문자열을 라인단위로 삽입할 것인지를 결정한다.
만약 문자열이 검색되지 않는다면 flag는 TRUE로 setting된다.
-------- < source 1.2.6 addOnNotExistChangeOnExist > -----------
void addOnNotExistChangeOnExist(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
/* 삽입할 지 혹은 변경할 지의 flag(flgAdd) */
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
/* 만약 검색문자열(pszSearch)이 검색된다면 */
else
{
/* 삽입문자열(pszIns)을 node에 삽입하고 */
insertAsLastDL(pszIns);
/* flag를 FALSE로 reset시킨다. */
flgAdd = FALSE;
}
}
fclose(fp);
/* flag가 TRUE라면 검색문자열이 발견되지 않았다는 것이므로 */
/* 링크리스트의 맨 끝node에 삽입문자열을 삽입한다. */
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
/* file 쓰기 */
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
-------------------------------------------------------------------
간단하게 몇줄만 추가 혹은 변경시켜 기능을 바꾸어놓았다.
앞으로 설명하게될 모든 file처리관련 함수들이 다 이런식이다.
이 routine을 응용하여 갖가지 source들을 재생성해보시기를...
이제부터는 위의 routine들은 물론 필자가 구현해놓은 모든 procedure들
을 한꺼번에 보여주기로 하겠다. 그리고 설명은 되도록 기능 위주로 하
겠다. 위 과정까지 모두 이해했다면 별다른 Comment없이도 이해할 수
있을 것이다.
-------- < source 1.2.7 total functions > -----------
/**************** proc_file.h *****************/
#include <stdio.h>
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#ifndef SUCCESS
#define SUCCESS 1
#define FAIL 0
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef _DOUBLE_LINKLIST_
#define _DOUBLE_LINKLIST_
typedef struct tagDOUBLELINK {
void *pzKey;
struct tagDOUBLELINK *next;
struct tagDOUBLELINK *prev;
} DList;
void initDL(void);
DList *searchDL(void *pzSearch);
DList *insertAsFirstDL(void *pzInsert);
DList *insertDL(void *pzInsert, DList *pIns); /* insert front node p */
DList *insertDLAsKey(void *pzInsert, void *pzSearch);
DList *insertDLAsSort(void *pzInsert);
int deleteDL(DList *pDel);
int deleteDLAsKey(void *pzSearch);
void deleteAllDL(void);
void deletePerfectDL(void);
void *printDL(DList *pPrint);
void deleteStrFromOneLine(char *pszSave, char *pszDel, char *pszLine);
void insertStrForeSearch(char *pszSave, char *pszInsert,
char *pszSearch, char *pszLine, char *pszTempLine);
void insertStrToOneLine(char *pszSave, char *pszIns, char *pszLine);
void changeSearchStr(char *pszIns, char *pszSearch, char *pszLine,
char *pszSave, int nMaxCol);
void *printToFileDL(DList *pPrint, FILE *fp);
int readOneLineSearchFile(char pszBuffer[], FILE *fp);
void insertStrToFile(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertStrToFileLast(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
int countAllLineToFile(char *pszFileName, int nMaxLine, int nMaxCol);
int countSearchLineToFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol);
int saveAllLineToFile(char *pszSaveListStr[], char *pszFileName,
int nMaxLine, int nMaxCol);
int saveSearchLineToFile(char *pszSearch, char *pszSaveListStr[],
char *pszFileName, int nMaxLine, int nMaxCol);
int saveSearchLineToFileWithoutComment(char *pszSearch,
char *pszSaveListStr[], char cComment,
char *pszFileName, int nMaxLine, int nMaxCol);
void changeStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void deleteStrToOneLineToFile(char *pszDel, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
int searchStrInFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol);
void addOnNotExistChangeOnExist(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void addOnNotExistChangeOnExistWithoutComment(char *pszIns,
char *pszSearch, char cComment, char *pszFileName,
int nMaxLine, int nMaxCol);
void addOnNotExistChangeOnExist2Search(char *pszIns,
char *pszFirstSearch, char *pszSecondSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertForeSearchToFile(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void attatchStrToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol);
void saveStrIdxToFileLine(char *pszSave, char *pszSearch,
char *pszFileName, int nStartIdx, int nEndIdx,
int nMaxLine, int nMaxCol);
void changeStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nStartIdx, int nEndIdx,
int nMaxLine, int nMaxCol);
void attatchStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nIdx, int nMaxLine, int nMaxCol);
void attatchStrToFileIdxLine(char *pszInsert, char *pszFileName,
int nLineIdx, int nColIdx, int nMaxLine, int nMaxCol);
void saveBetweenStrToFile(char *pszSaveLine[], char *pszStartLine,
char *pszEndLine, char *pszFileName,
int nMaxLine, int nMaxCol);
void changeBetweenStrToFile(char *pszInsert[], int nInsertNum,
char *pszStartLine, char *pszEndLine,
char *pszFileName, int nMaxLine, int nMaxCol);
void insertAfterStrToFile(char *pszInsert, char *pszStartLine,
char *pszFileName, int nMaxLine, int nMaxCol);
#endif
#endif
/**********************************************/
/**************** proc_file.c *****************/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include "proc_file.h"
DList *dblhead;
DList *dbltail;
void initDL(void)
{
dblhead = (DList *)malloc(sizeof(DList));
dbltail = (DList *)malloc(sizeof(DList));
dblhead->prev = dblhead;
dblhead->next = dbltail;
dbltail->prev = dblhead;
dbltail->next = dbltail;
}
DList *searchDL(void *pzSearch)
{
DList *s;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzSearch, sizeof(pzSearch)) == 0)
break;
s = s->next;
}
return s;
}
DList *insertAsFirstDL(void *pzInsert)
{
DList *p;
p = (DList *)malloc(sizeof(DList));
p->pzKey = pzInsert;
p->next = dblhead->next;
dblhead->next->prev = p;
dblhead->next = p;
p->prev = dblhead;
return p;
}
DList *insertAsLastDL(void *pzInsert)
{
insertDL(pzInsert, dbltail);
return (DList *)NULL;
}
DList *insertDL(void *pzInsert, DList *pIns) /* insert front node pIns */
{
DList *s;
if (pIns == dblhead)
return NULL;
s = (DList *)malloc(sizeof(DList));
s->pzKey = pzInsert;
pIns->prev->next = s;
s->prev = pIns->prev;
s->next = pIns;
pIns->prev = s;
return s;
}
DList *insertDLAsKey(void *pzInsert, void *pzSearch)
{ /* insert insk front findk */
DList *s;
DList *r = NULL;
s = searchDL(pzSearch);
if (s != dbltail)
{
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
}
return r;
}
DList *insertDLAsSort(void *pzInsert)
{
DList *s;
DList *r;
s = dblhead->next;
while (s != dbltail)
{
if (memcmp(s->pzKey, pzInsert, sizeof(pzInsert)) >= 0)
break;
s = s->next;
}
r = (DList *)malloc(sizeof(DList));
r->pzKey = pzInsert;
s->prev->next = r;
r->prev = s->prev;
r->next = s;
s->prev = r;
return r;
}
int deleteDL(DList *pDel)
{
if (pDel == dblhead || pDel == dbltail)
return FAIL;
pDel->prev->next = pDel->next;
pDel->next->prev = pDel->prev;
free(pDel);
return SUCCESS;
}
int deleteDLAsKey(void *pzSearch)
{
DList *s;
s = searchDL(pzSearch);
if (s != dbltail)
{
s->prev->next = s->next;
s->next->prev = s->prev;
free(s);
return SUCCESS;
}
return FAIL;
}
void deleteAllDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
dblhead->next = dbltail;
dbltail->prev = dblhead;
}
void deletePerfectDL(void)
{
DList *s;
DList *p;
p = dblhead->next;
while (p != dbltail)
{
s = p;
p = p->next;
free(s);
}
free(dblhead);
free(dbltail);
}
void *printDL(DList *pPrint)
{
pPrint = dblhead->next;
while (pPrint != dbltail)
{
printf("%s", (char *)(pPrint->pzKey));
pPrint = pPrint->next;
}
return pPrint->pzKey;
}
/****** FILE procedure ******/
/* 한 라인에서 검색문자열(pszDel)을 지운다. */
void deleteStrFromOneLine(char *pszSave, char *pszDel, char *pszLine)
{
char *pszPnt;
int i, nFirstLen;
if ((pszPnt = (char *)strstr(pszLine, pszDel)) == NULL)
return;
nFirstLen = pszPnt-pszLine;
for (i = 0; i < nFirstLen; i++)
pszSave[i] = pszLine[i];
for (i = nFirstLen; pszLine[i+strlen(pszDel)] != '\0'; i++)
pszSave[i] = pszLine[i+strlen(pszDel)];
pszSave[i] = '\0';
}
/* 한 라인에서 검색문자열(pszSearch)앞에 삽입문자열(pszInsert) */
/* 을 삽입한다. */
void insertStrForeSearch(char *pszSave, char *pszInsert,
char *pszSearch, char *pszLine, char *pszTempLine)
{
int i = 0;
int nLen;
char *pszPos;
if ((char *)strstr(pszLine, pszInsert) != NULL)
{
strcat(pszSave, pszTempLine);
return;
}
pszPos = (char *)strstr(pszLine, pszSearch);
nLen = pszPos-pszLine;
for (i = 0; i < nLen; i++)
pszSave[i] = pszLine[i];
strcat(pszSave, pszInsert);
strcat(pszSave, strstr(pszLine, pszSearch));
}
/* 한 라인에서 검색문자열(pszIns)이 있다면 return하고 없다면 */
/* 라인 끝에 검색문자열을 삽입한다. */
void insertStrToOneLine(char *pszSave, char *pszIns, char *pszLine)
{
int nFirstPos, nSecondPos;
if ((char *)strstr(pszLine, pszIns) != NULL)
return;
for (nFirstPos = 0; nFirstPos < strlen(pszLine); nFirstPos++)
pszSave[nFirstPos] = pszLine[nFirstPos];
for (nSecondPos = 0; nSecondPos < strlen(pszIns); nSecondPos++)
pszSave[nSecondPos+nFirstPos] = pszIns[nSecondPos];
pszSave[nSecondPos+nFirstPos] = '\0';
}
void changeSearchStr(char *pszIns, char *pszSearch, char *pszLine,
char *pszSave, int nMaxCol)
{
int i = 0;
char *pszPos;
int nPos;
pszPos = (char *)strstr(pszLine, pszSearch);
nPos = pszPos-pszLine;
for (i = 0; i < nPos; i++)
pszSave[i] = pszLine[i];
strcat(pszSave, pszIns);
strcat(pszSave, pszLine+nPos+strlen(pszSearch));
pszPos = (char *)strstr(pszSave, pszSearch);
if (pszPos == NULL)
return;
memset(pszLine, '\0', nMaxCol);
strcpy(pszLine, pszSave);
memset(pszSave, '\0', nMaxCol);
changeSearchStr(pszIns, pszSearch, pszLine, pszSave, nMaxCol);
}
int nLine;
void *printToFileDL(DList *pPrint, FILE *fp)
{
pPrint = dblhead->next;
while (pPrint != dbltail)
{
fprintf(fp, pPrint->pzKey);
pPrint = pPrint->next;
}
return pPrint->pzKey;
}
int readOneLineSearchFile(char pszBuffer[], FILE *fp)
{
int i = 0;
char c = '\0';
do
{
c = getc(fp);
pszBuffer[i++] = c;
if (c == EOF || c == '\n')
break;
} while (1);
pszBuffer[i] = '\0';
nLine++;
return c;
}
void insertStrToFileLast(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
fclose(fp);
deletePerfectDL();
return;
}
}
insertAsLastDL(pszInsert);
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
}
/* file의 라인수를 return한다. */
int countAllLineToFile(char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, nCount = 0;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return 0;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (nCount = 0;
(readOneLineSearchFile(pszBuffer[nCount], fp) != EOF) &&
(nCount < nMaxLine); nCount++);
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return nCount;
}
/* file에서 검색문자열(pszSearch)가 있는 line 수를 return한다. */
int countSearchLineToFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0;
int nCount = 0;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return 0;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
nCount++;
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return nCount;
}
/* file의 모든 라인을 이중포인터(pszSaveListStr)에 저장한다. */
int saveAllLineToFile(char *pszSaveListStr[], char *pszFileName,
int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return 0;
for (j = 0; j < nMaxLine; j++)
{
pszBuffer[j] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[j], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
strcpy(pszSaveListStr[i], pszBuffer[i]);
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
return i;
}
/* file에서 검색문자열(pszSearch)이 있는 line에 Comment(cComment)가 */
/* 없다면 이중포인터(pszSaveListStr)에 저장한다. */
int saveSearchLineToFileWithoutComment(char *pszSearch,
char *pszSaveListStr[], char cComment,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
int j = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
if ((fp = fopen(pszFileName, "r")) == NULL)
return FALSE;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL &&
pszBuffer[i][0] != cComment)
{
strcpy(pszSaveListStr[j], pszBuffer[i]);
j++;
}
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return TRUE;
}
/* file에서 검색문자열(pszSearch)이 있는 line을 Comment(cComment)에 상 */
/* 관없이 이중포인터(pszSaveListStr)에 저장한다. */
int saveSearchLineToFile(char *pszSearch, char *pszSaveListStr[],
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
int j = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
if ((fp = fopen(pszFileName, "r")) == NULL)
return FALSE;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
strcpy(pszSaveListStr[j], pszBuffer[i]);
j++;
}
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return TRUE;
}
void changeStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0, j = 0;
FILE *fp;
char *pszLine[nMaxLine];
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
pszLine[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszLine[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
changeSearchStr(pszIns, pszSearch, pszBuffer[i],
pszLine[j], nMaxCol);
insertAsLastDL(pszLine[j]);
j++;
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
{
free(pszBuffer[i]);
free(pszLine[i]);
}
}
/* 검색문자열(pszSearch)이 있는 라인에서 검색문자열 바로 */
/* 앞에 삽입문자열(pszIns)을 삽입한다. */
void insertStrToOneLineToFile(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char pszLine[nMaxCol];
char *pszBuffer[nMaxLine];
memset(pszLine, '\0', nMaxCol);
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
insertStrForeSearch(pszLine, pszIns, pszSearch, pszBuffer[i],
pszBuffer[i]);
insertAsLastDL(pszLine);
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* file에서 검색문자열(pszSearch)을 찾아 그 문자열만 지워준다. */
void deleteStrToOneLineToFile(char *pszDel, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char pszLine[nMaxCol];
char *pszBuffer[nMaxLine];
memset(pszLine, '\0', nMaxCol);
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
deleteStrFromOneLine(pszLine, pszDel, pszBuffer[i]);
insertAsLastDL(pszLine);
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
void insertStrToFile(char *pszInsert, char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
insertAsLastDL(pszInsert);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* file에서 검색문자열(pszSearch)이 있으면 TRUE, */
/* 없으면 FALSE를 return한다. */
int searchStrInFile(char *pszSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return FALSE;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
fclose(fp);
return TRUE;
}
}
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return FALSE;
}
void addOnNotExistChangeOnExist(char *pszIns, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL)
insertAsLastDL(pszBuffer[i]);
else
{
insertAsLastDL(pszIns);
flgAdd = FALSE;
}
}
fclose(fp);
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* Comment(cComment)가 없을 때, 검색문자열(pszSearch)이 */
/* 없으면 file 맨 끝에 삽입문자열(pszIns)를 라인으로 삽입 */
/* 하고, 있으면 검색문자열이 있는 라인을 삽입문자열로 바 */
/* 꾼다. */
void addOnNotExistChangeOnExistWithoutComment(char *pszIns,
char *pszSearch, char cComment,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) == NULL ||
pszBuffer[i][0] == cComment)
insertAsLastDL(pszBuffer[i]);
else
{
insertAsLastDL(pszIns);
flgAdd = FALSE;
}
}
fclose(fp);
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 두개의 검색문자열(pszFirstSearch, pszSecondSearch)이 모두 있으면 */
/* 그 라인을 삽입문자열(pszIns)로 바꾸고, 둘 중 하나라도 없으면 */
/* 삽입문자열을 file맨 끝에 라인으로 삽입한다. */
void addOnNotExistChangeOnExist2Search(char *pszIns, char *pszFirstSearch,
char *pszSecondSearch, char *pszFileName,
int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
int flgAdd = TRUE;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszFirstSearch) != NULL &&
(char *)strstr(pszBuffer[i], pszSecondSearch) != NULL)
{
insertAsLastDL(pszIns);
flgAdd = FALSE;
}
else
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
if (flgAdd == TRUE)
insertAsLastDL(pszIns);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)를 찾아 그 라인의 바로 앞에 삽입 */
/* 문자열을 라인으로 삽입한다. */
void insertForeSearchToFile(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
insertAsLastDL(pszBuffer[i]);
fclose(fp);
insertDLAsKey(pszInsert, pszSearch);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)을 찾아 그 라인의 끝위치에 삽입문자열 */
/* (pszInsert)을 삽입한다. */
void attatchStrToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
pszBuffer[i][strlen(pszBuffer[i])-1] = '\0';
strcat(pszBuffer[i], pszInsert);
pszBuffer[i][strlen(pszBuffer[i])] = '\n';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)를 찾아 그라인의 특정Coloum(nStartIdx)에서 */
/* 특정Coloum(nEndIdx)까지의 문자열을 Save(pszSave)한다. */
void saveStrIdxToFileLine(char *pszSave, char *pszSearch,
char *pszFileName, int nStartIdx, int nEndIdx,
int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
char pszEndBuffer[nMaxCol];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
memset(pszEndBuffer, '\0', nMaxCol);
strcpy(pszEndBuffer, pszBuffer[i]+nStartIdx);
for (j = 0; j < nEndIdx-nStartIdx; j++)
pszSave[j] = pszEndBuffer[j];
}
}
fclose(fp);
}
/* 검색문자열(pszSearch)를 찾아 그라인의 특정Coloum(nStartIdx)에서 */
/* 특정Coloum(nEndIdx)까지의 문자열을 삽입문자열(pszInsert)로 교체한다. */
void changeStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nStartIdx,
int nEndIdx, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
char pszEndBuffer[nMaxCol];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
memset(pszEndBuffer, '\0', nMaxCol);
strcpy(pszEndBuffer, pszBuffer[i]+nEndIdx);
pszBuffer[i][nStartIdx] = '\0';
strcat(pszBuffer[i], pszInsert);
strcat(pszBuffer[i], pszEndBuffer);
pszBuffer[i][strlen(pszBuffer[i])] = '\0';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszSearch)를 찾아 그라인의 특정Coloum(nIdx) 이후의 문자열 */
/* 을 모두 없애고, 삽입문자열(pszInsert)을 붙힌다. */
void attatchStrIdxToFileLine(char *pszInsert, char *pszSearch,
char *pszFileName, int nIdx, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszSearch) != NULL)
{
pszBuffer[i][nIdx] = '\0';
strcat(pszBuffer[i], pszInsert);
pszBuffer[i][strlen(pszBuffer[i])] = '\n';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 특정라인(nLineIdx)의 특정Coloum(nColIdx) 이후의 문자열 */
/* 을 모두 없애고, 삽입문자열(pszInsert)을 붙힌다. */
void attatchStrToFileIdxLine(char *pszInsert, char *pszFileName,
int nLineIdx, int nColIdx, int nMaxLine, int nMaxCol)
{
int i = 0, j;
FILE *fp;
char *pszBuffer[nMaxLine];
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if (i == nLineIdx-1)
{
pszBuffer[i][nColIdx] = '\0';
strcat(pszBuffer[i], pszInsert);
pszBuffer[i][strlen(pszBuffer[i])] = '\n';
}
insertAsLastDL(pszBuffer[i]);
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
}
/* 검색문자열(pszStartLine)을 찾아 그 라인의 바로 다음라인에 */
/* 삽입문자열(pszInsert)을 라인으로 삽입한다. */
void insertAfterStrToFile(char *pszInsert, char *pszStartLine,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j, k;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
insertAsLastDL(pszBuffer[i]);
if ((char *)strstr(pszBuffer[i], pszStartLine) != NULL)
{
insertAsLastDL(pszInsert);
insertAsLastDL("\n");
for (j = i+1;
readOneLineSearchFile(pszBuffer[j], fp) != EOF; j++)
insertAsLastDL(pszBuffer[j]);
break;
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return;
}
/* 첫번째 검색문자열(pszStartLine)과 두번째 검색문자열(pszEndLine) */
/* 을 찾아 그 사이에 있는 line들을 저장(pszSaveLine)한다. */
void saveBetweenStrToFile(char *pszSaveLine[], char *pszStartLine,
char *pszEndLine, char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
if ((char *)strstr(pszBuffer[i], pszStartLine) != NULL)
{
for (j = i+1;
readOneLineSearchFile(pszBuffer[j], fp) != EOF; j++)
{
if ((char *)strstr(pszBuffer[j], pszEndLine) == NULL)
strcpy(pszSaveLine[j-(i+1)], pszBuffer[j]);
else
break;
}
fclose(fp);
for (j = 0; j < nMaxLine; j++)
free(pszBuffer[j]);
return;
}
}
}
/* 첫번째 검색문자열(pszStartLine)과 두번째 검색문자열(pszEndLine) */
/* 을 찾아 그 사이에 있는 line들을 삽입문자열(pszInsert)로 대체 */
/* 시킨다. */
void changeBetweenStrToFile(char *pszInsert[], int nInsertNum,
char *pszStartLine, char *pszEndLine,
char *pszFileName, int nMaxLine, int nMaxCol)
{
char *pszBuffer[nMaxLine];
int i = 0, j, k;
FILE *fp;
nLine = 0;
fp = fopen(pszFileName, "r");
if (fp == NULL)
return;
for (i = 0; i < nMaxLine; i++)
{
pszBuffer[i] = (char *)malloc(sizeof(char)*nMaxCol);
memset(pszBuffer[i], '\0', nMaxCol);
}
initDL();
for (i = 0; readOneLineSearchFile(pszBuffer[i], fp) != EOF; i++)
{
insertAsLastDL(pszBuffer[i]);
if ((char *)strstr(pszBuffer[i], pszStartLine) != NULL)
{
for (j = i+1;
readOneLineSearchFile(pszBuffer[j], fp) != EOF; j++)
if ((char *)strstr(pszBuffer[j], pszEndLine) != NULL)
break;
for (k = 0; k < nInsertNum; k++)
insertAsLastDL(pszInsert[k]);
insertAsLastDL(pszBuffer[j]);
for (k = j+1;
readOneLineSearchFile(pszBuffer[k], fp) != EOF; k++)
insertAsLastDL(pszBuffer[k]);
break;
}
}
fclose(fp);
fp = fopen(pszFileName, "w");
printToFileDL(dblhead->next, fp);
deletePerfectDL();
fclose(fp);
for (i = 0; i < nMaxLine; i++)
free(pszBuffer[i]);
return;
}
-------------------------------------------------------
지금까지 링크리스트를 이용한 file처리에 관한 내용을 다루어보았다.
그런데, 왜 굳이 Simple LinkedList를 쓰지 않고, Double LinkedList
를 사용했는지 궁금할 것이다.
나는 후에라도 이 Source를 더욱 강화시키기위해 확장성이 좋은 이중
연결리스트를 사용한 것이다. 물론 단순연결리스트를 사용한다면 메
모리를 약간 절약할 수는 있겠지만 확장성에서는 훨씬 떨어진다.
따라서, Library를 후에 강화시킬 일이 있다면 이중연결리스트를 써
야한다고 생각한다.
1.3. 개선되어야할 내용
앞서 말했듯이 위의 Source는 특정 Routine이 수행될 때 순간적으로
Resource를 크게 차지할 수도 있는 단점이 있다.
이를 보완하려면 LinkList의 node에 key값을 삽입할 때 모든 file의
내용을 전부 LinkList에 삽입할 것이 아니라 조건에 맞을 때만 file
의 내용을 변경하도록 하는 것이 좋다.
이러한 개선점을 생각해 위의 Source들을 한번 수정해보시기를 바란
다.
===============================================================
< 2. QT 기본 다지기 >
-> 필자는 Ansi-C를 사용하는 원시 프로그래머였다.
주로 다뤘던 분야는 Network 관련 프로그램이었고 개발 환경또한
X-window가 아닌 text-mode에서였다. text-mode에서의 GUI구축에
온 힘을 쏟았고 그 결과로 Curses나 Slang보다도 더 좋다고 내세
울 수 있는 Library를 개발했다. 나는 내 자신이 상당한 수준에
올라와 있다고 착각하기 시작했고 점차 자만에 빠지게 되었다.
하지만 점차 Linux가 업그레이드 되면서 굉장한 수준의 그래픽환
경이 제공되면서 그 자만은 깨지기 시작했다. 그 충격이란 엄청
난 것이었다. 나와 비슷한 분들이 한분이라도 이 글을 읽는 분들
중에 계시다면 이 자료를 올린 보람이 있을 것이다.
기타등등의 이유로 C++에 대한 클래스 개념조차도 거의 모른 상태
에서 약 3개월 전부터 QT를 시작했다.
어떠한 자료도 없이 Alzza 리눅스에 포함되어있는 QT 1.44 의 예제
Source만으로 C++과 QT를 공부하자니 무척이나 힘이 들었다.
이렇게 힘들게 공부하면서 QT에 빨리 접근할 수 있는 방법을 저절
로 터득하게 되었는데 이 방법을 여러분께 가장 좋다고 생각하는
과정에 의거하여 글을 쓰겠다. 짧은 실력으로 글을 올리니 여러분
께 죄책감이 먼저 앞서지만 Source공유라는 차원에서 귀엽게 보아
주시기 바란다.
2.1. QT의 Class계층정보
우선 본격적인 QT 제공 Class들을 공부하기에 앞서 Class계층에
대해 눈으로 익혀두기 바란다. 이 계층정보는 상당히 중요하지만
외울 필요는 없다. 그냥 필요할 때 찾아보는 정도로 활용하기 바
란다. 아래의 표는 /usr/lib/qt/html/hierarchy.html 에서 발췌한
내용중 앞으로 쓰게될지도 모를 주요 클래스만을 정리한 것이다.
-------- < 표 2.1.1 QT Class 계층정보 > ---------
QBrush
QCollection
QGList
QList
QStrList
QStrIList
QColor
QColorGroup
QConnection
QCursor
QEvent
QChildEvent
QCloseEvent
QCustomEvent
QDragMoveEvent
QDropEvent
QFocusEvent
QKeyEvent
QMouseEvent
QMoveEvent
QPaintEvent
QResizeEvent
QTimerEvent
QFont
QFontInfo
QFontMetrics
QGArray
QArray
QByteArray
QString
QPointArray
QIconSet
QImage
QListBoxItem
QListBoxPixmap
QListBoxText
QListViewItem
QCheckListItem
QMenuData
QMenuBar
QPopupMenu
QMovie
QObject
QAccel
QApplication
QXtApplication
QDragObject
QImageDrag
QStoredDrag
QTextDrag
QUrlDrag
QLayout
QBoxLayout
QHBoxLayout
QVBoxLayout
QGridLayout
QSignal
QToolTipGroup
QValidator
QDoubleValidator
QIntValidator
QWidget
QButton
QCheckBox
QPushButton
QRadioButton
QToolButton
QComboBox
QDialog
QFileDialog
QMessageBox
QPrintDialog
QTabDialog
QFrame
QGroupBox
QButtonGroup
QLCDNumber
QLabel
QMenuBar
QProgressBar
QScrollView
QListView
QSpinBox
QSplitter
QTableView
QHeader
QListBox
QMultiLineEdit
QPopupMenu
QWidgetStack
QLineEdit
QMainWindow
QNPWidget
QScrollBar
QSemiModal
QProgressDialog
QSlider
QStatusBar
QTabBar
QToolBar
QWindow
QXtWidget
QPaintDevice
QPicture
QPixmap
QBitmap
QPrinter
QWidget
QPaintDeviceMetrics
QPainter
QPalette
QPen
QPixmapCache
QPoint
QRangeControl
QScrollBar
QSlider
QSpinBox
QRect
QSize
QTextStream
QTime
QToolTip
QWhatsThis
QWMatrix
-----------------------------------------
2.2. Widget(QWidget)이란?
-> 모든 사용자 인터페이스(User Interface)의 기본 Class이다.
위의 표와 같이 버튼, 프레임, 편집박스등 Graphic Interface를
주도하는 Class들중 최 상위에 위치해있다.
모든 Widget들에 대한 공부는 철저하게 예제위주로 하겠다.
2.2.1. 위젯(QWidget), 프레임(QFrame), 버튼(QButton)
---------- < source 2.2.1.1. testFrameButton.cpp > -----------
#include <qapplication.h> /* QApplication을 위한 header file */
#include <qwidget.h> /* QWidget을 위한 header file */
int main(int argc, char* argv[])
{
/* 이 부분은 QT 프로그램의 main함수에서 항상 써주어야 */
/* 하는 부분이다. */
QApplication myapp(argc, argv);
/* 인자가 없음에 주의 */
QWidget *wdgtTest = new QWidget();
/* setGeometry(x좌표, y좌표, 넓이, 높이) */
wdgtTest->setGeometry(100, 100, 200, 100);
/* application의 메인 윈도우를 wdgtTest로 정의 */
myapp.setMainWidget(wdgtTest);
/* wdgtTest를 보여준다. */
wdgtTest->show();
/* qt를 실행시킨다. 만약 이 부분이 없다면 윈도우가 뜨지 */
/* 않을 것이다. 항상 들어가야할 부분이다. */
return myapp.exec();
}
-----------------------------------------------------------
위의 Source에서 관심있게 보아야할 부분은 QWidget, QFrame,
QPushButton Class의 인자들이다.
우선 header file을 살펴보자. header file에 관한 정보는
/usr/lib/qt/include/ 에서 볼 수 있다.
public:
QWidget( QWidget *parent=0, const char *name=0, WFlags f=0 );
private: // Disabled copy constructor and operator=
#if defined(Q_DISABLE_COPY)
QWidget( const QWidget & );
QWidget &operator=( const QWidget & );
#endif
위 부분은 header file(/usr/lib/qt/include/qwidget.h)에 들어있
는 QWidget의 생성자 부분이다.
여기서 유심히 보아야할 부분은 인자들의 정의 부분이다. 즉,
QWidget *parent = 0, const char *name = 0, WFlags f = 0
이렇게 클래스정의부분에서 인자값까지도 정의할 수 있다는 것은
C++을 한번쯤 다뤄본 분이라면 모두 아실 것이다.
C에서 NULL값은 #define문으로 0으로 선언되어있다. 즉 parent,
name, f 모두 NULL로 정의를 했다는 뜻인데 그냥 초기화시켰다
는 의미로 받아들이자. 물론 이 부분은 class 생성자(constructor)
부분에서 직접 정의할 수도 있다.
이는 C++의 특권이라고 할 수 있겠다. 이런 방법은 여러곳에서 상
당히 유용하게 쓸 수 있는 기능이다. 자세한 예는 차차 이야기하
도록 하겠다.
이젠 인자들에 대해 살펴보자.
-> QWidget *parent: parent widget을 뜻한다.
위의 header file에 나타난 class선언에서도 보았
듯이 디폴트로 NULL(0)으로 설정되어있는데 이는
자신을 최상위 윈도우로 정하겠다는 뜻이다.
즉 parent가 정의되지 않은(인자가 없는) widget은
부모class가 없이 독립적인 윈도우 체제를 가진다.
parent인자에 만약 다른 widget값을 넘겨준다면
parent인자에 종속되게 된다. 자세한 내용은
source 2.2.1.2에서 다시 다루겠다.
-> const char *name:이것은 widget에 대한 식별자 역할을 한다.
이 식별자는 프로그래머가 거의 사용할 일이 없다.
library에서 자체적으로 compile시 debugging작업
을 할 때, 혹은 사용자가 debugging tool을 사용할
때 특정 부분에 대한 정보를 보고 싶을 때 name()
함수를 사용하여 정보를 추출할 수 있다.
필자는 거의 debugging tool을 사용하지 않으므로
이 인자에 대한 정의는 한번도 한적이 없다.
따라서 이 인자에 대한 설명은 생략하기로 하겠다.
-> WFlags f: 거의 사용할 일이 없으므로 디폴트값을 따르기로
하자. 설명도 생략하겠다. 자세한 정보는
/usr/lib/qt/html/qwidget.html file을 참조하기
바란다.
---------- < source 2.2.1.2. testFrameButton.cpp > -----------
#include <qapplication.h>
#include <qwidget.h>
#include <qframe.h> /* QFrame을 위한 header file */
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
QWidget *wdgtTest = new QWidget();
wdgtTest->setGeometry(100, 100, 200, 100);
/* parent 인자를 wdgtTest로 설정한다. */
QFrame *frTest = new QFrame(wdgtTest);
/* parent가 wdgtTest이므로 frTest에 대한 속성 */
/* 들(x좌표, y좌표)의 좌표값은 wdgtTest에 대한 */
/* 상대적인 좌표로 변환된다. */
frTest->setGeometry(10, 10, 150, 60);
/* frame의 형태를 정한다. */
frTest->setFrameStyle(QFrame::Box | QFrame::Raised |
QFrame::Plain);
myapp.setMainWidget(wdgtTest);
wdgtTest->show();
return myapp.exec();
}
-----------------------------------------------------------
QFrame에 대한 class선언 정보는
/usr/lib/qt/include/qframe.h에 정의되어있다. 다음과 같이
public과 private양쪽에 선언되어있다.
public:
QFrame( QWidget *parent=0, const char *name=0, WFlags f=0,
bool allowLines=TRUE );
private: // Disabled copy constructor and operator=
#if defined(Q_DISABLE_COPY)
QFrame( const QFrame & );
QFrame &operator=( const QFrame & );
#endif
public member와 private member에 대해서는 굳이 언급하지 않아도 되
겠지만 참조범위에 대해 아주 간단하게 설명을 하겠다.
-> public member: 하위, 상위, 혹은 형제 class에서 이 member를 참조할
수 있다.
-> private member: 다른 어떤 class도 이 member를 참조할 수 없다.
인자에 대해 알아보도록 하자.
-> QWidget *parent: 위에서 언급한 QWidget의 parent인자와 같다.
-> const char *name: " "
-> WFlags f: " "
-> bool allowLines: 만약 FALSE로 setting된다면 수평라인과 수직라인
을 쓰지 않는다.
---------- < source 2.2.1.3. testFrameButton.cpp > -----------
#include <qapplication.h>
#include <qwidget.h>
#include <qframe.h>
#include <qpushbutton.h> /* QPushButton을 위한 header file */
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
QWidget *wdgtTest = new QWidget();
wdgtTest->setGeometry(100, 100, 200, 100);
QFrame *frTest = new QFrame(wdgtTest);
frTest->setGeometry(10, 10, 150, 60);
/* 형태에 주의하여 보자. */
frTest->setFrameStyle(QFrame::Box | QFrame::Raised |
QFrame::Plain);
/* text가 "테스트", parent가 frTest로 설정되어있다. */
QPushButton *bttnTest = new QPushButton("테스트", frTest);
/* frTest와 마찬가지로 bttnTest 또한 frTest에 대한 상대 */
/* 적인 좌표로 설정된다. */
bttnTest->setGeometry(20, 20, 100, 30);
// bttnTest->resize(100, 30);
myapp.setMainWidget(wdgtTest);
wdgtTest->show();
return myapp.exec();
}
-----------------------------------------------------------
/usr/lib/qt/include/qframe.h에 보면 setFrameStyle에 대해 다음
과 같이 쓰여져 있다.
void setFrameStyle(int);
인자부분을 보면 integer형인데 이곳에 들어갈 수 있는 인자 또한
header file에 잘 나타나있다.
enum { NoFrame = 0, // no frame
Box = 0x0001, // rectangular box
Panel = 0x0002, // rectangular panel
WinPanel = 0x0003, // rectangular panel (Windows)
HLine = 0x0004, // horizontal line
VLine = 0x0005, // vertical line
MShape = 0x000f,
Plain = 0x0010, // plain line
Raised = 0x0020, // raised shadow effect
Sunken = 0x0030, // sunken shadow effect
MShadow = 0x00f0 };
enumeration 되어진 member들을 주의깊게 보면 숫자가 끊어져있는 것
을 알 수 있다. 즉 0x0001~0x0005, 0x000f, 0x0010, 0x0020, 0x0030,
0x00f0. 이 16진수들을 2진수로 풀어보자.
0x0001 => 0000 0000 0000 0001
0x0002 => 0000 0000 0000 0010
0x0003 => 0000 0000 0000 0011
0x0004 => 0000 0000 0000 0100
0x0005 => 0000 0000 0000 0101
0x000f => 0000 0000 0000 1111
0x0010 => 0000 0000 0001 0000
0x0020 => 0000 0000 0010 0000
0x0030 => 0000 0000 0011 0000
0x00f0 => 0000 0000 1111 0000
위의 2진수 값에서도 볼 수 있듯이 bit operation을 적당하게만 쓴
다면 여러 모양의 Frame Style을 나타낼 수 있는 조합이 나오게 된다.
예를 들어 [ Box | WinPanel | Sunken ]을 하게 되면 윈도우즈 스타
일의 움푹 들어간 입체 라인을 나타내준다.
/usr/lib/qt/include/qpushbutton.h에는 다음과 같이 class가 선
언되어있다.
public:
QPushButton( QWidget *parent=0, const char *name=0 );
QPushButton( const char *text, QWidget *parent=0,
const char *name=0 );
private: // Disabled copy constructor and operator=
#if defined(Q_DISABLE_COPY)
QPushButton( const QPushButton & );
QPushButton &operator=( const QPushButton & );
#endif
혹여 궁금하게 생각할 수도 있는 부분
QPushButton &operator=( const QPushButton & );
은 다음 보여드릴 source 2.2.1.4에서 설명하겠다.
보는 바와 같이 QPushButton의 인자에는 문자열 "테스트"와
frTest가 들어가있다. 종속성에 대한 설명은 위해서 이미 했
으므로 여기서는 생략하겠다. 각 widget들의 속성에 대한 설정은
setGeometry로 할 수도 있고, resize라는 함수로도 설정이 가능
하다. 이 두 함수는 설정값에서 약간의 차이가 있다.
setGeometry는 좌표와 크기에 대한 설정이 모두 들어가지만 resize
함수는 x좌표와 y좌표만이 인자로 들어간다. 이 두 함수는 widget
들에 대한 속성 설정이므로 QWidget의 하위 class들의 public함수
에 모두 선언이 되어있다. 물론 QWidget도 이들 함수를 포함하고 있다.
2.2.1.1. Class를 생성하여 만들기
이제부터는 위에서 설명한 source들을 사용하여 분할 컴파일하는 방
법을 알아보자. 우선 source들을 각 특성단위로 분리시켜야한다.
다음은 이렇게 분리된 source code들이다.
--------- < source 2.2.1.1.1 clssTestWidget.h > -----------
#include <qwidget.h> /* 아래쪽의 public QWidget 부분에 대한 header */
/* 아래쪽에 있는 frTest와 bttnTest를 선언하기 위한 class 선언. */
/* 일반함수 선언과 비슷함에 유의 */
class QFrame;
class QPushButton;
/* clssTestWidget class는 QWidget의 모든 public변수 혹은 class를 */
/* 공유할 수 있다. 즉 clssTestWidget class는 QWidget으로부터 '상속' */
/* 받은 class이다. */
class clssTestWidget : public QWidget
{
/* Qt library를 쓰는 class라면 항상 이렇게 선언해놓아야 한다. */
Q_OBJECT
public:
clssTestWidget(); /* 생성자 */
~clssTestWidget(); /* 소멸자 */
/* widget(bttnTest)의 특정 SIGNAL이 발생했을 때, event를 받아 */
/* 원하는 처리를 해줄 수 있는 slot function 선언 */
public slots:
void printMessage();
/* 후에 설명하겠다. 우선은 이런 format도 있다는 정도로 알아두자. */
protected:
private:
QFrame *frTest;
QPushButton *bttnTest;
};
-----------------------------------------------------------
--------- < source 2.2.1.1.2 clssTestWidget.cpp > ---------
#include <qframe.h> /* frTest를 위한 header 선언 */
#include <qpushbutton.h> /* bttnTest를 위해 */
#include <stdio.h>
#include "clssTestWidget.h"
clssTestWidget::clssTestWidget()
{
QFrame *frTest = new QFrame(this);
frTest->setGeometry(10, 10, 150, 60);
frTest->setFrameStyle(QFrame::Box | QFrame::Raised | QFrame::Plain);
QPushButton *bttnTest = new QPushButton("테스트", frTest);
bttnTest->setGeometry(20, 20, 100, 30);
/* 버튼이 'clicked()'되었을 때 printMessage()함수를 호출하라는 */
/* 행동 지침 */
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(printMessage()));
}
clssTestWidget::~clssTestWidget()
{
}
/* 버튼이 눌렸을 때 처리되는 함수 */
void clssTestWidget::printMessage()
{
emit printf("버튼이 눌렸습니다.\n");
}
-----------------------------------------------------------
--------- < source 2.2.1.1.3 main_test1.cpp > ---------
#include <qapplication.h> /* QApplication을 위한 header file */
#include "clssTestWidget.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
/* class 생성 */
clssTestWidget *wdgtTest = new clssTestWidget();
/* class 속성정의 */
wdgtTest->setGeometry(100, 100, 200, 100);
/* main으로 쓸 widget 정의 */
myapp.setMainWidget(wdgtTest);
/* widget을 보여줌 */
wdgtTest->show();
return myapp.exec();
}
-----------------------------------------------------------
이 source들에 앞서 설명한 source와의 차이점은, file들이 분리
되었고 class를 정의하여 그 class를 main에서 호출한다는 것
말고는 거의 없다. 따라서 더이상의 설명은 생략하고 다음으로
Maikfile에 대한 간단한 설명과 함께 이 분할 module들을 compile
하는 방법에 대해 알아보겠다.
2.2.2. Makefile 만들기
2.2.1.에서의 source들을 compile하는 방법에 대해서는 아직 언
급하지 않았다. Qt 2.0이상부터는 제법 GUI Tool이 괜찮아진 모
양이던데 필자도 한번은 사용을 해 보았지만 버그가 몇 가지 있
는 것 같아 지금은 전혀 사용하지 않고 있다. Makefile을 만드는
방법에 있어서도 요샌 tmake라는 GUI Tool을 사용한다고 들었다.
필자는 이 Tool도 사용하지 않는다. 이유는 간단하다. Makefile
을 마음대로 주무르기 위해서이다.
Makefile을 완전하게 이해하고 쉽게 쓸 수 있는 수준이라면 tmake
를 사용하는 것도 나쁘지는 않을 것이다.
아래의 code는 2.2.1.1의 source들을 분할 Compile하기 위한
Makefile이다.
---------------- < Makefile 2.2.2 > ---------------------
PROGS = main_test1 ### 보통 실행file의 이름을 적는다.
### X-window 환경에 관한 library들을 포함시킨다. (Qt 포함)
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
### Compiler를 정의한다.
CC = g++
GCC = gcc
### Header file들을 포함시킨다.
INCLUDE = -I/usr/lib/qt/include
### Compile option을 정의한다.
C_FLAG = -pipe -DNO_DEBUG -O2
### Header file에 대한 option과 Compile option을 합한다.
CFLAGS = $(INCLUDE) $(C_FLAG)
### 실행file생성을 위한 Linker를 정의한다. 여기서는 g++이 된다.
SYSCONF_LINK = $(CC)
### class를 분리시켜놓은 Header file을 .cpp source file과 연동
### 시키며 시스템에서 초기화된 graphic device에 관한 setting을
### 시키기 위한 특수한 cpp file인 moc file을 생성시키는 tool이
### 다. moc file에 대한 분석은 별로 필요없는 부분이므로 생략하
### 겠다.
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
### main함수가 들어있는 source file을 Link시킬 때 필요한 Object file
### 들을 후에 참조하기 편하도록 Label로 묶어놓는다.
MNU_OBJS = \
main_test1.o \
clssTestWidget_moc.o \
clssTestWidget.o
### 'make all'은 이 Makefile을 실행한다. (= make)
all: $(PROGS)
### 'make clean'은 make를 실행시켰을때 새로 생성되는 모든 file
### 을 지우는 역할을 한다. clean, all, 모두 사용자가 어떻게 정
### 의하는가에 따라 역할이 틀려지게 된다.
clean:
rm -f *.o
rm -f main_test1
rm -f *_moc.*
############# COMPILE #############
### main file을 컴파일한다.
main_test1.o: main_test1.cpp
$(CC) -c main_test1.cpp $(CFLAGS) -o $@
### Header file로부터 moc file을 만들어낸다.
clssTestWidget_moc.cpp: clssTestWidget.h
$(MOC) clssTestWidget.h -o clssTestWidget_moc.cpp
### 만들어진 moc file을 컴파일한다.
clssTestWidget_moc.o: clssTestWidget_moc.cpp
$(CC) -c clssTestWidget_moc.cpp $(CFLAGS) -o $@
### 새로 분리된 module file을 컴파일한다.
clssTestWidget.o: clssTestWidget.cpp
$(CC) -c clssTestWidget.cpp $(CFLAGS) -o $@
### 맨 윗부분의 PROG정의부에 있는 이름을 Label로 쓴다.
main_test1: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
------------------------------------------------------------
shell상에서 'make' 혹은 'make all' 을 실행시키면 자동적으로
compile 및 link를 시켜준다. make는 object file의 생성시간, 즉
compile된 시점을 기준으로 source에 변화가 생겼을 때 그 변화된
source module만을 compile함으로써 전체를 Compile하는 시간을 크
게 줄여주는 효과가 있다. 물론 source module이 두세개 정도로 작
다면 굳이 Makefile을 만들어줄 필요가 없다.
하지만 보통 실제 project들의 경우 상당한 수의 source module을
필요로 하기때문에 Makefile의 생성은 필수적이라 할 수 있다.
다음으로 소개하는 source들에 대해서는 모든 source의 Makefile
을 source바로 밑에 주석없이 소개하도록 하겠다. Makefile을 사용
하는데 있어 적게라도 도움이 되었으면 한다.
2.2.3. 라벨(QLabel)
이제부터 2.2.10까지의 모든 예제는 앞에서 사용한 분할 module compile
방법을 따르기로 하겠다. 물론 기존에 설명했던 부분들은 모두 생략한다.
우선 source를 보도록 하자.
-------------- < source 2.2.3.1 clssTestLabel.h > ---------------
#include <qwidget.h>
class QLabel;
class clssTestLabel : public QWidget
{
Q_OBJECT
public:
clssTestLabel();
~clssTestLabel();
public slots:
protected:
private:
QLabel *lblTest1;
QLabel *lblTest2;
QLabel *lblTest3;
};
-------------------------------------------------------------------
-------------- < source 2.2.3.2 clssTestLabel.cpp > ---------------
#include <qlabel.h>
#include <qframe.h>
#include "clssTestLabel.h"
clssTestLabel::clssTestLabel()
{
QLabel *lblTest1 = new QLabel(this);
lblTest1->setText("테스트 1");
lblTest1->setGeometry(20, 20, 100, 30);
/* QLabel에서 setFrameStyle함수를 호출했다는 것을 명심하자. */
lblTest1->setFrameStyle(QFrame::Box | QFrame::Panel | QFrame::Raised);
QLabel *lblTest2 = new QLabel(this);
lblTest2->setText("테스트 2");
lblTest2->setGeometry(20, 60, 100, 30);
lblTest2->setFrameStyle(QFrame::Box | QFrame::Panel | QFrame::Sunken);
QLabel *lblTest3 = new QLabel(this);
lblTest3->setText("테스트 3");
lblTest3->setGeometry(20, 100, 100, 30);
lblTest3->setFrameStyle(QFrame::Box | QFrame::Panel | QFrame::Plain);
}
clssTestLabel::~clssTestLabel()
{
}
-------------------------------------------------------------------
위의 Source는 총 세가지 모양의 Label format을 나타내준다.
특이할 만한 점은 QLabel에서 setFrameStyle을 호출했다는 것인데
/usr/lib/qt/include/qlabel.h에서는 setFrameStyle함수가 선언되어있지
않다. qframe.h file에서만이 선언되어있는데 QLabel은 QFrame class에서
파생되어 나온 자식 class이므로 QFrame class에서 선언되어있는 모든
public함수들을 공유하여 쓸 수 있다. 물론 enumeration된 Box, Panel등
을 쓸 때는 QLabel의 member들이 아니므로 QFrame::과 같이 소속을 적어
주어야 Compiler가 알아들을 수 있다.
-------------- < source 2.2.3.3 main_test2.cpp > ---------------
#include <qapplication.h>
#include "clssTestLabel.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestLabel *clssTest = new clssTestLabel();
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.3.1 > ----------------------
PROGS = main_test2
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test2.o \
clssTestLabel_moc.o \
clssTestLabel.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test2
rm -f *_moc.*
############# COMPILE #############
main_test2.o: main_test2.cpp
$(CC) -c main_test2.cpp $(CFLAGS) -o $@
clssTestLabel_moc.cpp: clssTestLabel.h
$(MOC) clssTestLabel.h -o clssTestLabel_moc.cpp
clssTestLabel_moc.o: clssTestLabel_moc.cpp
$(CC) -c clssTestLabel_moc.cpp $(CFLAGS) -o $@
clssTestLabel.o: clssTestLabel.cpp
$(CC) -c clssTestLabel.cpp $(CFLAGS) -o $@
main_test2: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.4. 편집박스(QLineEdit)
-------------- < source 2.2.4.1 clssTestLineEdit.h > ---------------
#include <qwidget.h>
class QLineEdit;
class QPushButton;
class clssTestLineEdit : public QWidget
{
Q_OBJECT
public:
clssTestLineEdit();
~clssTestLineEdit();
public slots:
void testClick();
protected:
private:
QLineEdit *leTest1;
QLineEdit *leTest2;
QPushButton *bttnTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.4.2 clssTestLineEdit.cpp > ---------------
#include <qlineedit.h>
#include <qpushbutton.h>
#include <stdio.h>
#include "clssTestLineEdit.h"
clssTestLineEdit::clssTestLineEdit()
{
leTest1 = new QLineEdit(this);
leTest1->setGeometry(20, 20, 100, 30);
leTest2 = new QLineEdit(this);
leTest2->setGeometry(20, 60, 100, 30);
leTest2->setText("환영!");
/* setEnabled는 편집을 할 수 있게 할 것인지 아닌지를 결정한다. */
/* true->편집가능, false->편집불가능 */
leTest2->setEnabled(false);
bttnTest = new QPushButton("확 인", this);
bttnTest->setGeometry(20, 100, 100, 30);
/* 버튼(bttnTest)이 눌렸(clicked())을 때 testClick()함수를 실행하라 */
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(testClick()));
}
clssTestLineEdit::~clssTestLineEdit()
{
}
void clssTestLineEdit::testClick()
{
/* leTest1->text()는 현재 leTest1 편집박스안에 표시되어있는 */
/* text를 가져온다. */
emit printf("편집박스1의 텍스트는 < %s >입니다.\n",
leTest1->text());
emit printf("편집박스2의 텍스트는 < %s >입니다.\n",
leTest2->text());
}
-------------------------------------------------------------------
QLineEdit class는 윈도우즈의 edit box와 같은 기능을 하는 class이다.
특수키(예: 방향키, insert, delete, home, end등)들을 쓸 수 있으며
X-window와의 호환도 이루어진다. 즉 편집박스에 있는 문자열은 마우스
로 < copy and paste > 할 수 있다는 이야기다. 가장 자주 쓰이는 Widget
이니 꼭 기억해두자.
-------------- < source 2.2.4.3 main_test3.cpp > ---------------
#include <qapplication.h>
#include "clssTestLineEdit.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestLineEdit *clssTest = new clssTestLineEdit();
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.4 > ----------------------
PROGS = main_test3
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test3.o \
clssTestLineEdit_moc.o \
clssTestLineEdit.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test3
rm -f *_moc.*
############# COMPILE #############
main_test3.o: main_test3.cpp
$(CC) -c main_test3.cpp $(CFLAGS) -o $@
clssTestLineEdit_moc.cpp: clssTestLineEdit.h
$(MOC) clssTestLineEdit.h -o clssTestLineEdit_moc.cpp
clssTestLineEdit_moc.o: clssTestLineEdit_moc.cpp
$(CC) -c clssTestLineEdit_moc.cpp $(CFLAGS) -o $@
clssTestLineEdit.o: clssTestLineEdit.cpp
$(CC) -c clssTestLineEdit.cpp $(CFLAGS) -o $@
main_test3: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.4.1 QObject::connect, SIGNAL과 SLOT
/usr/lib/qt/include/qobject.h에 선언되어있는 connect함수를 보자.
static bool connect( const QObject *sender, const char *signal,
const QObject *receiver, const char *member );
bool connect( const QObject *sender, const char *signal,
const char *member ) const;
위와 같이 두가지로 선언되어있는데 밑에 있는 bool type의 형태에서
빠져있는 const QObject *receiver 는 default로 < this >로 정해져있다.
즉 const char *member 는 this class 의 member라야한다.
마찬가지로 첫번째의 static bool type의 형태에서는 member가
< const QObject *receiver >의 member이어야 한다.
각 인자들을 보면 이 함수가 하는 기능이 무엇인지 알 수 있다.
const QObject *sender : signal을 보내줄 class 객체.
const char *signal : '사용자가 취한 행동'이다.
const QObject *receiver : signal을 받아서 처리할 class 객체.
const char *member : 보통 receiver class의 public member 함수를
쓴다.
const char *signal에 대해 좀더 자세히 알아보자.
예제 2.2.4.2의 Source중
QObject::connect(bttnTest, SIGNAL(clicked()), this, SLOT(testClick()));
부분에서 보면 const char *signal 부분을 SIGNAL(clicked())로 해놓았다.
QPushButton의 header file을 보면 다음과 같이 signal 함수들이 선언되어
있는 것을 알 수 있다.
signals:
void pressed();
void released();
void clicked();
void toggled( bool );
각각의 함수들은 함수 자체의 이름에 해당되는 사용자의 행동이 일어났을
때 부합하는 signal을 'this'라는 class에 전해주게 된다. 여기서 this는
class clssTestLineEdit를 말하며, 따라서 connect함수의 마지막 인자인
const char *member부분에는 clssTestLineEdit의 member함수인 testClick
함수가 오게 된 것이다.
< SLOT >이 하는 일은 차차 이야기 하겠다. 우선 그냥 이렇게 쓴다고 문법
처럼 생각을 해두자. member 인자부분은 만약 member 함수를 쓸 경우 반드
시 SLOT안에 member를 두어야하며 또한 이 member 함수는 public slots:
혹은 private slots: 에 선언되어있어야 한다.
2.2.5. 콤보박스(QComboBox)
-------------- < source 2.2.5.1 clssTestComboBox.h > ---------------
#include <qwidget.h>
class QComboBox;
class QPushButton;
class clssTestComboBox : public QWidget
{
Q_OBJECT
public:
clssTestComboBox();
~clssTestComboBox();
public slots:
void testClick();
protected:
private:
QComboBox *cbTest; /* 콤보박스에 대한 class */
QPushButton *bttnTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.5.2 clssTestComboBox.cpp > ---------------
#include <qcombobox.h>
#include <qpushbutton.h>
#include <stdio.h>
#include "clssTestComboBox.h"
clssTestComboBox::clssTestComboBox()
{
cbTest = new QComboBox(this);
cbTest->setGeometry(20, 20, 100, 30);
/* ComboBox에 item들을 넣는다. */
cbTest->insertItem("첫번째", 0);
cbTest->insertItem("두번째", 1);
cbTest->insertItem("세번째", 2);
cbTest->insertItem("네번째", 3);
cbTest->setCurrentItem(0);
bttnTest = new QPushButton("확 인", this);
bttnTest->setGeometry(20, 100, 100, 30);
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(testClick()));
}
clssTestComboBox::~clssTestComboBox()
{
}
void clssTestComboBox::testClick()
{
/* cbTest->currentText()는 현재 ComboBox에서 보이는 */
/* text를 return한다. */
emit printf("콤보박스1의 텍스트는 < %s >입니다.\n",
cbTest->currentText());
}
-------------------------------------------------------------------
틀린부분이 거의 없으므로 QComboBox에 대해서만 설명하겠다.
/usr/lib/qt/include/qcombobox.h를 열어보자.(주의: 만약 당신이 Qt를
다운받아 다른 곳에 인스톨 시켰다면 path가 틀려질 것임.)
public:
QComboBox( QWidget *parent=0, const char *name=0 );
QComboBox( bool rw, QWidget *parent=0, const char *name=0 );
몇몇 특정 widget들을 제외하고는 거의 인자가 비슷하다. 인자들에 관
한 설명은 앞서 이야기했던 부분들과 같으므로 설명을 생략하겠다.
그러면 이번에는 < cbTest->insertItem("첫번째", 0); >부분을 보자.
header file에는 insertItem함수가 다음과 같이 선언되어있다.
이 함수는 콤보박스에 item을 추가할 때 사용하는 함수이다.
void insertItem( const char *text, int index=-1 );
void insertItem( const QPixmap &pixmap, int index=-1 );
첫번째 선언문을 보자.
const char *text는 콤보박스에 추가시킬 text이고 int index는 그 text
의 순번이다. index는 0번부터 시작한다. -1로 초기화되어있다는 것은
콤보박스에 아무 item도 들어있지 않다는 뜻이다. Compile해서 실행시켜
보기 바란다. 좀더 확실하게 이해가 될 것이다.
다음으로 cbTest->currentText()부분을 보자.
header file의 선언을 보면 다음과 같다.
const char *currentText() const;
이 함수는 현재 콤보박스에 나타나있는 text를 return한다.
콤보박스의 현재 text를 setting하는 함수는
void setCurrentItem( int index );
이다. 여기서의 index의 의미는 insertItem의 index의 의미와 동일하다.
< cbTest->setCurrentItem(0); >이 뜻하는 것은 첫번째 item을 현재 text
로 보여주겠다는 이야기다.
-------------- < source 2.2.5.3 main_test4.cpp > ---------------
#include <qapplication.h>
#include "clssTestComboBox.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestComboBox *clssTest = new clssTestComboBox();
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.5 > ----------------------
PROGS = main_test4
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test4.o \
clssTestComboBox_moc.o \
clssTestComboBox.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test4
rm -f *_moc.*
############# COMPILE #############
main_test4.o: main_test4.cpp
$(CC) -c main_test4.cpp $(CFLAGS) -o $@
clssTestComboBox_moc.cpp: clssTestComboBox.h
$(MOC) clssTestComboBox.h -o clssTestComboBox_moc.cpp
clssTestComboBox_moc.o: clssTestComboBox_moc.cpp
$(CC) -c clssTestComboBox_moc.cpp $(CFLAGS) -o $@
clssTestComboBox.o: clssTestComboBox.cpp
$(CC) -c clssTestComboBox.cpp $(CFLAGS) -o $@
main_test4: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.6. 리스트(QListView)
-------------- < source 2.2.6.1 clssTestListView.h > ---------------
#include <qwidget.h>
class QListView;
class QListViewItem;
class QLabel;
class QFrame;
class clssTestListView : public QWidget
{
Q_OBJECT
public:
clssTestListView();
~clssTestListView();
public slots:
void appearItem();
protected:
private:
QFrame *frTest;
QListView *lvTest;
QListViewItem *lviTest[10];
QLabel *lblTest1;
QLabel *lblTest2;
QLabel *lblTest3;
};
-------------------------------------------------------------------
-------------- < source 2.2.6.2 clssTestListView.cpp > ---------------
#include <qlistview.h>
#include <qlabel.h>
#include <qframe.h>
#include <stdio.h>
#include "clssTestListView.h"
clssTestListView::clssTestListView()
{
int i;
char szTest1[50], szTest2[50], szTest3[50];
/* List의 가장자리선의 처리를 위해 frame을 그린다. */
frTest = new QFrame(this);
frTest->setGeometry(20, 20, 200, 200);
frTest->setFrameStyle(QFrame::Box | QFrame::Sunken |
QFrame::WinPanel);
lvTest = new QListView(frTest);
/* frame에서 약 3정도 사방으로 적은 크기가 가장 적당하다. */
lvTest->setGeometry(3, 3, 194, 194);
/* 각 field의 title을 정한다. */
lvTest->addColumn(" 첫번째 ");
lvTest->addColumn(" 두번째 ");
lvTest->addColumn(" 세번째 ");
/* tree장식을 할 것인지 아닌지를 결정한다. */
lvTest->setRootIsDecorated(true);
for (i = 0; i < 9; i++)
{
memset(szTest1, '\0', 50);
memset(szTest2, '\0', 50);
memset(szTest3, '\0', 50);
sprintf(szTest1, "환영 %d", i);
sprintf(szTest2, "리눅스 %d", i);
sprintf(szTest3, "만세 %d", i);
/* 첫번째 두번째 세번째*/
lviTest[i] = new QListViewItem(lvTest, szTest1, szTest2, szTest3);
}
/* List(lvTest)에서 item(lviTest[])이 선택(selectionChanged()) */
/* 되어질 때 this class(clssTestListView)의 member function */
/* (appearItem())을 수행해라. */
QObject::connect(lvTest, SIGNAL(selectionChanged()), this,
SLOT(appearItem()));
lblTest1 = new QLabel(this);
lblTest1->setGeometry(20, 230, 150, 20);
lblTest2 = new QLabel(this);
lblTest2->setGeometry(20, 260, 150, 20);
lblTest3 = new QLabel(this);
lblTest3->setGeometry(20, 290, 150, 20);
}
clssTestListView::~clssTestListView()
{
}
void clssTestListView::appearItem()
{
/* 선택된 item의 첫번째 field를 Label로 나타냄 */
lblTest1->setText(lvTest->currentItem()->text(0));
/* 선택된 item의 두번째 field를 Label로 나타냄 */
lblTest2->setText(lvTest->currentItem()->text(1));
/* 선택된 item의 세번째 field를 Label로 나타냄 */
lblTest3->setText(lvTest->currentItem()->text(2));
}
-------------------------------------------------------------------
/usr/lib/qt/include/qlistview.h file의 선언을 보자.
public:
QListView( QWidget * parent = 0, const char * name = 0 );
다른 widget들과 별 차이가 없으므로 설명은 생략한다.
virtual int addColumn( const char * label, int size = -1);
field의 제목(const char *label)을 지정한다.
size에 대해서는 값정의가 되어있으므로 써도 그만 안써도 그만이라는
이야기인데 흔히 field의 size를 임의로 정해서 해당 field의 값을 찾
아내는데 쓰이는 인자이다. 다시 이야기할 기회가 있을 지는 모르겠지
만 그렇게 중요한 인자는 아니므로 설명은 이쯤 해 두겠다.
virtual void setRootIsDecorated( bool );
만약 인자값을 false로 준다면 첫번째 field의 item들 앞에는 아무것
도 없게 된다.
현재 tree장식이 되어있는지 아닌지를 검사하고 싶다면 다음 함수를 써라.
bool rootIsDecorated() const;
이번에는 실제 item을 List에 삽입하는 방법에 대해 알아보자.
header file에는 다음과 같이 QListViewItem을 선언하고 있다.
public:
QListViewItem( QListView * parent );
QListViewItem( QListViewItem * parent );
QListViewItem( QListView * parent, QListViewItem * after );
QListViewItem( QListViewItem * parent, QListViewItem * after );
QListViewItem( QListView * parent,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
QListViewItem( QListViewItem * parent,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
QListViewItem( QListView * parent, QListViewItem * after,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
QListViewItem( QListViewItem * parent, QListViewItem * after,
const char *, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0,
const char * = 0, const char * = 0 );
약간 복잡하다고 생각할 지도 모르지만 상당히 간단한 선언이다.
우선 parent인자부터 살펴보자.
위에서 선언된 parent인자의 type은 < QListView * > 와 < QListViewItem * >
이 두가지이다.
< QListView * >를 parent인자로 받는다는 것은 현재 생성되어있는 List
에 < const char * > 형태의 text를 item으로 삽입한다는 뜻이다.
< QListViewItem * >를 parent인자로 받는다는 것은 List의 특정 item(parent)
을 root로 하는 tree구조로서 그 item의 자식 node를 삽입한다는 뜻이다.
언뜻 이해가 가지 않을 것이다. 위 source에서
< lviTest[i] = new QListViewItem(lvTest, szTest1, szTest2, szTest3); >
이 포함되어있는 for loop문을 다음과 같이 바꾼 후 Compile하여 실행시켜
보라.
lviTest[0] = new QListViewItem(lvTest,
"테스트1-1", "테스트1-2", "테스트1-3");
lviTest[1] = new QListViewItem(lviTest[0],
"테스트2-1", "테스트2-2", "테스트2-3");
실행시켜보면 위에서 필자가 이야기했던 부분들이 쉽게 이해가 갈 것이다.
다음은 List에서 삽입된 text를 얻는 방법에 대한 설명이다.
lblTest1->setText(lvTest->currentItem()->text(0));
QListViewItem의 text를 얻는 방법은 간단하다.
만약 QListViewItem type의 class가 lviTest1이라면, lviTest1의 첫번째
field의 text를 얻으려면 < lviTest1->text(0); >이라고 하면 될 것이다.
ComboBox와 비슷하지만 index가 의미하는 것이 틀리므로 유의하기 바란다.
위에서 lvTest->currentItem()이 의미하는 것은 현재 List에서 선택된
Item set을 말한다. header file에는 currentItem()함수가 다음과 같이
선언되어 있다.
QListViewItem * currentItem() const;
type을 보면 QListViewItem이다. 이는 currentItem()이 QListViewItem
class의 모든 member들을 참조할 수 있다는 이야기가 된다.
따라서 현재 List에서 선택된 Item의 두번째 field의 text를 알고 싶다면
다음과 같이 하면 된다.
lblTest1->setText(lvTest->currentItem()->text(1));
이번엔 QListView에서 발생가능한 Signal에 대해 알아보도록 하자.
header file을 보면 다음과 같이 총 6개의 signal함수가 있다.
signals:
void selectionChanged();
void selectionChanged( QListViewItem * );
void currentChanged( QListViewItem * );
void doubleClicked( QListViewItem * );
void returnPressed( QListViewItem * );
void rightButtonClicked( QListViewItem *, const QPoint&, int );
void rightButtonPressed( QListViewItem *, const QPoint&, int );
각 Signal들의 발생요인은 위함수의 제목과 동일하다.
-------------- < source 2.2.6.3 main_test5.cpp > ---------------
#include <qapplication.h>
#include "clssTestListView.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestListView *clssTest = new clssTestListView();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.6 > ----------------------
PROGS = main_test5
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test5.o \
clssTestListView_moc.o \
clssTestListView.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test5
rm -f *_moc.*
############# COMPILE #############
main_test5.o: main_test5.cpp
$(CC) -c main_test5.cpp $(CFLAGS) -o $@
clssTestListView_moc.cpp: clssTestListView.h
$(MOC) clssTestListView.h -o clssTestListView_moc.cpp
clssTestListView_moc.o: clssTestListView_moc.cpp
$(CC) -c clssTestListView_moc.cpp $(CFLAGS) -o $@
clssTestListView.o: clssTestListView.cpp
$(CC) -c clssTestListView.cpp $(CFLAGS) -o $@
main_test5: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.7. 체크박스(QCheckBox), 라디오버튼(QRadioButton)
-------------- < source 2.2.7.1 clssTestButton.h > ---------------
#include <qwidget.h>
class QButtonGroup;
class QRadioButton;
class QCheckBox;
class QPushButton;
class clssTestButton : public QWidget
{
Q_OBJECT
public:
clssTestButton();
~clssTestButton();
public slots:
void procButton();
protected:
private:
/* QRadioButton을 묶기 위한 Group class 선언 */
QButtonGroup *bgTest;
QRadioButton *rbTest1;
QRadioButton *rbTest2;
QCheckBox *chkbTest1;
QCheckBox *chkbTest2;
QPushButton *bttnTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.7.2 clssTestButton.cpp > ---------------
#include <qradiobutton.h>
#include <qpushbutton.h>
#include <qcheckbox.h>
#include <qbuttongroup.h>
#include <stdio.h>
#include "clssTestButton.h"
clssTestButton::clssTestButton()
{
/* radio button은 어느 한 버튼이 활성화되면 다른 버튼들은 */
/* 비활성화되어야 한다. 이런 기능을 위해 QButtonGroup class */
/* 를 정의한다. */
bgTest = new QButtonGroup(this);
bgTest->setGeometry(20, 20, 300, 100);
/* radio button들을 bgTest에 종속시킨다. */
rbTest1 = new QRadioButton("리눅스 라디오", bgTest);
rbTest1->setGeometry(20, 20, 100, 20);
rbTest2 = new QRadioButton("만세 라디오", bgTest);
rbTest2->setGeometry(150, 20, 100, 20);
/* radio button들이 단지 하나의 버튼만이 선택되게 하기 위해 */
/* 마지막으로 QButtonGroup class변수에 삽입시킨다. */
bgTest->insert(rbTest1, 0);
bgTest->insert(rbTest2, 1);
/* 체크박스 정의 */
chkbTest1 = new QCheckBox("리눅스 체크", this);
chkbTest1->setGeometry(20, 160, 100, 20);
chkbTest2 = new QCheckBox("만세 체크", this);
chkbTest2->setGeometry(150, 160, 100, 20);
bttnTest = new QPushButton("확 인", this);
bttnTest->setGeometry(60, 250, 100, 30);
QObject::connect(bttnTest, SIGNAL(clicked()), this,
SLOT(procButton()));
}
clssTestButton::~clssTestButton()
{
}
void clssTestButton::procButton()
{
if (rbTest1->isChecked())
emit printf("리눅스 라디오 버튼이 체크되어 있습니다.\n");
else if (rbTest2->isChecked())
emit printf("만세 라디오 버튼이 체크되어 있습니다.\n");
else
emit printf("체크되어있는 라디오 버튼이 없습니다.\n");
if (chkbTest1->isChecked())
emit printf("리눅스 체크 박스가 체크되어 있습니다.\n");
if (chkbTest2->isChecked())
emit printf("만세 체크 박스가 체크되어 있습니다.\n");
if (chkbTest1->isChecked() == false &&
chkbTest2->isChecked() == false)
emit printf("체크되어있는 체크박스가 없습니다.\n");
}
-------------------------------------------------------------------
QButtonGroup부터 보도록하자. /usr/lib/qt/include/qbuttongroup.h에는
다음과 같이 선언되어있다.
public:
QButtonGroup( QWidget *parent=0, const char *name=0 );
QButtonGroup( const char *title, QWidget *parent=0,
const char *name=0 );
인자들에 대한 설명은 별로 필요없으리라 생각하므로 생략하겠다.
생성자부분보다는 insert하는 부분의 설명이 필요하리라 생각한다.
int insert( QButton *, int id=-1 );
int id 는 group에 포함되는 버튼들의 id를 지정해주는 역할을 한다.
순서에는 상관 없으나 값은 고유값을 가져야 한다.
다음으로 checkbox의 header file /usr/lib/qt/include/qradiobutton.h 에
나타난 생성자부분을 보도록 하자.
public:
QRadioButton( QWidget *parent=0, const char *name=0 );
QRadioButton( const char *text, QWidget *parent=0,
const char *name=0 );
QRadioButton의 < const char *text > 인자에 문자열을 넘겨주면 그 문자열이
이 radiobutton의 title이 된다. 체크가 되어있는지를 검사하는 함수는
isChecked()이다. 인위적으로 checking하는 함수는 header file에 이렇게 선
언되어있다.
void setChecked( bool check );
isChecked(), setChecked(bool check) 두 함수 모두 QRadioButton과
QCheckBox에 똑같이 선언되어있고 또한 기능도 같다.
-------------- < source 2.2.7.3 main_test6.cpp > ---------------
#include <qapplication.h>
#include "clssTestButton.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestButton *clssTest = new clssTestButton();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.7 > ----------------------
PROGS = main_test6
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test6.o \
clssTestButton_moc.o \
clssTestButton.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test6
rm -f *_moc.*
############# COMPILE #############
main_test6.o: main_test6.cpp
$(CC) -c main_test6.cpp $(CFLAGS) -o $@
clssTestButton_moc.cpp: clssTestButton.h
$(MOC) clssTestButton.h -o clssTestButton_moc.cpp
clssTestButton_moc.o: clssTestButton_moc.cpp
$(CC) -c clssTestButton_moc.cpp $(CFLAGS) -o $@
clssTestButton.o: clssTestButton.cpp
$(CC) -c clssTestButton.cpp $(CFLAGS) -o $@
main_test6: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.8. 메뉴(QMenu)
-------------- < source 2.2.8.1 clssTestMenu.h > ---------------
#include <qwidget.h>
class QMenuBar;
class QPopupMenu;
class clssTestMenu : public QWidget
{
Q_OBJECT
public:
clssTestMenu();
~clssTestMenu();
public slots:
void funcTest1();
void funcTest2();
void funcTest3();
void funcTest4();
void funcTest5();
void funcTest6();
protected:
private:
/* Pulldown 메뉴를 위한 메뉴바 */
QMenuBar *mnuMainBar;
/* Popup 메뉴를 위한 메뉴 */
QPopupMenu *mnuTest1;
QPopupMenu *mnuTest2;
QPopupMenu *mnuTest3;
};
-------------------------------------------------------------------
-------------- < source 2.2.8.2 clssTestMenu.cpp > ---------------
#include <qapplication.h>
/* hot key를 위한 header. 여기서는 hot key를 쓰지 않는다. */
/* 한 번 시험삼아 hot key기능까지 넣어보기 바란다. */
#include <qkeycode.h>
/* QMenuBar를 위한 header */
#include <qmenubar.h>
/* QPopupMenu를 위한 header */
#include <qpopupmenu.h>
#include <stdio.h>
#include "clssTestMenu.h"
clssTestMenu::clssTestMenu()
{
/* 전체적인 window의 형태를 Microsoft Windows의 형태로 맞춘다. */
qApp->setStyle(WindowsStyle);
mnuTest1 = new QPopupMenu;
/* Popup menu에 아이템을 넣는다. */
mnuTest1->insertItem("테스트1메뉴의 서브메뉴1", this,
SLOT(funcTest1()));
mnuTest1->insertItem("테스트1메뉴의 서브메뉴2", this,
SLOT(funcTest2()));
mnuTest1->insertItem("테스트1메뉴의 서브메뉴3", this,
SLOT(funcTest3()));
mnuTest2 = new QPopupMenu;
mnuTest2->insertItem("테스트2메뉴의 서브메뉴1", this,
SLOT(funcTest4()));
mnuTest2->insertItem("테스트2메뉴의 서브메뉴2", this,
SLOT(funcTest5()));
mnuTest3 = new QPopupMenu;
mnuTest3->insertItem("테스트3메뉴의 서브메뉴1", this,
SLOT(funcTest6()));
/* 각 popup menu들을 pulldown menu에 삽입시킨다. */
mnuMainBar = new QMenuBar(this);
mnuMainBar->insertItem(" 테스트1 ", mnuTest1);
mnuMainBar->insertItem(" 테스트2 ", mnuTest2);
mnuMainBar->insertItem(" 테스트3 ", mnuTest3);
}
clssTestMenu::~clssTestMenu()
{
}
void clssTestMenu::funcTest1()
{
emit printf("test1 메뉴\n");
}
void clssTestMenu::funcTest2()
{
emit printf("test2 메뉴\n");
}
void clssTestMenu::funcTest3()
{
emit printf("test3 메뉴\n");
}
void clssTestMenu::funcTest4()
{
emit printf("test4 메뉴\n");
}
void clssTestMenu::funcTest5()
{
emit printf("test5 메뉴\n");
}
void clssTestMenu::funcTest6()
{
emit printf("test6 메뉴\n");
}
-------------------------------------------------------------------
위의 결과치를 미리 나타내자면 다음과 같다.
---------------------------------------------------------
| 테스트1 | 테스트2 | 테스트3 |
|---------------------------------------------------------|
|| 테스트1메뉴의 서브메뉴1 | |
|| 테스트1메뉴의 서브메뉴2 | |
|| 테스트1메뉴의 서브메뉴3 | |
| ------------------------- |
| |
| |
| -------------------------- |
---------------- ---------------
뭐 그렇게 특별한 내용은 없다. 한번만 훑어보면 금방 이해가 갈 것이다.
/usr/lib/qt/include/qpopupmenu.h를 살펴보자.
public:
QPopupMenu( QWidget *parent=0, const char *name=0 );
별로 설명할 필요는 없으리라 생각한다. 중요한 것은 insertItem함수인데
header file을 찾아보면 알겠지만 이 함수가 member로 선언되어있지 않을
것이다. 그러면 어디에 선언이 되어 있을까?
QPopupMenu class의 선언부를 보자.
class Q_EXPORT QPopupMenu : public QTableView, public QMenuData
여기서 보면 QPopupMenu class는 QMenuData class로부터 상속을 받았다.
즉, QMenuData class의 public member들을 모두 QPopupMenu도 접근할 수
있다는 이야기다. 그러면 다시 /usr/lib/qt/include/pmenudata.h를 열어
보자. insertItem함수에 대한 선언이 다음과 같이 여러개가 선언되어있다.
int insertItem( const char *text,
const QObject *receiver, const char *member,
int accel=0 );
int insertItem( const QPixmap &pixmap,
const QObject *receiver, const char *member,
int accel=0 );
int insertItem( const QPixmap &pixmap, const char *text,
const QObject *receiver, const char *member,
int accel=0 );
int insertItem( const char *text,
const QObject *receiver, const char *member,
int accel, int id, int index = -1 );
int insertItem( const QPixmap &pixmap,
const QObject *receiver, const char *member,
int accel, int id, int index = -1 );
int insertItem( const QPixmap &pixmap, const char *text,
const QObject *receiver, const char *member,
int accel, int id, int index = -1 );
int insertItem( const char *text, int id=-1, int index=-1 );
int insertItem( const char *text, QPopupMenu *popup,
int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, QPopupMenu *popup,
int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, const char *text,
int id=-1, int index=-1 );
int insertItem( const QPixmap &pixmap, const char *text,
QPopupMenu *popup,
int id=-1, int index=-1 );
이제는 밑도끝도 없이 insertItem이라는 함수가 어디서 튀어나왔는지 이
해가 될 것이다. 앞으로도 member function이 이런 식으로 호출되는 경우
가 가끔씩 있을 것이다. 당황하지 말고 차근차근 이해하기 바란다.
insertItem 함수에 대한 설명을 하자. 바로 위에 있는 모든 경우를 다 설
명할 필요는 없을 것 같다. source 2.2.8.2에서 쓰인 insertItem함수에
대한 설명만을 하겠다. 그 source에서 쓰인 함수는 바로 이 함수이다.
int insertItem( const char *text,
const QObject *receiver, const char *member,
int accel=0 );
각 인자에 대해 설명하겠다.
첫번째 인자 < const char *text >는 Popup menu에 들어갈 Item의 이름이다.
두번째 인자 < const QObject *receiver >는 Item의 이름이 삽입되는 class
이다.
세번째 인자 < const char *member >는 이 Item이 사용자에 의해 선택되어
click되었을 때 실행할 receiver의 member function이다.
네번째 인자 < int accel >은 header source 에서 잠깐 설명한 hot-key(혹
은 accel-key)를 말한다. accel-key에 대해 약간만 더 알아보자.
/usr/lib/qt/include/qkeycode.h에 보면 모든 key값들이 이런식으로 선언
혹은 정의되어있다.
.
.
.
const uint SHIFT = 0x00002000; // accelerator modifiers
const uint CTRL = 0x00004000;
const uint ALT = 0x00008000;
const uint ASCII_ACCEL = 0x10000000;
#define Key_Escape 0x1000 // misc keys
#define Key_Tab 0x1001
#define Key_Backtab 0x1002
#define Key_Backspace 0x1003
#define Key_Return 0x1004
#define Key_Enter 0x1005
#define Key_Insert 0x1006
#define Key_Delete 0x1007
#define Key_Pause 0x1008
#define Key_Print 0x1009
#define Key_SysReq 0x100a
#define Key_Home 0x1010 // cursor movement
#define Key_End 0x1011
#define Key_Left 0x1012
.
.
.
위에서 보면 SHIFT, CTRL, ALT, ASCII_ACCEL등은 4byte unsigned int
형태로 정의되어있고 나머지 key값들은 모두 2byte형태로 선언되어있
다. 이는 특수확장키인 SHIFT, CTRL, ALT key와 나머지 key들을 조합
하여 쓸수 있다는 이야기인데 예를 들어 다시 한번 설명하겠다.
만약 사용자가 < 테스트1메뉴의 서브메뉴1 >을 단축키 ALT+ENTER로
설정하고 싶다면 다음과 같이 하면 된다.
mnuTest1->insertItem("테스트1메뉴의 서브메뉴1", this,
SLOT(funcTest1()), ALT|ENTER);
이젠 QMenuBar에 대해 살펴보자.
/usr/lib/qt/include/qmenubar.h에는 다음과 같이 선언하고 있다.
class Q_EXPORT QMenuBar : public QFrame, public QMenuData
{
friend class QPopupMenu;
Q_OBJECT
public:
QMenuBar( QWidget *parent=0, const char *name=0 );
.
.
.
};
아무리 찾아보아도 insertItem함수를 찾을 수 없다. 하지만 QPopupMenu
의 경우와 마찬가지로 QMenuData의 모든 public member들을 상속받고
있다.
그러므로 이에 대한 설명은 더이상 하지 않아도 되리라 믿는다.
-------------- < source 2.2.8.3 main_test7.cpp > ---------------
#include <qapplication.h>
#include "clssTestMenu.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestMenu *clssTest = new clssTestMenu();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.8 > ----------------------
PROGS = main_test7
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test7.o \
clssTestMenu_moc.o \
clssTestMenu.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test7
rm -f *_moc.*
############# COMPILE #############
main_test7.o: main_test7.cpp
$(CC) -c main_test7.cpp $(CFLAGS) -o $@
clssTestMenu_moc.cpp: clssTestMenu.h
$(MOC) clssTestMenu.h -o clssTestMenu_moc.cpp
clssTestMenu_moc.o: clssTestMenu_moc.cpp
$(CC) -c clssTestMenu_moc.cpp $(CFLAGS) -o $@
clssTestMenu.o: clssTestMenu.cpp
$(CC) -c clssTestMenu.cpp $(CFLAGS) -o $@
main_test7: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.2.9. 툴바(QToolBar), MainWindow(QMainWindow)
-------------- < source 2.2.9.1 clssTestToolBar.h > ---------------
#include <qmainwindow.h>
class QToolBar;
/* 이부분 < : public QMainWindow >을 주의 깊게 보기 바란다. */
class clssTestToolBar : public QMainWindow
{
Q_OBJECT
public:
clssTestToolBar();
~clssTestToolBar();
public slots:
void funcTest1();
void funcTest2();
void funcTest3();
protected:
private:
QToolBar *tbTest;
};
-------------------------------------------------------------------
-------------- < source 2.2.9.2 clssTestToolBar.cpp > ---------------
#include <qapplication.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qpixmap.h>
#include <stdio.h>
#include "clssTestToolBar.h"
#ifndef TEST1_TOOLTIP
/* ToolButton에 마우스를 갖다대었을 때 나올 풍선도움말을 선언한다. */
#define TEST1_TOOLTIP "첫번째 테스트"
#define TEST2_TOOLTIP "두번째 테스트"
#define TEST3_TOOLTIP "세번째 테스트"
#endif
clssTestToolBar::clssTestToolBar()
{
/* Icon를 저장할 QPixmap변수 선언 */
QPixmap iconTest1, iconTest2, iconTest3;
qApp->setStyle(WindowsStyle);
/* ToolBar에 들어갈 image들을 선언한 QPixmap변수에 Loading한다. */
iconTest1.load("./test1.gif");
iconTest2.load("./test2.gif");
iconTest3.load("./test3.gif");
tbTest = new QToolBar(this);
/* ToolBar의 방향은 수평이다. */
tbTest->setOrientation(tbTest->Horizontal);
/* ToolButton을 생성한다. */
QToolButton *tbttnTest1 = new QToolButton(iconTest1,
TEST1_TOOLTIP, 0, this, SLOT(funcTest1()),
tbTest, "test1");
QToolButton *tbttnTest2 = new QToolButton(iconTest2,
TEST2_TOOLTIP, 0, this, SLOT(funcTest2()),
tbTest, "test2");
QToolButton *tbttnTest3 = new QToolButton(iconTest3,
TEST3_TOOLTIP, 0, this, SLOT(funcTest3()),
tbTest, "test3");
}
clssTestToolBar::~clssTestToolBar()
{
}
void clssTestToolBar::funcTest1()
{
emit printf("첫번째 단축아이콘\n");
}
void clssTestToolBar::funcTest2()
{
emit printf("두번째 단축아이콘\n");
}
void clssTestToolBar::funcTest3()
{
emit printf("세번째 단축아이콘\n");
}
-------------------------------------------------------------------
/usr/lib/qt/include/qtoolbar.h를 살펴보자.
public:
QToolBar( const char * label,
QMainWindow *, QMainWindow::ToolBarDock = QMainWindow::Top,
bool newLine = FALSE, const char * name = 0 );
QToolBar( const char * label, QMainWindow *, QWidget *,
bool newLine = FALSE, const char * name = 0, WFlags f = 0 );
QToolBar( QMainWindow * parent = 0, const char * name = 0 );
여기서 주의깊게 보아야 할 부분이 한군데 있다. 바로 QMainWindow *parent
인자 부분이다. 이제까지 다뤘던 widget들은 parent 의 type이 모두 QWidget
이었다. 하지만 여기서는 QMainWindow이다. 그러면 계층정보를 좀 보자.
QMainWindow와 QToolBar 모두 QWidget의 하위 class로서 서로 형제간이다.
형제간에는 서로 종속될 수 있으므로 QToolBar의 parent를 QMainWindow로 정
의할 수 있다. QMenuBar또한 마찬가지이다. 이렇듯 class계층정보를 바탕으
로 상하관계를 설정해준다면 명확한 프로그래밍이 가능하게 된다.
/usr/lib/qt/include/qtoolbutton.h를 보자.
public:
QToolButton( QWidget * parent = 0, const char * name = 0 );
QToolButton( const QPixmap & pm, const char * textLabel,
const char * grouptext,
QObject * receiver, const char * slot,
QToolBar * parent, const char * name = 0 );
QToolButton( QIconSet s, const char * textLabel,
const char * grouptext,
QObject * receiver, const char * slot,
QToolBar * parent, const char * name = 0 );
source 2.2.9.2에서 쓰인 QToolButton 함수와 비교해서 설명하겠다.
QToolButton *tbttnTest1 = new QToolButton(iconTest1,
TEST1_TOOLTIP, 0, this, SLOT(funcTest1()),
tbTest, "test1");
이 함수는 header file에 선언된 세개의 함수중 두번째 함수가 쓰인
것이다.
두번째 함수의 첫번째 인자는 icon을 나타낸다. Qt에서는 여러가지
image file들 즉, bmp, gif, jpg, xpm 등등을 지원한다. 각 file들을
loading하는 방법은 약간씩 다르다. 이런 file들을 loading하는 방법
은 header file이나 혹은 Qt에서 제공하는 예제 프로그램들을 보면
금방 알 수 있을 것이다. gif와 jpg는 압축file이므로 먼저 Linux의
X-window에서 사용할 만한 format으로 변환시켜야한다. X-window에서
사용하는 file format은 xpm file인데 다음은 간단한 xpm file의 내
부를 vi로 열어본 것이다.
static char * Ant_xpm[] = {
"48 48 60 1",
" c None",
". c #492449244924",
"X c #618561856185",
"o c #9E799E799E79",
"O c #208120812081",
"+ c #514455555144",
"@ c #410341034103",
"# c #965896589658",
"$ c #082008200820",
"% c #8E388A288E38",
"& c #69A669A669A6",
"* c #71C675D671C6",
"= c #861782078617",
"- c #28A228A228A2",
"; c #104014511040",
": c #10400C300000",
"> c #410324921040",
", c #082004100000",
"< c #000000000000",
"1 c #71C63CF32081",
"2 c #8E38492428A2",
"3 c #208114511040",
"4 c #30C234D330C2",
"5 c #208110400820",
"6 c #492424921040",
"7 c #51442CB21861",
"8 c #38E320811040",
"9 c #104008200000",
"0 c #208114510820",
"q c #28A214510820",
"w c #79E741032081",
"e c #596530C21861",
"r c #10400C300820",
"t c #618534D32081",
"y c #30C218611040",
"u c #28A218610820",
"i c #79E7451428A2",
"p c #9658514430C2",
"a c #18610C300820",
"s c #186110400820",
"d c #30C21C711040",
"f c #49242CB21861",
"g c #104010400820",
"h c #59652CB21861",
"j c #492428A21861",
"k c #618538E32081",
"l c #30C21C710820",
"z c #69A634D32081",
"x c #20811C711861",
"c c #30C224921861",
"v c #30C22CB228A2",
"b c #186110401040",
"n c #410328A21861",
"m c #8617451428A2",
"M c #38E31C711040",
"N c #61855D755965",
"B c #69A638E32081",
"V c #410320811040",
"C c #51443CF330C2",
"Z c #514430C22081",
" . ",
" X. ",
" oOo ",
" +@ ",
" # $% ",
" %+ O ",
" &* =O ",
" O* =- ",
" o; =O ",
" &@ =-=X+= ",
" O. X:>,--# ",
" #<# *<12>3* = ",
" &4- ;56789<&$# ",
" o+$-= *0q<6,w,$;= o#",
" %;4* +8wq,ee<<<% *;%",
" %<O.#o #Xr8>tw<<<; o#.;+# ",
" %4% #+;<O% .9y<<u>,08<<<<<$-4# ",
" X;*$% &O<@5ipa<<<sd:O% ",
" +$% +$* *;fp8<,$$<<Xo ",
" @$% o$;o X4gh5<<7$#* %++-# ",
" #.$% oO. ;5jk9<<ld$ #X-;;.## ",
" #*.-<;;% o;.<2zu<<5y<* ;;+# ",
" .O.X=# =%=&X+6ia<,j4$;4# +- ",
" *@;<<$O+*x0<97yX=4<$#o=<= ",
" +;O4+&+O9<<<<ae0<= @<-.$O ",
" *$o %c8etd<<<vb<+ &<<$o ",
" %$* &nt2pe<<<$X@& &;+ ",
" o$- =07t2e<<5aOOX<@ %+ ",
" oO$o xt2qae,,79O<#X<+ ",
" @<= &>mtd<Mtjq<@< 4<* ",
" o&<+ NB7B<<<se<$+< o$;% ",
" +;4= :waa1,9jV5+X$ =<Oo ",
" #@$.# o9e9<d5j5,C -- @4 ",
"-<+# <7<,ue5<$% <& &$o ",
"o -5<8q:Z+o $ &;# ",
" o4$--&o +@ &$% ",
" o4$* <# ",
" =;O% %< ",
" o.<4o =4 ",
" o$<& ++ ",
" XO% -X ",
" ;o O# ",
" X@ $ ",
" ;= & ",
" &- ",
" -= ",
" %; ",
" %+ "};
약간 의외일 것이다. xpm file은 이처럼 C에서 바로 쓸 수 있는 char
형 pointer 변수인 것이다. 약간 감이 잡힐 것이다.
즉 압축 file인 gif를 QPixmap으로 Loading을 하게 되면 Loader는
이러한 char형 pointer로 변환을 해주고 그를 memory에 loading하게
된다. Windows System과 비슷하지 않은가? Windows에서도 이런 과정
을 거쳐 그림을 화면에 뿌려준다는 것은 웬만한 programmer라면 알
고 있을 것이다. 즉, gif나 jpg format을 Windows에서 접근 가능한
bmp로 변환시켜 memory에 loading한 다음 그를 화면에 나타낸다.
이런 bmp의 역할을 하는 것이 바로 Pixmap file인 xpm format file들
인 것이다. 이해가 됐을 지 모르겠다.
두번째 인자인 char 형 pointer인 textlabel은 Mouse Pointer가
ToolButton에 위치했을 때 나타낼 수 있는 Tooltip(풍선도움말)이다.
나머지 인자들은 지금까지 착실하게 source들을 이해했다면 설명할
필요가 없을 것 같아 생략하겠다.
-------------- < source 2.2.9.3 main_test8.cpp > ---------------
#include <qapplication.h>
#include "clssTestToolBar.h"
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
clssTestToolBar *clssTest = new clssTestToolBar();
clssTest->setGeometry(100, 100, 500, 350);
myapp.setMainWidget(clssTest);
clssTest->show();
return myapp.exec();
}
-------------------------------------------------------------------
---------------------- < Makefile 2.2.9 > ----------------------
PROGS = main_test8
LIBS = -L/usr/lib/qt/lib -L/usr/X11R6/lib -lqt -lXext -lX11 -lm
CC = g++
GCC = gcc
INCLUDE = -I/usr/lib/qt/include
C_FLAG = -pipe -DNO_DEBUG -O2
CFLAGS = $(INCLUDE) $(C_FLAG)
SYSCONF_LINK = $(CC)
SYSCONF_MOC = /usr/bin/moc
MOC = $(SYSCONF_MOC)
MNU_OBJS = \
main_test8.o \
clssTestToolBar_moc.o \
clssTestToolBar.o
all: $(PROGS)
clean:
rm -f *.o
rm -f main_test8
rm -f *_moc.*
############# COMPILE #############
main_test8.o: main_test8.cpp
$(CC) -c main_test8.cpp $(CFLAGS) -o $@
clssTestToolBar_moc.cpp: clssTestToolBar.h
$(MOC) clssTestToolBar.h -o clssTestToolBar_moc.cpp
clssTestToolBar_moc.o: clssTestToolBar_moc.cpp
$(CC) -c clssTestToolBar_moc.cpp $(CFLAGS) -o $@
clssTestToolBar.o: clssTestToolBar.cpp
$(CC) -c clssTestToolBar.cpp $(CFLAGS) -o $@
main_test8: $(MNU_OBJS) $(OTHER_OBJS)
$(CC) $(MNU_OBJS) $(OTHER_OBJS) $(LIBS) -o $@
-------------------------------------------------------------------
2.3. QT Library를 다운받으려면?
여러 Site가 있지만 가장 대표적인 Url은
< http://www.troll.no >
에서 다운받아 사용할 수 있다.
< 한마디 >.
지금까지 하나의 Project를 꾸미기 위해 그리 짧지만은 않은 과정을 거쳐왔습
니다.
기초 과정은 모두 끝난 것입니다. 아직 기초과정에 속한 network관련 Library
에 대한 설명의 화두도 나오지 않은 상태이지만 Project를 구현하기에는 충분한
실력을 쌓았다고 할 수 있겠죠. 기초 과정은 대략 설명이 끝났으므로 앞으로
나올 기초 부분에 연관된 code들에 대해서는 되도록이면 설명을 생략하도록 하
겠습니다.
===============================================================
"QT" 카테고리의 다른 글
- 기본적인 컴파일 방법 default compile (qmake) (0)2007/04/12
- QT Designer (0)2007/04/12
- QT를 사용한 X윈도우용 네트워크 관리툴 02 (0)2007/04/12
- QT를 사용한 X윈도우용 네트워크 관리툴 01 (0)2007/04/12

수안이의 컴퓨터 연구실



Leave your greetings.