파이썬 강의/wx

파이썬 GUI(wxPython) 6. Sizer(레이아웃) : GridSizer, FlexGridSizer, GridBagSizer

마리사라 2021. 1. 9. 17:37
반응형

이번 강의는 저번 강의에 이어서 레이아웃 기능 중 하나인 GridSizer, FlexGridSizer, GridBagSizer를 알아보겠습니다.

 


1. GridSizer

Grid는 일반적으로 이차원의 직선이나 곡선으로 이루어진 격자를 뜻합니다.

(0, 0) (1, 0) (2, 0) (3, 0)
(0, 1) (1, 1) (2, 1) (3, 1)
(0, 2) (1, 2) (2, 2) (3, 2)
(0., 3 (1, 3) (2, 3) (3, 3)
(0, 4) (1, 4) (2, 4) (3, 4)

위와 같은 직선으로 이루어진 격자 공간에 위젯을 배치하는 것을 GridSizer라고 합니다. 위의 경우에는 5 x 4의 GridSizer라고 할 수 있습니다.

 

GridSizer도 이전 시간의 BoxSizer와 비슷하게 이루어집니다.

button_sizer = wx.GridSizer(5, 4, 0, 0)

GridSizer의 각각의 파라미터는 다음을 뜻합니다.

  • rows : 행의 갯수
  • cols : 열의 개수
  • vgap : 가로 단위의 공간. 이 수치만큼 각각의 위젯들은 가로로 벌어집니다.
  • hgap : 세로 단위의 공간. 이 수치만큼 각각의 위젯들은 세로로 벌어집니다.

저는 5 x 4의 GridSizer를 선언해 보았습니다.

 

sizer.Add(button_sizer, 1, wx.EXPAND, 5)

이것을 원래의 BoxSizer의 밑에 추가하면 BoxSizer에 포함된 위젯의 밑에 GridSizer에 포함된 위젯들이 선언된 행렬의 크기로 들어갑니다.

BoxSizer의 위젯들
GridSizer의 위젯들      
       
       
       
       

순서를 바꾸고 싶다면 Add의 순서를 GridSizer부터 선언하시면 됩니다.

sizer.Add(button_sizer, 1, wx.EXPAND, 5)
sizer.Add(self.button, 0, wx.ALL, 5)
sizer.Add(self.button2, 0, wx.ALL, 5)

위와 같이 선언하면 GridSizer의 위젯이 먼저 들어가고, 그 밑에 BoxSizer의 위젯들이 들어가게 됩니다.

 

이제 GridSizer에 버튼들을 추가해보겠습니다. 이때 버튼을 하나하나 선언하여 추가하기엔 비효율적이므로 for문을 통해서 추가하겠습니다.

for i in range(20):
    button = wx.Button(self, label='버튼')
    button_sizer.Add(button)

이렇게 하면 20개의 버튼이 GridSizer에 추가되게 됩니다.

 

 

이제 결과를 보겠습니다.

버튼 20개가 GridSizer에 추가된 모습

총 20개의 버튼이 GridSizer에 추가되었음을 확인할 수 있습니다.


2. FlexGridSizer

FlexGridSizer는 일반적인 GridSizer와 비슷합니다. 하지만 Flex라는 이름에서 알 수 있듯이 유연한 GridSizer입니다. FlexGridSizer의 위젯들은 화면의 크기에 따라서 위치를 바꿀 수 있습니다.

 

button_sizer = wx.FlexGridSizer(5, 4, 0, 0)

FlexGridSizer는 일반적인 GridSizer의 선언 방식과 완전히 똑같습니다. 하지만 위의 코드에서 GridSizer를 FlexGridSizer로 바꾸기만 해서는 어느 부분이 Flex인지 알기 힘듭니다.

FlexGridSizer 이미지 1
FlexGridSizer 이미지 2

그 이유는 FlexGridSizer에는 추가적으로 몇 개의 행과 열이 Flex 할지 정해주는 코드가 필요하기 때문입니다.

button_sizer.AddGrowableCol(1)
button_sizer.AddGrowableRow(1)

위의 코드를 통해 행과 열에 1줄씩 Flex 하도록 만들었습니다. 이제 결과를 확인해보겠습니다.

Flex가 적용된 FlexGridSizer 이미지 1
Flex가 적용된 FlexGridSizer 이미지 2

버튼 위젯들이 화면 크기에 따라서 위치가 바뀌는 것을 볼 수 있습니다.


3. GridBagSizer

GridBagSizer는 앞의 GridSizer의 진화형입니다. GridSizer가 단순히 추가된 순서대로 위젯을 정해진 위치에 넣었다면, GridBagSizer는 가방을 싸듯이(Bag) 원하는 위치에 넣을 수 있습니다. 또한 가방의 주머니처럼 위젯이 차지하는 공간 또한 원하는 대로 바꿔줄 수 있습니다.

 

button_sizer = wx.GridBagSizer(5, 4)

GridBagSizer는 다른 GridSizer와 다르게 행렬의 크기만 파라미터로 받습니다. 그 이유는 위젯을 Add 할 때 나타납니다.

 

button_sizer.Add(button, (i * 2, i * 2), (2, 2), wx.EXPAND)

GridBagSizer는 위젯을 Add할 때에 추가적인 인수를 받습니다.

  • window : 어느 위젯을 포함시킬지
  • pos : 어느 위치에 위젯을 넣을지
  • sapn : 위젯이 차지하는 크기는 얼마인지
  • flag : 어떠한 방식으로 위젯을 넣을지

저는 위의 for문을 그대로 가져가기에 각각의 위젯들은 (2, 2)의 공간을 가지며, 그 공간에서 최대의 크기를 가지고(wx.EXPAND), for문의 인수 i의 두배에 해당하는 (x, y) 좌표에 위치(i * 2, i * 2)시키도록 합니다.

 

GridBagSizer 이미지 1

결과 이미지를 보시면 각각의 위젯들은 좌표를 가지며, 원래의 버튼 크기(버튼 2)보다 세로 길이가 살짝 더 큰 것을 볼 수 있습니다.

GridBagSizer 이미지 2

또한 GridBagSizer를 선언할 때 정했던 범위를 벗어나는 것을 볼 수 있습니다. 이것이 다른 GridSizer들에는 없는 GridBagSizer만의 특징입니다.


4. 마치며

이번 시간에는 GridSizer의 3가지 종류에 대해서 알아보았습니다. 다음 시간에는 문자를 표현할 수 있는 StaticText와 TextCtrl에 대해서 알아보겠습니다.

import wx


class Mainframe(wx.Frame): # GridSizer 기준 코드
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(500, 500))

        # sizer = wx.BoxSizer(wx.VERTICAL)
        sizer = wx.StaticBoxSizer(wx.VERTICAL, self, '박스')
        button_sizer = wx.GridSizer(5, 4, 0, 0)
        # button_sizer = wx.FlexGridSizer(5, 4, 0, 0)
        # button_sizer = wx.GridBagSizer(5, 4)

        self.button = wx.Button(self, id=1, label='버튼')
        self.button2 = wx.Button(self, id=2, label='버튼2')

        self.Bind(wx.EVT_BUTTON, self.ButtonClick, id=1)
        # self.Bind(wx.EVT_BUTTON, self.ButtonClick, self.button)

        menu_bar = wx.MenuBar()

        menu = wx.Menu()
        menu_bar.Append(menu, '메뉴')
        menu.Append(101, '하위 메뉴')
        menu.Append(102, '종료')
        self.SetMenuBar(menu_bar)

        self.Bind(wx.EVT_MENU, self.MenuClick, id=101)
        self.Bind(wx.EVT_MENU, self.Quit, id=102)

        sizer.Add(self.button, 0, wx.ALL, 5)
        sizer.Add(self.button2, 0, wx.ALL, 5)
        sizer.Add(button_sizer, 1, wx.EXPAND, 5)

        # button_sizer.AddGrowableCol(1)
        # button_sizer.AddGrowableRow(1)

        for i in range(20):
            button = wx.Button(self, label='버튼')
            button_sizer.Add(button)
            # button_sizer.Add(button, (i * 2, i * 2), (2, 2), wx.EXPAND)

        self.SetSizer(sizer)

    def ButtonClick(self, event):
        dialog = wx.MessageDialog(self, '정말로 누르셨습니까?', '확인', wx.YES_NO)
        # dialog.ShowModal()
        if dialog.ShowModal() == wx.ID_YES:
            print("예")
        else:
            print("아니오")
        dialog.Destroy()

    def MenuClick(self, event):
        dialog = wx.MessageDialog(self, '메뉴를 누르셨습니다', '메뉴', wx.OK)
        dialog.ShowModal()
        dialog.Destroy()

    def Quit(self, event):
        dialog = wx.MessageDialog(self, '종료하시겠습니까?', '종료', wx.CANCEL)
        if dialog.ShowModal() == wx.ID_OK:
            quit(0)
        else:
            pass
        dialog.Destroy()


def run():
    app = wx.App()
    frame = Mainframe(None, -1, 'Calculator')
    frame.Show()
    app.MainLoop()


run()
반응형