导航

功能:

目的:想定时获取系统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_())