빅데이터 프로그래밍/Python

[Python] 18. [GUI] wxPython 그래픽 사용자 인터페이스, 기본 Widget, Sizer

밍글링글링 2017. 8. 2.
728x90

01. wxPython 그래픽 사용자 인터페이스

https://www.wxwidgets.org
- Python에 내장된 Tkinter UI toolkit에 비하여 더욱 향상된 GUI 환경 지원
- 1992년부터 개발되어온 wxWidget을 파이썬 개발 환경에서 사용가능 하도록 변경한 버전으로
  C#, Perl, Java도 지원함.


1. pip를 이용한 라이브러리 설치
- 관련 library를 다운로드 받아 자동으로 설치해줌.
- 공식 지원은 Python 2.7을 지원하나 개발 버전으로 Python 3.0을 지원하는
      피닉스(Phoenix)라고하는 프로젝트를 제공함.

1) https://wxpython.org/Phoenix/snapshot-builds/ 접속
- whl: wheel 파일, pip를 이용하여 파이썬 설치를 자동으로 지원하는 형식  
파일명: wxPython_Phoenix-3.0.3.dev2864+4fc5f9e-cp35-cp35m-win_amd64.whl
cp35: Python 3.5, amd64: 64 bit

2) 설치
① Python3.5 기반

F:\201701_python\setup>pip install wxPython_Phoenix-3.0.3.dev2864+4fc5f9e-cp35-cp35m-win_amd64.whl

Processing f:\201701_python\setup\wxpython_phoenix-3.0.3.dev2864+4fc5f9e-cp35-cp35m-win_amd64.whl
Collecting six (from wxPython-Phoenix==3.0.3.dev2864+4fc5f9e)
Downloading six-1.10.0-py2.py3-none-any.whl
Installing collected packages: six, wxPython-Phoenix
Successfully installed six-1.10.0 wxPython-Phoenix-3.0.3.dev2864+4fc5f9e


② Python3.6 기반 ★ 2017년 4월 14일 발표된 wxPython library는 PyDev에서 설정 없이 자동 인식됨.

F:\201701_python\setup>pip install wxPython_Phoenix-3.0.3.dev2902+a79cd32-cp36-cp36m-win_amd64.whl
Processing f:\201701_python\setup\wxpython_phoenix-3.0.3.dev2902+a79cd32-cp36-cp36m-win_amd64.whl
Collecting six (from wxPython-Phoenix==3.0.3.dev2902+a79cd32)
  Downloading six-1.10.0-py2.py3-none-any.whl
Installing collected packages: six, wxPython-Phoenix
Successfully installed six-1.10.0 wxPython-Phoenix-3.0.3.dev2902+a79cd32

* 32bit: pip install wxPython_Phoenix-3.0.3.dev2902+a79cd32-cp36-cp36m-win32.whl
 
3) 설치 확인
F:\201701_python\setup>pip list
DEPRECATION: The default format will switch to columns in the future. You can us
e --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.con
f under the [list] section) to disable this warning.
pip (9.0.1)
setuptools (28.8.0)
six (1.10.0)
wxPython-Phoenix (3.0.3.dev2864+4fc5f9e)

 

2. 삭제

pip uninstall wxPython-Phoenix

 

3. Update

pip install --upgrade wxPython-Phoenix

 

4. Search

pip search wxPython-Phoenix


 

5. Eclipse Neon 2 Pydev에서 wxPython 인식 안됨으로 Python IDLE 사용을 권장함.

 

02. wx.App class
- wxPython초기화 및 구동, 이벤트 처리등을 실행함으로 wxPython을 이용하는 모든 어플은

 반드시 하나의 wx.App의 인스턴스를 갖고 있어야 한다.


1.  처리 흐름
  ○ 시작
    ↓
    【 wx.App 객체 생성 】 OnInit() 호출
    ↓
    【 위젯 생성 】 Frame, Button, List등 각종 위젯 생성
    ↓
    【  이벤트 처리기 등록 】 이벤트와 이벤트 처리기 연결
    ↓
    【 wx.App.MainLoop() 호출 】 이벤트 루프 실행
    ↓
    ○ 종료

 

2. 기초 코드

import wx

app = wx.App()

top = wx.Frame(parent=None, title="Hello World", size=(300,200))
top.Show()

app.MainLoop()
 

3. 윈도우 생성

▷ /wxexam/StartingWxPython.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

app = wx.App() # wxPython 사용 객체 생성

# 하나의 화면 생성
frame = wx.Frame(parent=None, title='Hello!')
frame.Show() # 화면 출력

app.MainLoop()  # 이벤트 수신 대기


-------------------------------------------------------------------------------------
 
4. OnInit() 생성자
- OnInit(self): __init__() 내부적으로 생성자를 호출하고 widget 생성을위해 호출됨.
- if __name__ == "__main__": 현재 스크립트가 python 명령에의해 직접 실행 될 경우만 실행
▷ /wxexam/OnInit.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyApp(wx.App): # wx.App 상속
    def OnInit(self):      # 초기화
        frame = wx.Frame(parent=None, title="Hello World", size=(300,200))
        frame.Show(True)  # 화면 출력
        return True

if __name__ == "__main__":
    app = MyApp()  # 객체 생성
    app.MainLoop()  # 이벤트 루프 시작


-------------------------------------------------------------------------------------
 

 

5. Frame 생성
- wx.Frame.__init__(self, parent=None, title="Empty Window"): 부모클래스의 
 생성자를 호출하며서 초기값을 전달, 호출시 첫번째 인수로 self를 전달해야함.

 

▷ /wxexam/EmptyWindow.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):  # 생성자 선언
        # 부모 클래스의 생성자 호출
        wx.Frame.__init__(self, parent=None, title="Empty Window")
        
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()
    
    app.MainLoop()


-------------------------------------------------------------------------------------
 

6. Event의 등록
- self.Bind(wx.EVT_CLOSE, self.OnClose): 이벤트 등록, 이벤트 처리 핸들러 함수
- wx.MessageBox("윈도우를 닫을까요?", "확인", wx.YES_NO): 메시지, 창 제목, 다이얼로그의 종류 
- event.Skip(True): 아니오를 눌러도 윈도우 닫힘.

- event.Skip(False): 아니오를 누르면 윈도우를 닫는 이벤트가 무시됨.
https://wxpython.org/Phoenix/docs/html/wx.functions.html#wx.MessageBox
 
▷ /wxexam/CloseEvent.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Close Event")
        # 이벤트 등록, 이벤트 종류, 이벤트 핸들러(메소드)
        self.Bind(wx.EVT_CLOSE, self.OnClose) 
        
    def OnClose(self, event):
        sw = wx.MessageBox("윈도우를 닫을까요?", "확인", wx.YES_NO);

        print('sw:' + str(sw)); # 2 or 8
        print('wx.YES_NO:' + str(wx.YES_NO));
        print('wx.YES:' + str(wx.YES)); # 2
        print('wx.NO:' + str(wx.NO)); # 8    
        
        if sw == wx.YES:
            # 창 닫음.
            self.Destroy()
        else:
            # 창 닫지않음
            # event.Skip(True) # 아니오를 눌러도 닫힘.
            event.Skip(False) # 아니오를 누르면 닫는 이벤트 무시됨.

if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()
    
    app.MainLoop()
    print('이벤트 수신 대기');
    
    


-------------------------------------------------------------------------------------
 
7. wx.Frame의 속성
- wx.EVT_LEFT_DOWN: 마우스 왼쪽 버튼 클릭
- wx.EVT_RIGHT_DOWN: 마우스 오른쪽 버튼 클릭
▷ /wxexam/WindowSize.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Window Size")
        
        # Frame에 마우스 왼쪽 버튼 이벤트등록
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLButtonDown)
        # Frame에 마우스 오른쪽 버튼 이벤트등록
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRButtonDown)

    def OnMouseLButtonDown(self, event):
        frame.SetSize(wx.Size(400, 200))

    def OnMouseRButtonDown(self, event):
        frame.SetSize(wx.Size(200, 400))

if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop()


-------------------------------------------------------------------------------------
 

 

8. 색상의 지정

- wx.Colour(0, 0, 255, 0): Red, Green, Blue, Alpha, 0 ~ 255의 범위
▷ /wxexam/WindowColour.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Window Size")
        
        # Frame에 마우스 왼쪽 버튼 이벤트등록
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLButtonDown)
        # Frame에 마우스 오른쪽 버튼 이벤트등록
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRButtonDown)

    def OnMouseLButtonDown(self, event):
        self.SetSize(wx.Size(400, 200))

    def OnMouseRButtonDown(self, event):
        self.SetSize(wx.Size(200, 400))

app = wx.App()
frame1 = MyFrame()
frame1.Show()

app.MainLoop()


-------------------------------------------------------------------------------------
 
9. 윈도우style
- wx.DEFAULT_FRAME_STYLE: 기본 윈도우 Style
- wx.CAPTION: 창 제목 출력
- wx.RESIZE_BORDER: 윈도우 외곽의 크기조정 경계 표시
▷ /wxexam/WindowStyle.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Window Style")
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLButtonDown)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRButtonDown)

    def OnMouseLButtonDown(self, event):
        self.SetWindowStyle(wx.RESIZE_BORDER | wx.CAPTION)

    def OnMouseRButtonDown(self, event):
        self.SetWindowStyle(wx.DEFAULT_FRAME_STYLE)
        
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop()


-------------------------------------------------------------------------------------
 
10. Frame container widget에 Widget 올리기
- class MyFrame(wx.Frame): Frame class의 상속
- btnClick.Bind(wx.EVT_BUTTON, self.OnBtnClickMe): 버튼 클릭 이벤트의 등록
▷ /wxexam/SimpleButton.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Simple Button")

        btnClick = wx.Button(self, label="Click Me!")
        btnClick.Bind(wx.EVT_BUTTON, self.OnBtnClickMe)

    def OnBtnClickMe(self, event):
        wx.MessageBox("클릭 되었습니다!", "Simple Button", wx.OK)

if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop()    



-------------------------------------------------------------------------------------
 
11. Panel container widget
- self.button1.SetPosition((x, y)): 좌측 상단을 0,0으로 사용
  ┌──────→  X
  │0, 0
  │
  │
  ↓
   Y

▷ /wxexam/Panel.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Panel Example")
        self.panel = wx.Panel(self)
        self.panel.SetBackgroundColour(wx.RED)

        self.button1 = wx.Button(self.panel, label="(50, 50)")
        self.button1.SetPosition((50, 50))

        self.button2 = wx.Button(self.panel, label="(250, 100)")
        self.button2.SetPosition((250, 100)) # x,y
        
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop()


-------------------------------------------------------------------------------------
 
12. 추상 클래스 wx.Sizer 배치 도우미의 파생 클래스 BoxSizer의 사용
- 위젯을 수평, 수직으로 정렬 할 수 있음.
- 화면의 크기를 변경해도 레이아웃이 깨지지않음.
- wx.Panel(self.mainPanel): mainPanel에 현재 생성되는 Panel을 붙임.
- wx.Button(self.upperPanel, label="Left"): 생성되는 Button을 upperPanel에 붙일 예정임.
- wx.BoxSizer(wx.HORIZONTAL): 수평으로 위젯을 배치하는 BoxSizer 객체 생성
- self.hzBoxSizer.Add(self.leftButton): 버튼을 hzBoxSizer 객체에 붙임.
- self.upperPanel.SetSizer(self.hzBoxSizer): Panel에 Sizer 적용

- self.vtBoxSizer.Add(self.upperPanel, 0, wx.ALIGN_LEFT|wx.TOP|wx.LEFT, 1): 좌상단 배치, margin 1 px
- self.vtBoxSizer.Add(self.middleButton, 1, wx.EXPAND|wx.ALL, 1): 구역 채움 배치, margin 1 px
- self.vtBoxSizer.Add(self.lowerButton, 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.BOTTOM, 1): 우하단 배치, margin 1 px
- proportion: 0-기본 출력, 1: 나머지 공간 전부 사용,
  값이 여러개인경우 전체적인 widget의 합에 비례하여 결정, BoxSizer에만 효과가 있음.
 
▷ /wxexam/BoxSizer.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="BoxSizer Example")
        '''
        MyFrame
          └─ mainPanel
                  └─ upperPanel:  leftButton, rightButton
          └─ middleButton
          └─ lowerButton
           
        '''
        self.mainPanel = wx.Panel(self)        
        self.upperPanel = wx.Panel(self.mainPanel)    
        self.leftButton = wx.Button(self.upperPanel, label="Left")
        self.rightButton = wx.Button(self.upperPanel, label="Right")
        
        self.hzBoxSizer = wx.BoxSizer(wx.HORIZONTAL) # 수평
        self.hzBoxSizer.Add(self.leftButton)
        self.hzBoxSizer.Add(self.rightButton)
        self.upperPanel.SetSizer(self.hzBoxSizer)
        
        self.middleButton = wx.Button(self.mainPanel, label="Middle")
        self.lowerButton = wx.Button(self.mainPanel, label="Lower")
        
        self.vtBoxSizer = wx.BoxSizer(wx.VERTICAL) # 수직
        self.vtBoxSizer.Add(self.upperPanel, 0, wx.ALIGN_LEFT|wx.TOP|wx.LEFT, 1)
        self.vtBoxSizer.Add(self.middleButton, 1, wx.EXPAND|wx.ALL, 1)
        self.vtBoxSizer.Add(self.lowerButton, 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.BOTTOM, 1)
        
        self.mainPanel.SetSizer(self.vtBoxSizer)
        
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop()
    

-------------------------------------------------------------------------------------
 
 
13. StaticBoxSizer
- Fieldset 형태를 만들어줌.
- self.hzBoxSizer = wx.StaticBoxSizer(wx.HORIZONTAL, self.upperPanel, "Upper") : "Upper"를 Caption으로 사용함.
 
 
▷ /wxexam/StaticBoxSizer.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="StaticBoxSizer Example")
        '''
        MyFrame
          └─ mainPanel
                  └─ upperPanel:  leftButton, rightButton
          └─ middleButton
          └─ lowerButton
           
        '''
        self.mainPanel = wx.Panel(self)        
        self.upperPanel = wx.Panel(self.mainPanel)    
        self.leftButton = wx.Button(self.upperPanel, label="시작")
        self.rightButton = wx.Button(self.upperPanel, label="종료")
        
        self.hzBoxSizer = wx.StaticBoxSizer(
            wx.HORIZONTAL, self.upperPanel, "데이터 수집")  # 수평
        self.hzBoxSizer.Add(self.leftButton)
        self.hzBoxSizer.Add(self.rightButton)
        self.upperPanel.SetSizer(self.hzBoxSizer)
        
        self.middleButton = wx.Button(self.mainPanel, label="Middle")
        self.lowerButton = wx.Button(self.mainPanel, label="Lower")
        
        self.vtBoxSizer = wx.BoxSizer(wx.VERTICAL) # 수직
        self.vtBoxSizer.Add(self.upperPanel, 0, 
                            wx.ALIGN_LEFT|wx.TOP|wx.LEFT, 5)
        self.vtBoxSizer.Add(self.middleButton, 1, 
                            wx.EXPAND|wx.ALL, 5)
        self.vtBoxSizer.Add(self.lowerButton, 0, 
                            wx.ALIGN_RIGHT|wx.RIGHT|wx.BOTTOM, 5)
        
        self.mainPanel.SetSizer(self.vtBoxSizer)
        
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop()
     
  
-------------------------------------------------------------------------------------
 
 
14. GridSizer
- 격자 형태의 레이아웃을 지원
- wx.GridSizer(rows=4, cols=3, hgap=5, vgap=5): 행의 수, 열의 수, 수평 간격 px, 수직 간격 px
- self.vtBoxSizer.Add(self.gridSizer, 1, wx.EXPAND): Sizer, 비중 1(100%), 공간을 채움
 
▷ /wxexam/GridSizer.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="GridSizer Example")
        self.SetSize(400, 300) # width, height
        self.mainPanel = wx.Panel(self)        

        self.gridSizer = wx.GridSizer(rows=4, cols=3, hgap=30, vgap=5)

        self.buttons = (
            wx.Button(self.mainPanel, label="1"),
            wx.Button(self.mainPanel, label="2"),
            wx.Button(self.mainPanel, label="3"),
            wx.Button(self.mainPanel, label="4"),
            wx.Button(self.mainPanel, label="5"),
            wx.Button(self.mainPanel, label="6"),
            wx.Button(self.mainPanel, label="7"),
            wx.Button(self.mainPanel, label="8"),
            wx.Button(self.mainPanel, label="9"),
            wx.Button(self.mainPanel, label="*"),
            wx.Button(self.mainPanel, label="0"),
            wx.Button(self.mainPanel, label="#")
            )

        for button in self.buttons:
            self.gridSizer.Add(button, 0, wx.EXPAND)
        
        self.vtBoxSizer = wx.BoxSizer(wx.VERTICAL) # 위/아래
        self.vtBoxSizer.Add(self.gridSizer, 1, wx.EXPAND)
        self.mainPanel.SetSizer(self.vtBoxSizer)
        
        
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop()

-------------------------------------------------------------------------------------
 
 
15. FlexGridSizer
- GridSizer와 기본 기능이 같으나 특정 열이나 행의 사이즈를 조절 할 수 있음.

- self.fgridSizer.AddGrowableCol(1) : 윈도우 크기 변경시 두번째 컬럼의 너비도 따라 변경되도록 지정함.
- self.fgridSizer.AddGrowableRow(2): 윈도우 크기 변경시 세번째 행의 높이도 따라 변경되도록 지정함.
 
▷ /wxexam/FlexGridSizer.py
-------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="FlexGridSizer Example")
        self.SetSize(600, 400)
        self.mainPanel = wx.Panel(self)        

        self.fgridSizer = wx.FlexGridSizer(rows=3, cols=2, hgap=5, vgap=5)
        
        # StaticText: Label 기능, 출력 목적
        self.nameStatic  = wx.StaticText(self.mainPanel, label="성명 :")
        self.emailStatic = wx.StaticText(self.mainPanel, label="이메일 :")
        self.phoneStatic = wx.StaticText(self.mainPanel, label="전화번호 :")
        
        # TextCtrl: 텍스트 입력
        self.nameText  = wx.TextCtrl(self.mainPanel)
        self.emailText = wx.TextCtrl(self.mainPanel)
        self.phoneText = wx.TextCtrl(self.mainPanel)

        self.fgridSizer.Add(self.nameStatic)
        self.fgridSizer.Add(self.nameText, 0, wx.EXPAND)
        
        self.fgridSizer.Add(self.emailStatic)
        self.fgridSizer.Add(self.emailText, 0, wx.EXPAND)
        
        self.fgridSizer.Add(self.phoneStatic)
        self.fgridSizer.Add(self.phoneText, 0, wx.EXPAND)

        # 윈도우 크기 변경시 두 번째 컬럼의 너비도 따라 변경되도록 지정함.
        self.fgridSizer.AddGrowableCol(1) 

        # 윈도우 크기 변경시 세 번째 행의 높이도 따라 변경되도록 지정함.
        self.fgridSizer.AddGrowableRow(2)
        
        self.vtBoxSizer = wx.BoxSizer(wx.VERTICAL)
        # 10: gap
        self.vtBoxSizer.Add(self.fgridSizer, 1, wx.EXPAND | wx.ALL, 10)
        self.mainPanel.SetSizer(self.vtBoxSizer)
        
        
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    frame.Show()

    app.MainLoop() 
    
    
  
-------------------------------------------------------------------------------------
 
 
 

 

728x90

댓글