tag:blogger.com,1999:blog-36637850778238686682024-03-14T16:17:47.082+09:00§ Bluekyu Blog §bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.comBlogger238125tag:blogger.com,1999:blog-3663785077823868668.post-20609970586827339982013-04-17T22:24:00.000+09:002013-04-17T22:42:28.875+09:00[Project Euler] Problem 92원문: <a href="http://projecteuler.net/problem=92">Problem 92</a><br />
<div style="background-color: white; border: 1px solid rgb(204, 204, 204); padding: 10px;">A number chain is created by continuously adding the square of the digits in a number to form a new number until it has been seen before. For example,<br />
<br />
44 -> 32 -> 13 -> 10 -> <b>1</b> -> <b>1</b><br />
85 -> <b>89</b> -> 145 -> 42 -> 20 -> 4 -> 16 -> 37 -> 58 -> <b>89</b><br />
<br />
Therefore any chain that arrives at 1 or 89 will become stuck in an endless loop. What is most amazing is that EVERY starting number will eventually arrive at 1 or 89. How many starting numbers below ten million will arrive at 89?</div><br />
<div style="background-color: white; border: 1px solid rgb(204, 204, 204); padding: 10px;">숫자 체인은 숫자의 각 자리의 제곱을 연속해서 더한 것으로 만들어지고, 새로운 수는 전에 보였던 것이 나올 때까지 만든다. 예를 들면,<br />
<br />
44 -> 32 -> 13 -> 10 -> <b>1</b> -> <b>1</b><br />
85 -> <b>89</b> -> 145 -> 42 -> 20 -> 4 -> 16 -> 37 -> 58 -> <b>89</b><br />
<br />
그러므로 1 또는 89에 도달한 모든 체인은 무한 루프에 갇히게 된다. 매우 놀라운 것은 <b>모든</b> 시작하는 수는 결국 1 또는 89에 도달하게 된다. 얼마나 많은 천만 미만의 시작하는 수가 89에 도달하는가?</div><br />
Brute force로, 모든 경우를 하나씩 테스트 하면 약 7분의 시간이 걸린다. 여기서 천만 미만의 수는 7자리의 수이기 때문에 최대 가능한 수는 81 * 7이다. 따라서 1 ~ 567까지의 chain만 미리 구한다면 그 이후의 수는 한 번의 chain 계산만으로 1 또는 89를 결정할 수 있다.<br />
<br />
따라서 이를 적용하면 1분 정도로 줄게 되고, 0 ~ 9까지의 제곱을 미리 계산하면 20초 정도를 더 줄일 수 있다. 추가적으로 in 연산자를 사용하지 않고 1 ~ 567의 배열(리스트)을 만들어서 index를 사용하여 접근하면 20초 미만까지 줄일 수가 있다. 최종적으로 아래 코드는 15초까지 줄인 결과이다.<br />
<br />
<a href='javascript:void(0)' onclick=this.nextSibling.style.display=(this.nextSibling.style.display=='none')?'block':'none';>Python</a><div style='display:none'><pre class="brush:py">l = [0] * 600 # result cache
c = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # square cache
# initialize
l[1] = 1
l[89] = 89
count = 0
def nextNumber(number):
s = 0
while number > 0:
s += c[number % 10]
number //= 10
return s
# create result cache
for start in range(1, 568):
n = start
while True:
n = nextNumber(n)
if l[n] == 1:
l[start] = 1
break
elif l[n] == 89:
count += 1
l[start] = 89
break
for start in range(568, 10000000):
if l[nextNumber(start)] == 89:
count += 1
print(count)
</pre><a href="javascript:void(0)" none="" onclick="this.parentNode.style.display=">접기</a></div><br />
<br />
추가적으로, 똑같은 알고리즘을 사용해서 C++로 구현하면 1초 이내로 시간을 줄일 수 있다! 이 문제를 풀면서, 파이썬의 경우 많은 횟수의 반복 연산에 대해서는 여전히 느린 것을 알 수 있었다. (혹은 파이썬을 아직도 제대로 못 짜는 것일 수도 있는데, 위에서 동일 알고리즘 내에서 더 개선이 가능한 지 모르겠다.)<br />
<br />
<a href='javascript:void(0)' onclick=this.nextSibling.style.display=(this.nextSibling.style.display=='none')?'block':'none';>C++</a><div style='display:none'><pre class="brush:cpp">#include <iostream>
const int cache_number = 81 * 8 + 1;
int square_cache[10] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}; // square cache
int chain_cache[cache_number] = {0}; // chain cache
int nextNumber(int number)
{
int s = 0;
while (number > 0) {
s += square_cache[number % 10];
number /= 10;
}
return s;
}
int main(void)
{
int count = 0;
// initialize
chain_cache[89] = 89;
chain_cache[1] = 1;
// create chain_cache square_cache
for (int i = 1; i < cache_number; i++) {
int n = i;
while (1) {
n = nextNumber(n);
if (chain_cache[n] == 1) {
chain_cache[i] = 1;
break;
} else if (chain_cache[n] == 89) {
chain_cache[i] = 89;
count++;
break;
}
}
}
for (int i = cache_number; i < 10000000; i++) {
if (chain_cache[nextNumber(i)] == 89)
count++;
}
std::cout<<count<<std::endl;
return 0;
}
</pre><a href="javascript:void(0)" none="" onclick="this.parentNode.style.display=">접기</a></div><br />
<br />
참고로, 이 문제의 Thread에서 한 가지 힌트를 얻을 수 있었는데, 1234, 4321, 1423, 12340, 10234 등의 수의 경우 모두 같은 chain을 갖는다. 즉, 7자리의 숫자 내에서 같은 숫자를 가지는 조합은 같은 chain을 가지므로 1번만 계산을 할 수가 있다. 이를 이용하면 파이썬에서도 약 1초 이내로 계산이 가능하다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-80401144355500183352012-11-12T21:44:00.001+09:002012-11-12T21:46:19.149+09:00마우스 가운데 클릭 해제 방법 및 관련 내용리눅스에서는 마우스의 가운데 클릭이 Copy / Paste 기능으로 작동을 한다. 이 기능을 누군가는 매우 강력한 기능 중에 하나라고도 하는데, 마우스 휠 속도가 빨라지거나 실수로 가운데 클릭을 하는 경우 매우 짜증나는 기능이 되는 것 같다.<br />
<br />
그런데 이 기능은 X 윈도우 자체의 기능이기 때문에 KDE나 GTK에서 따로 처리를 하지 않는 이상은 Paste 기능을 막을 수 없는 것 같다.<br />
(Paste만 없애는 방법을 찾아보았지만 없는 것 같다.)<br />
<br />
그래서 지금까지 찾은 대안 방법을 적어놓겠다.<br />
(<a href="http://maketecheasier.com/disable-middle-mouse-click-to-paste-feature-in-linux-quick-tips/2012/03/23">http://maketecheasier.com/disable-middle-mouse-click-to-paste-feature-in-linux-quick-tips/2012/03/23</a><br />
찾은 내용들 중 대부분이 위 글로 누가 정리를 해두었다.)<br />
<br />
<br />
<b>1. 가운데 클릭 Remapping 하기 (마우스 가운데 클릭 기능 완전히 없앰)</b><br />
<br />
xinput list | grep 'id='<br />
<br />
명령을 통해 자신의 마우스 id를 확인해내고,<br />
<br />
xinput get-button-map id<br />
<br />
위에서 알아낸 id로 위 명령을 치면 현재 버튼 매핑이 나온다. 여기에서 2번이 가운데 클릭으로 작동을 하고 있는데, ~/.Xmodmap 파일에 pointer = 1 0 3 4 5 와 같이 써두면 가운데 2번 키를 0으로 바꿨으므로 마우스 가운데 클릭이 작동하지 않는다.<br />
<br />
이 방법은 마우스 가운데 클릭 자체를 없애기 때문에 링크 클릭이나 기타 마우스 가운데 클릭 기능이 작동을 하지 않는다!!!<br />
<br />
<br />
<b>2. 파이어폭스에서 마우스 가운데 클릭 해제 (붙여넣기 및 URL 로딩만 없애기 가능)</b><br />
<br />
마우스 가운데 클릭 해제가 가장 필요한 프로그램이였는데, about:config에서 middle로 검색을 해보면 "middlemosue.contentLoadURL"과 "middlemouse.paste"가 있다. 전자는 클립보드에 URL이 있으면 이를 로딩하는 것이고, 후자는 붙여넣기 기능이다.<br />
<br />
이 두 값을 false로 바꾸어주면 기능이 해제가 된다.<br />
(크롬도 가능하고, 이는 맨 위의 링크에서 찾을 수 있다.)<br />
<br />
<br />
<b>3. GTK 패치 방법</b><br />
<br />
이 방법은 누가 GTK 자체를 패치해서 붙여 넣기 기능을 막은 것이다. GTK를 쓰지 않기 때문에 이 내용도 자세히 보지는 않았다.<br />
<br />
이 역시 맨 위의 링크에서 찾을 수 있다.<br />
<br />
<b>4. xclip 계속 비우는 방법</b><br />
<br />
이는 단순히 클립보드를 계속 비우는 방법인데, 클립보드를 쓰는 이유가 없어지기 때문에 별로 안 좋은 방법인 것 같다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com1tag:blogger.com,1999:blog-3663785077823868668.post-72300035943226200952012-10-31T19:07:00.002+09:002012-10-31T19:07:20.646+09:00파이썬 미리 컴파일하기 및 .pyc, .pyo 자동 제거하기파이썬의 경우 인터프리터 언어이기 때문에 특정 라인이 컴파일 되기 전까지는 에러가 있는 지를 모른다.<br />
그래서 오랜 시간이 걸리는 작업 후에 컴파일 오류가 나면 짜증이 날 수도 있다.<br />
<br />
파이썬 모듈의 경우에는 소스 컴파일을 한 후에 .pyc 파일을 만들고 나서 임포트 되기 때문에 컴파일을 미리 해볼 수 있다.<br />
<br />
이를 응용해서 파이썬을 미리 컴파일 할 수 있다.<br />
<br />
단, 문제점이 있다면, 문법 오류에 대해서만 확인이 가능하다. 즉, 변수명이 틀린 것과 같이 실행이 되었을 때에 확인이 가능한 경우에 대해서는 알 수가 없다.<br />
<br />
1. IDLE에서 모듈 컴파일 하기<br />
IDLE에서 .py 파일을 열고 나서 Run 메뉴에서 Check Module을 실행 시키면 된다.<br />
<br />
2. compiler package 사용<br />
import compile<br />
compile.compile('test_module.py', 'output.txt', 'exec')<br />
를 하면 모듈을 컴파일해서 output.txt 파일에 오류를 기록해준다.<br />
<br />
여기까지는 <a href="http://www.ehow.com/how_12175242_compile-python-code-errors-running.html">http://www.ehow.com/how_12175242_compile-python-code-errors-running.html</a>를 참고하였다.<br />
<br />
3. pycompile 사용<br />
한 가지 방법이 더 있는데, 리눅스에서는 pycompile (py3compile)이라는 프로그램이 있다.<br />
(윈도우즈에서는 파이썬 폴더의 실행 파일에 있을 것 같다.)<br />
<br />
pycompile [파일명 또는 폴더명]<br />
<br />
을 사용하면 모듈을 모두 컴파일 하여 .pyc 파일을 만들어준다.<br />
<br />
<br />
================<br />
<br />
추가적으로 .pyc나 .pyo를 한 번에 제거할 수 있는 방법이 있는데, pyclean (py3clean)을 이용하면 이를 자동으로 제거해준다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-9701065574651041702012-10-23T07:42:00.000+09:002012-10-23T07:43:04.729+09:00창을 최대화 시켜서 띄우기 (KDE)웹브라우저의 경우 최대화 시켜서 사용하는 경우가 대부분이다.<br />
<br />
따라서 이러한 창들은 최대화가 기본적으로 되어 있으면 매우 편하다.<br />
<br />
이를 적용하려면, 해당 프로그램 또는 창의 규칙에서 (시스템 설정 > 창 동작 > 창 규칙을 보면 규칙들이 있다.) "위치와 크기" 탭에서 "수평 최대화", "수직 최대화"에서 "초기에 적용하기"를 설정해주면 된다.<br />
<br />
그러면 창 시작 시에 최대화 해서 나타난다.<br />
(강제를 설정하면 무조건 최대화이므로, 작은 화면으로 줄일 수 없다.)bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-83312523438025605672012-10-23T07:35:00.001+09:002012-10-23T07:35:46.120+09:00듀얼(멀티)모니터에서 마우스 위치에 창 띄우기 (KDE)마우스 제스쳐나 단축키로 프로그램을 실행 시키는 경우가 많은데, 대부분 마우스가 있는 위치가 현재 보고자 하는 창의 위치다.<br />
<br />
그래서 마우스가 있는 위치에서 창의 띄우려고 한다면,<br />
<br />
설정 > 시스템 설정 > 창 동작 메뉴 > 창 동작 탭 > "마우스 위치의 화면 활성화하기" 를 클릭해주면 마우스가 있는 위치에서 창의 항상 나타난다.<br />
<br />
만일 해제가 되어 있다면, 현재 활성화 되어 있는 창이 있는 곳이나 화면에서 창이 나타난다.<br />
(마우스가 왼쪽화면에 있고 활성창이 오른쪽에 있다면 오른쪽에 뜬다.)bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-22095211323186826362012-07-02T19:25:00.001+09:002012-10-10T19:37:46.763+09:00KDE 압축 메뉴에 7z 및 bzip2 추가하기7z이 압축률이 좋아서 많이 쓰다보니 7z 압축 메뉴가 필요해서 추가했다. 그리고 7z은 파일의 권한을 보존할 수가 없기 때문에 bzip2를 대신해서 사용해야 하므로 이것도 추가를 하였다.<br />
<br />
/usr/share/kde4/services/ServiceMenus/ 폴더 내에 있는 ark_addtoservicemenu.desktop를 수정하면 된다.<br />
<br />
콘솔 창을 열고 위 폴더로 이동 한 후에, 위 파일을 아래와 같이 수정하자.<br />
(sudo kate ark_addtoservicemenu.desktop)<br />
<br />
<pre class="brush:text">[Desktop Entry]
Type=Service
ServiceTypes=KonqPopupMenu/Plugin
MimeType=all/all;
Actions=compressHere;compressAsZip;compressAsRar;compressAsTar;compressAs7z;compressAsBz2;compressTo;
(중략)
[Desktop Action compressAs7z]
Name=As 7Z Archive
Name[en_GB]=As 7Z Archive
Name[ko]=7Z 파일로
Icon=ark
Exec=ark --changetofirstpath --add --autofilename 7z %F
[Desktop Action compressAsBz2]
Name=As BZ2 Archive
Name[en_GB]=As BZ2 Archive
Name[ko]=BZ2 파일로
Icon=ark
Exec=ark --changetofirstpath --add --autofilename tar.bz2 %F
</pre><br />
Actions 부분에 compressAs7z;compressAsBz2;을 추가하고, 중략 아래 부분을 새로 추가해주면 된다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-74906074936066971272012-06-30T17:31:00.000+09:002012-06-30T17:31:24.045+09:00파이썬 3.1 변경점 훑어본 것 정리그냥 훑어보기로 읽은 것을 정리한 것이므로 내용도 없고, 빠진 것도 있다.<br />
<br />
<br />
<br />
1. Ordered Dictionaries 추가<br />
순서 있는 사전 객체 추가. collections.OrederedDict 사용.<br />
<br />
<br />
2. 천 단위 분리자를 위한 포맷 지정자 추가<br />
format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]<br />
<br />
포맷 스펙은 위와 같고, format(1234, ',d')는 1,234 이다.<br />
<br />
<br />
3. 언어 변경점<br />
1) __main__.py 에 디렉토리나 zip 아카이브의 경우 바로 실행이 가능해짐.<br />
<br />
2) int 객체에 bit_length 추가. 비트로 변환 했을 경우의 길이를 반환함.<br />
<br />
3) format() 에 대해서 순서 지정 없이 자동으로 채워지는 기능 추가.<br />
'{} {}'.format(1, 2)는 '1 2'<br />
<br />
4) string.maketrans() 함수 폐기. bytes.maketrans() 와 bytearray.maketrans()로 대체됨.<br />
<br />
5) with 문에서 다중 context manager가 가능해짐.<br />
with A as B, C as D:<br />
...<br />
<br />
6) round(x, n) 함수가 x가 int일 경우 int 객체 리턴.<br />
<br />
7) 파이썬의 소수점 출력 관련 알고리즘 변경<br />
<br />
<br />
4. 새로운 점, 향상된 점, 폐기된 모듈<br />
1) collection.Counter 클래스 추가. 동일한 값의 개수를 세서 사전 객체로 리턴.<br />
<br />
2) tkinter.ttk 모듈 추가. Tk 테마 위젯 세트에 접근을 위한 것.<br />
<br />
3) gzip.GzipFile, bz2.BZ2File 클래스가 context manager protocol 지원.<br />
<br />
4) decimal 모듈이 이진 float 생성을 지원.<br />
<br />
5) itertools에서 itertools.combinations_with_replacement()와 itertools.compress() 함수 추가.<br />
<br />
6) collections.namedtuple()이 rename 키워드 인자를 지원.<br />
<br />
7) re.sub(), re.subn() 그리고 re.split()이 flag 인자를 지원.<br />
<br />
8) logging 모듈이 logging.NullHandler을 지원.<br />
<br />
9) runpy 모듈이 -m 커맨드를 지원<br />
<br />
10) pdb 모듈이 zipimport를 통해 로드된 소스 코드를 보여줄 수 있음.<br />
<br />
11) functools.partial가 pickled 될 수 있음.<br />
<br />
12) pydoc에 기호에 대한 topic을 도와주기를 추가...?<br />
<br />
13) unittest 모듈이 독립적인 테스트 또는 테스트의 클래스를 넘기는 것을 지원.<br />
<br />
14) io 모듈에서 seek()의 SEEK_SET, SEEK_CUR 그리고 SEEK_END를 위한 상수를 지원<br />
<br />
15) sys.version_info 튜플이 named tuple로 변경<br />
<br />
16) nntplib와 imaplib가 IPv6를 지원<br />
<br />
17) importlib 모듈이 추가됨.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-34478739797215784522012-06-30T16:33:00.001+09:002012-06-30T16:33:55.465+09:00테스팅과 디버깅 방식 정리종종 큰 프로그램을 만들게 되면 테스팅과 디버깅이 버거워지는데, 매번 관련 항목을 찾기 힘들어서 여기에다가 정리해둔다.<br />
<br />
때가 된다면 각 항목에 대해 더 자세히 작성하면 좋겠지만, 정리가 귀찮아서 못하고 있다.<br />
<br />
*** 개인적으로 적절한 방식이라고 생각되는 것을 적은 것이기 때문에, 전문적인 방식이 아닐 수 있다!<br />
<br />
<br />
1. 디버거(Debugger) 사용<br />
가장 기본적인 방식으로 디버거를 이용하여 단계적으로 실행을 하여 분석하는 방법.<br />
<br />
1) C & C++: IDE 내장 디버거 또는 GDB<br />
2) Python: PDB 사용<br />
<br />
<br />
<br />
2. 디버깅 모드와 릴리즈 모드를 구분하여 런타임에 값을 출력<br />
매우 심플하면서도 디버거를 모르는 경우에 자주 하는 방식으로, 콘솔로 값을 Print하는 방식이다.<br />
단, 여기서는 디버깅 모드를 사용할 때에만 값을 출력하게 끔 하여, 릴리즈 시에는 이들을 무시하는 방법이다.<br />
<br />
1) C & C++<br />
컴파일 시에 디버깅 모드일 경우에는 __DEBUG__ 매크로를 정의하고, 릴리즈 모드일 경우에는 이를 정의하지 않음으로써 아래 구문이 컴파일 되거나 되지 않도록 할 수 있다.<br />
<pre class="brush:c">#ifdef __DEBUG__
printf("디버깅 모드");
#endif
</pre><br />
2) Python<br />
파이썬의 경우에는 매크로를 지원하지 않을 뿐더러 컴파일 언어가 아니기 때문에 이 방식은 의미가 없다. 굳이 한다면 if 문을 사용할 수 있지만, 무조건 이를 실행하게 되므로 성능 상에서 불리한 점이 있다.<br />
<br />
<br />
<br />
3. 로깅(Logging) 방식<br />
본격적으로 현재 상태를 기록하는 방식이다. 프로그램이 실행됨에 따라서 파일이나 콘솔에 현재 상태를 기록 또는 출력한다.<br />
<br />
1) C & C++<br />
C++ 관련 라이브러리에서 표준적으로 많이 쓰는 Logging 라이브러리를 찾을 수 없었다. 따라서 Logging을 한다면 제 3자 라이브러리를 쓰거나, 따로 Logging 라이브러리를 만들어서 써야 한다.<br />
<br />
아주 간단한 방식으로는 logging 클래스를 생성하고 logging 클래스에서 파일을 열고, 이 클래스에서 write를 지원하면 된다.<br />
<br />
2) Python<br />
Logging 모듈을 지원하므로 이를 쓰면 된다. 더욱이 위에서 디버깅 모드와 같은 방식을 사용하기 힘들기 때문에 로깅 모듈을 사용해야 한다.<br />
<br />
다행히도, 실행 시에 --log 옵션을 통해서 로깅의 Level을 지정할 수 있기 때문에 디버깅 모드와 비슷한 방식을 사용할 수 있다.<br />
<br />
예를 들어, 일반 실행 중에는 기본 로그 수준을 사용하다가, 디버깅 시에는 --log=DEBUG와 같이 옵션을 주어서 실행되지 않던 로깅 구문들도 작동을 하도록 하면 된다.<br />
<br />
그리고 콘솔로 출력하려면 파일 출력 대신에 sys.stdout 출력을 사용하면 된다.<br />
<br />
<br />
<br />
4. 단위 테스트(Unit Test) 방식<br />
디버깅이 아닌 테스팅 방식으로 작성된 기능이 의도된 대로 작동하는지 테스트 하는 방식이다.<br />
<br />
1) C & C++<br />
C++의 경우에는 다양한 단위 테스트 Framework가 존재한다. 이를 가져다 쓰면 된다.<br />
<br />
2) Python<br />
unittest 모듈을 사용하면 간단히 해결된다.<br />
<br />
<br />
<br />
5. 덤프 파일 생성<br />
가장 Low-Level에 해당하는 방식으로 메모리에 있는 현재 상태를 그대로 파일로 만드는 방식. 생성된 덤프 파일을 분석해서 현재 값이 어떤 지를 분석할 수 있다.<br />
<br />
하지만 어렵고 복잡하므로 지금은 패스.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-76664267294234382982012-06-18T00:08:00.001+09:002012-06-30T16:48:18.845+09:00github 아이콘 깨짐 해결하기오늘 github에서 아이콘 모양이 깨지는 현상을 발견했다.<br />
<br />
기본적으로 나와야 할 저장소 모양 아이콘이나 폴더 아이콘, 파일 아이콘 등이 나오지 않고 이상한 문자 (또는 한글)이 나온다.<br />
<br />
처음에 왠 한글이 나와서 놀랬는데, 알고 보니 아이콘이 계속 깨지고 있던 것이었다.<br />
<br />
예를 들면 아래와 같이 아이콘이 깨져 보인다.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA2bc93yhDcp8ZBUbP2rJcazangna8i9HN0NShTpma9gT3cP65sreyLN-kD6XAh59hZ5WiQuQ_Z10P_Jq5PW-4o_FL1LO2fVmkY-ZyEj7P3fmvJkJeY45XTLrUqyYCA4S5xb0b2ZWhGgsd/s1600/image12.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="47" width="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA2bc93yhDcp8ZBUbP2rJcazangna8i9HN0NShTpma9gT3cP65sreyLN-kD6XAh59hZ5WiQuQ_Z10P_Jq5PW-4o_FL1LO2fVmkY-ZyEj7P3fmvJkJeY45XTLrUqyYCA4S5xb0b2ZWhGgsd/s400/image12.png" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD741hPGiRHTV6R-itHG2MUR5cNCx37uWNsYrjzp7JJWFjSE1psyo5R9idySyKSYKj93RRifXTveMmiA2O-00oR4T7DoBI8x-kvK9tthWgZOwpGsm3UNrWcfwuRez7SKKmFfVucsIRaIBh/s1600/image13.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="219" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD741hPGiRHTV6R-itHG2MUR5cNCx37uWNsYrjzp7JJWFjSE1psyo5R9idySyKSYKj93RRifXTveMmiA2O-00oR4T7DoBI8x-kvK9tthWgZOwpGsm3UNrWcfwuRez7SKKmFfVucsIRaIBh/s400/image13.png" /></a><br />
(이 스크린샷은 Nabi 프로젝트의 github 화면이다.)<br />
<br />
여기서 문제는 이 아이콘들은 이미지가 아니라 글자였다. 유니코드에서 개인적으로 사용이 가능한 범위 내에 있는 코드를 이용하여 글자 형식의 아이콘들을 만들어 둔 것이다.<br />
<br />
그래서 이 문제를 해결하려고 파이어폭스 버전을 낮추어보기도 했고, 바이너리 버전을 받아서 써보기도 했지만 전혀 해결되지 않았다.<br />
<br />
마지막으로 폰트 변경을 해보았는데, 일부 한글 폰트는 위와 같이 나오고, 영문 폰트는 또 다르게 나오는 것이였다. 그래서 "웹에서 사용하는 폰트 허용"을 체크하니 문제가 해결되었다.<br />
(기존에는 은 돋움을 기본 폰트로 계속 쓰려고 이를 해제하고 있었다.)<br />
<br />
즉, 폰트 내에 위 글꼴이 당연히 없으니 깨져서 나오고 있던 것이다. 그리고 실제로도, github에서 커스텀 폰트로 octicons 라는 글꼴을 사용하고 있으며, 이들 폰트에서 Private 유니코드 범위 내에 글꼴을 추가하여 사용하고 있다는 것을 알았다.<br />
<br />
ps. 예전에 웹 폰트라는 것을 들은 적이 있었는데, 찾아보니 CSS를 이용한 기술이라는 것을 알았다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-23704205200248683222012-06-17T23:51:00.001+09:002012-06-18T00:16:02.401+09:00Muon에서 설치할 패키지 버전 지정하기파폭 때문에 열심히 삽질을 하다가 한 가지 발견한 것이 있어서 여기에 남겨둔다.<br />
<br />
Muon 패키지 관리자에서 설치할 패키지 버전을 강제로 지정할 수 가 있다.<br />
<br />
공식 저장소를 쓰는 경우에는 이러한 경우가 없는데, 업데이트 저장소나 백포트 저장소까지 쓰게 되면 여러 패키지 버전이 발생하게 된다.<br />
<br />
이 경우 사용하고 싶은 패키지의 버전을 강제로 지정할 수 있다.<br />
<br />
Firefox의 경우 현재 업데이트 저장소에는 13버전이 등록되어 있고, 일반 저장소에는 11버전이 등록되어 있다.<br />
<br />
이때, 패키지를 클릭하고 아래에서 Version (버전) 탭을 보면 설치할 수 있는 패키지 버전이 보인다. 여기서 11 버전을 더블클릭 하여 오른쪽에 Force Version이 활성화 되고, 이 버튼을 누르면 패키지가 다운그레이드 되어진다.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnOYwGdKRJQYcx4u1Pwr7BqTugaoOohbFK_cGsK8yNeb-3nscrdrUZ0_QpsuaxlEPcoIxZ1rwJ6CVonLH_8sw5AZpt-EtBU_c3ECNEPZYmGthRkGb_4JmIXVN2Xda78TE3_zXvzZ5y3haS/s1600/image11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="123" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnOYwGdKRJQYcx4u1Pwr7BqTugaoOohbFK_cGsK8yNeb-3nscrdrUZ0_QpsuaxlEPcoIxZ1rwJ6CVonLH_8sw5AZpt-EtBU_c3ECNEPZYmGthRkGb_4JmIXVN2Xda78TE3_zXvzZ5y3haS/s640/image11.png" width="640" /></a>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-3968766084222960142012-05-04T12:39:00.000+09:002012-05-04T12:46:17.710+09:00Kubuntu 12.04 업그레이드 후기어제 Kubuntu 12.04로 업그레이드를 하였다. 바로 나온 후에 업그레이드를 하지 않고 조금 기다렸다가 했는데, 약간의 문제들이 해결되고 업데이트가 된 후에 하려고 기다렸다.<br />
<br />
그런데 업그레이드 시에 이번에도 문제가 발생하였는데, 이번에는 패키지 의존성이 엉키는 오류가 발생했다.<br />
두 패키지가 서로 의존성을 가지게 되면 패키지를 동시에 설치를 해주어야 하는데, 이것이 제대로 처리가 되지 못해서 오류가 발생했다.<br />
<br />
<br />
일단, 업그레이드 중간 중간 오류가 발생할 수도 있는데, 이때는 침착하게 콘솔창을 열고 sudo apt-get install -f를 해주자. 그러면 다시 설치가 진행 될 것이다.<br />
만약 안된다면, sudo apt-get upgrade -f를 해주고 업그레이드를 진행하거나 sudo apt-get check -f 를 해주어서 의존성을 제대로 재설정 하면 된다.<br />
<br />
<br />
그런데 이래도 안되는 경우가 존재 했었는데, vlc 관련 패키지에서 완전히 문제가 발생했다. vlc 패키지의 경우 위의 명령들을 입력해도 해결되지 않았다. 그래서 이때에는 muon 패키지 관리자를 열어서 vlc 패키지를 지웠는데, 패키지를 지우면서 의존성 관련 패키지들을 모두 삭제하였다.<br />
이렇게 한 후에 설치를 재시작 하니 다시 제대로 되었다.<br />
<br />
<br />
이렇게 해서 업그레이드를 간신히 완료했는데, 재부팅 하고 나서 보니 약간의 최적화가 안되어 있는 느낌이였고, 폰트 역시 이상하게 변경이 되어 있었다.<br />
<br />
먼저, 최적화를 하기 위해서 패키지를 일부 삭제하였다. 가장 용량을 많이 차지하고 있던 리눅스 커널 헤더와 이미지를 삭제했다. 특히 이전까지는 3.0.0.19 버전을 쓰고 있었고, 그 이전 커널들은 전혀 사용하지 않기 때문에 이를 모두 지웠다.<br />
<br />
이렇게 하면 커널 버전이 2개만 남개된다. 이전 11.10에서 사용하던 3.0 버전과 현재 3.2 버전이 남게 된다.<br />
이 정도만 해도 약 1G 정도를 비울 수 있다.<br />
<br />
그 다음으로 xterm 패키지와 기타 패키지들도 삭제를 했다. 이전에서는 xterm 을 삭제하려고 하면 다른 의존 패키지들도 삭제가 되어서 할 수 없었는데, 이제는 그런게 없어졌는지 삭제가 가능했다.<br />
<br />
<br />
그리고 폰트 문제가 발생했는데, 분명히 11.10과는 폰트 모양이 매우 달라보였다. 설정은 그대로인데, 글씨가 약간 작아지면서 깨져보였다. 아마, 우분투 쪽에서 폰트를 좀 더 깔끔하게 변경하면서 이전 설정들이 문제를 일으킨 것 같다.<br />
<br />
설정을 바꿔서 해결을 해보려고 했는데, 잘 되지는 않았다. 그래서 검색을 통해서 알아보니, 안티앨리어싱을 사용함으로 하고, 힌팅 스타일을 살짝으로 하고 서브픽셀 렌더링을 RGB로 사용했다. 이렇게 하니 글씨가 훨씬 깔끔해졌다.<br />
<br />
특히, 큰 크기의 글씨에서는 한영 모두 이전보다 깔끔하게 보인다. 다만, 작은 글씨에서는 이전보다 약간 뭉게져 보이는 듯한 느낌이 있다.<br />
(이를 해결하려면 글씨 기본 크기들을 키우는 방법이 있는데, 기본 글씨 크기를 10, 작은 글씨 크기들은 9로 하니, 이전보다는 크기가 약간 커진 듯하지만 글씨 모양은 매우 깔끔해졌다.)<br />
<br />
<br />
마지막으로 약간의 속도를 높이기 위해서 데스크톱 효과의 고급 탭에서 "크기 조절 방법"을 "빠르게"로 변경하고 "모든 효과" 탭에서 일부 효과들을 해제하고 난 후 훨씬 속도가 향상되었다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-14071334312966986492012-04-25T00:47:00.001+09:002012-04-25T00:47:34.506+09:00파이썬 부동 소수점(Floating Point) 문제(Issue) 파악KLDP에서 파이썬의 부동 소수점과 관련한 스레드가 올라왔다: <a href="http://kldp.org/node/132646">http://kldp.org/node/132646</a><br />
그래서 이에 대해서 찾아보다가 얻게 된 결론이다.<br />
<br />
<pre class="brush: py">>>> (0.05 + 0.05 + 0.05).hex()
'0x1.3333333333334p-3'
>>> (0.15).hex()
'0x1.3333333333333p-3'
>>> float.fromhex('0x0.0000000000001p-3')
2.7755575615628914e-17
>>> Decimal(0.15) - Decimal(0.05 + 0.05 + 0.05)
Decimal('-2.775557561562891351059079170E-17')
</pre>1번 실행은 0.05를 세 번 더한 hex 값 표현을 나타낸다. 2번 실행은 0.15 자체의 hex 값을 나타낸다. 그런데 두 hex 값의 오차가 발생한다.<br />
<br />
이를 직접 빼서 다시 부동 소수점으로 환산해보면 그 오차를 알 수 있다.<br />
<br />
그리고 파이썬에서 Decimal 모듈을 지원하는데, 이 모듈은 정확한 소수 연산을 수행해준다. 즉, Decimal(5)/Decimal(100)과 같이 쓰면 정확히 5/100 을 나타내고, 이를 20번 빼면 정확히 0과 같이 나타내준다.<br />
<br />
그래서 이를 사용해서 빼보면 4번째 실행이 나온다. 즉, hex 의 결과와 같은 것을 알 수 있다. 따라서 오차 계산이 정확히 된 것을 알 수 있다.<br />
<br />
그리고 0.1 - (0.05 + 0.05)의 경우 0으로 정확히 나오는데, 아래와 같이 hex 값이 같기 때문이다.<br />
<pre class="brush:py">>>> 0.1.hex()
'0x1.999999999999ap-4'
>>> (0.05 + 0.05).hex()
'0x1.999999999999ap-4'
>>> Decimal(0.1) - Decimal(0.05 + 0.05)
Decimal('0E-55')
</pre><br />
또, 추가적으로 0.15 - 0.05 - 0.05 - 0.05 와 0.15 - (0.05 + 0.05 + 0.05)의 오차 결과는 또 다르다!<br />
<pre class="brush:py">>>> 0.15 - 0.05 - 0.05 - 0.05
-1.3877787807814457e-17
>>> 0.15 - (0.05 + 0.05 + 0.05)
-2.7755575615628914e-17
</pre><br />
따라서 스레드의 질문을 정확히 분석하자면 0.05의 hex 값을 1.0의 hex 값에서 모두 뺌으로써 마지막 결과의 이유를 알 수 있을 것이다.<br />
그리고 0.05 - 0.05의 경우 같은 hex 값을 갖는 수를 빼는 것이므로 당연히 0이 나오게 될 것이다.<br />
<br />
참고자료:<br />
<a href="http://docs.python.org/tutorial/floatingpoint.html">http://docs.python.org/tutorial/floatingpoint.html</a><br />
<a href="http://docs.python.org/library/stdtypes.html#float.hex">http://docs.python.org/library/stdtypes.html#float.hex</a>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-14037150198685654322012-04-06T12:30:00.001+09:002014-05-25T15:42:54.437+09:00C에서 고해상도(High Resolution) 타이머 사용 방법<h3>1. 윈도우즈</h3>
QueryPerformanceCounter와 QueryPerformanceFrequency를 이용하면 된다.
<pre class="brush: c">
#include <windows.h>
int main(void)
{
LARGE_INTEGER start, end, frequency;
double runTime;
QueryPerformanceFrequency(&frequency); // 초당 틱 수
QueryPerformanceCounter(&start); // 시작 틱의 개수
... // 수행할 내용
QueryPerformanceCounter(&end); // 종료 틱의 개수
runTime = ((double)(end.QuadPart - start.QuadPart) / freq.QuadPart) * 1000; // 단위는 ms
}
</pre>
<br />
<h3>2. 리눅스</h3>
<b>1) gettimeofday 이용 방법 - Wall-clock Time 방식</b><br />
Reference 2번과 man page의 "conforming to"에 따르면 이 함수는 <b>폐기예정(obsolete)</b>로 지정 되었다. 대신 clock_gettime을 사용을 권장하고 이는 nano seconds 까지 측정 할 수 있다. clock_gettime의 사용법은 3번 항목에 적어두었다.
<pre class="brush: c">
#include <sys/time.h>
int main(void)
{
struct timeval start, end;
double runTime;
gettimeofday(&start, NULL); // 시작 시간
... // 수행할 내용
gettimeofday(&end, NULL); // 종료 시간
runTime = (end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_usec - start.tv_usec) / 1000.0; // 단위는 ms
}
</pre>
<b>2) getrusage 이용 방법 - Process Time 방식</b>
<pre class="brush: c">
#include <sys/time.h>
#include <sys/resource.h>
int main(void)
{
struct timeval start, end;
struct rusage usage;
double runTime;
getrusage(RUSAGE_SELF, &usage);
// start = usage.ru_stime; // 이것은 system CPU time
start = usage.ru_utime; // 이것은 user CPU time
... // 수행할 내용
getrusage(RUSAGE_SELF, &usage);
// end = usage.ru_stime;
end = usage.ru_utime;
runTime = (end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_usec - start.tv_usec) / 1000.0; // 단위는 ms
}
</pre>
<b>3) clock_gettime 이용 방법 - Wall-clock Time 및 Process Time 방식</b><br />
<b>주의</b>: 링크 시에 rt 라이브러리가 필요하다. gcc에서는 -lrt 옵션을 주면 된다.
<pre class="brush: c">
#include <time.h>
int main(void)
{
struct timespec start, end;
double run_time;
clock_gettime(CLOCK_REALTIME, &start); // Wall-clock time
// clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); // Process time
... // 수행할 내용
clock_gettime(CLOCK_REALTIME, &end); // Wall-clock time
// clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end); // Process time
run_time = (end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_nsec - start.tv_nsec) / 1000000.0; // 단위는 ms
}
</pre>
<b>Reference</b>
<ol>
<li><a href="http://www.songho.ca/misc/timer/timer.html">http://www.songho.ca/misc/timer/timer.html</a></li>
<li><a href="http://sunyzero.tistory.com/161">http://sunyzero.tistory.com/161</a></li>
</ol>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-28547302534757692492012-03-31T16:20:00.001+09:002012-03-31T16:20:56.601+09:00이미지 및 PDF 유틸리티이미지를 변환하거나 pdf로 만들거나, 여러 변환 작업을 하는 데 대표적인 유틸리티로 ImageMagick 이 있다.<br />
<br />
이미지 처리에서는 매우 강력한 도구이면서 자유 소프트웨어이기 때문에 무료로 사용이 가능하다. 또한 리눅스에서도 패키지를 제공하기 때문에 쉽게 사용이 가능하다.<br />
<br />
설치는 ImageMagick 패키지를 검색하여 설치하면 된다.<br />
<br />
예시 사용법은 아래와 같다.<br />
<pre class="brush: bash">Convert input.png output.png
Convert img1.jpg img2.jpg output.pdf
Convert input1.pdf input2.pdf output.pdf
</pre>이 외에도 여러 기법들이 있으므로 필요하다면 man convert나 ImageMagick의 사용법을 찾아보면 된다.<br />
<br />
그런데 이미지 처리 프로그램이기 때문에 pdf를 그림으로 처리하기 때문에, 다른 크기의 pdf에 대해서는 해상도 문제가 발생할 수 있다.<br />
이를 대체할 수 있는 프로그램으로 pdftk라는 툴이 있다. 이를 사용하면, pdf를 해상도 문제 없이 깔끔하게 변환이 가능하다.<br />
<br />
설치는 pdftk 패키지를 설치하면 되고, 사용법은 아래와 같다.<br />
<pre class="brush: bash">pdftk input1.pdf input2.pdf cat output output.pdf
</pre>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-22292373729317379152012-03-19T13:00:00.002+09:002012-03-19T13:00:29.040+09:00[Project Euler] Problem 33원문: <a href="http://projecteuler.net/problem=33">Problem 33</a><br />
<div style="background-color: white; border: 1px solid rgb(204, 204, 204); padding: 10px;"><p>The fraction <sup>49</sup>/<sub>98</sub> is a curious fraction, as an inexperienced mathematician in attempting to simplify it may incorrectly believe that <sup>49</sup>/<sub>98</sub> = <sup>4</sup>/<sub>8</sub>, which is correct, is obtained by cancelling the 9s.</p><p>We shall consider fractions like, <sup>30</sup>/<sub>50</sub> = <sup>3</sup>/<sub>5</sub>, to be trivial examples.</p><p>There are exactly four non-trivial examples of this type of fraction, less than one in value, and containing two digits in the numerator and denominator.</p><p>If the product of these four fractions is given in its lowest common terms, find the value of the denominator.</p></div><br />
<div style="background-color: white; border: 1px solid rgb(204, 204, 204); padding: 10px;"><p>분수 <sup>49</sup>/<sub>98</sub>는 특이한 분수이다. 단순하게 만드는 시도에서 경험이 부족한 수학자는 <sup>49</sup>/<sub>98</sub> = <sup>4</sup>/<sub>8</sub>이 9를 소거하여 얻는 것이 맞는 것이라고 부정확하게 믿게 된다.</p><p>우리는 단순한 예제로써, <sup>30</sup>/<sub>50</sub> = <sup>3</sup>/<sub>5</sub>과 같은 분수를 생각할 수도 있다.</p><p>이러한 분수의 유형으로, 값이 1보다 작고, 분자와 분모에 2자리 숫자를 포함하는 '단순하지 않은' 예제가 정확히 4개 있다.</p><p>이 4개의 분수의 곱이 최소 공통 항으로 주어진다면, 분모의 값을 찾아라.</p></div><br />
<a href='javascript:void(0)' onclick=this.nextSibling.style.display=(this.nextSibling.style.display=='none')?'block':'none';>Python</a><div style='display:none'>약간의 수학을 가미해서 풀었다.<br />
<br />
10x+y / 10z+x = y / z와 10y+x / 10x+z = y / z라는 식을 만족하면 되는 x, y, z를 찾으면 된다. 이때, x, y, z는 1 이상 9 이하이고, 그 결과가 1보다 작아야 하므로 y >= z 가 될 수 없다. (10y+x / 10z+x = y / z 와 같은 식도 있는데, 이 경우는 x가 0으로, 위에서 말한 단순한 예제에 속한다.)<br />
<pre class="brush:py">import fractions
result = [1, 1]
for x in range(1, 10):
for y in range(1, 10):
for z in range(1, 10):
if y >= z:
continue
numerator = 10 * x + y
denominator = 10 * z + x
if numerator * z == denominator * y:
result[0] *= numerator
result[1] *= denominator
numerator = 10 * y + x
denominator = 10 * x + z
if numerator * z == denominator * y:
result[0] *= numerator
result[1] *= denominator
print(fractions.Fraction(*result).denominator)
</pre><a onclick=this.parentNode.style.display='none'; href=javascript:void(0)>접기</a></div>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com1tag:blogger.com,1999:blog-3663785077823868668.post-90191588376020972562012-03-19T00:48:00.003+09:002012-03-19T00:51:14.759+09:00[Project Euler] Problem 48원문: <a href="http://projecteuler.net/problem=48">Problem 48</a><br />
<div style="background-color: white; border: 1px solid rgb(204, 204, 204); padding: 10px;">The series, 1<sup>1</sup> + 2<sup>2</sup> + 3<sup>3</sup> + ... + 10<sup>10</sup> = 10405071317.<br />
Find the last ten digits of the series, 1<sup>1</sup> + 2<sup>2</sup> + 3<sup>3</sup> + ... + 1000<sup>1000</sup>.<br />
</div><br />
<div style="background-color: white; border: 1px solid rgb(204, 204, 204); padding: 10px;">다음과 같은 급수가 있다. 1<sup>1</sup> + 2<sup>2</sup> + 3<sup>3</sup> + ... + 10<sup>10</sup> = 10405071317.<br />
다음 급수의 마지막 열 자리를 찾아라. 1<sup>1</sup> + 2<sup>2</sup> + 3<sup>3</sup> + ... + 1000<sup>1000</sup>.<br />
</div><br />
매우 오랫만에 풀어본 (심심해서 해본...) 문제다.<br />
ps. 역시 파이썬<br />
<br />
<a href='javascript:void(0)' onclick=this.nextSibling.style.display=(this.nextSibling.style.display=='none')?'block':'none';>Python</a><div style='display:none'><pre class="brush:py">>>> sum = 0
>>> for k in range(1, 1001):
... sum += k ** k
...
>>> str(sum)[-10:]
</pre><a onclick=this.parentNode.style.display='none'; href=javascript:void(0)>접기</a></div>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-80079451948790035862012-03-11T21:15:00.001+09:002012-03-11T21:15:03.957+09:00Django에서 jquery-ui 의 autocomplete 사용꾸준히 Django 관련 글을 올리는 이유 중 하나가 "쉽고 빠른 웹개발 Django"라는 책을 보고 있기 때문이다.<br />
그런데 책이 너무 오래...(2009년도 판인데...) 되어서 그런지 안 맞는 게 너무 많다.<br />
<br />
어쨌든, Django에서 자동 추천 기능인 autocomplete를 사용하기 위해서, 이전에는 따로 jquery 플러그인을 사용했지만, 현재는 jquery-ui 라는 추가 프로젝트에 합쳐져서 제공된다.<br />
<br />
설치는 소스 파일을 가져다 쓰거나 아니면 패키지를 이용해서 설치하면 된다.<br />
<br />
사용 방법은 간단히, jquery-ui 자바스크립트 파일(jquery-ui.min.js 등)과 css 파일(jquery-ui.min.css 등)이 필요하다.<br />
(당연히 jquery 자바스크립트 파일은 포함되어야 한다.)<br />
<br />
그리고 사용 방법은 <a href="http://jqueryui.com/demos/autocomplete/#default">http://jqueryui.com/demos/autocomplete/#default</a>의 View Source에 나와 있듯이, 원하는 input 에 autocomplete 함수를 실행시키면 된다.<br />
<br />
이때, 필요한 인자로 객체 값을 받고, 그 객체 내에는 source 값이 필요하다. source가 가져야 할 값으로는 위 문서에 있듯이, 문자열 배열 또는 label과 value를 속성(attribute)로 하는 객체 배열이 필요하다.<br />
<br />
그리고 외부 페이지를 통해 값을 가져오는 경우에는 GET 방식으로 term 값에 주어진 입력을 추가하여 보내게 된다. 그리고 리턴되어야 하는 값으로는 JSON 형식으로 값이 리턴되면 된다.<br />
<br />
이를 종합하여 책에 있는 내용을 수정하면 아래와 같다.<br />
<br />
bookmark_save.html 파일에 추가<br />
<pre class="brush: html"><link type="text/css" rel="stylesheet" href="{{ STATIC_URL }}jquery-ui/css/smoothness/jquery-ui.min.css" />
<script type="text/javascript" src="{{ STATIC_URL }}jquery-ui/jquery-ui.min.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}django_bookmarks/js/tag_autocomplete.js"></script>
</pre>tag_autocomplete.js 파일에 추가 (autocompletePage는 페이지를 불러올 url)<br />
<pre class="brush: js">$(document).ready(function () {
$("#id_tags").autocomplete({
source: autocompletePage
});
});
</pre>views.py 파일에 추가<br />
<pre class="brush: py">import json
# 중략...
def ajax_tag_autocomplete(request):
if request.GET.has_key('term'):
tags = \
Tag.objects.filter(name__istartswith=request.GET['term'])[:10]
return HttpResponse(json.dumps([tag.name for tag in tags]))
return HttpResponse()
</pre><br />
참고자료: <br />
<a href="http://stackoverflow.com/questions/5020844/jqueryui-autocomplete-with-url-as-source-am-using-django">http://stackoverflow.com/questions/5020844/jqueryui-autocomplete-with-url-as-source-am-using-django</a><br />
<a href="http://jqueryui.com/demos/autocomplete/#default">http://jqueryui.com/demos/autocomplete/#default</a>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com1tag:blogger.com,1999:blog-3663785077823868668.post-38899363237484250992012-03-11T01:16:00.002+09:002012-03-11T01:17:15.735+09:00Django에서 Ajax의 POST 시에 csrf_token 추가하기Django 에서 폼이 post 형식으로 데이터를 보낼 때 {% csrf_token %}을 쓰도록 되어 있다.<br />
<br />
그런데 jquery 나 다른 Ajax 와 같이 외부 스크립트에서는 위의 태그를 사용하지 못하는데, 이를 해결 하는 방법으로 아래 공식 문서가 있기는 하다.<br />
<br />
<a href="https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax">https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax</a><br />
<br />
위 스크립트를 통해서 해결을 할 수 있다고는 하는 것 같은데, 너무 복잡하게 되어 있다.<br />
<br />
그러나 위 방법 말고도 다른 방법이 있는데, 문서에 보면 csrfmiddlewaretoken 이라는 이름으로 특정 값을 POST 시에 데이터로 같이 보내도록 되어 있다.<br />
그리고 {{ csrf_token }} 변수가 그 값을 가지고 있는데, 이전에 올렸던 글처럼 변수를 미리 선언하여 외부 스크립트에 사용하는 방식으로 이를 해결할 수 있다.<br />
<br />
템플릿:<br />
<pre class="brush:html"><script type="text/javascript">
var csrf_token = "{{ csrf_token }}";
</script>
<script type="text/javascript" src="/js/test.js">
</pre>외부 스크립트:<br />
<pre class="brush:js">if (this.csrf_token === undefined)
var csrf_token = '';
// POST로 보낼 데이터
var data = {
'csrfmiddlewaretoken': csrf_token
}
</pre><br />
이러한 방법(혹시 편법 일수도...?)으로 해결할 수 있다.<br />
<br />
참고자료: <a href="http://stackoverflow.com/a/5407506">http://stackoverflow.com/a/5407506</a>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-505566859980293072012-03-11T00:46:00.001+09:002012-03-11T00:46:46.370+09:00자바스크립트에 Django의 url 태그 사용 방법Django 템플릿에서 항상 명확한 url이 아닌 경우에는 url tag를 사용해서 경로를 지정하는 것이 편리하고 안전하다.<br />
<br />
어떠한 서버 설정이나 상황에서도 정확한 url을 표시할 수 있기 때문이다.<br />
<br />
그런데 외부 자바스크립트를 사용하는 경우에는 Django의 태그를 처리한 후에 스크립트가 불려지므로, url tag를 쓸 수가 없다.<br />
<br />
이를 해결하기 위해 자바스크립트에 변수를 할당하는 방법을 사용할 수 있다.<br />
<br />
<pre class="brush:html"><script type="text/javascript">
var my_page = {% url my_page %};
</script>
<script type="text/javascript" src="/js/test.js"></script>
</pre><br />
위에서처럼 my_page라는 변수를 할당해두고, 외부 스크립트를 가져오면 된다.<br />
그리고 아래와 같이 외부 스크립트에는 만일에 생길 수 있는 선언되지 않은 변수가 생기는 것을 막기 위해 변수 선언을 해주었다.<br />
<br />
<pre class="brush:javascript">if (this.my_page === undefined)
var my_page = "";
...
</pre><br />
이렇게 하면 외부 스크립트에서도 url 태그의 원하는 페이지를 가져올 수 있다.<br />
<br />
참고자료: <a href="http://www.quora.com/Can-I-use-Django-template-tags-in-JavaScript\">http://www.quora.com/Can-I-use-Django-template-tags-in-JavaScript</a>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-90650739587016781242012-03-06T20:53:00.001+09:002012-03-08T00:21:05.872+09:00서버의 접속 가능을 확인하는 자바스크립트서버가 접속이 가능하면 On을 표시해주고, 접속이 불가능하면 Off를 표시하도록 하는 기능이다.<br />
<br />
<pre class="brush: javascript">서버 작동: <b id="check_server">Off</b>
<script type="text/javascript" src="서버_작동_확인_스크립트.js" defer="defer"></script>
</pre><br />
먼저, 위 코드는 서버 작동 문구를 표시할 곳에 추가해주면 된다. 서버_작동_확인_스크립트.js 는 서버 쪽에 추가할 스크립트로, 스크립트를 불러오는 정확한 url을 써주면 된다.<br />
<br />
그리고 서버 쪽에는 아래 자바 스크립트 파일을 추가해주면 된다.<br />
<br />
<pre class="brush: js">document.getElementById("check_server").innerHTML = "On";
</pre><br />
원리는 간단하다. b 태그에 유일한 id를 지정해주고, 서버에 접속하여 자바스크립트를 가져온다. 이때, 가져온 스크립트가 id를 검색해서 앞에서 지정한 id를 갖는 태그에 대해서 값을 On으로 변경하게 된다.<br />
<br />
만약, 서버에 접속을 못할 경우에는 스크립트를 불러오지 못하므로 Off로 남아있게 된다.<br />
<br />
* <b>주의사항</b>: 서버에 접속을 하지 못할 경우, 계속 해서 스크립트를 불러오려는 대기 상태에 머무르기 때문에 웹페이지 로딩이 완료되지 못하는 현상이 있다. 따라서 같은 서버로부터 다른 페이지를 부르거나 스크립트를 부르는 경우가 있을 때에 쓰는 것이 좋을 것 같다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-36561605237484374712012-03-06T10:06:00.000+09:002012-03-06T20:42:21.226+09:00데비안에서 Django + nginx + uwsgi 설정 기록데비안 6 버전을 기준으로 설명을 하겠다.<br />
<br />
<b>1. django 설치</b><br />
1.2 버전은 apt 를 이용하여 설치.<br />
1.3 버전 이상은 pip 를 이용하여 설치(static 파일에 관한 쉬운 설정 방식이 제공된다.)<br />
<br />
<br />
<b>2. nginx 설치</b><br />
0.8 버전 이상부터 uwsgi에 관한 지원이 기본적으로 제공된다. 따라서 이를 설치하기 위해 /etc/apt/source.list 에 아래 저장소(nginx 저장소)를 추가하여 설치하면 된다.<br />
<pre class="brush:text">deb http://nginx.org/packages/debian/ squeeze nginx
deb-src http://nginx.org/packages/debian/ squeeze nginx
</pre>그리고 설치 시에 저장소의 키가 인증되지 않아서 경고가 발생하는데, 이를 해결하려면 http://nginx.org/packages/keys/ 로부터 nginx_signing.key 를 받은 후, cat nginx_signing.key | sudo apt-key add - 를 하여 키를 추가해주자.<br />
* 참고 자료: <a href="http://wiki.nginx.org/Install">http://wiki.nginx.org/Install</a><br />
<br />
* 주의 사항: nginx 0.7 버전의 경우 문제가 없지만, nginx 저장소를 이용하여 설치한 버전의 경우 nginx 를 삭제할 때 nginx 가 실행되지 않으면 삭제 진행이 안된다. 예를 들어, nginx 의 설정 파일이 없어서 실행이 안될 경우 오류가 발생한다. 또, nginx 가 사용할 포트가 이미 연결되어 있어서 nginx가 포트 연결을 하지 못할 경우 삭제가 안된다. 즉, nginx를 실행한 상태에서 삭제를 진행하는 경우에 깔끔하게 지울 수 있었다.<br />
<br />
<br />
<b>3. uwsgi 설치</b><br />
데비안 7 버전부터 uwsgi 이 지원이 되고, 6 버전에는 패키지가 존재하지 않는다.<br />
따라서 pip 를 통해서 uwsgi 를 설치하자. 이때, build-essential 과 python-dev, libxml2-dev 가 필요하다.<br />
* 참고 자료: <a href="http://projects.unbit.it/uwsgi/wiki/Install">http://projects.unbit.it/uwsgi/wiki/Install</a><br />
<br />
그리고 설치 후에는 컴파일이 되어 설치되므로, 이후에 pip uninstall을 하여도 실행 파일이 지워지지 않고 남는 현상이 있다.<br />
<br />
<br />
<b>4. django, nginx 및 uwsgi 설정</b><br />
uwsgi 의 경우 init.d 에 실행 스크립트가 제공되지 않고, 직접 실행하는 방식으로 작동하기 때문에 서비스를 실행하고 중지하는데 불편하다.<br />
그리고 단일 스크립트로 여러 어플리케이션을 실행하려면 복잡하므로, uwsgi 에서 제공하는 emperor 모드를 사용하기로 하였다.<br />
* 참고 자료: <a href="http://projects.unbit.it/uwsgi/wiki/Emperor">http://projects.unbit.it/uwsgi/wiki/Emperor</a><br />
<br />
먼저, init.d 에 uwsgi 스크립트를 추가하였다.<br />
<pre class="brush:bash">#! /bin/bash
### BEGIN INIT INFO
# Provides: uwsgi
# Required-Start: $network $remote_fs $local_fs
# Required-Stop: $network $remote_fs $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Stop/start uwsgi
### END INIT INFO
# Custom init.d script for uwsgi
NAME=uwsgi
PIDFILE=/pid/파일/경로/$NAME.pid
LOGFILE=/로그/파일/경로/$NAME.log
VASSALS=/uwsgi/하위/앱의/설정/파일이/있는/경로
DAEMON=/uwsgi/실행/경로
CHUID=<uwsgi을 실행시킬 UID>
CHGID=<uwsgi을 실행시킬 GID>
OPTION="--emperor $VASSALS --uid=$CHUID --gid=$CHGID --daemonize $LOGFILE --pidfile $PIDFILE --master"
case "$1" in
start)
echo -n "Starting $NAME: "
start-stop-daemon --start --pidfile $PIDFILE \
--exec $DAEMON -- $OPTION
echo "Done."
;;
stop)
echo -n "Stoping $NAME: "
start-stop-daemon --signal INT --stop --pidfile $PIDFILE \
--exec $DAEMON -- $OPTION
echo "Done."
;;
restart)
echo -n "Restarting $NAME: "
start-stop-daemon --signal INT --stop --pidfile $PIDFILE \
--exec $DAEMON -- $OPTION
sleep 1
start-stop-daemon --start --pidfile $PIDFILE \
--exec $DAEMON -- $OPTION
echo "Done."
;;
*)
echo "Usage: $NAME {start|stop|restart}"
exit 1
;;
esac
exit 0
</pre>스크립트에서 "VASSALS=/uwsgi/하위/앱의/설정/파일이/있는/경로"의 경우 emperor 모드에서 각 앱들의 설정 파일이 존재하는 폴더로 지정해주면 된다.<br />
<br />
예를 들어, 장고 프로젝트가 2개가 있어서 따로 실행을 해주어야 할 경우, uwsgi 설정 파일을 두 개를 만들고 이를 하나의 폴더에 넣은 다음, 그 폴더의 경로를 적어주면 된다.<br />
<br />
그리고 스크립트를 추가한 후에 시스템의 종료 및 시작 시에 스크립트를 작동하기 위해서 sudo update-rc.d uwsgi defaults 명령을 내려주자.<br />
* 참고 자료: <a href="http://www.mrj0.com/2011/05/06/outage/">http://www.mrj0.com/2011/05/06/outage/</a><br />
<br />
<br />
uwsgi을 실행할 스크립트는 완료되었고, uwsgi를 위한 설정 파일을 추가하면 된다. 예를 들어,<br />
<pre class="brush: text">[uwsgi]
socket = 127.0.0.1:포트번호
chdir = /장고/프로젝트/폴더/경로
pythonpath = /장고/프로젝트/폴더의/하위/경로
env = DJANGO_SETTINGS_MODULE=프로젝트 폴더 명.settings
module = django.core.handlers.wsgi:WSGIHandler()
master = true
workers = 2
</pre>이 외에 자세한 옵션은 아래를 참고하면 된다.<br />
* 참고 자료: <a href="http://projects.unbit.it/uwsgi/wiki/Doc">http://projects.unbit.it/uwsgi/wiki/Doc</a><br />
<br />
<br />
uwsgi 설정은 모두 끝났다. 이제, nginx 설정을 하면 된다. nginx 설정은 매우 쉽다. 일반적인 설정 부분은 아래를 참고하자.<br />
* 참고 자료: <a href="http://wiki.nginx.org/Configuration">http://wiki.nginx.org/Configuration</a><br />
<br />
uwsgi 을 사용하는 주요 부분의 설정은 아래와 같이 하면 된다.<br />
<pre class="brush: text">location /url/경로 {
include uwsgi_params;
uwsgi_pass 127.0.0.1:포트번호;
}
</pre>포트번호는 설정 파일에서 적은 것과 같이 하면 된다.<br />
<br />
<br />
이제 nginx 설정도 끝났다. 이제 실행만 하면 서버가 작동 한다.<br />
sudo /etc/init.d/nginx start<br />
sudo /etc/init.d/uwsgi start<br />
<br />
<br />
추가로, 아파치의 mod-wsgi과는 다르게, location으로 지정한 정확한 url 주소가 django 앱으로 넘어간다.<br />
mod-wsgi에서는 http://test.com/project 주소에 django 프로젝트를 지정해두면, urls.py에서 project 뒤부터 경로를 파악한다.<br />
그러나 nginx에서는 project 부터 경로를 파악하므로, urls.py 에 r'^$' 주소의 경우 작동하지 않게 된다.<br />
<br />
이를 간단히 해결하려면, extra_patterns라는 새로운 patterns를 만들고, urlpatterns에는 r'^project/', include(extra_patterns)를 하여 project 에 연결하는 방법을 사용하면 된다.<br />
<br />
<br />
<b>5. 참고 자료 정리</b><br />
* <a href="http://wiki.nginx.org/Main">http://wiki.nginx.org/Main</a><br />
* <a href="http://projects.unbit.it/uwsgi/">http://projects.unbit.it/uwsgi/</a><br />
* <a href="http://hako.04p.kr/?p=145">http://hako.04p.kr/?p=145</a><br />
* <a href="http://www.mrj0.com/2011/05/06/outage/">http://www.mrj0.com/2011/05/06/outage/</a>bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-45867926939756130522012-03-04T14:27:00.000+09:002012-03-07T00:22:42.915+09:00Django 초기 개발 설정 기록<b>1. 프로젝트 시작</b><br />
<pre class="brush:bash">django-admin startproject
</pre><br />
<br />
<b>2. 아파치 서버를 위한 wsgi.py 설정</b><br />
어느 폴더 위치에서나 사용할 수 있는 방법.<br />
<pre class="brush:py">import os, sys
import django.core.handlers.wsgi
path = os.path.dirname(os.path.abspath(__file__))
if path not in sys.path:
sys.path.append(path)
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
application = django.core.handlers.wsgi.WSGIHandler()
</pre><br />
<br />
<b>3. wsgi 스크립트의 아파치 서버 설정</b><br />
<pre class="brush:text">WSGIScriptAlias /url/path /path/to/wsgi.py
</pre>* 주의 사항: 상세한 주소가 먼저 나와야 함. 예를 들어, /url/path 가 /url 보다 먼저 나와야 함.<br />
<br />
<br />
<b>4. settings.py 설정</b><br />
1) 폴더 경로 설정<br />
<pre class="brush:py">import os
dirPath = os.path.dirname(os.path.abspath(__file__))
</pre>위 코드를 통해서 settings.py 가 있는 폴더의 경로를 지정해줄 수 있음.<br />
<br />
2) 데이터 베이스<br />
1.3 버전 이상에서는 데이터 베이스 이름을 적을 때, 'django.db.backends.***' 와 같이 적어주어야 함. *** 앞의 이름을 적지 않으면 이름이 짧다고 경고를 보냄.<br />
<br />
sqlite3 의 경우 데이터 베이스 이름을 적을 때, os.path.join(dirPath, 'dbname') 과 같이 적어주면 됨.<br />
<br />
3) static 파일 위치<br />
css, 이미지, 자바 스크립트 파일이 위치하는 폴더를 설정해주는 방법이다. 1.3 버전 이상부터 django.contrib.staticfiles 앱을 이용하여 매우 편리한 방법으로 변경되었다.<br />
<br />
STATIC_URL: static 파일을 찾을 url 주소이다. static 파일 경로를 html 에서 적을 때, {{ STATIC_URL }}css/style.css 와 같이 경로를 지정해주면 된다.<br />
<br />
STATICFILES_DIRS: runserver를 통한 개발 서버 실행 또는 urls.py에 patterns += staticfiles_urlpatterns() 추가할 경우에 static 파일을 찾는 폴더 경로이다.<br />
<br />
프로젝트의 경우에는 이 폴더를 검색하게 되고, 앱의 경우, 앱 폴더 아래에 있는 static 폴더를 검색한다.<br />
(이들은 STATICFIELS_FINDERS 변수에 있는 함수들을 통해서 이루어진다.)<br />
<br />
STATIC_ROOT: 실제 장고 서비스를 할 때에는 static 파일들을 웹 서버에게 맡기는 것이 일반적이다. 그러므로 static 파일들을 모아두는 경로가 존재하는데, 그 경로를 여기에 적어주면 된다.<br />
<br />
그리고 서비스를 하기 전에 manage.py collectstatic 명령을 내려서, STATICFILES_DIRS 폴더에 있는 static 파일들을 STATIC_ROOT 폴더로 복사를 시키면 된다.<br />
<br />
이때, 파일을 그대로 복사하므로 이름이 겹치지 않게 하기 위해서, STATICFILES_DIRS 에 있는 폴더들에 대해서 구별할 수 있는 폴더 구조를 미리 만들어 주는 것이 좋다.<br />
<br />
예를 들어, 프로젝트나 앱의 이름이 myBlogApp 이라고 할 때, static 폴더 아래에 myBlogApp 폴더를 만들고 그 안에 static 파일을 넣게 되면, 다른 static 파일들과 경로를 구분할 수 있게 된다.<br />
<br />
그리고 html 에서 static 파일의 경로는 {{ STATIC_URL }}myBlogApp/css/style.css 와 같이 해주면 된다.<br />
<br />
<br />
지금까지 설명한 것에 대한 요약이다.<br />
<pre class="brush:py">STATIC_ROOT = '/path/to/static/'
STATIC_URL = '/url/to/static/' # 만약, 다른 서버일 경우 'http://static.server.com/static/' 처럼 하면 된다.
STATICFILES_DIRS = (os.path.join(dirPath, 'static'),)
</pre>그리고 폴더 구조:<br />
myBlog/static/myBlog/css/style.css<br />
myBlog/static/myBlog/js/function.js<br />
<br />
개발 시에: Django에서 STATICFILES_DIRS 폴더 아래에 있는 파일을 검색해줌.<br />
서비스 시에: manage.py collectstatic 명령 실행. 그러면 static 폴더 아래에 있는 폴더 및 파일을 /path/to/static/ 폴더 아래로 복사. 그리고 웹 서버에서 STATIC_ROOT 폴더에 있는 파일을 검색해줌.<br />
<br />
* 주의 사항: 개발 모드로 작동을 시키기 위해서는, DEBUG가 True로 되어 있어야 하며, STATIC_URL 값은 /static/으로 되어 있어야 하고, STATICFILES_DIR 에 static 파일 폴더가 등록 되어 있어야 하며, django.contrib.staticfiles 가 INSTALLED_APPS로 등록 되어 있어야 한다. 그래야 runserver 시에 제대로 파일을 검색한다.<br />
(참고 자료: <a href="http://stackoverflow.com/questions/9181047/django-static-files-development">http://stackoverflow.com/questions/9181047/django-static-files-development</a>)<br />
<br />
<br />
4) Template 폴더<br />
프로젝트의 경우, TEMPALTE_DIRS 변수에 템플릿 파일이 있는 폴더를 명시하면 된다.<br />
앱의 경우, django에서 앱 폴더 아래에 있는 "templates"라는 폴더를 자동으로 검색한다.<br />
<br />
<br />
<b>5. 데이터 베이스 동기화</b><br />
<pre class="brush:bash">manage.py syncdb
</pre><br />
<br />
<b>6. urls.py 설정</b><br />
1) view를 만들지 않고, 템플릿을 그대로 쓰기<br />
1.3 버전 미만:<br />
<pre class="brush:py">from django.views.generic.simple import direct_to_template
(r'^&', direct_to_template, {'template': 'template.html'})
</pre><br />
1.3 버전 이상:<br />
<pre class="brush:py">from django.views.generic.base import TemplateView
(r'^&', TemplateView.as_view(template_name='template.html'))
</pre><br />
2) url 함수 사용<br />
html 파일에 url 경로를 지정하다보면 절대 경로가 필요할 때가 많은데, 웹 서버 설정에 따라서 /url/to/page 처럼 명시할 수 없는 경우가 있다. <br />
<br />
예를 들어, 아파치에서 장고 서비스 주소를 /django_project 로 하고, html 에서 url을 /main_page 라고 하게 되면, http://test.com/django_project/main_page 가 아닌, http://test.com/main_page 주소로 지정된다.<br />
<br />
이 외에도 앱을 배포할 때, 설치하는 사람의 설정이 어떻게 되어 있을 지 모르기 때문에, 어느 설정에서나 정확한 경로를 지정 해줄 수 있는 방법이 필요하다.<br />
<br />
이를 위해, url dispatcher라는 장고 문서를 보면, html에 url 태그를 사용할 수 있게 했다. url 태그에 뷰를 명시하게 되면, urls.py 에서 그 뷰의 주소를 리턴하므로 항상 정확한 경로를 지정해줄 수 있다. (아래서 태그 설명)<br />
<br />
그런데, 뷰의 이름이 길거나 템플릿 자체를 쓰게 되면, 쓰기가 힘드므로 url 함수를 통해서 뷰에 대한 이름을 지정할 수 있다.<br />
<br />
url(r'^$', 'path.to.view', name='view_name') 처럼 쓰면 이후에 path.to.view 대신 view_name을 통해서 절대 경로를 받을 수 있다.<br />
<br />
<br />
<b>7. 템플릿 설정</b><br />
1) url 태그<br />
위에서 언급 했듯이, 항상 일정하고 정확한 url 경로를 위해서 url 태그를 사용할 수 있다.<br />
<br />
{% url path.to.view arg1 arg2 %} 처럼 사용 하면 된다. 만약, urls.py 에 url 함수로 이름을 지정해두었다면, {% url view_name arg1 arg2 %} 처럼 사용할 수 있다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-34472809474426590142012-02-24T20:31:00.001+09:002012-03-06T20:41:47.200+09:00안드로이드 하드웨어 기기 인식 및 직접 설치에뮬레이터를 사용하지 않고 직접 하드웨어 기기를 사용해서 어플리케이션을 테스트 해볼 수 있는데, 이를 위한 작업을 적어둔다.<br />
<br />
기기에서 usb 연결 시 디버깅 모드가 되도록 설정을 해주어야 한다. 어플리케이션 > 개발 > USB 디버깅을 체크해두면 된다.<br />
<br />
그리고 usb 연결을 하면 바로 인식이 된다. (리눅스의 경우 바로 인식 되고, 윈도우즈는 특별한 설정이 필요하다. 윈도우즈는 여기서 다루지 않는다.)<br />
이를 확인하려면 platform-tools 폴더 내에서 ./adb devices 를 입력하면 연결된 기기에 대한 정보가 나온다.<br />
<br />
이때, 인식이 제대로 되었다면 List of devices 아래에는 번호가, attached 아래에는 device 라고 나와야 한다.<br />
만약 나오지 않고 ??? 와 no permission이 나온다면 제대로 인식되지 않은 것이다.<br />
<br />
제대로 인식시키기 위해서는 <a href="http://developer.android.com/guide/developing/device.html">http://developer.android.com/guide/developing/device.html</a> 글을 참고하면 된다.<br />
<br />
간단히 말하면, /etc/udev/rules.d/51-android.rules 파일을 생성해서, 그 안에 아래와 같이 적어준다.<br />
<pre class="brush:text">SUBSYSTEM=="usb", ATTR{idVendor}=="0bb4", MODE="0666", GROUP="plugdev"
</pre>이때, ATTR{idVendor}의 값은 위 페이지 아래에 나와 있는 기기에 대한 값을 적어주면 된다. (위에 값은 HTC의 값이다.)<br />
그리고 GROUP 값은 기기 접속에 대한 권한을 가지고 있는 그룹을 명시해주면 된다. (정확히 어떤 그룹을 의미하는 지는 모르겠다.)<br />
<br />
그리고 생성된 파일에 대해서 아래와 같이 권한을 변경해주면 된다.<br />
<pre class="brush:text">chmod a+r /etc/udev/rules.d/51-android.rules
</pre><br />
이제, 기기 인식이 끝났으므로 ant installd 를 실행하면 현재 연결된 기기에 바로 어플리케이션이 설치가 된다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-19399605703487591202012-02-03T23:56:00.000+09:002012-02-03T23:56:23.869+09:00PyQt 예제 4.1.24 버전의 마지막 글이다. 이 버전의 내용이 단순히 Qt Designer를 이용하여 Ui를 꾸미고 사용해보는 것이기 때문에 내용이 크게 많지 않다.<br />
<br />
이번 글은 이전에 만든 ui 파일을 직접 적용해보는 것이다.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYuuQgUtuo4h1QzAAHsOtstStDOePPcClLYwvjtvhIis9wcbNcJyJ-NG2zfPFaP1DitJ7DKu7WzsGGeyeCdXd2EFm0TTtv-H6Mwl5mAtqOSsQdRbcSw8xr2Yy75xzCZrQ8WQiHRnbxa9fu/s1600/%25EC%258A%25A4%25EB%2583%2585%25EC%2583%25B781.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="254" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYuuQgUtuo4h1QzAAHsOtstStDOePPcClLYwvjtvhIis9wcbNcJyJ-NG2zfPFaP1DitJ7DKu7WzsGGeyeCdXd2EFm0TTtv-H6Mwl5mAtqOSsQdRbcSw8xr2Yy75xzCZrQ8WQiHRnbxa9fu/s400/%25EC%258A%25A4%25EB%2583%2585%25EC%2583%25B781.png" /></a></div><br />
먼저, 대화 상자를 넣을 액션을 추가하기 위해서 mainWindow.py를 조금 수정하였다.<br />
<pre class="brush:py"> # File
fileMenu = QMenu()
objCont.AddActions(fileMenu, (self.textEdit.actions[0], None,
self.textEdit.actions[1], self.textEditRecentFilesMenu,
None, self.textEdit.actions[2], self.textEdit.actions[3],
None, quitAction))
fileMenuAction = objCont.CreateAction(self, "파일(&F)", None, None,
"파일의 열기 및 저장 등을 포함합니다")
fileMenuAction.setMenu(fileMenu)
self.menuBar().addAction(fileMenuAction)
######## 수정 후
# File
fileMenu = QMenu()
objCont.AddActions(fileMenu, (self.textEdit.fileActions[0], None,
self.textEdit.fileActions[1], self.textEditRecentFilesMenu,
None, self.textEdit.fileActions[2], self.textEdit.fileActions[3],
None, quitAction))
fileMenuAction = objCont.CreateAction(self, "파일(&F)", None, None,
"파일의 열기 및 저장 등을 포함합니다")
fileMenuAction.setMenu(fileMenu)
self.menuBar().addAction(fileMenuAction)
</pre><pre class="brush:py"> # Image
imageMenu = QMenu()
objCont.AddActions(imageMenu, self.imageLabel.actions)
imageMenuAction = objCont.CreateAction(self, "이미지(&M)", None, None,
"이미지를 조절합니다")
imageMenuAction.setMenu(imageMenu)
self.menuBar().addAction(imageMenuAction)
######## 수정 후
# Edit
editMenu = QMenu()
objCont.AddActions(editMenu,
self.imageLabel.actions + [None] + self.textEdit.editActions)
editMenuAction = objCont.CreateAction(self, "편집(&E)", None, None,
"텍스트 에디터나 이미지를 편집합니다")
editMenuAction.setMenu(editMenu)
self.menuBar().addAction(editMenuAction)
</pre>메뉴에서 "이미지" 부분은 "편집"으로 바꾸고 이 메뉴 아래에 찾는 부분을 추가한 것 뿐이다.<br />
그리고 이 대화 상자에 대한 부분은 TextEdit 를 다루고 있는 dockWidget 부분에 추가하였다.<br />
<br />
먼저, TextEdit 클래스 쪽에서 수정된 부분을 살펴보겠다.<br />
<pre class="brush:py"> actions = []
#### 수정 후
fileActions = []
editActions = []
</pre><pre class="brush: py"> # Save As Text File Action
saveAsTextFileAction = objCont.CreateAction(self,
"다른 이름으로 텍스트 파일 저장(&A)",
":/saveAsTextFileIcon.png", QKeySequence.SaveAs,
"다른 이름으로 텍스트 파일을 저장합니다.", self.SaveAsTextFile)
self.actions = [newTextFileAction, openTextFileAction,
saveTextFileAction, saveAsTextFileAction]
# Plain Text Edit Context Menu
self.titleLabel.setContextMenuPolicy(Qt.ActionsContextMenu)
objCont.AddActions(self.titleLabel, self.actions)
self.connect(self, SIGNAL("textChanged()"), self.UserChange)
################################################################ Method ###
def UserChange(self):
#### 수정 후
# Save As Text File Action
saveAsTextFileAction = objCont.CreateAction(self,
"다른 이름으로 텍스트 파일 저장(&A)",
":/saveAsTextFileIcon.png", QKeySequence.SaveAs,
"다른 이름으로 텍스트 파일을 저장합니다.", self.SaveAsTextFile)
# Find and Replace Action
findReplaceAction = objCont.CreateAction(self,
"텍스트 찾기 및 바꾸기(&F)",
None, QKeySequence.Find,
"텍스트에서 단어를 찾거나 바꿉니다.", self.FindReplace)
self.fileActions = [newTextFileAction, openTextFileAction,
saveTextFileAction, saveAsTextFileAction]
self.editActions = [findReplaceAction]
# Plain Text Edit Context Menu
self.titleLabel.setContextMenuPolicy(Qt.ActionsContextMenu)
separator = QAction(self)
separator.setSeparator(True)
objCont.AddActions(self.titleLabel,
self.fileActions + [separator] + self.editActions)
self.connect(self, SIGNAL("textChanged()"), self.UserChange)
################################################################ Method ###
def FindReplace(self):
findReplaceDialog = FindReplaceDialog(self.toPlainText(), self)
findReplaceDialog.exec_()
self.clear()
self.setPlainText(findReplaceDialog.GetText())
def UserChange(self):
</pre>대화 상자 호출을 위한 부분들이 많이 추가되었다. findReplaceAction을 통한 대화 상자 호출 액션이 추가되었고, 컨텍스트 메뉴에도 이 액션이 추가 되었다.<br />
<br />
그리고 FindReplace 메소드가 새로 추가되어, 대화 상자를 호출하도록 하였다.<br />
<br />
원래 일반적으로 "찾기 및 바꾸기" 대화 상자가 실행되면, 찾고 난 후 단어에 블록이 되어서 선택되어지는 것이 일반적이다. 하지만 여기서는 그렇게 복잡한 부분까지는 들어가지 않고 간단히 찾았다는 것만 알리도록 하였다.<br />
(원래는 할 줄 몰라서 + 너무 어렵고 복잡해서 + 이미 텍스트 에디터 기능에 Find라는 메소드를 통해서 지원하므로)<br />
<br />
그리고 바꾸는 부분에서는 기존 텍스트를 변경한 후에 이를 대화 상자로부터 가져와서 대체하는 방법으로 처리하였다.<br />
<br />
<br />
<br />
그럼 이제 실제로 ui 파일을 사용하는 방법을 보겠다.<br />
<br />
먼저, pyuic4 -o ui_findReplaceDlg.py findReplaceDlg.ui 명령을 내려서 ui 파일을 파이썬 파일로 변환시켜주자. pyuic4는 ui 파일을 파이썬 모듈로 변환 시켜주는 도구이다. pyrcc4와 같은 도구이다.<br />
<br />
명령을 수행하고 나면 파이썬 파일이 하나 생성되는데, 실제로 이를 열어보면 ui 파일에서 생성한 대화 상자 클래스가 생성되어 있고, setupUi라는 메소드 아래에 상세한 설정들이 지정되어 있다.<br />
<br />
이 모듈을 사용해야 하므로 import ui_findReplaceDlg 를 하여서 모듈을 가져오자. 추가로, import re도 하여 정규식을 사용할 수 있도록 하자. 그리고 클래스를 생성해야 하는데, 자세한 코드를 먼저 보겠다.<br />
<pre class="brush: py">class FindReplaceDialog(QDialog, ui_findReplaceDlg.Ui_FindReplaceDialog):
"""텍스트 에디터의 찾기 및 바꾸기 대화상자"""
def __init__(self, text, parent=None):
super().__init__(parent)
self.text = text
self.index = 0
self.setupUi(self)
self.connect(self, SIGNAL('found'),
self.FoundText)
self.connect(self, SIGNAL('notfound'),
self.NotFoundText)
self.connect(self, SIGNAL('replace'),
self.ReplaceText)
self.connect(self, SIGNAL('replaceAll'),
self.ReplaceAllText)
MAC = 'qt_mac_set_native_menubar' in globals()
if not MAC:
self.findButton.setFocusPolicy(Qt.NoFocus)
self.replaceButton.setFocusPolicy(Qt.NoFocus)
self.replaceAllButton.setFocusPolicy(Qt.NoFocus)
self.closeButton.setFocusPolicy(Qt.NoFocus)
self.updateUi()
### Slot ###
@pyqtSignature('QString')
def on_findLineEdit_textEdited(self, text):
self.index = 0
self.updateUi()
@pyqtSignature('')
def on_findButton_clicked(self):
regex = self.MakeRegex()
match = regex.search(self.text, self.index)
if match is not None:
self.index = match.end()
self.emit(SIGNAL('found'), match.start())
else:
self.emit(SIGNAL('notfound'))
@pyqtSignature('')
def on_replaceButton_clicked(self):
regex = self.MakeRegex()
self.text = regex.sub(self.replaceLineEdit.text(), self.text, 1)
self.emit(SIGNAL('replace'))
@pyqtSignature('')
def on_replaceAllButton_clicked(self):
regex = self.MakeRegex()
self.text = regex.sub(self.replaceLineEdit.text(), self.text)
self.emit(SIGNAL('replaceAll'))
### Method ###
def updateUi(self):
enable = bool(self.findLineEdit.text())
self.findButton.setEnabled(enable)
self.replaceButton.setEnabled(enable)
self.replaceAllButton.setEnabled(enable)
def MakeRegex(self):
findText = self.findLineEdit.text()
if self.syntaxComboBox.currentText() == '문자열':
findText = re.escape(findText)
flags = re.MULTILINE | re.DOTALL
if not self.caseCheckBox.isChecked():
flags |= re.IGNORECASE
if self.wholeCheckBox.isChecked():
findText = r"\b%s\b" % findText
return re.compile(findText, flags)
def GetText(self):
return self.text
def FoundText(self):
QMessageBox.information(self,
'문자열 찾음!', '일치하는 문자열을 찾았습니다!')
def NotFoundText(self):
QMessageBox.information(self,
'더 이상 찾는 문자열 없음!',
'더 이상 일치하는 문자열이 없습니다!')
def ReplaceText(self):
QMessageBox.information(self,
'문자열 바꿈!',
'문자열을 바꾸었습니다!')
def ReplaceAllText(self):
QMessageBox.information(self,
'문자열 모두 바꿈!',
'문자열을 모두 바꾸었습니다!')
</pre>FindReplaceDialog라는 클래스를 새로 생성하였는데, QDialog과 ui_findReplaceDlg 모듈에 있는 클래스를 상속하고 있다.<br />
<br />
여기서 pyuic4를 통해 메인 윈도우나 대화 상자를 생성하게 되면, 이들에 대한 객체(object) 이름에 Ui_ 가 앞에 붙어서 클래스 이름이 정해진다.<br />
따라서 대화 상자 클래스를 상속하기 위해서 Ui_FindReplaceDialog를 사용한 것이다.<br />
<br />
그리고 ui 모듈을 사용하는 방법으로 다른 것도 있지만, 이와 같이 다중 상속을 통해서 매우 간편하게 ui 모듈을 쓸 수 있다.<br />
<br />
다음으로 클래스의 부모를 초기화 해주어야 하므로, 예전처럼 super()를 사용하여 부모를 초기화 시켜주었다. 그리고 클래스 내에서 사용할 변수들을 초기화 해준 다음, 앞에서 말했던 setupUi()를 실행시켜서 ui를 초기화 시켜주었다.<br />
<br />
이렇게 하게 되면 아주 간단하게 ui가 클래스에 설정된다. 그리고 여기서 setupUi가 한 가지 추가 작업을 시행하는데, QtCore.QMetaObejct.connectSlotByName() 이라는 메소드를 호출한다.<br />
<br />
이것은 ui 파일에서 생성한 객체들에 대한 시그널과 슬롯을 자동으로 연결해주는 기능을 한다. 이 메소드를 호출할 때 객체를 하나 넘겨주는데, 이 객체 내에 있는 모든 자식들을 검색해서, 자식의 시그널과 슬롯들을 연결해준다.<br />
<br />
이때 연결하는 슬롯의 이름은 특별한 이름으로 지정되어 있는데, "on_위젯이름_시그널이름" 이다.<br />
<br />
즉, connect 함수를 쓸 필요 없이, 위 이름으로 메소드를 만들어두면 자동으로 연결을 해주는 것이다. 이에 대한 자세한 사용은 아래에 나오므로, 다음 코드를 다시 보겠다.<br />
<br />
<br />
다음 코드에서는 MAC 변수에 대해서 체크를 하는데, MAC을 제외한 리눅스, 윈도우즈에서는 버튼에 대한 탭 포커스가 중요하지 않기 때문에 이를 제외하는 코드이다. 리눅스, 윈도우즈에서는 키보드를 쓸 때에서 단축키 사용이 가능하기 때문이다.<br />
<br />
따라서 MAC 에만 있는 함수 PyQt4.QtGui.qt_mac_set_native_menubar 가 현재 import 되어 있는 지를 확인한 다음, 없을 경우에는 리눅스, 윈도우즈이므로 버튼에 대한 포커스를 제거하는 것이다.<br />
<br />
그리고 updateUi를 통해서 ui를 갱신하도록 하였다.<br />
<br />
<br />
이제 슬롯을 보겠는데, 앞에서 말한 슬롯들이다. 이 슬롯들은 connectSlotByName이 실행되면서 지정된 시그널들과 자동으로 연결이 된다.<br />
<br />
슬롯을 만드는 것은 어렵지 않으므로, 몇 가지만 살펴보겠다. 먼저, @pyqtSignature 라는 장식자(decorator)를 사용하고 있다. 이것을 사용하는 이유는, 시그널을 구분하기 위함이다.<br />
<br />
원래 connect를 쓸 때는 SIGNAL('c++용 시그널 이름') 방식을 사용하기 때문에 시그널 이름에 자료형이 포함되어서 같은 이름의 시그널들을 구분할 수 있었다.<br />
(즉, 오버로딩된 시그널들을 구분할 수 있었다.)<br />
<br />
하지만 파이썬에는 자료형이 없기 때문에, 이를 구분할 수가 없다. 따라서 장식자에 시그널의 인자를 명시하여 시그널들을 구분하고 하는 것이다.<br />
<br />
textEdited 시그널의 경우 QString을 넘기므로 장식자에 QString을 추가해주었다. 그리고 시그널로부터 오는 인자를 text로 받게 된다.<br />
<br />
그 아래 버튼 클릭에 대한 시그널들은 인자를 보내지 않기 때문에, 장식자에 빈 문자열을 추가해주었다.<br />
<br />
<br />
<br />
슬롯에 내용을 보기 전에, 먼저 메소드의 내용을 보겠다. updateUi 메소드는 findLineEdit 에 적어도 글자가 1개 이상 들어와야만 버튼들이 활성화 되도록 한 것이다. 즉, 빈 문자열에 대해서는 검색을 하지 않도록 막는 것이다.<br />
<br />
MakeRegex 메소드는 정규식 표현을 만드는 메소드이다. 검색어를 가져와서, 콤보 박스의 내용이 '문자열'일 경우에는 \ 문자에 대해서 escape 문자를 처리하게 된다. 즉, 정규식 표현에 대한 기호가 아닌, 실제 \ 문자를 표현하기 위해서 \\로 변경을 할 것이다.<br />
<br />
그리고 정규식에서 사용할 조건을 지정하기 위해서 flags를 추가하였고, caseCheckBox가 체크 되어 있다면, 대소문자를 구분한다는 의미이므로, 체크가 안 되어 있을 경우에만, 대소문자 구별을 무시한다는 조건을 추가하였다.<br />
<br />
그리고 wholeCheckBox의 경우에는 단어 단위로 검색을 하는 것이므로 검색어 주위에 공백이 있어야 한다. 따라서 주어진 검색어 주변에 공백 기호인 \b를 추가하여 정규식을 생성하였다.<br />
<br />
그런 다음, 정규식 표현과 조건으로부터 실제 사용할 정규식을 리턴하도록 하였다.<br />
<br />
GetText 메소드는 단순히 변경된 텍스트를 리턴하기 위한 것이고, 그 아래 메소드들은 문자열을 찾거나 변경하였을 경우 메시지를 통해 알려주는 메소드이다.<br />
<br />
<br />
<br />
이제, 슬롯의 내용을 보면 findLineEdit에 대한 슬롯은, 새로운 찾기 검색어가 입력될 때마다 검색을 새로 시작하는 것이다.<br />
<br />
findButton에 대한 슬롯은, MakeRegex로 부터 만든 정규식을 이용해서 주어진 위치(self.index)부터 검색을 하는 것이다. 문자열을 찾으면 match에 값이 존재하게 되고, 검색 위치를 match 이후로 변경한 후에 대화 상자에서 found 시그널을 보내도록 하였다.<br />
<br />
그리고 찾지 못한 경우에는 notfound 시그널을 보내게 된다.<br />
(참고로, 깜박하여 match.start() 인자를 사용하지 않았는데, 이를 통해서 어느 위치에서 단어가 검색되었는지 알 수 있다.)<br />
<br />
replaceButton에 대한 슬롯도 search 대신 sub를 사용해서 지정된 단어를 대체하도록 하는 것이다. 이때, 마지막 인자로 1을 넘겨서 한 번만 대체하도록 하였다.<br />
<br />
그리고 replaceAllButton 에서는 모두 변경이므로, 마지막 인자를 주지 않고 여러 번 모두 변경시키도록 하였다.<br />
<br />
<br />
<br />
이로써 Qt Designer로부터 생성한 ui 를 사용하는 방법을 배웠다. 이제 매우 쉽고 간편하게 PyQt를 개발할 수 있게 되었다.<br />
<br />
그리고 지금까지 한 내용을 바탕으로 충분히 프로그램을 만들 수 있다. 책에서도 이 이후에 내용은 좀 더 고급 내용으로, 데이터를 다루거나 사용자 위젯을 만드는 내용이다.<br />
<br />
아마도 이후 내용은 장기간 다루지 않을 예정이므로, 다음 글이 언제 올라올지는 모르겠다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0tag:blogger.com,1999:blog-3663785077823868668.post-320994519576529742012-01-30T17:18:00.000+09:002012-02-03T23:44:54.279+09:00PyQt 예제 4.1.14 버전에서는 Qt Designer를 사용하여 대화 상자를 만들어 볼 것이다.<br />
<br />
Qt Designer는 메인 윈도우나 대화 상자를 GUI를 통해서 쉽게 생성하고 디자인 해볼 수 있는 도구이다.<br />
<br />
윈도우즈의 경우 SDK 를 설치했다면 디자이너까지 설치가 되어 있을 것이고, 우분투에서는 qt4-designer를 설치하면 된다.<br />
<br />
<br />
디자이너를 설치한 후에 실행을 시키면 아래와 같이 창이 나오는데, 여기서는 대화 상자를 만들 것이므로 Dialog without Buttons을 선택하여 버튼 없는 대화 상자를 생성하자.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglKrK3FyOx0gF5k5T8elhCoVwyE2vMNX4DlS_HKDzWVgiHsjpG8vDytF8ZuQboyUakuyTXXXSmADyafOjLR719hhvArfH058FyVW5JGqzHhwilwtllOPjpXFq64SIhSF6y6HrLWKs0Agfl/s1600/%25EC%258A%25A4%25EB%2583%2585%25EC%2583%25B779.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="250" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglKrK3FyOx0gF5k5T8elhCoVwyE2vMNX4DlS_HKDzWVgiHsjpG8vDytF8ZuQboyUakuyTXXXSmADyafOjLR719hhvArfH058FyVW5JGqzHhwilwtllOPjpXFq64SIhSF6y6HrLWKs0Agfl/s400/%25EC%258A%25A4%25EB%2583%2585%25EC%2583%25B779.png" /></a></div><br />
디자이너의 사용법은 매우 간단하기 때문에 자세한 설명은 필요 없기 때문에, 디자인을 수행해가는 방식에 대해서 설명을 해보겠다.<br />
(특히, 비주얼 베이직을 해보신 분들이라면 사용하는 방법이 더욱 쉬울 것이다.)<br />
<br />
디자인을 하는 순서는 아래와 같다.<br />
<br />
<ol><li>원하는 위젯을 폼에 적절히 배치한다.</li>
<li>위젯의 속성을 설정한다.</li>
<li>원하는 위젯들에 대해서 1, 2를 반복한다.</li>
<li>창의 크기 조절이나 위젯 간의 공간이 필요하다면 spacer를 추가한다.</li>
<li>두 개 이상의 위젯을 선택하여 레이아웃을 배치한다.</li>
<li>모든 위젯에 대해서 5번을 반복한다.</li>
<li>폼의 전체 위젯에 대해서 레이아웃을 설정한다.</li>
<li>폼의 레이블에 대해서 버디(Buddy)를 생성한다.</li>
<li>탭 순서가 잘못 되었다면 이를 정정한다.</li>
<li>내장 시그널-슬롯을 사용하는 경우에는 이를 생성해준다.</li>
<li>폼을 미리보기 하여 점검한다.</li>
<li>폼의 객체 이름을 정해주고, 파일을 저장해준다.</li>
</ol><br />
많은 순서가 있어 보이지만 각각은 매우 단순한 작업이다. 여기서는 이전에 만들었던 텍스트 에디터에 "찾기 및 바꾸기" 대화 상자를 추가해보도록 하겠다.<br />
(참고로 이것은 책에 있는 예제라서 그대로 따라가는 것이 많을 것이다.)<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHmR3YV6OJDHfwb6RAhU9Vaix1KgvoScBBJL1WHZxbUzzlpF35NH1z3sxb00aoDiKuyjwAFl0PfeJjNAcBwV6coihLEP5LDHg58N_z-Cb9RBRgduKqzvayhnPXljYyPwjMw1GYK6Bt7TJW/s1600/%25EC%258A%25A4%25EB%2583%2585%25EC%2583%25B780.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="182" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHmR3YV6OJDHfwb6RAhU9Vaix1KgvoScBBJL1WHZxbUzzlpF35NH1z3sxb00aoDiKuyjwAFl0PfeJjNAcBwV6coihLEP5LDHg58N_z-Cb9RBRgduKqzvayhnPXljYyPwjMw1GYK6Bt7TJW/s400/%25EC%258A%25A4%25EB%2583%2585%25EC%2583%25B780.png" /></a></div><br />
먼저, Label과 Line Edit를 추가하자. 일반적으로 탭 순서는 위젯의 생성 순서를 따라가므로 위젯을 옳바르게 생성하는 것이 탭 순서 교정을 피하는데 좋다.<br />
<br />
레이블을 더블 클릭하면 텍스트를 변경할 수 있다. 그리고 이렇게 객체를 생성한 후에는 반드시 오른쪽 속성에서 objectName을 설정해주자. 이는 나중에 파이썬 모듈을 사용할 때 각 객체의 변수 명이 되기 때문이다.<br />
<br />
예를 들어, "찾을 단어:"의 레이블은 findLabel, 이것의 라인 에디트는 findLineEdit과 같이 정해주자.<br />
<br />
그런 다음 체크 박스를 추가하고, 마찬가지로 텍스트 변경 및 객체 이름을 지정해주자. 그리고 레이블과 콤보 박스를 추가해주자. 콤보 박스를 더블클릭하면 창이 하나 뜨는데, 여기서 + 버튼을 눌러서 아이템을 추가해줄 수 있다. 아이템으로는 문자열, 정규표현식을 추가하였다.<br />
(<strike>체크 박스의 경우 정확한 기능을 몰라서 아직 한글명을 쓰지 않았다.</strike><br />
Case Sensetive 는 대소문자 구별, Whole Words는 단어 단위로 검색이다.)<br />
<br />
그리고 창 크기를 조절했을 때 위젯은 가만히 있고 창 여백만 늘리기 위해서 콤보 박스 아래에 Vertical Spacer를 추가해주자. 그 다음 오른쪽에 버튼과 그 외 위젯을 분리한다는 것을 나타내기 위해서 Vertical Line을 추가해주었다.<br />
<br />
그리고 마지막으로 버튼과 스페이스를 순서대로 추가해주면, 위젯 배치는 완료가 된다.<br />
<br />
<br />
이제 레이아웃을 배치해야 하는데, 방법은 간단하다. 먼저, 위젯들을 컨트롤 키 + 왼쪽 클릭 또는 드래그를 통해서 여러 개를 선택하고, 위의 도구 상자를 보면 Lay out *** 들이 있다. 이를 클릭하면 레이아웃이 설정된다.<br />
<br />
만약, 레이아웃을 잘못 설정했다면, Break Layout을 눌러주면 된다.<br />
<br />
먼저, 2개의 라인 에디트와 레이블, 총 4개를 선택해서 Grid로 레이아웃 해주자. 그리고 체크박스끼리 Horizontal 로 배치해주고, 레이블과 콤보박스도 Horizontal로 배치해주자. 그 다음 왼쪽에 있는 모든 위젯(스페이스 포함)들을 모두 선택해서 Vertical로 배치해주고, 버튼들도 묶어서 Vertical로 배치해주자.<br />
<br />
그런 다음 폼을 선택하면 선택이 해제가 되는데, 이 상태에서 Horizontal을 누르면 모든 위젯들을 묶어서 수평 배치해준다.<br />
<br />
이렇게 하면 레이아웃 배치도 완료되고, 위젯들도 깔끔하게 배치된다. 만약, 창의 크기가 크다면 작게 줄여서 보기 좋게 해준다.<br />
<br />
이 상태에서 컨트롤 + R 또는 Form > Preview를 해주면, 대화 상자를 미리 볼 수 있다.<br />
<br />
<br />
이제 Buddy를 지정해야 하는데, 도구 상자에 보면 Edit Buddies가 있다. 이를 클릭해준 다음 레이블을 클릭한 상태에서 알맞은 다른 위젯으로 드래그 해주자.<br />
<br />
그러면 화살표가 다른 위젯으로 가리키면서 Buddy가 되었다는 것을 알려준다. 이렇게 하고나면 레이블에 &S와 같이 되어 있던 텍스트에서 &가 사라지고 밑줄로 변경된다.<br />
<br />
<br />
탭 순서도 변경할 수 있는데, 위젯을 제대로 생성하였다면 자동으로 깔끔하게 설정되어 있을 것이다. 일단, 변경 방법은 도구 상자에서 Edit Tab Order를 클릭하여 번호를 클릭하여 알맞게 변경시켜주면 된다.<br />
<br />
<br />
이제 시그널과 슬롯을 지정해주어야 하는데, 여기서 "닫기"를 제외한 나머지 버튼을 따로 슬롯을 생성해주어야 하므로 여기서 지정할 수는 없고, "닫기"의 경우 폼에 reject 슬롯과 연결이 되기 때문에 여기서 바로 지정할 수 있다.<br />
<br />
도구 상자에서 Edit Signals/Slots을 클릭하고 Buddy를 지정한 것처럼, "닫기" 버튼을 클릭한 상태에서 "폼"의 아무 곳으로 드래그를 해주면 가능한 시그널과 슬롯이 나타난다.<br />
<br />
여기서 clicked 시그널과 reject 슬롯을 선택해주면 시그널, 슬롯이 연결된다.<br />
<br />
<br />
이제 모든 디자인이 완료되었다. 마지막으로 폼을 선택해서 오른쪽에 objectName을 지정해주자. 이것은 대화 상자의 클래스 이름이 된다. 그리고 아래에서 windowTitle을 찾아서 대화상자의 제목을 설정해주자.<br />
<br />
그리고 저장(Ctrl + S)을 해서 디자인한 대화 상자를 저장해주면 완료가 된다.<br />
<br />
<br />
<br />
이로써 아주 간단하게 대화 상자를 생성할 수 있었다. 이전에는 이 작업을 모두 코드로 타이핑 해야 했지만, 간단하게 마우스로 대화 상자 디자인을 보면서 할 수 있었다.<br />
<br />
이 외에 내용들은 책에도 문서를 참고하라고 되어 있으므로 문서를 통해서 더 자세한 설명과 Custom(사용자) 위젯을 만들 수도 있을 것이다.<br />
<br />
이것으로 디자인 내용에 대해서는 마치고 다음 글에서 이를 파이썬에서 사용하는 방법을 설명하겠다.bluekyuhttp://www.blogger.com/profile/13411943152339006741noreply@blogger.com0