project:pysubrenamer
차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판이전 판다음 판 | 이전 판 | ||
project:pysubrenamer [2013/01/26 02:55] – 127.0.0.1 | project:pysubrenamer [2014/10/09 21:24] (현재) – 바깥 편집 127.0.0.1 | ||
---|---|---|---|
줄 1: | 줄 1: | ||
+ | ====== pySubRenamer (GUI 시작하기) ====== | ||
+ | ===== 시작하며 ===== | ||
+ | 아주 간단하고 단촐한 GUI 기반의 프로그램을 만들어 보겠습니다. 이름은 ' | ||
+ | pySubRenamer의 완성 형태는 제가 클리앙에 공개한 MFC 기반의 SubRenamer가 아닌 프로토타입 버전과 거의 동일합니다. 오랜 기간 사용해보니 오히려 프로토타입의 기능 이상을 사용할 일이 없는데다 GUI가 매우 간단해 파이썬의 GUI 프로그래밍 연습용으로 쓰기 좋다고 생각해 이번에 다시 한번 작성해 보았습니다. | ||
+ | |||
+ | 사실 파이썬을 사용하려 했던 몇 가지 이유가 있었는데, | ||
+ | |||
+ | ===== GUI Toolkit ===== | ||
+ | 비주얼 프로그래밍 라이브러리, | ||
+ | MFC는 마이크로소프트 윈도우 운영 체제가 아니면 동작하지 않고 소스 공개도 되어 있지 않습니다. 이런 것들은 MFC의 단점입니다. 반면 좋은 점도 있습니다. 제가 생각하는 MFC의 장점과 단점은 대략 이렇습니다. | ||
+ | * 장점 | ||
+ | * 데스크탑 OS 중 가장 점유율이 높은 윈도우 기반이라, | ||
+ | * 오피스의 인터페이스, | ||
+ | * 비주얼 툴을 이용한 UI 제작이 매우 편리하다. | ||
+ | * ' | ||
+ | * 단점 | ||
+ | * Visual Studio Professional 이상이어야만 MFC를 이용 가능하다. 무료인 [[http:// | ||
+ | * 윈도우 OS 전용이다. | ||
+ | |||
+ | 한편 MFC 이외에 대안으로 쓸 수 있는, 크로스-플랫폼(Cross-Platform) 기반의 GUI 툴킷들이 많이 있습니다.[[http:// | ||
+ | |||
+ | | ||
+ | |||
+ | ===== 파이썬을 이용한 GUI 제작 ===== | ||
+ | ==== 장점과 단점 ==== | ||
+ | /*C/C++로 핵심 모듈을 만들고, UI 부분은 파이썬의 스크립트를 활용한다는 시나리오를 생각해 보았습니다. 왠지 굉장한 일처럼 느껴집니다. 그리고 서로 다른 언어가 하나로 똘똘 뭉쳐 일하는 모습도 멋져 보입니다. 적어도 저는 그렇습니다. 단일 언어, 일례로 C++로 UI와 핵심 모듈을 모두 제작한다고 생각하면 당연히 같은 언어에서 모든 사항이 구현되므로 UI와 다른 모듈이 매우 자연스럽게 융합될 것입니다. 속도도 빠르구요. 그러나 편의성은 조금 하락할 것입니다. UI 부분은 다소 제작하기 까다로운 단점도 있습니다. 물론 숙련된 분 앞에 무엇이 어렵겠습니까, | ||
+ | 파이썬을 이용해 UI를 제작했을 때 기존에 사용하던 MFC(혹은 C++ 기반의 GUI 툴킷)와 대비했을 때 생각나는 장점은 다음과 같습니다. | ||
+ | * 컴파일과 링크를 위해 복잡한 세팅을 할 필요가 없다. 인클루드, | ||
+ | * 지루한 컴파일, 링크가 사라진다. 에러가 생기면 바로 고쳐서 바로 확인할 수 있다. | ||
+ | * 대개 파이썬으로 바인딩된 GUI 툴킷 라이브러리 사용시 원래의 라이브러리보다 더 사용하기 쉽다. 이는 파이썬이 보다 유연한 문법과 풍부한 기본자료형을 제공하기 때문으로 생각한다. | ||
+ | |||
+ | 또한 생각나는 단점은 다음과 같습니다. 첫 항목은 이 문서에서 다루지 않기 때문에 ' | ||
+ | * 파이썬과 다른 언어를 이어주기 위한 별다른 절차가 필요하다. 이것이 예상외로 오버헤드가 될 수 있으리라 예상한다. | ||
+ | * MFC는 비주얼 기반 디자이너와 마법사를 이용할 수 있다. 이와 같이 강력한 툴을 구비할 수 없으면, 오히려 파이썬이 더 비생산적이다. | ||
+ | * 역시 파이썬이 성능상 조금 더 느리지 않을까? 아주 까다롭게 성능 이슈를 논하자면. | ||
+ | |||
+ | ==== wxGlade ==== | ||
+ | 앞서 언급했던 단점 중 두번째 항목인 ' | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | 좌측의 창은 wxGlade의 메인 메뉴 창입니다. wxGlade 프로그램 메뉴 및 위젯을 추가할 수 있는 메뉴가 구비되어 있습니다. 중앙의 창은 디자인하고 있는 UI의 구조를 트리로 보여줍니다. 각 항목을 선택하면 선택의 결과가 우측의 창에 반영됩니다. 우측 창에는 선택된 위젯에 대한 자세한 속성이 출력되고 사용자는 마우스 클릭으로 편하게 위젯에 대한 설정을 할 수 있습니다. | ||
+ | |||
+ | ===== SubRenamer 소개 ===== | ||
+ | ==== 제작 동기 ==== | ||
+ | 보통 한 동영상의 자막 파일과 영상 파일의 이름을 똑같이 해 두면 동영상이 재생될 때 자막도 자동으로 출력됩니다. 대개 이 한 쌍의 파일은 잘 맞춰진 상태로 되어 있지만, 필시 누군가가 미리 둘의 이름을 맞추어 놓았을 것입니다. 저는 자막 파일을 자막 제작자들의 블로그나 홈페이지에서 직접 구하는 편이라 영상 파일과 자막 파일의 이름이 서로 다를 때가 대부분입니다. | ||
+ | |||
+ | 처음에는 둘의 이름을 맞추기 위해 탐색기를 열고, 영상 파일의 이름을 찾아, 이름을 복사해(F2-> | ||
+ | 상당히 번거로웠습니다. 많은 목록 중 한 파일을 찾아는 것도 헷갈립니다. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | 하루에 1~2회정도 뿐이니 조금 불편해도 그냥 이대로 써도 되지만, 프로그래밍에 대해 눈을 뜨니 이런 불편을 계속 감수하고 쓰기 싫어지더군요. 게다가 별로 어려운 작업도 아닌 듯하니 직접 만들어보면 어떨까 하는 생각이 들었습니다. 그래서 처음엔 MFC 기반으로 아주 간결하게 프로토타입을 만들게 되었습니다. | ||
+ | |||
+ | 사용해본 결과 프로그램 자체는 매우 간단하지만 개인적으로 매우 요긴하고 유용한 툴이었습니다. 동영상과 자막의 이름을 맞출 때마다 사용하니 한 번에 오랜 시간을 이용하는 것은 아니지만, | ||
+ | |||
+ | ==== 프로그램의 목적 및 구상 ==== | ||
+ | 단순하기 짝이 없습니다. | ||
+ | * 특정 디렉토리 내부에 서로 짝이 맞지 않는 동영상 파일과 자막 파일의 목록만을 출력해 사람이 좀 더 편하게 식별할 수 있도록 한다. | ||
+ | * 두 목록 중에서 서로 짝이 맞는 것을 마우스 클릭으로 골라낼 수 있도록 한다. | ||
+ | * 자막 파일의 이름을 동영상 파일의 이름으로 바꾸어준다. | ||
+ | 궁극적으로 이 프로그램은 도스 명령어 | ||
+ | <code dos> | ||
+ | ren a.smi b.smi | ||
+ | </ | ||
+ | 를 하는 겁니다. 단지 a, b 부분을 일일이 찾아 입력하기 귀찮으니 부려 보는 하찮은 꼼수입니다. 대량의 파일에 대한 이름 변경은 전문적인 툴이 따로 있으니, 한 시즌이나 전 에피소드 전부에 대해 작업을 하려면 그쪽을 사용하면 됩니다. 이 프로그램은 단지 한번에 1~2개의 동영상, 자막 쌍을 맞추는 용도로 사용할 것입니다. 결코 복잡하거나 어려운 일을 하지 않습니다. | ||
+ | |||
+ | 다음 그림은 SubRenamer의 UI 그림입니다. 편의상 파이썬 기반으로 완성된 것으로 삽입하였습니다. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | ===== UI 구현하기 ===== | ||
+ | ==== 메인 다이얼로그 창 생성 ==== | ||
+ | 이제 wxGlade를 이용해 UI 디자인을 시작해 보겠습니다. wxGlade를 열어 아무 것도 없는 초기 상태로 만듭니다. pySubRenamer는 단순하게 다이얼로그 창으로 되어 있습니다. 트리 창에서 ' | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | 이렇게 하면 다이얼로그에 대한 디자인 창이 하나 더 생성됩니다. Properties 창의 내용이 바뀐 것에 주목하세요. Properties 창에 ' | ||
+ | |||
+ | ==== 위젯 삽입 ==== | ||
+ | === 배치(layout) 결정 === | ||
+ | 프로그램은 5개의 StaticText 위젯({{: | ||
+ | |||
+ | BoxSizer는 사각형의 레이아웃 장치로, 수직/ | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | 그러면 우선 그림의 붉은 색과 푸른 색 사각형으로 그린 수평/ | ||
+ | |||
+ | BoxSizer 아이콘을 클릭하고 Design 창으로 커서를 가져다대면 모양이 십자(+)로 변합니다. ' | ||
+ | |||
+ | * mainSizer | ||
+ | * 수직 방향 | ||
+ | * 8개의 슬롯을 가짐 | ||
+ | * pathSizer | ||
+ | * mainSizer의 첫번째 슬롯 | ||
+ | * 수평 방향 | ||
+ | * 3개의 슬롯을 가짐 | ||
+ | * movieSizer | ||
+ | * mainSizer의 여섯번째 슬롯 | ||
+ | * 수평 방향 | ||
+ | * 2개의 슬롯을 가짐 | ||
+ | * subtitleSizer | ||
+ | * mainSizer의 일곱번째 슬롯 | ||
+ | * 수평 방향 | ||
+ | * 2개의 슬롯을 가짐 | ||
+ | * footerSizer | ||
+ | * mainSizer의 여덟번째 슬롯 | ||
+ | * 수평 방향 | ||
+ | * 4개의 슬롯을 가짐 | ||
+ | Sizer만 나열하고 나면 Tree와 Design은 아래 그림과 같이 될 것입니다. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | === 각 요소 배치 === | ||
+ | 각 슬롯에 정해진 나머지 위젯들을 배열합니다. 위젯 아이콘을 클릭한 후 지정된 Sizer의 슬롯에 마우스 커서를 가져가면 커서가 십자로 변합니다. 그 상태에서 클릭하면 슬롯마다 위젯이 삽입됩니다. 모든 위젯의 삽입이 끝나면 아래 그림처럼 됩니다. 아직은 뒤죽박죽입니다. 그럴듯한 모습이 되기 위해서는 각 위젯마다 알맞은 속성을 지정해 주어야 합니다. 우선은 각 위젯의 이름을 그림과 같이 변경해둡니다. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | |||
+ | ==== 위젯의 속성 지정 ==== | ||
+ | 이제 각 위젯마다 세세하게 속성을 지정하여 맵시있게 모양을 다듬습니다. 아래 표는 각 위젯의 수정된 속성을 정리한 것입니다. | ||
+ | |||
+ | ^위젯 이름 | ||
+ | ^mainDialog (MyDialog) | ||
+ | ^::: |::: | ||
+ | ^::: |Widget | ||
+ | ^pathSizer | ||
+ | ^::: |::: | ||
+ | ^labelPath | ||
+ | ^::: |Widget | ||
+ | ^textCtrlPath | ||
+ | ^::: |::: | ||
+ | ^::: |::: | ||
+ | ^::: |Widget | ||
+ | ^buttonPath | ||
+ | ^::: |Events | ||
+ | ^labelMovie | ||
+ | ^::: |Widget | ||
+ | ^listCtrlMovie | ||
+ | ^::: |::: | ||
+ | ^::: |::: | ||
+ | ^::: |Widget | ||
+ | ^::: |Events | ||
+ | ^labelSubtitle | ||
+ | ^::: |Widget | ||
+ | ^listCtrlSubtitle | ||
+ | ^::: |::: | ||
+ | ^::: |::: | ||
+ | ^::: |Widget | ||
+ | ^::: |Events | ||
+ | ^movieSizer | ||
+ | ^::: |::: | ||
+ | ^::: |::: | ||
+ | ^labelChosenMovie | ||
+ | ^::: |Widget | ||
+ | ^textCtrlChosenMovie | ||
+ | ^::: |::: | ||
+ | ^::: |Widget | ||
+ | ^subtitleSizer | ||
+ | ^::: |::: | ||
+ | ^::: |::: | ||
+ | ^labelChosenSubtitle | ||
+ | ^::: |Widget | ||
+ | ^textCtrlChosenSubtitle |Layout | ||
+ | ^::: |::: | ||
+ | ^::: |Widget | ||
+ | ^footerSizer | ||
+ | ^::: |::: | ||
+ | ^::: |::: | ||
+ | ^checkboxRecursive | ||
+ | ^::: |::: | ||
+ | ^::: |::: | ||
+ | ^::: |Widget | ||
+ | ^::: |Events | ||
+ | ^buttonScan | ||
+ | ^::: |Events | ||
+ | ^buttonAccept | ||
+ | ^::: |Events | ||
+ | ^buttonExit | ||
+ | ^::: |Events | ||
+ | |||
+ | 모든 속성을 입력하고 나면 mainDialog를 선택합니다. Common 속성 아래에 ' | ||
+ | |||
+ | |||
+ | ==== 코드 생성하기 ==== | ||
+ | 모든 위젯의 배치와 속성이 알맞게 수정되었다면 이제는 파이썬 코드로 결과를 출력해야 합니다. Tree 창에서 Application을 선택한 후 Properties 창 안의 내용을 다음과 같이 수정합니다. | ||
+ | |||
+ | ^탭 ^키워드 | ||
+ | ^Application |Name |pySubRenamer | ||
+ | ^::: | ||
+ | ^::: | ||
+ | ^::: | ||
+ | ^::: | ||
+ | |||
+ | ' | ||
+ | |||
+ | ==== 이벤트 핸들러 작성하기 ==== | ||
+ | 출력한 후 바로 코드를 실행해 올바르게 창이 동작하는지 확인해 봅니다. 핸들러 함수는 지정했지만, | ||
+ | |||
+ | 또한 각 핸들러의 역할은 자명하므로 세세한 설명은 생략하도록 하겠습니다. 핸들러의 상세한 구현은 최종 소스를 참고하기 바랍니다. 완성되어 안정적으로 동작하는 소스는 확장자를 pyw로 변경해도 됩니다. 이렇게 하면 콘솔 윈도우가 생성되지 않습니다. | ||
+ | |||
+ | |||
+ | ===== 코어 구현하기 ===== | ||
+ | 하는 일이 단순하니 코어라고 해 봐야 그리 길지 않습니다. 이 부분은 MFC 기반으로 작성할 때 MFC로도, Boost 라이브러리를 이용해서도 작성해 보았습니다. 속도는 역시 C++가 더 낫지만 코드의 간결함은 파이썬을 따라올 수 없었습니다. 코어 함수가 하는 일은 간단합니다. 지정된 디렉토리의 파일의 이름을 보고 동영상 파일이면 같은 이름의 자막 파일이 있는지 확인하고, | ||
+ | |||
+ | |||
+ | |||
+ | ===== 마치며 ===== | ||
+ | 본 문서에서는 다이얼로그 창 형태의 동영상과 자막의 쌍을 맞추는 아주 단순한 GUI 툴을 디자인하고 제작해 보았습니다. | ||
+ | |||
+ | 파이썬의 간결함에 다시 한 번 놀랬습니다. 같은 프로그램을 C++ MFC로도, 파이썬으로도 작성해 보았습니다. 코드 양이 이 정도로 적어질줄은 상상도 하지 못했습니다. 코어 부분은 주석까지 포함해 100줄이 되지 않았고, wxGlade가 생성해준 UI 코드 또한 핸들러와 추가 코드를 포함하여 230줄 내외로 작성되었습니다. C/C++ MFC GUI 코딩보다 훨씬 간결합니다. wxGlade를 처음 사용할 때는 매우 어색해서 이걸로 어떻게 결과를 만들어낼지 막막했는데, | ||
+ | |||
+ | 물론 일정 수준 이상의 GUI 프로그래밍을 하려면 여러 지식이 필요합니다만, | ||
+ | |||
+ | ====== 최종 코드 ====== | ||
+ | 최종 결과 코드를 ZIP 파일로 압축하였습니다. 총 3개의 파일이 안에 있습니다. 프로그램을 실행하려면 [[http:// | ||
+ | *pySubRenamer.wxg: | ||
+ | *pySubRenamerGUI.pyw: | ||
+ | *SubRenamerCore.py: | ||
+ | |||
+ | |||
+ | |||
+ | {{: |