Markdown-Pod

 view release on metacpan or  search on metacpan

t/mkd/2011-12-07.mkd  view on Meta::CPAN

Title:    윈도우 환경에서 화면 캡쳐 후 자동 저장 기능의 구현
Package:  Seoul.pm
Category: perl
Category: Seoul.pm
Author:   owl0908

저자
-----

@owl0908 - 
Perl과는 그닥 어울릴 것 같아보이지 않는, 법학을 전공한 고시 준비생.
10여년 전 개인 홈페이지를 Perl로 구현한 것이 계기가 되어,
슬럼프가 올 때마다 하라는 공부는 안하고 Perl 코딩을 한 결과,
이제는 자신의 정체성의 혼란을 겪고 있다(물론 실력과는 전혀 관계가 없다).
[부엉이의 나무구멍 속 작은 공간][blog-owl0908] 블로그를 운영하고 있다.
webmaster _at_ dormouse.pe.kr


시작하며
---------

이것저것 스크린샷이 포함된 글을 쓰실 때, 대개 어떻게 하시나요?
`Shift + PrtSc` 키를 이용하면 현재 화면을 쉽게 클립보드에 저장할 수 있습니다.
이렇게 클립보드로 저장된 이미지는 어디서든 `Ctrl-V` 로 붙여넣기를 할 수 있죠.
그러나, 스크린샷이 파일 형태로 필요하다면 어떨까요?
이미지 편집 프로그램을 실행시킨 다음, 붙여넣기를 한 후, 저장을 해야 합니다.
서너장이 아니라 수십 수백장의 화면을 갈무리해야 한다면
일일이 이렇게 저장하는 것은 너무 힘든 일입니다.
스크린샷을 찍으면 자동으로 알아서 파일로 저장까지 해주면 딱 좋을 것 같지 않나요?
자, 시작해보죠. :-)


준비물
-------

우선 현재 화면을 갈무리해서, 그것을 특정 파일로 저장하는 기능을 구현해야 합니다.
윈도우즈는 기본적으로 `Shift + PrtSc` 키를 입력하면 전체화면을 갈무리해서 클립보드에 저장합니다.
이 기능을 이용한다고 했을때 고민해야할 부분은 다음과 같습니다.

- 어떤 행위를 했을때 화면을 갈무리 할 것인지 결정
- 결정된 행위가 이루어졌을 경우 `Shift + PrtSc` 키가 입력된 것처럼 동작
- 클립보드의 내용을 읽어와서 파일로 저장

인터페이스 디자인에 해당하는 부분으로 어떤 식으로 컴퓨터에게 명령을 내릴지 정해야 합니다.
스크린샷을 찍을 때마다 스크립트를 실행하는 방법은 불편할 것 같습니다.
가능한 간편하게 사용할 수 있으며 눈에 띄지않을 방법을 생각해보니
시스템 트레이에 아이콘을 상주시켜 놓고 이 아이콘을 클릭할 때마다
화면을 갈무리하는 것이 꽤나 그럴듯해 보입니다.
(믿기 어렵겠지만, 시스템 트레이 아이콘을 이용하는 것은
꽤나 있어보이는 것에 비해 너무 간단합니다. :)

필요한 모듈은 다음과 같습니다.

- [CPAN의 Win32::GUI 모듈][cpan-win32-gui]
- [CPAN의 Win32::GuiTest 모듈][cpan-win32-guitest]
- [CPAN의 Win32::HideConsole 모듈][cpan-win32-hideconsole]
- [CPAN의 Win32::Clipboard 모듈][cpan-win32-clipboard]
- [CPAN의 DateTime 모듈][cpan-datetime]
- [CPAN의 File::Slurp 모듈][cpan-file-slurp]

[딸기펄][strawberry-perl]을 사용한다면 콘솔에서 `cpan` 명령을 이용해서 설치합니다.
[딸기펄 5.14][strawberry-perl-514] 버전에서 필요한 모든 모듈이 정상적으로
설치되는 것을 확인했으므로 버전 선택시 참고하시길 바랍니다.

    #!bash
    > cpan Win32::GUI
    > cpan Win32::GuiTest
    > cpan Win32::HideConsole
    > cpan Win32::Clipboard
    > cpan DateTime
    > cpan File::Slurp

[ActiveState사의 ActivePerl][activeperl]을 사용한다면 PPM 관리자를 이용해서 설치하면 됩니다.


뼈대 만들기
------------

GUI 구성은 윈도우즈 네이티브 UI 라이브러리인 `Win32::GUI` 모듈을 사용합니다.
윈도우즈의 시스템 트레이에 아이콘을 상주시키는 것은 `Win32::GUI` 모듈이 제공하는
`AddNotifyIcon` 메소드를 사용하면 간단하게 구현할 수 있습니다.
기본적인 뼈대 코드는 다음과 같습니다.

    #!perl
    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    use Win32::GUI ();
    
    # 사용할 아이콘을 정의합니다.
    my $icon = Win32::GUI::Icon->new('icon.ico');
    
    # 기본으로 사용할 창을 정의합니다.
    my $main = Win32::GUI::Window->new(
        -name => 'Main',
        -size => [ 1, 1 ],
    );
    
    # 시스템 트레이 아이콘을 정의합니다.
    $main->AddNotifyIcon(
        -name    => "NI",
        -icon    => $icon,
        -tip     => "Click to Screenshot!",
        -balloon => 0,
        -onClick => sub {
            # 스크린샷을 찍는 부분을 구현할 예정입니다.
            return 0;
        },
        -onRightClick => sub {
            # 트레이에 좀비 아이콘이 그냥 남아있는 것을 방지합니다.
            $main->NI->Remove;
    
            # 프로그램 종료
            return -1;
        },
    );
    
    # 창을 숨깁니다.
    $main->Hide;
    
    # GUI 메인 루프에 진입합니다.
    Win32::GUI::Dialog;

작성한 스크립트가 있는 디렉터리와 동일한 위치에 마음에 드는 아이콘을 구한 뒤
`icon.ico`로 이름을 바꿔서 저장한 후 스크립트를 실행하면
시스템 트레이에 아이콘이 나타나는 것을 볼 수 있습니다.
아이콘에 마우스를 가져가면 `Click to Screenshot!`이라는 툴팁이 표시될 것입니다.
아이콘 위에서 마우스 왼쪽 버튼을 누르면 `onClick` 이벤트가 발생하며
오른쪽 버튼을 누르면 `onRightClick` 이벤트가 발생합니다.
오른쪽 버튼을 누를 경우 연결된 함수 레퍼런스가 `-1`을 반환하므로 프로그램이 종료됩니다.

*그림 1.* 실행 후 시스템 트레이에 아이콘이 적재된 화면 [(원본)][img-01-original]
![실행 후 시스템 트레이에 아이콘이 적재된 화면][img-01]

이제 스크린샷을 찍을 부분을 구현해야겠죠.
`PrtSc` 명령은 [CPAN의 Win32::GuiTest 모듈][cpan-win32-guitest]의 `SendKeys` 함수를 이용하면 됩니다.
`SendKeys` 함수는 문자 그대로 특정한 키 입력을 컴퓨터에게 던져주는 역할을 합니다.
`onClick` 이벤트 발생시 실행시키는 함수 레퍼런스에 다음 내용을 추가합니다.

    #!perl
    ...
    
    use Win32::GUI ();
    use Win32::GuiTest qw(SendKeys); # 추가된 부분
    
    ...
    
    $main->AddNotifyIcon(
        -name    => "NI",
        -icon    => $icon,
        -tip     => "Click to Screenshot!",
        -balloon => 0,
        -onClick => sub {
            # 스크린샷을 찍습니다.
            SendKeys( "+{PRTSCR}" ); # 추가된 부분
            return 0;
        },
        ...
    );

`{PRTSCR}` 앞의 `+`는 `Shift`를 같이 입력한다는 의미입니다.
참고로 `^`은 `Ctrl`, `%`는 `Alt`를 의미합니다.
여러 키 조합과 딜레이 타임을 옵션으로 지정할 수도 있기 때문에,
`SendKeys`와 `Win32::GUI` 모듈의 타이머 기능을 결합하면 여러가지로 활용이 가능합니다.
예를 들자면, 특정 온라인 게임에 대응하는 매크로도 만들 수 있겠죠. ;-)


클립보드 이미지를 파일로 저장
------------------------------

이제 클립보드로 들어간 이미지를 저장해야겠죠?
[CPAN의 Win32::Clipboard 모듈][cpan-win32-clipboard]을 사용하면
간편하게 클립보드 이미지를 불러올 수 있습니다.
불러오고 싶다면 다음처럼 단 두 줄만 작성하면 됩니다.

    #!perl
    use Win32::Clipboard;
    my $bitmap = Win32::Clipboard::GetBitmap;

정말 끝내주지 않나요? :-D

클립보드에 저장된 비트맵을 불러오는 것이므로 클립보드에 있는 것이
이미지인지 텍스트인지 확인할 필요도 없을 것 같습니다.
`$bitmap` 스칼라 변수에 비트맵 이미지가 고스란히 저장되기 때문에
`.bmp` 파일에 써주기만 하면 파일 저장도 끝납니다.
파일명을 일일이 사용자에게 물어보는 것도 번거로운 일이니까
현재 날짜과 시간을 사용해서 자동으로 저장하도록 하겠습니다.
`onClick` 이벤트에 대한 함수 레퍼런스는 이렇게 바뀝니다.

    #!perl
    ...
    
    use Win32::GUI ();
    use Win32::GuiTest qw(SendKeys);
    use Win32::Clipboard;
    use DateTime;
    use File::Slurp
    
    ...
    
    $main->AddNotifyIcon(
        -name    => "NI",
        -icon    => $icon,
        -tip     => "Click to Screenshot!",
        -balloon => 0,
        -onClick => sub {
            # 스크린샷을 찍습니다.
            SendKeys( "+{PRTSCR}" );
    
            # 스크린샷을 저장합니다.
            my $dt       = DateTime->now( time_zone => 'Asia/Seoul' );
            my $filename = sprintf('%s %s.bmp', $dt->ymd, $dt->hms);
            my $bitmap   = Win32::Clipboard::GetBitmap;
    
            write_file( $filename, {binmode => ':raw'}, $bitmap ) ;
    
            return 0;
        },
        ...
    );

*그림 2.* 화면 갈무리 결과 [(원본)][img-02-original]
![화면 갈무리 결과][img-02]


용량 줄이기
------------

일단 프로그램 작성은 끝났습니다.
다만 실행해보면 갈무리되는 파일의 크기가 무척 크다는 것을 알 수 있습니다.
비트맵 형식은 압축을 하지 않기 때문에 일반적인 HD 해상도의 파일은 약 3MB,
그리고 그 이상의 해상도의 화면을 사용한다면 5MB는 가볍게 훌쩍 넘깁니다.

t/mkd/2011-12-07.mkd  view on Meta::CPAN




정리하며
---------

글쓴이 소개란을 읽으셨을지 모르겠지만, 전 전산/컴퓨터와는 전혀 관계 없는 법학 전공자거든요. (^^)
MS 윈도우 환경에서 약간의 Perl 지식만 있다면 Perl을 이용해 자신이 필요한 기능을
자신이 직접 설계해서 사용할 수 있다는 사실은 정말 매력적입니다.
모듈을 사용한 것은 온전한 제 지식이 아니라구요?
뭐 그럼 어떤가요? 어쨌든 만들고 싶었던 것을 만들어냈잖아요!
이미 다른 사람들이 만들어 놓은 수많은 CPAN 모듈들을 활용하면,
혼자 힘으로는 역부족이라고 생각했던 부분은 해낼 수 있게,
가능한 부분은 더욱 빠른 시간내에 구현할 수 있게 도와줍니다.
어떤가요? 매력적이지 않나요?
이미 Perl을 훌륭하게 사용하고 계신 많은 분들이 있지만,
저처럼 전산 전공이 아닌, 일반적 지식만을 갖춘 *잠재적 Perl 이용자*께 이 글을 바칩니다.
화이팅! ;-)


생각해볼 거리
--------------

지금 프로그램은 파일을 저장하는 경로가 정해져있습니다.
실행하는 시점에 인자로 넘긴다던가, 또는 설정파일을 참고하도록 수정할 수도 있겠지요.
또한 트레이 아이콘에서 오른쪽 버튼을 눌렀을때 바로 종료가 되는 것이 아니라
팝업 메뉴를 띄운 후 설정을 변경하거나 종료하는 옵션을 추가할 수도 있을 것입니다.
나머지는 여러분의 상상력에 맡기겠습니다.

마지막으로 이 프로그램에 단축키 기능을 추가한다면 어떨까요?
이를테면 윈도우 환경 어디서나 CTRL-F12 를 클릭하면 이 기능이 동작하도록 하는 것입니다.
윈도우용 Perl은 [Win32::API][cpan-win32-api] 모듈을 이용해 윈도우 기본 API를 직접 사용할 수 있기 때문에,
`user32.dll`의 `RegisterHotKey/UnregisterHotKey`를 임포트해 적당한 키 조합을 등록하고
키보드 입력을 전역으로 모니터링(후킹)하면, 단축키를 이용한 스크린샷의 조작이 가능합니다.
단축키 방식을 도입할 경우 지금까지 사용한 전체 화면 스크린캡쳐 외에도,
`Alt + PrtSc`를 사용해 현재 활성화 된 창만을 갈무리 할 수 있습니다.
지금처럼 아이콘을 클릭하는 방식이라면 아이콘을 클릭하는 순간 활성창이
작업표시줄로 바뀌기 때문에, 항상 작업 표시줄만 갈무리 될 것입니다.
기사에서 사용한 모든 화면은 단축키까지 구현한 완전한 스크립트를 이용해서 갈무리한 것입니다.
`PAR::Packer`로 빌드한 [실행 바이너리와 전체 소스][capture-source]도
꼭 다운로드 받아서 확인해보세요. ;-)



[img-01]:                   2011-12-07-1.png width=700
[img-02]:                   2011-12-07-2.png width=700
[img-03]:                   2011-12-07-3.png width=700

[img-01-original]:          2011-12-07-1.png
[img-02-original]:          2011-12-07-2.png
[img-03-original]:          2011-12-07-3.png

[activeperl]:               http://www.activestate.com/activeperl
[blog-owl0908]:             http://www.dormouse.pe.kr
[capture-source]:           http://www.nightowl.pe.kr/software/prtscrsave
[cpan-gd]:                  https://metacpan.org/module/GD
[cpan-image-magick]:        https://metacpan.org/module/Image::Magick
[cpan-imager]:              https://metacpan.org/module/Imager
[cpan-par-packer]:          https://metacpan.org/module/Par::Packer
[cpan-win32-api]:           https://metacpan.org/module/Win32::API
[cpan-win32-clipboard]:     https://metacpan.org/module/Win32::Clipboard
[cpan-win32-console]:       https://metacpan.org/module/Win32::Console
[cpan-datetime]:            https://metacpan.org/module/DateTime
[cpan-file-slurp]:          https://metacpan.org/module/File::Slurp
[cpan-win32-gui-dibitmap]:  https://metacpan.org/module/Win32::GUI::DIBitmap
[cpan-win32-gui]:           https://metacpan.org/module/Win32::GUI
[cpan-win32-guitest]:       https://metacpan.org/module/Win32::GuiTest
[cpan-win32-hideconsole]:   https://metacpan.org/module/Win32::HideConsole
[home-bmp2png]:             http://cetus.sakura.ne.jp/softlab/b2p-home/
[home-png]:                 http://www.libpng.org/pub/png/pngcode.html
[strawberry-perl-514]:      http://strawberryperl.com/beta/index.html
[strawberry-perl]:          http://strawberryperl.com



( run in 1.643 second using v1.01-cache-2.11-cpan-84e82930d8c )