Qt Designer界面设计,matplotlib画图,横坐标是日期类型,实时显示获取的数据
Posted on 2022-03-22 12:59 初之萌萌 阅读(1239) 评论(0) 编辑 收藏 举报功能:
目的:想定时获取系统cpu,内存的数值,然后以图表的形式直观显示出来,方便监控异常情况
1.定时写数据到excel文件中,为创建测试数据
2.定时从文件取一定时间范围的数据显示并更新图表
3.鼠标移动到数据点上就显示对应的x,y数据坐标,其中x轴数值是日期时间
4.标识最高数值
吐槽:没想到过程这么艰难,脑细胞不知道死了多少,还好我基本完成了,欣慰哈
效果图:
数据格式:
代码:
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'plot.ui' # # Created by: PyQt5 UI code generator 5.15.4 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(815, 458) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.verticalLayout_4.setSpacing(0) self.verticalLayout_4.setObjectName("verticalLayout_4") self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setSpacing(0) self.verticalLayout_3.setObjectName("verticalLayout_3") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setSpacing(0) self.verticalLayout.setObjectName("verticalLayout") self.plot_widget = QtWidgets.QWidget(self.centralwidget) self.plot_widget.setStyleSheet("") self.plot_widget.setObjectName("plot_widget") self.verticalLayout.addWidget(self.plot_widget) self.verticalLayout_3.addLayout(self.verticalLayout) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.label = QtWidgets.QLabel(self.centralwidget) self.label.setObjectName("label") self.horizontalLayout_2.addWidget(self.label) self.start_dateTimeEdit = QtWidgets.QDateTimeEdit(self.centralwidget) self.start_dateTimeEdit.setFocusPolicy(QtCore.Qt.ClickFocus) self.start_dateTimeEdit.setContextMenuPolicy(QtCore.Qt.NoContextMenu) self.start_dateTimeEdit.setWrapping(False) self.start_dateTimeEdit.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows) self.start_dateTimeEdit.setKeyboardTracking(True) self.start_dateTimeEdit.setMaximumDate(QtCore.QDate(9999, 12, 31)) self.start_dateTimeEdit.setCurrentSection(QtWidgets.QDateTimeEdit.YearSection) self.start_dateTimeEdit.setCalendarPopup(True) self.start_dateTimeEdit.setTimeSpec(QtCore.Qt.LocalTime) self.start_dateTimeEdit.setObjectName("start_dateTimeEdit") self.horizontalLayout_2.addWidget(self.start_dateTimeEdit) self.label_2 = QtWidgets.QLabel(self.centralwidget) self.label_2.setObjectName("label_2") self.horizontalLayout_2.addWidget(self.label_2) self.end_dateTimeEdit = QtWidgets.QDateTimeEdit(self.centralwidget) self.end_dateTimeEdit.setFocusPolicy(QtCore.Qt.ClickFocus) self.end_dateTimeEdit.setTime(QtCore.QTime(23, 59, 59)) self.end_dateTimeEdit.setMaximumDateTime(QtCore.QDateTime(QtCore.QDate(9999, 12, 31), QtCore.QTime(23, 59, 59))) self.end_dateTimeEdit.setCurrentSection(QtWidgets.QDateTimeEdit.YearSection) self.end_dateTimeEdit.setCalendarPopup(True) self.end_dateTimeEdit.setObjectName("end_dateTimeEdit") self.horizontalLayout_2.addWidget(self.end_dateTimeEdit) spacerItem = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem) self.select_cbox = QtWidgets.QComboBox(self.centralwidget) self.select_cbox.setObjectName("select_cbox") self.select_cbox.addItem("") self.select_cbox.addItem("") self.select_cbox.addItem("") self.horizontalLayout_2.addWidget(self.select_cbox) spacerItem1 = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem1) self.fresh_btn = QtWidgets.QPushButton(self.centralwidget) self.fresh_btn.setObjectName("fresh_btn") self.horizontalLayout_2.addWidget(self.fresh_btn) spacerItem2 = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem2) self.total_data_lbl = QtWidgets.QLabel(self.centralwidget) self.total_data_lbl.setLayoutDirection(QtCore.Qt.LeftToRight) self.total_data_lbl.setText("") self.total_data_lbl.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.total_data_lbl.setObjectName("total_data_lbl") self.horizontalLayout_2.addWidget(self.total_data_lbl) spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem3) self.xy_data_lbl = QtWidgets.QLabel(self.centralwidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(10) sizePolicy.setVerticalStretch(10) sizePolicy.setHeightForWidth(self.xy_data_lbl.sizePolicy().hasHeightForWidth()) self.xy_data_lbl.setSizePolicy(sizePolicy) self.xy_data_lbl.setMinimumSize(QtCore.QSize(120, 60)) self.xy_data_lbl.setStyleSheet("background-color: rgb(255, 255, 255);") self.xy_data_lbl.setText("") self.xy_data_lbl.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.xy_data_lbl.setObjectName("xy_data_lbl") self.horizontalLayout_2.addWidget(self.xy_data_lbl) spacerItem4 = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem4) self.horizontalLayout_2.setStretch(10, 2) self.horizontalLayout_2.setStretch(11, 1) self.horizontalLayout_2.setStretch(12, 1) self.verticalLayout_3.addLayout(self.horizontalLayout_2) self.verticalLayout_3.setStretch(0, 3) self.verticalLayout_4.addLayout(self.verticalLayout_3) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 815, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label.setText(_translate("MainWindow", " 时间范围: ")) self.start_dateTimeEdit.setDisplayFormat(_translate("MainWindow", "yyyy/MM/dd HH:mm")) self.label_2.setText(_translate("MainWindow", " -- ")) self.end_dateTimeEdit.setDisplayFormat(_translate("MainWindow", "yyyy/MM/dd HH:mm")) self.select_cbox.setItemText(0, _translate("MainWindow", "全部数据")) self.select_cbox.setItemText(1, _translate("MainWindow", "显示CPU")) self.select_cbox.setItemText(2, _translate("MainWindow", "显示内存")) self.fresh_btn.setText(_translate("MainWindow", "刷新图表"))
import os import sys from xlutils.copy import copy import matplotlib.pyplot as plt from threading import Thread plt.style.use('seaborn-whitegrid') import xlrd import xlwt from PyQt5.QtCore import QTimer, pyqtSignal, QDate from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout) from matplotlib import dates from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from plot import Ui_MainWindow from datetime import datetime import pandas as pd import random data_file = 'data.xls' sheet_name = '192.168.0.66' class myFigure(FigureCanvas): ''' 画布类,通过Figure创建画布,并且作为参数传递给父类FigureCanvas,最后添加绘图区self.axes ''' # draw_signal = pyqtSignal(object, object) def __init__(self, parent=None): self.fig = Figure() super(myFigure, self).__init__(self.fig) # 在父类中激活self.fig,否则不能显示图像 self.axes = self.fig.add_subplot(111) plt.rcParams['font.family'] = 'sans-serif' plt.rcParams['font.sans-serif'] = ['Microsoft Yahei'] self.axes.spines['left'].set_position(('data', 0)) def show_max_value(self, x, y, tip, color): max_b = 0 cur_a = 0 for a, b in zip(x, y): if int(b) > int(max_b): cur_a = a max_b = b if len(y) > 3: cur_a = dates.date2num(datetime.strptime(cur_a, "%Y/%m/%d %H:%M")) self.axes.text(cur_a, int(max_b) + 3, '最高%s %.0f' % (tip,int(max_b)), ha='center', va='bottom', fontsize=8, color=color) def set_mat_func(self, x, y, y1, show_cpu, show_mem): ''' 用清除画布刷新的方法绘图 :param x: x轴数据 :param y: y轴数据 :return: ''' # 数据清洗后,数据类型变成series self.x = x.values self.y = y.values self.y1 = y1.values # 清除绘图区 self.axes.cla() self.axes.set_xlabel('日期', fontsize=10) self.axes.set_ylabel('CPU/MEM(%)', fontsize=10) self.axes.grid(True, linestyle = '--', alpha = 0.5) self.axes.xaxis.set_major_formatter(dates.DateFormatter("%Y/%m/%d %H:%M")) x = [dates.date2num(datetime.strptime(self.x[i], "%Y/%m/%d %H:%M")) for i in range(len(self.x))] # 显示/隐藏数据判断 if show_cpu == True: self.axes.plot(x, self.y, 'r-', linewidth=1, marker='.', mfc='w') self.show_max_value(self.x, self.y, "CPU", color='r') if show_mem == True: self.axes.plot(x, self.y1, 'g-', linewidth=1, marker='.', mfc='w') self.show_max_value(self.x, self.y1, "MEM", color='g') self.axes.yaxis.set_major_locator(plt.MultipleLocator(20)) self.axes.set_ylim(-5, 100) self.fig.autofmt_xdate() self.fig.subplots_adjust(top=0.92, bottom=0.34, right=0.979, left=0.0999) self.fig.canvas.draw() self.fig.canvas.flush_events() class UI(QMainWindow, Ui_MainWindow): draw_signal = pyqtSignal(object, object, object, object, object) def __init__(self): super(UI, self).__init__() self.setupUi(self) self.fresh_btn.clicked.connect(lambda : Thread(target=self.on_fresh).start()) self.canvas = myFigure(self.plot_widget) self.draw_signal.connect(self.canvas.set_mat_func) self.canvas.mpl_connect('motion_notify_event', self.on_motion_notify_event) self.x = pd.Series([],dtype=object) self.y = pd.Series([], dtype=float) self.y1 = pd.Series([], dtype=float) self.layout = QVBoxLayout(self.plot_widget) self.layout.addWidget(self.canvas) self.init_ui() self.get_cpu() self.plotcos(self.x, self.y, self.y1, self.show_cpu, self.show_mem) self.total_data_lbl.setText(' 数据共%s条' % len(self.x)) self.flags = False self.timer = QTimer() self.timer.timeout.connect(self.set_data) self.timer.start(1000*60*5) def fresh_datetime(self): self.start_dateTimeEdit.setDate(QDate.currentDate()) self.end_dateTimeEdit.setDate(QDate.currentDate()) def init_ui(self): self.setWindowTitle('显示matplotlib绘制图形') self.fresh_datetime() self.xy_data_lbl.setText('数据坐标:\nX:\nY:') def on_motion_notify_event(self, event): ''' 功能:鼠标移动显示数据坐标轴上的x,y值 :param event: :return: ''' if event.xdata == None or event.ydata == None: return x = dates.num2date(event.xdata).strftime("%Y/%m/%d %H:%M") y = event.ydata.astype(int) x_values = self.x.values y_values = self.y.values y1_values = self.y1.values str_x = [str(x_values[i]) for i in range(len(x_values))] int_y = [y_values[j].astype(int) for j in range(len(y_values))] int_y1 = [y1_values[j].astype(int) for j in range(len(y1_values))] if x in str_x: if y in int_y: self.xy_data_lbl.setText('数据坐标:\nX:%s\nY:%s' %(x, y)) elif y in int_y1: self.xy_data_lbl.setText('数据坐标:\nX:%s\nY:%s' % (x, y)) else: self.xy_data_lbl.setText('数据坐标:\nX:\nY:') else: self.xy_data_lbl.setText('数据坐标:\nX:\nY:') def on_fresh(self): self.get_data() self.draw_signal.emit(self.x, self.y, self.y1, self.show_cpu, self.show_mem) def plotcos(self, x, y, y1, show_cpu, show_mem): self.canvas.set_mat_func(x, y, y1, show_cpu, show_mem) self.canvas.fig.suptitle('CPU/内存使用率实时监控', fontsize=10) def thread_set_data(self): self.t = Thread(target=self.get_cpu) self.t.setDaemon(True) self.t.start() def create_sheet(self): writebook = xlwt.Workbook(data_file) sheet = writebook.add_sheet(sheet_name) sheet.write(0, 0, '日期') sheet.write(0, 1, 'cpu使用率') sheet.write(0, 2, '内存使用率') writebook.save(data_file) def write(self): if not os.path.exists(data_file): self.create_sheet() try: readbook = xlrd.open_workbook(data_file) # 判断工作表是否存在 if not sheet_name in readbook.sheet_names(): self.create_sheet() row = readbook.sheet_by_name(sheet_name).nrows new_excel = copy(readbook) sheet = new_excel.get_sheet(sheet_name) except: return # 为测试,造数据 time_result = datetime.now().strftime("%Y/%m/%d %H:%M") data = random.randint(0, 70) data_mem = random.randint(0, 80) sheet.write(row, 0, time_result) sheet.write(row, 1, data) sheet.write(row, 2, data_mem) new_excel.save(data_file) def get_data(self): start_time = self.start_dateTimeEdit.dateTime().toString("yyyy/MM/dd hh:mm") end_time = self.end_dateTimeEdit.dateTime().toString("yyyy/MM/dd hh:mm") # 判断开始时间小于结束时间 d_start_time = datetime.strptime(start_time,"%Y/%m/%d %H:%M") d_end_time = datetime.strptime(end_time,"%Y/%m/%d %H:%M") if d_start_time >= d_end_time: self.fresh_datetime() start_time = self.start_dateTimeEdit.dateTime().toString("yyyy/MM/dd hh:mm") end_time = self.end_dateTimeEdit.dateTime().toString("yyyy/MM/dd hh:mm") df = pd.read_excel(data_file, sheet_name=sheet_name) # 数据清洗,缺失值的行要作删除处理,整行为空删除, 日期重复删除,重置索引 df.dropna(inplace=True) df = df.drop_duplicates(['日期'], keep='last').reset_index(drop=True) df = df.sort_values(by='日期', ascending=True) # 设置日期为索引, 然后获取某个区间数据 df = df.set_index('日期', drop=True) df = df[start_time:end_time] self.x = df.index self.cur_text = self.select_cbox.currentText() self.show_cpu = True self.show_mem = True if self.cur_text == '显示CPU': self.show_cpu = True self.show_mem = False elif self.cur_text == '显示内存': self.show_mem = True self.show_cpu = False self.y = df['cpu使用率'] self.y1 = df['内存使用率'] self.total_data_lbl.setText(' 数据共%s条' % len(self.x)) def get_cpu(self): self.write() self.get_data() def set_data(self): self.thread_set_data() self.t.join() self.draw_signal.emit(self.x, self.y, self.y1, self.show_cpu, self.show_mem) if __name__ == '__main__': app = QApplication([]) server = UI() server.show() sys.exit(app.exec_())