#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = "Franek Łazarewicz-Muradyan" __copyright__ = "Copyright 2021, Franek Łazarewicz-Muradyan" __version__ = "0.1" __status__ = "Beta" __email__ = "uinarf@autistici.org" """ This is the main program starting the GUI """ import sys, os from PyQt5 import QtWidgets import matplotlib matplotlib.use('qt5agg') from matplotlib.figure import Figure from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib import dates from datetime import datetime, timedelta from time import sleep import configparser from RPGH_gui import Ui_MainWindow #from RPGH_main_gui import Ui_MainWindow import pandas as pd from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() import mysql.connector __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) # read and extract/format configuration data config_f = 'settings_config_parser.cfg' config_cl = 'settings_client_local.cfg' config_abs = os.path.join(__location__, config_f) config_cl_abs = os.path.join(__location__, config_cl) cfg = configparser.ConfigParser(interpolation=None) cfg_cl = configparser.ConfigParser(interpolation=None) cfg.read(config_abs) cfg_cl.read(config_cl_abs) class Data: def fetch_table(self, table): """ Fetches last 24 hours of data saved in the database. """ self.table = table try: conn = mysql.connector.connect(**cfg_cl['mysql']) except mysql.connector.Error as e: try: conn = mysql.connector.connect( host=cfg_cl['mysql']['host'], user=cfg_cl['mysql']['user'], password=cfg_cl['mysql']['password'], database=cfg_cl['mysql']['database'] ) except mysql.connector.Error as e: print(f"Couldn't connect to a database: {e}") sys.exit() cur = conn.cursor() sql = ( f'SELECT * FROM {self.table} ORDER BY timestamp DESC LIMIT 1;' ) cur.execute(sql) last = cur.fetchall()[0][0] first = last - timedelta(hours=24) sql = ( f"SELECT * FROM {self.table} WHERE timestamp BETWEEN '{first}' and '{last}';" ) cur.execute(sql) rows = cur.fetchall() fields = [i[0] for i in cur.description] df = pd.DataFrame([[x for x in i] for i in rows]) return fields, df class DesignerMainWindow(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent = None): super(DesignerMainWindow, self).__init__(parent) self.setupUi(self) # setting up mplwidget self.mplwidget() def mplwidget(self): timestamp_format = cfg.get('various', 'timestamp_format') _now_p = datetime.now() _past_p = _now_p - timedelta(hours=12) # time limits formated for mysql: _now = _now_p.strftime(timestamp_format) _past = _past_p.strftime(timestamp_format) # unpacking data settings temp_max = cfg.getint('temperature', 'temp_max') temp_min = cfg.getint('temperature', 'temp_min') light_max = cfg.getint('light', 'light_max') light_min = cfg.getint('light', 'light_min') humid_max = cfg.getint('humidity', 'humidity_max') humid_min = cfg.getint('humidity', 'humidity_min') soil_max = cfg.getint('water', 'soil_moisture_max') soil_min = cfg.getint('water', 'soil_moisture_min') # ax limits now = _now_p.strftime("%H:%M:%S") past = _past_p.strftime("%H:%M:%S") # unpacking temperature data # couldn't it be: temp_fields, temp_data = Data.fetch_table(... _temp_data = Data() temp_fields, temp_data = _temp_data.fetch_table('temperature') # below results in key error if there's no current data timestamp_temp = pd.to_datetime( pd.Series(temp_data[0]) ) temp_01 = ( temp_data[1], cfg['w1_mapping'][f'{temp_fields[1]}'] ) temp_02 = ( temp_data[2], cfg['w1_mapping'][f'{temp_fields[2]}'] ) temp_03 = ( temp_data[3], cfg['w1_mapping'][f'{temp_fields[3]}'] ) temp_04 = ( temp_data[4], cfg['w1_mapping'][f'{temp_fields[4]}'] ) # unpacking light data self.light_data = Data() light_fields, light_data = self.light_data.fetch_table('light') timestamp_1 = pd.to_datetime( pd.Series(light_data[0]), format=timestamp_format ) broadband = light_data[1] infrared = light_data[2] visible = light_data[3] # unpacking humidity data self.hum_data = Data() hum_fields, hum_data = self.hum_data.fetch_table('humidity') timestamp_2 = pd.to_datetime( pd.Series(hum_data[0]), format=timestamp_format ) humid = hum_data[2] # unpacking rain data self.rain_data = Data() rain_fields, rain_data = self.hum_data.fetch_table('rain') timestamp_3 = pd.to_datetime( pd.Series(rain_data[0]), format=timestamp_format ) rain = rain_data[1] days = dates.DayLocator() hours = dates.HourLocator() minutes = dates.MinuteLocator() seconds = dates.SecondLocator() dfmt = dates.DateFormatter('%b %d') tmfd = dates.DateFormatter('%H %M') # ploting temperature data self.mpl.canvas.ax.set_ylabel('Temperature ($^\circ$C)') self.mpl.canvas.ax.grid(True) self.mpl.canvas.ax.plot( timestamp_temp, temp_01[0], c='brown', ls=':', label=f'{temp_01[1]}' ) self.mpl.canvas.ax.plot( timestamp_temp, temp_02[0], c='orange', ls='dotted', label=f'{temp_02[1]}' ) self.mpl.canvas.ax.fill_between( timestamp_temp, temp_01[0], temp_02[0] ) self.mpl.canvas.ax.fill_between( timestamp_temp, temp_01[0], temp_max, where=temp_01[0]>=temp_max, edgecolor='red', facecolor='none', hatch='/', interpolate=True ) self.mpl.canvas.ax.fill_between( timestamp_temp, temp_02[0], temp_min, where=temp_02[0]<=temp_min, edgecolor='red', facecolor='none', hatch='/', interpolate=True ) self.mpl.canvas.ax.legend(loc='upper left') #self.mpl.canvas.ax.set_xlim([now, past]) # adding light subplot self.mpl.canvas.ax_1.set_ylabel('Light') self.mpl.canvas.ax_1.grid(True) self.mpl.canvas.ax_1.plot( timestamp_1, broadband, c='cyan', ls='-.', label='broadband' ) self.mpl.canvas.ax_1.plot( timestamp_1, infrared, c='red', ls='-.', label='infrared' ) self.mpl.canvas.ax_01 = self.mpl.canvas.ax_1.twinx() self.mpl.canvas.ax_01.set_ylabel("light lm") self.mpl.canvas.ax_01.plot( timestamp_1, visible, c='orange',ls='-.', label='visible' ) self.mpl.canvas.ax_1.legend(loc='upper left') self.mpl.canvas.ax_01.legend(loc='lower left') self.mpl.canvas.ax_1.fill_between( timestamp_1, broadband, light_max, where=broadband>=light_max, edgecolor='red', facecolor='none', hatch="/", interpolate=True ) self.mpl.canvas.ax_1.fill_between( timestamp_1, broadband, light_min, where=broadband<=light_min, edgecolor='red', facecolor='none', hatch='/', interpolate=True ) #self.mpl.canvas.ax_1.set_xlim([now, past]) # adding humidity subplot self.mpl.canvas.ax_2.set_ylabel('Humidity %') self.mpl.canvas.ax_2.set_xlabel('time') self.mpl.canvas.ax_2.grid(True) self.mpl.canvas.ax_2.plot( timestamp_2, humid, c='blue', ls='--', label='humidity' ) self.mpl.canvas.ax_2.legend(loc='upper left') #self.mpl.canvas.ax_2.set_xlim([now, past]) # setting up weather station tab: # adding temperature plot self.mpl1.canvas.ax.set_ylabel('Temperature ($^\circ$C)') self.mpl1.canvas.ax.grid(True) self.mpl1.canvas.ax.plot( timestamp_temp, temp_03[0], c='brown', ls=':', label=f'{temp_03[1]}' ) self.mpl1.canvas.ax.plot( timestamp_temp, temp_04[0], c='orange', ls='dotted', label=f'{temp_04[1]}' ) self.mpl1.canvas.ax.legend(loc='upper left') #self.mpl1.canvas.ax.set_xlim([now, past]) # adding light plot self.mpl1.canvas.ax_1.set_ylabel('Light lm') self.mpl1.canvas.ax_1.grid(True) self.mpl1.canvas.ax_1.plot( timestamp_1, visible, c='yellow', label='light' ) self.mpl1.canvas.ax_1.legend(loc='upper left') #self.mpl1.canvas.ax_1.set_xlim([now, past]) # adding humidity/rainfall graph # mind you that hist() doesn't do what I'd like it to do. redo. self.mpl1.canvas.ax_2.set_ylabel('Rainfall mm') self.mpl1.canvas.ax_2.set_xlabel('time') self.mpl1.canvas.ax_2.grid(True) # this is a hack: unit for bar width is days hence hum_data.shape[0] = numbers # of columns in dataframe allowing for dynamic sizing self.mpl1.canvas.ax_2.bar( timestamp_3, rain, width = .01*(1/rain_data.shape[0]), edgecolor = 'black', label='rainfall' ) self.mpl1.canvas.ax_2.legend(loc='upper left') #self.mpl1.canvas.ax_2.set_xlim([now, past]) def mpl_replot(self): self.mpl.canvas.ax.clear() self.mpl.canvas.ax_1.clear() self.mpl.canvas.ax_2.clear() self.mpl1.canvas.ax.clear() self.mpl1.canvas.ax_1.clear() self.mpl1.canvas.ax_2.clear() self.mpl.canvas.draw_idle() self.mpl1.canvas.draw_idle() self.mplwidget() if __name__ == '__main__': app = QtWidgets.QApplication([sys.argv]) gui = DesignerMainWindow() gui.show() sys.exit(app.exec_())