Matplotlib 마우스 이벤트 처리하기

개요

이번 주제는 Matplotlib마우스 이벤트 처리에 대해 살펴보겠습니다.

차트를 그리는 예제는 많은데 그려진 차트에 마우스, 키보드 등 이벤트를 처리하는 코드는 찾기가 어려워 공부삼아 만들어 보았습니다.

먼저 완성된 결과물은 아래와 같습니다.

 

이 예제에서 다루는 Matplotlib Mouse Event 는 다음과 같습니다.

  • Pressed           (Matplitlib callback 이용)

  • Released         (Matplitlib callback 이용)

  • Move             (Matplitlib callback 이용)

  • ContextMenu   (PyQt5 callback 이용)

아래와 같이 차트위 마우스 이벤트 발생시 해당 좌표가 출력되며, 수직선을 변경된 위치로 옮겨 그려주도록 구성하였습니다.

[예제 프로그램 실행화면]

 

소스코드

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QMenu, QAction
from PyQt5.QtCore import Qt
import sys

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
import matplotlib.pyplot as plt

import numpy as np

# 4k
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)

class Form(QWidget):

    def __init__(self):
        super().__init__()   
        self.setWindowTitle('Ocean Coding School')
        
        # matplotlib        
        self.fig = plt.Figure()
        self.canvas = FigureCanvasQTAgg(self.fig)

        vbox = QVBoxLayout()
        vbox.addWidget(self.canvas)
        self.setLayout(vbox)

        self.drawChart()

        # event callback
        self.pressed = False
        self.canvas.mpl_connect('button_press_event', self.onPress)
        self.canvas.mpl_connect('button_release_event', self.onRelease)
        self.canvas.mpl_connect('motion_notify_event', self.onMove)
        
    def drawChart(self, vx=6):
        self.fig.clear()

        x = np.arange(0, 4*np.pi, 0.1)
        y = np.sin(x)

        ax = self.fig.subplots()
        self.rect = ax.plot(x, y, label='sin')        
        
        ax.vlines( vx, y.min(), y.max(), linestyle='--', color='#ff0000C8')
        ax.text(vx, y.mean(), f'X:{vx:.1f}')

        ax.legend()
        self.canvas.draw()

    def onPress(self, e):
        self.pressed = True

    def onRelease(self, e):
        self.pressed = False

    def onMove(self, e):
        if self.pressed and e.inaxes == self.rect[0].axes:
            print(f'Move X:{e.xdata:.3f}\tY:{e.ydata:.3f}')
            self.drawChart(e.xdata)

    def contextMenuEvent(self, e):
        # context menu
        act1 = QAction('menu1')
        act2 = QAction('menu2')

        menu = QMenu(self)
        menu.addActions( (act1, act2) )

        act = menu.exec_(self.mapToGlobal(e.pos()))
        if act:
            print(act.text())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Form()
    w.show()
    sys.exit(app.exec_()) 

[라인 1 ~ 11]

필요한 모듈을 불러오고 4K모니터 설정 처리


[라인 13 ~ 33]

QWidget에서 상속받은 위젯 (윈도우) 클래스의 생성자 함수.

PyQt5 위젯에 matplotlib 차트를 그리도록 해주는 FigureCanvasQTAgg 객체 생성

해당 Canvas 객체를 수직레이아웃 박스에 추가하고 위젯에 배치.

Canvas 객체의 mpl_connect() 함수를 이용해 마우스 이벤트에 해당하는 Callback Function 연결.

(즉, 마우스를 누르면 onPress, 때면 onRelease, 이동하면 onMove 함수가 호출되며 함수 포인터를 등록해주는 방식으로 구현됩니다.)

추가적인 마우스 이벤트 처리는 matploblib Doc. 참조 바랍니다.

[matplotlib event list,  출처:matplotlib Doc.]

 

[라인 35 ~ 48]

Canvas 객체에 차트를 그리는 함수.

0~4 pi 까지 0.1 간격으로 sin 파 생성. (x, y)

pyplot의 vlines() 함수를 이용해 수직선 생성.


[라인 50 ~ 54]

마우스가 눌러졌는지 구분하기 위해 마우스가 눌러지면 self.pressed 변수를 참, 아니면 거짓으로 저장.

 

[라인 56 ~ 59]

마우스가 눌러진 상태로 차트의 영역안에 있는 경우 차트 위 마우스 위치를 출력하고, 이때 얻은 X 좌표를 이용 수직선을 갱신.

 

[라인 61 ~ 71]

마우스 우클릭시 발생하는 Context Event를 처리하는 부분.

주의할 점은 해당 함수는 PyQt QWidget 클래스에서 처리되는 Callback 함수임. (matplotlob X)


[라인 73 ~ 77]

Python의 메인함수.


이상으로 모든 코드 설명을 마칩니다.

감사합니다.

댓글

이 블로그의 인기 게시물

Qt Designer 설치하기

PyQt5 기반 동영상 플레이어앱 만들기