功能:
目的:想定時獲取系統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_())