본문 바로가기

Temp_Monitor Projects

【 Machine Learning Project_01 】 Temp_Monitor : day03 QT 데이터프레임, 로그데이터 삽입

☀️ 운영체제 : macos

☀️ Tools : VScode, mariaDB, Sequel Pro

☀️ DATA : 제공하지 않음

 

* Sequel Pro, home brew, macos 등 사용하지 않는 사람에게는 권장하지 않음

 

아래 링크의 day02 와 이어집니다

https://mealhouse.tistory.com/10

 

【 Machine Learning Project_01 】 Temp_Monitor : day02

☀️ 운영체제 : macos ☀️ Tools : VScode, mariaDB, Sequel Pro ☀️ DATA : 제공하지 않음 * Sequel Pro, home brew, macos 등 사용하지 않는 사람에게는 권장하지 않음 아래 링크의 day01 과 이어집니다 https://mealhouse

mealhouse.tistory.com

 

 

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내에서 진행할 수 있도록 진행해보겠습니다.

 

 

현재 진행중인 과정은 빅데이터인재양성 국비 학습 진행 중 프로젝트 내용을 기반으로
복습 겸 일부 내용을 개인만의 방식으로 변경하여 진행중에 있습니다.