def _init_chart(self) -> None: series = QBarSeries() axis_x = QBarCategoryAxis() axis_y = QValueAxis() data_set = defaultdict(int) for title, money in self.base: data_set[title] += money for title, money in data_set.items(): bar = QBarSet(title) axis_x.append(title) bar.append(money) series.append(bar) # self.chart = QChart() self.chart.addSeries(series) self.chart.setAnimationOptions(QChart.SeriesAnimations) # series.attachAxis(axis_x) self.chart.addAxis(axis_x, Qt.AlignBottom) axis_y.setRange(0, max(data_set.values())) axis_y.setTickCount(11) self.chart.addAxis(axis_y, Qt.AlignLeft) series.attachAxis(axis_y) # self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom)
def draw(self, chart_data: PieChartData) -> None: # series = QPieSeries() # for project, duration in chart_data.data.items(): # series.append("{} ({} s)".format(project, int(duration)), duration) # self.chart_view.setRenderHint(QPainter.Antialiasing) # self.chart_view.chart().removeAllSeries() # self.chart_view.chart().addSeries(series) # TODO redo with stacked bar chart series = QBarSeries() bar_set = QBarSet('Default') categories = [] for project, duration in chart_data.data.items(): if project == self.config.projects.none_project: project = 'None' categories.append(project) bar_set.append(duration) series.append(bar_set) axis_x = QBarCategoryAxis() axis_x.append(categories) self.chart.removeAllSeries() self.chart.addSeries(series) self.chart.setAxisX(axis_x) series.attachAxis(axis_x) self.initialized = True
def __actualizar_grafico_barras(self): self.grafico.legend().setVisible(True) medias = [ math.floor(np.mean(datos)) if len(datos) > 0 else 0 for datos in self.datos.values() ] mejores = [ np.min(datos) if len(datos) > 0 else 0 for datos in self.datos.values() ] peores = [ np.max(datos) if len(datos) > 0 else 0 for datos in self.datos.values() ] self.eje_y.setRange(1, max(max(medias), max(peores))) barset_media = QBarSet('Media') barset_media.append(medias) barset_mejor = QBarSet('Mejor') barset_mejor.append(mejores) barset_peor = QBarSet('Peor') barset_peor.append(peores) series = QBarSeries() series.append(barset_media) series.append(barset_mejor) series.append(barset_peor) series.setLabelsVisible(True) self.grafico.addSeries(series) series.attachAxis(self.eje_y) self.init_grafico()
def show_diagram(self): self.student_diagram_ui.label_4.setText(self.label_2.text()) self.student_diagram_ui.label_7.setText(self.label_8.text()) self.student_diagram_ui.label_8.setText(self.label_6.text()) self.student_diagram_ui.label_10.setText(self.label_4.text()) max_value = 0 series = QBarSeries() for i in self.result: set0 = QBarSet(i[0]) set0.append(float(i[1])) series.append(set0) if max_value < (float(i[1])): max_value = float(i[1]) axisY = QValueAxis() axisY.setRange(0, max_value) chart = QChart() series.attachAxis(axisY) chart.addSeries(series) chart.setAnimationOptions(QChart.SeriesAnimations) chart.addAxis(axisY, Qt.AlignLeft) chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) centralwidget = self.student_diagram_ui.centralwidget self.student_diagram_ui.chartview = QChartView(chart, centralwidget) self.student_diagram_ui.chartview.setGeometry(QtCore.QRect(10, 110, 880, 371)) self.student_diagram_ui.pushButton_3.show() self.student_diagram_ui.update(self.dark_theme) self.student_diagram_window.show()
def ALL(self): self.ind = 0 set0 = QBarSet('Daya Tarik') set1 = QBarSet('Aksesbilitas') set2 = QBarSet('Kebersihan') set3 = QBarSet('Fasilitas') set0.setColor(QtGui.QColor("blue")) set1.setColor(QtGui.QColor("red")) set2.setColor(QtGui.QColor("green")) set3.setColor(QtGui.QColor("orange")) gf = Grafik_2() jml = gf.jumlah_per_pantai() jml_pantai = [] for i in range(len(jml)): jml_pantai.append(jml[i]) hasil = gf.hasil_dayatarik() for i in range(len(hasil)): set0.append((hasil[i] / jml_pantai[i]) * 100) hasil2 = gf.hasil_aksesbilitas() for i in range(len(hasil2)): set1.append((hasil2[i] / jml_pantai[i]) * 100) hasil3 = gf.hasil_kebersihan() for i in range(len(hasil3)): set2.append((hasil3[i] / jml_pantai[i]) * 100) hasil4 = gf.hasil_fasilitas() for i in range(len(hasil4)): set3.append((hasil4[i] / jml_pantai[i]) * 100) series = QBarSeries() series.append(set0) series.append(set1) series.append(set2) series.append(set3) series.setLabelsVisible(True) series.setLabelsPosition(QAbstractBarSeries.LabelsInsideEnd) series.setLabelsAngle(-90) self.chart = QChart() self.chart.addSeries(series) self.chart.setTitle('Grafik Prosentase Ulasan Pantai') self.chart.setAnimationOptions(QChart.SeriesAnimations) axisY = QValueAxis() axisY.setTitleText("Prosentase (%)") axisY.applyNiceNumbers() self.chart.addAxis(axisY, Qt.AlignLeft) series.attachAxis(axisY) self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom) self.chart.createDefaultAxes() self.graphicsView.setChart(self.chart) self.adjust_axes(0, 2) self.lims = np.array([0, 6]) self.onAxisSliderMoved(self.verticalScrollBar.value())
def __init__(self, parent=None): super().__init__(parent) self.m_model = CustomTableModel() tableView = QTableView() tableView.setModel(self.m_model) tableView.setMinimumWidth(300) tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) tableView.verticalHeader().setSectionResizeMode(QHeaderView.Stretch) self.m_model.setParent(tableView) chart = QChart() chart.setAnimationOptions(QChart.AllAnimations) series = QBarSeries() first = 3 count = 5 mapper = QVBarModelMapper(self) mapper.setFirstBarSetColumn(1) mapper.setLastBarSetColumn(4) mapper.setFirstRow(first) mapper.setRowCount(count) mapper.setSeries(series) mapper.setModel(self.m_model) chart.addSeries(series) seriesColorHex = "#000000" barsets = series.barSets() for i, barset in enumerate(barsets): seriesColorHex = barset.brush().color().name() self.m_model.addMapping( seriesColorHex, QRect(1 + i, first, 1, barset.count()) ) categories = ("April", "May", "June", "July", "August") axisX = QBarCategoryAxis() axisX.append(categories) chart.addAxis(axisX, Qt.AlignBottom) series.attachAxis(axisX) axisY = QValueAxis() chart.addAxis(axisY, Qt.AlignLeft) series.attachAxis(axisY) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) chartView.setMinimumSize(640, 480) mainLayout = QGridLayout(self) mainLayout.addWidget(tableView, 1, 0) mainLayout.addWidget(chartView, 1, 1) mainLayout.setColumnStretch(1, 1) mainLayout.setColumnStretch(0, 0)
def main(): import sys app = QApplication(sys.argv) set0 = QBarSet("Jane") set1 = QBarSet("John") set2 = QBarSet("Axel") set3 = QBarSet("Mary") set4 = QBarSet("Samantha") set0 << 1 << 2 << 3 << 4 << 5 << 6 set1 << 5 << 0 << 0 << 4 << 0 << 7 set2 << 3 << 5 << 8 << 13 << 8 << 5 set3 << 5 << 6 << 7 << 3 << 4 << 5 set4 << 9 << 7 << 5 << 3 << 1 << 2 series = QBarSeries() series.append(set0) series.append(set1) series.append(set2) series.append(set3) series.append(set4) chart = QChart() chart.addSeries(series) chart.setTitle("Simple barchart example") chart.setAnimationOptions(QChart.SeriesAnimations) categories = ("Jan", "Feb", "Mar", "Apr", "May", "Jun") axisX = QBarCategoryAxis() axisX.append(categories) chart.addAxis(axisX, Qt.AlignBottom) series.attachAxis(axisX) axisY = QValueAxis() axisY.setRange(0, 15) chart.addAxis(axisY, Qt.AlignLeft) series.attachAxis(axisY) chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) window = QMainWindow() window.setCentralWidget(chartView) window.resize(420, 300) window.show() sys.exit(app.exec_())
class Chart(QChartView): def __init__(self, barCount: int): super().__init__() self.chart = QChart() self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) self.chart.setAnimationOptions(QChart.SeriesAnimations) self.chart.setBackgroundVisible(True) self.chart.legend().setVisible(False) self.series = QBarSeries() self.chart.addSeries(self.series) self.barValues = QBarSet('') self.series.append(self.barValues) for i in range(barCount): self.barValues << 0. self.xAxis = QBarCategoryAxis() self.chart.addAxis(self.xAxis, Qt.AlignBottom) self.series.attachAxis(self.xAxis) self.xAxis.setTitleText('yPlus ranges') self.yAxis = QValueAxis() self.chart.addAxis(self.yAxis, Qt.AlignLeft) self.series.attachAxis(self.yAxis) self.yAxis.setTitleText('% of surface area') self.yAxis.setRange(0, 100) def setBarRanges(self, pois: List[float]): for i in range(len(pois)): if i == 0: tag = 'lt ' + str(pois[0]) elif i == len(pois) - 1: tag = 'gt ' + str(pois[-1]) else: tag = str(pois[i]) + ' - ' + str(pois[i + 1]) if not self.xAxis.count(): self.xAxis.append(tag) else: self.xAxis.replace(self.xAxis.at(i), tag) def setBarValues(self, values: List[float]): assert len(values) == self.barValues.count() for i in range(len(values)): if not self.barValues.count(): self.barValues.insert(i, 0.) else: self.barValues.replace(i, values[i])
def DT(self): self.ind = 1 gf = Grafik_2() jml = gf.jumlah_per_pantai() jml_pantai = [] for i in range(len(jml)): jml_pantai.append(jml[i]) value = [] hasil = gf.hasil_dayatarik() for i in range(len(hasil)): value.append((hasil[i] / jml_pantai[i]) * 100) sorting = sorted(value, reverse=True) index = np.argsort(value)[::-1] label_sorting = [] for i in index: label_sorting.append(self.label_pantai[i]) set0 = QBarSet('Daya Tarik') set0.setColor(QtGui.QColor("blue")) for i in range(len(hasil)): set0.append(sorting) series = QBarSeries() series.append(set0) series.setLabelsVisible(True) series.setLabelsPosition(QAbstractBarSeries.LabelsInsideEnd) self.chart = QChart() self.chart.addSeries(series) self.chart.setTitle('Grafik Prosentase Ulasan Pantai') self.chart.setAnimationOptions(QChart.SeriesAnimations) axisX = QBarCategoryAxis() axisX.setLabelsAngle(-90) axisY = QValueAxis() axisY.setTitleText("Prosentase (%)") self.chart.addAxis(axisX, Qt.AlignBottom) self.chart.addAxis(axisY, Qt.AlignLeft) series.attachAxis(axisY) axisY.applyNiceNumbers() self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom) self.chart.createDefaultAxes() self.graphicsView.setChart(self.chart) self.lims = np.array([0, 10]) self.onAxisSliderMoved(self.verticalScrollBar.value())
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 KB(self): self.ind = 3 gf = Grafik_2() jml = gf.jumlah_per_pantai() jml_pantai = [] for i in range(len(jml)): jml_pantai.append(jml[i]) value = [] hasil = gf.hasil_kebersihan() for i in range(len(hasil)): value.append((hasil[i] / jml_pantai[i]) * 100) sorting = sorted(value, reverse=True) set0 = QBarSet('Kebersihan') set0.setColor(QtGui.QColor("green")) for i in range(len(hasil)): set0.append(sorting) series = QBarSeries() series.append(set0) series.setLabelsVisible(True) series.setLabelsPosition(QAbstractBarSeries.LabelsInsideEnd) self.chart = QChart() self.chart.addSeries(series) self.chart.setTitle('Grafik Prosentase Ulasan Pantai') self.chart.setAnimationOptions(QChart.SeriesAnimations) axisY = QValueAxis() axisY.setTitleText("Prosentase (%)") axisY.applyNiceNumbers() self.chart.addAxis(axisY, Qt.AlignLeft) series.attachAxis(axisY) self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom) self.chart.createDefaultAxes() self.graphicsView.setChart(self.chart) self.adjust_axes(0, 2) self.lims = np.array([0, 10]) self.onAxisSliderMoved(self.verticalScrollBar.value())
class BandPowerGraph(QWidget): def __init__(self, name: str): super().__init__() self.band_power_chart = QChart() self.band_power_chart.setAnimationOptions(QChart.SeriesAnimations) self.channel_band_power_set = QBarSet("Band Power") self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.bands_axis = QBarCategoryAxis() self.bands_axis.append("Delta (1 - 3 Hz)") self.bands_axis.append("Theta (4 - 7 Hz)") self.bands_axis.append("Alpha (8 - 13 Hz)") self.bands_axis.append("Beta (13 - 30 Hz)") self.bands_axis.append("Gamma (30 - 100)") self.power_axis = QValueAxis() self.chart_series = QBarSeries() self.chart_series.append(self.channel_band_power_set) self.band_power_chart.addSeries(self.chart_series) self.band_power_chart.setTitle(f"<h1>Band Power For {name}</h1>") self.band_power_chart.addAxis(self.bands_axis, Qt.AlignBottom) self.band_power_chart.addAxis(self.power_axis, Qt.AlignLeft) self.chart_series.attachAxis(self.bands_axis) self.chart_series.attachAxis(self.power_axis) self.chart_view = QChartView(self.band_power_chart) self.chart_view.setRenderHint(QPainter.Antialiasing) self.root_layout = QStackedLayout() self.setLayout(self.root_layout) self.root_layout.addWidget(self.chart_view) def set_name(self, name: str): self.band_power_chart.setTitle(f"<h1>Band Power For {name}</h1>") def update_values(self, data: np.ndarray, fft_window_size: float = 0): eeg_data = utils.EegData(data) feature_extractor = eeg_data.feature_extractor(0, global_config.SAMPLING_RATE) self.channel_band_power_set.replace( 0, feature_extractor.average_band_amplitude(utils.FrequencyBand.delta_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 1, feature_extractor.average_band_amplitude(utils.FrequencyBand.theta_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 2, feature_extractor.average_band_amplitude(utils.FrequencyBand.alpha_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 3, feature_extractor.average_band_amplitude(utils.FrequencyBand.beta_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 4, feature_extractor.average_band_amplitude(utils.FrequencyBand.gama_freq_band(), fft_window_size) ) def auto_adjust_axis(self): utils.auto_adjust_axis(self.power_axis, [self.channel_band_power_set], 0.1)
def bars_stacked(name, table_df, x_bottom_cols, y_left_cols, y_right_cols, legend_labels, tick_count): """ 柱形堆叠图 :param name: 图表名称 :param table_df: 用于画图的pandas DataFrame对象 :param x_bottom_cols: 下轴列索引列表 :param y_left_cols: 左轴列索引列表 :param y_right_cols: 右轴列索引列表 :param legend_labels: 图例名称标签列表 :param tick_count: 横轴刻度标签数 :return: QChart实例 """ """ 过滤轴 """ for y_left_col in y_left_cols: if is_datetime64_any_dtype(table_df[y_left_col]): # 如果是时间轴 y_left_cols.remove(y_left_col) for y_right_col in y_right_cols: if is_datetime64_any_dtype(table_df[y_right_col]): # 如果是时间轴 y_right_cols.remove(y_right_col) # 将x轴转为字符串 x_bottom = x_bottom_cols[0] # x轴列 if is_datetime64_any_dtype(table_df[x_bottom]): # 如果x轴是时间轴 table_df[x_bottom] = table_df[x_bottom].apply( lambda x: x.strftime('%Y-%m-%d')) else: # x轴非时间轴 table_df[x_bottom] = table_df[x_bottom].apply(lambda x: str(x)) font = QFont() # 轴文字风格 font.setPointSize(7) # 轴标签文字大小 chart = QChart() # 图表实例 chart.layout().setContentsMargins(0, 0, 0, 0) # chart的外边距 chart.setMargins(QMargins(15, 5, 15, 0)) # chart内边距 chart.setTitle(name) # chart名称 """ x轴 """ axis_x_bottom = QCategoryAxis( chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue) axis_x_bottom.setLabelsAngle(-90) # 逆时针旋转90度 axis_x_bottom.setLabelsFont(font) # 设置字体样式 axis_x_bottom.setGridLineVisible(False) # 竖向连接线不可见 # chart.addAxis(axis_x_bottom, Qt.AlignBottom) # 加入坐标x轴 has_x_labels = False # 收集x轴刻度的开关 chart.x_labels = list() # 绑定chart一个x轴的刻度列表 """ 左Y轴 """ axis_y_left = QValueAxis() axis_y_left.setLabelsFont(font) axis_y_left.setLabelFormat('%i') chart.addAxis(axis_y_left, Qt.AlignLeft) # 图表加入左轴 """ 右Y轴 """ axis_y_right = QValueAxis() axis_y_right.setLabelsFont(font) axis_y_right.setLabelFormat('%.2f') chart.addAxis(axis_y_right, Qt.AlignRight) # 记录各轴的最值 x_bottom_min, x_bottom_max = 0, table_df.shape[0] y_left_min, y_left_max = 0, 0 y_right_min, y_right_max = 0, 0 # 柱形图 left_bars = QBarSeries() """ 根据左轴画柱形 """ for y_left_col in y_left_cols: # 根据左轴画折线 # 计算做轴的最值用于设置范围 table_df[y_left_col] = table_df[y_left_col].apply( covert_float) # y轴列转为浮点数值 # 获取最值 y_min, y_max = table_df[y_left_col].min(), table_df[y_left_col].max() if y_min < y_left_min: y_left_min = y_min if y_max > y_left_max: y_left_max = y_max # 取得图表的源数据作柱形 left_bar_data = table_df.iloc[:, [x_bottom, y_left_col]] bar = QBarSet(legend_labels[y_left_col]) bar.setPen(QPen(Qt.transparent)) # 设置画笔轮廓线透明(数据量大会出现空白遮住柱形) for index, point_item in enumerate(left_bar_data.values.tolist()): bar.append(point_item[1]) if not has_x_labels: chart.x_labels.append(point_item[0]) has_x_labels = True # 关闭添加轴标签 left_bars.append(bar) # 柱形加入系列 left_bars.attachAxis(axis_y_left) # 左轴的范围 axis_y_left.setRange(y_left_min, y_left_max) """ 根据右轴画柱形 """ right_bars = QBarSeries() for y_right_col in y_right_cols: # 根据右轴画柱形 # 计算做轴的最值用于设置范围 table_df[y_right_col] = table_df[y_right_col].apply( covert_float) # y轴列转为浮点数值 # 获取最值 y_min, y_max = table_df[y_right_col].min(), table_df[y_right_col].max() if y_min < y_right_min: y_right_min = y_min if y_max > y_right_max: y_right_max = y_max # 取得图线的源数据作折线图 right_bar_data = table_df.iloc[:, [x_bottom, y_right_col]] bar = QBarSet(legend_labels[y_right_col]) bar.setPen(QPen(Qt.transparent)) for position_index, point_item in enumerate( right_bar_data.values.tolist()): bar.append(point_item[1]) # 取出源数据后一条线就2列数据 right_bars.append(bar) right_bars.attachAxis(axis_x_bottom) right_bars.attachAxis(axis_y_right) # 右轴范围 axis_y_right.setRange(y_right_min, y_right_max) chart.addSeries(left_bars) # 左轴的柱形图加入图表 # print(right_bars.count()) if right_bars.count() != 0: # 为空时加入会导致空位 chart.addSeries(right_bars) chart.setAxisX(axis_x_bottom, left_bars) # 关联设置x轴 # 横轴标签设置 x_bottom_interval = int(x_bottom_max / (tick_count - 1)) if x_bottom_interval == 0: for i in range(0, x_bottom_max): axis_x_bottom.append(chart.x_labels[i], i) else: for i in range(0, x_bottom_max, x_bottom_interval): axis_x_bottom.append(chart.x_labels[i], i) chart.legend().setAlignment(Qt.AlignBottom) return chart
class SENSOR_TEST(QWidget): bars = None series = None chart = None labelWidgets = [] comms = None commsStatus = False deviceID = "HARP" def __init__(self): QWidget.__init__(self) # ADD GUI WIDGETS self.setupLayout() # LAUNCH GUI self.show() self.resize(800,600) def setupLayout(self): parentLayout = QVBoxLayout() parentLayout.setContentsMargins(20,20,20,20) buttonLayout = QHBoxLayout() # CREATE SERIAL COMMUNICATION CONNECT BUTTON self.connectButton = QPushButton("CONNECT") self.connectButton.setFixedSize(150, 40) self.connectButton.setCheckable(True) self.connectButton.setStyleSheet("QPushButton {background-color: #66BB6A; color: black; border-radius: 20px}" "QPushButton:hover {background-color: #4CAF50; color: black; border-radius: 20px}" "QPushButton:checked {background-color: #EF5350; color: white; border-radius: 20px}" "QPushButton:checked:hover {background-color: #F44336; color: white; border-radius: 20px}") # CREATE SENSORS CALIBRATION BUTTON self.calibrateButton = QPushButton("CALIBRATE") self.calibrateButton.setFixedSize(150, 40) self.calibrateButton.setStyleSheet("QPushButton {background-color: #E0E0E0; color: black; border-radius: 20px}" "QPushButton:hover {background-color: #BDBDBD; color: black; border-radius: 20px}" "QPushButton:pressed {background-color: #D5D5D5; color: black; border-radius: 20px}") # ADD BUTTONS TO HORIZONTAL LAYOUT buttonLayout.addWidget(self.connectButton, alignment = Qt.AlignLeft) buttonLayout.addWidget(self.calibrateButton, alignment = Qt.AlignRight) # CREATE BAR CHART TO DISPLAY SENSOR READINGS self.bars = QBarSet('Sensor Readings') self.bars.append([0 for i in range(5)]) self.chart = QChart() self.chart.setBackgroundRoundness(20) self.chart.layout().setContentsMargins(0, 0, 0, 0) self.series = QBarSeries() self.series.append(self.bars) self.chart.addSeries(self.series) self.chart.setTitle('Sensor Readings') # CREATE X-AXIS AS SENSOR LABELS xAxis = QBarCategoryAxis() labels = ["Sensor {}".format(i+1) for i in range(5)] xAxis.append(labels) # CREATE Y-AXIS AND SENSOR READINGS yAxis = QValueAxis() yAxis.setRange(-100, 100) yAxis.setTickCount(11) yAxis.setTitleText("Pressure (%)") # ADD AXES TO CHART self.chart.addAxis(xAxis, Qt.AlignBottom) self.chart.addAxis(yAxis, Qt.AlignLeft) self.chart.legend().setVisible(False) # ATTACH AXES TO SERIES self.series.attachAxis(xAxis) self.series.attachAxis(yAxis) # ADD CHART TO A VIEW chartView = QChartView(self.chart) labelLayout = QFrame() labelLayout.setStyleSheet("background-color: white; border-radius: 20px") layout1 = QVBoxLayout() labelLayout.setLayout(layout1) layout2 = QHBoxLayout() layout3 = QHBoxLayout() for i in range(5): label = QLabel("Sensor {}".format(i+1)) label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) label.setStyleSheet("font-weight: bold;") layout2.addWidget(label) value = QLabel("0 mV") value.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.labelWidgets.append(value) layout3.addWidget(value) layout1.addLayout(layout2, 1) layout1.addLayout(layout3, 1) parentLayout.addLayout(buttonLayout) parentLayout.addWidget(chartView, 5) parentLayout.addWidget(labelLayout, 1) # LINK WIDGETS self.connectButton.clicked.connect(self.serialToggle) self.calibrateButton.clicked.connect(self.sensorCalibrate) self.setLayout(parentLayout) def getSensorReadings(self): """ PURPOSE Requests sensor readings from ROV and updates GUI. INPUT NONE RETURNS NONE """ # SENSOR POLLING RATE (HZ) refreshRate = 100 # START QTIMER TO REPEATEDLY UPDATE SENSORS AT THE DESIRED POLLING RATE self.timer = QTimer() self.timer.setTimerType(Qt.PreciseTimer) self.timer.timeout.connect(self.getSensorReadings) self.timer.start(int(1000*1/refreshRate)) # STOP REQUESTING SENSOR VALUES IF ROV IS DISCONNECTED if self.commsStatus == False: self.timer.stop() else: # REQEST SINGLE READING sensorReadings = self.getSensors() # UPDATE VOLTAGE READINGS self.updateVoltageReadings(sensorReadings) # SCALE SENSOR READINGS scaledReadings = self.mapPressureReadings(sensorReadings) try: # UPDATE GUI self.series.remove(self.bars) self.bars = QBarSet("") self.bars.append([float(item) for item in scaledReadings]) self.series.append(self.bars) except: self.teensyDisconnect() def mapPressureReadings(self, values): mappedValues = [] for value in values: try: mappedValues.append((200/1023)*float(value) - 100) except: pass return mappedValues def updateVoltageReadings(self, values): voltageValues = [round((3300/1023)*float(i)) for i in values] for i, label in enumerate(self.labelWidgets): label.setText(str(voltageValues[i]) + " mV") def serialToggle(self, buttonState): """ PURPOSE Determines whether to connect or disconnect from the ROV serial interface. INPUT - buttonState = the state of the button (checked or unchecked). RETURNS NONE """ # CONNECT if buttonState: self.teensyConnect() # DISCONNECT else: self.teensyDisconnect() def sensorCalibrate(self): """ PURPOSE INPUT RETURNS """ self.calibrateSensors() ######################## ### SERIAL FUNCTIONS ### ######################## def teensyConnect(self): """ PURPOSE Attempts to connect to the ROV using the comms library. Changes the appearance of the connect buttons. If connection is successful, the ROV startup procedure is initiated. INPUT NONE RETURNS NONE """ # DISABLE BUTTONS TO AVOID DOUBLE CLICKS self.connectButton.setEnabled(False) # FIND ALL AVAILABLE COM PORTS availableComPorts, comPort, identity = self.findComPorts(115200, self.deviceID) print(availableComPorts, comPort, identity) # ATTEMPT CONNECTION TO ROV COM PORT status, message = self.serialConnect(comPort, 115200) print(status, message) # IF CONNECTION IS SUCCESSFUL if status == True: # MODIFY BUTTON STYLE self.connectButton.setText('DISCONNECT') # START READING SENSOR VALUES self.getSensorReadings() # IF CONNECTION IS UNSUCCESSFUL else: self.teensyDisconnect() # RE-ENABLE CONNECT BUTTONS self.connectButton.setEnabled(True) def teensyDisconnect(self): """ PURPOSE Disconnects from the ROV using the comms library. Changes the appearance of the connect buttons INPUT NONE RETURNS NONE """ # MODIFY BUTTON STYLE self.connectButton.setText('CONNECT') self.connectButton.setChecked(False) # CLOSE COM PORT if self.commsStatus: self.comms.close() self.commsStatus = False def findComPorts(self, baudRate, identity): """ PURPOSE Find all available COM ports and requests the devices identity. INPUT - menuObject = pointer to the drop down menu to display the available COM ports. - baudRate = baud rate of the serial interface. - identity = string containing the required device identity to connect to. RETURNS - availableComPorts = list of all the available COM ports. - rovComPort = the COM port that belongs to the device. - identity = the devices response from an identity request. """ # CREATE LIST OF ALL POSSIBLE COM PORTS ports = ['COM%s' % (i + 1) for i in range(256)] deviceIdentity = "" comPort = None availableComPorts = [] # CHECK WHICH COM PORTS ARE AVAILABLE for port in ports: try: comms = serial.Serial(port, baudRate, timeout = 1) availableComPorts.append(port) # REQUEST IDENTITY FROM COM PORT self.commsStatus = True deviceIdentity = self.getIdentity(comms, identity) comms.close() self.commsStatus = False # FIND WHICH COM PORT IS THE ROV if deviceIdentity == identity: comPort = port break # SKIP COM PORT IF UNAVAILABLE except (OSError, serial.SerialException): pass return availableComPorts, comPort, deviceIdentity def getIdentity(self, serialInterface, identity): """ PURPOSE Request identity from a defined COM port. INPUT - serialInterface = pointer to the serial interface object. - identity = the desired identity response from the device connected to the COM port. RETURNS - identity = the devices response. """ identity = "" startTime = datetime.now() elapsedTime = 0 # REPEATIDELY REQUEST IDENTIFICATION FROM DEVICE FOR UP TO 3 SECONDS while (identity == "") and (elapsedTime < 3): self.serialSend("?I", serialInterface) identity = self.serialReceive(serialInterface) elapsedTime = (datetime.now() - startTime).total_seconds() return identity def serialConnect(self, comPort, baudRate): """ PURPOSE Attempts to initialise a serial communication interface with a desired COM port. INPUT - rovComPort = the COM port of the ROV. - baudRate = the baud rate of the serial interface. RETURNS NONE """ self.commsStatus = False if comPort != None: try: self.comms = serial.Serial(comPort, baudRate, timeout = 1) message = "Connection to ROV successful." self.commsStatus = True except: message = "Failed to connect to {}.".format(comPort) else: message = "Failed to recognise device identity." return self.commsStatus, message def serialSend(self, command, serialInterface): """ PURPOSE Sends a string down the serial interface to the ROV. INPUT - command = the command to send. - serialInterface = pointer to the serial interface object. RETURNS NONE """ if self.commsStatus: try: serialInterface.write((command + '\n').encode('ascii')) except: self.teensyDisconnect() print("Failed to send command.") def serialReceive(self, serialInterface): """ PURPOSE Waits for data until a newline character is received. INPUT - serialInterface = pointer to the serial interface object. RETURNS NONE """ received = "" try: received = serialInterface.readline().decode('ascii').strip() except: self.teensyDisconnect() print("Failed to receive data.") return(received) def getSensors(self): """ PURPOSE Send request to device to return sensor readings. INPUT NONE RETURNS - results = array containing the sensor readings. """ results = [] # REQUEST SENSOR READINGS command = "?S" self.serialSend(command, self.comms) # READ RESPONSE INTO AN ARRAY results = self.serialReceive(self.comms).split(",") print(results) return results def calibrateSensors(self): """ """ # REQUEST SENSOR READINGS command = "?C" self.serialSend(command, self.comms)
class RowingMonitorMainWindow(QtWidgets.QMainWindow): COLOR_RED = QColor('#E03A3E') COLOR_BLUE = QColor('#009DDC') DISABLE_LOGGING = False PLOT_VISIBLE_SAMPLES = 200 PLOT_MIN_Y = -1 PLOT_MAX_Y = 55 PLOT_TIME_WINDOW_SECONDS = 7 PLOT_WIDTH_INCHES = 2 PLOT_HEIGHT_INCHES = 1 PLOT_DPI = 300 PLOT_FAST_DRAWING = False WORK_PLOT_VISIBLE_STROKES = 64 WORK_PLOT_MIN_Y = 0 WORK_PLOT_MAX_Y = 350 BOAT_SPEED_PLOT_VISIBLE_STROKES = 64 BOAT_SPEED_PLOT_MIN_Y = 0 BOAT_SPEED_PLOT_MAX_Y = 10 GUI_FONT = QtGui.QFont('Nunito SemiBold', 12) GUI_FONT_LARGE = QtGui.QFont('Nunito', 24) GUI_FONT_MEDIUM = QtGui.QFont('Nunito', 16) def __init__(self, config, data_source, *args, **kwargs): super(RowingMonitorMainWindow, self).__init__(*args, **kwargs) self.setWindowTitle('Rowing Monitor') self.config = config self.log_folder_path = config.log_folder_path self.workout = wo.WorkoutMetricsTracker( config=config, data_source=data_source ) # Connect workut emitter to UI update self.workout_qt_emitter = SignalEmitter() self.workout_qt_emitter.updated.connect(self.ui_callback) # Setup main window layout self.main_widget = QtWidgets.QWidget() self.main_widget.setFocus() self.setCentralWidget(self.main_widget) self.app_layout = QtWidgets.QVBoxLayout(self.main_widget) self.app_layout.setContentsMargins(0, 0, 0, 0) #(left, top, right, bottom) # Build button bar self.button_bar_background_widget = QtWidgets.QWidget(self.main_widget) self.button_bar_background_widget.setObjectName('ButtonBarBackground') self.button_bar_background_widget.setStyleSheet('QWidget#ButtonBarBackground {background-color: #F1F1F1;}') self.button_bar_layout = QtWidgets.QHBoxLayout(self.button_bar_background_widget) self.start_button = QtWidgets.QPushButton('Start') self.start_button.setFlat(True) # Start button style palette = self.start_button.palette() palette.setColor(palette.Button, QColor('#E03A3E')) palette.setColor(palette.ButtonText, QColor('white')) self.start_button.setAutoFillBackground(True) self.start_button.setPalette(palette) self.start_button.update() self.start_button.setFont(self.GUI_FONT) self.start_button.setMinimumSize(97, 60) self.start_button.setMaximumSize(97, 60) # Add to main window self.button_bar_layout.addWidget(self.start_button) #self.button_bar_layout.addWidget(self.button_bar_background_widget) self.button_bar_layout.setAlignment(QtCore.Qt.AlignLeft) self.button_bar_layout.setContentsMargins(0, 0, 0, 0) #(left, top, right, bottom) self.app_layout.addWidget(self.button_bar_background_widget)#.addLayout(self.button_bar_layout) self.stats_layout = QtWidgets.QHBoxLayout() self.stats_layout.setContentsMargins(0, 0, 0, 0) self.stats_layout.setSpacing(0) self.app_layout.addLayout(self.stats_layout) # Build workout stats bar self.metrics_panel_layout = QtWidgets.QVBoxLayout() self.charts_panel_layout = QtWidgets.QVBoxLayout() self.workout_totals_layout = QtWidgets.QVBoxLayout() self.time_label = QtWidgets.QLabel(self._format_total_workout_time(0)) self.distance_label = QtWidgets.QLabel(self._format_total_workout_distance(0)) self.time_label.setAlignment(QtCore.Qt.AlignCenter) self.distance_label.setAlignment(QtCore.Qt.AlignCenter) self.time_label.setFixedHeight(40) self.distance_label.setFixedHeight(30) self.workout_totals_layout.addWidget(self.time_label) self.workout_totals_layout.addWidget(self.distance_label) #self.workout_totals_layout.setSpacing(0) self.workout_totals_layout.setContentsMargins(0, 0, 0, 30) self.metrics_panel_layout.addLayout(self.workout_totals_layout) self.stroke_stats_layout = QtWidgets.QVBoxLayout() self.spm_label = QtWidgets.QLabel(self._format_strokes_per_minute(99)) self.stroke_ratio_label = QtWidgets.QLabel(self._format_stroke_ratio(1)) self.spm_label.setAlignment(QtCore.Qt.AlignCenter) self.stroke_ratio_label.setAlignment(QtCore.Qt.AlignCenter) self.spm_label.setFixedHeight(40) self.stroke_ratio_label.setFixedHeight(30) self.stroke_stats_layout.addWidget(self.spm_label) self.stroke_stats_layout.addWidget(self.stroke_ratio_label) #self.stroke_stats_layout.setSpacing(0) self.stroke_stats_layout.setContentsMargins(0, 30, 0, 30) self.metrics_panel_layout.addLayout(self.stroke_stats_layout) self.boat_stats_layout = QtWidgets.QVBoxLayout() self.boat_speed_label = QtWidgets.QLabel(self._format_boat_speed(0)) self.split_time_label = QtWidgets.QLabel(self._format_boat_pace(0)) self.boat_speed_label.setAlignment(QtCore.Qt.AlignCenter) self.split_time_label.setAlignment(QtCore.Qt.AlignCenter) self.boat_speed_label.setFixedHeight(40) self.split_time_label.setFixedHeight(30) self.boat_stats_layout.addWidget(self.boat_speed_label) self.boat_stats_layout.addWidget(self.split_time_label) #self.boat_stats_layout.setSpacing(0) self.boat_stats_layout.setContentsMargins(0, 30, 0, 0) self.metrics_panel_layout.addLayout(self.boat_stats_layout) # Appearance self.time_label.setFont(self.GUI_FONT_LARGE) self.distance_label.setFont(self.GUI_FONT_MEDIUM) self.spm_label.setFont(self.GUI_FONT_LARGE) self.stroke_ratio_label.setFont(self.GUI_FONT_MEDIUM) self.boat_speed_label.setFont(self.GUI_FONT_LARGE) self.split_time_label.setFont(self.GUI_FONT_MEDIUM) # Add to main window self.metrics_panel_layout.setSpacing(0) self.metrics_panel_layout.setContentsMargins(60, 30, 30, 30) #(left, top, right, bottom) self.charts_panel_layout.setSpacing(30) self.charts_panel_layout.setContentsMargins(30, 30, 60, 60)#(30, 30, 60, 60) #(left, top, right, bottom) self.stats_layout.addLayout(self.metrics_panel_layout) self.stats_layout.addLayout(self.charts_panel_layout) self.xdata = [0.0 for i in range(self.PLOT_VISIBLE_SAMPLES)] self.ydata = [0.0 for i in range(self.PLOT_VISIBLE_SAMPLES)] self.work_per_stroke_data = [0.0 for i in range(self.WORK_PLOT_VISIBLE_STROKES)] self.boat_speed_data = [0.0 for i in range(self.WORK_PLOT_VISIBLE_STROKES)] self.seen_strokes = 0 ############################################ # Add torque chart self.torque_plot = QChart() self.torque_plot.setContentsMargins(-26, -26, -26, -26) #self.torque_plot.setAnimationOptions(QChart.GridAxisAnimations) self.torque_plot.legend().setVisible(False) self.torque_plot_horizontal_axis = QValueAxis() self.torque_plot_vertical_axis = QValueAxis() self.torque_plot.addAxis(self.torque_plot_vertical_axis, QtCore.Qt.AlignLeft) self.torque_plot.addAxis(self.torque_plot_horizontal_axis, QtCore.Qt.AlignBottom) # Line series self.torque_plot_series = QLineSeries(self) for i in range(self.PLOT_VISIBLE_SAMPLES): self.torque_plot_series.append(0, 0) #self.torque_plot_series.setColor(QColor('#009DDC')) pen = self.torque_plot_series.pen() pen.setWidth(3) pen.setColor(self.COLOR_BLUE) pen.setJoinStyle(QtCore.Qt.RoundJoin) pen.setCapStyle(QtCore.Qt.RoundCap) self.torque_plot_series.setPen(pen) # Area series self.torque_plot_area_series = QAreaSeries() self.torque_plot_area_series.setUpperSeries(self.torque_plot_series) self.torque_plot_area_series.setLowerSeries(QLineSeries(self)) for i in range(self.PLOT_VISIBLE_SAMPLES): self.torque_plot_area_series.lowerSeries().append(0, 0) self.torque_plot_area_series.setColor(self.COLOR_BLUE) # Compose plot # self.torque_plot.addSeries(self.torque_plot_area_series) # self.torque_plot_area_series.attachAxis(self.torque_plot_horizontal_axis) # self.torque_plot_area_series.attachAxis(self.torque_plot_vertical_axis) self.torque_plot.addSeries(self.torque_plot_series) self.torque_plot_series.attachAxis(self.torque_plot_horizontal_axis) self.torque_plot_series.attachAxis(self.torque_plot_vertical_axis) # Set axes range self.torque_plot_vertical_axis.setRange(self.PLOT_MIN_Y, self.PLOT_MAX_Y) #self.torque_plot_vertical_axis.setTickCount(10) self.torque_plot_vertical_axis.setVisible(False) self.torque_plot_horizontal_axis.setRange(-self.PLOT_TIME_WINDOW_SECONDS, 0) self.torque_plot_horizontal_axis.setVisible(False) # Add plot view to GUI self.torque_plot_chartview = QChartView(self.torque_plot) self.torque_plot_chartview.setRenderHint(QPainter.Antialiasing) #self.torque_plot_chartview.setMinimumHeight(250) #self.torque_plot_chartview.resize(250, 250) self.torque_plot_box = QtWidgets.QGroupBox("Force") self.torque_plot_box.setFont(self.GUI_FONT) self.torque_plot_box.setAlignment(QtCore.Qt.AlignLeft) self.torque_plot_box_layout = QtWidgets.QVBoxLayout() self.torque_plot_box_layout.addWidget(self.torque_plot_chartview) self.torque_plot_box.setLayout(self.torque_plot_box_layout) self.charts_panel_layout.addWidget(self.torque_plot_box) ############################################ ############################################ # Add work chart self.work_plot = QChart() self.work_plot.setContentsMargins(-26, -26, -26, -26) self.work_plot.legend().setVisible(False) self.work_plot_horizontal_axis = QBarCategoryAxis() self.work_plot_vertical_axis = QValueAxis() self.work_plot.addAxis(self.work_plot_vertical_axis, QtCore.Qt.AlignLeft) self.work_plot.addAxis(self.work_plot_horizontal_axis, QtCore.Qt.AlignBottom) # Define series self.work_plot_series = QBarSeries() self.work_plot_bar_set_list = [QBarSet(str(i)) for i in range(self.WORK_PLOT_VISIBLE_STROKES)] self.work_plot_series.append(self.work_plot_bar_set_list) for bar_set in self.work_plot_bar_set_list: bar_set.append(0) self.work_plot_series.setBarWidth(1.0) # Compose plot self.work_plot.addSeries(self.work_plot_series) self.work_plot_series.attachAxis(self.work_plot_horizontal_axis) self.work_plot_series.attachAxis(self.work_plot_vertical_axis) # Set axes range self.work_plot_vertical_axis.setRange(self.WORK_PLOT_MIN_Y, self.WORK_PLOT_MAX_Y) self.work_plot_vertical_axis.setTickCount(8) self.work_plot_vertical_axis.setVisible(False) self.work_plot_horizontal_axis.append("1") self.work_plot_horizontal_axis.setVisible(False) # Add plot view to GUI self.work_plot_chartview = QChartView(self.work_plot) self.work_plot_chartview.setRenderHint(QPainter.Antialiasing) #self.work_plot_chartview.setMinimumHeight(250) #self.work_plot_chartview.resize(250, 250) self.work_plot_box = QtWidgets.QGroupBox("Work per stroke") self.work_plot_box.setFont(self.GUI_FONT) self.work_plot_box.setAlignment(QtCore.Qt.AlignLeft) self.work_plot_box_layout = QtWidgets.QVBoxLayout() self.work_plot_box_layout.addWidget(self.work_plot_chartview) self.work_plot_box.setLayout(self.work_plot_box_layout) self.charts_panel_layout.addWidget(self.work_plot_box) ############################################ ############################################ # Add boat speed chart self.boat_speed_plot = QChart() self.boat_speed_plot.setContentsMargins(-26, -26, -26, -26) #self.boat_speed_plot.setBackgroundRoundness(0) self.boat_speed_plot.legend().setVisible(False) self.boat_speed_plot_horizontal_axis = QBarCategoryAxis() self.boat_speed_plot_vertical_axis = QValueAxis() self.boat_speed_plot.addAxis(self.boat_speed_plot_vertical_axis, QtCore.Qt.AlignLeft) self.boat_speed_plot.addAxis(self.boat_speed_plot_horizontal_axis, QtCore.Qt.AlignBottom) # Define series self.boat_speed_plot_series = QBarSeries() self.boat_speed_plot_bar_set_list = [QBarSet(str(i)) for i in range(self.BOAT_SPEED_PLOT_VISIBLE_STROKES)] self.boat_speed_plot_series.append(self.boat_speed_plot_bar_set_list) for bar_set in self.boat_speed_plot_bar_set_list: bar_set.append(0) self.boat_speed_plot_series.setBarWidth(1.0) # Compose plot self.boat_speed_plot.addSeries(self.boat_speed_plot_series) self.boat_speed_plot_series.attachAxis(self.boat_speed_plot_horizontal_axis) self.boat_speed_plot_series.attachAxis(self.boat_speed_plot_vertical_axis) # Set axes range self.boat_speed_plot_vertical_axis.setRange(self.BOAT_SPEED_PLOT_MIN_Y, self.BOAT_SPEED_PLOT_MAX_Y) self.boat_speed_plot_vertical_axis.setTickCount(8) self.boat_speed_plot_vertical_axis.setVisible(False) self.boat_speed_plot_horizontal_axis.append("1") self.boat_speed_plot_horizontal_axis.setVisible(False) # Add plot view to GUI self.boat_speed_plot_chartview = QChartView(self.boat_speed_plot) self.boat_speed_plot_chartview.setRenderHint(QPainter.Antialiasing) #self.boat_speed_plot_chartview.setContentsMargins(0, 0, 0, 0) self.boat_speed_plot_box = QtWidgets.QGroupBox("Boat speed") self.boat_speed_plot_box.setFont(self.GUI_FONT) #self.boat_speed_plot_box.setFlat(True) #self.boat_speed_plot_box.setContentsMargins(0, 0, 0, 0) #self.boat_speed_plot_box.setObjectName("BoatSpeedGB") # Changed here... #self.boat_speed_plot_box.setStyleSheet('QGroupBox {background-color: white;}') #self.main_widget.setStyleSheet('QGroupBox::title { background-color: blue }') self.boat_speed_plot_box.setAlignment(QtCore.Qt.AlignLeft) self.boat_speed_plot_box_layout = QtWidgets.QVBoxLayout() self.boat_speed_plot_box_layout.addWidget(self.boat_speed_plot_chartview) self.boat_speed_plot_box.setLayout(self.boat_speed_plot_box_layout) self.charts_panel_layout.addWidget(self.boat_speed_plot_box) ############################################ # Set interaction behavior self.start_button.clicked.connect(self.start) # Update workout duration every second self.timer = QtCore.QTimer() self.timer.setInterval(1000) self.timer.timeout.connect(self.timer_tick) self.start_timestamp = None self.started = False self.show() def update_torque_plot(self): self.torque_plot_series.append(self.xdata[-1], self.ydata[-1]) self.torque_plot_series.remove(0) self.torque_plot_area_series.lowerSeries().append(self.xdata[-1], 0) self.torque_plot_area_series.lowerSeries().remove(0) self.torque_plot_horizontal_axis.setRange(self.xdata[-1] - self.PLOT_TIME_WINDOW_SECONDS, self.xdata[-1]) def update_work_plot(self): # Create new bar set new_bar_set = QBarSet(str(self.seen_strokes)) value = self.work_per_stroke_data[-1] value_rel = int(value * 255 / self.WORK_PLOT_MAX_Y) new_bar_set.append(value) new_bar_set.setColor(self.COLOR_BLUE) # QColor(value_rel, value_rel, value_rel)) # Append new set, and remove oldest self.work_plot_series.append(new_bar_set) self.work_plot_series.remove(self.work_plot_series.barSets()[0]) def update_boat_speed_plot(self): # Create new bar set new_bar_set = QBarSet(str(self.seen_strokes)) value = self.boat_speed_data[-1] value_rel = int(value * 255 / self.BOAT_SPEED_PLOT_MAX_Y) new_bar_set.append(value) new_bar_set.setColor(self.COLOR_BLUE) # QColor(value_rel, value_rel, value_rel)) # Append new set, and remove oldest self.boat_speed_plot_series.append(new_bar_set) self.boat_speed_plot_series.remove(self.boat_speed_plot_series.barSets()[0]) def start(self): if not self.started: self.start_workout() self.start_button.setText('Stop') self.started = True else: self.stop_workout() self.start_button.setText('Start') self.started = False def start_workout(self): self.timer.start() self.workout.start(qt_signal_emitter=self.workout_qt_emitter) def stop_workout(self): self.timer.stop() self.workout.stop() if not self.DISABLE_LOGGING and not DEV_MODE: self.workout.save(output_folder_path=self.log_folder_path) def _format_total_workout_time(self, value_seconds): minutes = value_seconds // 60 seconds = value_seconds % 60 return '%d:%02d' % (minutes, seconds) def _format_total_workout_distance(self, value): return f'{int(value):,} m' def _format_strokes_per_minute(self, value): return '%.1f spm' % value def _format_stroke_ratio(self, value): return '1:%.1f ratio' % value def _format_boat_speed(self, value): return '%0.2f m/s' % value def _format_boat_pace(self, value_seconds): return '%s /500m' % (self._format_total_workout_time(value_seconds)) def ui_callback(self): # If this is the first pulse, capture the current time if self.start_timestamp is None: self.start_timestamp = QtCore.QTime.currentTime() # Update distance distance = self.workout.boat.position.values[-1] self.distance_label.setText(self._format_total_workout_distance(distance)) if len(self.workout.person.torque) > 0: self.ydata = self.ydata[1:] + [self.workout.person.torque.values[-1]] self.xdata = self.xdata[1:] + [self.workout.person.torque.timestamps[-1]] self.update_torque_plot() # Update SPM new_stroke_info_available = len(self.workout.person.strokes) > self.seen_strokes if new_stroke_info_available: # SPM indicator spm = 60 / self.workout.person.strokes.values[-1].duration ratio = self.workout.person.strokes.values[-1].drive_to_recovery_ratio self.spm_label.setText(self._format_strokes_per_minute(spm)) self.stroke_ratio_label.setText(self._format_stroke_ratio(ratio)) # Work plot self.work_per_stroke_data = self.work_per_stroke_data[1:] + \ [self.workout.person.strokes.values[-1].work_done_by_person] self.update_work_plot() self.seen_strokes += 1 # Boat speed plot average_boat_speed = self.workout.boat.speed.get_average_value( start_time=self.workout.person.strokes.values[-1].start_time, end_time=self.workout.person.strokes.values[-1].end_time ) self.boat_speed_data = self.boat_speed_data[1:] + [average_boat_speed] self.boat_speed_label.setText(self._format_boat_speed(average_boat_speed)) split_time_seconds = 500.0 / average_boat_speed self.split_time_label.setText(self._format_boat_pace(split_time_seconds)) self.update_boat_speed_plot() def timer_tick(self): # Do nothing if we haven't received an encoder pulse yet. if self.start_timestamp is None: return # Update workout time label time_since_start = self.start_timestamp.secsTo(QtCore.QTime.currentTime()) self.time_label.setText(self._format_total_workout_time(time_since_start))
def createUnlinkedBar(self): max_count = 0 unlinked_bag_list = [] try: df = self.unlinked['Beam Diff'].dropna() unlinked_bag_list = df.values.tolist() except AttributeError: self.statusbar.showMessage('Data not ready') count = [0] * 4 for num in unlinked_bag_list: if -1000 <= num and num <= -51: count[0] += 1 elif -50 <= num and num <= -1: count[1] += 1 elif 151 <= num and num <= 200: count[2] += 1 elif 201 <= num: count[3] += 1 # print(count) max_count = max(count) setBar = QBarSet('Beam Difference Occurrence') setBar.append(count) series = QBarSeries() series.append(setBar) brush = QBrush(QColor(0xfdb157)) pen = QPen(QColor(0xfdb157)) pen.setWidth(2) setBar.setPen(pen) setBar.setBrush(brush) chart = QChart() font = QFont() font.setPixelSize(18) chart.setTitleFont(font) chart.setTitle('Unlinked Bags Summary') chart.addSeries(series) chart.setAnimationOptions(QChart.SeriesAnimations) labels = ['Not useful(-50 to -1000)', 'Pushed by operator(-1 to -50)', 'Slipping on belt(151 to 200)', 'Not useful 201+'] axisX = QBarCategoryAxis() axisX.append(labels) # chart.setAxisX(axisX, series) chart.addAxis(axisX, Qt.AlignBottom) # chart.ChartAreas[0].AxisX.LabelAutoFitStyle = LabelAutoFitStyle.WrodWrap series.attachAxis(axisX) axisY = QValueAxis() axisY.setRange(0, max_count+1) # chart.setAxisY(axisY, series) chart.addAxis(axisY, Qt.AlignLeft) series.attachAxis(axisY) chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) # MainWindow.setCentralWidget(chartView) return chartView
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)