Matplotlib 로 간단한 메뉴 만들기

Beginner

This tutorial is from open-source community. Access the source code

소개

이 랩에서는 Matplotlib 을 사용하여 간단한 메뉴를 만드는 방법을 배우게 됩니다. 메뉴는 사용자가 선택할 수 있는 항목 목록을 포함합니다. 선택된 항목은 특정 작업을 수행하는 콜백 함수 (callback function) 를 트리거합니다.

VM 팁

VM 시작이 완료되면, 왼쪽 상단을 클릭하여 Notebook 탭으로 전환하여 실습을 위해 Jupyter Notebook에 접근하십시오.

때로는 Jupyter Notebook 이 로딩을 완료하는 데 몇 초 정도 기다려야 할 수 있습니다. Jupyter Notebook 의 제한으로 인해 작업의 유효성 검사는 자동화될 수 없습니다.

학습 중에 문제가 발생하면 Labby 에게 문의하십시오. 세션 후 피드백을 제공해주시면 문제를 신속하게 해결해 드리겠습니다.

라이브러리 임포트

시작하려면 필요한 라이브러리를 임포트해야 합니다. 이 경우, matplotlib.pyplot, matplotlib.artist, matplotlib.patches, 그리고 matplotlib.transforms에서 IdentityTransform을 임포트해야 합니다.

import matplotlib.pyplot as plt
import matplotlib.artist as artist
import matplotlib.patches as patches
from matplotlib.transforms import IdentityTransform

ItemProperties 클래스 정의

다음으로, 각 메뉴 항목의 속성을 설정하는 데 사용될 ItemProperties 클래스를 정의합니다. 이 클래스를 사용하여 각 항목의 글꼴 크기, 레이블 색상, 배경 색상 및 알파 값을 설정할 수 있습니다.

class ItemProperties:
    def __init__(self, fontsize=14, labelcolor='black', bgcolor='yellow',
                 alpha=1.0):
        self.fontsize = fontsize
        self.labelcolor = labelcolor
        self.bgcolor = bgcolor
        self.alpha = alpha

이제 메뉴의 각 항목을 생성하는 데 사용될 MenuItem 클래스를 정의합니다. 이 클래스에 figure, 레이블 문자열, 속성, hover 속성 및 on select 콜백 함수를 매개변수로 전달합니다. MenuItem 클래스는 artist.Artist 클래스를 상속합니다.

class MenuItem(artist.Artist):
    padx = 5
    pady = 5

    def __init__(self, fig, labelstr, props=None, hoverprops=None,
                 on_select=None):
        super().__init__()

        self.set_figure(fig)
        self.labelstr = labelstr

        self.props = props if props is not None else ItemProperties()
        self.hoverprops = (
            hoverprops if hoverprops is not None else ItemProperties())
        if self.props.fontsize != self.hoverprops.fontsize:
            raise NotImplementedError(
                'support for different font sizes not implemented')

        self.on_select = on_select

        ## Setting the transform to IdentityTransform() lets us specify
        ## coordinates directly in pixels.
        self.label = fig.text(0, 0, labelstr, transform=IdentityTransform(),
                              size=props.fontsize)
        self.text_bbox = self.label.get_window_extent(
            fig.canvas.get_renderer())

        self.rect = patches.Rectangle((0, 0), 1, 1)  ## Will be updated later.

        self.set_hover_props(False)

        fig.canvas.mpl_connect('button_release_event', self.check_select)

    def check_select(self, event):
        over, _ = self.rect.contains(event)
        if not over:
            return
        if self.on_select is not None:
            self.on_select(self)

    def set_extent(self, x, y, w, h, depth):
        self.rect.set(x=x, y=y, width=w, height=h)
        self.label.set(position=(x + self.padx, y + depth + self.pady/2))
        self.hover = False

    def draw(self, renderer):
        self.rect.draw(renderer)
        self.label.draw(renderer)

    def set_hover_props(self, b):
        props = self.hoverprops if b else self.props
        self.label.set(color=props.labelcolor)
        self.rect.set(facecolor=props.bgcolor, alpha=props.alpha)

    def set_hover(self, event):
        """
        Update the hover status of event and return whether it was changed.
        """
        b, _ = self.rect.contains(event)
        changed = (b != self.hover)
        if changed:
            self.set_hover_props(b)
        self.hover = b
        return changed

또한 메뉴를 생성하는 데 사용될 Menu 클래스를 정의해야 합니다. 이 클래스에 figure 와 메뉴 항목 목록을 매개변수로 전달합니다.

class Menu:
    def __init__(self, fig, menuitems):
        self.figure = fig

        self.menuitems = menuitems

        maxw = max(item.text_bbox.width for item in menuitems)
        maxh = max(item.text_bbox.height for item in menuitems)
        depth = max(-item.text_bbox.y0 for item in menuitems)

        x0 = 100
        y0 = 400

        width = maxw + 2*MenuItem.padx
        height = maxh + MenuItem.pady

        for item in menuitems:
            left = x0
            bottom = y0 - maxh - MenuItem.pady

            item.set_extent(left, bottom, width, height, depth)

            fig.artists.append(item)
            y0 -= maxh + MenuItem.pady

        fig.canvas.mpl_connect('motion_notify_event', self.on_move)

    def on_move(self, event):
        if any(item.set_hover(event) for item in self.menuitems):
            self.figure.canvas.draw()

메뉴 생성

이제 메뉴를 생성할 수 있습니다. 새로운 figure 를 생성하고 왼쪽 여백을 조정합니다. 그런 다음 메뉴 항목 목록을 생성하고 이를 메뉴에 추가합니다. 또한 선택된 항목을 출력하는 각 항목에 대한 콜백 함수를 정의합니다.

fig = plt.figure()
fig.subplots_adjust(left=0.3)
props = ItemProperties(labelcolor='black', bgcolor='yellow',
                       fontsize=15, alpha=0.2)
hoverprops = ItemProperties(labelcolor='white', bgcolor='blue',
                            fontsize=15, alpha=0.2)

menuitems = []
for label in ('open', 'close', 'save', 'save as', 'quit'):
    def on_select(item):
        print('you selected %s' % item.labelstr)
    item = MenuItem(fig, label, props=props, hoverprops=hoverprops,
                    on_select=on_select)
    menuitems.append(item)

menu = Menu(fig, menuitems)
plt.show()

요약

축하합니다! Matplotlib 을 사용하여 간단한 메뉴를 성공적으로 만들었습니다. MenuItem 클래스, Menu 클래스를 생성하는 방법과 각 항목에 대한 속성을 정의하는 방법을 배웠습니다. 또한 일부 작업을 수행할 각 항목에 대한 콜백 함수를 생성하는 방법도 배웠습니다.