[PyQt] QtChart를 이용한 실시간 그래프 출력 (+QScrollBar)

이정찬·2022년 5월 23일
0

PyQt

목록 보기
4/6
post-thumbnail

2021년 8월부터 2021년 11월까지 진행된 PyQt를 이용한 스마트팩토리 외주에서 배운 사항들을 정리하기 위해 작성한 글입니다.
Raspberry Pi 3 환경에 Raspbian OS를 설치하여 진행하였습니다.

1. QtChart 설치

QtChart는 PyQt5의 내장 라이브러리가 아닙니다. 따라서, 설치를 진행해야 합니다. 그런데, Raspbian OS를 구성하는 ARM linux에 맞는 QtChart 라이브러리 패키지가 없었습니다. 당시 매우 당황했고, 차트를 직접 만들어야 하나 싶었지만, 어떻게든 수동으로 필요 패키지를 모두 설치해서 사용하는 방법을 찾아냈습니다.

Raspberry Pi doesn't have PyQtChart #441

참고한 github 이슈 페이지입니다. 혹시 Raspberry Pi에 QtChart 라이브러리를 설치할 분이 계시다면, 이 글이 도움이 될 것입니다.

설치가 진행됐다면, 여타 라이브러리와 똑같이 import 후 진행합니다.

from PyQt5 import QtChart

2. 스크롤바가 붙은 차트 생성

### machineStateUI.py
def noiseGraphWidget(self):
    chartView = QtChart.QChartView()
    self.scrollbar = QtWidgets.QScrollBar(
    	QtCore.Qt.Horizontal,
    	sliderMoved = self.onNoiseAxisSliderMoved,
    	pageStep = 100,
    )
    self.scrollbar.setVisible(False)
    self.scrollbar.setValue(99)
    self.scrollbar.setStyleSheet("height: 20px;")
	...

def onNoiseAxisSliderMoved(self, value):
	if self.second > 30:
		minX = math.floor(value / 100 * (self.second - 30)) + 1
		maxX = math.floor(value / 100 * (self.second - 30)) + 31
		self.adjustAxis("noise", minX, maxX)

먼저, 차트를 띄우기 위해 QChartView() 함수를 사용해줍니다. 그리고, 가로 스크롤바를 이용하여 그래프를 띄울 것이기 때문에, QScrollBar를 Horizontal 옵션을 넣어서 생성하고, 스크롤 위치에 따라 내부 값이 바뀌어야 하기 때문에, 직접 만든 함수인 onNoiseAxisSliderMoved 함수를 연결해줍니다.

저 함수는 센서가 연결되고 나서 지난 시간과, 스크롤바의 위치를 0~100의 수로 받아옵니다. 그 두 개의 값을 가지고 N초~N+30초의 값을 출력할 수 있도록 X축의 min, max 값을 계속 바꿔줍니다.

스크롤이 최초에는 보이지 않고, 계속 오른쪽 끝에 고정이 되어있어야 하므로, setVisible(False)setValue(99)를 선언해 줍니다.

### machineStateUI.py
noiseGraphWidget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(noiseGraphWidget)
for w in (chartView, self.scrollbar):
	lay.addWidget(w)

# chart setting
self.series = QtChart.QLineSeries()
self.chart = QtChart.QChart()
self.chart.addSeries(self.series)
self.chart.createDefaultAxes()
self.chart.legend().hide()
self.chart.axisX(self.series).setTickCount(31)
self.chart.axisX(self.series).setRange(0, 30)
self.chart.axisX(self.series).setLabelFormat("%d")
self.chart.axisY(self.series).setRange(config.noiseMinY, config.noiseMaxY)
self.chart.axisY(self.series).setLabelFormat("%d")
chartView.setChart(self.chart)
chartView.setRenderHints(QtGui.QPainter.Antialiasing)
self.chart.setTitle("소음")
self.onNoiseAxisSliderMoved(self.scrollbar.value())

return noiseGraphWidget

사용할 layout을 선언하고, 차트를 세팅해줍니다. 일직선 그래프를 사용할 것이므로 QLineSeries()를 사용하고, 내부 사각선을 없애기 위해 legend().hide()를 선언합니다. 이후, X축과 Y축의 눈금 수(setTickCount), 범례 간격(setRange), 출력 포맷(setLabelFormat)을 결정하는 코드가 5줄 진행됩니다.

차트가 실시간으로 그려질 때, 매끄럽게 그려지는 듯한 효과를 주고 싶다면,

self.chart.setAnimationOptions(QtChart.QChart.SeriesAnimations)

이 코드를 추가해줍니다. 그러면 차트가 매끄럽게 그려지게 됩니다.

chartView.setRenderHints(QtGui.QPainter.Antialiasing)

이 코드를 사용하면, 차트가 자글자글거리는 효과 없이 예쁜 일직선으로 나옵니다.

마지막으로, onNoiseAxisSliderMoved() 함수에 스크롤바의 값을 연결시켜주면, 스크롤바가 있는 차트가 생성됩니다.

3. 실시간 그래프 값 갱신

이렇게 만들어둔 QtChart 틀에 1초에 1번씩 값을 받아서 업데이트 해주는 코드를 작성합니다.

def setGraphValue(self, _noise, _vibe):
	self.series.append(self.second, _noise)
	...

이렇게 써준다면, 아까 QLineSeries로 지정해둔 series에 하나씩 추가되면서 출력하는 코드가 완성됩니다.

4. 마치며

사실상 PyQt 외주에서 가장 오랜 시간을 썼던 파트입니다. 가장 중요한 파트였으며, 저렇게 출력함과 동시에 서버로 보내는 과정까지 거쳐야 했었기 때문에, 예외처리를 하는 것도 매우 신경썼던 경험이었습니다.

QtChart를 설치하는데에 실패했다면, 정말 더 힘들었을 뻔 했지만, Raspbian OS에 QtChart를 설치할 수 없다는 것을 깨닫고, github에서 저와 같은 문제를 겪는 사람을 발견하는 데까지 3일이 결렸고, 결국 설치 후 해결했었습니다. 구글링만 잘 하면 문제는 대부분 해결된다는 사실을 다시 한 번 깨달았습니다.

profile
개발자를 꿈꾸는 사람

0개의 댓글