Kivy Interactive Applications in Python - Chapter 1

Chapter1. GUI Basics - Building an Interface

본 Post는 "Kivy Interactive Application in Python" 의 내용을 직접 번역한 글로 컨텐츠에 대한 저작권은 "Kivy Interactive Application in Python" 저자에게 있습니다.

Chapter 1. GUI Basics - Building an Interface

Kivy는 (Multitouch Application 을 위한 라이브러리인) PyMT를 잇는 모듈로 단순하지만 모든 플랫폼에서 같은 코드로 동작시키려는 목적은 같다. (Linux/Windows/Mac OSX/MacosX/Android/iOS(Mathieu Virbel, http:// txzone.net/2011/01/kivy-next-pymt-on-android-step-1-done/) 이러한 생각은 Kivy를 만든 Mathieu Virbel에 의해 시작된 라즈베리 파이로 확장된다. Kivy는 EuroPython 2011에서 UI를 제작하도록 설계된 Python 프레임워크로 소개되었다.

재미있고 강력한 Kivy Language (.kv)를 이용해 UI를 만들 수 있다. Kivy Language는 화면에 보여주는 부분과 코드를 분리할 수 있도록 한다. 이는 쉽고 직관적인 코드를 작성하도록 만드닌 기본적인 엔지니어링 개념이다. 그래도 여전히, 순수 Python 과 라이브러리로써 Kivy를 이용해 Kivy Application을 만들 수 있다. 다음 Chapter에서 동적 인터페이스에 대해 배우도록 하겠다.

이 Chapter에서는 Kivy의 GUI를 작성하기 위한 기초를 다룰 것이다. 나중에 원하는 어떤 GUI든 만들 수 있고 심지어 Windows 크기에 반응하도록 만들 수 있다. 다음은 앞으로 다룰 기술 목록들이다.

  • Kivy Application 실행하기
  • Kivy Language
  • Widget를 생성하고 사용하기
  • Widget의 기본 속성과 변수
  • 고정 좌료, 비례 좌표, 절대 좌표, 상대 좌표
  • Layout을 이용한 GUI 구성하기
  • 반응형 GUI를 만들기 위한 Tip

Python에서는 Object-Oriented Programming에 대한 개념을 숙지해야 한다. 특히, 상속, 인스턴스클래스의 차이를 이해해야 한다. 시작하기 앞서, Kivy를 설치해야 한다. 이책의 예제는 Kivy 1.7.0에서 테스트되었지만 최신 버전ㅇ에서도 동작할 것이다.

이 Chapter의 마지막에, 연필과 종이 스케치로 시작된 GUI를 만들수 있을 것이다. 이 책의 Main 프로젝트를 소개하겠다.

Hello World!

첫번째 코드를 작성해 보자. 다음은 Hello World 프로그램이다.

In [ ]:
# File Name : hello.py
import kivy
kivy.require("1.7.0")

from kivy.app import App
from kivy.uix.button import Label

class HelloApp(App):
    def build(self):
        return Label(text="Hello World!")

if __name__ == "__main__":
    HelloApp().run()
In [ ]:
%save -r hello.py 1 14

이는 Python 코드이다. Kivy 프로그램 실행은 다른 Python Application 실행과 다르지 않다. 코드를 실행하기 위해서, terminal을 실행해 다음 명령을 실행하면 된다.

> python hello.py --size=150x100

(--size는 출력 크기를 지정하는 파라미터다.) 실행 이전에 코드를 살펴보면, Line2와 Line3은 컴퓨터에 설치된 Kivy의 버전이 실행가능한 버전인지 검증하는 코드이다.

만약 예전 버전의 Kivy에서 Application을 실행하면, 특정 버전에 대한 예외가 발생한다. 최신 버전을 사용한다면 예외가 발생하지 않는다. 물론 하위 호환성을 지원하지만 항상 가능하진 않는다. 그래서 최신 버전에서는 문제가 발생할 수 있다. 

이 책에서 대부분의 예제에 대한 코드를 지원하지 않는다. 하지만 인터넷에서 코드를 찾아 다운로드해 실제 프로젝트에 사용할 수 있다. 이 프로그램의 Kivy 라이브러리(Line 5, 6)로 부터 2개의 클래스를 사용한다. : App과 Label. App 클래스는 Kivy Application의 시작 지점이다. 다음 그림에서 Hello World 문자를 Label에 출력한 Window를 볼 수 있다.

우리는 App 클래스를 상속해 HelloApp의 기본 클래스가 되었다.(Line 8) 실제로, 이는 App 클래스의 모든 속성과 메소드를 갖는 HelloApp이 되었음을 HelloApp 클래스의 Line 9, 10에 정의했다. 이 경우, HelloApp은 단지 App 메소드 중 하나인 build(self) 메소드를 수정한 것에 그친다. 이 메소드는 windows content를 반환한다. 이때, Label은 Hello World!를 출력한다. (Line 10) 마지막으로, Line 13에서 HelloApp의 인스턴스를 생성해 실행한다.

Kivy는 단지 Python의 라이브러리일 뿐인가? 맞다. 그러나 라이브러리의 일부로써, Kivy는 화면 출력 부분과 코드를 분리하는 Kivy 만의 Language를 제공한다. 예를 들어, 2개로 나누어진 파일에서 Python 코드를 작성할 수 있다. 첫번째 파일에 다음과 같은 Python 코드를 작성한다.

In [ ]:
# File name : hello2.py
from kivy.app import App
from kivy.uix.button import Label

class Hello2App(App):
    def build(self):
        return Label()
    
if __name__ == "__main__":
        Hello2App().run()
In [ ]:
%save -r hello2.py 1 10

hello2.py 코드는 hello.py와 매우 비슷하다. 차이는 Line 20에서 Hello World! 메시지가 없다는 것 뿐이다. 대신, 메시지는 Kivy Language로 작성된 두번째 파일의 text property로 옮겼다. (hello2.kv)

In [ ]:
# File name : hello2.kv
#:kivy 1.7.0
<Label>:
    text: "Hello World!"
In [ ]:
%save -r hello2.kv 1 4

Python 또는 Kivy는 어떻게 관련 파일을 실행할까? 이 점은 매우 중요하고 시작할 때 혼란을 야기한다. App의 서브 클래스의 이름, HelloApp이 핵심이다.

App의 서브 클래스 이름 초기화는 Kivy 파일의 이름과 일치해야 한다. 예를 들어, 만약 클래스 이름은 Class FooApp(App)으로 정의했다면, Kivy 파일명은 foo.kv이 되고 (App의 run() 메소드를 실행하는) Main 파일과 동일한 경로에 위치해야 한다.

이 예제는 hello.py를 실행시켯던 방법과 동일한 방법으로 실행한다. 단지, 새로운 Main 파일(hello2.py)을 실행한다는 점만 다르다.

> python hello2.py --size=150x100

Kivy Language를 사용한 첫번째 프로그램를 살펴보자. Hello2.kv 파일의 "#:Kivy 1.7.0"는 Python에세 Kivy의 최소 버전을 알리는 코드이다. 이 코드는 hello.py의 Line2, 3과 같은 의미이다. Kivy Language 헤더의 "#:" 명령어는 지시어이다. 앞으로 이 책에서 버전 지시어를 별도로 표기하지 않는다. < Label: > (Line 26)은 Label 클래스의 text property (Line 27)를 "Hello World!"로 설정을 변경한다는 것을 의미한다. 이 코드는 이전 그림에서 보여준 것과 같은 결과를 보여준다. 첫번째 예제인 hello.py과 같이 순수하게 Python만 사용할 수 없고 Kivy 라이브러리로 부터 필요한 클래스를 가져와야 한다. 그러나, 화면 출력과 코드를 쉽게 분리했다. 그러므로, 이 책에서는 동적 컴포넌트를 추가히지 않는다면 Kivy Language를 통한 모든 화면 출력 프로그램을 설명한다.

우리가 Label 클래스를 수정하면 모든 인스턴스에 영향을 미치지 않을까 걱정할 것이다. 맞다. 그래서 다음 섹션에서, 우리는 클래스 대신 특정 인스턴스를 직접 수정하는 방법을 배울 것이다.

Basic Widget - Labels and Buttons

이전 섹션에서, 우리는 이미 Kivy가 제공하는 Widget 중 하나인 Label 클래스를 살펴봤다. Widget은 GUI를 설정하기 위해 사용되는 인터페이스 블럭이다. Kivy는 Buttons, Labels, checkboxes, dropdowns등을 갖는 Widget Set이다. kivy.uix 패키지에 포함된 Kivy API kivy.uix에서 모두 살펴볼 수 있다.

hello2.kv에서 한 것처럼 Kivy 클래스를 직접 사용하는 대신 Application을 위한 Widget을 생성하는 것이 더 좋은 방법이다. (Line 26) 다음 코드는 상속을 통한 방법을 보여준다.

In [ ]:
# File name: widgets.py
from kivy.app import App
from kivy.uix.widget import Widget

class MyWidget(Widget):
    pass

class WidgetApp(App):
    def budil(self):
        return MyWidget()
    
if __name__ == "__main__":
    WidgetApp().run()
In [ ]:
%save -r widgets.py 1 13

widgets.py의 Line32를 살펴보면, Widget 베이스 클래스로부터 상속받아 hello2.py에서 처럼 Kivy Label 클래스를 직접 수정하는 대신 Line 37에서 MyWidget 서브클래스를 생성했다. 나머지 코드는 이전 코드와 유사하다. 다음은 Kivy Language 코드이다. (widgets.kv)

In [ ]:
# File name: widgets.kv
<MyWidget>:
    Button:
        text: "Hello"
        pos: 0, 100
        size: 100, 50
        color: .8, .9, 0, 1
        font_size: 32
    Button:
        text: "World!"
        pos: 100, 0
        size: 100, 50
        color: .8, .9, 0, 1
        font_size: 32
In [ ]:
%save -r widgets.kv 1 14

Label 대신 Button을 사용했다. Kivy에서 대부분의 기본 Widget은 비슷한 방식으로 동작한다. 실제로, Button은 배경색과 같은 대부분의 속성을 갖는 Label의 서브 클래스이다. Hello2.kv의 Line 26(< Label: >)과 Widgets.kv의 Line 43(Button:)의 표기법을 비교해 보자. MyWidget의 Label을 위해 클래스 표기법(< Class: >)을 사용했지만 Button은 인스턴스 표기법(Instance:)을 사용했다. MyWidget은 2개의 Button 인스턴스를 갖는 것으로 정의했고 (Line 43, 49) 인스턴스의 속성을 정의했다. size와 pos 속성은 고정값으로 window에서 특정 픽셀이 된다.

(0, 0)은 Button-Left 코너를 가리킨다. (CSS와 같은) 다른 Language에서 (0, 0)은 Top-Left 코너이므로 주의하기 바란다. 

다음의 그림은 widgets.py와 widgets.kv 코드 파일의 결과를 보여준다.

이전 widgets.kv에서 두가지를 수정해야 한다. 첫번째는 pos, color, font_size와 같은 반복되는 Button의 속성이다. MyWidget에서 사용되는 Button을 생성해 보자. 두번째는 위치가 고정되어 있기 때문에 화면 크기가 변경될 때 위치가 고정되기 때문에 매우 귀찮다. widgets을 다음과 같이 수정했다.

In [ ]:
# File name: widgets2.kv
<MyButton@Button>:
    color: .8, .9, 0, 1
    font_size: 32
    size: 100, 50
    
<MyWidget>:
    MyButton:
        text: "Hello"
        pos: root.x, root.top - self.height
    MyButton:
        text: "World!"
        pos: root.right - self.width, root.y    
In [ ]:
%save -r widget2.kv 1 13

widget2.kv에서 (MyButton@Button)을 만들어 MyButton 클래스(Line 56, 59)와 인스턴스(Line 62, 67)를 새롭게 정의했다. MyWidget과 MyButton을 서로 다른 방법으로 정의했음을 주의깊게 봐야 한다. MyWidget (widgets.py의 Line 32)과 같이 Python 파일에서 기본 클래스를 정의하지 않는 경우 @Class로 정의해야 한다. 다시말해, Python 파일에서 MyWidget을 정의해야 한다. 직접 인스턴스화하기 위해선 Python 파일에서 MyWidget을 정의해야 한다. (widgets.py Line 37) 이 예에서, 각 Button의 위치는 Window의 크기와 상관없이 화면의 코너에 따라 달라진다. 이를 위해선, 우린 self와 root 변수를 사용해야 한다. 변수 self는 익숙할 것이다. 추측한 것처럼, Widget 자신을 가리킨다. 예를 들어, self.height (Line 64)는 MyButton에 의해 50이 된다. 변수 root는 Widget 클래스의 최상위 계층을 가리킨다. 예를 들어, root.x(Line 64)는 0으로 widget.py의 Line 37에서 생성한 MyWidget 인스턴스의 x값을 가리킨다. MyWidget 인스턴스는 WidgetApp에서 유일하기 때문에, 어디든 사용된다: 그러므로, 시작점은 (0, 0)이 된다. 또한 x, y, width, height는 widget 속성이다. 여전히, 고정 좌표는 Widget과 Window를 작성하기 좋은 방법이 아니다.

Layouts

고정 좌표를 사용하는 것은 N차원의 공간에서 Element를 구성하기에 가장 안정적인 방법임은 의심할 여지가 없다. 그러나, 고정 좌표는 많은 시간을 소모한다. 대신, Kivy는 좋은 Layout Set을 제공한다. Layout Set은 Widget을 구성하는 기능을 갖고 있다. Layout은 Widget의 서브 클래스로 임베딩된 Widget을 구성하기 위해 다른 방법을 사용한다. 예를 들어, 한가지 방법은 grid (GridLayout)를 이용하는 방법이다. 간단한 FloatLayout 예제를 작성해 보자. 다른 Widget에서 직접 Widget을 구성하는 것과 그 방법이 매우 유사하다. 차이점은 고정 좌표가 아닌 (전체 Window의 크기에 따른)비례 좌표를 사용한다는 점이다. 이는 이전의 self와 root를 연산할 필요가 없음을 의미한다. 다음 Python 코드를 보자.

In [ ]:
# File name : floatlayout.py

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout

class FloatLayoutApp(App):
    def build(self):
        return FloatLayout()

if __name__ == "__main__":
    FloatLayoutApp().run()
In [ ]:
%save -r floatlayout.py 1 11

FloatLayout (Line 75)를 사용했다는 점을 제외하고 (floatlayout.py) 이전 코드와 차이가 없다. 흥미로운 점은 Kivy Language에 있다. (floatlayout.kv)

In [ ]:
# File name: floatlayout.kv
<Button>:
    color: .8, .9, 0, 1
    font_size: 32
    size_hint: .4, .3
    
<FloatLayout>:
    Button:
        text: "Hello"
        pos_hint: {"x": 0, "top": 1}
    Button:
        text: "World!"
        pos_hint: {"right": 1, "y":0}
In [ ]:
%save -r floatlayout.kv 1 13

floatlayout.kv에서, 0부터 1까지의 범위에서 비례 좌표를 사용하는 size_hint와 pos_hint 2가지 속성을 사용했다.:(0, 0)은 Button-Left 코너이고 (1, 1)은 Top-Right 코너이다. 예를 들어, size_hint (Line 5)는 현재 Window 폭의 40%에 해당하는 폭과 현재 Window 높이의 30%에 해당하는 높이를 갖는다. pos_hint 또한 동일하게 연산되지만 표기법은 다르다. Python 딕션너리에서 Key는 Widget이 참조하는 부분으로, 예를 들어, "x"는 Left 경계를 가리킨다. Line 10에서 "y" 대신에 "top"을, Line 13에서 "x" 대신 "right"를 사용한다. "top"과 "right" 속성은 각각 Button의 top 모서리와 right 모서리를 가리킨다. 그래서 위치를 더욱 단순하게 만든다. "x", "y" 두 축을 사용하지 않는다. 예를 들어, Line 13에서 pos_hint:{"x": .85, "y": 0}와 같다. "right"와 "top"은 연산을 피하고 코드를 더욱 명확하게 만든다. 다음 그림은 pos_hint 딕셔너리에서 key를 이용한 코드의 결과를 보여준다.

pos_hint(x, center_x, right, y, center_y, top)은 모서리나 중앙을 맞추기에 유용하다. 예를 들어, pos_hint:{"center_x": .5, "center_y": .5}는 Window의 크기에 상관없이 Widget을 중앙에 정렬한다. top과 right를 이용해 widgets2.kv의 고정 위치를 지정할 수 있을까? (Line 64, 67) 가능하다. 그러나 pos는 Python 딕셔너리({"x":0, "y":0})에서 사용할 수 없다. 단지, 리스트만 사용할 수 있다.(0, 0) 그러므로, pos 속성을 사용하는 대신에, x, center_x, right, y, center_y, top을 사용해야 한다. 예를 들어, pos: root.x, root.top - self.height 대신다음과 같은 방식을 이용해야 한다.

x: 0
top: root.height

이러한 속성은 항상 고정값이다.

만약 비례 좌표를 사용하길 원한다면, Layout 내부에서 pos_hint 속성을 사용해야 한다. 

만약 Layout 인스턴스를 사용한다면, 고정값을 강제할 수 있을까? 물론 가능하다. 하지만 속성값을 주의깊에 사용하지 않으면 충돌이 발생할 수 있다. 만약 임의의 Layout을 사용한다면, pos_hint와 size_hint는 우선권을 갖는다. 만약 고정 위치 속성(pos, x, center_x, right, y, center_y, top)을 사용한다면, pos_hint 속성을 사용하지 않을 것을 확신해야 한다. 두번째로, 만약 size, height, width 속성을 사용하길 원한다면, size_hint를 None으로 설정해야 한다. 예를 들어, size_hint: (None, .10)는 height 속성을 사용할 수 있지만 width는 Window 크기의 10%가 적용된다. 다음 표는 위치 속성과 크기 속성에 대하 알아야 할 점을 요약했다. 첫번째와 두번째 열은 속성명과 개별 값을 가리키고 세번째와 네번째 열은 Layout과 Widget에서 사용가능 여부를 가리킨다.

Property Value For Layouts For Widget
size_hint [w, h] 쌍의 비율 (0부터 1사이 또는 None) Yes No
size_hint_x, size_hint_y 비율 (0부터 1사이 또는 None), 폭(size_hint_x), 높이(size_hint_y) Yes No
pos_hint x축(x, center_x, right)과 y축(y, center_y, top)을 갖는 딕셔너리(0부터 1사이의 값) Yes No
size [w, h]쌍, w는 고정폭, h는 고정 높이 Yes(단, size_hint:(None, None)으로 설정) Yes
width 고정값 Yes(단, size_hint_x: None) Yes
height 고정값 Yes(단, size_hint_y: None) Yes
pos [x, y]쌍으로 고정 좌표 Yes(단, pos_hint는 사용할 수 없다.) Yes
x, right, center_x 고정값 Yes(단, pos_hint에서 x, right, center_x는 사용할 수 없다.) Yes
y, top, center_y 고정값 Yes(단, pos_hint에서 y, top, center_y는 사용할 수 없다.) Yes

사용하는 Layout에 따라 속성이 다르게 동작할 수 있으므로 주의해야 한다. 현재 Kivy는 7개의 Layout을 지원한다.: 그중 6개는 다음 표에서 간단히 설명한다. 왼쪽 열은 Kivy Layout 클래스 명이고 오른쪽 열은 그 동작을 간단히 설명했다.

Layout Details
FloatLayout size_hint와 pos_hint 속성을 통한 비례 좌표로 Widget을 구성한다. 그 값은 Window 크기에 따른 비율을 가리키는 0부터 1사이의 값이다.
RelativeLayout FloatLayout과 같은 방법으로 구성되지만 위치 속성(pos, x, center_x, right, y, center_y, top)은 Window 크기가 아닌 Layout 크기에 따른 상대 좌표이다.
GridLayout grid로 구성된 Layout으로 적어도 cols(열)과 rows(행) 중 하나의 값은 지정해야 한다.
BoxLayout 수평(horizontal) 또는 수직(vertical)에 의존한 행이나 열로 Widget을 구성한다.
StackLayout BoxLayout과 유사하지만 공간이 부족할 때 다음 행과 열로 이동한다. StackLayout에서, 방향 설정이 매우 유연하다. 예를 들어, "rl-bt"는 오른쪽에서 왼쪽으로, 아래에서 위로 Widget을 구성한다. lr, rl, tb, bt 조합이 가능하다.
AnchorLayout 경계(border) 또는 중앙(center)로 Widget을 구성한다. anchor_x 속성은 x위치(left, center, right)를, anchor_y는 y위치(top, center, bottom)를 가리킨다.

7번째 Layout은 ScatterLayout으로 RelativeLayout과 유사하게 동작한다. 하지만 멀티터치 회전, 크기 조절, 변환을 지원한다. 그 구현이 약간 다르므로 나중에 살펴보도록 하겠다. Kivy API는 자세한 설명과 함께 각각에 대한 예를 제공한다. Layout에 따른 속성의 동작상의 차이는 예상과 많이 다르다. 다음은 GUI 제작 과정에 도움이 될만한 Hint이다.

  • size_hint, size_hint_x, size_hint_y 속성은 모든 Layout에서 동작한다. 그러나 차이가 있다. 예를 들어, GridLayout는 같은 행이나 열에서 x hint와 y hint의 평균을 구한다.
  • size_hint, size_hint_x, size_hint_y 속성은 0부터 1사이의 값이다. 그러나, 1보다 큰 값을 사용할 수 있다. Layout에 따라, Kivy는 container보다 큰 Button을 만들거나 같은 중심에서 hint의 합을 기준으로 비율을 재연산한다.
  • pos_hint 속성은 FloatLayout, RelativeLayout, BoxLayout에서만 동작한다. BoxLayout에서, x (x, center_x, right)는 수직과 반대로 동작한다. 비슷한 규칙은 고정 위치 속성(pos, x, center_x, right, y, center_y, top)에도 적용된다.
  • size_hint, size_hint_x, size_hint_y 속성은 size, width, height에 항상 None으로 설정한다.

각 Layout에 대한 더 자세한 특징이 있지만, 이 정도면 원하느 GUI를 만들 수 있다. 일반적으로, Layout을 사용하길 추천한다. 속성에 대한 이상한 속성을 설정하지 않도록 주의하길 바란다. 대신, 더 많은 Layout과 임베딩을 사용하는 것이 우리가 원하는 목적을 달성하는데 도움이 된다. 다음 섹션에서, 우린 임베딩 Layout에 대해 알아보고 포괄적인 예제를 살펴본다.

Embedding Layouts

이전 섹션에서 학습한 Layout은 Widget의 서브 클래스이다. 우린 이미 이 Chapter 시작부터 Widget안에 Widget을 임베딩해 보았다. 물론, 임베딩한 Widget이 Layout은 아니다. 다음 Python 코드는 임베딩 Layout에 대한 예제이다.

In [ ]:
# File name : layouts.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout

class MyGridLayout(GridLayout):
    pass

class LayoutApp(App):
    def build(self):
        return MyGridLayout()

if __name__ == "__main__":
    LayoutApp().run()
In [ ]:
%save -r layouts.py 1 13

코드에 새로운 부분은 전혀 없다. 단지 MyGridLayout 클래스를 선언한 것 뿐이다. 그 결과는 다음 그림의 첫번째 부분과 같다.

위 그림에서, Kivy Layout의 모든 타입은 GridLayout 2행 3열 내에 모두 임베딩되었다. 이는 중요한 예제이므로, 일치하는 Kivy Language 코드를 5개의 Fragment로 나누어 학습한다. (layouts.kv) 코드의 양에 압도되지 않기 바란다. 다음 코드는 Fragment 1이다.

In [ ]:
# File name: layouts.kv (Fragment 1)
<MyGridLayout>:
    rows: 2
    FloatLayout:
        Button:
            text: "F1"
            size_hint: .3, .3
            pos: 0, 0
    RelativeLayout:
        Button:
            text: "R1"
            size_hint: .3, .3
            pos: 0, 0
In [ ]:
%save -r layouts_f1.kv 1 13

위의 코드에서, 첫번째 줄(Line 3)에서 MyGridLayout의 행수를 정의한다. 그 후 각각 1개의 Button을 갖는 FloatLayout과 RelativeLayout 2개를 추가한다. 각 Button (F1과 R1)의 속성 pos: 0, 0을 정의한다. (Line 8, 13) 그러나 위의 그림을 보면, Button F1 (Line 6)은 전체 Window의 왼쪽 아래 코너에, Button R1 (Line 11)은 RelativeLayout의 왼쪽 아래 코너에 위치한다. 그 이윤 FloatLayout의 좌표는 Layout의 상대 위치가 아니다. 만약 pos_hint를 사용하면, 항상 상대 좌표로 사용되는 것은 아니다.

Fragment 2의 GridLayout은 다음과 같은 MyGridLayout을 추가했다.

In [ ]:
# File name: layouts.py (Fragment 2)
    GridLayout:
        cols: 2
        spacing: 10
        Button:
            text: "G1"
            size_hint_x: None
            width: 50
        Button:
            text: "G2"
        Button:
            text: "G3"
            size_hint_x: None
            width: 50
In [ ]:
%save -r layout_f2.kv 1 14

위의 코드에서, 2개의 열을 정의하고 (Line 3) 각각 10픽셀로 내부 Widget을 구분하기 위해 spacing 10 (Line 4)을 정의했다. 또한 위의 그림에서, 첫번째 열은 두번째 열보다 더 가늘다. 이를 위해 size_hint_x 속성을 None으로 정의하고 Button G1(Line 5)과 Button G3(Line 11)의 width를 50으로 설정했다.

Fragment 3의 AnchorLayout 코드는 다음과 같다.

In [ ]:
# File name: layouts.kv (Fragment 3)
    AnchorLayout:
        anchor_x: "right"
        anchor_y: "top"
        Button:
            text: "A1"
            size_hint: [.5, .5]
        Button:
            text: "A2"
            size_hint: [.2, .2]
In [ ]:
%save -r layout_f3.kv 1 10

위의 코드에서, anchor_x를 right로(Line 3), anchor_y를 top으로(Line 4) 지정했다. 위의 그림에서, AnchorLayout의 두 Button (Line 5, 8)은 오른쪽 위 코너에 위치한다. 이는 상위 메뉴바나 사이드 메뉴바와 같은 다른 Layout을 임베딩하기에 매우 유용하다

Fragment 4의 BoxLayout 코드는 다음과 같다.

In [ ]:
# File name: layouts.kv (Fragment 4)
    BoxLayout:
        orientation: "horizontal"
        Button:
            text: "B1"
        Button:
            text: "B2"
            size_hint: [2, 3]
            pos_hint: {"y": .4}
        Button:
            text: "B3"
In [ ]:
%save -r layouts_f4.kv 1 11

위의 코드에서, 수평 방향으로 BoxLayout이 출력된다. 또한 Line 8, 9에서, Button B2를 최대로 이동하기 위해 size_hint와 pos_hint를 사용했다.

마지막으로, Fragment 5의 StackLayout 코드는 다음과 같다.

In [ ]:
# File name: layouts.kv (Fragment 5)
    StackLayout:
        orientation: "rl-tb"
        padding: 10
        Button:
            text: "S1"
            size_hint: [.6, .2]
        Button:
            text: "S2"
            size_hint: [.4, .4]
        Button:
            text: "S3"
            size_hint: [.3, .2]
        Button:
            text: "S4"
            size_hint: [.4, .3]
In [ ]:
%save -r layouts_f5.kv 1 16

여기에서 다른 크기의 Button 4개를 추가했다. rl-tb 방향(Line 3)으로 Widget을 구성하기 위해 적용된 규칙을 이해하기 위해, 이전 그림을 참고하기 바란다. 또한, padding 속성 (Line 4)은 Widget과 StackLayout의 경계 사이에 공백 픽셀 10을 추가했다.

댓글

가장 많이 본 글