☀️ 운영체제 : macos
☀️ Tools : VScode, mariaDB, Sequel Pro
☀️ DATA : 제공하지 않음
* Sequel Pro, home brew, macos 등 사용하지 않는 사람에게는 권장하지 않음
아래 링크의 day02 와 이어집니다
https://mealhouse.tistory.com/10
qt로 그래프를 보여줄 수 있는 틀을 아래와 같이 만드는 시간을 가져보았습니다.
메인 그래프 자리에는 총 6개의 전체 온도 치수를 나타내고자 하는데
생각보다 가로의 폭이 좁아 각 그래프의 배치를 먼저 신경써보겠습니다.
<< 위 이미지의 QT틀에 해당하는 전체코드 >>
import pandas as pd
import numpy as np
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import font_manager, rc
import pymysql
import tensorflow as tf
import keras
from keras.models import Sequential
from keras.layers import SimpleRNN, LSTM, GRU, Dropout, Dense
from sklearn.preprocessing import MinMaxScaler
class MyApp(QWidget) :
def __init__(self) :
super().__init__()
self.initUI()
def initUI(self) :
# self.conn = pymysql.connect(hots = '127.0.0.1', user = USER, password = PASSWORD, db = DBNAME)
# self.cursor = self.conn.cursor()
self.tbltemporg = QTableWidget(100, 7) # 테이블 (100행 7열)
col_head = ['Date', 'temp1', 'temp2', 'temp3', 'temp4', 'temp5', 'temp6']
self.tbltemporg.setHorizontalHeaderLabels(col_head) # setHorizontalHeaderLabels : 열이름 설정
self.txtLog = QTextEdit()
self.txtLog.setReadOnly(True)
self.txtLog.append('이 부분에 로그데이터가 표시됩니다')
layout = QHBoxLayout() # 최상위 레이아웃(QHBoxLayout : 수평배열)
layoutMonitor = QVBoxLayout() # 수직배열 레이아웃(QVBoxLayout : 수직배열)
layoutML = QVBoxLayout() # 수직배열 레이아웃
# 왼쪽 레이아웃 및 위젯 생성
layoutMenu = QHBoxLayout() # 메뉴 레이아웃
layoutTbl = QHBoxLayout() # 테이블 레이아웃
layoutGraph = QVBoxLayout() # 그래프 레이아웃
layout.addLayout(layoutMonitor)
layout.addLayout(layoutML)
self.fig = plt.Figure(figsize=(6,6)) # 그래프 영역 변수 생성
self.canvas = FigureCanvas(self.fig) # 그래프 그리기 영역
self.fig1 = plt.Figure(figsize=(7,3))
self.fig2 = plt.Figure(figsize=(7,3))
self.fig3 = plt.Figure(figsize=(7,3))
self.fig4 = plt.Figure(figsize=(7,3))
self.fig5 = plt.Figure(figsize=(7,3))
self.fig6 = plt.Figure(figsize=(7,3))
self.canvas1 = FigureCanvas(self.fig1) # 그래프 그리기 영역
self.canvas2 = FigureCanvas(self.fig2) # 그래프 그리기 영역
self.canvas3 = FigureCanvas(self.fig3) # 그래프 그리기 영역
self.canvas4 = FigureCanvas(self.fig4) # 그래프 그리기 영역
self.canvas5 = FigureCanvas(self.fig5) # 그래프 그리기 영역
self.canvas6 = FigureCanvas(self.fig6) # 그래프 그리기 영역
layoutMonitor.addLayout(layoutMenu)
layoutMonitor.addLayout(layoutTbl)
layoutMonitor.addLayout(layoutGraph)
# 오른쪽 레이아웃 및 위젯 생성 : 3행 2열의 배치로 그래프 그릴 영역
layoutTemp1 = QHBoxLayout()
layoutTemp2 = QHBoxLayout()
layoutTemp3 = QHBoxLayout()
layoutTemp1.addWidget(self.canvas1) # 1번 그래프 영역 추가
layoutTemp1.addWidget(self.canvas2) # .
layoutTemp2.addWidget(self.canvas3) # .
layoutTemp2.addWidget(self.canvas4) # .
layoutTemp3.addWidget(self.canvas5) # .
layoutTemp3.addWidget(self.canvas6) # 6번 그래프 영역 추가
# ======== 1번 레이아웃에 1,2번 그래프 영역 위젯 추가 : 1행에 2열짜리 영역 생성 =========
layoutML.addLayout(layoutTemp1)
layoutML.addLayout(layoutTemp2)
layoutML.addLayout(layoutTemp3)
layoutTbl.addWidget(self.tbltemporg) # 앞서 만든 테이블 위젯 추가
layoutTbl.addWidget(self.txtLog) # 로그 텍스트 위젯 추가
layoutGraph.addWidget(self.canvas) # 그림 그리는 영역 위젯 추가
self.setLayout(layout) # 최상위 레이아웃 세팅
self.setWindowTitle('TempMonitor') # setWindowTitle : 생성되는 윈도우의 타이틀 설정
self.setGeometry(0, 30, 1600, 900) # setGeometry : 생성되는 윈도우의 좌표 및 크기 설정(x, y, 너비, 길이)
self.show()
if __name__ == '__main__' :
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
위 코드를 살펴보면 큰 레이아웃은 총 3가지로 나뉘어 있습니다
1) layout : 최상위 레이아웃
2) layoutMonitor : 메뉴(구현 안되어있음), 테이블, 그래프
3) layoutML : 안에 3개의 레이아웃을 포함해 각 레이아웃당 2개의 그래프 영역을 나타내는 레이아웃
이 레이아웃을 add 시킬때, stretch 옵션을 이용하여 분배하면 각 비율에 맞추어 레이아웃들이 배열됩니다.
layout.addLayout(layoutMonitor, stretch=2) # 2의 비율(총량이 3이라 가정할 때 그중 2의 비율)
layout.addLayout(layoutML, stretch=1) # 1의 비율(총량이 3이라 가정할 때 그중 1의 비율)
총량은 임의대로 정해주면 되는데, 소수점과 같은 비율로는 진행되지 않습니다.
즉, 총량을 임의로 5로 설정했으면 2,3 / 3,2 / 1,4 / 4,1 등의 비율 배분은 가능하지만 2.5 / 2.5 등과 같이
소수점이 들어가는 비율은 배정할 수 없습니다.
여기까지 수정이 들어갔다면
로그데이터 불러오기 및 학습을 통해 나온 예측값과 실제 데이터를 볼 수 있는 그래프 그리기까지 진행해보겠습니다.
1. 쿼리 입력 및 데이터프레임 생성
self.cursor = self.conn.cursor() # cursor 지정
query = 'select * from tbldata order by s_measuretime desc limit 1000' # query문 작성
self.cursor.execute(query) # query 실행
result = self.cursor.fetchall() # 해당하는 데이터 모두 가져오기
rowCount = self.cursor.rowcount # 해당 데이터의 행 개수 세기
self.df = [[0] * 7 for i in range(rowCount)] # 한줄 반복문으로 데이터프레임 생성 및 0으로 초기값 설정
# 테이블 내용 작성 및 데이터프레임 생성용
count = 0 # 초기값 설정
for i in result : # Tuple 로 가져오는 result의 각 행의
for j in range(7) : # 0-6번 인덱스까지
self.df[count][j] = i[j] # result의 각 행의 0-6 인덱스까지 0으로 초기화된 데이터 베이스 안에 result값을 넣어준다
count += 1
self.conn.commit()
initUI 가장 첫줄에 DB 커넥트를 진행했었고 해당 변수를 self.conn 으로 지정했었습니다.
해당 변수를 활용하여 DB 쿼리문을 작성하고 실행시켜 result 변수에 넣어주었고,
데이터의 행 개수를 rowCount 변수에 저장해두었습니다.
self.df 라는 변수안에 모든 값이 0으로 이루어진 데이터프레임을 만들었고,
그 행의 개수는 rowCount 수만큼, 열의 개수는 7개로 설정하였습니다.
테이블 내용 작성에는 초기값을 0으로 가지는 변수 생성(count)과
행 데이터를 불러오는 result를 활용하여
모든 값이 0으로 되어있는 데이터프레임 self.df 안에 각 행과 열마다 result의 값을 넣어줍니다.
최종적으로 self.conn.commit() 을 활용하여 DB가 갱신될 때마다 QT에서도 인식이 가능하게 만들어줍니다.
2. 로그데이터 출력
for i in range(1) :
self.txtLog.append(str(self.df[0][0]) # txtLog 위젯 안에 append
for j in range(1, 7) :
self.txtLog.append('temp value : %f' % (self.df[0][j]) # 포맷 문자열 활용, self.df 데이터프레임 안의 0행(가장 최신 데이터)의 1 ~ 6번 인덱스 호출
txtLog : QTextEdit()
일전에 만들어두었던 QTextEdit() txtLog 안에 self.df 데이터프레임의 가장 첫 데이터(날짜) 를 삽입하고,
이어서 각 temp 들의 값을 1~6까지 j 로 불러와 삽입해줍니다.
3. 테이블 데이터 출력
for i in ragne(100) : # 총 100개의 행에
for j in range(7) : # 7열까지
self.tbltemporg.setItem(i, j, QTableWidgetItem(str(self.df[i][j]))) # 0~99번 행에 7열까지 값 삽입
tbltemporg : QTableWidget()
총 100개의 행에 7열까지 setItem을 진행해줍니다.
=> setItem(0~99까지, 0~6까지, 아이템:(self.df 데이터프레임의 0~99행/0~6열)
이들을 각각 데이터프레임 생성 및 출력 등과 같이 다른 함수로 만들어두면 추후 관리가 쉽습니다.
함수로 만들어진 코드는 다음과 같습니다.
def dataProcess(self) :
self.cursor = self.conn.cursor() # cursor 지정
query = 'select * from tbldata order by s_measuretime desc limit 1000' # query문 작성
self.cursor.execute(query) # query 실행
result = self.cursor.fetchall() # 해당하는 데이터 모두 가져오기
rowCount = self.cursor.rowcount # 해당 데이터의 행 개수 세기
self.df = [[0] * 7 for i in range(rowCount)] # 한줄 반복문으로 데이터프레임 생성 및 0으로 초기값 설정
# 테이블 내용 작성 및 데이터프레임 생성용
count = 0 # 초기값 설정
for i in result : # Tuple 로 가져오는 result의 각 행의
for j in range(7) : # 0-6번 인덱스까지
self.df[count][j] = i[j] # result의 각 행의 0-6 인덱스까지 0으로 초기화된 데이터 베이스 안에 result값을 넣어준다
count += 1
self.conn.commit()
def display(self) :
# 로그 데이터 출력용
for i in range(1) :
self.txtLog.append(str(self.df[0][0]))
for j in range(1, 7) :
self.txtLog.append('temp value : %f' % (self.df[0][j]))
# 테이블 데이터 출력용
for i in range(100) :
for j in range(7) :
self.tbltemporg.setItem(i, j, QTableWidgetItem(str(self.df[i][j])))
4. 일정 시간마다 반복 갱신
QTimer 함수를 활용하여 일정 시간마다 반복 갱신할 수 있도록 만들어줍니다.
self.timer = QTimer(self)
self.timer.start(60000) # 단위 : 밀리초 / 1000ms = 1sec
self.timer.timeout.connect(self.timeHandler)
60000 밀리초마다 시작을 해주도록 설정 : 1분마다 데이터가 갱신되어 들어오기 때문
self.timer.timeout : 타이머 작동이 완료되면, connect로 timeHandler를 이어줍니다.
<< timeHandler >>
self.dataProcess()
self.display()
위 테이블데이터 및 로그데이터 출력에 쓰였던 함수들을 다시 불러주고,
QT 틀을 세팅하기 위해 만들었던 initUI 마지막에 해당 함수를 다시 불러주면 순서상으론 아래와 같습니다.
QT 틀 생성 -> self.dataProcess() 호출 -> self.display() 호출 -> QTimer 진행 -> 60초 완료 후 timeHandler() 호출
-> self.dataProcess() 호출 -> self.display() 호출 -> QTimer 진행 -> 반복
최종 코드 및 출력되는 화면은 아래와 같습니다.
import pandas as pd
import numpy as np
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import font_manager, rc
import pymysql
import tensorflow as tf
import keras
from keras.models import Sequential
from keras.layers import SimpleRNN, LSTM, GRU, Dropout, Dense
from sklearn.preprocessing import MinMaxScaler
class MyApp(QWidget) :
def __init__(self) :
super().__init__()
self.initUI()
def initUI(self) :
self.conn = pymysql.connect(host = '127.0.0.1', user = 'USER', password = 'PASSWORD', db = 'DBNAME')
# self.cursor = self.conn.cursor()
self.tbltemporg = QTableWidget(100, 7) # 테이블 (100행 7열)
col_head = ['Date', 'temp1', 'temp2', 'temp3', 'temp4', 'temp5', 'temp6']
self.tbltemporg.setHorizontalHeaderLabels(col_head) # setHorizontalHeaderLabels : 열이름 설정
self.txtLog = QTextEdit()
self.txtLog.setReadOnly(True)
self.txtLog.append('이 부분에 로그데이터가 표시됩니다')
layout = QHBoxLayout() # 최상위 레이아웃(QHBoxLayout : 수평배열)
layoutMonitor = QVBoxLayout() # 수직배열 레이아웃(QVBoxLayout : 수직배열)
layoutML = QVBoxLayout() # 수직배열 레이아웃
# 왼쪽 레이아웃 및 위젯 생성
layoutMenu = QHBoxLayout() # 메뉴 레이아웃
layoutTbl = QHBoxLayout() # 테이블 레이아웃
layoutGraph = QVBoxLayout() # 그래프 레이아웃
layout.addLayout(layoutMonitor, stretch=2)
layout.addLayout(layoutML, stretch=1)
self.fig = plt.Figure(figsize=(6,6)) # 그래프 영역 변수 생성
self.canvas = FigureCanvas(self.fig) # 그래프 그리기 영역
self.fig1 = plt.Figure(figsize=(7,3))
self.fig2 = plt.Figure(figsize=(7,3))
self.fig3 = plt.Figure(figsize=(7,3))
self.fig4 = plt.Figure(figsize=(7,3))
self.fig5 = plt.Figure(figsize=(7,3))
self.fig6 = plt.Figure(figsize=(7,3))
self.canvas1 = FigureCanvas(self.fig1) # 그래프 그리기 영역
self.canvas2 = FigureCanvas(self.fig2) # 그래프 그리기 영역
self.canvas3 = FigureCanvas(self.fig3) # 그래프 그리기 영역
self.canvas4 = FigureCanvas(self.fig4) # 그래프 그리기 영역
self.canvas5 = FigureCanvas(self.fig5) # 그래프 그리기 영역
self.canvas6 = FigureCanvas(self.fig6) # 그래프 그리기 영역
layoutMonitor.addLayout(layoutMenu)
layoutMonitor.addLayout(layoutTbl)
layoutMonitor.addLayout(layoutGraph)
# 오른쪽 레이아웃 및 위젯 생성 : 3행 2열의 배치로 그래프 그릴 영역
layoutTemp1 = QHBoxLayout()
layoutTemp2 = QHBoxLayout()
layoutTemp3 = QHBoxLayout()
layoutTemp1.addWidget(self.canvas1) # 1번 그래프 영역 추가
layoutTemp1.addWidget(self.canvas2) # .
layoutTemp2.addWidget(self.canvas3) # .
layoutTemp2.addWidget(self.canvas4) # .
layoutTemp3.addWidget(self.canvas5) # .
layoutTemp3.addWidget(self.canvas6) # 6번 그래프 영역 추가
# ======== 1번 레이아웃에 1,2번 그래프 영역 위젯 추가 : 1행에 2열짜리 영역 생성 =========
layoutML.addLayout(layoutTemp1)
layoutML.addLayout(layoutTemp2)
layoutML.addLayout(layoutTemp3)
layoutTbl.addWidget(self.tbltemporg) # 앞서 만든 테이블 위젯 추가
layoutTbl.addWidget(self.txtLog) # 로그 텍스트 위젯 추가
layoutGraph.addWidget(self.canvas) # 그림 그리는 영역 위젯 추가
self.setLayout(layout) # 최상위 레이아웃 세팅
self.setWindowTitle('TempMonitor') # setWindowTitle : 생성되는 윈도우의 타이틀 설정
self.setGeometry(0, 30, 1600, 900) # setGeometry : 생성되는 윈도우의 좌표 및 크기 설정(x, y, 너비, 길이)
self.show()
self.dataProcess()
self.display()
self.timer = QTimer(self)
self.timer.start(60000)
self.timer.timeout.connect(self.timeHandler)
def dataProcess(self) :
self.cursor = self.conn.cursor() # cursor 지정
query = 'select * from tbldata order by s_measuretime desc limit 1000' # query문 작성
self.cursor.execute(query) # query 실행
result = self.cursor.fetchall() # 해당하는 데이터 모두 가져오기
rowCount = self.cursor.rowcount # 해당 데이터의 행 개수 세기
self.df = [[0] * 7 for i in range(rowCount)] # 한줄 반복문으로 데이터프레임 생성 및 0으로 초기값 설정
# 테이블 내용 작성 및 데이터프레임 생성용
count = 0 # 초기값 설정
for i in result : # Tuple 로 가져오는 result의 각 행의
for j in range(7) : # 0-6번 인덱스까지
self.df[count][j] = i[j] # result의 각 행의 0-6 인덱스까지 0으로 초기화된 데이터 베이스 안에 result값을 넣어준다
count += 1
self.conn.commit()
def display(self) :
# 로그 데이터 출력용
for i in range(1) :
self.txtLog.append(str(self.df[0][0]))
for j in range(1, 7) :
self.txtLog.append('temp value : %f' % (self.df[0][j]))
# 테이블 데이터 출력용
for i in range(100) :
for j in range(7) :
self.tbltemporg.setItem(i, j, QTableWidgetItem(str(self.df[i][j])))
def timeHandler(self) :
self.dataProcess()
self.display()
if __name__ == '__main__' :
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
그래프 그리기에 앞서 머신러닝에 필요한 모델들이 필요한데
직접 학습시켜 보고 h5 파일로 만들어 해당 h5파일로의 학습을 QT내에서 진행할 수 있도록 진행해보겠습니다.
현재 진행중인 과정은 빅데이터인재양성 국비 학습 진행 중 프로젝트 내용을 기반으로
복습 겸 일부 내용을 개인만의 방식으로 변경하여 진행중에 있습니다.
'Temp_Monitor Projects' 카테고리의 다른 글
【 Machine Learning Project_01 】 Temp_Monitor : day02 (1) | 2022.12.29 |
---|---|
【 Machine Learning_Project01 】Temp_Monitor : day01 (0) | 2022.12.22 |