Home » ASCIIMATIC 101

ASCIIMATIC 101

파이썬으로 가장 GUI를 구현하는 방법으로는 여러 가지가 있다. 기본으로 내장된 Tkinter 도 있고, wxPython 이나 pyQt 같은 것까지. 개인적으로 매우 간단한 UI를 갖춘 프로그램을 작성하려고 했지만 이래 저래 복잡해서 뭐 좀 없을까 하고 하다가 asciimatics를 알게되었다. asciimatics는 기본적으로 아스키문자를 사용해서 콘솔 화면에 애니메이션을 구현하는 라이브러리인데, 이 중에서 입출력과 관련된 기능도 있고, 간단한 TUI를 구현하는 기능 역시 존재한다. 이를 사용하면 간단한 폼이나 대화상자 등을 시각적으로 구현할 수 있고, 그외 이것 저것 재미난 것들을 화면에 구현해볼 수 있다. (이미지를 텍스트로 변환해서 표시한다던지…)

그래서 몇 개의 포스팅에 걸쳐서 asciimatics를 사용하는 방법에 대해서 차근차근 알아보도록 하겠다. 오늘은 먼저 기본적인 개념과 아주 간단한 몇 가지 예제를 만들어보자. 참고로 asciimatics는 모든 플랫폼에서 사용 가능하며, pip로 간단히 설치할 수 있다. (pip install asciimatics)

먼저 간단하게 화면에 문자를 찍어보도록 하자. 다음 코드는 화면 가운데 빨간 글씨로 텍스트를 출력하고 이를 3초간 보여준다.

from asciimatics.screen import Screen
from time import sleep
def demo(screen: Screen):
    s = "ASCIIMATICS!!!"
    x = (screen.width - len(s)) // 2
    y = screen.height // 2
    screen.print_at(s, x, y, screen.COLOUR_RED)
    screen.refresh()
    sleep(3)
Screen.wrapper(demo)

이 예제는 ASCIIMATICS가 어떤식으로 작동하는지를 간단하게 보여준다. Screen 객체는 콘솔 화면을 표현하는 객체이다. 현재 콘솔의 화면을 직접적으로 얻기보다는, 라이브러리가 제공하는 screen 인스턴스를 사용한다고 생각하면 된다. 스크린 객체를 인자로 받는 함수 demo를 하나 작성하고, 이 함수를 Screen.wrapper() 함수에 전달해서 호출하면, demo에 의해서 화면을 조작하는 내용이 콘솔에 출력된다.

print_at(ext, pos_x, pos_y, fg=, attr=, bg=)의 형식으로 문자를 특정 위치에 출력한다. screen 그 자체는 아마도 저수준 그래픽 라이브러리에서 사용하는 context 객체의 개념과 비슷하다고 이해된다. 어쨌든 여기에 뭔가 찍어서 그림/글자를 그렸다면 이를 화면에 적용해주기 위해서는 refresh()를 호출해주면 된다. 이후 바로 종료하지 않도록 sleep()을 3초간 해주었다. wrapper() 함수가 리턴되면 screen 객체는 파괴되고 화면은 다시 평소의 콘솔 화면 모양으로 돌아오게 된다.

문자열의 속성은 전경색, 모양속성, 배경색 순으로 나열되어 있는데, 많이 쓰는 순서대로 나열되어 있다. 색상과 모양 속성은 다음과 같이 정의되어 있다.

  • COLOUR_BLACK = 0
  • COLOUR_RED = 1
  • COLOUR_GREEN = 2
  • COLOUR_YELLOW = 3
  • COLOUR_BLUE = 4
  • COLOUR_MAGENTA = 5
  • COLOUR_CYAN = 6
  • COLOUR_WHITE = 7
  • A_BOLD = 1
  • A_NORMAL = 2
  • A_REVERSE = 3
  • A_UNDERLINE = 4

Scene + Effect

asciimatics는 TUI를 만드는데도 사용될 수 있지만, 주 목적은 아스키코드 기반 애니메이션을 구현하는 라이브러리이다. 애니메이션에는 여러 타입이 있을 수 있는데, 여러 종류의 애니메이션을 추상화하여 Effect라는 클래스로 다룰 수 있게 했으며, 여러 효과들은 하나의 장면(Scene)에 모이게 된다. 그리고 화면(Screen)은 애니메이션을 구성하는 각각의 장면을 재생/렌더링하는 역할을 담당한다.

가장 간단하게 만들어 볼 수 있는 효과 두 가지로 예를 들어보자. StarsClock 이 있다. Stars는 말 그대로 빈 스크린에 별이 반짝이는 효과를 부여한다. Clock은 현재 시각을 무려 ‘아날로그’로 표현해준다.

먼저 별을 찍어보자.

from asciimatics.screen import Screen
from asciimatics.Scene import Scene
from asciimatics.effects import Stars
def demo(screen: Screen):
    scene = Scene(Stars(screen, 40), -1, name="Main")
    screen.play([scene])
Screen.wrapper(demo)
 

위 파일을 작성하고 실행하자, 화면이 검게 변했다가 별이 반짝이는 효과가 나는 것을 볼 수 있다. 애니메이션이 재생될 때 기본적으로 종료하는 키는 Q 이므로 Q를 누르면 프로그램이 종료된다.

Stars를 생성할 때, pattern=’..+.. …x… …*… ‘을 지정할 수 있다. 이건 기본 값인데, 마침표와 x, +, *와 공백이 매 프레임마다 표시되면서 별이 반짝이는 효과가 만들어진다. 자세히 들여다보면 모든 별이 각각 반짝이는 타이밍이 다르게 되어 있어서 한결 자연스럽다는 것을 알 수 있다.

시계 애니메이션

이번에는 시계를 만들어보자. 시계는 중심위치와 반경을 필요로 한다.

from asciimatics.screen import Screen
from asciimatics.scene import Scene
from asciimatics.effects Clock
def demo_clock(screen: Screen):
    r = int(min(screen.width, screen.height) * 0.45)
    x, y = screen.width // 2, screen.height // 2
    scene = Scene([Clock(screen, x, y, r)], -1, name="Main")
    screen.play([scene])
Screen.wrapper(demo_clock)

코드의 내용은 크게 다르지 않으니 어렵지 않게 이해할 수 있으리라고 본다. 실행해보면 역시 재밌는 결과가 표시된다.

 
                                                .^
                                               /'
                                             .^
                                            /'
                                          ./
                                         w'
                                       ./
                                      w'
                                    ./
                        wwwwwwww####o
            .www########^^^^^^^^    Y.
            '^^^                    ||
                                    'b
                                     #
                                     Y.

눈여겨볼 점은 Scene을 만들 때 단일 이펙트가 아니라, 이펙트의 리스트들을 넘겨준다는 것을 알 수 있다. 이 말은 한 장면에 여러 이펙트를 넘겨주면 Screen은 각각의 이펙트에 대한 프레임을 계산하여 그려주는 것으로 이해할 수 있다. 아래와 같이 Stars와 Clock을 같은 장면에 추가해서 재생하면 두 종류의 애니메이션이 한 번에 적용되는 것을 볼 수 있다.

def demo_all(screen: Screen):
    r = int(min(screen.width, screen.height) * 0.45)
    x, y = screen.width // 2, screen.height // 2
    scene = Scene(
        [
            Clock(screen, x, y, r),
            Stars(screen, randint(20, 40)),
        ],
        -1,
        name="Main",
    )
    screen.play([scene])

Renderer

어떤 이펙트 중에는 Renderer를 요구하는 것들이있다. 렌더러는 말 그대로 화면에 표시해야 할 시각적 요소를 아스키 문자로 된 텍스트로 렌더링하는 것이다. FigletText는 문자열을 그림문자로 표현하는 것이고 ImageFile, ColourImageFile은 이미지 파일을 텍스트로 표현해주는 것이다. 렌더러를 통해 변형된 정보는 rendered_text 속성으로 얻을 수 있다. 그외에 렌더러로 렌더링한 결과에 색을 입히는 RainBow 같은 렌더러도 있다.

다음 예는 FigletText와 Rainbow 를 사용해서 문자열을 만들고 이를 화면 중앙에 출력하는 예이다. 단순히 출력만 하기 위해서 Print 이펙트를 사용했다.

from asciimatics.effects import Print
from asciimatics.renderers import FigletText, Rainbow
from asciimatics.scene import Scene
from asciimatics.screen import Screen
def demo(screen: Screen):
    s = "ASCIIMATICS"
    text = Rainbow(screen, FigletText(s, font="basic"))
    prn = Print(screen, text, (screen.height - text.max_height) // 2)
    screen.play([Scene([prn], -1, name="Main")])
Screen.wrapper(demo)

참고로 피글릿텍스트는 여러 폰트를 지원한다. 사용 가능한 폰트의 목록은 여기에서 찾아볼 수 있다.

ImageFile 이나 ColourImageFile을 사용하면 아스키문자로 그림을 표현할 수 있다. 다음은 펭수짤을 텍스트로 렌더링한 것이다. 컬러 이미지의 경우에는 같은 문자의 색으로 표현하며, 단색 이미지를 렌더링하는 경우는 픽셀 영역 밝기에 따라서 사용하는 문자가 달라진다.

from asciimatics.effects import Print
from asciimatics.renderers import ImageFile, ColourImageFile
from asciimatics.scene import Scene
from asciimatics.screen import Screen
def demo(screen: Screen):
    img1 = ColourImageFile(screen, 'sample11.jpg', height=50)
    prn = Print(screen, img1, 0)
    screen.play([Scene([prn], -1, name="Main")])
Screen.wrapper(demo)

그외에 스프라이트 애니메이션 같은 것들도 있지만 오늘은 여기까지. 다음 시간에는 TUI는 어떻게 구성하고 사용하는지 알아보도록 하겠다.

태그:asciimatics
0 Comments

No Comment.