class BaseTimedGraph(QChartView): def __init__(self, chart=None, parent=None): super(BaseTimedGraph, self).__init__(parent) self.setObjectName('BaseGraph') self.seriesPurgedT = None self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.chart = chart if chart else QChart() self.setRenderHint(QPainter.Antialiasing) self.axisX = QDateTimeAxis() self.axisX.setTickCount(5) self.axisX.setFormat('hh:mm:ss') self.axisX.setTitleText('Time') self.chart.addAxis(self.axisX, Qt.AlignBottom) self.axisY = QValueAxis() self.axisY.setLabelFormat('%i') self.axisY.setMin(0) self.axisY.setMax(1024) self.chart.addAxis(self.axisY, Qt.AlignLeft) def removeOldSeries(self, series, before: int): for point in series.pointsVector(): if point.x() < before: series.remove(point) async def onBwStats(self, stats): pass
class ChartView(QChartView,QChart): def __init__(self, *args, **kwargs): super(ChartView, self).__init__(*args, **kwargs) self.resize(800, 600) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self.chart_init() self.timer_init() def timer_init(self): #使用QTimer,2秒触发一次,更新数据 self.timer = QTimer(self) self.timer.timeout.connect(self.drawLine) self.timer.start(100) def chart_init(self): self.chart = QChart() self.series = QScatterSeries() #设置曲线名称 self.series.setName("实时数据") self.series.setColor(QColor(255,0,0)) self.series.setMarkerSize(20.0) self.series.setPen(QPen(QtCore.Qt.PenStyle.NoPen)) #把曲线添加到QChart的实例中 self.chart.addSeries(self.series) #声明并初始化X轴,Y轴 self.dtaxisX = QDateTimeAxis() self.vlaxisY = QValueAxis() #设置坐标轴显示范围 self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300*1)) self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0)) self.vlaxisY.setMin(0) self.vlaxisY.setMax(1500) #设置X轴时间样式 self.dtaxisX.setFormat("MM月dd hh:mm:ss") #设置坐标轴上的格点 self.dtaxisX.setTickCount(6) self.vlaxisY.setTickCount(11) #设置坐标轴名称 self.dtaxisX.setTitleText("时间") self.vlaxisY.setTitleText("量程") #设置网格不显示` #把坐标轴添加到chart中 self.chart.addAxis(self.dtaxisX,Qt.AlignBottom) self.chart.addAxis(self.vlaxisY,Qt.AlignLeft) #把曲线关联到坐标轴 self.series.attachAxis(self.dtaxisX) self.series.attachAxis(self.vlaxisY) self.setChart(self.chart) def drawLine(self): #获取当前时间 bjtime = QDateTime.currentDateTime() #更新X轴坐标 self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300*1)) self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0)) #当曲线上的点超出X轴的范围时,移除最早的点 if(self.series.count()>149): self.series.removePoints(0,self.series.count()-149) #产生随即数 yint = random.randint(0,1500) #添加数据到曲线末端 self.series.append(bjtime.toMSecsSinceEpoch(),yint)
def auto_adjust_axis(axis: QValueAxis, bar_sets: [QBarSet], padding: float = 2): # Adjust the range so that everything is visible and add some gaps set_count = len(bar_sets) minimums = [] maximums = [] for i in range(set_count): minimums.append(sys.maxsize) maximums.append(-sys.maxsize) for set_index in range(set_count): bar_set = bar_sets[set_index] for i in range(bar_set.count()): minimums[set_index] = min(minimums[set_index], bar_set.at(i)) maximums[set_index] = max(maximums[set_index], bar_set.at(i)) minimums.append(0) axis_min = min(minimums) - padding maximums.append(0) axis_max = max(maximums) + padding print("axis min = {}, axis max = {}".format(axis_min, axis_max)) axis.setMin(axis_min) axis.setMax(axis_max)
def make_axis(self): if self.x_time_scaled: axis_x = QDateTimeAxis() axis_x.setFormat("yyyy-MM-dd HH:mm:ss") axis_x.setTitleText("Время") else: axis_x = QValueAxis() axis_x.setTitleText(self.x_name) if self.x_min: axis_x.setMin(self.x_min) if self.x_max: axis_x.setMax(self.x_max) axis_x.setLabelsAngle(self.x_label_angle) axis_x.setTickCount(self.x_tick_num) axis_y = QValueAxis() if self.y_min: axis_y.setMin(self.y_min) if self.y_max: axis_y.setMax(self.y_max) axis_y.setTitleText(self.y_name) axis_y.setTickCount(self.y_tick_num) axis_y.setLabelsAngle(self.y_label_angle) return axis_x, axis_y
class ChartView(QChartView): def __init__(self): QChartView.__init__(self) #self.resize(300, 300) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self.chart = QChart() self.seriesAcc = QLineSeries() self.seriesAcc.setName(CONF.leftUpNames[0]) self.chart.addSeries(self.seriesAcc) #声明并初始化X轴,Y轴 self.dtaxisX = QValueAxis() self.vlaxisY = QValueAxis() #设置坐标轴显示范围 self.dtaxisX.setMin(0) #self.dtaxisX.setMax(100) self.vlaxisY.setMin(0) #self.vlaxisY.setMax(100) self.dtaxisX.setTickCount(3) self.vlaxisY.setTickCount(3) #设置坐标轴名称 self.dtaxisX.setTitleText(CONF.leftUpNames[1]) self.vlaxisY.setTitleText(CONF.leftUpNames[2]) #设置网格不显示 self.vlaxisY.setGridLineVisible(False) #把坐标轴添加到chart中 self.chart.addAxis(self.dtaxisX, Qt.AlignBottom) self.chart.addAxis(self.vlaxisY, Qt.AlignLeft) self.seriesAcc.attachAxis(self.dtaxisX) self.seriesAcc.attachAxis(self.vlaxisY) self.initUI() def initUI(self): self.backend = BackendThread() self.backend.update_line.connect(self.handleLine) self.backend.start() def handleLine(self, data): if data[0] == 0: self.seriesAcc.clear() else: self.dtaxisX.setMax(data[0]) self.vlaxisY.setMax(data[0]) self.seriesAcc.clear() self.seriesAcc.append(0, 0) self.seriesAcc.append(data[0], data[1]) self.setChart(self.chart)
def update_graph(self, index): if index == 2: self.graph_chart.removeAllSeries() axis_x = QBarCategoryAxis() axis_x.setTitleText("日期") if self.graph_chart.axisX(): self.graph_chart.removeAxis(self.graph_chart.axisX()) self.graph_chart.addAxis(axis_x, Qt.AlignBottom) axis_y = QValueAxis() axis_y.setLabelFormat("%i") axis_y.setTitleText("售出量") if self.graph_chart.axisY(): self.graph_chart.removeAxis(self.graph_chart.axisY()) self.graph_chart.addAxis(axis_y, Qt.AlignLeft) max_num = 0 total_date = 0 set_dict = {} for key, data in sorted(self.graph_series.items(), key=lambda i: int(i[0])): axis_x.append( QDateTime.fromSecsSinceEpoch( int(key)).toString("yyyy年MM月dd日")) for set_name, value in data.items(): if set_name not in set_dict: set_dict[set_name] = QBarSet(set_name) for _ in range(total_date): set_dict[set_name].append(0) set_dict[set_name].append(value) max_num = max(max_num, value) total_date += 1 for _, bar_set in set_dict.items(): if bar_set.count() < total_date: bar_set.append(0) bar_series = QBarSeries() for _, bar_set in set_dict.items(): bar_series.append(bar_set) bar_series.hovered.connect(self.graph_tooltip) axis_y.setMax(max_num + 1) axis_y.setMin(0) self.graph_chart.addSeries(bar_series) bar_series.attachAxis(axis_x) bar_series.attachAxis(axis_y)
def createChart(self): self.timeChart = QChart() titleFont = QFont() titleFont.setPixelSize(18) titleBrush = QBrush(QColor(0, 0, 255)) self.timeChart.setTitleFont(titleFont) self.timeChart.setTitleBrush(titleBrush) self.timeChart.setTitle('Signal (Past ' + str(self.maxPoints) + ' Samples)') # self.timeChart.addSeries(testseries) # self.timeChart.createDefaultAxes() self.timeChart.legend().hide() # Axis examples: https://doc.qt.io/qt-5/qtcharts-multiaxis-example.html newAxis = QValueAxis() newAxis.setMin(0) newAxis.setMax(self.maxPoints) newAxis.setTickCount(11) newAxis.setLabelFormat("%d") newAxis.setTitleText("Sample") self.timeChart.addAxis(newAxis, Qt.AlignBottom) newAxis = QValueAxis() newAxis.setMin(-100) newAxis.setMax(-10) newAxis.setTickCount(9) newAxis.setLabelFormat("%d") newAxis.setTitleText("dBm") self.timeChart.addAxis(newAxis, Qt.AlignLeft) chartBorder = Qt.darkGray self.timePlot = QChartView(self.timeChart, self) self.timePlot.setBackgroundBrush(chartBorder) self.timePlot.setRenderHint(QPainter.Antialiasing) self.timeSeries = QLineSeries() pen = QPen(Qt.yellow) pen.setWidth(2) self.timeSeries.setPen(pen) self.timeChart.addSeries(self.timeSeries) self.timeSeries.attachAxis(self.timeChart.axisX()) self.timeSeries.attachAxis(self.timeChart.axisY())
class XChart(QChart): def __init__(self, parent=None): super(QChart, self).__init__(parent) self.setAnimationOptions(QChart.SeriesAnimations) self.axisX = QValueAxis() self.axisY = QValueAxis() self.axisX.setTickCount(10) self.axisY.setTickCount(10) self.axisX.setMin(0) self.axisX.setMax(1) self.axisY.setMin(0) self.axisY.setMax(1) series = [] #Temporay Test Data for i in range(0, 10): x = np.random.rand(10) y = np.random.rand(10) #self.series = XScatterSeries(tooltips = True) #self.series.addXY(x,y) #self.addSeries(self.series) #self.setAxisX(self.axisX, self.series); self.setAxisY(self.axisY, self.series) serset = XScatterSet() # many options not supported with openGl serset.setChart(self) serset.addXY(x, y) serset.setAxisX(self.axisX) serset.setAxisY(self.axisY) series.append(serset) #self.mousePressEvent.connect(self.zoomSelectionAction) def mousePressEvent(self, event): self._clickpos = event.buttonDownScenePos(Qt.LeftButton) self._clickval = self.mapToValue(self._clickpos) def mouseMoveEvent(self, event): self._clickpos = event.buttonDownScenePos(Qt.LeftButton) self._clickval = self.mapToValue(self._clickpos) dragpos = event.scenePos() c1 = self._clickpos.x() c2 = self._clickpos.y() d1 = dragpos.x() d2 = dragpos.y() x1 = min(c1, d1) y1 = min(c2, d2) x2 = abs(c1 - d1) y2 = abs(c2 - d2) try: self.selectbox.prepareGeometryChange() self.scene().removeItem(self.selectbox) except AttributeError: pass self.selectbox = self.scene().addRect( x1, y1, x2, y2, QPen(QColor(0, 0, 0, 0)), QBrush(QColor(125, 125, 255, 100))) def mouseReleaseEvent(self, event): try: self.selectbox.prepareGeometryChange() self.scene().removeItem(self.selectbox) except AttributeError: pass self._releasepos = event.lastScenePos() self._releaseval = self.mapToValue(self._releasepos) self.scene().update() print(self._clickval, self._releaseval) def keyReleaseEvent(self, event): print(dir(event)) if event == Qt.Key_Z: print('zoom') @pyqtSlot() def zoomSelectionAction(self): print('click')
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, weight_path, out_file_path, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.setWindowTitle("VAT ROLL COMPARE LABEL TOOL" + " " + CODE_VER) self.showMaximized() '''按键绑定''' # 输入媒体 self.import_media_pushButton.clicked.connect(self.import_media) # 导入 self.start_predict_pushButton.clicked.connect( self.predict_button_click) # 开始推理 # 输出媒体 self.open_predict_file_pushButton.clicked.connect( self.open_file_in_browser) # 文件中显示推理视频 # 下方 self.play_pushButton.clicked.connect( self.play_pause_button_click) # 播放 self.pause_pushButton.clicked.connect( self.play_pause_button_click) # 暂停 self.button_dict = dict() self.button_dict.update({ "import_media_pushButton": self.import_media_pushButton, "start_predict_pushButton": self.start_predict_pushButton, "open_predict_file_pushButton": self.open_predict_file_pushButton, "play_pushButton": self.play_pushButton, "pause_pushButton": self.pause_pushButton, }) '''媒体流绑定输出''' self.input_player = QMediaPlayer() # 媒体输入的widget self.input_player.setVideoOutput(self.input_video_widget) self.input_player.positionChanged.connect( self.change_slide_bar) # 播放进度条 self.output_player = QMediaPlayer() # 媒体输出的widget self.output_player.setVideoOutput(self.output_video_widget) '''初始化GPU chart''' self.series = QSplineSeries() self.chart_init() '''初始化GPU定时查询定时器''' # 使用QTimer,0.5秒触发一次,更新数据 self.timer = QTimer(self) self.timer.timeout.connect(self.draw_gpu_info_chart) self.timer.start(500) # 播放时长, 以 input 的时长为准 self.video_length = 0 self.out_file_path = out_file_path # 推理使用另外一线程 self.predict_handler_thread = PredictHandlerThread( self.output_player, self.out_file_path, weight_path, self.predict_info_plainTextEdit, self.predict_progressBar, self.fps_label, self.button_dict) # 界面美化 self.gen_better_gui() def gen_better_gui(self): """ 美化界面 :return: """ # Play 按钮 play_icon = QIcon() play_icon.addPixmap(QPixmap("./UI/icon/play.png"), QIcon.Normal, QIcon.Off) self.play_pushButton.setIcon(play_icon) # Pause 按钮 play_icon = QIcon() play_icon.addPixmap(QPixmap("./UI/icon/pause.png"), QIcon.Normal, QIcon.Off) self.pause_pushButton.setIcon(play_icon) def chart_init(self): """ 初始化 GPU 折线图 :return: """ # self.gpu_info_chart._chart = QChart(title="折线图堆叠") # 创建折线视图 self.gpu_info_chart._chart = QChart() # 创建折线视图 # chart._chart.setBackgroundVisible(visible=False) # 背景色透明 self.gpu_info_chart._chart.setBackgroundBrush(QBrush( QColor("#FFFFFF"))) # 改变图背景色 # 设置曲线名称 self.series.setName("GPU Utilization") # 把曲线添加到QChart的实例中 self.gpu_info_chart._chart.addSeries(self.series) # 声明并初始化X轴,Y轴 self.dtaxisX = QDateTimeAxis() self.vlaxisY = QValueAxis() # 设置坐标轴显示范围 self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300 * 1)) self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0)) self.vlaxisY.setMin(0) self.vlaxisY.setMax(100) # 设置X轴时间样式 self.dtaxisX.setFormat("hh:mm:ss") # 设置坐标轴上的格点 self.dtaxisX.setTickCount(5) self.vlaxisY.setTickCount(10) # 设置坐标轴名称 self.dtaxisX.setTitleText("Time") self.vlaxisY.setTitleText("Percent") # 设置网格不显示 self.vlaxisY.setGridLineVisible(False) # 把坐标轴添加到chart中 self.gpu_info_chart._chart.addAxis(self.dtaxisX, Qt.AlignBottom) self.gpu_info_chart._chart.addAxis(self.vlaxisY, Qt.AlignLeft) # 把曲线关联到坐标轴 self.series.attachAxis(self.dtaxisX) self.series.attachAxis(self.vlaxisY) # 生成 折线图 self.gpu_info_chart.setChart(self.gpu_info_chart._chart) def draw_gpu_info_chart(self): """ 绘制 GPU 折线图 :return: """ # 获取当前时间 time_current = QDateTime.currentDateTime() # 更新X轴坐标 self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300 * 1)) self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0)) # 当曲线上的点超出X轴的范围时,移除最早的点 remove_count = 600 if self.series.count() > remove_count: self.series.removePoints(0, self.series.count() - remove_count) # 对 y 赋值 # yint = random.randint(0, 100) gpu_info = get_gpu_info() yint = gpu_info[0].get("gpu_load") # 添加数据到曲线末端 self.series.append(time_current.toMSecsSinceEpoch(), yint) def import_media(self): """ 导入媒体文件 :return: """ self.parameter_source = QFileDialog.getOpenFileUrl()[0] self.input_player.setMedia(QMediaContent( self.parameter_source)) # 选取视频文件 # 设置 output 为一张图片,防止资源被占用 path_current = str(Path.cwd().joinpath("area_dangerous\1.jpg")) self.output_player.setMedia( QMediaContent(QUrl.fromLocalFile(path_current))) # 将 QUrl 路径转为 本地路径str self.predict_handler_thread.parameter_source = self.parameter_source.toLocalFile( ) self.input_player.pause() # 显示媒体 image_flag = os.path.splitext( self.predict_handler_thread.parameter_source)[-1].lower( ) in img_formats for item, button in self.button_dict.items(): if image_flag and item in ['play_pushButton', 'pause_pushButton']: button.setEnabled(False) else: button.setEnabled(True) # self.output_player.setMedia(QMediaContent(QFileDialog.getOpenFileUrl()[0])) # 选取视频文件 def predict_button_click(self): """ 推理按钮 :return: """ # 启动线程去调用 self.predict_handler_thread.start() def change_slide_bar(self, position): """ 进度条移动 :param position: :return: """ self.video_length = self.input_player.duration() + 0.1 self.video_horizontalSlider.setValue( round((position / self.video_length) * 100)) self.video_percent_label.setText( str(round((position / self.video_length) * 100, 2)) + '%') @pyqtSlot() def play_pause_button_click(self): """ 播放、暂停按钮回调事件 :return: """ name = self.sender().objectName() if self.parameter_source == "": return if name == "play_pushButton": print("play") self.input_player.play() self.output_player.play() elif name == "pause_pushButton": self.input_player.pause() self.output_player.pause() @pyqtSlot() def open_file_in_browser(self): os.system(f"start explorer {self.out_file_path}") @pyqtSlot() def closeEvent(self, *args, **kwargs): """ 重写关闭事件 :param args: :param kwargs: :return: """ print("Close")
class PulseGen(QtWidgets.QMainWindow, Ui_MainWindow): GENERATOR_PORT = 5000 def __init__(self, parent=None): super(PulseGen, self).__init__(parent) self.setupUi(self) self.pulse_gen_display = PulseGenDisplay() self.display_data = [0] * 500 self.pulse_height_max = self.pulse_height_dial.maximum() self.pulse_height_min = self.pulse_height_dial.minimum() self.pulse_gen_display.pulse_height = self.pulse_height_max # set pulse height self.pulse_gen_display.pulse_height = self.pulse_height_max self.pulse_height_dial.setValue(self.pulse_gen_display.pulse_height) self.pulse_height_dial.actionTriggered.connect(self.avoid_wrapping) self.pulse_height_dial.valueChanged.connect(self.pulse_height_changed) self.pulse_height_text.setText(str( self.pulse_gen_display.pulse_height)) # don't allow editing self.pulse_height_text.textChanged.connect(self.no_editing) # pulse shape for pulse_form in self.pulse_gen_display.pulse_shape.keys(): self.pulse_shape_combo.addItem(pulse_form) self.pulse_shape_combo.currentIndexChanged.connect( self.pulse_shape_changed) current_pulse_form = self.pulse_shape_combo.currentText() self.pulse_gen_display.pulse_form = self.pulse_gen_display.pulse_shape[ current_pulse_form] print("Current pulse form: " + current_pulse_form + " code: " + str(self.pulse_gen_display.pulse_form)) for pulse_freq in self.pulse_gen_display.frequencies.keys(): self.gen_freq_combo.addItem(pulse_freq) current_pulse_freq = self.gen_freq_combo.currentText() self.pulse_gen_display.pulse_T = self.pulse_gen_display.frequencies[ current_pulse_freq] print("Current pulse T: " + str(self.pulse_gen_display.pulse_T)) self.gen_freq_combo.currentIndexChanged.connect( self.pulse_freq_changed) for display_freq in self.pulse_gen_display.frequencies.keys(): self.display_freq_combo.addItem(display_freq) self.display_freq_combo.currentIndexChanged.connect( self.display_freq_changed) current_display_freq = self.display_freq_combo.currentText() self.pulse_gen_display.display_T = self.pulse_gen_display.frequencies[ current_display_freq] print("Current display T: " + str(self.pulse_gen_display.display_T)) self.start_stop_pb.clicked.connect(self.start_stop_meas) self.connect_pb.clicked.connect(self.connect) self.gen_chart = QChart() self.gen_chart.legend().hide() self.x_axis = QValueAxis() self.x_axis.setMax(500 * self.pulse_gen_display.display_T) self.x_axis.setMin(0) self.y_axis = QValueAxis() self.y_axis.setMax(3.3) self.y_axis.setMin(0) self.gen_chart.addAxis(self.x_axis, Qt.AlignBottom) self.gen_chart.addAxis(self.y_axis, Qt.AlignLeft) self.generator_screen.setChart(self.gen_chart) # display the initial pulse self.pulse_gen_display.calc_pulse() self.pulse_gen_display.get_display_data() self.series = QLineSeries() for i in range(500): self.series.append( i * self.pulse_gen_display.display_T, self.pulse_gen_display.display_data[i] * self.pulse_gen_display.calib) self.gen_chart.addSeries(self.series) self.series.attachAxis(self.x_axis) self.series.attachAxis(self.y_axis) self.display_pulse() self.pulse_height_timer = QTimer() self.pulse_height_timer.setSingleShot(True) self.pulse_height_timer.timeout.connect(self.pulse_height_ready) self.genRunning = False self.actionQuit.triggered.connect(self.quit) self.client_socket = QTcpSocket(self) self.connected = False self.show() def no_editing(self): self.pulse_height_text.setText(str( self.pulse_gen_display.pulse_height)) def avoid_wrapping(self, val): if val == QtWidgets.QAbstractSlider.SliderMove: minDistance = 1 if self.pulse_height_dial.value() == self.pulse_height_max \ and self.pulse_height_dial.sliderPosition()<self.pulse_height_max-minDistance: self.pulse_height_dial.setSliderPosition(self.pulse_height_max) elif self.pulse_height_dial.value() == self.pulse_height_min \ and self.pulse_height_dial.sliderPosition()>self.pulse_height_min+minDistance: self.pulse_height_dial.setSliderPosition(self.pulse_height_min) def pulse_shape_changed(self): ''' if self.genRunning: print("Current shape index: ",self.pulse_shape_combo.currentIndex()) print("current pulse form: ",self.pulse_gen_display.pulse_form) if self.pulse_shape_combo.currentIndex() == self.pulse_gen_display.pulse_form: return QMessageBox.warning(self, 'Pulse generation is active', 'Pulse generator is running\nPlease stop pulse generation before changing parameters') self.pulse_shape_combo.setCurrentIndex(self.pulse_gen_display.pulse_form) return ''' print("pulse shape changed") current_pulse_form = self.pulse_shape_combo.currentText() self.pulse_gen_display.pulse_form = self.pulse_gen_display.pulse_shape[ current_pulse_form] print("Current pulse form: " + current_pulse_form + " code: " + str(self.pulse_gen_display.pulse_form)) self.display_pulse() if not self.connected: return else: pulse_form_msg = "pulse_shape=" + str( self.pulse_gen_display.pulse_form) + "\n" print("Sending: " + pulse_form_msg) self.client_socket.write(bytes(pulse_form_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) def pulse_freq_changed(self): new_freq = self.gen_freq_combo.currentText() self.pulse_gen_display.pulse_T = self.pulse_gen_display.frequencies[ new_freq] print("Pulse frequency changed to " + new_freq + "T: " + str(self.pulse_gen_display.pulse_T)) self.display_pulse() ''' if self.genRunning: if self.pulse_gen_display.frequencies[self.gen_freq_combo.currentText()] == self.pulse_gen_display.pulse_T: return QMessageBox.warning(self, 'Pulse generation is active', 'Pulse generator is running\nPlease stop pulse generation before changing parameters') index = 0 for freq in self.pulse_gen_display.frequencies.keys(): print("freq: ",freq) print("val: ",self.pulse_gen_display.frequencies[freq]) if self.pulse_gen_display.frequencies[freq] == self.pulse_gen_display.pulse_T: self.gen_freq_combo.setCurrentIndex(index) break index += 1 return ''' if not self.connected: return else: pulse_T_msg = "pulse_T=" + str( self.pulse_gen_display.pulse_T) + "\n" print("Sending: " + pulse_T_msg) self.client_socket.write(bytes(pulse_T_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) def display_freq_changed(self): new_freq = self.display_freq_combo.currentText() self.pulse_gen_display.display_T = self.pulse_gen_display.frequencies[ new_freq] self.display_pulse() def pulse_height_changed(self): self.pulse_gen_display.pulse_height = self.pulse_height_dial.value() self.pulse_height_text.setText(str( self.pulse_gen_display.pulse_height)) self.display_pulse() self.pulse_height_timer.start(500) def pulse_height_ready(self): print("Timeout") if not self.connected: return else: pulse_height_msg = "pulse_height=" + str( self.pulse_gen_display.pulse_height) + "\n" print("Sending: " + pulse_height_msg) self.client_socket.write(bytes(pulse_height_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) def connect(self): if self.connected: self.client_socket.close() self.connected = False self.connect_pb.setChecked(False) self.connect_pb.setText("Connect to Pulse Generator") return server_ip = str(self.server_ip_text.text()) port = self.GENERATOR_PORT print("Server IP: ", server_ip) if server_ip.find('xxx') != -1: print("bad IP") QMessageBox.about( self, 'Bad Server IP', 'Please give a correct Server IP\n' 'IP is ' + server_ip) self.connect_pb.setChecked(False) return else: print("Connecting to " + server_ip + ":", port) self.client_socket.connectToHost(server_ip, port) self.client_socket.waitForConnected(1000) if self.client_socket.state() != QTcpSocket.ConnectedState: QMessageBox.warning( self, 'Connection failed', 'Please check IP address and port number\nIs the server running?' ) self.connect_pb.setChecked(False) return print("Connection established") self.connect_pb.setText("Connected") self.client_socket.waitForReadyRead() connectMsg = self.client_socket.readAll() msgstring = bytes(connectMsg).decode() print("Connection message:" + msgstring) print("Send generator settings") pulse_form_msg = "pulse_shape=" + str( self.pulse_gen_display.pulse_form) + "\n" print("Sending: " + pulse_form_msg) self.client_socket.write(bytes(pulse_form_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) pulse_T_msg = "pulse_T=" + str(self.pulse_gen_display.pulse_T) + "\n" print("Sending: " + pulse_T_msg) self.client_socket.write(bytes(pulse_T_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) pulse_height_msg = "pulse_height=" + str( self.pulse_gen_display.pulse_height) + "\n" print("Sending: " + pulse_height_msg) self.client_socket.write(bytes(pulse_height_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) running_msg = 'running?=\n' print("Sending: " + running_msg) self.client_socket.write(bytes(running_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() response = bytes(responseMsg).decode() print("reponse msg: ", response) if response == 'yes\n': print("task is running") elif response == 'no\n': print("task is not running") else: print("Don't know if task is running") self.connected = True def start_stop_meas(self): if not self.connected: QMessageBox.about( self, 'You must be connected to the generator server\n' 'please connect and try again') return if self.start_stop_pb.isChecked(): print("Start generation") # send current generator settings self.start_stop_pb.setText("Stop Pulse Generation") msg = 'start=\n' print("Sending: " + msg) self.client_socket.write(msg.encode()) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) self.genRunning = True else: print("Stop generation") self.start_stop_pb.setText("Start Pulse Generation") msg = 'stop=\n' self.client_socket.write(msg.encode()) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) self.genRunning = False def readTrace(self): print("Message from the generator") data = self.server_socket.readAll() msgstring = bytes(data).decode() print(msgstring) print(data, type(data)) def display_pulse(self): self.pulse_gen_display.calc_pulse() self.pulse_gen_display.get_display_data() self.x_axis.setMax(500 * self.pulse_gen_display.display_T) data_points = self.series.pointsVector() for i in range(500): data_points[i].setX(i * self.pulse_gen_display.display_T) data_points[i].setY(self.pulse_gen_display.display_data[i] * self.pulse_gen_display.calib) self.series.replace(data_points) def quit(self): app.exit()
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) # 调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() # 创建UI对象 self.ui.setupUi(self) # 构造UI界面 self.setWindowTitle("Demo12_8, 极坐标图") self.__iniChart() # 创建self.chart self.__drawRose() ## ==============自定义功能函数======================== def __iniChart(self): #图表初始化 self.chart = QPolarChart() ##极坐标图 self.chart.legend().setVisible(False) self.ui.chartView.setChart(self.chart) self.ui.chartView.setRenderHint(QPainter.Antialiasing) self.ui.chartView.setCursor(Qt.CrossCursor) self.__axisAngle = QValueAxis() #角度坐标轴 self.__axisAngle.setRange(0, 360) #角度范围 self.__axisAngle.setLabelFormat("NE %.0f") self.__axisAngle.setTickCount(9) self.chart.addAxis(self.__axisAngle, QPolarChart.PolarOrientationAngular) self.__axisRadial = QValueAxis() #径向坐标轴 self.__axisRadial.setTickCount(6) self.__axisRadial.setLabelFormat("%.1f") #标签格式 self.chart.addAxis(self.__axisRadial, QPolarChart.PolarOrientationRadial) pen = QPen(Qt.blue) pen.setWidth(2) seriesSpLine = QSplineSeries() seriesSpLine.setPen(pen) seriesSpLine.setPointsVisible(True) #数据点可见 seriesSpLine.hovered.connect(self.do_series_hovered) self.chart.addSeries(seriesSpLine) seriesSpLine.attachAxis(self.__axisAngle) seriesSpLine.attachAxis(self.__axisRadial) def __drawRose(self): series0 = self.chart.series()[0] series0.clear() theta = 0.0 #角度 delta = 5.0 #角度变化量 R = 10.0 #最大长度 N = self.ui.spinCount.value() #花瓣数 cnt = 1 + math.ceil(360 / delta) #数据点个数 for i in range(cnt): ang = math.radians(theta) #角度转换为弧度 rho = R * math.cos(N * ang) series0.append(theta, rho) #数据点:角度,长度 theta = theta + delta self.__axisRadial.setRange(0, 2 + R) ## ==============event处理函数========================== ## ==========由connectSlotsByName()自动连接的槽函数============ ## 工具栏按钮 @pyqtSlot() ##重画曲线 def on_actRedraw_triggered(self): self.ui.spinAngle_Min.setValue(0) self.ui.spinAngle_Max.setValue(360) self.__drawRose() @pyqtSlot() ##放大 def on_actZoomIn_triggered(self): self.ui.chartView.chart().zoom(1.2) @pyqtSlot() ##缩小 def on_actZoomOut_triggered(self): self.ui.chartView.chart().zoom(0.8) @pyqtSlot() ##恢复原始大小 def on_actZoomReset_triggered(self): self.ui.chartView.chart().zoomReset() ##图表外观控制 @pyqtSlot(int) ##主题 def on_comboTheme_currentIndexChanged(self, index): self.ui.chartView.chart().setTheme(QChart.ChartTheme(index)) @pyqtSlot(bool) ##显示数据点 def on_chkBox_ShowPoints_clicked(self, checked): series = self.ui.chartView.chart().series()[0] series.setPointsVisible(checked) ## 角度坐标轴设置 @pyqtSlot(int) ##设置坐标最小值 def on_spinAngle_Min_valueChanged(self, arg1): self.__axisAngle.setMin(arg1) @pyqtSlot(int) ##设置坐标最大值 def on_spinAngle_Max_valueChanged(self, arg1): self.__axisAngle.setMax(arg1) @pyqtSlot(int) ##分度数 def on_spinAngle_Ticks_valueChanged(self, arg1): self.__axisAngle.setTickCount(arg1) ## 径向坐标轴设置 @pyqtSlot(int) ##设置坐标范围 def on_spinRadial_Max_valueChanged(self, arg1): self.__axisRadial.setMax(arg1) @pyqtSlot(int) ##分度数 def on_spinRadial_Ticks_valueChanged(self, arg1): self.__axisRadial.setTickCount(arg1) @pyqtSlot(int) ##花瓣个数 def on_spinCount_valueChanged(self, value): self.__drawRose() ## 旋转 @pyqtSlot() ##两个相邻点的角度差如果大于180度,就不会直连,而是连接到中心 def on_btnRotate_clicked(self): dltAng = self.ui.spinRotate.value() #旋转的角度,顺时针方向为正 series0 = self.chart.series()[0] #获取序列 pointCount = len(series0.pointsVector()) #数据点个数 for i in range(pointCount): pt = series0.pointsVector()[i] #QPointF ang = pt.x() + dltAng if ang >= 360: ang = ang - 360 elif ang < 0: ang = 360 + ang series0.replace(i, ang, pt.y()) ## =============自定义槽函数=============================== def do_series_hovered(self, point, state): info = "Series 极径=, 角度=" if state: info = "Series 极径=%.1f,角度=NE %.1f°" % (point.y(), point.x()) self.ui.statusBar.showMessage(info)
class MyWidget(QWidget): def __init__(self): super().__init__() uic.loadUi('ws_ui.ui', self) self.con = sqlite3.connect("ws_database.db") self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.create_linechart() self.tmp, self.hmd, self.prs = 0, 0, 0 self.make_measure() self.measure_timer = QTimer(self) self.measure_timer.setInterval(MEASURE_FREQUENCIES * 1000) self.measure_timer.timeout.connect(self.make_measure) self.measure_timer.start() self.update_timer = QTimer(self) self.update_timer.setInterval(1000) self.update_timer.timeout.connect(self.update_labels) self.update_timer.start() self.update_labels() def quit(self): self.destroy() quit() def make_measure(self): sns = sensor_measure() if sns[0] != ERROR_CODE: self.tmp = sns[0] else: print('tmp error') if sns[1] != ERROR_CODE: self.hmd = sns[1] else: print('hmd error') if sns[2] != ERROR_CODE: self.prs = sns[2] else: print('prs error') time = int(dt.datetime.now().timestamp()) req = """ INSERT INTO short_term_data(tmp, hmd, prs, time_from_epoch) VALUES(?,?,?,?) """ self.con.execute(req, (self.tmp, self.hmd, self.prs, time)) self.con.commit() self.update_linechart() def update_labels(self): deg = u'\N{DEGREE SIGN}' hpa = 'ʰᴾᵃ' self.time_label.setText(dt.datetime.now().strftime('%H:%M')) self.tmp_label.setText('{} {}C'.format(self.tmp, deg)) self.hmd_label.setText('{} %'.format(self.hmd)) self.prs_label.setText('{} {}'.format(self.prs, hpa)) def create_linechart(self): self.chart = QChart() self.chart.legend().hide() self.series = QLineSeries() self.axisValue = QValueAxis() self.axisCurrentTime = QValueAxis() self.axisTime = QDateTimeAxis() self.axisTime.setFormat("hh:mm") self.chartview = QChartView(self.chart, self.groupBox) self.chartview.resize(540, 460) self.chartview.move(0, 0) self.chartview.setRenderHint(QPainter.Antialiasing) def update_linechart(self): if self.axisTime in self.chart.axes(): self.chart.removeAxis(self.axisTime) if self.axisCurrentTime in self.chart.axes(): self.chart.removeAxis(self.axisCurrentTime) if self.axisValue in self.chart.axes(): self.chart.removeAxis(self.axisValue) if self.series in self.chart.series(): self.chart.removeSeries(self.series) self.series.clear() self.axisValue.setMax(50) self.axisValue.setMin(-50) req = """ SELECT tmp, time_from_epoch FROM short_term_data WHERE (time_from_epoch - ?) < 86400 AND NOT tmp = ? """ cur = self.con.cursor() result = list(cur.execute(req, (int(dt.datetime.now().timestamp()), ERROR_CODE))) for measure in result: self.series.append(measure[1] * 1000, measure[0]) self.axisTime.setMin(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000 - 86390000)) self.axisTime.setMax(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000)) self.chart.addSeries(self.series) self.chart.addAxis(self.axisTime, Qt.AlignBottom) self.series.attachAxis(self.axisTime) self.chart.addAxis(self.axisValue, Qt.AlignLeft) self.series.attachAxis(self.axisValue) self.chart.setTitle('Температура')
class BalanceHistoryChartView(QChartView): """ Chart that displays the balance between several dates from an account, token or whole portfolio """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.chart = QChart() self.FIAT_CURRENCY = confighandler.get_fiat_currency() def setupChartWithData(self, selectiontype, name=None): """ Chart gets updated displaying new data. The data gets extracted from cbalancehistory, according to the selection Parameters: - selectiontype : in ('account','token','all') - name: str, corresponds to account/token on cbalancehistory """ self.chart = QChart() self.chart.setTheme(QChart.ChartThemeDark) self.chart.setAnimationOptions(QChart.SeriesAnimations) self.chart.setBackgroundBrush(QBrush(QColor('#19232d'))) # self.chart.setTitle("") # self.chart.setTitleBrush(QBrush(QColor('white'))) # Data # Get data if selectiontype == 'token': assert(name is not None) data = chistoricalbalances.get_balances_with_token_tuple(name) elif selectiontype == 'account': assert(name is not None) data = chistoricalbalances.get_balances_with_account_tuple(name) elif selectiontype == 'all': data = chistoricalbalances.get_balances_by_day_tuple() # Separate balance_btc from balance_fiat dates, balances_btc, balances_fiat = [], [], [] for date in data: dates.append(int(date)) balances_btc.append(data[date][0]) balances_fiat.append(data[date][1]) # Series self.btcseries = QSplineSeries() self.fiatseries = QSplineSeries() for date, balance_btc, balance_fiat in zip(dates, balances_btc, balances_fiat): date = datetime.fromtimestamp(date) date = datetime(date.year, date.month, date.day) dateQ = QDateTime(date).toMSecsSinceEpoch() self.btcseries.append(dateQ, balance_btc) self.fiatseries.append(dateQ, balance_fiat) # Append current point currentdate = QDateTime(datetime.today()).toMSecsSinceEpoch() if selectiontype == "all": # Append current balances self.btcseries.append(currentdate, cbalances.get_total_balance_all_accounts()) self.fiatseries.append(currentdate, cbalances.get_total_balance_all_accounts_fiat()) elif name != '': if selectiontype == "account": # Append current balances self.btcseries.append( currentdate, cbalances.get_total_account_balance(name)) self.fiatseries.append( currentdate, cbalances.get_total_account_balance_fiat(name)) elif selectiontype == "token": pass # Axis X (Dates) self.x_axis = QDateTimeAxis() self.x_axis.setTickCount(11) self.x_axis.setLabelsAngle(70) self.x_axis.setFormat("dd-MM-yy") self.x_axis.setTitleText(self.tr('Date')) # Axis Y (Balances) # BTC self.y_axis_btc = QValueAxis() if len(balances_btc) > 0: self.y_axis_btc.setMax(max(balances_btc)*1.1) self.y_axis_btc.setMin(min(balances_btc)*0.9) # Fiat self.y_axis_fiat = QValueAxis() if len(balances_fiat) > 0: self.y_axis_fiat.setMax(max(balances_fiat)*1.1) self.y_axis_fiat.setMin(min(balances_fiat)*0.9) self.chart.addAxis(self.y_axis_btc, Qt.AlignLeft) self.chart.addAxis(self.y_axis_fiat, Qt.AlignRight) self.chart.addAxis(self.x_axis, Qt.AlignBottom) # Add series to chart # BTC self.btcseries.setName("BTC") self.chart.addSeries(self.btcseries) self.btcseries.attachAxis(self.x_axis) self.btcseries.attachAxis(self.y_axis_btc) # Fiat self.fiatseries.setName(self.FIAT_CURRENCY.upper()) self.chart.addSeries(self.fiatseries) self.fiatseries.attachAxis(self.x_axis) self.fiatseries.attachAxis(self.y_axis_fiat) self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) self.setStyleSheet("border: 0px")
class AmzHistoryChart(QWidget): """A chart that graphs the history of an AmazonListing's sales rank, price, and number of offers.""" def __init__(self, parent=None): super(AmzHistoryChart, self).__init__(parent=parent) self.dbsession = Session() self.context_menu_actions = [] self._avg_pointspan = 0 self._max_points = 100 self.source = None self.history = None layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) # Set up the chart self.chart_view = QChartView(self) self.chart_view.setRenderHint(QPainter.Antialiasing) self.chart_view.setContextMenuPolicy(Qt.CustomContextMenu) self.chart_view.customContextMenuRequested.connect(self.context_menu) self.chart = QChart() self.chart.legend().hide() self.chart.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsSelectable) self.chart.installEventFilter(self) self.chart_view.setChart(self.chart) self.layout().addWidget(self.chart_view) # Create the axes rcolor = QColor(50, 130, 220) pcolor = QColor(0, 200, 0) ocolor = QColor(255, 175, 0) self.timeAxis = QDateTimeAxis() self.timeAxis.setFormat('M/dd hh:mm') self.timeAxis.setTitleText('Date/Time') self.chart.addAxis(self.timeAxis, Qt.AlignBottom) self.timeAxis.minChanged.connect(self.on_timeaxis_min_changed) self.rankAxis = QValueAxis() self.rankAxis.setLabelFormat('%\'i') self.rankAxis.setTitleText('Sales Rank') self.rankAxis.setLinePenColor(rcolor) self.rankAxis.setLabelsColor(rcolor) self.chart.addAxis(self.rankAxis, Qt.AlignLeft) self.priceAxis = QValueAxis() self.priceAxis.setLabelFormat('$%.2f') self.priceAxis.setTitleText('Price') self.priceAxis.setLinePenColor(pcolor) self.priceAxis.setLabelsColor(pcolor) self.chart.addAxis(self.priceAxis, Qt.AlignRight) # Create the series self.rankLine = QLineSeries() self.chart.addSeries(self.rankLine) self.rankLine.attachAxis(self.timeAxis) self.rankLine.attachAxis(self.rankAxis) self.rankLine.setColor(rcolor) self.priceLine = QLineSeries() self.chart.addSeries(self.priceLine) self.priceLine.attachAxis(self.timeAxis) self.priceLine.attachAxis(self.priceAxis) self.priceLine.setColor(pcolor) self.salesPoints = QScatterSeries() self.chart.addSeries(self.salesPoints) self.salesPoints.attachAxis(self.timeAxis) self.salesPoints.attachAxis(self.rankAxis) self.salesPoints.setColor(ocolor) def add_context_action(self, action): """Add an action to the chart's context menu.""" self.context_menu_actions.append(action) def add_context_actions(self, actions): """Adds all action in an iterable.""" self.context_menu_actions.extend(actions) def remove_context_action(self, action): """Removes an action from the chart's context menu.""" self.context_menu_actions.remove(action) def context_menu(self, point): """Show a context menu on the chart.""" menu = QMenu(self) menu.addActions(self.context_menu_actions) point = self.chart_view.viewport().mapToGlobal(point) menu.popup(point) def set_source(self, source): """Set the source listing for the graph.""" self.source = source # Update the chart self.rankLine.clear() self.priceLine.clear() self.salesPoints.clear() self.history = None start_date = datetime.utcnow() - timedelta(days=5) self.load_history_from(start_date) self.reset_axes() def load_history_from(self, start_date=datetime.utcfromtimestamp(0)): """Load history data from start-present.""" if not self.source: self._avg_pointspan = 0 return # Get the earliest point already in the chart points = self.rankLine.pointsVector() if points: # The chart is drawn right-to-left, so the last point is the earliest point earliest_msecs = points[-1].x() earliest = datetime.fromtimestamp(earliest_msecs / 1000, timezone.utc) if earliest <= start_date: return else: earliest = datetime.now(timezone.utc) # Get the product history stats if we don't already have them if self.history is None: self.history = dbhelpers.ProductHistoryStats(self.dbsession, self.source.id) # Start adding points to the chart last_row = None for row in self.dbsession.query(AmzProductHistory).\ filter(AmzProductHistory.amz_listing_id == self.source.id, AmzProductHistory.timestamp > start_date.replace(tzinfo=None), AmzProductHistory.timestamp < earliest.replace(tzinfo=None)).\ order_by(AmzProductHistory.timestamp.desc()): # SqlAlchemy returns naive timestamps time = row.timestamp.replace(tzinfo=timezone.utc).timestamp() * 1000 self.rankLine.append(time, row.salesrank or 0) self.priceLine.append(time, row.price or 0) if last_row: # It's possible for salesrank to be None try: slope = (last_row.salesrank - row.salesrank) / (last_row.timestamp.timestamp() - row.timestamp.timestamp()) if slope < -0.3: self.salesPoints.append(last_row.timestamp.replace(tzinfo=timezone.utc).timestamp() * 1000, last_row.salesrank) except (TypeError, AttributeError): pass last_row = row # Calculate the average span between points spans = 0 for p1, p2 in itertools.zip_longest(itertools.islice(points, 0, None, 2), itertools.islice(points, 1, None, 2)): if p1 and p2: spans += abs(p1.x() - p2.x()) self._avg_pointspan = spans // 2 def on_timeaxis_min_changed(self, min): """Respond to a change in the time axis' minimum value.""" # toTime_t() converts to UTC automatically utc_min = datetime.fromtimestamp(min.toTime_t(), timezone.utc) self.load_history_from(start_date=utc_min - timedelta(days=1)) def reset_axes(self): """Resets the chart axes.""" r = self.rankLine.pointsVector() p = self.priceLine.pointsVector() # If there is only one data point, set the min and max to the day before and the day after if len(r) == 1: tmin = QDateTime.fromMSecsSinceEpoch(r[0].x(), Qt.LocalTime).addDays(-1) tmax = QDateTime.fromMSecsSinceEpoch(r[0].x(), Qt.LocalTime).addDays(+1) else: tmin = min(r, key=lambda pt: pt.x(), default=QPointF(QDateTime.currentDateTime().addDays(-1).toMSecsSinceEpoch(), 0)).x() tmax = max(r, key=lambda pt: pt.x(), default=QPointF(QDateTime.currentDateTime().addDays(+1).toMSecsSinceEpoch(), 0)).x() tmin = QDateTime.fromMSecsSinceEpoch(tmin, Qt.LocalTime) tmax = QDateTime.fromMSecsSinceEpoch(tmax, Qt.LocalTime) self.timeAxis.setMin(tmin) self.timeAxis.setMax(tmax) # Find the min and max values of the series min_point = lambda pts: min(pts, key=lambda pt: pt.y(), default=QPointF(0, 0)) max_point = lambda pts: max(pts, key=lambda pt: pt.y(), default=QPointF(0, 0)) rmin = min_point(r) rmax = max_point(r) pmin = min_point(p) pmax = max_point(p) # Scale the mins and maxes to 'friendly' values scalemin = lambda v, step: ((v - step / 2) // step) * step scalemax = lambda v, step: ((v + step / 2) // step + 1) * step # The the axis bounds rmin = max(scalemin(rmin.y(), 1000), 0) rmax = scalemax(rmax.y(), 1000) pmin = max(scalemin(pmin.y(), 5), 0) pmax = scalemax(pmax.y(), 5) self.rankAxis.setMin(rmin) self.rankAxis.setMax(rmax) self.priceAxis.setMin(pmin) self.priceAxis.setMax(pmax) def eventFilter(self, watched, event): """Intercept and handle mouse events.""" if event.type() == QEvent.GraphicsSceneWheel and event.orientation() == Qt.Vertical: factor = 0.95 if event.delta() < 0 else 1.05 self.chart.zoom(factor) return True if event.type() == QEvent.GraphicsSceneMouseDoubleClick: self.chart.zoomReset() self.reset_axes() return True if event.type() == QEvent.GraphicsSceneMouseMove: delta = event.pos() - event.lastPos() self.chart.scroll(-delta.x(), delta.y()) return True return False
class LastMonthsHistogram(QChartView): """ Chart that displays the balance from the whole portfolio from the last months """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.chart = QChart() def setupChartWithData(self): """ Chart gets updated displaying the new data. Data has to be expressed on a dictionary form: - keys are timestamps - values are total balance for that timestamp """ self.chart = QChart() self.chart.setTheme(QChart.ChartThemeDark) self.chart.setAnimationOptions(QChart.SeriesAnimations) self.chart.setBackgroundBrush(QBrush(QColor("transparent"))) # Series self.barseries = QBarSeries() currentyear, currentmonth = datetime.today().year, datetime.today( ).month dates, amounts = [], [] # Get 5 previous month numbers for _ in range(5): dates.append((currentmonth, currentyear)) currentmonth -= 1 if currentmonth == 0: currentmonth = 12 currentyear -= 1 # Get amounts for each month for d in dates: month, year = d amounts.append( dbhandler.get_total_wealth_on_month(month, year=year)) # Iterate months and amount and insert them into the histogram appropiately barset = QBarSet('Total wealth') labelsfont = QFont() labelsfont.setFamily('Inter') labelsfont.setBold(True) barset.setLabelFont(labelsfont) barset.setColor(QColor("#D3EABD")) x_values = [] for d, a in zip(reversed(dates), reversed(amounts)): if a > 0: barset.append(int(a)) x_values.append(calendar.month_name[d[0]]) self.barseries.append(barset) self.barseries.setName("Last Months") self.barseries.setLabelsVisible(True) self.barseries.setBarWidth(0.2) self.barseries.setLabelsPosition(QAbstractBarSeries.LabelsOutsideEnd) self.chart.addSeries(self.barseries) # Axis X (Dates) self.x_axis = QBarCategoryAxis() self.x_axis.setTitleText(self.tr('Date')) labelsfont = QFont() labelsfont.setFamily('Roboto') labelsfont.setLetterSpacing(QFont.AbsoluteSpacing, 1) labelsfont.setWeight(QFont.Light) labelsfont.setPointSize(9) self.x_axis.setLabelsFont(labelsfont) self.x_axis.setGridLineVisible(False) self.x_axis.setLineVisible(False) self.x_axis.setLinePenColor(QColor("#D3EABD")) self.x_axis.setTitleVisible(False) self.x_axis.append(x_values) self.chart.addAxis(self.x_axis, Qt.AlignBottom) # Axis Y (Balances) self.y_axis = QValueAxis() self.y_axis.setMax(max(amounts) * 1.3) self.y_axis.setMin(min(amounts) * 0.95) self.y_axis.hide() labelsfont = QFont() labelsfont.setPointSize(4) self.y_axis.setLabelsFont(labelsfont) self.chart.addAxis(self.y_axis, Qt.AlignLeft) # Attach axis to series self.barseries.attachAxis(self.x_axis) self.barseries.attachAxis(self.y_axis) # Legend self.chart.legend().hide() # Set up chart on ChartView self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) self.setStyleSheet("border: 0px; background-color: rgba(0,0,0,0)")
class ResonanceFrequencyFinder(QMainWindow): # AVAILABLE_WINDOW_SIZES = ["5 Sec", "8 Sec", "10 Sec", "15 Sec", "20 Sec"] DEFAULT_GRAPH_PADDING = 2 DEFAULT_FFT_WINDOW_SIZE = 5 DEFAULT_SAMPLE_PADDING = 2 DEFAULT_RECORDING_DURATION = 30 + DEFAULT_SAMPLE_PADDING DEFAULT_MIN_FREQUENCY = 17 DEFAULT_MAX_FREQUENCY = 35 DEFAULT_FREQUENCY_STEP = 2 # Used to create a band for which the average frequency amplitude is computed DEFAULT_FREQUENCY_PADDING = 0.2 DEFAULT_BANDPASS_MIN = 8 DEFAULT_BANDPASS_MAX = 40 DEFAULT_C3_CHANNEL_INDEX = 4 DEFAULT_CZ_CHANNEL_INDEX = 2 DEFAULT_C4_CHANNEL_INDEX = 0 def __init__(self, board: BoardShim): super().__init__() self.setGeometry(0, 0, 1800, 900) self.setWindowTitle("Resonance-Like Frequency") self.board = board self.recording_progress_dialog = None self.eeg_data_buffer = utils.EegData() self.reading_timer = QTimer() self.recording = False self.recording_reference = False self.reference_eeg_data = utils.EegData() self.index_generator = utils.FrequencyIndexGenerator(global_config.SAMPLING_RATE) self.eeg_sample_count = 0 self.root_widget = QWidget() self.root_layout = QGridLayout() self.root_widget.setLayout(self.root_layout) self.setCentralWidget(self.root_widget) title = QLabel("<h1>Resonance Frequency Finder</h1>") title.setAlignment(Qt.AlignCenter) self.root_layout.addWidget(title, 0, 0, 1, 3) # window_size_label = QLabel("window size: ") # window_size_label.setAlignment(Qt.AlignRight) # self.window_size_combo_box = QComboBox() # self.window_size_combo_box.addItems(self.AVAILABLE_WINDOW_SIZES) self.root_directory_label = QLabel() self.select_root_directory = QPushButton("Select/Change") self.select_root_directory.clicked.connect(self.pick_root_directory) self.record_btn = QPushButton("Record") self.record_btn.setEnabled(False) self.record_btn.clicked.connect(self.record_clicked) self.record_reference_btn = QPushButton("Record Reference") self.record_reference_btn.clicked.connect(self.record_reference_clicked) # self.root_layout.addWidget(utils.construct_horizontal_box([ # window_size_label, self.window_size_combo_box, self.record_btn # ]), 1, 0, 1, 3) self.load_results_btn = QPushButton("Load Existing Data") self.load_results_btn.clicked.connect(self.load_existing_data) self.root_layout.addWidget(utils.construct_horizontal_box([ self.record_btn, self.record_reference_btn, self.root_directory_label, self.select_root_directory, self.load_results_btn ]), 1, 0, 1, 3) self.current_freq_label = QLabel() self.root_layout.addWidget(utils.construct_horizontal_box([self.current_freq_label]), 2, 0, 1, 3) self.frequency_slider = QSlider() self.frequency_slider.setRange(self.DEFAULT_MIN_FREQUENCY, self.DEFAULT_MAX_FREQUENCY) self.frequency_slider.setSingleStep(self.DEFAULT_FREQUENCY_STEP) self.frequency_slider.setTickInterval(self.DEFAULT_FREQUENCY_STEP) self.frequency_slider.valueChanged.connect(self.update_freq_label) self.frequency_slider.setTickPosition(QSlider.TicksBelow) self.frequency_slider.setOrientation(Qt.Horizontal) min_freq_label = QLabel(f"<b>{self.DEFAULT_MIN_FREQUENCY} Hz</b>") max_freq_label = QLabel(f"<b>{self.DEFAULT_MAX_FREQUENCY} Hz</b>") self.root_layout.addWidget(utils.construct_horizontal_box([ min_freq_label, self.frequency_slider, max_freq_label ]), 3, 0, 1, 3) self.c3_amplitude_bar_set = QBarSet("Electrode C3") self.cz_amplitude_bar_set = QBarSet("Electrode Cz") self.c4_amplitude_bar_set = QBarSet("Electrode C4") self.frequencies = [] for freq in range(self.DEFAULT_MIN_FREQUENCY, self.DEFAULT_MAX_FREQUENCY + 1, self.DEFAULT_FREQUENCY_STEP): self.frequencies.append(f"{freq} Hz") self.c3_amplitude_bar_set.append(1) self.cz_amplitude_bar_set.append(1) self.c4_amplitude_bar_set.append(1) self.freq_axis = QBarCategoryAxis() self.freq_axis.append(self.frequencies) self.amplitude_axis = QValueAxis() self.amplitude_axis.setRange(0, 4) self.freq_chart = QChart() self.freq_chart.setAnimationOptions(QChart.SeriesAnimations) self.electrodes_data_series = QBarSeries() self.electrodes_data_series.append(self.c3_amplitude_bar_set) self.electrodes_data_series.append(self.cz_amplitude_bar_set) self.electrodes_data_series.append(self.c4_amplitude_bar_set) self.freq_chart.addSeries(self.electrodes_data_series) self.freq_chart.setTitle("<h1>Frequency Amplitude Increase</h1>") self.freq_chart.addAxis(self.freq_axis, Qt.AlignBottom) self.freq_chart.addAxis(self.amplitude_axis, Qt.AlignLeft) self.electrodes_data_series.attachAxis(self.amplitude_axis) self.electrodes_data_series.attachAxis(self.freq_axis) self.frequency_amplitude_graph = QChartView(self.freq_chart) self.frequency_amplitude_graph.setRenderHint(QPainter.Antialiasing) self.root_layout.addWidget(self.frequency_amplitude_graph, 4, 0, 15, 3) self.auto_adjust_axis() def update_freq_label(self): self.current_freq_label.setText("Selected Frequency: {} Hz".format(self.frequency_slider.value())) def pick_root_directory(self): path = QFileDialog.getExistingDirectory(self, "Root Directory...") self.root_directory_label.setText(path) def record_clicked(self, reference: bool = False): # selected_window_text = self.window_size_combo_box.currentText() # window_size_text = selected_window_text.replace(" Sec", "") # window_size = -1 # # if utils.is_integer(window_size_text): # window_size = int(window_size_text) # else: # print("Invalid window size...") # return # window_size_in_samples = window_size * SAMPLING_RATE recording_duration_in_samples = self.DEFAULT_RECORDING_DURATION * global_config.SAMPLING_RATE if not reference and (self.frequency_slider.value() - self.DEFAULT_MIN_FREQUENCY) % self.DEFAULT_FREQUENCY_STEP != 0: err = QErrorMessage(self) err.showMessage("Invalid Frequency Selected") err.exec() return self.recording_progress_dialog = \ QProgressDialog("Reading EEG data from board...", "Stop Recording", 0, int(recording_duration_in_samples), self) self.recording_progress_dialog.setWindowTitle("Reading Data, Please Wait...") self.recording_progress_dialog.setWindowModality(Qt.WindowModal) self.recording_progress_dialog.show() if reference: self.recording_reference = True else: self.recording = True self.eeg_data_buffer.clear() self.board.start_stream() self.reading_timer = QTimer() self.reading_timer.timeout.connect(self.read_data) self.reading_timer.start(100) def record_reference_clicked(self): print("Record reference clicked") if self.reference_eeg_data.get_channel_data(0).shape[0] > 0: self.reference_eeg_data.clear() self.record_clicked(reference=True) def read_data(self): if not self.recording and not self.recording_reference: return recording_duration_in_samples = self.recording_progress_dialog.maximum() if self.recording_reference: if self.reference_eeg_data.get_channel_data(0).shape[0] > recording_duration_in_samples or\ self.recording_progress_dialog.wasCanceled(): self.stop_recording(True) return if self.recording: if self.recording_progress_dialog.wasCanceled() or\ self.eeg_data_buffer.get_channel_data(0).shape[0] > recording_duration_in_samples: self.stop_recording(self.recording_reference) return if self.board.get_board_data_count() > 0: raw_data = self.board.get_board_data() raw_eeg_data = utils.extract_eeg_data(raw_data, global_config.BOARD_ID) self.eeg_sample_count += raw_eeg_data.shape[1] path = self.root_directory_label.text() if path != "": full_path = path + "/" + global_config.RESONANCE_DATA_FILE_NAME DataFilter.write_file(raw_eeg_data, full_path, "a") # c3 = raw_eeg_data[self.DEFAULT_C3_CHANNEL_INDEX, :] # cz = raw_eeg_data[self.DEFAULT_CZ_CHANNEL_INDEX, :] # c4 = raw_eeg_data[self.DEFAULT_C4_CHANNEL_INDEX, :] if self.recording_reference: self.reference_eeg_data.append_data(raw_eeg_data) print(f"reference size: {self.reference_eeg_data.sample_count()}") self.recording_progress_dialog.setValue(self.reference_eeg_data.get_channel_data(0).shape[0]) else: self.eeg_data_buffer.append_data(raw_eeg_data) print(f"data size: {self.eeg_data_buffer.sample_count()}") self.recording_progress_dialog.setValue(self.eeg_data_buffer.get_channel_data(0).shape[0]) def load_existing_data(self): path = QFileDialog.getExistingDirectory(self, "Root Directory...") if path == "": return filter_settings = utils.FilterSettings(global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX) frequencies, eeg_data, reference_data = utils.load_slice_and_filter_resonance_data(path, filter_settings) print(frequencies) size = len(frequencies) x = np.arange(size) x_ticks = [] plot_data = np.zeros((3, size)) for i in range(size): current_eeg_data = eeg_data[i] freq = frequencies[i] x_ticks.append(f"{freq} Hz") freq_band = utils.FrequencyBand( freq - self.DEFAULT_FREQUENCY_PADDING, freq + self.DEFAULT_FREQUENCY_PADDING) reference_c3_extractor = reference_data.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_cz_extractor = reference_data.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_c4_extractor = reference_data.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c3_extractor = current_eeg_data.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_cz_extractor = current_eeg_data.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c4_extractor = current_eeg_data.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) c3_diff, cz_diff, c4_diff = self.amplitude_diff(freq_band, reference_c3_extractor, reference_cz_extractor, reference_c4_extractor, data_c3_extractor, data_cz_extractor, data_c4_extractor) plot_data[0, i] = c3_diff plot_data[1, i] = cz_diff plot_data[2, i] = c4_diff plt.figure() plt.title("Amplitude Increase") plt.bar(x, plot_data[0], width=0.25, label="C3 amplitude increase") plt.bar(x + 0.25, plot_data[1], width=0.25, label="Cz amplitude increase") plt.bar(x + 0.50, plot_data[2], width=0.25, label="C4 amplitude increase") plt.xticks(x + 0.25, x_ticks) plt.ylabel("Average Band Amplitude") plt.legend(loc="best") plt.show() def amplitude_diff(self, freq_band, ref_c3_extractor, ref_cz_extractor, ref_c4_extractor, data_c3_extractor, data_cz_extractor, data_c4_extractor): ref_c3_amplitude = ref_c3_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) ref_cz_amplitude = ref_cz_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) ref_c4_amplitude = ref_c4_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) data_c3_amplitude = data_c3_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) data_cz_amplitude = data_cz_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) data_c4_amplitude = data_c4_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) c3_diff = data_c3_amplitude - ref_c3_amplitude cz_diff = data_cz_amplitude - ref_cz_amplitude c4_diff = data_c4_amplitude - ref_c4_amplitude return c3_diff, cz_diff, c4_diff # return data_c3_amplitude, data_cz_amplitude, data_c4_amplitude def stop_recording(self, reference: bool = False): if self.reading_timer is not None: self.reading_timer.deleteLater() self.board.stop_stream() self.recording = False self.recording_reference = False self.recording_progress_dialog.setValue(self.recording_progress_dialog.maximum()) recording_duration_in_samples = self.recording_progress_dialog.maximum() selected_freq = self.frequency_slider.value() if reference: sample_count = min(self.reference_eeg_data.get_channel_data(0).shape[0], recording_duration_in_samples) sample_count -= global_config.SAMPLING_RATE * self.DEFAULT_SAMPLE_PADDING self.index_generator.add_slice(0, self.eeg_sample_count - sample_count, self.eeg_sample_count) else: sample_count = min(self.eeg_data_buffer.get_channel_data(0).shape[0], recording_duration_in_samples) sample_count -= global_config.SAMPLING_RATE * self.DEFAULT_SAMPLE_PADDING self.index_generator.add_slice(selected_freq, self.eeg_sample_count - sample_count, self.eeg_sample_count) if self.root_directory_label.text() != "": self.index_generator.write_to_file(self.root_directory_label.text()) QApplication.beep() start = self.DEFAULT_SAMPLE_PADDING * global_config.SAMPLING_RATE if reference: print(f"reference size: {self.reference_eeg_data.sample_count()}") self.record_btn.setEnabled(True) # self.record_reference_btn.setEnabled(False) self.reference_eeg_data.filter_all_channels( global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX, True ) self.reference_eeg_data = utils.EegData(self.reference_eeg_data.to_row_array()[:, start:]) print("Reference data saved...") else: print("Stopping the recording...") print("Filtering data...") self.eeg_data_buffer.filter_all_channels( global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX, subtract_average=True ) self.eeg_data_buffer = utils.EegData(self.eeg_data_buffer.to_row_array()[:, start:]) print(f"data size: {self.eeg_data_buffer.sample_count()}") reference_c3_extractor = self.reference_eeg_data.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_cz_extractor = self.reference_eeg_data.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_c4_extractor = self.reference_eeg_data.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c3_extractor = self.eeg_data_buffer.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_cz_extractor = self.eeg_data_buffer.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c4_extractor = self.eeg_data_buffer.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) for i in range(self.c3_amplitude_bar_set.count()): current_freq = int(self.frequencies[i].replace(" Hz", "")) if current_freq == selected_freq: freq_band = utils.FrequencyBand( current_freq - self.DEFAULT_FREQUENCY_PADDING, current_freq + self.DEFAULT_FREQUENCY_PADDING) c3_diff, cz_diff, c4_diff = self.amplitude_diff(freq_band, reference_c3_extractor,reference_cz_extractor, reference_c4_extractor, data_c3_extractor, data_cz_extractor, data_c4_extractor) print(f"C3 diff = {c3_diff}") print(f"Cz diff = {cz_diff}") print(f"C4 diff = {c4_diff}") self.c3_amplitude_bar_set.replace(i, c3_diff) self.cz_amplitude_bar_set.replace(i, cz_diff) self.c4_amplitude_bar_set.replace(i, c4_diff) utils.auto_adjust_axis(self.amplitude_axis, [self.c3_amplitude_bar_set, self.cz_amplitude_bar_set, self.c4_amplitude_bar_set], self.DEFAULT_GRAPH_PADDING) def auto_adjust_axis(self): # Adjust the range so that everything is visible and add some gaps c3_min = sys.maxsize cz_min = sys.maxsize c4_min = sys.maxsize c3_max = -sys.maxsize cz_max = -sys.maxsize c4_max = -sys.maxsize for i in range(self.c3_amplitude_bar_set.count()): c3_min = min(c3_min, self.c3_amplitude_bar_set.at(i)) cz_min = min(cz_min, self.cz_amplitude_bar_set.at(i)) c4_min = min(c4_min, self.c4_amplitude_bar_set.at(i)) c3_max = max(c3_max, self.c3_amplitude_bar_set.at(i)) cz_max = max(cz_max, self.cz_amplitude_bar_set.at(i)) c4_max = max(c4_max, self.c4_amplitude_bar_set.at(i)) print("c3 min = {}, cz min = {}, c4 min = {}".format(c3_min, cz_min, c4_min)) print("c3 max = {}, cz max = {}, c4 max = {}".format(c3_max, cz_max, c4_max)) axis_min = min(0, c3_min, cz_min, c4_min) - self.DEFAULT_GRAPH_PADDING axis_max = max(0, c3_max, cz_max, c4_max) + self.DEFAULT_GRAPH_PADDING print("axis min = {}, axis max = {}".format(axis_min, axis_max)) self.amplitude_axis.setMin(axis_min) self.amplitude_axis.setMax(axis_max)
class Plot(QChartView): def __init__(self, parent=None): super(Plot, self).__init__(parent) self.setFixedSize(PLOT_WIDTH, PLOT_HEIGHT) self.xMax = None self.yMin = None self.yMax = None self.xAutoRescale = True self.yMinAutoRescale = True self.yMaxAutoRescale = True self.seriesList = [] self.chart = QChart() self.xAxis = QValueAxis() self.chart.addAxis(self.xAxis, Qt.AlignBottom) self.yAxis = QValueAxis() self.chart.addAxis(self.yAxis, Qt.AlignLeft) self.setSubplots(1) self.chart.legend().setVisible(False) self.chart.legend().setAlignment(Qt.AlignBottom) self.setChart(self.chart) # self.setRenderHint(QPainter.Antialiasing) def grabScreenshot(self, title): pixmap = QPixmap(self.grab()) pixmap.save(title + '.png', 'PNG') @pyqtSlot(int) def setSubplots(self, count: int): for _ in range(count): series = QLineSeries() self.chart.addSeries(series) series.attachAxis(self.xAxis) series.attachAxis(self.yAxis) self.seriesList.append(series) @pyqtSlot(str) def setTitle(self, title: str): self.chart.setTitle(title) @pyqtSlot(str) def setXaxisTitle(self, title: str): self.xAxis.setTitleText(title) @pyqtSlot(str) def setYaxisTitle(self, title: str): self.yAxis.setTitleText(title) @pyqtSlot(float) def setXmax(self, xMax: float): self.xAxis.setMax(xMax) self.xAutoRescale = False @pyqtSlot(float) def setYmin(self, yMin: float): self.yAxis.setMin(yMin) self.yMinAutoRescale = False @pyqtSlot(float) def setYmax(self, yMax: float): self.yAxis.setMax(yMax) self.yMaxAutoRescale = False @pyqtSlot(float, float, int) def plot(self, x: float, y: float, subplotIndex: int): if subplotIndex > len(self.seriesList) - 1: raise IndexError(f'Invalid subplot index ({subplotIndex}, max: {len(self.seriesList)})') if self.xAutoRescale: self.xAxis.setMax(x) if self.yMinAutoRescale: if not self.yMin or y < self.yMin: self.yMin = y self.yAxis.setMin(y - 0.001) if self.yMaxAutoRescale: if not self.yMax or y > self.yMax: self.yMax = y self.yAxis.setMax(y + 0.001) self.seriesList[subplotIndex] << QPointF(float(x), float(y))
class TotalEquityChartView(QChartView): """ Chart that displays the balance between several dates from an account, token or whole portfolio """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.chart = QChart() def setupChartWithData(self, data, linecolor='#422F8A'): """ Chart gets updated displaying the new data. Data has to be expressed on a dictionary form: - keys are timestamps - values are total balance for that timestamp """ self.chart = QChart() self.chart.setTheme(QChart.ChartThemeDark) self.chart.setAnimationOptions(QChart.SeriesAnimations) self.chart.setBackgroundBrush(QBrush(QColor("transparent"))) # self.chart.setTitle("") # self.chart.setTitleBrush(QBrush(QColor('white'))) # Axis X (Dates) self.x_axis = QDateTimeAxis() self.x_axis.setTickCount(11) self.x_axis.setLabelsAngle(70) font = QFont() font.setFamily('Roboto') font.setLetterSpacing(QFont.PercentageSpacing, 110) font.setPointSize(8) self.x_axis.setLabelsFont(font) self.x_axis.setFormat("dd-MM-yy") self.x_axis.setTitleText(self.tr('Date')) self.x_axis.setTitleVisible(False) self.x_axis.setLineVisible(False) self.x_axis.setGridLineVisible(False) # Axis Y (Balances) self.y_axis = QValueAxis() if data != {}: self.y_axis.setMax(max(data.values()) * 1.05) self.y_axis.setMin(min(data.values()) * 0.95) # self.y_axis.setMinorGridLineVisible(False) self.y_axis.setLineVisible(False) self.y_axis.setGridLineColor(QColor("#ECE9F1")) self.chart.addAxis(self.y_axis, Qt.AlignLeft) self.chart.addAxis(self.x_axis, Qt.AlignBottom) # Series self.btcseries = QSplineSeries() for date in data: balance = data[date] date = QDateTime(datetime.fromtimestamp(int(float(date)))) self.btcseries.append(date.toMSecsSinceEpoch(), balance) self.btcseries.setName("BTC") pen = QPen(QColor(linecolor)) pen.setWidth(3) self.btcseries.setPen(pen) # Series functionality self.btcseries.hovered.connect(self.selectPoint) self.chart.addSeries(self.btcseries) self.btcseries.attachAxis(self.x_axis) self.btcseries.attachAxis(self.y_axis) self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) self.setStyleSheet("border: 0px; background-color: rgba(0,0,0,0); ") self.chart.legend().hide() def selectPoint(self, point, state): """ Shows point where mouse is hovered """ self.chart.setTitle( f"{int(point.y())} {confighandler.get_fiat_currency().upper()}")
class XChartProbit(QChart): def __init__(self, parent=None): super(QChart, self).__init__(parent) # Class Vars self.activeDistr = 'lognorm' self.knowndistr = distr._distrnames() self.data = dict() # Axis Setup self.axisX = QValueAxis() self.axisY = QValueAxis() self.axisX.setLabelsVisible(False) self.axisX.setTickCount(2) self.axisX.setTitleText("Series Fractional Probability") self.axisY.setTitleText("Value") self.setAxesMinMax(-3, 3, 0.01, 1.5) self.axisX.setMinorGridLineVisible(False) self.axisX.setGridLineVisible(False) # define the default grid colour to grey self.setGridColor(110, 110, 110) self.setActiveProbit(self.activeDistr) self.plotAreaChanged.connect(self.onPlotSizeChanged) # method needed for axes change to redraw grid lines def addLinearReg(self, seriesname): x = self.data[seriesname]['X'], y = self.data[seriesname][seriesname] # adds a linear regression line for a data set x,y slope, intercept, r_value, p_value, std_err = linregress(x, y) xmin = distr.distrppf(self.activeDistr, 0.01) xmax = distr.distrppf(self.activeDistr, 0.99) ymin = slope * xmin + intercept ymax = slope * xmax + intercept data = dict() data['X'] = [xmin, xmax] data['LinearReg'] = [ymin, ymax] lines = XLineSeries(data, xkey='X', openGL=True) self.addSeries(lines[0]) self.setAxes(lines[0]) def loadSeries(self, arr, name): # takes a list/array arr y = array(arr).copy() y.sort() self.data[name] = y self.redrawChart() def plotSeries(self, name): nsamp = len(self.data[name]) # add data to temport dictionary tdict = dict() if self.activeScale == 'log10': tdict[name] = log10(self.data[name]) elif self.activeScale == 'linear': tdict[name] = self.data[name] tdict['X'] = distr.distrppf(self.activeProbit, [ percentileofscore(self.data[name], self.data[name][i]) / 100.00001 for i in range(0, nsamp) ]) series = XScatterSeries(tdict, xkey='X', openGL=True) self.addSeries(series[0]) self.setAxes(series[0]) def _replotData(self): for key in self.data.keys(): self.pl def axesMinMax(self): # returns a length 4 list of the axes min and max values [x1,x2,y1,y2] return [ self.axisX.min(), self.axisX.max(), self.axisY.min(), self.axisY.max() ] def redrawChart(self): self.removeAllSeries() self._removeHorizontalGridLabels() self.resetAxes() self._drawVerticalGridLines() if self.activeScale == 'log10': self.axisY.setLabelsVisible(False) self.axisY.setTickCount(1) self.setTitle("Log Probit Plot") self.axisY.setMinorGridLineVisible(False) self.axisY.setGridLineVisible(False) self._drawHorizontalGridLine() self._drawHorizontalLabels() self._drawHorizontalGridlLabels() elif self.activeScale == 'linear': self.axisY.setLabelsVisible(True) self.axisY.setTickCount(10) self.setTitle("Probit Plot") self.axisY.setMinorGridLineVisible(True) self.axisY.setGridLineVisible(True) for serkey in self.data.keys(): self.plotSeries(serkey) def resetAxes(self): ymins = [] ymaxs = [] for key in self.data.keys(): ymins.append(min(self.data[key])) ymaxs.append(max(self.data[key])) try: ymin = min(ymins) ymax = max(ymaxs) except ValueError: ymin = 1.1 ymax = 2 xmin = distr.distrppf(self.activeProbit, 0.001) xmax = distr.distrppf(self.activeProbit, 0.999) if self.activeScale == 'linear': yscal = 0.1 * (ymax - ymin) self.setAxesMinMax(xmin, xmax, ymin - yscal, ymax + yscal) elif self.activeScale == 'log10': yscal = 0.1 * (log10(ymax) - log10(ymin)) self.setAxesMinMax(xmin, xmax, log10(ymin), log10(ymax)) #self.setAxesMinMax(xmin,xmax,log10(ymin)-yscal,log10(ymax)+yscal*0.1) def setGridColor(self, r, g, b): # sets the colour of the background grid self.gridcolor = QColor(r, g, b) def setActiveProbit(self, type): if type in self.knowndistr: self.activeDistr = type if type == 'norm': self.activeProbit = 'norm' self.activeScale = 'linear' elif type == 'lognorm': self.activeProbit = 'norm' self.activeScale = 'log10' #self.redrawChart() def setActiveScale(self, newscale): self.activeScale = newscale def setAxes(self, series): # assigns a series to the chart default axes self.setAxisX(self.axisX, series) self.setAxisY(self.axisY, series) def setAxesMinMax(self, x1, x2, y1, y2): # sets the min max values in X and Y self.axisX.setMin(x1) self.axisX.setMax(x2) self.axisY.setMin(y1) self.axisY.setMax(y2) def _drawHorizontalLabels(self): xmin = self.axisX.min() xmax = self.axisX.max() axisScale = 1 / (xmax - xmin ) # scaler for plotted axis (reduces to 0-1.0) # calculate probit values to scale from grid lines insert min and max values to scale correctly vlabx = distr.distrppf(self.activeProbit, self.vgridx) vlabx = insert(vlabx, 0, xmin) vlabx = insert(vlabx, len(vlabx), xmax) vlabx = ( vlabx - xmin ) * axisScale #scale the probit value to ratios of the Xaxis length paw = self.plotArea().width() pah = self.plotArea().height() #find the plot width and height # find plot bottom left corner X and Y pblx = self.plotArea().bottomLeft().x() pbly = self.plotArea().bottomLeft().y() # offset from axix by 10 pixels -> may need to automate this offset in future pbly_lab = pbly + 10 # calculate the position on the chart in x plane with which to place each label. pblx = [pblx + int(paw * x) for x in vlabx[1:-1]] try: self.hlabels except AttributeError: self.hlabels = [] for i, labx in enumerate( pblx): #run through labels and create and position them # label text based on P scale ltext = 'P' + '%02d' % round(100.0 * (1.0 - self.vgridx[i])) self.hlabels.append(self.scene().addText(ltext)) for i, labx in enumerate( pblx): #run through labels and create and position them self.hlabels[i].setPos( labx - 0.5 * self.hlabels[i].boundingRect().width(), pbly) #centre on tick marks def _drawVerticalGridLines(self): self.vgridx = arange(0.05, 1.0, 0.05) self.vgridx = insert(self.vgridx, 0, [0.01, 0.02]) self.vgridx = insert(self.vgridx, len(self.vgridx), [0.98, 0.99]) vgridy = [self.axisY.min(), self.axisY.max()] self.vgridseries = [] for val in self.vgridx: line = 'P' + '%02d' % round(100.0 * (1.0 - val)) tdict = { 'X': [distr.distrppf(self.activeProbit, val)] * 2, line: vgridy } self.vgridseries = self.vgridseries + XLineSeries( tdict, xkey='X', openGL=True) for i, line in enumerate(self.vgridseries): pen = line.pen() pen.setColor(self.gridcolor) pen.setWidthF(0.4), line.setPen(pen) line.setPointLabelsVisible(True) self.addSeries(line) self.setAxes(line) self.legend().markers(line)[0].setVisible(False) def _drawHorizontalGridLine(self): # calculate xmin and xmax points for lines to completely cross graph hgridx = [ distr.distrppf(self.activeProbit, 0.0001) - 1, distr.distrppf(self.activeProbit, 0.9999) + 1 ] # calculate log scale for lines y values self.hgridy = self._logrange(10**self.axisY.min(), 10**self.axisY.max(), base=10) self.hgridseries = [] # create a line series for each lines and add to list for val in self.hgridy: line = '%d' % val tdict = {'X': hgridx, line: [log10(val)] * 2} self.hgridseries = self.hgridseries + XLineSeries( tdict, xkey='X', openGL=True) # add each of the series to the grid with special formatting for i, line in enumerate(self.hgridseries): pen = line.pen() pen.setColor(self.gridcolor) pen.setWidthF(0.4), line.setPen(pen) self.addSeries(line) self.setAxes(line) self.legend().markers(line)[0].setVisible(False) def _drawHorizontalGridlLabels(self): ymin = self.axisY.min() ymax = self.axisY.max() axisScale = 1 / (ymax - ymin ) # scaler for plotted axis (reduces to 0-1.0) # calculate base10 values to scale from grid lines insert min and max values to scale correctly vlaby = log10(self.hgridy) vlaby = insert(vlaby, 0, ymin) vlaby = insert(vlaby, len(vlaby), ymax) vlaby = ( vlaby - ymin ) * axisScale # scale the probit value to ratios of the Xaxis length paw = self.plotArea().width() pah = self.plotArea().height() # find the plot width and height # find plot bottom left corner X and Y pblx = self.plotArea().bottomLeft().x() pbly = self.plotArea().bottomLeft().y() # offset from axix by 10 pixels -> may need to automate this offset in future pblx_lab = pblx - 10 # calculate the position on the chart in y plane with which to place each label. pbly = [pbly - int(pah * y) for y in vlaby[1:-1]] self.vlabels = [] for i, labx in enumerate( pbly): # run through labels and create and position them # label text based on P scale ltext = str(self.hgridy[i]) self.vlabels.append(self.scene().addText(ltext)) for i, laby in enumerate( pbly): #run through labels and create and position them # label text based on P scale self.vlabels[i].setPos( pblx - self.vlabels[i].boundingRect().width() - 10, laby - 0.5 * self.vlabels[i].boundingRect().height()) #centre on tick marks def _removeHorizontalGridLine(self): for ser in self.hgridseries: self.removeSeries(ser) def _removeVerticalGridLine(self): for ser in self.vgridseries: self.removeSeries(ser) def _removeHorizontalGridLabels(self): try: for lab in self.vlabels: self.scene().removeItem(lab) except AttributeError: pass def _logrange(self, min, max, base=10): if min <= 0: min += max / (base**10) y = 1 bpow = base if min < base: while min < bpow: y -= 1 bpow = pow(base, y) else: while min > bpow: y += 1 bpow = pow(base, y) out = array([]) while bpow < max: y += 1 bpown = pow(base, y) out = append(out, arange(bpow, bpown, bpow)) bpow = bpown i = 0 j = 0 for ind, val in enumerate(out): if val <= min: i = ind if val <= max: j = ind return out[i:j + 1] @pyqtSlot() def onPlotSizeChanged(self): #reset position of labels self.redrawChart()
class Example(QMainWindow): def __init__(self, ): super().__init__() self.bijiao = [0, 0, 0] self.res = "" self.queue = [] self.k = -1 self.flag = -1 try: self.initUI() self.chart_init(10) except: traceback.print_exc() def initUI(self): icon = QtGui.QIcon() icon.addPixmap( QtGui.QPixmap("D:\\Sysytem\\Desktop\\zhong\\data\\ico.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.setWindowIcon(icon) self.resize(1000, 700) self.setWindowTitle('Icon') self.setObjectName("清华大学得不到的学生") self.setWindowTitle("一枚小乖乖~") self.label = QLabel(self) self.label.setWindowTitle("清华大学得不到的学生") self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.label.setGeometry(QtCore.QRect(15, 15, 970, self.height() - 30)) self.label.setText("") palette = QtGui.QPalette() self.label.setStyleSheet("background-color: #fff;border-radius: 15px;") self.labelshadow = QGraphicsDropShadowEffect(self) self.labelshadow.setBlurRadius(15) self.labelshadow.setOffset(1, 1) self.label.setGraphicsEffect(self.labelshadow) self.label.setScaledContents(True) self.label.setObjectName("label") self.pushButton_12 = QtWidgets.QPushButton(self) self.pushButton_12.setGeometry( QtCore.QRect(self.width() - 55, 29, 20, 20)) self.pushButton_12.setStyleSheet( "QPushButton{\n" " background:#fc625d;\n" " color:white;\n" " box-shadow: 1px 1px 3px rgba(0,0,0,0.3);font-size:20px;border-radius: 10px;font-family: 微软雅黑;\n" "}\n" "QPushButton:hover{ \n" " background:#FF2D2D;\n" "}\n" "QPushButton:pressed{\n" " border: 1px solid #3C3C3C!important;\n" " background:#AE0000;\n" "}") self.pushButton_12.clicked.connect(self.close) self.pushButton_14 = QtWidgets.QPushButton(self) self.pushButton_14.setGeometry( QtCore.QRect(self.width() - 55 - 35, 29, 20, 20)) self.pushButton_14.setStyleSheet( "QPushButton{\n" " background:#35cd4b;\n" " color:white;\n" " box-shadow: 1px 1px 3px rgba(0,0,0,0.3);font-size:20px;border-radius: 10px;font-family: 微软雅黑;\n" "}\n" "QPushButton:hover{ \n" " background:#00CC00;\n" "}\n" "QPushButton:pressed{\n" " border: 1px solid #3C3C3C!important;\n" " background:#009900;\n" "}") self.pushButton_14.clicked.connect(self.showMinimized) self.color = ["#e89291", "#c4b98b", "#81a8e1", "#8cc9c4", "#83bde2"] # -----------------------------------------------------------------测试数量------------------------ error = QtWidgets.QLineEdit(self) error.setGeometry(QtCore.QRect(70, 70, 150, 50)) error.setStyleSheet( "text-align: center;background-color: " + self.color[0] + ";border-radius: 7px;border: 0px solid #000;color:#ffffff;font-size:20px;font-family: 微软雅黑;" ) errorshadow = QGraphicsDropShadowEffect(self) error.setPlaceholderText("测试数量") errorshadow.setBlurRadius(30) cl = QColor("#cacaca") errorshadow.setColor(cl) error.setAlignment(Qt.AlignCenter) errorshadow.setOffset(0, 0) error.textChanged.connect(self.set_suliang) # error.setGraphicsEffect(errorshadow) # -----------------------------------------------------------------随机范围------------------------ fan = QtWidgets.QLineEdit(self) fan.setGeometry(QtCore.QRect(240, 70, 150, 50)) fan.setStyleSheet( "text-align: center;background-color: " + self.color[2] + ";border-radius: 7px;border: 0px solid #000;color:#ffffff;font-size:20px;font-family: 微软雅黑;" ) fanshadow = QGraphicsDropShadowEffect(self) fanshadow.setBlurRadius(30) fancl = QColor("#cacaca") fan.setPlaceholderText("随机范围") fanshadow.setColor(fancl) fan.setAlignment(Qt.AlignCenter) fanshadow.setOffset(0, 0) fan.textChanged.connect(self.set_fanwei) # fan.setGraphicsEffect(fanshadow) # -----------------------------------------------------------------内存块数----------------------- kuai = QtWidgets.QLineEdit(self) kuai.setGeometry(QtCore.QRect(410, 70, 150, 50)) kuai.setStyleSheet( "text-align: center;background-color: " + self.color[3] + ";border-radius: 7px;border: 0px solid #000;color:#ffffff;font-size:20px;font-family: 微软雅黑;" ) kuaishadow = QGraphicsDropShadowEffect(self) kuaishadow.setBlurRadius(30) kuaicl = QColor("#cacaca") kuai.setPlaceholderText("内存块数") kuaishadow.setColor(kuaicl) kuai.setAlignment(Qt.AlignCenter) kuaishadow.setOffset(0, 0) kuai.textChanged.connect(self.set_kuai) # kuai.setGraphicsEffect(kuaishadow) self.Button = QtWidgets.QPushButton(self) self.Button.setGeometry(QtCore.QRect(580, 70, 150, 50)) self.Button.setStyleSheet( "QPushButton{text-align: center;background-color: #83bde2;" "border-radius: 7px;border: 0px solid #000;color:#ffffff;" "font-size:20px;font-family: 微软雅黑;}" + "QPushButton:hover{ " + " background-color: #9ad0d0;color: white;" + "}") Buttonshadow = QGraphicsDropShadowEffect(self) Buttonshadow.setBlurRadius(30) Buttoncl = QColor("#cacaca") self.Button.setText("执行") Buttonshadow.setColor(Buttoncl) Buttonshadow.setOffset(0, 0) # Button.setGraphicsEffect(Buttonshadow) self.Button.clicked.connect(self.on_click_start) self.avgflag = 0 self.qq = QtWidgets.QPushButton(self) self.qq.setGeometry(QtCore.QRect(750, 70, 180, 50)) self.qq.setStyleSheet( "color: #000;text-align: center;background-color: #f0f0f0;border-radius: 7px;border: 0px solid #000;font-size:14px;font-family: 微软雅黑;" ) self.qq.clicked.connect(self.on_avg) self.show() def on_avg(self): if self.avgflag == 0: self.series.hide() self.serieslru.hide() self.seriesopt.hide() self.seriesfifoavg.show() self.serieslruavg.show() self.seriesoptavg.show() self.avgflag = 1 else: self.series.show() self.serieslru.show() self.seriesopt.show() self.seriesfifoavg.hide() self.serieslruavg.hide() self.seriesoptavg.hide() self.avgflag = 0 def set_kuai(self, text): self.kuai = text def set_fanwei(self, text): self.fan = text def set_suliang(self, text): self.su = text def hideerror(self): self.error.hide() def set_kuaishu(self, text): self.kuaishu = text def set_yemian(self, text): self.yemian = text def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate def mousePressEvent(self, e): if e.button() == Qt.LeftButton: self.m_drag = True self.m_DragPosition = e.globalPos() - self.pos() e.accept() def mouseReleaseEvent(self, e): if e.button() == Qt.LeftButton: self.m_drag = False def mouseMoveEvent(self, e): try: if Qt.LeftButton and self.m_drag: self.move(e.globalPos() - self.m_DragPosition) e.accept() except: print("错误代码:000x0") def on_click_start(self): try: self.jishu = 0 self.thread = MyThread() self.thread.set_su(self.su) self.thread.set_kuai(self.kuai) self.thread.set_x(self.dtaxisX) self.thread.set_fan(self.fan) self.thread.det_bijiao(self.bijiao) self.dailist = [] self.avg = [] self.avg1 = [] self.avg2 = [] self.dailistlru = [] self.dailistopt = [] self.thread.sinOut.connect(self.change) self.thread.start() # self.qq.setText("FIFO:%0.2f LRU:%0.2f \n OPT:%0.2f" % (self.bijiao[0], self.bijiao[1], self.bijiao[2])) except: traceback.print_exc() # self.sinOut.emit(self.dailist) # self.sinOut2.emit(self.dailistlru) # self.sinOut3.emit(self.dailistopt) def change(self, dailist): try: # print(dailist) # arr = str.split(dailist) # print(arr) flag = 0 start = 0 flag = dailist.find(" ", flag) flag = dailist.find(" ", flag + 1) end = flag arr = str.split(dailist[start:end]) start = flag flag = dailist.find(" ", flag + 1) flag = dailist.find(" ", flag + 1) end = flag arr1 = str.split(dailist[start:end]) start = flag flag = dailist.find(" ", flag + 1) flag = dailist.find(" ", flag + 1) end = flag arr2 = str.split(dailist[start:end]) self.dailist.append(QPointF(float(arr[0]), float(arr[1]))) self.dailistlru.append(QPointF(float(arr1[0]), float(arr1[1]))) self.dailistopt.append(QPointF(float(arr2[0]), float(arr2[1]))) # self.series.replace(self.dailist) self.serieslru.replace(self.dailistlru) self.seriesopt.replace(self.dailistopt) if self.jishu == 0: self.bijiao[0] = float(arr[1]) self.bijiao[1] = float(arr1[1]) self.bijiao[2] = float(arr2[1]) self.jishu = 1 else: self.bijiao[0] = (float(arr[1]) + self.bijiao[0]) / 2.0 self.bijiao[1] = (float(arr1[1]) + self.bijiao[1]) / 2.0 self.bijiao[2] = (float(arr2[1]) + self.bijiao[2]) / 2.0 self.jishu = self.jishu + 1 self.avg.append(QPointF(float(arr[0]), self.bijiao[0])) self.avg1.append(QPointF(float(arr[0]), self.bijiao[1])) self.avg2.append(QPointF(float(arr[0]), self.bijiao[2])) # print(self.avg) self.seriesfifoavg.replace(self.avg) self.serieslruavg.replace(self.avg1) self.seriesoptavg.replace(self.avg2) self.qq.setText("FIFO:%0.2f LRU:%0.2f \n OPT:%0.2f" % (self.bijiao[0], self.bijiao[1], self.bijiao[2])) except: traceback.print_exc() def drawLine(self): self.series.replace(self.dailist) # print("uiy") def chart_init(self, su): self.start_num = 0 self.chart = QChartView(self) self.chart.setGeometry(50, 150, self.width() - 100, self.height() - 150 - 50) # 设置charView位置、大小 self.series = QSplineSeries() self.series.setName("FIFO") self.chart.chart().addSeries(self.series) pen = QPen(Qt.gray) pen.setWidth(2) self.serieslru = QSplineSeries() self.serieslru.setPen(pen) self.serieslru.setName("LRU") self.serieslru.setColor(QColor("#e89291")) self.chart.chart().addSeries(self.serieslru) pen2 = QPen(Qt.gray) pen2.setWidth(2) self.seriesopt = QSplineSeries() self.seriesopt.setPen(pen2) self.seriesopt.setColor(QColor("#3ea54f")) self.seriesopt.setName("OPT") self.chart.chart().addSeries(self.seriesopt) penfifo = QPen(Qt.gray) penfifo.setWidth(2) self.seriesfifoavg = QSplineSeries() self.seriesfifoavg.setPen(penfifo) self.seriesfifoavg.setName("FIFO-avg") self.seriesfifoavg.setColor(QColor("#209fdf")) self.chart.chart().addSeries(self.seriesfifoavg) self.seriesfifoavg.hide() penavg = QPen(Qt.gray) penavg.setWidth(2) self.serieslruavg = QSplineSeries() self.serieslruavg.setPen(penavg) self.serieslruavg.setName("LRU-avg") self.serieslruavg.setColor(QColor("#e89291")) self.chart.chart().addSeries(self.serieslruavg) self.serieslruavg.hide() pen2avg = QPen(Qt.gray) pen2avg.setWidth(2) self.seriesoptavg = QSplineSeries() self.seriesoptavg.setPen(pen2avg) self.seriesoptavg.setColor(QColor("#3ea54f")) self.seriesoptavg.setName("OPT-avg") self.chart.chart().addSeries(self.seriesoptavg) self.seriesoptavg.hide() self.dtaxisX = QValueAxis() self.vlaxisY = QValueAxis() self.dtaxisX.setMin(10) self.dtaxisX.setMax(100) self.vlaxisY.setMin(0) self.vlaxisY.setMax(100) self.dtaxisX.setTickCount(6) self.vlaxisY.setTickCount(11) self.dtaxisX.setTitleText("页数") self.vlaxisY.setTitleText("缺页率") self.vlaxisY.setGridLineVisible(False) self.chart.chart().addAxis(self.dtaxisX, Qt.AlignBottom) self.chart.chart().addAxis(self.vlaxisY, Qt.AlignLeft) self.series.attachAxis(self.dtaxisX) self.series.attachAxis(self.vlaxisY) self.serieslru.attachAxis(self.dtaxisX) self.serieslru.attachAxis(self.vlaxisY) self.seriesopt.attachAxis(self.dtaxisX) self.seriesopt.attachAxis(self.vlaxisY) self.seriesoptavg.attachAxis(self.dtaxisX) self.seriesoptavg.attachAxis(self.vlaxisY) self.serieslruavg.attachAxis(self.dtaxisX) self.serieslruavg.attachAxis(self.vlaxisY) self.seriesfifoavg.attachAxis(self.dtaxisX) self.seriesfifoavg.attachAxis(self.vlaxisY) self.chart.chart().setTitleBrush(QBrush(Qt.cyan)) cc = QColor("#f0f0f0") self.chart.setBackgroundBrush(cc) self.chart.setStyleSheet( "QChartView{ background-color: #83bde2;border-radius: 20px;}") self.chart.show()