본문 바로가기

wxPython

파이썬 GUI, 다중 창 인터페이스 wx.MDIParentFrame

반응형

 

개요 

    MDI Frame (Multiple Document Frame)은 하나의 창 안에서 또 다른 자식 창을 열 수 있는 wxPython 프레임이다. 여기서 모든 창을 포함하는 Frame은 MDI Parent Frame(wx.MDIParentFrame)이고, MDI Parent Frame 안에 들어가는 창은 MDI Child Frame(wx.MDIChildFrame) 이다. 하나의 프로그램 안에서 개별 창을 띄워 GUI를 관리하고자 할 때 유용하게 사용할 수 있다. 

 

wx.MDIParentFrame 

    MDI Parent Frame의 선언은 아래와 같이 기본 Frame과 동일하게 할 수 있다. 

wx.MDIParentFrame(parent, id=wx.ID_ANY, title="",
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_FRAME_STYLE|wx.VSCROLL|wx.HSCROLL, name="")

    그러나 보통 Frame의 경우와 마찬가지로, MDI Parent Frame도 사용자 Window 클래스를 별도로 생성하여 MDI Parent Frame을 Super Class로 상속하여 사용하는 경우를 추천한다. 

# 일반 Frame과 마찬가지로 MDIParentFrame을 상속받아 Frame 클래스를 작성한다
class UserMDIFrame(wx.MDIParentFrame):
    def __init__(self, parent):
        # 수퍼클래스인 MDIParentFrame을 초기화 시킨다
        wx.MDIParentFrame.__init__(self,parent, -1,'MDI Parent Window', size=wx.DefaultSize)

        """
        사용자 GUI 코드 입력 
        """


if __name__=="__main__":
    app = wx.App()
    frame = UserMDIFrame(parent=None)
    frame.Show()
    app.MainLoop()

 

wx.MDIChildFrame

    부모 창(MDI Parent Frame) 안에 들어가는 자식 창의 경우도 선언은 비슷하다. 그러나 부모 창과 다른 점은, 자식창의 경우 부모창에 반드시 wx.MenuBar가 셋팅 되어 있어야 생성이 된다. (아래 예제의 SetMenuBar 부분을 주석처리 해보자!) 

wx.MDIChildFrame(parent, id=wx.ID_ANY, title="",
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_FRAME_STYLE|wx.VSCROLL|wx.HSCROLL, name="")

   아래와 같이 일반적인 메뉴바와 함께 자식 창을 생성하게 되면 자동으로 "Window"라는 메뉴가 추가로 생성된다. 

자식창을 생성하면 "Window" 메뉴가 추가생성됨

    이 메뉴에는 자식창 배치에 대한 아이템들(Cascade, Tile Horizontally, Tile Vertically, ... )이 자동으로 생성된다. 

    이를 아래 예제로 수행해보자. 

 

예제 - 1

    위에서 설명한 간단한 MDI Frame 예제이다. 

반응형
import wx

# 일반 Frame과 마찬가지로 MDIParentFrame을 상속받아 Frame 클래스를 작성한다
class UserMDIFrame(wx.MDIParentFrame):
    def __init__(self, parent):
        # 수퍼클래스인 MDIParentFrame을 초기화 시킨다
        wx.MDIParentFrame.__init__(self,parent, -1,'MDI Parent Window', size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE)

        """
        MDI Child Frame을 위한 메뉴바 생성
        """
        # File 메뉴의 하위 메뉴 생성
        filemenu = wx.Menu()
        filemenu.Append(100, "New Window")
        filemenu.Append(200, "Exit")

        # 메뉴바 생성 후 File 메뉴 붙이기
        menubar = wx.MenuBar()
        menubar.Append(filemenu, "&File")

        # MDI 프레임에 메뉴바를 연결
        # 이 부분을 주석처리하고 실행해보세요!! (어떤 에러가 발생하는지..?!)
        # MDI Child Frame은 부모 창에 menubar가 없는 경우 에러가 납니다.
        self.SetMenuBar(menubar)

        # 자식 창 생성
        self.OpenUpChildFrame()

        # Frame 화면 중앙에 놓기
        self.Center()



    def OpenUpChildFrame(self):
        win = wx.MDIChildFrame(self, -1, "wow", size=wx.DefaultSize)
        win.Show(True)



if __name__=="__main__":
    app = wx.App()
    frame = UserMDIFrame(parent=None)
    frame.Show()
    app.MainLoop()

 

 

예제 - 2

    자식 창의 크기를 직접 입력할 수 있는 예제이다.

    창 크기는 숫자만 가능하므로 숫자 이외의 값은 입력되지 않게 wx.TextCtrl을 수정하여 NumberTextCtrl 위젯을 커스텀으로 만들었다. 창 크기를 입력하는 대화상자의 경우도 wx.Dialog를 수정하여 위젯들을 재배치 하는 부분을 배울 수 있으므로 유용하게 사용할 수 있다. 

숫자만 입력받는 텍스트컨트롤과 커스텀 다이얼로그

import wx

class MDIFrame(wx.MDIParentFrame):
    def __init__(self, parent):
        # 수퍼클래스인 MDIParentFrame을 초기화 시킨다
        wx.MDIParentFrame.__init__(self,parent, -1,'MDI Parent Window', size=(800,600))

        # File 메뉴의 하위 메뉴 생성
        filemenu = wx.Menu()
        filemenu.Append(100, "New Window")
        filemenu.Append(200, "Exit")

        # 메뉴바 생성 후 File 메뉴 붙이기
        menubar = wx.MenuBar()
        menubar.Append(filemenu, "&File")

        # MDI 프레임에 메뉴바를 연결
        self.SetMenuBar(menubar)

        # 메뉴 아이템 클릭시 동작함수 바인딩
        self.Bind(wx.EVT_MENU, self.OnNewWindow, id=100)
        self.Bind(wx.EVT_MENU, self.OnExit, id=200)

        # Frame 화면 중앙에 놓기
        self.Center()

    # 종료
    def OnExit(self, evt):
        self.Close(True)

    # New Window 메뉴 아이템 클릭시 동작
    def OnNewWindow(self, evt):

        """""""""""""""""""""""""""""
        숫자만 입력 가능한 Text Ctrl 제작 
        """""""""""""""""""""""""""""
        class NumberTextCtrl(wx.TextCtrl):
            def __init__(self, parent, id, value):
                wx.TextCtrl.__init__(self, parent, id, value, pos=wx.DefaultPosition, size=(100,-1))
                # 키보드 눌림 이벤트 바인딩
                # wx.EVT_CHAR도 가능합니다!!
                self.Bind(wx.EVT_KEY_DOWN, self.handle_keypress)

            # 키보드 입력시 동작: 숫자만 입력 가능하게
            def handle_keypress(self, event):
                # 눌린 키의 키코드 값
                keycode = event.GetKeyCode()
                if chr(keycode).isnumeric():
                    # 이벤트 스킵
                    event.Skip(skip=True)
                # 백스페이스, DEL, 화살표 키도 입력 가능하도록 이벤트에서 제외.
                if keycode in [8, 127, 314, 315, 316, 317]:
                    event.Skip(skip=True)

        # 윈도우 사이즈 입력 대화상자 : wx.Dialog를 상속받아 만듭니다
        class GetWindowSizeDialog(wx.Dialog):
            def __init__(self, parent, id=wx.ID_ANY, title="", label="",
                         pos=wx.DefaultPosition, size=wx.DefaultSize):
                wx.Dialog.__init__(self, parent, id, title, pos, size)
                self.panel = wx.Panel(self, -1)

                # 대화상자 메시지
                self.message = wx.StaticText(self.panel, label=label)
                # 창 크기 x, y 값 : 숫자만 입력가능
                self.xvalue = NumberTextCtrl(self.panel, -1, "300")
                self.yvalue = NumberTextCtrl(self.panel, -1, "250")
                # OK 버튼
                self.btnOK = wx.Button(self.panel,-1, "OK")

                # 수직 박스사이저
                bsizer = wx.BoxSizer(wx.VERTICAL)
                # 수평 박스사이저
                hsizer = wx.BoxSizer(wx.HORIZONTAL)
                bsizer.Add(self.message,0, wx.ALL|wx.ALIGN_CENTER, 20)
                hsizer.Add(self.xvalue,0, wx.ALL|wx.ALIGN_CENTER, 10)
                hsizer.Add(self.yvalue,0, wx.ALL|wx.ALIGN_CENTER, 10)
                bsizer.Add(hsizer, 0, wx.ALL|wx.ALIGN_CENTER, 10)
                bsizer.Add(self.btnOK, 0, wx.ALL|wx.ALIGN_CENTER, 10)
                self.panel.SetSizer(bsizer)

                self.Bind(wx.EVT_BUTTON, self.ok_pressed, self.btnOK)

            # OK가 눌렸을 때 동작
            def ok_pressed(self, event):
                if self.IsModal():
                    self.EndModal(wx.ID_OK)
                else:
                    self.Close()

            # 창 크기 (x,y)값 출력
            def GetValue(self):
                return (int(self.xvalue.GetValue()), int(self.yvalue.GetValue()))


        modal = GetWindowSizeDialog(None, -1, title="MDI Child Frame 사이즈 입력",
                                    label="MDI Child Frame을 추가합니다.\n창크기를 입력하세요.")


        # # OK를 클릭하면 동작
        if modal.ShowModal() == wx.ID_OK:
            # 사용자가 입력한 값 얻기
            winx,winy = modal.GetValue()

            # MDI Child Frame 생성
            win = wx.MDIChildFrame(self, -1, "Child Window", size=(winx,winy))

            # MDI Child Frame 내부 GUI
            win.panel = wx.Panel(win, -1)
            win.st = wx.StaticText(win, -1, "입력한 창 크기: ("+str(winx)+", "+str(winy)+")")
            win.bsizer = wx.BoxSizer(wx.HORIZONTAL)
            win.bsizer.Add(win.st, 0, wx.ALL, 30)
            win.SetSizer(win.bsizer)
            win.Show(True)



if __name__=="__main__":
    app = wx.App()
    frame = MDIFrame(parent=None)
    frame.Show()
    app.MainLoop()

 

 

 

 

 

    도움되셨다면 하트(♥) 부탁드리고, 더 궁금한 사항은 댓글로 남겨주세요 :) 

 

 

반응형